美文网首页
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