美文网首页
Android多线程:IntentService

Android多线程:IntentService

作者: 南子李 | 来源:发表于2019-07-12 14:30 被阅读0次

前言

Service作为Android的四大组件之一,实现了在后台执行不需要界面的任务,例如音乐的播放等,若要实现一个在后台执行的耗时任务呢?例如离线下载。于是,Android 实现了 IntentService ,通过开启一个子线程在后台执行耗时任务。

1. 使用步骤

1)新建IntentService子类MyIntentService
2)重写 onHandleIntent() 方法,根据传入的不同Intent处理不同的任务
3)在 AndroidManifest.xml 中注册服务MyIntentService
4)需要发起任务请求的地方,新建 Intent 对象,使用 Bundle 保存传入的数据
5)调用 ActivitystartService(intent) 发起一次任务请求

MyIntentService.java

//step1: 新建IntentService子类MyIntentService
public class MyIntentService extends IntentService {

    private String taskName;

    //step2: 调用父类构造方法,传入线程名字
    public MyIntentService() {
        super("myIntentService");
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i("myIntentService----", "onCreate");
    }

    //默认实现,将传入的Intent请求依次加入到消息队列中
    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        Log.i("myIntentService----", "onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i("myIntentService----", "onDestroy");
    }
}

AndroidManifest.xml

<service
       android:name=".MyIntentService"
       tools:ignore="ExportedService"/>

发起任务请求的方法:

    //在需要发起任务请求的地方调用此方法
    private void startIntentService() {

        //step5: 新建Intent传入任务处理请求,指向MyIntentService
        Intent task1 = new Intent(MainActivity.this, MyIntentService.class);
        Bundle bundle1 = new Bundle();
        bundle1.putString("taskName", "task1");
        task1.putExtras(bundle1);

        Intent task2 = new Intent(MainActivity.this, MyIntentService.class);
        Bundle bundle2 = new Bundle();
        bundle2.putString("taskName", "task2");
        task2.putExtras(bundle2);

        //step6: 启动服务,发起一次任务请求 (多次调用startService(intent) 则发起多次请求)
        startService(task1);
        startService(task2);
        //再次发起task1的任务请求
        startService(task1);
    }
  • bug记录
    在AndoridManifest.xml文件中注册MyIntentService时,为Service添加了属性name,并在代码中用包名的方式调用报错:
    Service Intent must be explicit: Intent
    原因:Android 5.0(Lollipop) 之后规定不能以包名的方式定义Service的Intent,而应用显示声明 new Intent(xxxActivity.this, xxxService.class);

2. 源码分析

1)IntentService的初始化

    //step2: 调用父类构造方法,传入线程名字
    public MyIntentService() {
        super("myIntentService");
    }

    //调用了此构造方法,赋值线程名称mName
    public IntentService(String name) {
        super();
        mName = name;
    }
    
    //IntentService的onCreate()中
    @Override
    public void onCreate() {
        super.onCreate();
        //下面即是开启一个HandlerThread线程的常用步骤,详细可参考之前文章中有关HandlerThread的解析
        //创建一个新线程HandlerThread对象,传入线程名称mName
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        //启动线程
        thread.start();
        //获取HandlerThread自动创建的Looper
        mServiceLooper = thread.getLooper();
        //关联HandlerThread和IntentService的Handler对象
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

2)onHandleIntent(Intent intent)

    //step3: 重写onHandleIntent,判断Intent类型,依次处理不同的任务
    @Override
    protected void onHandleIntent(Intent intent) {
        ...
    }

    //IntentService中,创建ServiceHandler类,继承自Hanlder
    private final class ServiceHandler extends Handler {
        //构造方法,传入ServiceHandler所在线程的Looper对象
        public ServiceHandler(Looper looper) {
            super(looper);
        }
        //复写handleMessage,处理任务请求
        @Override
        public void handleMessage(Message msg) {
            //进入子类MyIntentService中重写的构造方法onHandleIntent(intent),这个方法的执行是在工作线程中
            onHandleIntent((Intent)msg.obj);
            //调用Service的stopSelf() 自动停止开启的IntentService服务,标记为startId
            stopSelf(msg.arg1);
        }
    }
    //子类必须实现的构造方法,工作线程执行,根据Intent
    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);

3)onStartCommand(intent, flags, startId)

    //将传入的Intent请求依次加入到消息队列中
    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }
    
    //IntentService中onStartCommand
    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        //调用onStart(),传入发起的任务请求Intent和该次请求的id值startId
        onStart(intent, startId);//——→跳转至onStart()方法
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

    //onStart()方法
    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        //新建Message对象,存储intent和startId
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        //ServiceHandler发送这条消息Message到消息队列MessageQueue中,等待对应的Handler处理消息任务
        mServiceHandler.sendMessage(msg);
    }

4)onDestroy()

    @Override
    public void onDestroy() {
        //IntentService结束即MessageQueue内部的Message处理完成时,
        //自动退出当前线程所持有的Looper,Looper自动退出所持有的MessageQueue
        mServiceLooper.quit();
    }

5)onBind(intent)

    @Override
    @Nullable
    public IBinder onBind(Intent intent) {
        return null;
        //返回为空
    }

启动IntentService时,若采用bindService()方式启动,IntentService的生命周期为:
onCrtate() → onBind() → onUnBind() → onDestroy()
若采用startService()方式启动,IntentService的生命周期为:
onCrtate() → onStartCommand() → onDestroy()
从生命周期来看,bindService()不会执行onStartCommand(),而IntentService在onStartCommand()方法中实现消息的创建和发送入队操作,所以IntentService只能通过startService()的方式启动才能实现HandlerThread线程发送消息并在后台Service中执行耗时任务。

3. 应用场景

线程任务需要 按序 且在 后台执行 ,所有任务都在同一个Thread的Looper中执行

  • 离线下载

思考

  • 为什么IntentService是 Service + HandlerThread + Handler 的结合?

1)IntentService 继承自 Service
2)在 IntentService 的 onCreate() 方法中,创建 HandlerThread 对象,开启子线程执行耗时任务;
3)创建内部类 ServiceHandler 继承自 Handler ,实现消息的发送和处理。

  • 为什么IntentService会在执行完所有任务请求后自动停止Service?

内部类ServiceHandler的handleMessage(msg)方法中,调用了父类Service方法stopSelf(),停止了IntentService。

相关文章

网友评论

      本文标题:Android多线程:IntentService

      本文链接:https://www.haomeiwen.com/subject/ycztkctx.html