【安卓笔记】IntentService源码剖析

Service组件想必都不陌生,这里不费口舌了。强调一点,Service组件默认运行在UI线程,所以也是会阻塞主线程的,使用时切记不可在Service中执行耗时操作,而应该创建子线程,异步执行。

IntentService类封装了在Service中创建子线程的工作(其实创建的是HandlerThread),我们只需继承IntentService,复写其onHandleIntent方法即可,onHandleIntent方法在子线程中运行,该方法的参数Intent来自onStart或者onStartCommand,即我们将原来应该在onStart方法中写的逻辑放到onHandleIntent中即可。

一、使用示例

创建如下所示的Service:

注:实际开发时仅需复写onHandleIntent方法,这里是为了观察其生命周期

package com.example.intentservicedemo;
import android.app.IntentService;
import android.content.Intent;
import android.util.Log;
public class MyIntentService extends IntentService
{
	private static final String TAG = "MyIntentService";
	public MyIntentService()//注意提供无参构造器
	{
		super("MyIntentService");
	}
	@Override
	protected void onHandleIntent(Intent intent)
	{
		int data = intent.getIntExtra("TEST",-1);
		Log.i(TAG,"data = "+data);

		switch (data)
		{
		case 1:
			Log.i(TAG,"service run task 1...");
			try
			{
				Thread.sleep(3000);
			} catch (InterruptedException e)
			{
				e.printStackTrace();
			}
			break;
		case 2:
			Log.i(TAG,"service run task 2...");
			break;
		}
	}

	@Override
	public void onCreate()
	{
		super.onCreate();
		Log.i(TAG,"oncreate...");
	}

	@Override
	public void onStart(Intent intent, int startId)
	{
		super.onStart(intent, startId);
		Log.i(TAG,"onstart...");
	}
	@Override
	public int onStartCommand(Intent intent, int flags, int startId)
	{
		Log.i(TAG,"onstartcommand...");
		return super.onStartCommand(intent, flags, startId);
	}

	@Override
	public void onDestroy()
	{
		super.onDestroy();
		Log.i(TAG, "ondestroy...");
	}
}

在清单文件中配置之:

<service
android:name="com.example.intentservicedemo.MyIntentService"
></service>

创建一个Activity:

package com.example.intentservicedemo;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
public class MainActivity extends Activity
{
	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		Intent intent = new Intent(this,MyIntentService.class);
		intent.putExtra("TEST",1);
		startService(intent);
		intent.putExtra("TEST",2);
		startService(intent);
	}

}

观察日志:

处理完事件之后,服务将被销毁。

二、源码剖析

IntentService内部封装了子线程消息循环:

 private volatile Looper mServiceLooper;//Looper
 private volatile ServiceHandler mServiceHandler;//Handler
 private String mName;//子线程名
 private boolean mRedelivery;

其中ServiceHandler是Handler子类:

  private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }
        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }

在handleMessage中调用onHandleIntent方法,当完成服务之后,调用stopSelf销毁服务

消息循环的创建在onCreate方法中:

 public void onCreate() {
        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();
        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

在onCreate方法中创建了子线程HandlerThread,然后利用HandlerThread提供的Looper创建消息循环。

在onStart方法中将Intent作为消息发送给Handler,最终交由onHandleIntent方法处理:

public void onStart(Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

调用destroy方法会销毁消息循环,进而子线程终止:

 @Override
    public void onDestroy() {
        mServiceLooper.quit();
    }

-----------------------------------------

附:笔者之前有个疑问,上面例子中两次执行startService,分析源码可知,最终由其内部Handler的handleMessage处理,handleMessage执行完onHandleIntent方法后,应该会调用stopSelf销毁服务,所以日志中应该有两次destroy才是,可事实上只执行了一次destroy。这是为什么呢?我们注意到handleMessage方法的stopSelf有个参数为startId,也就是停止具有特定startId的服务,而上述两次启动startService的startId是不同的,故服务并不会停止。

打印两次的startId

文档解释:

However, if your
service handles multiple requests to onStartCommand() concurrently, then
you shouldn‘t
stop the service when you‘re
done processing a start request, because
you might have since received a new start
request (stopping
at the end of the first request would terminate the second one). To avoid this problem, you
can use stopSelf(int) to
ensure that your request to stop the service is always based on the most recent start request. That is, when
you call stopSelf(int), you
pass the ID of the start request (the
startId delivered to onStartCommand()) to
which your stop request corresponds. Then if the
service received a new start
request before you were able to call stopSelf(int), then
the ID will not match and the service will not stop.

时间: 2024-10-17 15:18:26

【安卓笔记】IntentService源码剖析的相关文章

【安卓笔记】HandlerThread源码剖析

有时候我们需要在应用程序中创建一些常驻的子线程不定期地执行一些计算型任务,这时候可以考虑使用HandlerThread,它具有创建带消息循环的子线程的作用. 一.HanderThread使用示例 先熟悉下HandlerThread的一般用法.我们创建一个如下所示的Activity: package com.example.handlethreaddemo; import android.app.Activity; import android.os.Bundle; import android.

《STL源码剖析》---stl_pair.h阅读笔记

pair是STL中的模板类型,它可以存储两个元素,它也被称作"对组".在map中已经用到了它,pair其实就是一个struct结构,存有两个public的元素,重载了几个运算符,没有什么成员函数,源代码很简单. G++ 2.91.57,cygnus\cygwin-b20\include\g++\stl_pair.h 完整列表 /* * * Copyright (c) 1994 * Hewlett-Packard Company * * Permission to use, copy,

《STL源码剖析》---stl_tree.h阅读笔记

STL中,关联式容器的内部结构是一颗平衡二叉树,以便获得良好的搜索效率.红黑树是平衡二叉树的一种,它不像AVL树那样要求绝对平衡,降低了对旋转的要求,但是其性能并没有下降很多,它的搜索.插入.删除都能以O(nlogn)时间完成.平衡可以在一次或者两次旋转解决,是"性价比"很高的平衡二叉树. RB-tree(red black tree)红黑树是平衡二叉树.它满足一下规则 (1)每个节点不是红色就是黑色. (2)根节点是黑色. (3)如果节点为红色,则其子节点比为黑色. (4)任何一个节

《STL源码剖析》---stl_iterator.h阅读笔记

STL设计的中心思想是将容器(container)和算法(algorithm)分开,迭代器是容器(container)和算法(algorithm)之间的桥梁. 迭代器可以如下定义:提供一种方法,能够依序寻访某个容器内的所有元素,而又无需暴露该容器的内部表达方式. 在阅读代码之前,要先了解一个新概念:Traits编程技法 template <class T> struct MyIter { typedef T value_type //内嵌型别声明 T *ptr; MyIter(T *p = 0

《python源码剖析》笔记 Python虚拟机框架

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1. Python虚拟机会从编译得到的PyCodeObject对象中依次读入每一条字节码指令, 并在当前的上下文环境中执行这条字节码指令. Python虚拟机实际上是在模拟操作中执行文件的过程 PyCodeObject对象中包含了字节码指令以及程序的所有静态信息,但没有包含 程序运行时的动态信息--执行环境(PyFrameObject) 2.Python源码中的PyFrameObject

《python源码剖析》笔记 python虚拟机中的一般表达式

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.字节码指令 LOAD_CONST:从consts表中读取序号为i的元素并压入到运行时栈中 STORE_NAME:改变local名字空间.从符号表names取序号为i的元素作为变量名, 取运行时栈的栈顶元素作为变量值,完成从变量名到变量值的映射关系的创建. BUILD_MAP:创建一个空的PyDictObject对象,并压入运行时栈 DUP_TOP:将栈顶元素的引用计数增加1,并将它再次

Python源码剖析笔记3-Python执行原理初探

Python源码剖析笔记3-Python执行原理初探 本文简书地址:http://www.jianshu.com/p/03af86845c95 之前写了几篇源码剖析笔记,然而慢慢觉得没有从一个宏观的角度理解python执行原理的话,从底向上分析未免太容易让人疑惑,不如先从宏观上对python执行原理有了一个基本了解,再慢慢探究细节,这样也许会好很多.这也是最近这么久没有更新了笔记了,一直在看源码剖析书籍和源码,希望能够从一个宏观层面理清python执行原理.人说读书从薄读厚,再从厚读薄方是理解了

Python源码剖析笔记0 ——C语言基础

python源码剖析笔记0--C语言基础回顾 要分析python源码,C语言的基础不能少,特别是指针和结构体等知识.这篇文章先回顾C语言基础,方便后续代码的阅读. 1 关于ELF文件 linux中的C编译得到的目标文件和可执行文件都是ELF格式的,可执行文件中以segment来划分,目标文件中,我们是以section划分.一个segment包含一个或多个section,通过readelf命令可以看到完整的section和segment信息.看一个栗子: char pear[40]; static

python源码剖析笔记1——Python对象初见

python源码剖析笔记1--Python对象初见 工作整两年了,用python最多,然而对于python内部机制不一定都清楚,每天沉醉于增删改查的简单逻辑编写,实在耗神.很多东西不用就忘记了,比如C语言,正好,python源码用C写的,分析python源码的同时又能温故C语言基础,实在是件很好的事情.另外,还有陈儒大神的<python源码剖析>做指引,分析也不至于没头没脑.期望在一个月的业余时间,能有所小成,以此为记. 1 python中的对象 python中,一切东西都是对象,在c语言实现