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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容