1. IntentService原理

IntentService是一种特殊的Service,既然是Service,使用的时候记得在AndroidManifest清单文件中注册。

并且它是一个抽象类,因此必须创建它的子类才能使用IntentService,IntentService可用于执行后台耗时的任务,当任务执行后它会自动停止,同时由于IntentService是服务的原因,这导致它的优先级比单纯的线程要高级很多,所以IntentService比较适合执行高优先级的后台任务,因为它的优先级高不容易被系统杀死。

在实现上,IntentService封装了HandlerThread 和 Handler,这一点我们可以在IntentService的onCreate方法看出来:

@Override
public void onCreate() {
// TODO: It would be nice to have an option to hold a partial wakelock
// during processing, and to have a static startService(Context, Intent)
// method that would launch the service & hand off a wakelock. super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start(); mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}

当IntentService被第一次启动时候,它的onCreate方法会被调用,onCreate方法会创建一个HandlerThread,然后利用它的Looper构造一个Handler对象mServiceHandler(ServiceHandler继承自Handler,ServiceHandler是IntentService内部类)

我们来到IntentService中的内部类ServiceHandler的实现源码如下:

 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);
}
}

这样通过mServiceHandler发送的消息都会最终在HandlerThread中执行,从这个角度来看,IntentService也可以用于执行后台任务。

每次启动IntentService,它的onStartCommand方法就会调用一次,IntentService在onStartCommand中处理每个后台任务的Intent。

接下来我们要看一下IntentService在OnStartCommand方法是如何处理外界的Intent的,如下:

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

在上面源码中可以知道onStartCommand调用了onStart,我们再看看IntentService中onStart方法的实现如下所示:

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

上面的源码可以看出,在onStart方法之中,IntentService仅仅是通过mServiceHandler发送了一个消息,而mServiceHandler录属于HandlerThread的,所以这个消息会在HandlerThread这个线程之中被处理。

mServiceHandler收到消息之后,会将Intent对象传递给onHandleIntent方法去处理。

需要特别注意:

  这里的Intent对象的内容 和 外界的StartService(intent)中的intent的内容是完全一致的,通过这个Intent对象即可解析出外界启动IntentService时候所传递的参数,通过这些参数就可以区分具体的后台任务,这样在onHandleIntent方法中就可以对不同的后台任务做处理了。

当onHandleIntent方法执行结束之后,IntentService会通过stopSelf(int startId)方法尝试停止服务。这里之所以采用stopSelf(int startId)而不是stopSelf()来停止服务,那是因为stopSelf()会立刻停止服务,而这个时候可能还有其他消息未处理,stopSelf(int startId)则会等待所有的消息都处理完毕之后才终止服务。一般来说,stopSelf(int startId)在尝试停止服务之前会判断最近启动服务的次数是否和startId相等,如果相等就立刻停止服务,不相等则不停止服务。

IntentService的onHandleIntent方法是一个抽象方法,它需要在子类中实现,它的作用是从Intent参数中区分具体的任务并执行这些任务。如果目前只存在一个后台任务,那么onHandleIntent方法执行完整个任务之后,stopSelf(int startId)就会直接停止服务;如果目前存在多个后台任务,那么当onHandleIntent方法执行完最后一个任务时候,stopSelf(int startId)才会直接停止服务。

另外,由于每执行一个后台任务就必须启动一次IntentService,而IntentService内部则可以通过消息的方式向HandlerThread请求执行任务,Handler中的Looper是顺序处理消息的,这就意味着IntentService也是顺序执行后台任务的,当有多个后台任务同时存在,这些后台任务会按照外界发起的顺序排队执行。

总而言之,流程如下:

2. IntentService使用示例:

(1)新建Android工程如下:

(2)首先我们来到主Activity,如下:

package com.himi.intentservicedemo;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle; public class MainActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); /**
* 可以启动多次,每启动一次,就会新建一个work thread
* 但IintentService的实例始终只有一个
*/
// Operation 1
Intent startServiceIntent = new Intent(this,MyIntentService.class);
Bundle bundle = new Bundle();
bundle.putString("param", "oper1");
startServiceIntent.putExtras(bundle);
startService(startServiceIntent); // Operation 2
Intent startServiceIntent2 = new Intent(this,MyIntentService.class);
Bundle bundle2 = new Bundle();
bundle2.putString("param", "oper2");
startServiceIntent2.putExtras(bundle2);
startService(startServiceIntent2);
}
}

(3)其次这里在MainActivity中开启IntentService服务,但是它是个抽象类,需要一个IntentService具体实现子类,这里为MyIntentService,如下:

package com.himi.intentservicedemo;

import android.app.IntentService;
import android.content.Intent;
import android.os.IBinder; public class MyIntentService extends IntentService { public MyIntentService() {
// 必须实现父类的构造方法
super("IntentServiceDemo");
} @Override
public IBinder onBind(Intent intent) {
System.out.println("onBind");
return super.onBind(intent);
} @Override
public void onCreate() {
System.out.println("onCreate");
super.onCreate();
} @Override
public void onStart(Intent intent, int startId) {
System.out.println("onStart");
super.onStart(intent, startId);
} @Override
public int onStartCommand(Intent intent, int flags, int startId) {
System.out.println("onStartCommand");
return super.onStartCommand(intent, flags, startId);
} @Override
public void setIntentRedelivery(boolean enabled) {
super.setIntentRedelivery(enabled);
System.out.println("setIntentRedelivery");
} @Override
protected void onHandleIntent(Intent intent) { //这个方法在子线程中执行,因此需要用到handler跟主线程进行通信
// Iintent是从Activity发过来的,携带识别参数,根据参数不同执行不同的任务
String action = intent.getExtras().getString("param");
if (action.equals("oper1")) {
System.out.println("Operation1");
} else if (action.equals("oper2")) {
System.out.println("Operation2");
} try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} @Override
public void onDestroy() {
System.out.println("onDestroy");
super.onDestroy();
} }

(4)最后很重要一步,别忘了配置IntentService,因为它继承于Service,所以,它还是一个Service,一定要配置,否则是不起作用的,在清单文件中配置一下IntentService,如下:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.himi.intentservicedemo"
android:versionCode="1"
android:versionName="1.0" > <uses-sdk
android:minSdkVersion="15"
android:targetSdkVersion="17" /> <application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity> <service
android:name="MyIntentService">
</service> </application> </manifest>

(5)部署程序到手机上,观察Logcat如下:

  从结果可以看到,onCreate方法只执行了一次,而onStartCommand和onStart方法执行了两次,开启了两个Work Thread,这就证实了之前所说的,启动多次,但IntentService的实例只有一个,这跟传统的Service是一样的。Operation1也是先于Operation2打印,并且我让两个操作间停顿了2s,最后是onDestroy销毁了IntentService。

这就是IntentService,一个方便我们处理业务流程的类,它是一个Service,但是比Service更智能。

Android(java)学习笔记266:Android线程形态之 IntentService的更多相关文章

  1. Java学习笔记-多线程-创建线程的方式

    创建线程 创建线程的方式: 继承java.lang.Thread 实现java.lang.Runnable接口 所有的线程对象都是Thead及其子类的实例 每个线程完成一定的任务,其实就是一段顺序执行 ...

  2. Android开发学习笔记-关于Android的消息推送以及前后台切换

    下面是最简单的Android的消息推送的实现方法 package com.example.shownotic; import java.util.Random; import android.supp ...

  3. 0036 Java学习笔记-多线程-创建线程的三种方式

    创建线程 创建线程的三种方式: 继承java.lang.Thread 实现java.lang.Runnable接口 实现java.util.concurrent.Callable接口 所有的线程对象都 ...

  4. 0038 Java学习笔记-多线程-传统线程间通信、Condition、阻塞队列、《疯狂Java讲义 第三版》进程间通信示例代码存在的一个问题

    调用同步锁的wait().notify().notifyAll()进行线程通信 看这个经典的存取款问题,要求两个线程存款,两个线程取款,账户里有余额的时候只能取款,没余额的时候只能存款,存取款金额相同 ...

  5. JAVA学习笔记16——控制线程

    Java的线程支持提供了一些便捷的工具方法,通过这些便捷的工具方法可以很好地控制线程执行.   join线程 Thread提供了让一个线程等待另一个线程完成的方法——join().当在某个线程执行流中 ...

  6. java学习笔记 --- 多线程(线程安全问题——同步代码块)

    1.导致出现安全问题的原因: A:是否是多线程环境 B:是否有共享数据 C:是否有多条语句操作共享数据 2.解决线程安全问题方法: 同步代码块: synchronized(对象){ 需要同步的代码; ...

  7. Java 学习笔记之 Daemon线程

    Daemon线程: 线程: 用户线程 守护线程 守护线程是一种特殊的线程,在进程中不存在非守护线程了,则守护线程自动销毁. public class DaemonThread extends Thre ...

  8. Android 数字签名学习笔记

    Android 数字签名学习笔记 在Android系统中,所有安装到系统的应用程序都必有一个数字证书,此数字证书用于标识应用程序的作者和在应用程序之间建立信任关系,如果一个permission的pro ...

  9. 0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁

    什么是同步 在上一篇0036 Java学习笔记-多线程-创建线程的三种方式示例代码中,实现Runnable创建多条线程,输出中的结果中会有错误,比如一张票卖了两次,有的票没卖的情况,因为线程对象被多条 ...

  10. Android动画学习笔记-Android Animation

    Android动画学习笔记-Android Animation   3.0以前,android支持两种动画模式,tween animation,frame animation,在android3.0中 ...

随机推荐

  1. 常见mongo命令

    @(编程) 查询 db.getCollection('SalaryEntity').find({"Month" : "201601"}) db.getColle ...

  2. Java的位运算符—— 与(&)、非(~)、或(|)、异或(^)

    位运算符主要针对二进制,它包括了:“与”.“非”.“或”.“异或”.从表面上看似乎有点像逻辑运算符,但逻辑运算符是针对两个关系运算符来进行逻辑运算,而位运算符主要针对两个二进制数的位进行逻辑运算.下面 ...

  3. POJ3280(DP)

    题目大意是说一个字符串,每插入或者删除一个字符都需要一定的代价,问怎样可以使这个字符串变成一个回文串,且花费最小. 首先明确的就是如果已经将区间[i,j]整理成为一个回文串(不管中间有多少个字符或者是 ...

  4. HDU 5437 Alisha’s Party (优先队列模拟)

    题意:邀请k个朋友,每个朋友带有礼物价值不一,m次开门,每次开门让一定人数p(如果门外人数少于p,全都进去)进来,当最后所有人都到了还会再开一次门,让还没进来的人进来,每次都是礼物价值高的人先进.最后 ...

  5. How Tomcat Works(十四)

    我们已经知道,在tomcat中有四种类型的servlet容器,分别为Engine.Host.Context 和Wrapper,本文接下来对tomcat中Wrapper接口的标准实现进行说明. 对于每个 ...

  6. HDU 2437 Jerboas (剪枝搜索)

    题意:给定一幅图,图上有两种点T,P.......一只跳鼠在一个T点作为起始点,它想通过图上的路到达某个P点,P点满足如下要求: (1).到达P点的途中路径权值为k的倍数 (2).尽量让路径权值取最小 ...

  7. CFileDialog 、CFile 如何进行文件操作 [转]

    如何进行文件操作 [1]显示对话框,取得文件名 CString FilePathName; CFileDialog dlg(TRUE);//TRUE为OPEN对话框,FALSE为SAVE AS对话框 ...

  8. 学习C++的一些问题总结

    C++ 问题 (一) int main() { int i,j,m,n; i=8; j=10; m=++i+j++;  //++i是先递加再使用,j++是先使用再递加,故:9+10=19 n=++i+ ...

  9. 启动Tomcat的时候遇到错误

    严重: IOException while loading persisted sessions: java.io.EOFException java.io.EOFException at java. ...

  10. HttpRuntime.Cache 失效

    最近做一个报纸内容类网站,为了提高响应速度,将首页各栏目以及二级栏目中Part文献列表存储在HttpRuntime.Cache缓存中,发布后发现问题,刚插入的缓存很快就失效,本机调试没有问题. 由于H ...