Android 进阶16:IntentService 使用及源码解析

2023-12-14 16:30

本文主要是介绍Android 进阶16:IntentService 使用及源码解析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

  • It’s time to start living the life you’ve only imagined.

读完本文你将了解:

    • IntentService 简介
    • IntentService 源码分析
    • IntentService 的使用
      • 创建 IntentService 的子类
      • 布局界面
      • 调用方代码
      • 运行效果
    • 总结
    • 代码地址
    • Thanks

在前面两篇文章 源码解读 Android 消息机制( Message MessageQueue Handler Looper) 和 HandlerThread 使用场景及源码解析 中我们了解了 Android 中执行异步任务的两种方式。

本篇文章介绍另外一种:IntentService。

IntentService 简介

public abstract class IntentService extends Service {...}

IntentService 是一个抽象类,继承了 Service

由于是一个 Service,IntentService 的优先级比较高,在后台不会轻易被系统杀死;它可以接收 Intent 请求,然后在子线程中按顺序执行。

官方文档关于它的介绍:

IntentService 使用工作线程逐一处理所有启动请求。如果你不需要在 Service 中执行并发任务,IntentService 是最好的选择。

IntentService 源码分析

IntentService 源码很短:

public abstract class IntentService extends Service {private volatile Looper mServiceLooper;private volatile ServiceHandler mServiceHandler;private String mName;private boolean mRedelivery;//内部创建的 Handlerprivate final class ServiceHandler extends Handler {public ServiceHandler(Looper looper) {super(looper);}@Overridepublic void handleMessage(Message msg) {//调用这个方法处理数据onHandleIntent((Intent)msg.obj);//处理完就自尽了stopSelf(msg.arg1);}}//子类需要重写的构造函数,参数是服务的名称public IntentService(String name) {super();mName = name;}//设置当前服务被意外关闭后是否重新//如果设置为 true,onStartCommand() 方法将返回 Service.START_REDELIVER_INTENT,这样当//当前进程在 onHandleIntent() 方法返回前销毁时,会重启进程,重新使用之前的 Intent 启动这个服务//(如果有多个 Intent,只会使用最后的一个)//如果设置为 false,onStartCommand() 方法返回 Service.START_NOT_STICKY,当进程销毁后也不重启服务public void setIntentRedelivery(boolean enabled) {mRedelivery = enabled;}@Overridepublic void onCreate() {super.onCreate();//创建时启动一个 HandlerThreadHandlerThread thread = new HandlerThread("IntentService[" + mName + "]");thread.start();//拿到 HandlerThread 中的 Looper,然后创建一个子线程中的 HandlermServiceLooper = thread.getLooper();mServiceHandler = new ServiceHandler(mServiceLooper);}@Overridepublic void onStart(@Nullable Intent intent, int startId) {//将 intent 和 startId 以消息的形式发送到 HandlerMessage msg = mServiceHandler.obtainMessage();msg.arg1 = startId;msg.obj = intent;mServiceHandler.sendMessage(msg);}/*** You should not override this method for your IntentService. Instead,* override {@link #onHandleIntent}, which the system calls when the IntentService* receives a start request.* @see android.app.Service#onStartCommand*/@Overridepublic int onStartCommand(@Nullable Intent intent, int flags, int startId) {onStart(intent, startId);return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;}@Overridepublic void onDestroy() {mServiceLooper.quit();    //值得学习的,在销毁时退出 Looper}@Override@Nullablepublic IBinder onBind(Intent intent) {return null;}@WorkerThreadprotected abstract void onHandleIntent(@Nullable Intent intent);
}

从上述代码可以看到,IntentService 做了以下工作:

  • 创建了一个 HandlerThread 默认的工作线程
  • 使用 HandlerThreadLooper 创建了一个 Handler,这个 Handler 执行在子线程
  • onStartCommand() 中调用 onStart(),然后在 onStart() 中将 intent 和 startId 以消息的形式发送到 Handler
  • Handler 中将消息队列中的 Intent 按顺序传递给 onHandleIntent() 方法
  • 在处理完所有启动请求后自动停止服务,不需要我们调用 stopSelf()
public void handleMessage(Message msg) {onHandleIntent((Intent)msg.obj);stopSelf(msg.arg1);
}

有同学可能有疑问,在 handleMessage 方法中不是调用了一次 onHandleIntent() 后就调用 stopSelf() 了吗,这不是只能执行一个任务么?

仔细看下可以发现,这个 stopSelf() 方法传递了一个 id,这个 id 是启动服务时 IActivityManager 分配的 id,当我们调用 stopSelf(id) 方法结束服务时,IActivityManager 会对比当前 id 是否为最新启动该服务的 id,如果是就关闭服务。

public final void stopSelf(int startId) {if (mActivityManager == null) {return;}try {mActivityManager.stopServiceToken(new ComponentName(this, mClassName), mToken, startId);} catch (RemoteException ex) {}
}

因此只有当最后一次启动 IntentService 的任务执行完毕才会关闭这个服务。

此外还要注意的是,IntentService 中除了 onHandleIntent 方法其他都是运行在主线程的。

IntentService 的使用

通过前面的源码分析,我们可以看到,最终每个任务的处理都会调用 onHandleIntent(),因此使用 IntentService 也很简单,只需实现 onHandleIntent() 方法,在这里执行对应的后台工作即可。

举个例子:

我们写一个使用 IntentService 实现在子线程下载多张 美女图片 的效果。

创建 IntentService 的子类

/*** Description:* <br> 使用 IntentService 实现下载* <p>* <br> Created by shixinzhang on 17/6/8.* <p>* <br> Email: shixinzhang2016@gmail.com* <p>* <a  href="https://about.me/shixinzhang">About me</a>*/public class DownloadService extends IntentService {private static final String TAG = "DownloadService";public static final String DOWNLOAD_URL = "down_load_url";public static final int WHAT_DOWNLOAD_FINISHED = 1;public static final int WHAT_DOWNLOAD_STARTED = 2;public DownloadService() {super(TAG);}private static Handler mUIHandler;public static void setUIHandler(final Handler UIHandler) {mUIHandler = UIHandler;}/*** 这个方法运行在子线程** @param intent*/@Overrideprotected void onHandleIntent(final Intent intent) {String url = intent.getStringExtra(DOWNLOAD_URL);if (!TextUtils.isEmpty(url)) {sendMessageToMainThread(WHAT_DOWNLOAD_STARTED, "\n " + DateUtils.getCurrentTime() + " 开始下载任务:\n" + url);try {Bitmap bitmap = downloadUrlToBitmap(url);SystemClock.sleep(1000);    //延迟一秒发送消息sendMessageToMainThread(WHAT_DOWNLOAD_FINISHED, bitmap);} catch (Exception e) {e.printStackTrace();}}}/*** 发送消息到主线程** @param id* @param o*/private void sendMessageToMainThread(final int id, final Object o) {if (mUIHandler != null) {mUIHandler.sendMessage(mUIHandler.obtainMessage(id, o));}}/*** 下载图片** @param url* @return* @throws Exception*/private Bitmap downloadUrlToBitmap(String url) throws Exception {HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();BufferedInputStream in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024);Bitmap bitmap = BitmapFactory.decodeStream(in);urlConnection.disconnect();in.close();return bitmap;}
}

在上面的代码中,我们做了以下几件事:

  • onHandleIntent() 中接收任务,开始下载,同时将状态返回给主线程
  • 下载完成后将得到的 Bitmap 通过 Handler 发送到主线程

为了界面上有明显效果,设置了一定延时。

IntentService 也是 Service,别忘了在 AndroidManifest 中注册!

布局界面

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center_horizontal"android:orientation="vertical"android:padding="8dp"><ImageView
        android:id="@+id/iv_display"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"/><TextView
        android:id="@+id/tv_status"android:layout_width="match_parent"android:layout_height="250dp"android:padding="8dp"android:text="状态信息:"/><Button
        android:id="@+id/btn_download"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="开始下载"/>
</LinearLayout>

界面上有一个开始下载按钮,一个显示下载状态的 TextView,一个展示图片的 ImageView.

调用方代码

/*** Description:* <br> IntentService 实例* <p>* <br> Created by shixinzhang on 17/6/9.* <p>* <br> Email: shixinzhang2016@gmail.com* <p>* <a  href="https://about.me/shixinzhang">About me</a>*/public class IntentServiceActivity extends AppCompatActivity implements Handler.Callback {@BindView(R.id.iv_display)ImageView mIvDisplay;@BindView(R.id.btn_download)Button mBtnDownload;@BindView(R.id.tv_status)TextView mTvStatus;private List<String> urlList = Arrays.asList("https://ws1.sinaimg.cn/large/610dc034ly1fgepc1lpvfj20u011i0wv.jpg","https://ws1.sinaimg.cn/large/d23c7564ly1fg6qckyqxkj20u00zmaf1.jpg","https://ws1.sinaimg.cn/large/610dc034ly1fgchgnfn7dj20u00uvgnj.jpg");    //美女图片地址int mFinishCount;   //完成的任务个数@Overrideprotected void onCreate(@Nullable final Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_intent_service);ButterKnife.bind(this);DownloadService.setUIHandler(new Handler(this));}@OnClick(R.id.btn_download)public void downloadImage() {Intent intent = new Intent(this, DownloadService.class);for (String url : urlList) {intent.putExtra(DownloadService.DOWNLOAD_URL, url);startService(intent);}mBtnDownload.setEnabled(false);}@Overridepublic boolean handleMessage(final Message msg) {if (msg != null) {switch (msg.what) {case DownloadService.WHAT_DOWNLOAD_FINISHED:mIvDisplay.setImageBitmap((Bitmap) msg.obj);mBtnDownload.setText("完成 " + (++mFinishCount) + "个任务");break;case DownloadService.WHAT_DOWNLOAD_STARTED:mTvStatus.setText(mTvStatus.getText() + (String) msg.obj);break;}}return true;}
}

Activity 中做了以下几件事:

  • 设置 UI 线程的 Handler 给 IntentService
  • 使用 startService(intent) 启动 IntentService 执行图片下载任务
  • 在 Handler 的 handleMessage 中根据消息类型进行相应处理

可以看到,调用方的代码和上一篇使用 HandlerThread 的方法很相似。

运行效果

这里写图片描述

总结

本篇文章介绍了 IntentService 的使用和源码。

在第一次启动 IntentService 后,IntentService 仍然可以接受新的请求,接受到的新的请求被放入了工作队列中,等待被串行执行。

使用 IntentService 显著简化了启动服务的实现,如果您决定还重写其他回调方法(如 onCreate()、onStartCommand() 或 onDestroy()),请确保调用超类实现,以便 IntentService 能够妥善处理工作线程的生命周期。

由于大多数启动服务都不必同时处理多个请求(实际上,这种多线程情况可能很危险),因此使用 IntentService 类实现服务也许是最好的选择。

一句话总结 IntentService:

  • 优先级比较高的、用于串行执行异步任务、会自尽的 Service。

代码地址

Thanks

《Android 开发艺术探索》
https://developer.android.com/guide/components/services.html#ExtendingIntentService
http://rainbow702.iteye.com/blog/1143286
http://blog.csdn.net/javazejian/article/details/52426425

这篇关于Android 进阶16:IntentService 使用及源码解析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/493225

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象