前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android多线程:深入剖析IntentService源码

Android多线程:深入剖析IntentService源码

作者头像
Carson.Ho
发布2022-03-25 09:19:10
5600
发布2022-03-25 09:19:10
举报
文章被收录于专栏:Android知识分享Android知识分享

前言

  • 多线程的应用在Android开发中是非常常见的,常用方法主要有:
    1. 继承Thread类
    2. 实现Runnable接口
    3. AsyncTask
    4. Handler
    5. HandlerThread
    6. IntentService
  • 今天,我将全面解析多线程其中一种常见用法:IntentService

Carson带你学多线程系列 基础汇总 Android多线程:基础知识汇总 基础使用 Android多线程:继承Thread类使用(含实例教程) Android多线程:实现Runnable接口使用(含实例教程) 复合使用 Android多线程:AsyncTask使用教程(含实例讲解) Android多线程:AsyncTask的原理及源码分析 Android多线程:HandlerThread使用教程(含实例讲解) Android多线程:HandlerThread原理及源码分析 Android多线程:IntentService使用教程(含实例讲解) Android多线程:IntentService的原理及源码分析 Android多线程:线程池ThreadPool全方位教学 相关使用 Android异步通信:这是一份全面&详细的Handler机制学习攻略 Android多线程:手把手教你全面学习神秘的Synchronized关键字 Android多线程:带你了解神秘的线程变量 ThreadLocal

目录

1. 定义

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

2. 作用

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

3. 使用场景

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

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

4. 使用步骤

步骤1:定义 IntentService的子类

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

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

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

5. 实例应用

步骤1:定义 IntentService的子类

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

代码语言:javascript
复制
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中注册服务

代码语言:javascript
复制
<service android:name=".myIntentService">
            <intent-filter >
                <action android:name="cn.scu.finch"/>
            </intent-filter>
        </service>

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

代码语言:javascript
复制
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()方法

代码语言:javascript
复制
@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 传递给服务 & 依次插入到工作队列中

代码语言:javascript
复制
/** 
  * 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个新的任务请求,这个新的任务会一直等待直到前面一个任务执行完毕后才开始执行

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

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

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

原因:

代码语言:javascript
复制
// 在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用法 & 源码
  • Carson带你学多线程系列

基础汇总 Android多线程:多线程基础知识汇总 基础使用 Android多线程:继承Thread类使用(含实例教程) Android多线程:实现Runnable接口使用(含实例教程) 复合使用 Android多线程:AsyncTask使用教程(含实例讲解) Android多线程:AsyncTask的原理及源码分析 Android多线程:HandlerThread使用教程(含实例讲解) Android多线程:HandlerThread的原理及源码分析 Android多线程:IntentService使用教程(含实例讲解) Android多线程:IntentService的原理及源码分析 Android多线程:线程池ThreadPool全方位教学 相关使用 Android异步通信:这是一份全面&详细的Handler机制学习攻略 Android多线程:手把手教你全面学习神秘的Synchronized关键字 Android多线程:带你了解神秘的线程变量 ThreadLocal

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 目录
  • 1. 定义
  • 2. 作用
  • 3. 使用场景
  • 4. 使用步骤
  • 5. 实例应用
    • 步骤1:定义 IntentService的子类
      • 步骤2:在Manifest.xml中注册服务
        • 步骤3:在Activity中开启Service服务
          • 测试结果
          • 6. 源码分析
            • 问题1:IntentService如何单独开启1个新的工作线程
              • 问题2:IntentService 如何通过onStartCommand() 将Intent 传递给服务 & 依次插入到工作队列中
                • 总结
                • 7. 注意事项
                  • 注意事项1. 工作任务队列 = 顺序执行
                    • 注意事项2:不建议通过 bindService() 启动 IntentService
                    • 8. 对比
                      • 8.1 IntentService与Service的区别
                        • 8.2 IntentService与其他线程的区别
                        • 9. 总结
                        相关产品与服务
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档