一 IntentService介绍

IntentService定义的三个基本点:是什么?怎么用?如何work?

官方解释如下:

//IntentService定义的三个基本点:是什么?怎么用?如何work?*/

1、IntentService is a base class for Services that handle asynchronous requests (expressed as Intents) on demand.

2、Clients send requests through startService(Intent) calls;

3、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的好处,以及How to use IntentService*/

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.

总结IntentService的特点如下:

1、IntentService是Service类的子类,用来处理异步请求。

2、客户端可以通过startService(Intent)方法传递请求给IntentService

3、IntentService在onCreate()函数中通过HandlerThread单独开启一个线程来处理所有Intent请求对象(通过startService的方式发送过来的)所对应的任务,这样以免事务处理阻塞主线程。

4、执行完所一个Intent请求对象所对应的工作之后,如果没有新的Intent请求达到,则自动停止Service;否则执行下一个Intent请求所对应的任务。

5、IntentService在处理事务时,还是采用的Handler方式,创建一个名叫ServiceHandler的内部Handler,并把它直接绑定到HandlerThread所对应的子线程。 ServiceHandler把处理一个intent所对应的事务都封装到叫做onHandleIntent的虚函数;因此我们直接实现虚函数onHandleIntent,再在里面根据Intent的不同进行不同的事务处理就可以了。

IntentService最重要的一个方法onHandleIntent:

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().

1、该函数用于针对Intent的不同进行不同的事务处理.执行完所一个Intent请求对象所对应的工作之后,如果没有新的Intent请求达到,
则自动停止Service;否则ServiceHandler会取得下一个Intent请求传人该函数来处理其所对应的任务。

2、所有请求都在一个单线程中,不会阻塞应用程序的主线程(UI Thread),但是同一时间只处理一个请求。

IntentService的特点、优点、缺点?

IntentService的特点:

  1. IntentService是借助于消息队列实现的,所以任务的执行顺序就是一个queue的形式;
  2. 由于是单线程(一个工作线程),所以所有的任务需要排队执行;/
  3. 避免了我们再去创建线程和管理service的结束工作;

基于以上,IntentService与Service比较的好处有:

  • 第一,使用方便,代码简洁,不再需要我们自己像Service里面还要去手动创建线程;
  • 第二,当操作完成时,我们不用手动停止Service。

当然,IntentService的缺点也是显而易见:

由于是单个的worker thread,所以任务需要排队,不适合大多数的多任务情况;

二 DEMO实践

DEMO场景描述:

主要由MainActivity和MyIntentService构成,在MainActivity中启动服务,并传递两个参数a和b,在MyIntentService中获取参数,求和,并通过发送广播的形式向UI返回结果,然后MainActivity在接收到广播之后更新UI。

MainActivity.java主要做了三件事:

  1. 注册/注销广播接收器;
  2. 创建UI和更新UI;
  3. 传递参数,启动MyIntentService;

代码如下:

public class MainActivity extends AppCompatActivity {

    private LinearLayout ll_container;
private BroadcastReceiver forSumReceiver=new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.i("TAG","onReceive ()");
if(intent.getAction()==Constans.ACTION_RESULT){
int a=intent.getIntExtra(Constans.A,0);
int result=intent.getIntExtra(Constans.RESULT,0);
Log.i("TAG","onReceive --result:"+result);
handleResult(a,result);
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ll_container= (LinearLayout)findViewById(R.id.ll_container);
Log.i("TEST","MainActivity:"+android.os.Process.myTid());
registerBroadcast();
} private void handleResult(int a,int result){
TextView textView=(TextView)ll_container.findViewWithTag(a);
String old=textView.getText().toString();
String newText=old.replaceAll(" 正在计算中...",String.valueOf(result)+" 计算Success");
textView.setText(newText);
} private void registerBroadcast(){
IntentFilter intentFilter=new IntentFilter();
intentFilter.addAction(Constans.ACTION_RESULT);
registerReceiver(forSumReceiver,intentFilter);
} private int a=1;
public void addTask(View view){
int b=new Random().nextInt(101)+1;
MyIntentService.startMyIntentService(this,a,b);
TextView textView=new TextView(this);
textView.setText(a+"+"+b+"= "+ " 正在计算中...");
textView.setTag(a);
ll_container.addView(textView);
a++;
} @Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(forSumReceiver);
}
}

MyIntentService用于接收参数,并返回计算的结果:

public class MyIntentService extends IntentService {

    public MyIntentService() {
//必须实现父类的构造方法
super("MyIntentService");
} @Override
public void onCreate() {
Log.i("TEST","onCreate()");
super.onCreate();
} @Override
public void onStart(Intent intent, int startId) {
Log.i("TEST","onStart()");
super.onStart(intent, startId);
} @Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("TEST","onStartCommand()");
return super.onStartCommand(intent, flags, startId);
} @Override
public IBinder onBind(Intent intent) {
Log.i("TEST", "onBind()");
return super.onBind(intent);
} @Override
public void onDestroy() {
Log.i("TEST","onDestroy()");
super.onDestroy();
} @Override
protected void onHandleIntent(Intent intent) {
Log.i("TEST","onHandleIntent():"+android.os.Process.myTid());
if (intent!=null){
String action=intent.getAction();
if(Constans.ACTION_FOR_SUM.equals(action)){
int a=intent.getIntExtra(Constans.A,0);
int b=intent.getIntExtra(Constans.B,0);
int result=a+b;
Log.i("TEST","result: "+result);
handleResult(a,result);
}
}
} private void handleResult(int a,int result){
try{
//模拟计算耗时
Thread.sleep(3000);
Intent intent=new Intent(Constans.ACTION_RESULT);
intent.putExtra(Constans.RESULT,result);
intent.putExtra(Constans.A,a);
sendBroadcast(intent);
}catch (InterruptedException e){
e.printStackTrace();;
}
} public static void startMyIntentService(Context context,int a,int b){
Intent intent=new Intent(context,MyIntentService.class);
intent.setAction(Constans.ACTION_FOR_SUM);
intent.putExtra(Constans.A,a);
intent.putExtra(Constans.B,b);
context.startService(intent);
} }

IntentService生命周期方法执行顺序如下:

07-08 10:18:51.579 com.troy.intentservicedemo I/TEST: MainActivity:30060
07-08 10:19:26.009 com.troy.intentservicedemo I/TEST: onCreate()
07-08 10:19:26.009 com.troy.intentservicedemo I/TEST: onStartCommand()
07-08 10:19:26.009 com.troy.intentservicedemo I/TEST: onStart()
07-08 10:19:26.039 com.troy.intentservicedemo I/TEST: onHandleIntent():30223
07-08 10:19:26.039 com.troy.intentservicedemo I/TEST: result: 23
07-08 10:19:29.100 com.troy.intentservicedemo I/TEST: onDestroy()
07-08 10:19:31.839 com.troy.intentservicedemo I/TEST: onCreate()
07-08 10:19:31.849 com.troy.intentservicedemo I/TEST: onStartCommand()
07-08 10:19:31.849 com.troy.intentservicedemo I/TEST: onStart()
07-08 10:19:31.869 com.troy.intentservicedemo I/TEST: onHandleIntent():30305
07-08 10:19:31.869 com.troy.intentservicedemo I/TEST: result: 52
07-08 10:19:34.899 com.troy.intentservicedemo I/TEST: onDestroy()

从上面Log中可以看出:

  1. UI线程的线程ID=30060,而onHandleIntent()的线程ID分别是30223,30305;由此知道onHandleIntent()方法是执行在工作线程中的;
  2. 并且IntentService的所有请求如果是在一个生命周期中完成的话,则所有请求是在一个工作线程中顺序执行的。否则,是在不同的工作线程中完成。
  3. 同时验证了:执行完所一个Intent请求对象所对应的工作之后,如果没有新的Intent请求达到,则自动停止Service;

运行效果如图:

三 IntentService源码简单解析

IntentService是Service的子类,拥有Service的所有生命周期方法;同时还有自己的ServiceHandler;

ServiceHandler相关源码如下:

public abstract class IntentService extends Service {
private volatile Looper mServiceLooper;//volatile关键字保证同步,保证可见性
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) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
} public IntentService(String name) {//构造方法
super();
mName = name;
}

IntentService 实际上是Looper,Handler,Service 的集合体,他不仅有服务的功能,还有处理和循环消息的功能。

onCreate()的源码:创建了一个HandlerThread

@Override
public void onCreate() {
super.onCreate(); HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start(); mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}

IntentService创建时就会创建Handler线程(HandlerThread)并且启动,然后再得到当前线程的Looper对象来初始化IntentService的mServiceLooper,接着创建mServicehandler对象。

下面是onStart()的源码: 调用mServiceHandler

@Override
public void onStart(Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent; mServiceHandler.sendMessage(msg);
}

当你启动IntentService的时候,就会产生一条附带startId和Intent的 Message并发送到MessageQueue中,接下来Looper发现MessageQueue中有Message的时候,就会停止Handler 处理消息。

handleMessage处理的代码如下:

@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}

接着调用 onHandleIntent((Intent)msg.obj),这是一个抽象的方法,其实就是我们要重写实现的方法,我们可以在这个方法里面处理我们的工作.当任务完成时就会调用stopSelf(msg.arg1)这个方法来结束指定的工作。

stopSelf(msg.arg1):

注意下:回调完成后回调用 stopSelf(msg.arg1),注意这个msg.arg1是个int值,相当于一个请求的唯一标识。每发送一个请求,会生成一个唯一的标识,然后将请求放入队列,当全部执行完成(最后一个请求也就相当于getLastStartId == startId),或者当前发送的标识是最近发出的那一个(getLastStartId == startId),则会销毁我们的Service.如果传入的是-1则直接销毁。

当所有的工作执行完后:就会执行onDestroy方法。

onDestroy源码如下:

@Override
public void onDestroy() {
mServiceLooper.quit();
}

服务结束后调用这个方法 mServiceLooper.quit()使looper停下来。

最后总结一下上述的分析:

1、 IntentService是一个基于消息的服务,每次启动该服务并不是马上处理你的工作,而是首先会创建对应的Looper,Handler并且在MessageQueue中添 加的附带客户Intent的Message对象, 当Looper发现有Message的时候接着得到Intent对象通过在 onHandleIntent((Intent)msg.obj)中调用你的处理程序. 处理完后即会停止自己的服务. 意思是Intent的生命周期跟你的 处理的任务是一致的. 所以这个类用下载任务中非常好,下载任务结束后服务自身就会结束退出。

2、IntentService是不适合用于bindService()这样的启动方式的。其次我们通过startService多次启动Service时,相当于在MessageQueue中添加了多个任务,就可以实现多任务按照顺序执行。

3、只有在onHandleIntent()方法中执行的代码才是在工作线程中运行的。IntentService的停止不是因为在handleMessage() 中执行了stopSelf(msg.arg1);而是系统自己停止的。

【转载】Android IntentService使用全面介绍及源码解析的更多相关文章

  1. Android IntentService使用介绍以及源码解析

    版权声明:本文出自汪磊的博客,转载请务必注明出处. 一.IntentService概述及使用举例 IntentService内部实现机制用到了HandlerThread,如果对HandlerThrea ...

  2. 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新

    [原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...

  3. 【原】Android热更新开源项目Tinker源码解析系列之二:资源文件热更新

    上一篇文章介绍了Dex文件的热更新流程,本文将会分析Tinker中对资源文件的热更新流程. 同Dex,资源文件的热更新同样包括三个部分:资源补丁生成,资源补丁合成及资源补丁加载. 本系列将从以下三个方 ...

  4. 【原】Android热更新开源项目Tinker源码解析系列之三:so热更新

    本系列将从以下三个方面对Tinker进行源码解析: Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Android热更新开源项目Tinker源码解析系列之二:资源文件热更新 A ...

  5. Android进阶:五、RxJava2源码解析 2

    上一篇文章Android进阶:四.RxJava2 源码解析 1里我们讲到Rxjava2 从创建一个事件到事件被观察的过程原理,这篇文章我们讲Rxjava2中链式调用的原理.本文不讲用法,仍然需要读者熟 ...

  6. IPerf——网络测试工具介绍与源码解析(4)

    上篇随笔讲到了TCP模式下的客户端,接下来会讲一下TCP模式普通场景下的服务端,说普通场景则是暂时不考虑双向测试的可能,毕竟了解一项东西还是先从简单的情况下入手会快些. 对于服务端,并不是我们认为的直 ...

  7. 【Android编程】android平台的MITM瑞士军刀_cSploit源码解析及中间人攻击复现

    /文章作者:Kali_MG1937 作者博客ID:ALDYS4 QQ:3496925334 未经允许,禁止转载/ 何为MITM欺骗,顾名思义,中间人攻击的含义即为在局域网中充当数据包交换中间人的角色 ...

  8. Android开发——AsyncTask的使用以及源码解析

    .AsyncTask使用介绍  转载请标明出处:http://blog.csdn.net/seu_calvin/article/details/52172248 AsyncTask封装了Thread和 ...

  9. IPerf——网络测试工具介绍与源码解析(1)

    IPerf是一个开源的测试网络宽带并能统计并报告延迟抖动.数据包丢失率信息的控制台命令程序,通过参数选项可以方便地看出,通过设置不同的选项值对网络带宽的影响,对于学习网络编程还是有一定的借鉴意义,至少 ...

随机推荐

  1. 使用GDAL/OGR读写矢量文件

    感觉GIS中矢量相关内容还是挺庞杂的,并且由于版本迭代的关系,使用GDAL/OGR读写矢量的资料也有点不太一样.这里总结了一个读写矢量的示例,实现代码如下: #include <iostream ...

  2. Android 开发凉了吗!

    昨天我拿了本<安卓开发大全>的书,把它放进了冰箱,你猜怎么样? 它凉了. 记得2013年的时候,安卓崛起,一夜之间遍地谈论安卓这个奇怪的机器人. 安卓受宠的原因,主要围绕着: 1 应用商城 ...

  3. DG中switchover切换操作

    问题描述:我们配置DG的目的就是为了在主库出现故障时,备库能够提供服务,保证业务的正常运行,switchover是用户有计划的进行停机切换,能够保证不丢失数据,我记录一下我进行switchover中的 ...

  4. STM32的Keil找不到想要flash的解决方法

    STM32的Keil找不到想要flash的解决方法:https://blog.csdn.net/qq_38376586/article/details/79582020

  5. Leetcode823 : 因子二叉树问题

    问题描述 给定一个数组,数组中的数不重复,且均大于1.要求使用数组中的数构建二叉树,每个数字可以被重复使用,除了叶子节点,每个节点的值等于其子节点的乘积,求构建二叉树的数量,返回的结果mod 10** ...

  6. Linux ipv6 无状态 设置为 eui64

    Linux ipv6 无状态 设置为 eui64 转载注明来源: 本文链接 来自osnosn的博客,写于 2019-08-22. 无状态的ipv6有eui64和stable-privacy模式, 在家 ...

  7. 牛客国庆训练 H.千万别用树套树

    链接https://ac.nowcoder.com/acm/contest/1108/H 国庆队内训练的题,当时还完全没思路,就没补.现在会树状数组了,倒是能想一想,不过网上题解好多用线段树传数组的? ...

  8. oc:定时删除ES日志数据释放空间

    修改方法: 1.直接编辑修改 查看当前logging-curator配置,了解当前定时删除大的策略. oc edit configmap/logging-curator 打开后,可以直接编辑保存. 2 ...

  9. Exceptionless应用--自定义插件

    遇到的问题/需求 这里会把一些敏感的参数记录下来,我们需要屏蔽掉,如图 我们希望日志里面有当前登录用户的信息,如图: 处理方法 tip:这里用的是.net非.net core 第一个问题(屏蔽敏感参数 ...

  10. 五种团队的组织方式落地 DevOps

    原文链接:https://blog.matthewskelton.net/2013/10/22/what-team-structure-is-right-for-devops-to-flourish/ ...