Android Service
创建、配置 Service
- 定义一个继承
Service
的子类
public class TestService extends Service {
/*
Service 被绑定时回调该方法
必须实现的方法,返回一个 IBinder 对象,应用程序可通过该对象与 Service 组件通信
*/
@Nullable
@Override
public IBinder onBind(Intent intent) {
System.out.println("intent = " + intent);
return null;
}
/*
Service 被创建时回调该方法
*/
@Override
public void onCreate() {
super.onCreate();
System.out.println("onCreate");
}
/*
Service 被启动时回调该方法
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
/*
Service 被关闭之前回调该方法
*/
@Override
public void onDestroy() {
super.onDestroy();
}
/*
当该 Service 上绑定的所有客户端都断开连接时将会回调该方法
*/
@Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
}
- 在
AndroidManifest.xml
文件中配置该Service
<service
android:name=".TestService"
android:enabled="true"
android:exported="true"></service>
-
name
指定该Service
的实现类类名 -
exported
指定该Service
是否能被其它 APP 启动。如果在配置该Service
时指定了<intent-filter.../>
子元素,则该属性默认为true
-
permission
指定启动该Service
所需的权限 -
process
指定该Service
所处的线程,该Service
组件默认处于该 APP 所在的进程中。Android 四大组件都可通过该属性指定进程
没有配置 <intent-filter.../>
意味着该 Service
不能响应任何 Intent
,只能通过指定 Component
的 Intent
来启动
运行 Service
的两种方式
- 通过
Context
的startService()
方法:通过该方法启动Service
,访问者与Service
之间没有关联,即使访问者退出了,Service
也仍然运行。 - 通过
Context
的bindService()
方法:使用该方法启动Service
,访问者与Service
绑定在一起,访问者一旦退出,Service
也就终止了。
启动和停止 Service
// 创建启动Service的Intent
Intent intent = new Intent(this, TestService.class);
// 启动指定Service
startBn.setOnClickListener(view -> this.startService(intent));
// 停止指定Service
stopBn.setOnClickListener(view -> stopService(intent));
绑定本地 Service 并与之通信
当程序通过 stariService()
和 StopService()
启动、关闭 Service
时,Service
与访问者之间基本上不存在太多的关联,因此 Service
和访问者之间也无法进行通信、交换数据。
如果 Service
和访问者之间需要进行方法调用或交换数据,则应该使用 bindService()
和 unbindService()
方法启动、关闭 Service
。
-
service
该参数通过Intent
指定要启动的Service
。 -
conn
该参数是一个ServiceConnection
对象,该对象用于监听访问者与Service
之间的连接情况。当访问者与Service
之间连接成功时将回调该ServiceConnection
对象的onServiceConnected(ComponentName name, IBinder service)
方法;当Service
所在的宿主进程由于异常中止或其他原因终止,导致该Service
与访问者之间断开连接时回调该ServiceConnection
对象的onServiceDisconnected(ComponentName name)
方法。当调用者主动通过unbindService()
方法断开与Service
的连接时,ServiceConnection
时象的onServiceDisconnected(ComponentName name)
方法并不会被调用。 -
flags
指定绑定时是否自动创建Service
(如果Service
还未创建)。该参数可指定为 0(不自动创建)或 BIND_AUTO_CREATE(自动创建)。
BindService
public class BindService extends Service {
private int count;
private boolean quit;
// 定义onBinder方法所返回的对象
private MyBinder binder = new MyBinder();
// 通过继承Binder来实现IBinder类
class MyBinder extends Binder // ①
{
// 获取Service的运行状态:count
public int getCount() {
return BindService.this.count;
}
}
// Service被绑定时回调该方法
@Override
public IBinder onBind(Intent intent) {
System.out.println("Service is Binded");
// 返回IBinder对象
return binder;
}
// Service被创建时回调该方法
@Override
public void onCreate() {
super.onCreate();
System.out.println("Service is Created");
// 启动一条线程,动态地修改count状态值
new Thread(() ->
{
while (!quit) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
BindService.this.count++;
}
}).start();
}
// Service被断开连接时回调该方法
@Override
public boolean onUnbind(Intent intent) {
System.out.println("Service is Unbinded");
return true;
}
// Service被关闭之前回调该方法
@Override
public void onDestroy() {
super.onDestroy();
this.quit = true;
System.out.println("Service is Destroyed");
}
}
MainActivity
public class MainActivity extends AppCompatActivity {
// 保持所启动的Service的IBinder对象
private BindService.MyBinder binder;
// 定义一个ServiceConnection对象
private ServiceConnection conn = new ServiceConnection() {
// 当该Activity与Service连接成功时回调该方法
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
System.out.println("--Service Connected--");
// 获取Service的onBind方法所返回的MyBinder对象
binder = (BindService.MyBinder) service; // ①
}
// 当该Activity与Service断开连接时回调该方法
@Override
public void onServiceDisconnected(ComponentName name) {
System.out.println("--Service Disconnected--");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 获取程序界面中的start、stop、getServiceStatus按钮
Button bindBn = findViewById(R.id.bind);
Button unbindBn = findViewById(R.id.unbind);
Button getServiceStatusBn = findViewById(R.id.getServiceStatus);
// 创建启动Service的Intent
Intent intent = new Intent(this, BindService.class);
bindBn.setOnClickListener(view ->
// 绑定指定Service
bindService(intent, conn, Service.BIND_AUTO_CREATE));
unbindBn.setOnClickListener(view ->
// 解除绑定Service
unbindService(conn));
getServiceStatusBn.setOnClickListener(view ->
// 获取并显示Service的count值
Toast.makeText(MainActivity.this, "Service的count值为:" +
binder.getCount(), Toast.LENGTH_SHORT).show()); // ②
}
}
IntentService
IntentService 是 Service 的子类
Service存在的两个问题
-
Service
不会专门启动一个单独的进程,Service
与它所在应用位于同一个进程中。 -
Service
不是一条新的线程,因此不应该在Service
中直接处理耗时的任务。
IntentService
将会使用队列来管理请求 Intent
,每当客户端代码通过 Intent
请求启动 IntentService
时,IntentService
会将该 Intent
加入队列中,然后开启一条新的 worker
线程来处理该 Intent
。对于异步的 startService()
请求,IntentService
会按次序依次处理队列中的 Intent
,该线程保证同一时刻只处理一个 Intent
。由于 IntentService
使用新的 worker
线程处理 Intent
请求,因此 IntentService
不会阻塞主线程,所以 IntentService
自己就可以处理耗时任务。
IntentService
具有如下特征
-
IntentService
会创建单独的worker
线程来处理所有的Intent
请求。 -
IntentService
会创建单独的worker
线程来处理onHandleIntent()
方法实现的代码,因此开发者无须处理多线程问题。 - 当所有请求处理完成后,
IntentService
会自动停止,因此开发者无须调用stopSelf()
方法来停止该Service
。 - 为
Service
的onBind()
方法提供了默认实现,默认实现的onBind()
方法返回null
。 - 为
Service
的onStartCommand()
方法提供了默认实现,该实现会将请求Intent
添加到队列中。
TestIntentService
不会阻塞前台 UI 线程,可顺利完成任务
public class TestIntentService extends IntentService {
public TestIntentService() {
super("MyIntentService");
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
// 该方法内可以执行任何耗时任务,比如下载文件等,此处只是让线程暂停20秒
long endTime = System.currentTimeMillis() + 20 * 1000;
System.out.println("onStartCommand");
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("---耗时任务执行完成---");
}
}
TestService
会导致程序 UI 线程被阻塞,阻塞时间过长会出现 ANR 异常
public class TestService extends Service {
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 该方法内执行耗时任务可能导致ANR(Application Not Responding)异常
long endTime = System.currentTimeMillis() + 20 * 1000;
System.out.println("onStart");
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("---耗时任务执行完成---");
return START_STICKY;
}
}
public void startService(View source) {
// 创建需要启动的Service的Intent
Intent intent = new Intent(this, TestService.class);
// 启动Service
startService(intent);
}
public void startIntentService(View source) {
// 创建需要启动的IntentService的Intent
Intent intent = new Intent(this, TestIntentService.class);
// 启动IntentService
startService(intent);
}
摘抄至《疯狂Android讲义(第4版)》