Android IntentService详解(源码分析)

3,219 阅读7分钟

目录: 目录

1. 前言

本文是对 IntentService 的深入学习,包含其基本使用方法、IntentService更新处理UI工作,以及对 IntentService 的源码分析。

1.1 定义

IntentService 是 Service 的子类,继承于 Service 类,用于处理后台异步请求任务。

用户通过调用 Context.StartService(Intent) 发送请求,服务根据请求启动,使用工作线程依次处理每个 Intent任务请求,并在处理完所有任务请求后自身停止服务。

使用时,扩展 IntentService ,即实现它的子类并具体实现 onHandleIntent(android.content.Intent) 方法。IntentService 接收 Intent,启动工作线程,并在适当时机停止服务。

所有的请求都在同一个工作线程上处理,一次处理一个请求,所以处理完所以的请求可能会花费很长的时间,但由于 IntentService 是另外创建的线程来工作,所以保证不会阻止App 的主线程,防止 App 出现 ANR。

1.2 使用场景

用于处理后台长时间的耗时操作,如:下载文件、播放音乐...

2. 使用方法

由于 IntentService 是抽象类,所以在实际使用中,我们需要创建一个 IntentService 子类来具体实现。

使用步骤分为两步:

  1. 创建 IntentService 子类,并在清单文件中注册
  2. 在 Activity 中通过调用 startService(Intent) 方法发送任务请求。

例子: 我们模拟在后台进行下载以及读取文件的耗时操作,观察打印出来的 Log 信息。

如下所示:

MyIntentService处理耗时操作的日志信息 IntentService 的执行流程为:onCreate() -> onStartCommand() -> onStart() -> onHandleIntent -> ···处理完所有请求··· -> onDestroy()

具体代码如下所示:分 Java & Kotlin 两个版本展示。

2.1 Java版本

步骤一:创建 MyIntentService

public class MyIntentService extends IntentService {

    private static final String TAG = "MyIntentService";
    public static final String DOWNLOAD_ACTION = "DOWNLOAD_ACTION";
    public static final String READ_ACTION = "READ_ACTION";
    public static final String TEST_AUTHOR = "TEST_AUTHOR";

    public MyIntentService() {
        super("MyIntentService");
    }

    /**
     * 进行一些耗时操作
     * @param intent 通过startService(Intent intent)方法传入
     */
    @Override
    protected void onHandleIntent(Intent intent) {
        Log.e(TAG, "onHandleIntent: ");
        if (intent != null) {
            final String action = intent.getAction();
            String author = intent.getExtras().getString(TEST_AUTHOR);
            //模拟下载动作
            if (DOWNLOAD_ACTION.equals(action)) {
                for (int i = 0; i < 5; i++) {
                    try {
                        //线程等待1s,模拟耗时操作
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    Log.e(TAG, author + " " + action + " " + i);
                }
            }
            //模拟读操作
            if (READ_ACTION.equals(action)) {
                for (int i = 0; i < 5; i++) {
                    try {
                        //线程等待2s,模拟耗时操作
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    Log.e(TAG, author + " " + action + " " + i);
                }
            }
        }

    }

    @Override
    public void onCreate() {
        super.onCreate();
        Toast.makeText(this, "onCreate", Toast.LENGTH_SHORT).show();
        Log.e(TAG, "onCreate: ");
    }

    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        Toast.makeText(this, "onStartCommand", Toast.LENGTH_SHORT).show();
        Log.e(TAG, "onStartCommand: ");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        super.onStart(intent, startId);
        Log.e(TAG, "onStart: ");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e(TAG, "onDestroy: ");
        Toast.makeText(this, "onDestroy", Toast.LENGTH_SHORT).show();
    }
}

并记得在清单文件中注册,这一步你也可以通过 Android Studio 来帮我们完成

<service
    android:name=".intentservice.MyIntentService"
    android:exported="false">
</service>

步骤二:通过 startService(Intent) 发送任务请求

public class IntentServiceActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_intent_service);

        //模拟 Jere 做下载动作
        Intent intent = new Intent(this, MyIntentService.class);
        intent.setAction(MyIntentService.DOWNLOAD_ACTION);
        intent.putExtra(MyIntentService.TEST_AUTHOR, "Jere");
        startService(intent);

        //模拟 James 做读取动作
        Intent jamesIntent = new Intent(this, MyIntentService.class);
        jamesIntent.setAction(MyIntentService.READ_ACTION);
        jamesIntent.putExtra(MyIntentService.TEST_AUTHOR, "James");
        startService(jamesIntent);
    }
}

2.2 Koltin版本

步骤一:创建 MyIntentService

class MyIntentService : IntentService("MyIntentService") {
    companion object {
        private val TAG: String = "MyIntentService"
        val DOWNLOAD_ACTION = "DOWNLOAD_ACTION"
        val READ_ACTION = "READ_ACTION"
        val TEST_AUTHOR = "TEST_AUTHOR"
    }


    override fun onHandleIntent(intent: Intent?) {
        val action: String? = intent?.action
        val author: String? = intent?.extras?.getString(TEST_AUTHOR)

        when (intent?.action) {
            DOWNLOAD_ACTION -> {
                for (i in 0..10) {
                    Thread.sleep(1000)
                    Log.e(TAG, "$author $action $i")
                }
            }
            READ_ACTION -> {
                for (j in 0..10) {
                    Thread.sleep(2000)
                    Log.e(TAG, "$author $action $j")
                }
            }
        }
    }

    override fun onCreate() {
        super.onCreate()
        Log.e(TAG, "onCreate")
        Toast.makeText(this, "conCreate", Toast.LENGTH_SHORT).show()
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.e(TAG, "onStartCommand")
        Toast.makeText(this, "onStartCommand", Toast.LENGTH_SHORT).show()
        return super.onStartCommand(intent, flags, startId)
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.e(TAG, "onDestroy")
        Toast.makeText(this, "onDestroy", Toast.LENGTH_SHORT).show()
    }
}

在清单文件中注册:

<service
    android:name=".multiplethread.intentservice.MyIntentService"
    android:exported="false">
</service>

步骤二:通过 startService(Intent) 发送任务请求

class TestIntentServiceActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_test_intent_service)

        val intent = Intent()
        intent.setClass(this, MyIntentService::class.java)
        intent.action = MyIntentService.DOWNLOAD_ACTION
        intent.putExtra(MyIntentService.TEST_AUTHOR, "Jere")
        startService(intent)

        val jamesIntent = Intent()
        jamesIntent.setClass(this, MyIntentService::class.java)
        jamesIntent.action = MyIntentService.READ_ACTION
        jamesIntent.putExtra(MyIntentService.TEST_AUTHOR, "James")
        startService(jamesIntent)
    }
}

2.3 IntentService更新处理UI工作

思考: 在上面的例子中,我们是通过在打印出来的日志信息来观察后台异步任务的执行情况,那 IntentService 可以处理 UI 工作吗?

答: IntentService 是服务端,肯定是不可以处理 UI 工作的,但是他可以通过其对应的客户端,也就是 Activity 来更新处理 UI 工作。 客户端与服务端之间的通信 客户端与服务端之间的通信,我们使用 Messenger ,关于 Messenger 内容请看另一篇博客 Android Messenger初探,具体实现如下所示:

例子: 我们在 IntentService 模拟下载任务,通过 ProgressBar 显示下载进度。

效果图如下所示:

IntentService更新处理UI工作 具体代码如下所示:分Java & Kotlin 两个版本展示。

2.3.1 Java版本

  1. 在 Activity 中发送请求时,新建一个 Messenger 对象,传递给 IntentService。
public class IntentServiceActivity extends AppCompatActivity {
    private ProgressBar downloadProgressBar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_intent_service);

        downloadProgressBar = findViewById(R.id.download_progress_bar);

        //用于创建 Messenger,接收 IntentService 回复的消息
        MessengerHandler messengerHandler = new MessengerHandler(this);

        //模拟 Jere 做下载动作
        Intent intent = new Intent(this, MyIntentService.class);
        intent.setAction(MyIntentService.DOWNLOAD_ACTION);
        intent.putExtra(MyIntentService.TEST_AUTHOR, "Jere");
        //将 Messenger 传递给 IntentService,让其回复消息回来
        intent.putExtra(MyIntentService.TEST_MESSENGER, new Messenger(messengerHandler));
        startService(intent);
    }

    /**
     * 用于创建 Messenger 对象
     * 
     * 静态内部类,防止内存泄漏
     */
    public static class MessengerHandler extends Handler {
        private WeakReference<IntentServiceActivity> weakReference;

        MessengerHandler(IntentServiceActivity activity) {
            weakReference = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            //msg 为 IntentService 回复的消息,包含 Bundle 等信息。
            Bundle bundle = msg.getData();
            //获取 IntentService 传递过来的 下载进度 参数
            int downloadProgressBarValue = bundle.getInt(MyIntentService.DOWNLOAD_PROGRESS_VALUE_KEY);

            //将下载进度设置成 ProgressBar 的进度,显示出来。
            IntentServiceActivity activity = weakReference.get();
            if (activity != null && !activity.isFinishing()) {
                activity.downloadProgressBar.setProgress(downloadProgressBarValue);
            }

        }
    }

}
  1. 在 IntentService 中获取 Messenger 对象,并回复消息给 Activity。
public class MyIntentService extends IntentService {

    private static final String TAG = "MyIntentService";
    public static final String DOWNLOAD_ACTION = "DOWNLOAD_ACTION";
    public static final String READ_ACTION = "READ_ACTION";
    public static final String TEST_AUTHOR = "TEST_AUTHOR";
    public static final String TEST_MESSENGER = "TEST_MESSENGER";
    public static final String DOWNLOAD_PROGRESS_VALUE_KEY = "DOWNLOAD_PROGRESS_VALUE";

    public MyIntentService() {
        super("MyIntentService");
    }

    /**
     * 进行一些耗时操作
     * @param intent 通过startService(Intent intent)方法传入
     */
    @Override
    protected void onHandleIntent(Intent intent) {
        Log.e(TAG, "onHandleIntent: ");
        if (intent != null) {
            final String action = intent.getAction();
            String author = intent.getExtras().getString(TEST_AUTHOR);

            //模拟下载动作
            if (DOWNLOAD_ACTION.equals(action)) {
                for (int i = 0; i <= 6; i++) {
                    try {
                        //线程等待1s,模拟耗时操作
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    Log.e(TAG, author + " " + action + " " + i);
                    //获取从 Activity 传入的 Messenger
                    Messenger messenger = (Messenger) intent.getExtras().get(TEST_MESSENGER);
                    //新建消息,设置下载进度参数
                    Message msg = new Message();
                    Bundle bundle = new Bundle();
                    bundle.putInt(DOWNLOAD_PROGRESS_VALUE_KEY, i);
                    msg.setData(bundle);
                    try {
                        //回复消息
                        messenger.send(msg);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

    }

    @Override
    public void onCreate() {
        super.onCreate();
        Toast.makeText(this, "onCreate", Toast.LENGTH_SHORT).show();
        Log.e(TAG, "onCreate: ");
    }

    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        Toast.makeText(this, "onStartCommand", Toast.LENGTH_SHORT).show();
        Log.e(TAG, "onStartCommand: ");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        super.onStart(intent, startId);
        Toast.makeText(this, "onStart", Toast.LENGTH_SHORT).show();
        Log.e(TAG, "onStart: ");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e(TAG, "onDestroy: ");
        Toast.makeText(this, "onDestroy", Toast.LENGTH_SHORT).show();
    }

}

这样就实现了 IntentService 更新处理 UI 工作,具体效果看上图 IntentService更新处理UI工作

2.3.2 Kotlin版本

同样,Kotlin的实现方式为:

  1. 在 Activity 中发送请求时,新建一个 Messenger 对象,传递给 IntentService。
class TestIntentServiceActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_test_intent_service)

        val intent = Intent()
        intent.setClass(this, MyIntentService::class.java)
        intent.action = MyIntentService.DOWNLOAD_ACTION_KEY
        intent.putExtra(MyIntentService.TEST_AUTHOR_KEY, "Jere")
        //将 Messenger 传递给 IntentService, 使其传递消息回来,实现客户端与服务端之间进行沟通
        intent.putExtra(MyIntentService.TEST_MESSENGER_KEY, Messenger(MessengerHandler(this)))
        startService(intent)
    }

    /**
     * 此 Handler 用于创建 Messenger 对象,接收 IntentService 回复回来的消息
     *
     * 静态内部类,防止内存泄漏
     */
    class MessengerHandler(activity: TestIntentServiceActivity) : Handler() {
        var weakReference: WeakReference<TestIntentServiceActivity> = WeakReference(activity)

        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
            //msg 为 IntentService 回复的消息,包含 Bundle 等信息。
            val bundle: Bundle = msg.data
            //获取 IntentService 传递过来的 下载进度 参数
            val downloadProgressBarValue: Int =
                bundle.get(MyIntentService.DOWNLOAD_PROGRESS_BAR_VALUE_KEY) as Int
            val activity: TestIntentServiceActivity? = weakReference.get()
            //将下载进度设置成 ProgressBar 的进度,显示出来。
            if (activity != null && !activity.isFinishing) {
                activity.intentServiceDownloadProgressBar.progress = downloadProgressBarValue
            }
        }
    }
}
  1. 在 IntentService 中获取 Messenger 对象,并回复消息给 Activity。
class MyIntentService : IntentService("MyIntentService") {
    companion object {
        private val TAG: String = "MyIntentService"
        val DOWNLOAD_ACTION_KEY = "DOWNLOAD_ACTION"
        val READ_ACTION_KEY = "READ_ACTION"
        val TEST_AUTHOR_KEY = "TEST_AUTHOR"
        val TEST_MESSENGER_KEY = "TEST_MESSENGER"
        val DOWNLOAD_PROGRESS_BAR_VALUE_KEY = "DOWNLOAD_PROGRESS_BAR_VALUE"
    }

    override fun onHandleIntent(intent: Intent?) {
        val action: String? = intent?.action
        val author: String? = intent?.extras?.getString(TEST_AUTHOR_KEY)

        when (intent?.action) {
            DOWNLOAD_ACTION_KEY -> {
                for (i in 0..6) {
                    Thread.sleep(1000)
                    Log.e(TAG, "$author $action $i")
                    //获取从 Activity 中传入的 Messenger 对象
                    val messenger: Messenger = intent.extras?.get(TEST_MESSENGER_KEY) as Messenger
                    //新建一个 Message 对象
                    val msg: Message = Message()
                    //为 Message 对象设置 下载进度 参数
                    val bundle: Bundle = Bundle()
                    bundle.putInt(DOWNLOAD_PROGRESS_BAR_VALUE_KEY, i)
                    msg.data = bundle
                    //Messenger 回复消息给 Activity
                    messenger.send(msg)
                }
            }
            READ_ACTION_KEY -> {
                for (j in 0..10) {
                    Thread.sleep(2000)
                    Log.e(TAG, "$author $action $j")
                }
            }
        }
    }

    override fun onCreate() {
        super.onCreate()
        Log.e(TAG, "onCreate")
        Toast.makeText(this, "onCreate", Toast.LENGTH_SHORT).show()
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.e(TAG, "onStartCommand")
        Toast.makeText(this, "onStartCommand", Toast.LENGTH_SHORT).show()
        return super.onStartCommand(intent, flags, startId)
    }

    override fun onStart(intent: Intent?, startId: Int) {
        super.onStart(intent, startId)
        Log.e(TAG, "onStart")
        Toast.makeText(this, "onStart", Toast.LENGTH_SHORT).show()
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.e(TAG, "onDestroy")
        Toast.makeText(this, "onDestroy", Toast.LENGTH_SHORT).show()
    }
}

具体效果看上图 IntentService更新处理UI工作

3. 源码分析

上面介绍了 IntentService 的使用方法,接下来,我们来看一下 IntentService 的源码。如果你理解了 Hander、Looper、MessageQueue三者之间的关系,那么理解 IntentService 的源代码十分容易。(如果不理解,请看博客 Android Handler深入学习(源码分析)

根据上面的例子,我们知道了 IntentService 的执行顺序为:

onCreate() -> onStartCommand() -> onStart() -> onHandleIntent -> ···处理完所有请求··· -> onDestroy()

IntentService 源码如下:

public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;

    //创建一个内部类,继承于 Handler 类。
    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 构造函数,在你的子类构造函数中调用(在子类中通过 super(name) 方法调用)
     *
     * @param 参数 name 用于命名工作线程
     * 
     */
    public IntentService(String name) {
        super();
        mName = name;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        //创建一个 HandlerThread 对象
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        //启动 HandlerThread 线程
        thread.start();
        //获取 HandlerThread 中创建的 Looper 对象,赋值给全局变量 mServiceLooper。
        mServiceLooper = thread.getLooper();
        //创建一个 Handler 对象,并关联刚刚通过 HandlerThread 创建的 Looper 对象,即关联 mServiceLooper
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        //发送消息给 mServiceHanlder,
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

    /**
     * 你不应该在你的 IntentService 类中复写 onStartCommand() 方法。
     * 相反,你应该复写 onHandleIntent() 方法,该方法会在 IntentService 接收到启动请求时被调用。
     */
    @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() {
        //退出 Looper 消息循环
        mServiceLooper.quit();
    }

    /**
     * 除非是绑定服务,否则你不需要实现此方法,因为此方法的默认实现返回 null.
     */
    @Override
    @Nullable
    public IBinder onBind(Intent intent) {
        return null;
    }

    /**
     * 此方法在工作线程中被调用用于处理请求,一次只能处理一个请求,处理工作运行于独立于其他应用程序的工作线程中。
     * 因此,如果该段代码花费了很长的一段时间,它会阻塞同一个 IntentService 的其他请求,但不会阻塞其他任何东西。
     * 当所有的请求都被处理完,该 IntentService 就会停止运行,所以你不用调用 stopSelf() 方法。
     *
     * @param intent 通过 startService(Intent) 方法传入。如果服务在其进程消失后重新启动,则此值可能为空;
     */
    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
}

总结:

  1. 在 onCreate() 方法中,我们新建一个 HandlerThread 对象,然后启用它,获取其创建的 Looper 对象,然后新创建一个 Looper 对象关联该 Looper 对象。关于 HandlerThread 的知识点请看另一篇博客 Android HandlerThread详解(源码分析)
  2. 然后我们在 onStart() 方法中,通过 Message,将 Intent任务请求 发送给 mServiceHandler,Intent任务请求就是我们在 Activity 中通过 startService(Intent) 传入的 Intent。
  3. mServiceHanlder 接受到任务请求,调用 onHandleIntent() 方法处理任务请求,处理完所有请求后,调用 stopSelf() 结束 IntentService。

至此 IntentService 的源码也就分析结束了。 其实分享文章的最大目的正是等待着有人指出我的错误,如果你发现哪里有错误,请毫无保留的指出即可,虚心请教。

另外,如果你觉得文章不错,对你有所帮助,请给我点个赞,就当鼓励,谢谢~Peace~!