IntentService

一、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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容