美文网首页Android 开发相关文章收集Android多线程
Android 多线程 解析:IntentService(含源码

Android 多线程 解析:IntentService(含源码

作者: Carson带你学安卓 | 来源:发表于2016-11-14 10:00 被阅读9449次

    前言

    • 多线程的应用在Android开发中是非常常见的,常用方法主要有:

      1. 继承Thread类
      2. 实现Runnable接口
      3. AsyncTask
      4. Handler
      5. HandlerThread
      6. IntentService
    • 今天,我将全面解析多线程其中一种常见用法:IntentService


    目录

    示意图

    1. 定义

    Android里的一个封装类,继承四大组件之一的Service


    2. 作用

    处理异步请求 & 实现多线程


    3. 使用场景

    线程任务 需 按顺序在后台执行

    1. 最常见的场景:离线下载
    2. 不符合多个数据同时请求的场景:所有的任务都在同一个Thread looper里执行

    4. 使用步骤

    步骤1:定义 IntentService的子类

    需 传入线程名称、复写onHandleIntent()方法

    步骤2:在Manifest.xml中注册服务
    步骤3:在Activity中开启Service服务


    5. 实例应用

    步骤1:定义 IntentService的子类

    传入线程名称、复写onHandleIntent()方法

    public class myIntentService extends IntentService {
    
      /** 
        * 在构造函数中传入线程名字
        **/  
        public myIntentService() {
            // 调用父类的构造函数
            // 参数 = 工作线程的名字
            super("myIntentService");
        }
    
       /** 
         * 复写onHandleIntent()方法
         * 根据 Intent实现 耗时任务 操作
         **/  
        @Override
        protected void onHandleIntent(Intent intent) {
    
            // 根据 Intent的不同,进行不同的事务处理
            String taskName = intent.getExtras().getString("taskName");
            switch (taskName) {
                case "task1":
                    Log.i("myIntentService", "do task1");
                    break;
                case "task2":
                    Log.i("myIntentService", "do task2");
                    break;
                default:
                    break;
            }
        }
    
        @Override
        public void onCreate() {
            Log.i("myIntentService", "onCreate");
            super.onCreate();
        }
       /** 
         * 复写onStartCommand()方法
         * 默认实现 = 将请求的Intent添加到工作队列里
         **/  
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            Log.i("myIntentService", "onStartCommand");
            return super.onStartCommand(intent, flags, startId);
        }
    
        @Override
        public void onDestroy() {
            Log.i("myIntentService", "onDestroy");
            super.onDestroy();
        }
    }
    
    

    步骤2:在Manifest.xml中注册服务

    <service android:name=".myIntentService">
                <intent-filter >
                    <action android:name="cn.scu.finch"/>
                </intent-filter>
            </service>
    

    步骤3:在Activity中开启Service服务

    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
                // 同一服务只会开启1个工作线程
                // 在onHandleIntent()函数里,依次处理传入的Intent请求
                // 将请求通过Bundle对象传入到Intent,再传入到服务里
    
                // 请求1
                Intent i = new Intent("cn.scu.finch");
                Bundle bundle = new Bundle();
                bundle.putString("taskName", "task1");
                i.putExtras(bundle);
                startService(i);
    
                // 请求2
                Intent i2 = new Intent("cn.scu.finch");
                Bundle bundle2 = new Bundle();
                bundle2.putString("taskName", "task2");
                i2.putExtras(bundle2);
                startService(i2);
    
                startService(i);  //多次启动
            }
        }
    

    测试结果

    运行结果

    6. 源码分析

    • IntentService的源码工作流程如下:
    工作流程

    特别注意:若启动IntentService 多次,那么 每个耗时操作 则 以队列的方式IntentServiceonHandleIntent回调方法中依次执行,执行完自动结束

    接下来,我们将通过 源码分析 解决以下问题:

    • IntentService 如何单独开启1个新的工作线程
    • IntentService 如何通过onStartCommand() 将Intent 传递给服务 & 依次插入到工作队列中

    问题1:IntentService如何单独开启1个新的工作线程

    主要分析内容 = IntentService源码中的 onCreate()方法

    @Override
    public void onCreate() {
        super.onCreate();
        
        // 1. 通过实例化andlerThread新建线程 & 启动;故 使用IntentService时,不需额外新建线程
        // HandlerThread继承自Thread,内部封装了 Looper
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();
      
        // 2. 获得工作线程的 Looper & 维护自己的工作队列
        mServiceLooper = thread.getLooper();
    
        // 3. 新建mServiceHandler & 绑定上述获得Looper
        // 新建的Handler 属于工作线程 ->>分析1
        mServiceHandler = new ServiceHandler(mServiceLooper); 
    }
    
    
       /** 
         * 分析1:ServiceHandler源码分析
         **/ 
         private final class ServiceHandler extends Handler {
    
             // 构造函数
             public ServiceHandler(Looper looper) {
             super(looper);
           }
    
            // IntentService的handleMessage()把接收的消息交给onHandleIntent()处理
            @Override
             public void handleMessage(Message msg) {
      
              // onHandleIntent 方法在工作线程中执行
              // onHandleIntent() = 抽象方法,使用时需重写 ->>分析2
              onHandleIntent((Intent)msg.obj);
              // 执行完调用 stopSelf() 结束服务
              stopSelf(msg.arg1);
    
        }
    }
    
       /** 
         * 分析2: onHandleIntent()源码分析
         * onHandleIntent() = 抽象方法,使用时需重写
         **/ 
          @WorkerThread
          protected abstract void onHandleIntent(Intent intent);
    

    问题2:IntentService 如何通过onStartCommand() 将Intent 传递给服务 & 依次插入到工作队列中

    
    /** 
      * onStartCommand()源码分析
      * onHandleIntent() = 抽象方法,使用时需重写
      **/ 
      public int onStartCommand(Intent intent, int flags, int startId) {
    
        // 调用onStart()->>分析1
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }
    
    /** 
      * 分析1:onStart(intent, startId)
      **/ 
      public void onStart(Intent intent, int startId) {
    
        // 1. 获得ServiceHandler消息的引用
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
    
        // 2. 把 Intent参数 包装到 message 的 obj 发送消息中,
        //这里的Intent  = 启动服务时startService(Intent) 里传入的 Intent
        msg.obj = intent;
    
        // 3. 发送消息,即 添加到消息队列里
        mServiceHandler.sendMessage(msg);
    }
    
    

    总结

    从上面源码可看出:IntentService本质 = Handler + HandlerThread

    1. 通过HandlerThread 单独开启1个工作线程:IntentService
    2. 创建1个内部 HandlerServiceHandler
    3. 绑定 ServiceHandlerIntentService
    4. 通过 onStartCommand() 传递服务intentServiceHandler 、依次插入Intent到工作队列中 & 逐个发送给 onHandleIntent()
    5. 通过onHandleIntent() 依次处理所有Intent对象所对应的任务

    因此我们通过复写onHandleIntent() & 在里面 根据Intent的不同进行不同线程操作 即可


    7. 注意事项

    注意事项1. 工作任务队列 = 顺序执行

    即 若一个任务正在IntentService中执行,此时你再发送1个新的任务请求,这个新的任务会一直等待直到前面一个任务执行完毕后才开始执行

    • 原因:
    1. 由于onCreate()只会调用一次 = 只会创建1个工作线程;
    2. 当多次调用 startService(Intent)时(即 onStartCommand()也会调用多次),其实不会创建新的工作线程,只是把消息加入消息队列中 & 等待执行。
      3, 所以,多次启动 IntentService 会按顺序执行事件

    若服务停止,则会清除消息队列中的消息,后续的事件不执行

    注意事项2:不建议通过 bindService() 启动 IntentService

    原因:

    // 在IntentService中,onBind()`默认返回null
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    
    • 采用 bindService()启动 IntentService的生命周期如下:

    onCreate() ->> onBind() ->> onunbind()->> onDestory()

    • 即,并不会调用onStart()onStartcommand()故不会将消息发送到消息队列,那么onHandleIntent()将不会回调,即无法实现多线程的操作

    此时,你应该使用Service,而不是IntentService


    8. 对比

    8.1 IntentService与Service的区别

    示意图

    8.2 IntentService与其他线程的区别

    示意图

    9. 总结

    • 本文主要 全面介绍了 多线程IntentService用法 & 源码
    • 接下来,我会继续讲解Android开发中关于多线程的知识,包括继承Thread类、实现Runnable接口、Handler等等,有兴趣可以继续关注Carson_Ho的安卓开发笔记

    请点赞!因为你的鼓励是我写作的最大动力!

    相关文章阅读
    Android开发:最全面、最易懂的Android屏幕适配解决方案
    Android事件分发机制详解:史上最全面、最易懂
    Android开发:史上最全的Android消息推送解决方案
    Android开发:最全面、最易懂的Webview详解
    Android开发:JSON简介及最全面解析方法!
    Android四大组件:Service服务史上最全面解析
    Android四大组件:BroadcastReceiver史上最全面解析


    欢迎关注Carson_Ho的简书!

    不定期分享关于安卓开发的干货,追求短、平、快,但却不缺深度

    相关文章

      网友评论

      • 笔墨Android:我有一个问题,这个服务要是在启动的过程中调用相应的stop方法为什么不能停止相应的Handler操作,是这种操作不需要相应的stop操作吗???
      • 8598bf4e0765:还有一个问题需要注意,就是Intent在5.0之后需要显式调用
      • 8598bf4e0765:直接继承IntentService ,然后直接写构造方法
        public MyIntentService(String name) {
        super(name);
        }
        这个时候minifest.xml里会报继承IntentService的这个类没有默认的构造函数。
        需要自己再添加一个参数为空的构造函数才能通过编译,
        比如:public MyIntentService(){super("WorkThread");}
        8598bf4e0765:应该是manifest.xml,写错了,抱歉
      • 8587af06ce05:完美解决了我下载的问题。感谢
      • 741a982bae87:看了你的那个LOG日志感觉有点晕,貌似错了,我在onStartCommand里加了标识,我自己跑了一遍,得到的是这个结果:
        05-14 18:41:51.531 2629-2629/? I/myIntentService: onCreate
        05-14 18:41:51.531 2629-2629/? I/myIntentService: onStartCommand task1
        05-14 18:41:51.531 2629-2629/? I/myIntentService: onStartCommand task2
        05-14 18:41:51.531 2629-2629/? I/myIntentService: onStartCommand task1
        05-14 18:41:51.541 2629-2642/? I/myIntentService: do task1
        05-14 18:41:51.541 2629-2642/? I/myIntentService: do task2
        05-14 18:41:51.541 2629-2642/? I/myIntentService: do task1
        05-14 18:41:51.721 2629-2629/? I/myIntentService: onDestroy
        这下感觉能和你的文章逻辑匹配上了
        995d60b46079:因为开启了新线程,执行顺序随机的,只要各自线程的执行顺序对了,以及任务在onStartCommand和onHandleIntent执行顺序正确都没错
      • Ruheng:SeriviceHanlder是个非静态内部类,是放置在线程HandlerThread中的,这种情况下会不会有内存泄漏的风险?
        Carson带你学安卓:非常正解~
        Carson带你学安卓:@崔小波 非常正解~
        cuixbo:@Ruheng
        ServiceHandler绑定的是后台线程的Looper,而在Service停止时,Looper退出了,内存很快会被回收,所以不存在内存泄露。
      • 7a79714f3eb1:看到private final class ServiceHandler extends Handler 发现个问题 源码中这里并没有使用弱引用,请问他是怎么避免出现handle内存泄漏的?难道是在整个service结束后移除所有的handler任务吗
        Carson带你学安卓:非常正解~
        Carson带你学安卓:@崔小波 非常正解~
        cuixbo:@长风撼树
        ServiceHandler绑定的是后台线程的Looper,而在Service停止时,Looper退出了,内存很快会被回收,所以不存在内存泄露。
      • David倪:谢谢分享

      本文标题:Android 多线程 解析:IntentService(含源码

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