一、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的使用和源碼解析上。
首先看下效果圖
效果圖
每當我們點擊一次按鈕,會將一個任務交給后臺的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