服务 IntentService 工作线程 stopself MD


目录

介绍

Google 为方便开发者使用,提高开发效率,封装了 IntentService 和 HandlerThread。HandlerThread 继承自 Thread,内部封装了 Looper。

IntentService 是继承自 Service 并处理异步请求的一个类,在 IntentService 内有一个工作线程来处理耗时操作,当任务执行完后,IntentService 会自动停止,不需要我们去手动结束。如果启动 IntentService 多次,那么每一个耗时操作会以工作队列的方式在 IntentService 的 onHandleIntent 回调方法中执行,依次去执行,执行完自动结束。

同一个IntentService只会新建一个线程,因为其采用的就是类似主线程的机制。

IntentService 中使用的 Handler、Looper、MessageQueue 机制把消息发送到线程中去执行的,所以多次启动 IntentService 不会重新创建新的服务和新的线程,只是把消息加入消息队列中等待执行,而如果服务停止,会清除消息队列中的消息,后续的事件得不到执行。

IntentService 类

IntentService是Service的基类,可根据需要处理异步请求(表示为Intents)。客户端通过Context#startService(Intent)调用发送请求; 根据需要启动服务,使用工作线程依次处理每个Intent,并在工作完成时自行停止。

这种“工作队列处理器”模式通常用于从应用程序的主线程卸载任务。IntentService类的存在就是为了简化此模式并处理机制。要使用它,请扩展IntentService并实现onHandleIntent(Intent)。 IntentService将接收Intents,启动一个工作线程,并根据需要停止服务。

所有请求都在一个工作线程上处理 - 它们可能需要运行很长时间(并且不会阻止应用程序的主循环),但一次只能处理一个请求。

onHandleIntent 方法

在工作线程上调用此方法并请求处理。一次只处理一个Intent,但处理过程发生在独立于其他应用程序逻辑运行的工作线程上。因此,如果此代码需要很长时间,它将阻止对同一个IntentService的其他请求,但它不会阻止其他任何内容。处理完所有请求后,IntentService会自行停止,因此您不应该调用 stopSelf。

测试

参考

最佳应用场景:后台队列按顺序异步下载、下载完毕后自动结束服务!

测试代码

IntentService 源码

public abstract class IntentService extends Service {
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private String mName;
private boolean mRedelivery; private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
} @Override
public void handleMessage(Message msg) {
Log.i("bqt", "开始onHandleIntent");
onHandleIntent((Intent) msg.obj);
Log.i("bqt", "结束onHandleIntent,尝试stopSelf");
stopSelf(msg.arg1);//处理完毕后自动调用stopSelf结束服务,注意如果此时还有未执行完的message则不会结束!
}
} public IntentService(String name) {
super();
mName = name; //Used to name the worker thread, important only for debugging.
} public void setIntentRedelivery(boolean enabled) {
mRedelivery = enabled;
} @Override
public void onCreate() {
// TODO: It would be nice to have an option to hold a partial wakelock 如果有一个部分唤醒锁的选项会更好
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start(); mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
} @Override
public int onStartCommand(Intent intent, int flags, int startId) {
onStart(intent, startId); //You should not override this method for your IntentService. Instead, override onHandleIntent
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
} @Override
public void onStart(Intent intent, int startId) {
//每启动一次onStart方法,就会把消息数据发给mServiceHandler,相当于发送了一次Message消息给HandlerThread的消息队列
//mServiceHandler会把数据传给onHandleIntent方法,所以每次onStart之后都会调用我们重写的onHandleIntent方法去处理数据
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
} @Override
public void onDestroy() {
mServiceLooper.quit();
} @Override
public IBinder onBind(Intent intent) {
return null; //Unless you provide binding for your service, you don't need to implement this method
} protected abstract void onHandleIntent(Intent intent);
}

MyIntentService

public class MyIntentService extends IntentService {

    public MyIntentService() {
super("包青天的工作线程");
} @Override
protected void onHandleIntent(Intent intent) {//注意,因为这里是异步操作,所以这里不能直接使用Toast。
//所有请求的Intent记录会按顺序加入到【队列】中并按顺序【异步】执行,并且每次只会执行【一个】工作线程
//当所有任务执行完后IntentService会【自动】停止
Log.i("bqt", "onHandleIntent");
try {
int count = 0;
sendThreadStatus("线程启动", count);
Thread.sleep(500);
while (count <= 100) {
count++;
Thread.sleep(30);
sendThreadStatus("线程运行中...", count);
}
sendThreadStatus("线程结束", count);
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
} @Override
public void onCreate() {
super.onCreate();
Log.i("bqt", "onCreate");
sendServiceStatus("服务启动");
} @Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("bqt", "onStartCommand");
return super.onStartCommand(intent, flags, startId);
} @Override
public void onDestroy() {
super.onDestroy();
Log.i("bqt", "onDestroy");
sendServiceStatus("服务结束");
} // 发送服务状态信息
private void sendServiceStatus(String status) {
Intent intent = new Intent(MainActivity.ACTION_TYPE_SERVICE);
intent.putExtra("status", status);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
} // 发送线程状态信息
private void sendThreadStatus(String status, int progress) {
Intent intent = new Intent(MainActivity.ACTION_TYPE_THREAD);
intent.putExtra("status", status);
intent.putExtra("progress", progress);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
}

Activity

public class MainActivity extends ListActivity {

    public static final String ACTION_TYPE_SERVICE = "com.bqt.type.service";
public static final String ACTION_TYPE_THREAD = "com.bqt.type.thread"; ProgressBar progressBar;
TextView tvServiceStatus;
TextView tvThreadStatus; protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String[] array = {"启动 IntentService",
"结束 IntentService",
"",};
setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, Arrays.asList(array))); tvServiceStatus = new TextView(this);
tvThreadStatus = new TextView(this);
progressBar = new ProgressBar(this, null, android.R.attr.progressBarStyleHorizontal);
progressBar.setIndeterminate(false);
progressBar.setMax(100);
getListView().addFooterView(tvServiceStatus);
getListView().addFooterView(tvThreadStatus);
getListView().addFooterView(progressBar); IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ACTION_TYPE_SERVICE);
intentFilter.addAction(ACTION_TYPE_THREAD);
LocalBroadcastManager.getInstance(this).registerReceiver(new MyBroadcastReceiver(), intentFilter);
} @Override
protected void onListItemClick(ListView l, View v, int position, long id) {
switch (position) {
case 0:
startService(new Intent(this, MyIntentService.class));// 像启动 Service 那样启动 IntentService
break;
case 1:
stopService(new Intent(this, MyIntentService.class));
break;
case 2: break;
}
} class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
switch (intent.getAction()) {
case ACTION_TYPE_SERVICE:
tvServiceStatus.setText("服务状态:" + intent.getStringExtra("status"));
break;
case ACTION_TYPE_THREAD:
int progress = intent.getIntExtra("progress", 0);
tvThreadStatus.setText("线程状态:" + intent.getStringExtra("status"));
progressBar.setProgress(progress);
break;
}
}
}
}

测试情形

启动一次

点击[启动 IntentService],服务运行,线程计数完成后,服务和线程都结束。

15:18:45.488: onCreate
15:18:45.489: onStartCommand
15:18:45.489: onStart
15:18:45.489: 开始onHandleIntent
15:18:45.489: onHandleIntent //【开始在工作线程执行任务】
15:18:49.651: 结束onHandleIntent,尝试stopSelf //【线程执行完毕后调用stopSelf结束任务】
15:18:49.659: onDestroy //【服务成功被结束】

启动一次后立即结束

点击[启动 IntentService],服务运行,线程计数完成前,点击[停止 IntentService],服务结束,线程计数完成后线程结束。

15:43:17.569: onCreate
15:43:17.570: onStartCommand
15:43:17.570: onStart
15:43:17.570: 开始onHandleIntent
15:43:17.570: onHandleIntent 15:43:18.895: onDestroy //【调用stopService会立即结束服务,但工作线程并不会结束,而会正常的执行】 15:43:21.724: 结束onHandleIntent,尝试stopSelf //【线程正常执行完毕,此前服务已经结束了】

连续启动两次

点击两次[启动 IntentService],服务运行,第一次线程计数完成后,进行第二次线程计数,两次完成后,服务和线程都结束。

14:36:30.794: onCreate
14:36:30.795: onStartCommand
14:36:30.795: 开始onHandleIntent
14:36:30.796: onHandleIntent 14:36:32.218: onStartCommand //【在执行第一个任务时启动第二个任务】 14:36:34.986: 结束onHandleIntent,尝试stopSelf //【因为此startId不是最新的startId,所以服务结束失败】
-----------------------------------------
14:36:34.993: 开始onHandleIntent
14:36:34.993: onHandleIntent
14:36:39.178: 结束onHandleIntent,尝试stopSelf //【第二个任务完成时的startId是最新的startId,所以能成功结束服务】
-----------------------------------------
14:36:39.188: onDestroy //【服务结束】

连续启动两次后立即结束

点击两次[启动 IntentService],服务运行,在第一次线程计数完成前,点击[停止 IntentService],服务结束,第一次线程计数结束后不进行第二次计数。

16:34:05.052: onCreate
16:34:05.054: onStartCommand
16:34:05.054: onStart
16:34:05.055: 开始onHandleIntent
16:34:05.055: onHandleIntent 16:34:05.463: onStartCommand
16:34:05.463: onStart 16:34:05.985: onDestroy //【调用stopService会立即结束服务,但工作线程并不会结束,而会正常的执行】 16:34:09.237: 结束onHandleIntent,尝试stopSelf //【因为服务已经结束,所以不会再执行未开启的任务】

Service 的 stopself 方法详解

结论

  • stopSelf():直接停止服务,和调用 Context#stopService 的效果完全一致。
  • stopSelf(int startId):在其参数 startId 跟最后启动该 service 时生成的 ID 相等时才会执行停止服务。
  • stopSelfResult(int startId):和 stopSelf(int startId) 的区别仅仅是有返回值,当成功停止服务时返回 true,否则返回 false。
  • 每次调用 startService 时 Service 都会重新赋值 startId 值,从1开始,如果 Service 没有被关闭,那这个值会顺序增加。
  • IntentService 在调用 stopSelf(int startId) 结束自己时,总是拿最新的 startId 和上一次传入的 startId 作比较,如果相等则结束自己,如果不相等则不结束,而IntentService 中维护着一个消息队列,要想结束自己必须等到消息队列没有消息了。

使用场景

如果同时有多个服务启动请求发送到onStartCommand(),不应该在处理完一个请求后调用stopSelf(),因为在调用此函数销毁service之前,可能 service 又接收到新的启动请求,如果此时 service 被销毁,新的请求将得不到处理。此情况应该调用stopSelf(int startId),例如 IntentService 中,在执行完异步任务后,就是通过调用stopSelf(int startId)来结束服务的。

源码

先看看 onStartCommand 方法。

每次通过 startService 方式启动服务时,都会调用 onStartCommand 方法,此方法中有一个 startId,这个 startId 代表启动服务的次数,也代表着某一特定启动请求。

文档中明确说明了,这个 startId 其实就是在 stopSelfResult(int) 中用的。

//startId:A unique integer representing this specific request to start. Use with stopSelfResult(int).
public int onStartCommand(Intent intent, @StartArgFlags int flags, int startId) {
onStart(intent, startId);
return mStartCompatibility ? START_STICKY_COMPATIBILITY : START_STICKY;
}

再看看 stopSelf() 方法,可以看到其只是调用了stopSelf(-1)

//Stop the service, if it was previously先前 started.
//This is the same as calling Context#stopService for this particular特定 service.
public final void stopSelf() {
stopSelf(-1);
}

再看看 stopSelf(int startId) 方法,文档中没有什么解释,只说是旧版本中stopSelfResult的无返回值形式。

//Old version of stopSelfResult that doesn't return a result.
public final void stopSelf(int startId) {
if (mActivityManager == null) return;
try {
mActivityManager.stopServiceToken(new ComponentName(this, mClassName), mToken, startId);
} catch (RemoteException ex) {
}
}
public final boolean stopSelfResult(int startId) {
if (mActivityManager == null) return false; //停止失败
try {
return mActivityManager.stopServiceToken(new ComponentName(this, mClassName), mToken, startId);
} catch (RemoteException ex) {
}
return false; //停止失败
}

以下是文档中的详细描述:

  • 如果最近启动服务时的ID就是此startId,则停止服务。

  • 这与对此特定服务调用Context#stopService的效果相同,但如果有一个来自客户端的启动请求(在执行此方法时这个请求还在onStart中,所以你没发现这个请求),则允许您安全地避免停止服务。

  • 要小心调用此函数后的顺序。

  • 如果在为之前收到的ID调用之前使用最近收到的ID调用此函数,则无论如何都会立即停止该服务。

  • 如果您最终可能无序处理ID(例如通过在不同的线程上调度它们),那么您有责任按照收到它们的顺序停止它们。

参数与返回值:

  • startId:The most recent start identifier received in #onStart. onStart中收到的最新启动标识符
  • Returns true if the startId matches the last start request and the service will be stopped, else false.

2019-2-13

04-13 16:00