Android IntentService源码分析

序言

最近有用到IntentService,对IntentService的源码做了一定的学习,所以今天来谈谈我对intentservice的理解,后面的内容我会从官方文档对intentservice的解释、我个人的理解、源码分析三个方面做讲解。

IntentService作为Service的子类,service作为android四大组件之一,它的作用自然不言而喻。所以讲IntentService之前肯定要先说说Service。

什么是Service

A Service is an application component representing either an application's desire to perform a longer-running operation while not interacting with the user or to supply functionality for other applications to use. Each service class must have a corresponding <service> declaration in its package's AndroidManifest.xml. Services can be started with Context.startService() and [Context.bindService()](https://developer.android.google.cn/reference/android/content/Context.html#bindService(android.content.Intent, android.content.ServiceConnection, int))
.
Note that services, like other application objects, run in the main thread of their hosting process. This means that, if your service is going to do any CPU intensive (such as MP3 playback) or blocking (such as networking) operations, it should spawn its own thread in which to do that work. More information on this can be found in Processes and Threads. The IntentService
class is available as a standard implementation of Service that has its own thread where it schedules its work to be done.

What is a Service?
Most confusion about the Service class actually revolves around what it is not:

A Service is not a separate process. The Service object itself does not imply it is running in its own process; unless otherwise specified, it runs in the same process as the application it is part of.
A Service is not a thread. It is not a means itself to do work off of the main thread (to avoid Application Not Responding errors).

这是官方文档对service的定义,下面是我对官方文档的翻译(英语水平有限,如有翻译错误,还请见谅)

service是app组件,它代表app希望执行较长的运行操作而不与用户交互或为其他应
用程序提供功能的愿望.service必须在AndroidManifest.xml中注册。启动service的方式有Context.startService()和Context.bindService()。

注意:
service是运行在app的主线程上的,这就意味着,如果你的service将执行任何cpu密集(如mp3播放)或阻止(如网络)操作,应该放到另外的线程上去完成这项工作。

什么是服务?
关于服务类的大多数混乱实际上都围绕着它不是什么

1.服务不是一个单独的进程,服务本身不意味着它在自己的进程中运行,除非有特殊的说明(可在注册的时候指定进程,它运行在app相同的进程中。
2.服务不是一个线程,这也不是意味着service的工作是在主线程上完成的(为了避免anr的发生);

我对service的理解

1.首先service是运行在主线程上的,所以 service上不能执行耗时操作,不然会导致anr。
2.同一个service在app中只有一个实例,类似单例。
3.什时候使用service呢?我的理解是,你需要做的工作要与可视界面(activity)解耦,也就是页面的隐藏或是关闭不会影响到你的工作,这样的工作你需要放到service中去,当然如果的耗时操作,你也是要在service中new thread的。
4.当然service还要一个很重要的作用就是作为两个不同app之间通讯的媒介,这个用到的是AIDL可以参考https://developer.android.google.cn/guide/components/aidl.html/

什么是IntentService

IntentService is a base class for Service that handle asynchronous requests (expressed as Intent) on demand. Clients send requests through 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.

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.

这是官方文档对service的定义,下面是我对官方文档的翻译(英语水平有限,如有翻译错误,还请见谅)

IntentService是Service的子类,按要求处理异步请求。通过startService发起异步任务,该服务根据需要启动,使用一个工作线程依次处理每个异步任务,并在完成所有异步任务后关闭service。

这种“工作队列处理器”模式通常用于从应用程序的主线程卸载任务,IntentService的存在就是方便开发者使用这种模式。使用方法:通过继承IntentService类并实现 onHandleIntent(Intent)方法,IntentService接受到Intent后,启动IntentService的HandlerThread处理异步任务,并且在适当的时候关闭自己。

所有请求都是在一个单独的线程上处理的--它们可能需要尽可能长的时间(并且不会阻塞应用程序的主循环),但每次只处理一个请求。

我的理解

1.IntentService用来处理异步任务
2.IntentService中只有一个默认的HandlerThread线程用来处理异步任务,所有异步任务按发起顺序以队列的形式依次在HanderThread中完成,并且在所有任务完成自后自行关闭自己。
3.正如官方的解释一样,IntentService适合一些卸载任务、下载更新包的任务并安装的任务。

源码分析IntentService

如何实现队列的形式依次处理异步任务

@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 thread = new HandlerThread("IntentService[" + mName + "]");
    thread.start();

    mServiceLooper = thread.getLooper();
    mServiceHandler = new ServiceHandler(mServiceLooper);
}

在IntentService创建的时候默认生成一个HandlerThread(IntentService中唯一的线程,并且已队列的形式依次处理耗时任务),并指定ServiceHandler

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);
    }
}

onHandleIntent(Intent intent)是抽象方法,通过实现onHandleIntent方法,将异步任务放在onHandleIntent中实现,ServiceHandler接收到消息后 ,依次调用onHandleIntent方法和stopSelf方法,stopSelf(msg.arg1)只有在Service没有其他任务时才会关闭自己。所以其实IntentService完成每个异步任务都会调用关闭自己的方法,只有在完成所有异步任务的时候才会正真的关闭自己。

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

/**
 * 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
 */
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
    onStart(intent, startId);
    return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}

每次startService()都会往HandlerThread的消息队列中发送一个消息mServiceHandler.sendMessage(msg)

来个例子

public class MyIntentService extends IntentService {

public static final  String  TAG = "MyIntentService";

public MyIntentService() {
    //调用父类的构造函数
    //构造函数参数=工作线程的名字
    super("MyIntentService");
}

/*复写onHandleIntent()方法*/
//实现耗时任务的操作
@Override
protected void onHandleIntent(Intent intent) {
    //根据Intent的不同进行不同的事务处理
    String taskName = intent.getExtras().getString("taskName");
    switch (taskName) {
        case "task1":
            Log.e(TAG, "do task1");
            Log.e(TAG, "onHandleIntent()所在线程:"+android.os.Process.myTid());
            break;
        case "task2":
            Log.e(TAG, "do task2");
            Log.e(TAG, "onHandleIntent()所在线程:"+android.os.Process.myTid());
            break;
        default:
            break;
    }
}


@Override
public void onCreate() {
    super.onCreate();
    Log.e(TAG, "onCreate");
}

/*复写onStartCommand()方法*/
//默认实现将请求的Intent添加到工作队列里
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Log.e(TAG, "onStartCommand");
    Log.e(TAG,"MyIntentService所在的线程是"+android.os.Process.myTid());
    return super.onStartCommand(intent, flags, startId);
}

@Override
public void onDestroy() {
    Log.e(TAG,"onDestroy");
    super.onDestroy();
}

}


public class MainActivity extends AppCompatActivity {
public static final String TAG = "MainActivity";

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    //同一服务只会开启一个工作线程
    //在onHandleIntent函数里依次处理intent请求。

    findViewById(R.id.test).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Log.e(TAG, "MainActivity所在的线程:"+android.os.Process.myTid());
            Intent i = new Intent(v.getContext(),MyIntentService.class);
            Bundle bundle = new Bundle();
            bundle.putString("taskName", "task1");
            i.putExtras(bundle);
            startService(i);

            Intent i2= new Intent(v.getContext(),MyIntentService.class);
            Bundle bundle2 = new Bundle();
            bundle2.putString("taskName", "task2");
            i2.putExtras(bundle2);
            startService(i2);
            startService(i);  //多次启动
        }
    });

}

}
Paste_Image.png

从打印的日志就能看出,主线程id是8211 IntentService的onStartCommand()也是在主线程8211中执行的,而两个task都是在另外的一个线程8928中执行的,而且在同一个线程中根据发起顺序依次执行。

到这里基本就结束了,希望我写的东西能对大家有所帮助,如有不足的地方希望大家多多指教。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容

  • 1.什么是Activity?问的不太多,说点有深度的 四大组件之一,一般的,一个用户交互界面对应一个activit...
    JoonyLee阅读 5,665评论 2 51
  • 本文出自 Eddy Wiki ,转载请注明出处:http://eddy.wiki/interview-androi...
    eddy_wiki阅读 3,179评论 0 20
  • 一、IntentService简介 IntentService是Service的子类,比普通的Service增加了...
    Ten_Minutes阅读 1,579评论 2 6
  • 开会,是个司空见惯却又让人疲惫不堪的事情。在过去的一年中经历了公司大大小小的各种会议。管理层周例会,部门例会,产品...
    知否读书阅读 592评论 1 7
  • 提起顾城,只是在高中时,语文老师本机介绍过两首他的诗 -黑夜给了我黑色的眼睛,我却用它寻找光明 -你一会儿看我,一...
    跳舞的小雪人阅读 348评论 0 0