尊重原创,转载请注明出处:http://blog.csdn.net/a740169405/article/details/50274537

前言:

   大家都知道,Service是Android中运行在后台的,不可见的服务。但其和异步线程又不一样,因为Service是运行在主线程,也就是UI线程里。所以我们并不能在Service中执行耗时的操作。为了解决这个问题,大家可能回在Service中开启一个线程,来执行耗时操作。谷歌为我们实现了类似的功能,那就是IntentService。

IntentService介绍:

  1. IntentService是继承了Service的抽象类,它是个服务,要使用它必须继承IntentService并实现抽象方法。
  2. IntentService可以执行后台耗时任务,当任务都执行完成后,IntentService会自动结束,不需要手动调用stopSelf();
  3. IntentService的优先级比线程高,因为是跑在后台的服务,所以不容易被系统杀死。
  4. IntentService内部是通过封装HandlerIntent来执行后台任务的,因为是通过消息队列接受Intent,所以后台任务是串行执行。

IntentService使用:

新建一个Activity与一个IntentService,从Activity中发送三个Intent,也就是启动执行startService三次,每次intent里的数据都不一样。

IntentServiceActivity:

public class IntentServiceActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);Intent intent = new Intent(this, MyIntentService.class);intent.putExtra(MyIntentService.EXTRA_KEY, "my task one #");// 发送任务1startService(intent);intent.putExtra(MyIntentService.EXTRA_KEY, "my task two #");// 发送任务2startService(intent);intent.putExtra(MyIntentService.EXTRA_KEY, "my task three #");// 发送任务3startService(intent);}
}

Activity中仅仅是向IntentService发送了三个消息

继承IntentService,并实现onHandleIntent方法。
MyIntentService:

public class MyIntentService extends IntentService {public static final String TAG = MyIntentService.class.getSimpleName();public static final String EXTRA_KEY = "task_name";public MyIntentService() {super(TAG);}@Overrideprotected void onHandleIntent(Intent intent) {String value = intent.getStringExtra(EXTRA_KEY);if (value.contains("two")) {try {// 如果是第二个任务,则sleep三秒。// 发现第三个任务仍然在第二个任务之后执行,说明任务是串行执行的Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}}Log.e(TAG, "thread : " + Thread.currentThread().getName() + "  " + value);}@Overridepublic void onDestroy() {super.onDestroy();// 服务结束时,打印LogLog.e(TAG, "onDestroy");}
}

在MyIntentService的onHandleIntent方法里,打印出了intent的key为task_name对应的值。
当收到含有”two”字符(这里我为了方便这样做的),线程Sleep三秒。在每次onHandleIntent被调用的时候,
打印出当前线程的名字。

看看Log信息:

chen.com.incubationcenter E/MyIntentService: thread : IntentService[MyIntentService] my task one #
chen.com.incubationcenter E/MyIntentService: thread : IntentService[MyIntentService] my task two #
chen.com.incubationcenter E/MyIntentService: thread : IntentService[MyIntentService] my task three #
chen.com.incubationcenter E/MyIntentService: onDestroy

我们会发现,处理任务的线程并不是主线程,并且即使第二个任务sleep了三秒,但是第三个任务还是在第二个任务完成后才执行,说明任务的处理是串行的。

IntentService源码解析:

下面我们分析三个问题:
1. IntentService是怎么实现异步线程执行任务的。
2. 为什么任务是串行执行的。
3. 每次startService并没有重新启动服务,并且,在所以任务完成后,服务自动终止。

带着问题分析源码:

IntentService内部是通过HandlerThread实现异步执行任务的。
想了解HandlerThread的相关知识的同学,可以查阅博客:HandlerThread源码解析
HandlerThread是一个内部维护了一个消息队列的线程。任务就是通过它来执行的。
我们找到IntentService类的onCreate方法:

@Override
public void onCreate() {// TODO: It would be nice to have an option to hold a partial wakelock// during processing, and to have a static startService(Context, Intent)// method that would launch the service & hand off a wakelock.super.onCreate();// 创建一个HandlerThread类并传入工作线程的名字HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");// 开启后台线程thread.start();// 获取后台线程的LoopermServiceLooper = thread.getLooper();// 创建一个ServiceHandler对象,用来处理消息。mServiceHandler = new ServiceHandler(mServiceLooper);
}

在onCreate方法里,首先新建了一个HandlerThread对象,接着创建了一个叫ServiceHandler的类,
该类是IntentService的内部类,该类继承了Handler,构造函数传入了HandlerThread对象的Looper。
看看他的定义:

private final class ServiceHandler extends Handler {public ServiceHandler(Looper looper) {super(looper);}@Overridepublic void handleMessage(Message msg) {// 收到消息的时候,回调了onHandleIntent方法,并把Intent对象传递过去onHandleIntent((Intent)msg.obj);// 完成任务后,调用stopSelf方法,并传入消息的索引stopSelf(msg.arg1);}
}

我们发现,onHandleIntent方法就是在ServiceHandler收到消息后回调的。
也就是说onHandleIntent方法是在HandlerThread线程中执行的,这也就是疑惑一中为什么任务在异步线程中执行。

此外,因为任务是通过ServiceHandler发送给异步线程的消息队列的,而消息队列里的消息是依次取出并执行的,这也就是疑惑二中为什么任务是串行执行的。

最后,我们发现,onHandleIntent执行完成以后,IntentService并没有马上执行onstopSerlf方法,而是执行了其另一个带参数的方法。
该方法是定义在Service类下的。

/*** Old version of {@link #stopSelfResult} that doesn't return a result.** @see #stopSelfResult*/
public final void stopSelf(int startId) {if (mActivityManager == null) {return;}try {mActivityManager.stopServiceToken(new ComponentName(this, mClassName), mToken, startId);} catch (RemoteException ex) {}
}

该方法调用了ActivityManager的stopServiceToken方法来停止当前服务,关于ActivityManager这里不做介绍,有兴趣的朋友可以google相关资料。
既然调用了结束服务的方法,为什么服务没有马上结束,而是继续完成剩下的任务后才结束呢。
我们发现这里有个startId,没错就是因为他。startId是在Service类的onStart方法里被初始化的,
而onStart方法又是在onStartCommand方法里被调用的。
我们看看IntentService的源码:
onStartCommand:

   /*** You should not override this method for your IntentService. Instead,* override {@link #onHandleIntent}, which the system calls when the IntentService* receives a start request.* @see android.app.Service#onStartCommand*/@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {// 直接调用onStart方法onStart(intent, startId);return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;}

onStart:

    @Overridepublic void onStart(Intent intent, int startId) {// 构建了一个消息Message msg = mServiceHandler.obtainMessage();// 将startId作为参数传递msg.arg1 = startId;// 需要处理的msg.obj = intent;// 调用ServiceHandler发送消息mServiceHandler.sendMessage(msg);}

我们知道,每次调用startServcie方法启动一个服务,都会调用一次onStartCommand,每次也都会带一个startId过去,那么执行多个任务就有多个startId(关于这里不做介绍,大家可以理解为服务启动的顺序ID)
那么在onHandleIntent执行后调用的stopSelf方法确实告诉系统需要停止服务,但是系统会根据startId来判断是否所有的任务都已完成,如果是,则停止服务,如果不是,则等待所有的任务都完成后停止服务。到此,疑惑三搞定。

**

总结:

**
1. IntentService内部通过HandlerThread线程来执行异步任务,我们不需要自己创建线程
2. IntentService默认实现了onStartCommand以及onStart方法来讲intent传递给HandlerThread的消息队列
3. IntentService默认实现了onBind方法,并返回了空。
4. 无需手动调用stopSelf方法,所有任务都执行完成后,IntentService会自动结束服务。

感谢:
Android Service总结05 之IntentService
Android基本功:IntentService的使用
《Android开发艺术探秘》

Android IntentService的使用与源码解析相关推荐

  1. Android多线程之ArrayBlockingQueue源码解析

    阻塞队列系列 Android多线程之LinkedBlockingQueue源码解析 Android多线程之SynchronousQueue源码解析 Andorid多线程之DelayQueue源码分析 ...

  2. Android Glide 3.7.0 源码解析(八) , RecyclableBufferedInputStream 的 mark/reset 实现

    个人博客传送门 一.mark / reset 的作用 Android Glide 3.7.0 源码解析(七) , 细说图形变换和解码有提到过RecyclableBufferedInputStream ...

  3. 【Android应用开发】EasyDialog 源码解析

    示例源码下载 : http://download.csdn.net/detail/han1202012/9115227 EasyDialog 简介 : -- 作用 : 用于在界面进行一些介绍, 说明; ...

  4. 【Android 控件使用及源码解析】 GridView规则显示图片仿微信朋友圈发图片

    今天闲下来想用心写一点东西,发现没什么可写的,就写一下最近项目上用到的一些东西吧.最近项目要求上传多图并且多图显示,而且要规则的显示,就像微信朋友圈的图片显示一样. 想了一下用GridView再适合不 ...

  5. Android 网络框架之Retrofit源码解析,flutter边框特效

    Retrofit的构建使用了建造者模式,这个模式的优点就是可以构造复杂的对象,方便扩展,并且看起来代码比较简洁,美观: 在开始之前,我们先来看一下Retrofit的成员变量: 这里的变量并不是很多,我 ...

  6. Android 网络编程之OkHttp源码解析

    前言:OkHttp框架是Android的网络请求框架,无数的项目都在使用着这个框架,重要性不言而喻; 本文会将OKHTTP的源码进行拆解,每个部分来单独学习,由简入深,循序渐进,篇幅较长,建议收藏,慢 ...

  7. android输入法01:SoftKeyboard源码解析01

      本文主要介绍android自带输入法实例SoftKeyboard的源码,共分为两篇:第一篇为SoftKeyboard框架概述,第二篇为源码注释. 1.IMF简介 一个IMF结构中包含三个主要的部分 ...

  8. 覆盖式理解Android 消息处理机制(带源码解析)

    转载自:https://www.jianshu.com/p/02962454adf7 Android 消息处理机制估计都被写烂了,但是依然还是要写一下,因为Android应用程序是通过消息来驱动的,A ...

  9. Android FM模块学习之四源码解析(一)

    前一章我们了解了FM手动调频,接下来我们要分析FM模块用到的源码.此源码是基于高通平台的,别的平台都大同小异,只不过是平台自己作了些小改动而已. 首先要看的当然是主activity, FMRadio. ...

  10. Android FM 模块学习之四 源码解析(1)

    前一章我们了解了FM手动调频,接下来我们要分析FM模块用到的源码.此源码是基于高通平台的,别的平台都大同小异,只不过是平台自己作了些小改动而已. 首先要看的当然是主activity, FMRadio. ...

最新文章

  1. 关于学习Python的一点学习总结(28->收集参数及分配参数)
  2. FPGA 核和FPGA Fabric的区别是什么?
  3. 【五线谱】五线谱的常用符号 ( 花连谱号 | 高音谱号 | 低音谱号 | 休止符 | 小节线 )
  4. 面试官:你对Kafka比较熟? 那说说kafka日志段如何读写的吧?
  5. 【HTML】------HTML的标签
  6. 服务器后端 项目代码常用目录图
  7. linux 多线程并行计算,浅谈.NET下的多线程和并行计算(五)线程池基础上
  8. 【三维路径规划】基于matlab麻雀算法求解无人机三维航迹优化问题【含Matlab源码 301期】
  9. Linux中文件权限(有图详细讲解)
  10. 人脸重建速览,从3DMM到表情驱动动画
  11. 集成电路哪家强?现在就为你透彻分析!
  12. java 解析GZIP 和 Deflate 网页源文件
  13. 传奇开区发布广告和选择广告投放网站的那些事
  14. 终端/SSH/Telnet ConnectBot v1.7.1中文版
  15. 我们的管理:创新产品研发管理
  16. 好用的可视化报表在线生成工具
  17. java实现鸭子类型_面向对象—多态、鸭子类型(Day21)
  18. 中国车用尿素市场规模调研与发展趋势分析报告2022-2028年
  19. TP6安装使用easywechat
  20. 连接型CRM助力医疗企业把“成本中心”变成“利润中心”

热门文章

  1. LocED-Location-aware Energy Disggregation Framework
  2. 【CF 706】(C.Hard problem) + (D.Vasiliy's Multiset) + (E.Working routine)【最短路、01字典树、十字链表模拟】
  3. TCP和TCP/IP的区别
  4. matlab 天线设计 泰勒加权_波导缝隙天线的设计仿真方案详细教程
  5. 编译时:virtual memory exhausted: Cannot allocate memory
  6. FreeBSD下使用Blogbio写cnblogs博客
  7. ionic cordova 热更新
  8. ubuntu 下 4412烧写SuperBoot
  9. 微星P55-主板是怎样造出来的
  10. win7 IIS7 发布网站遇到 HTTP 错误 500.19 由于权限不足