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 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);  //多次启动
        }
    });

}

}
Android IntentService源码分析_第1张图片
Paste_Image.png

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

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

你可能感兴趣的:(Android IntentService源码分析)