前言
- 多線程的應用在Android開發中是非常常見的,常用方法主要有:
- 繼承Thread類
- 實作Runnable接口
- AsyncTask
- Handler
- HandlerThread
- IntentService
- 今天,我将全面解析多線程其中一種常見用法:
IntentService
目錄
1. 定義
Android
裡的一個封裝類,繼承四大元件之一的
Service
2. 作用
處理異步請求 & 實作多線程
3. 使用場景
線程任務 需 按順序、在背景執行
- 最常見的場景:離線下載下傳
- 不符合多個資料同時請求的場景:所有的任務都在同一個
裡執行
Thread looper
4. 使用步驟
步驟1:定義
IntentService
的子類
需 傳入線程名稱、複寫 onHandleIntent()
方法
步驟2:在
Manifest.xml
中注冊服務
步驟3:在
Activity
中開啟
Service
服務
5. 執行個體應用
步驟1:定義 IntentService
的子類
IntentService
傳入線程名稱、複寫 onHandleIntent()
方法
public class myIntentService extends IntentService {
/**
* 在構造函數中傳入線程名字
**/
public myIntentService() {
// 調用父類的構造函數
// 參數 = 工作線程的名字
super("myIntentService");
}
/**
* 複寫onHandleIntent()方法
* 根據 Intent實作 耗時任務 操作
**/
@Override
protected void onHandleIntent(Intent intent) {
// 根據 Intent的不同,進行不同的事務處理
String taskName = intent.getExtras().getString("taskName");
switch (taskName) {
case "task1":
Log.i("myIntentService", "do task1");
break;
case "task2":
Log.i("myIntentService", "do task2");
break;
default:
break;
}
}
@Override
public void onCreate() {
Log.i("myIntentService", "onCreate");
super.onCreate();
}
/**
* 複寫onStartCommand()方法
* 預設實作 = 将請求的Intent添加到工作隊列裡
**/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("myIntentService", "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
Log.i("myIntentService", "onDestroy");
super.onDestroy();
}
}
步驟2:在Manifest.xml中注冊服務
<service android:name=".myIntentService">
<intent-filter >
<action android:name="cn.scu.finch"/>
</intent-filter>
</service>
步驟3:在Activity中開啟Service服務
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 同一服務隻會開啟1個工作線程
// 在onHandleIntent()函數裡,依次處理傳入的Intent請求
// 将請求通過Bundle對象傳入到Intent,再傳入到服務裡
// 請求1
Intent i = new Intent("cn.scu.finch");
Bundle bundle = new Bundle();
bundle.putString("taskName", "task1");
i.putExtras(bundle);
startService(i);
// 請求2
Intent i2 = new Intent("cn.scu.finch");
Bundle bundle2 = new Bundle();
bundle2.putString("taskName", "task2");
i2.putExtras(bundle2);
startService(i2);
startService(i); //多次啟動
}
}
測試結果
6. 源碼分析
-
的源碼工作流程如下:IntentService
特别注意:若啟動
IntentService
多次,那麼 每個耗時操作 則 以隊列的方式 在
IntentService
的
onHandleIntent
回調方法中依次執行,執行完自動結束
接下來,我們将通過 源碼分析 解決以下問題:
-
如何單獨開啟1個新的工作線程IntentService
-
如何通過IntentService
将Intent 傳遞給服務 & 依次插入到工作隊列中onStartCommand()
問題1:IntentService如何單獨開啟1個新的工作線程
主要分析内容 =
IntentService
源碼中的
onCreate()
方法
@Override
public void onCreate() {
super.onCreate();
// 1. 通過執行個體化andlerThread建立線程 & 啟動;故 使用IntentService時,不需額外建立線程
// HandlerThread繼承自Thread,内部封裝了 Looper
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
// 2. 獲得工作線程的 Looper & 維護自己的工作隊列
mServiceLooper = thread.getLooper();
// 3. 建立mServiceHandler & 綁定上述獲得Looper
// 建立的Handler 屬于工作線程 ->>分析1
mServiceHandler = new ServiceHandler(mServiceLooper);
}
/**
* 分析1:ServiceHandler源碼分析
**/
private final class ServiceHandler extends Handler {
// 構造函數
public ServiceHandler(Looper looper) {
super(looper);
}
// IntentService的handleMessage()把接收的消息交給onHandleIntent()處理
@Override
public void handleMessage(Message msg) {
// onHandleIntent 方法在工作線程中執行
// onHandleIntent() = 抽象方法,使用時需重寫 ->>分析2
onHandleIntent((Intent)msg.obj);
// 執行完調用 stopSelf() 結束服務
stopSelf(msg.arg1);
}
}
/**
* 分析2: onHandleIntent()源碼分析
* onHandleIntent() = 抽象方法,使用時需重寫
**/
@WorkerThread
protected abstract void onHandleIntent(Intent intent);
問題2:IntentService 如何通過onStartCommand() 将Intent 傳遞給服務 & 依次插入到工作隊列中
/**
* onStartCommand()源碼分析
* onHandleIntent() = 抽象方法,使用時需重寫
**/
public int onStartCommand(Intent intent, int flags, int startId) {
// 調用onStart()->>分析1
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
/**
* 分析1:onStart(intent, startId)
**/
public void onStart(Intent intent, int startId) {
// 1. 獲得ServiceHandler消息的引用
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
// 2. 把 Intent參數 包裝到 message 的 obj 發送消息中,
//這裡的Intent = 啟動服務時startService(Intent) 裡傳入的 Intent
msg.obj = intent;
// 3. 發送消息,即 添加到消息隊列裡
mServiceHandler.sendMessage(msg);
}
總結
從上面源碼可看出:
IntentService
本質 =
Handler
+
HandlerThread
:
- 通過
單獨開啟1個工作線程:HandlerThread
IntentService
- 建立1個内部
:Handler
ServiceHandler
- 綁定
與ServiceHandler
IntentService
- 通過
傳遞服務onStartCommand()
到intent
、依次插入ServiceHandler
到工作隊列中 & 逐個發送給Intent
onHandleIntent()
- 通過
依次處理所有onHandleIntent()
對象所對應的任務Intent
是以我們通過複寫& 在裡面 根據
onHandleIntent()
的不同進行不同線程操作 即可
Intent
7. 注意事項
注意事項1. 工作任務隊列 = 順序執行
即 若一個任務正在 IntentService
中執行,此時你再發送1個新的任務請求,這個新的任務會一直等待直到前面一個任務執行完畢後才開始執行
- 原因:
- 由于
隻會調用一次 = 隻會建立1個工作線程;onCreate()
- 當多次調用
時(即startService(Intent)
onStartCommand()
也會調用多次),其實不會建立新的工作線程,隻是把消息加入消息隊列中 & 等待執行。
3, 是以,多次啟動 IntentService 會按順序執行事件
若服務停止,則會清除消息隊列中的消息,後續的事件不執行
注意事項2:不建議通過 bindService() 啟動 IntentService
原因:
// 在IntentService中,onBind()`預設傳回null
@Override
public IBinder onBind(Intent intent) {
return null;
}
- 采用
啟動bindService()
的生命周期如下:IntentService
onCreate() ->> onBind() ->> onunbind()->> onDestory()
- 即,并不會調用
或onStart()
,故不會将消息發送到消息隊列,那麼onHandleIntent()将不會回調,即無法實作多線程的操作onStartcommand()
此時,你應該使用,而不是
Service
IntentService
8. 對比
8.1 IntentService與Service的差別
8.2 IntentService與其他線程的差別
9. 總結
- 本文主要 全面介紹了 多線程
用法 & 源碼IntentService
- 接下來,我會繼續講解
開發中關于多線程的知識,包括繼承Android
類、實作Thread
接口、Runnable
等等,感興趣的同學可以繼續關注carson_ho的微信公衆号Handler