Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱
MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina.com

服务 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 is a base class for Services that handle asynchronous requests (expressed as Intents) on demand. Clients send requests through Context#startService(Intent) calls; the service is started as needed, handles each Intent in turn using a worker thread, and stops itself when it runs out of work.

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

This "work queue processor" pattern is commonly used to offload tasks from an application's main thread. The IntentService class exists to simplify this pattern and take care of the mechanics. To use it, extend IntentService and implement onHandleIntent(Intent). IntentService will receive the Intents, launch a worker thread, and stop the service as appropriate.

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

All requests are handled on a single worker thread -- they may take as long as necessary (and will not block the application's main loop), but only one request will be processed at a time.

onHandleIntent 方法

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

This method is invoked on the worker thread with a request to process. Only one Intent is processed at a time, but the processing happens on a worker thread that runs independently from other application logic. So, if this code takes a long time, it will hold up other requests to the same IntentService, but it will not hold up anything else. When all requests have been handled, the IntentService stops itself, so you should not call 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,则停止服务。

    Stop the service if the most recent time it was started was startId.

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

    This is the same as calling Context#stopService for this particular service ,but allows you to safely avoid stopping ,if there is a start request from a client ,that you haven't yet seen in #onStart.

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

    Be careful about ordering of your calls to this function.

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

    If you call this function with the most-recently received ID before you have called it for previously received IDs, the service will be immediately stopped anyway.

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

    If you may end up processing IDs out of order (such as by dispatching them on separate threads), then you are responsible for stopping them in the same order you received them.

参数与返回值:

  • 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

IntentService 服务 工作线程 stopself MD的更多相关文章

  1. 服务 Service 简单案例 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  2. Android系统Surface机制的SurfaceFlinger服务的线程模型分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/8062945 在前面两篇文章中,我们分析了Sur ...

  3. 转载:IIS 之 连接数、并发连接数、最大并发工作线程数、队列长度、最大工作进程数

    一.IIS连接数 一般购买过虚拟主机的朋友都熟悉购买时,会限制IIS连接数,顾名思义即为IIS服务器可以同时容纳客户请求的最高连接数,准确的说应该叫“IIS限制连接数”. 客户请求的连接内容包括: [ ...

  4. RxJava 操作符 on和doOn 线程切换 调度 Schedulers 线程池 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  5. IIS 之 连接数、并发连接数、最大并发工作线程数、队列长度、最大工作进程数

    一.IIS连接数 一般购买过虚拟主机的朋友都熟悉购买时,会限制IIS连接数,顾名思义即为IIS服务器可以同时容纳客户请求的最高连接数,准确的说应该叫“IIS限制连接数”. 客户请求的连接内容包括: [ ...

  6. IIS:连接数、并发连接数、最大并发工作线程数、应用程序池的队列长度、应用程序池的最大工作进程数详解

    Internet Information Services(IIS,互联网信息服务),是由微软公司提供的基于运行Microsoft Windows的互联网基本服务.最初是Windows NT版本的可选 ...

  7. TSQLDBServerHttpApi使用工作线程池

    TSQLDBServerHttpApi使用工作线程池 TSQLDBServerHttpApi创建时,默认是使用单线程模式,且只使用一个数据库连接,服务端要应对众多的客户端只靠一个工作线程(主线程)和一 ...

  8. IIS最大并发连接数 = 队列长度 + IIS最大并发工作线程数

    深入理解IIS的多线程工作机制   首先让我们来看看IIS里面的这2个数字:最大并发连接数,队列长度.先说这2个数字在哪里看. 最大并发连接数:在IIS中选中一个网站,右键网站名称,在右键菜单中找到并 ...

  9. JAVA之工作线程数究竟要设置多少

    一.需求缘起 Web-Server通常有个配置,最大工作线程数,后端服务一般也有个配置,工作线程池的线程数量,这个线程数的配置不同的业务架构师有不同的经验值,有些业务设置为CPU核数的2倍,有些业务设 ...

随机推荐

  1. web----Twisted

    Twisted模块: Twisted是一个事件驱动的网络框架,其中包含了诸多功能,例如:网络协议.线程.数据库管理.网络操作.电子邮件等.

  2. Python 索引迭代

    1.使用enumerate函数 L = ['Adam', 'Lisa', 'Bart', 'Paul'] for index, name in enumerate(L):     print inde ...

  3. Android Monkey压力测试环境搭建及使用

    Android Monkey压力测试学习笔记 步骤:下载SDK -> 解压进入SDK Manager下载系统 -> 配置环境变量 -> 创建虚拟设备或连接真机 -> 进入命令模 ...

  4. Java 使用Jedis连接Redis数据库(-)

    redis 安装: Linux 安装redis 1)下载jar包: 使用Jedis需要以下两个jar包: jedis-2.8.0.jar commons-pool2-2.4.2.jar 2)测试red ...

  5. for循环输出9~0

    示例 for(var i = 9; i>-1;i--){ println(i) } function println(a) { document.write(a+"<br> ...

  6. RDLC 主从报表筛选

    今天继续学习RDLC报表的“参数传递”及“主从报表” 一.先创建DataSet,如下图: 二.创建一个报表rptDEPT.rdlc,显示部门T_DPET的数据 三.嵌入Default.aspx中,写在 ...

  7. Maven多模块项目

    1.项目结构-父项目 其中parent是父项目,这个父项目的父项目是springboot,我搭建这个多模块的项目的目的主要是为了研究学习springbatch 父项目的pom文件内容: <pro ...

  8. dict 知识汇总

    增: 1. copy 浅复制 2. setdefault (有就查询,没有就添加): 删: 3. clear:清除 4. pop :删除指定键值对 5. popitem :  随机删除一组键值对 改: ...

  9. 第一篇:fastadmin的页面是如何生成的?

    第一步: 访问URL http://www.fastadmin.cc/admin/mydir/test/index?addtabs=1 对应的方法是admin模块,controller文件夹下的myd ...

  10. HTML的lang属性的作用

    今天翻了一下<css权威指南>选择器章节,看到伪类选择器,也叫语言选择器:lang(language),顾名思义它会根据html设置的语言应用对应样式,如: *:lang(en){ col ...