IntentService使用以及源码分析

一 概述

我们知道,在Android开发中,遇到耗时的任务操作时,都是放到子线程去做,或者放到Service中去做,在Service中开一个子线程来执行耗时操作。 那么,在Service里面我们需要自己管理Service的生命周期,何时开启何时关闭,还是很麻烦的,还好Android给我们提供了一个这样的类,叫做IntentService

那么IntentService是做什么用的呢? IntentService: 是继承于Service的一个类,用来处理异步请求。可以直接通过startService(Intent intent)来提交请求,Service的创建,关闭,开子线程等工作,IntentService内部都帮我们封装好了,我们只需要发请求处理请求就行了。 如此简单

我们先来看一下IntentService的用法

二 IntentService用法

假如我们现在有一个下载文件的需求,我们需要开启一个Service并在里面开启一个子线程中去下载文件。

1 先继承IntentService实现一个类,我们叫做 DownloadService如下

1public class DownloadService extends IntentService { 2 3 //重写无参的构造方法 4 public DownloadService(){ 5 super("DownloadService"); 6 } 7 8 @Override 9 protected void onHandleIntent(Intent intent) { 10 String url = intent.getStringExtra("url"); 11 downloadFile(url); 12 } 13 14 //下载文件 15 private void downloadFile(String url){ 16 try { 17 Log.e("DownloadService","当前线程名:" + Thread.currentThread().getName()); 18 Log.e("DownloadService","文件开始下载... url="+url); 19 20 //模拟下载文件操作 21 Thread.sleep(3000); 22 23 Log.e("DownloadService","文件下载完成..."); 24 } catch (InterruptedException e) { 25 e.printStackTrace(); 26 } 27 } 28}

继承IntentService类,实现onHandleIntent(Intent intent)方法 注意,不要忘了DownloadService需要在清单文件中注册

2 我们在界面上的按钮的点击事件中,来下载一个文件,如下

1 findViewById(R.id.tv_hello).setOnClickListener(new View.OnClickListener() { 2 @Override 3 public void onClick(View v) { 4 Log.e("DownloadService","当前线程名:"+Thread.currentThread().getName()); 5 6 //像平时启动service方式一样,没有任何区别 7 Intent intent = new Intent(MainActivity.this,DownloadService.class); 8 intent.putExtra("url","http://xx/test.apk"); 9 startService(intent); 10 } 11 });

点击按钮,输出 E/DownloadService: 当前线程名:main E/DownloadService: 当前线程名:IntentService[DownloadService] E/DownloadService: 文件开始下载... url=http://xx/test.apk E/DownloadService: 文件下载完成...

通过输出日志,可以看到,开启任务是在主线程中,而执行下载文件的耗时任务,是在子线程中,使用的时候,只需要startService(Intent intent),并通过intent把所需要的数据带过去就行了。是不是很简单。

下面来分析IntentService是如何做到的?

三 IntentService源码分析

1 看下IntentService的类定义

1public abstract class IntentService extends Service { 2 private volatile Looper mServiceLooper; 3 private volatile ServiceHandler mServiceHandler; 4 private String mName; 5 private boolean mRedelivery; 6 7 private final class ServiceHandler extends Handler { 8 public ServiceHandler(Looper looper) { 9 super(looper); 10 } 11 12 @Override 13 public void handleMessage(Message msg) { 14 onHandleIntent((Intent)msg.obj); 15 stopSelf(msg.arg1); 16 } 17 } 18 19 20 ...... 21}

可以看到,IntentService有几个需要注意的地

  1. IntentService是一个Service也是一个抽象类,子类只需要实现void onHandleIntent(@Nullable Intent intent)并在里面添加自己的业务即可

  2. IntentService类中有一个 Looper mServiceLooper以及一个ServiceHandler mServiceHandler,

  3. ServiceHandler继承Handler,并在handleMessage()中调用了外部类的onHandleIntent方法

  4. ServiceHandlerhandleMessage()方法中,执行完onHandleIntent((Intent)msg.obj),调用了stopSelf(msg.arg1)来关闭这个Service

所以IntentService不用去管怎么创建的,怎么关闭的,怎么开启线程的,我们只需要继承IntentService,实现onHandleIntent()方法并在里面执行我们的逻辑就行了,执行完任务之后,自动会把Service关闭。这一切都是IntentService帮我们封装好了的。

2 startService之后,会走Service的生命周期方法,onCreate()源码如下

1 @Override 2 public void onCreate() { 3 super.onCreate(); 4 5 //还记得上一篇讲的HandlerThread源码分析吗 6 //创建一个HandlerThread对象,并调用start()方法 7 //使之成为一个looper线程 8 HandlerThread thread = new HandlerThread("IntentService[" + mName + "]"); 9 thread.start(); 10 11 //拿到上面创建的looper线程对应的looper 12 //并传给ServiceHandler 13 mServiceLooper = thread.getLooper(); 14 mServiceHandler = new ServiceHandler(mServiceLooper); 15 }

很明显,在onCreate中创建了一个线程而且是一个looper线程,又创建了一个Handler对象,并把这个线程的looper传给了这个Handler,那么这个Handler发送的任务消息都会由这个ServiceHandler处理了

再看看 onStart()方法和onStartCommand()方法,如下

onStart()方法

1 @Override 2 public void onStart(@Nullable Intent intent, int startId) { 3 Message msg = mServiceHandler.obtainMessage(); 4 msg.arg1 = startId; 5 msg.obj = intent; 6 mServiceHandler.sendMessage(msg); 7 }

onStartCommand()方法

1 @Override 2 public int onStartCommand(@Nullable Intent intent, int flags, int startId) { 3 onStart(intent, startId); 4 return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY; 5 }

startService方法启动的时候,第一次会调用onCreate() -> onStart() -> onStartCommand()方法 而且之后再次调用startService方法启动的时候,不会再调用onCreate()方法了,而是会调用onStandCommand()方法

而在IntentService中,onStartCommand()方法中又会调用onStart()方法,所以,我们只需要分析onStart()方法即可

onStart()方法源码

1 @Override 2 public void onStart(@Nullable Intent intent, int startId){ 3 //使用mServiceHandler获取一个消息 4 Message msg = mServiceHandler.obtainMessage(); 5 msg.arg1 = startId; 6 msg.obj = intent; 7 8 //发送这个消息 9 mServiceHandler.sendMessage(msg); 10 } 11

通过上面可知,我们调用startService方法启动service的时候,会调用onStart()方法,在这个方法里面,把intent和startId赋值给了 msg ,并发送消息。这时候会调用Handler的handleMessag()方法,ServiceHandler的源码如下:

1public abstract class IntentService extends Service { 2 private volatile Looper mServiceLooper; 3 private volatile ServiceHandler mServiceHandler; 4 private String mName; 5 private boolean mRedelivery; 6 7 private final class ServiceHandler extends Handler { 8 public ServiceHandler(Looper looper) { 9 super(looper); 10 } 11 12 @Override 13 public void handleMessage(Message msg) { 14 onHandleIntent((Intent)msg.obj); 15 stopSelf(msg.arg1); 16 } 17 } 18 19 ...... 20}

可以看到ServiceHandler是IntentService的内部类,而且在handleMessage中会调用IntentService的 onHandleIntent()方法,并把 intent 参数也传过去。

我们来看下 onHandleIntent()方法 protected abstract void onHandleIntent(@Nullable Intent intent); 是一个抽象方法,这时候就需要子类去实现这个方法并在里面做耗时的操作即可。

经过上面的分析可知,IntentService使用也非常方便,原理就是利用HandlerThread开启了一个looper线程,并在onStart中把intent通过msg发送出去,并在handleMessage中又调用了onHandleIntent方法,子类实现即可

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )