序言
最近有用到IntentService,對IntentService的源碼做了一定的學習,所以今天來談談我對intentservice的理解,后面的內容我會從官方文檔對intentservice的解釋、我個人的理解、源碼分析三個方面做講解。
IntentService作為Service的子類,service作為android四大組件之一,它的作用自然不言而喻。所以講IntentService之前肯定要先說說Service。
什么是Service
A Service is an application component representing either an application's desire to perform a longer-running operation while not interacting with the user or to supply functionality for other applications to use. Each service class must have a corresponding <service> declaration in its package's AndroidManifest.xml. Services can be started with Context.startService() and [Context.bindService()](https://developer.android.google.cn/reference/android/content/Context.html#bindService(android.content.Intent, android.content.ServiceConnection, int))
.
Note that services, like other application objects, run in the main thread of their hosting process. This means that, if your service is going to do any CPU intensive (such as MP3 playback) or blocking (such as networking) operations, it should spawn its own thread in which to do that work. More information on this can be found in Processes and Threads. The IntentService
class is available as a standard implementation of Service that has its own thread where it schedules its work to be done.
What is a Service?
Most confusion about the Service class actually revolves around what it is not:
A Service is not a separate process. The Service object itself does not imply it is running in its own process; unless otherwise specified, it runs in the same process as the application it is part of.
A Service is not a thread. It is not a means itself to do work off of the main thread (to avoid Application Not Responding errors).
這是官方文檔對service的定義,下面是我對官方文檔的翻譯(英語水平有限,如有翻譯錯誤,還請見諒)
service是app組件,它代表app希望執行較長的運行操作而不與用戶交互或為其他應
用程序提供功能的愿望.service必須在AndroidManifest.xml中注冊。啟動service的方式有Context.startService()和Context.bindService()。
注意:
service是運行在app的主線程上的,這就意味著,如果你的service將執行任何cpu密集(如mp3播放)或阻止(如網絡)操作,應該放到另外的線程上去完成這項工作。
什么是服務?
關于服務類的大多數混亂實際上都圍繞著它不是什么
1.服務不是一個單獨的進程,服務本身不意味著它在自己的進程中運行,除非有特殊的說明(可在注冊的時候指定進程,它運行在app相同的進程中。
2.服務不是一個線程,這也不是意味著service的工作是在主線程上完成的(為了避免anr的發生);
我對service的理解
1.首先service是運行在主線程上的,所以 service上不能執行耗時操作,不然會導致anr。
2.同一個service在app中只有一個實例,類似單例。
3.什時候使用service呢?我的理解是,你需要做的工作要與可視界面(activity)解耦,也就是頁面的隱藏或是關閉不會影響到你的工作,這樣的工作你需要放到service中去,當然如果的耗時操作,你也是要在service中new thread的。
4.當然service還要一個很重要的作用就是作為兩個不同app之間通訊的媒介,這個用到的是AIDL可以參考https://developer.android.google.cn/guide/components/aidl.html/
什么是IntentService
IntentService is a base class for Service that handle asynchronous requests (expressed as Intent) on demand. Clients send requests through 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.
This "work queue processor" pattern is commonly used to offload tasks from an application's main thread. The IntentService class exists to simplify this pattern and take care of the mechanics. To use it, extend IntentService and implement onHandleIntent(Intent)
. IntentService will receive the Intents, launch a worker thread, and stop the service as appropriate.
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.
這是官方文檔對service的定義,下面是我對官方文檔的翻譯(英語水平有限,如有翻譯錯誤,還請見諒)
IntentService是Service的子類,按要求處理異步請求。通過startService發起異步任務,該服務根據需要啟動,使用一個工作線程依次處理每個異步任務,并在完成所有異步任務后關閉service。
這種“工作隊列處理器”模式通常用于從應用程序的主線程卸載任務,IntentService的存在就是方便開發者使用這種模式。使用方法:通過繼承IntentService類并實現 onHandleIntent(Intent)方法,IntentService接受到Intent后,啟動IntentService的HandlerThread處理異步任務,并且在適當的時候關閉自己。
所有請求都是在一個單獨的線程上處理的--它們可能需要盡可能長的時間(并且不會阻塞應用程序的主循環),但每次只處理一個請求。
我的理解
1.IntentService用來處理異步任務
2.IntentService中只有一個默認的HandlerThread線程用來處理異步任務,所有異步任務按發起順序以隊列的形式依次在HanderThread中完成,并且在所有任務完成自后自行關閉自己。
3.正如官方的解釋一樣,IntentService適合一些卸載任務、下載更新包的任務并安裝的任務。
源碼分析IntentService
如何實現隊列的形式依次處理異步任務
@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創建的時候默認生成一個HandlerThread(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);
}
}
onHandleIntent(Intent intent)是抽象方法,通過實現onHandleIntent方法,將異步任務放在onHandleIntent中實現,ServiceHandler接收到消息后 ,依次調用onHandleIntent方法和stopSelf方法,stopSelf(msg.arg1)只有在Service沒有其他任務時才會關閉自己。所以其實IntentService完成每個異步任務都會調用關閉自己的方法,只有在完成所有異步任務的時候才會正真的關閉自己。
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
/**
* You should not override this method for your IntentService. Instead,
* override {@link #onHandleIntent}, which the system calls when the IntentService
* receives a start request.
* @see android.app.Service#onStartCommand
*/
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
每次startService()都會往HandlerThread的消息隊列中發送一個消息mServiceHandler.sendMessage(msg)
來個例子
public class MyIntentService extends IntentService {
public static final String TAG = "MyIntentService";
public MyIntentService() {
//調用父類的構造函數
//構造函數參數=工作線程的名字
super("MyIntentService");
}
/*復寫onHandleIntent()方法*/
//實現耗時任務的操作
@Override
protected void onHandleIntent(Intent intent) {
//根據Intent的不同進行不同的事務處理
String taskName = intent.getExtras().getString("taskName");
switch (taskName) {
case "task1":
Log.e(TAG, "do task1");
Log.e(TAG, "onHandleIntent()所在線程:"+android.os.Process.myTid());
break;
case "task2":
Log.e(TAG, "do task2");
Log.e(TAG, "onHandleIntent()所在線程:"+android.os.Process.myTid());
break;
default:
break;
}
}
@Override
public void onCreate() {
super.onCreate();
Log.e(TAG, "onCreate");
}
/*復寫onStartCommand()方法*/
//默認實現將請求的Intent添加到工作隊列里
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(TAG, "onStartCommand");
Log.e(TAG,"MyIntentService所在的線程是"+android.os.Process.myTid());
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
Log.e(TAG,"onDestroy");
super.onDestroy();
}
}
public class MainActivity extends AppCompatActivity {
public static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//同一服務只會開啟一個工作線程
//在onHandleIntent函數里依次處理intent請求。
findViewById(R.id.test).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e(TAG, "MainActivity所在的線程:"+android.os.Process.myTid());
Intent i = new Intent(v.getContext(),MyIntentService.class);
Bundle bundle = new Bundle();
bundle.putString("taskName", "task1");
i.putExtras(bundle);
startService(i);
Intent i2= new Intent(v.getContext(),MyIntentService.class);
Bundle bundle2 = new Bundle();
bundle2.putString("taskName", "task2");
i2.putExtras(bundle2);
startService(i2);
startService(i); //多次啟動
}
});
}
}
從打印的日志就能看出,主線程id是8211 IntentService的onStartCommand()也是在主線程8211中執行的,而兩個task都是在另外的一個線程8928中執行的,而且在同一個線程中根據發起順序依次執行。
到這里基本就結束了,希望我寫的東西能對大家有所幫助,如有不足的地方希望大家多多指教。