Android四大组件之2 - Service
目录
1. 什么是服务 Service
Service(服务)是一个一种可以在后台执行长时间运行操作而没有用户界面的应用组件。服务可由其他应用组件启动(如Activity),服务一旦被启动将在后台一直运行,即使启动服务的组件(Activity)已销毁也不受影响。 此外,组件可以绑定到服务,以与之进行交互,甚至是执行进程间通信 (IPC)。
服务的作用:例如,服务可以后台下载更新、处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序交互,而所有这一切均可在后台进行。
服务的2 种状态:
状态 | 描述 |
---|---|
Started | Android的应用程序组件,如活动,通过startService()启动了服务,则服务是Started状态。一旦启动,服务可以在后台无限期运行,即使启动它的组件已经被销毁。 |
Bound | 当Android的应用程序组件通过bindService()绑定了服务,则服务是Bound状态。Bound状态的服务提供了一个客户服务器接口来允许组件与服务进行交互,如发送请求,获取结果,甚至通过IPC来进行跨进程通信。 |
2. 声明Service
Service包含的属性有
<service android:description="string resource"
android:directBootAware=["true" | "false"]
android:enabled=["true" | "false"]
android:exported=["true" | "false"]
android:icon="drawable resource"
android:isolatedProcess=["true" | "false"]
android:label="string resource"
android:name="string"
android:permission="string"
android:process="string" >
</service>
属性 |
说明 |
---|---|
description |
对服务进行描述,属性值应为对字符串资源的引用,以便进行本地化 |
directBootAware |
设置是否可以在用户解锁设备之前运行,默认值为“false” |
enabled |
设置是否可以由系统来实例化服务。 < application >元素有自己的enabled属性,适用于包括服务在内的所有应用程序组件。要启用服务,< application >和< service >属性必须都为“true”(默认情况下都为true)。如果其中一个是“false”,则服务被禁用 |
exported |
设置其他应用程序的组件是否可以调用本服务或与其交互,如果可以,则为“true”。当值为“false”时,只有同一个应用程序或具有相同用户ID的应用程序的组件可以启动该服务或绑定到该服务。该属性的默认值取决于服务是否包含Intent filters。没有任何过滤器意味着它只能通过指定其确切的类名来调用,这意味着该服务仅用于应用程序内部使用(因为其他人不知道类名)。所以在这种情况下,默认值为“false”。 另一方面,如果存在至少一个过滤器,意味着该服务打算供外部使用,因此默认值为“true” |
icon |
服务的图标,属性值应是对drawable资源的引用。如果未设置,则将使用应用程序图标 |
isolatedProcess |
设置该服务是否作为一个单独的进程运行,如果设置为true,此服务将在与系统其余部分隔离的特殊进程下运行,并且没有自己的权限,与它唯一的通信是通过服务API(绑定和启动) |
label |
可以向用户显示的服务的名称,属性值应是对字符串资源的引用 |
name |
服务类的完全限定名 |
permission |
设定组件必须具有的权限,得以启动服务或绑定服务。 如果startService(),bindService()或stopService()的调用者没有被授予此权限,则该方法将不会工作,并且Intent对象不会传递到服务中 |
process |
用来运行服务的进程的名称。 通常,应用程序的所有组件都运行在应用程序创建的默认进程中,它与应用程序包名具有相同的名称。 < application >元素的process属性可以为所有组件设置不同的默认值,但组件可以使用自己的进程属性覆盖默认值,从而允许跨多个进程扩展应用程序 |
3. Service的生命周期
Service 是 Android 提供一个允许长时间留驻后台的一个组件,后台服务
Android 中使用 Service 的方式有两种
- 图片左侧: startService() 启动 Service
- 图片右侧: bindService() 启动 Service
服务的整个生命周期从调用 onCreate() 开始起,到 onDestroy() 返回时结束。与 Activity 类似,服务也在 onCreate() 中完成初始设置,并在 onDestroy() 中释放所有剩余资源。例如,音乐播放服务可以在 onCreate() 中创建用于播放音乐的线程,然后在 onDestroy() 中停止该线程。
无论服务是通过 startService() 还是 bindService() 创建,都会为所有服务调用 onCreate() 和 onDestroy() 方法。
服务的有效生命周期从调用 onStartCommand() 或 onBind() 方法开始。每种方法均有 Intent 对象,该对象分别传递到 startService() 或 bindService()。
对于启动服务,有效生命周期与整个生命周期同时结束(即便是在 onStartCommand() 返回之后,服务仍然处于活动状态)。对于绑定服务,有效生命周期在 onUnbind() 返回时结束。
Service的相关方法:
方法 | 说明 |
---|---|
onCreate() | Service 第一次被创建后立即回调该方法,该方法在整个生命周期中只会调用一次 |
onDestory() | Service 被关闭时会回调该方法,该方法只会回调一次 |
onStartCommand(intent,flag,startId) | 早期版本是 onStart(intent,startId) ,当客户端调用 startService(Intent) 方法时会回调,可多次调用 startService() 方法,但不会再创建新的 Service 对象,而是继续复用前面产生的 Service 对象,但会继续回调 onStartCommand() 方法 |
IBinder onOnbind(intent) | Service 必须实现的方法,该方法会返回一个 IBinder 对象,app 通过该对象与 Service 组件进行通信 |
onUnbind(intent) | Service 上绑定的所有客户端都断开时会回调该方法 |
4. 开启 Service 的两种方式
4.1 startService() 启动 Service
首次启动会创建一个 Service 实例,依次调用 onCreate()
和 onStartCommand()
方法
此时 Service 进入运行状态,如果再次调用 startService()
启动 Service, 将不会再创建新的 Service 对象,系统会直接复用前面创建的 Service 对象,调用它的 onStartCommand()
方法
这样的 Service 与它的调用者无必然的联系,就是说当调用者结束了自己的生命周期,但是只要不调用 stopService()
,那么 Service 还是会继续运行的
无论启动了多少次 Service, 只需调用一次 stopService()
即可停掉 Service
定义一个类继承
Service
在
Manifest.xml
文件中配置该Service
使用
Context
的startService(intent)
方法开启服务。使用
Context
的stopService(intent)
方法关闭服务。该启动方式,
app
杀死、Activity
销毁没有任何影响,服务不会停止销毁。
例子:利用两个按钮对服务测试,一个按钮开启,一个按钮停止服务
1,新建一个 Service MsTestService.java
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
public class MsTestService extends Service {
private final String TAG = "MsTestService";
//必须要实现的方法
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind方法被调用!");
return null;
}
//Service被创建时调用
@Override
public void onCreate() {
Log.i(TAG, "onCreate方法被调用!");
super.onCreate();
}
//Service被启动时调用
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "onStartCommand方法被调用!");
return super.onStartCommand(intent, flags, startId);
}
//Service被关闭之前回调
@Override
public void onDestroy() {
Log.i(TAG, "onDestory方法被调用!");
super.onDestroy();
}
}
2. AndroidManifest.xml
完成 Service
注册,在 </activity>
后添加
[注意:service 需要自己动手注册]
<!-- 配置 Service 组件,同时配置一个 action -->
<service android:name=".MsTestService">
<intent-filter>
<action android:name="cn.twle.android.startservice.MS_TEST_SERVICE"/>
</intent-filter>
</service>
3. UI 设置2 个按钮
<?xml version="1.0" encoding="utf-8" ?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="horizontal" >
<Button
android:text="启动 Service"
android:id="@+id/service_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:text="停止 Service"
android:id="@+id/service_stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
4. MainActivity.java
在按钮的点击事件中分别 调用 startService()
和 stopService()
import android.support.v7.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button service_start = (Button) findViewById(R.id.service_start);
Button service_stop = (Button) findViewById(R.id.service_stop);
//创建启动 Service 的 Intent
final Intent it = new Intent(this,MsTestService.class);
//为两个按钮设置点击事件,分别是启动与停止 service
service_start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startService(it);
}
});
service_stop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
stopService(it);
}
});
}
}
-
多次点击启动 Service, 只会重复地调用
onStartCommand()
-
无论我们启动多少次 Service,一个
stopService()
就会停止 Service
4.2 .使用 bindService()
startService()
方式启动一个非绑定的 Service
,发现 onBind()
方法并没有被调用
bindService(Intent Service,ServiceConnection conn,int flags)
参数 | 说明 |
---|---|
service | 通过该 Intent 指定要启动的 Service |
conn | ServiceConnection 对象,用户监听访问者与 Service 间的连接情况,连接成功回调该对象中的 onServiceConnected(ComponentName,IBinder) 方法;如果 Service 所在的宿主由于异常终止或者其它原因终止,导致 Service 与访问者间断开连接时调用 onServiceDisconnected(CompanentName ) 方法,主动通过 unBindService() 方法断开并不会调用上述方法 |
flags | 指定绑定时是否自动创建 Service** (如果 Service 还未创建),参数可以是 0(不自动创建),BIND_AUTO_CREATE (自动创建) |
1. 新建一个 Service MsTestService.java
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.Binder;
import android.util.Log;
public class MsTestService extends Service {
private final String TAG = "MsTestService";
private int count;
private boolean quit;
//定义onBinder方法所返回的对象
private MsBinder binder = new MsBinder();
public class MsBinder extends Binder
{
public int getCount()
{
return count;
}
}
//必须实现的方法,绑定改Service时回调该方法
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind方法被调用!");
return binder;
}
//Service被创建时回调
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "onCreate方法被调用!");
//创建一个线程动态地修改count的值
new Thread()
{
public void run()
{
while(!quit)
{
try
{
Thread.sleep(1000);
}catch(InterruptedException e){e.printStackTrace();}
count++;
}
};
}.start();
}
//Service断开连接时回调
@Override
public boolean onUnbind(Intent intent) {
Log.i(TAG, "onUnbind方法被调用!");
return true;
}
//Service被关闭前回调
@Override
public void onDestroy() {
super.onDestroy();
this.quit = true;
Log.i(TAG, "onDestroyed方法被调用!");
}
@Override
public void onRebind(Intent intent) {
Log.i(TAG, "onRebind方法被调用!");
super.onRebind(intent);
}
}
2. 修改 AndroidManifest.xml
完成 Service
注册,在 </activity>
后添加
<!-- 配置 Service 组件,同时配置一个 action -->
<service android:name=".MsTestService">
<intent-filter>
<action android:name="cn.twle.android.bindservice.MS_TEST_SERVICE"/>
</intent-filter>
</service>
3. 修改 activity_main.xml
添加两个按钮
<?xml version="1.0" encoding="utf-8" ?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="horizontal" >
<Button
android:text="锁定 Service"
android:id="@+id/service_lock"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:text="解除锁定"
android:id="@+id/service_unlock"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:text="获取状态"
android:id="@+id/service_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
4. MainActivity.java
import android.app.Service;
import android.support.v7.app.AppCompatActivity;
import android.content.Intent;
import android.content.ComponentName;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import android.util.Log;
public class MainActivity extends AppCompatActivity {
//保持所启动的Service的IBinder对象,同时定义一个ServiceConnection对象
MsTestService.MsBinder binder;
private ServiceConnection conn = new ServiceConnection() {
// Activity 与 Service 断开连接时回调该方法
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i("MsTestService","------Service DisConnected-------");
}
// Activity 与 Service 连接成功时回调该方法
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i("MsTestService","------Service Connected-------");
binder = (MsTestService.MsBinder) service;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button service_lock = (Button) findViewById(R.id.service_lock);
Button service_unlock = (Button) findViewById(R.id.service_unlock);
Button service_status = (Button) findViewById(R.id.service_status);
final Intent it = new Intent(this,MsTestService.class);
service_lock.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//绑定service
bindService(it, conn, Service.BIND_AUTO_CREATE);
}
});
service_unlock.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//解除service绑定
unbindService(conn);
}
});
service_status.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(getApplicationContext(), "Service的count的值为:"
+ binder.getCount(), Toast.LENGTH_SHORT).show();
}
});
}
}
使用 bindService
绑定 Service,依次调用 onCreate()
,onBind()
方法
我们可以在 onBind()
方法中返回自定义的 IBinder 对象;
再接着调用的是 ServiceConnection 的 onServiceConnected()
方法该方法中可以获得 IBinder 对象,从而进行相关操作;
当 Service 解除绑定后会自动调用 onUnbind()
和 onDestroyed()
方法
如果绑定多客户端情况需要解除所有的绑定才会调用 onDestoryed()
方法进行销毁
两种 服务绑定的区别:
-
启动服务
该服务在其他组件调用 startService() 时创建,然后无限期运行,且必须通过调用 stopSelf() 来自行停止运行。此外,其他组件也可以通过调用 stopService() 来停止服务。服务停止后,系统会将其销毁。 -
绑定服务
该服务在另一个组件(客户端)调用 bindService() 时创建。然后,客户端通过 IBinder 接口与服务进行通信。客户端可以通过调用 unbindService() 关闭连接。多个客户端可以绑定到相同服务,而且当所有绑定全部取消后,系统即会销毁该服务。 (服务不必自行停止运行)
4.3 IntentService 耗时操作
如果我们直接把耗时线程放到 Service 中的 onStart()
方法中,虽然可以这样做,但是很容易 会引起 ANR 异常 ( Application Not Responding),这是因为
-
Service
不是一个单独的进程,它和它的应用程序在同一个进程中 -
Service
也不是一个线程,我们应该避免在 Service 中进行耗时操作
为了解决可能需要在
Service
中进行耗时的任务,Android
提供了IntentService
IntentService
是继承自 Service 并用来处理异步请求的一个类
IntentService
中有一个工作线程来处理耗时操作,请求的 Intent 记录会加入队列
IntentService 的一般用法如下
客户端通过
startService(Intent)
来启动IntentService
不需要手动的去控制
IntentService
,当任务执行完后IntentService
会自动停止IntentService 可以被启动多次,每个耗时操作会以工作队列的方式在
IntentService
的onHandleIntent()
回调方法中执行,并且每次只会执行一个工作线程,执行完一,再到二这样
会创建独立的
worker
线程来处理所有的Intent
请求;会创建独立的
worker
线程来处理onHandleIntent()
方法实现的代码,无需处理多线程问题;所有请求处理完成后,
IntentService
会自动停止,无需调用stopSelf()
方法停止Service
;
传入多个参数:使用bundle
Intent intent3 = new Intent(this, MyIntentService.class);
Bundle bundle3 = new Bundle();
bundle3.putString("param", "s3");
intent3.putExtras(bundle3);
1)Service 与 Activity 通讯:
前面几章节 Activity 只是负责启动和停止 Service
,
但如果启动的是一个下载 的后台 Service,而我们又想知道 Service 中下载任务的进度,那么就需要 Service 与 Activity 进行通信
Activity 与 Service 通信的媒介就是 Service 中的 onBind()
方法
onBind()
方法会返回一个自定义的 Binder 对象
推荐阅读
-
Android组件之DrawerLayout实现抽屉菜单
-
android开发之listView组件用法实例简析
-
Android四大组件之Service(服务)实例详解
-
Android编程四大组件之Activity用法实例分析
-
Android编程四大组件之BroadcastReceiver(广播接收者)用法实例
-
Android编程开发之TextView控件用法(2种方法)
-
Android开发之OpenGL绘制2D图形的方法分析
-
Android编程开发之Spinner组件用法
-
android开发之listView组件用法实例简析
-
Android编程四大组件之BroadcastReceiver(广播接收者)用法实例