Blog-09-《Android悟道 IntentService》

Blog-09-《Android悟道 IntentService》

    • 1、定义
    • 2、IntentService特点
    • 3、IntentService使用场景
    • 4、实例(Kotlin)
    • 5、IntentService 源码解析

目录
Blog-09-《Android悟道 IntentService》_第1张图片

1、定义

提问:什么是 IntentService ?

官方文档:

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.
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是Service 的基类,可根据需要处理异步请求。客户端通过Context.startService(Intent)呼叫发送请求; IntentService根据需要启动,使用工作线程依次处理每个Intent,并在工作失败时自行停止。

这种“工作队列处理”模式通常用于减轻应用程序主线程的工作任务负担。要使用它,请扩展IntentService并实现onHandleIntent(Intent)。

IntentService将接收Intents,启动工作线程,并根据需要停止服务。

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

有三点需要强调:

1、IntentService 可处理异步请求。
2、IntentService中的工作线程会依次处理每个Intent,且每次只能处理一个请求。
3、需实现onHandleIntent(Intent)来接收Intent任务。

2、IntentService特点

(1)自带工作线程,可直接执行耗时操作,不会出现ANR问题。

(2)强制按顺序执行任务。

(3)完成任务后会自动关闭 IntentService(其实就是 Service)。

3、IntentService使用场景

IntentService 适用于处理短期的、轻量的耗时后台任务,以及需要按顺序执行的任务。举个例子,比如在后台上传几张图片后,就可以完成的任务,而且需要按顺序上传。再举个反例,像后台音乐播放这类型的,就不属于短期的、轻量型的后台任务,不适用于IntentService。

4、实例(Kotlin)

4.1 创建IntentService

4.1.1 Demo解读:

1、重写onHandleIntent(…)函数,再根据传来的Intent action来实现启动任务的处理。

2、对外部提供启动任务接口startActionFoo()、startActionBaz()。

3、创建handleActionFoo()、handleActionBaz()方法来处理任务。在两个方法中加入了30秒的延迟操作,用于测试IntentService的异步处理功能。

4.1.2 工作流程解读:

在外部调用startActionFoo()或startActionBaz()后,将启动MyIntentService然后会调用到onHandleIntent(…),onHandleIntent根据调用的时序,来决定优先处理哪个方法。在此实例中是先执行startActionFoo()然后再执行startActionBaz()。

private const val ACTION_FOO = "blog.intentservice.action.FOO"
private const val ACTION_BAZ = "blog.intentservice.action.BAZ"
private const val EXTRA_PARAM = "blog.intentservice.extra.PARAM"

private const val TAG = "MyIntentService"

class MyIntentService : IntentService("MyIntentService") {
    override fun onHandleIntent(intent: Intent?) {
        when (intent?.action) {
            ACTION_FOO -> {
                val param = intent.getStringExtra(EXTRA_PARAM)
                handleActionFoo(param)
            }
            ACTION_BAZ -> {
                val param = intent.getStringExtra(EXTRA_PARAM)
                handleActionBaz(param)
            }
        }
    }
    override fun onCreate() {
        super.onCreate()
        Log.i(TAG, "onCreate")
    }
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.i(TAG, "onStartCommand")
        return super.onStartCommand(intent, flags, startId)
    }
    override fun onStart(intent: Intent?, startId: Int) {
        Log.i(TAG, "onStart")
        super.onStart(intent, startId)
    }
    override fun onDestroy() {
        Log.i(TAG, "onDestroy")
        super.onDestroy()
    }
    /**
     * 处理Foo事件
     */
    private fun handleActionFoo(param: String) {
        Log.i(TAG, "handleActionFoo")
        Thread.sleep(30 * 1000) // 延迟执行30秒
        Log.i(TAG, "handleActionFoo end")
    }
    /**
     * 处理Baz事件
     */
    private fun handleActionBaz(param: String) {
        Log.i(TAG, "handleActionBaz")
        Thread.sleep(30 * 1000) // 延迟执行30秒
        Log.i(TAG, "handleActionBaz end")
    }
    companion object {
        var intent: Intent? = null
        /**
         * 对外部提供启动startActionFoo事件处理
         */
        @JvmStatic
        fun startActionFoo(context: Context, param: String) {
            intent = Intent(context, MyIntentService::class.java).apply {
                action = ACTION_FOO
                putExtra(EXTRA_PARAM, param)
            }
            context.startService(intent)
        }
        /**
         * 对外部提供启动startActionBaz事件处理
         */
        @JvmStatic
        fun startActionBaz(context: Context, param: String) {
            intent = Intent(context, MyIntentService::class.java).apply {
                action = ACTION_BAZ
                putExtra(EXTRA_PARAM, param)
            }
            context.startService(intent)
        }
        /**
         * 对外部提供stopService
         */
        fun stopSelfService(context: Context) {
            if (intent != null) {
                context.stopService(intent)
            }
        }
    }
}

最后,不要忘记在AndroidManifest.xml中注册IntentService


4.2 客户端启动IntentService

分别在客户端启动执行startActionFoo()、startActionBaz()任务。

    class TestIntentServiceActivity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_test_intent_service)
            MyIntentService.startActionFoo(this, "task_foo")
            MyIntentService.startActionBaz(this, "task_baz")
        }
    }

4.3 工作流程分析

4.3.1 IntentService生命周期

如图是Service的生命周期图。大家可能会有疑惑为什么不用IntentService生命周期图而使用Service生命周期图,原因很简单,因为IntentService继承自Service,所以它的生命周期与Service生命周期基本就是完全相同的。

但是,因为在IntentService中onBind()是默认返回null的,所以Call to bindService的内容就不需要去看了。对于IntentService来说,我们只关心Call to startService左边图这块的内容即可。

Blog-09-《Android悟道 IntentService》_第2张图片

4.3.2 实例工作流程

在此实例中分析一下完整的工作流程。

Blog-09-《Android悟道 IntentService》_第3张图片

日志打印顺序如下:

onCreate —> onStartCommand —> onStart —> handleActionFoo —> onStartCommand —> onStart —> handleActionBaz —> onDestroy

可以确定IntentService中按顺序执行任务的特性,以及拥有可以执行耗时任务的特性,若使用Service延迟执行20秒则可肯定会ANR。

4.4 特殊使用场景分析

若执行 handleActionFoo 任务未结束时强制stopservice,则当前IntentService会被销毁,但是其工作线程还是会继续执行。日志打印顺序:

onCreate —> onStartCommand —> onStart —> handleActionFoo —> onDestroy -> handleActionFoo end

若在执行 handleActionFoo 任务未结束时强制stopservice,然后又开始执行 handleActionBaz 任务,那么这两个任务则会在不同的工作线程中执行任务。但是由于在不同的工作线程,所以这里无法确定哪个任务先执行完成。

5、IntentService 源码解析

带着疑问看问题

(1)如何实现的工作线程?

(2)如何做到按顺序执行任务的?

(3)如何实现主动判断自动关闭 Service 的时间?

5.1 IntentService 源码

从IntentService源码中可以看到,IntentService其实就是继承自Service的,而且拥有着和Service一样的生命周期。比较特别的就是IntentService中自带着有一个工作线程,统一由onHandleIntent来做任务执行。

注意:在IntentService中是不推荐采用bindService()来启动IntentService的,因为在IntentService中onBind()是始终返回null的。

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) {
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }
    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     *
     * @param name Used to name the worker thread, important only for debugging.
     */
    public IntentService(String name) {
        super();
        mName = name;
    }
    /**
     * Sets intent redelivery preferences.  Usually called from the constructor
     * with your preferred semantics.
     *
     * 

If enabled is true, * {@link #onStartCommand(Intent, int, int)} will return * {@link Service#START_REDELIVER_INTENT}, so if this process dies before * {@link #onHandleIntent(Intent)} returns, the process will be restarted * and the intent redelivered. If multiple Intents have been sent, only * the most recent one is guaranteed to be redelivered. * *

If enabled is false (the default), * {@link #onStartCommand(Intent, int, int)} will return * {@link Service#START_NOT_STICKY}, and if the process dies, the Intent * dies along with it. */ 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 // 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); } @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; } @Override public void onDestroy() { mServiceLooper.quit(); } /** * Unless you provide binding for your service, you don't need to implement this * method, because the default implementation returns null. * @see android.app.Service#onBind */ @Override @Nullable public IBinder onBind(Intent intent) { return null; } /** * 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 {@link #stopSelf}. * * @param intent The value passed to {@link * android.content.Context#startService(Intent)}. * This may be null if the service is being restarted after * its process has gone away; see * {@link android.app.Service#onStartCommand} * for details. */ @WorkerThread protected abstract void onHandleIntent(@Nullable Intent intent); }

5.2 IntentService 如何实现的工作线程?

IntentService在onCreate()中会创建一个HandlerThread,整个工作线程就是由此处的HandlerThread来完成。

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

5.3 如何实现顺序执行任务?

在IntentService中包含了 ServiceHandler 的一个内部类,其实就是一个Handler。然后在onCreate中获取HandlerThread的Looper,与HandlerThread做了关联。在待执行的任务添加到IntentService中时就会调用都onStart(…)函数,此时就把任务消息发送到ServiceHandler中,形成一个消息队列,所以就保证了任务的执行均按照顺序进行。

@Override
public void onCreate() {
    super.onCreate();
    ...
    mServiceLooper = thread.getLooper();
    mServiceHandler = new ServiceHandler(mServiceLooper);
}
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);
    }
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
    Message msg = mServiceHandler.obtainMessage();
    msg.arg1 = startId;
    msg.obj = intent;
    mServiceHandler.sendMessage(msg);
}

5.4 如何判断自动关闭IntentService的时间?

在ServiceHandler中会在每次执行完成任务后都主动调用stopSelf(…)去尝试关闭IntentService,但是会判断当前消息队列是否为空才会真正的去执行关闭IntentService的后台服务,若队列中存在有消息,则会继续执行。

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

以上均为我对IntentService的见解,有任何理解不正确的地方,也请多多大家指正,一起学习,共同进步。感谢阅读,也希望这篇文章让你有所收获。

欢迎关注微信公众号:OAOAAce

坚持分享 Android高级进阶|Flutter|工作感悟 等有趣的原创文章
Blog-09-《Android悟道 IntentService》_第4张图片

你可能感兴趣的:(Android开发进阶)