一、IntentService简介
IntentService是Service的子类,比普通的Service增加了额外的功能。先看Service本身存在两个问题:
Service不会专门启动一条单独的进程,Service与它所在应用位于同一个进程中;
Service也不是专门一条新线程,因此不应该在Service中直接处理耗时的任务;
二、IntentService特征
会创建独立的worker线程来处理所有的Intent请求;
会创建独立的worker线程来处理onHandleIntent()方法实现的代码,无需处理多线程问题;
所有请求处理完成后,IntentService会自动停止,无需调用stopSelf()方法停止Service;
为Service的onBind()提供默认实现,返回null;
为Service的onStartCommand提供默认实现,将请求Intent添加到队列中;
三、使用步骤(详情参考Service项目)
继承IntentService类,并重写onHandleIntent()方法即可;
MainActivity.java文件
[java]view plaincopy
publicclassMainActivityextendsActivity {
@Override
protectedvoidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
publicvoidstartService(View source) {
// 创建所需要启动的Service的Intent
Intent intent =newIntent(this, MyService.class);
startService(intent);
}
publicvoidstartIntentService(View source) {
// 创建需要启动的IntentService的Intent
Intent intent =newIntent(this, MyIntentService.class);
startService(intent);
}
}
MyIntentService.java文件
[java]view plaincopy
publicclassMyIntentServiceextendsIntentService {
publicMyIntentService() {
super("MyIntentService");
}
@Override
protectedvoidonHandleIntent(Intent intent) {
// IntentService会使用单独的线程来执行该方法的代码
// 该方法内执行耗时任务,比如下载文件,此处只是让线程等待20秒
longendTime = System.currentTimeMillis() +20*1000;
System.out.println("onStart");
while(System.currentTimeMillis() < endTime) {
synchronized(this) {
try{
wait(endTime - System.currentTimeMillis());
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("----耗时任务执行完成---");
}
}
MyService.java文件
[java]view plaincopy
publicclassMyServiceextendsService {
@Override
publicIBinder onBind(Intent arg0) {
returnnull;
}
@Override
publicintonStartCommand(Intent intent,intflags,intstartId) {
// 该方法内执行耗时任务可能导致ANR(Application Not Responding)异常
longendTime = System.currentTimeMillis() +20*1000;
System.out.println("onStart");
while(System.currentTimeMillis() < endTime) {
synchronized(this) {
try{
wait(endTime - System.currentTimeMillis());
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("----耗时任务执行完成---");
returnSTART_STICKY;
}
}
运行上述代码,启动MyIntentService的会使用单独的worker线程,因此不会阻塞前台的UI线程;而MyService会。
在Android开发中,我们或许会碰到这么一种业务需求,一项任务分成几个子任务,子任务按顺序先后执行,子任务全部执行完后,这项任务才算成功。那么,利用几个子线程顺序执行是可以达到这个目的的,但是每个线程必须去手动控制,而且得在一个子线程执行完后,再开启另一个子线程。或者,全部放到一个线程中让其顺序执行。这样都可以做到,但是,如果这是一个后台任务,就得放到Service里面,由于Service和Activity是同级的,所以,要执行耗时任务,就得在Service里面开子线程来执行。那么,有没有一种简单的方法来处理这个过程呢,答案就是IntentService。
什么是IntentService,首先看看官方的解释:
IntentService is a base class forServices that handle asynchronous requests (expressed asIntents) on demand. Clients send requests throughstartService(Intent)calls; the service is started as needed, handles each Intent in turn using a worker thread, and stops itself when it runs out of work
简单说,IntentService是继承于Service并处理异步请求的一个类,在IntentService内有一个工作线程来处理耗时操作,启动IntentService的方式和启动传统Service一样,同时,当任务执行完后,IntentService会自动停止,而不需要我们去手动控制。另外,可以启动IntentService多次,而每一个耗时操作会以工作队列的方式在IntentService的onHandleIntent回调方法中执行,并且,每次只会执行一个工作线程,执行完第一个再执行第二个,以此类推。
还有一个说明是:
All requests are handled on a single worker thread -- they may take as long as necessary (and will not block the application's main loop), but only one request will be processed at a time.
大致意思是:所有请求都在一个单线程中,不会阻塞应用程序的主线程(UI Thread),同一时间只处理一个请求。
那么,用IntentService有什么好处呢?首先,我们省去了在Service中手动开线程的麻烦,第二,当操作完成时,我们不用手动停止Service,第三,it's so easy to use!
ok,接下来让我们来看看如何使用,我写了一个Demo来模拟两个耗时操作,Operation1与Operation2,先执行1,2必须等1执行完才能执行:
新建工程,新建一个继承IntentService的类,我这里是IntentServiceDemo.java
[java]view plaincopy
publicclassIntentServiceDemoextendsIntentService {
publicIntentServiceDemo() {
//必须实现父类的构造方法
super("IntentServiceDemo");
}
@Override
publicIBinder onBind(Intent intent) {
System.out.println("onBind");
returnsuper.onBind(intent);
}
@Override
publicvoidonCreate() {
System.out.println("onCreate");
super.onCreate();
}
@Override
publicvoidonStart(Intent intent,intstartId) {
System.out.println("onStart");
super.onStart(intent, startId);
}
@Override
publicintonStartCommand(Intent intent,intflags,intstartId) {
System.out.println("onStartCommand");
returnsuper.onStartCommand(intent, flags, startId);
}
@Override
publicvoidsetIntentRedelivery(booleanenabled) {
super.setIntentRedelivery(enabled);
System.out.println("setIntentRedelivery");
}
@Override
protectedvoidonHandleIntent(Intent intent) {
//Intent是从Activity发过来的,携带识别参数,根据参数不同执行不同的任务
String action = intent.getExtras().getString("param");
if(action.equals("oper1")) {
System.out.println("Operation1");
}elseif(action.equals("oper2")) {
System.out.println("Operation2");
}
try{
Thread.sleep(2000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
@Override
publicvoidonDestroy() {
System.out.println("onDestroy");
super.onDestroy();
}
}
我把生命周期方法全打印出来了,待会我们来看看它执行的过程是怎样的。接下来是Activity,在Activity中来启动IntentService:
[java]view plaincopy
publicclassTestActivityextendsActivity {
/** Called when the activity is first created. */
@Override
publicvoidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//可以启动多次,每启动一次,就会新建一个work thread,但IntentService的实例始终只有一个
//Operation 1
Intent startServiceIntent =newIntent("com.test.intentservice");
Bundle bundle =newBundle();
bundle.putString("param","oper1");
startServiceIntent.putExtras(bundle);
startService(startServiceIntent);
//Operation 2
Intent startServiceIntent2 =newIntent("com.test.intentservice");
Bundle bundle2 =newBundle();
bundle2.putString("param","oper2");
startServiceIntent2.putExtras(bundle2);
startService(startServiceIntent2);
}
}
最后,别忘了配置Service,因为它继承于Service,所以,它还是一个Service,一定要配置,否则是不起作用的,开始我就是忘了,结果半天没反应。
[html]view plaincopy
ok,最后来看看执行结果:
从结果可以看到,onCreate方法只执行了一次,而onStartCommand和onStart方法执行了两次,开启了两个Work Thread,这就证实了之前所说的,启动多次,但IntentService的实例只有一个,这跟传统的Service是一样的。Operation1也是先于Operation2打印,并且我让两个操作间停顿了2s,最后是onDestroy销毁了IntentService。
一 概述
大家都清楚,在Android的开发中,凡是遇到耗时的操作尽可能的会交给Service去做,比如我们上传多张图,上传的过程用户可能将应用置于后台,然后干别的去了,我们的Activity就很可能会被杀死,所以可以考虑将上传操作交给Service去做,如果担心Service被杀,还能通过设置startForeground(int, Notification)方法提升其优先级。
那么,在Service里面我们肯定不能直接进行耗时操作,一般都需要去开启子线程去做一些事情,自己去管理Service的生命周期以及子线程并非是个优雅的做法;好在Android给我们提供了一个类,叫做IntentService,我们看下注释。
IntentService is a base class for {@link Service}s that handle asynchronous
requests (expressed as {@link Intent}s) on demand. Clients send requests
through {@link android.content.Context#startService(Intent)} calls; the
service is started as needed, handles each Intent in turn using a worker
thread, and stops itself when it runs out of work.
意思说IntentService是一个基于Service的一个类,用来处理异步的请求。你可以通过startService(Intent)来提交请求,该Service会在需要的时候创建,当完成所有的任务以后自己关闭,且请求是在工作线程处理的。
这么说,我们使用了IntentService最起码有两个好处,一方面不需要自己去new Thread了;另一方面不需要考虑在什么时候关闭该Service了。
好了,那么接下来我们就来看一个完整的例子。
二 IntentService的使用
我们就来演示一个多个图片上传的案例,当然我们会模拟上传的耗时,毕竟我们的重心在IntentService的使用和源码解析上。
首先看下效果图
效果图
每当我们点击一次按钮,会将一个任务交给后台的Service去处理,后台的Service每处理完成一个请求就会反馈给Activity,然后Activity去更新UI。当所有的任务完成的时候,后台的Service会退出,不会占据任何内存。
Service
packagecom.zhy.blogcodes.intentservice;importandroid.app.IntentService;importandroid.content.Context;importandroid.content.Intent;importandroid.util.Log;publicclassUploadImgServiceextendsIntentService{privatestaticfinalString ACTION_UPLOAD_IMG ="com.zhy.blogcodes.intentservice.action.UPLOAD_IMAGE";publicstaticfinalString EXTRA_IMG_PATH ="com.zhy.blogcodes.intentservice.extra.IMG_PATH";publicstaticvoidstartUploadImg(Context context, String path) { Intent intent =newIntent(context, UploadImgService.class); intent.setAction(ACTION_UPLOAD_IMG); intent.putExtra(EXTRA_IMG_PATH, path); context.startService(intent); }publicUploadImgService() {super("UploadImgService"); }@OverrideprotectedvoidonHandleIntent(Intent intent) {if(intent !=null) {finalString action = intent.getAction();if(ACTION_UPLOAD_IMG.equals(action)) {finalString path = intent.getStringExtra(EXTRA_IMG_PATH); handleUploadImg(path); } } }privatevoidhandleUploadImg(String path) {try{//模拟上传耗时Thread.sleep(3000); Intent intent =newIntent(IntentServiceActivity.UPLOAD_RESULT); intent.putExtra(EXTRA_IMG_PATH, path); sendBroadcast(intent); }catch(InterruptedException e) { e.printStackTrace(); } }@OverridepublicvoidonCreate() {super.onCreate(); Log.e("TAG","onCreate"); }@OverridepublicvoidonDestroy() {super.onDestroy(); Log.e("TAG","onDestroy"); }
代码很短,主要就是继承IntentService,然后复写onHandleIntent方法,根据传入的intent来选择具体的操作。startUploadImg是我写的一个辅助方法,省的每次都去构建Intent,startService了。
Activity
packagecom.zhy.blogcodes.intentservice;importandroid.content.BroadcastReceiver;importandroid.content.Context;importandroid.content.Intent;importandroid.content.IntentFilter;importandroid.os.Bundle;importandroid.support.v7.app.AppCompatActivity;importandroid.view.Menu;importandroid.view.MenuItem;importandroid.view.View;importandroid.widget.LinearLayout;importandroid.widget.TextView;importcom.zhy.blogcodes.R;publicclassIntentServiceActivityextendsAppCompatActivity{publicstaticfinalString UPLOAD_RESULT ="com.zhy.blogcodes.intentservice.UPLOAD_RESULT";privateLinearLayout mLyTaskContainer;privateBroadcastReceiver uploadImgReceiver =newBroadcastReceiver() {@OverridepublicvoidonReceive(Context context, Intent intent) {if(intent.getAction() == UPLOAD_RESULT) { String path = intent.getStringExtra(UploadImgService.EXTRA_IMG_PATH); handleResult(path); } } };privatevoidhandleResult(String path) { TextView tv = (TextView) mLyTaskContainer.findViewWithTag(path); tv.setText(path +" upload success ~~~ "); }@OverrideprotectedvoidonCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState); setContentView(R.layout.activity_intent_service); mLyTaskContainer = (LinearLayout) findViewById(R.id.id_ll_taskcontainer); registerReceiver(); }privatevoidregisterReceiver() { IntentFilter filter =newIntentFilter(); filter.addAction(UPLOAD_RESULT); registerReceiver(uploadImgReceiver, filter); }inti =0;publicvoidaddTask(View view) {//模拟路径String path ="/sdcard/imgs/"+ (++i) +".png"; UploadImgService.startUploadImg(this, path); TextView tv =newTextView(this); mLyTaskContainer.addView(tv); tv.setText(path +" is uploading ..."); tv.setTag(path); }@OverrideprotectedvoidonDestroy() {super.onDestroy(); unregisterReceiver(uploadImgReceiver); }}
Activity中,每当我点击一次按钮调用addTask,就回模拟创建一个任务,然后交给IntentService去处理。
注意,当Service的每个任务完成的时候,会发送一个广播,我们在Activity的onCreate和onDestroy里面分别注册和解注册了广播;当收到广播则更新指定的UI。
参考:http://blog.csdn.net/p106786860/article/details/17885115
http://blog.csdn.net/ryantang03/article/details/8146154
http://blog.csdn.net/lmj623565791/article/details/47143563