目錄:
1. 前言
本文是對 IntentService 的深入學習,包含其基本使用方法、IntentService更新處理UI工作,以及對 IntentService 的源碼分析。
1.1 定義
IntentService 是 Service 的子類,繼承于 Service 類,用于處理后臺異步請求任務。
用戶通過調用 Context.StartService(Intent) 發送請求,服務根據請求啟動,使用工作線程依次處理每個 Intent任務請求,并在處理完所有任務請求后自身停止服務。
使用時,擴展 IntentService ,即實現它的子類并具體實現 onHandleIntent(android.content.Intent) 方法。IntentService 接收 Intent,啟動工作線程,并在適當時機停止服務。
所有的請求都在同一個工作線程上處理,一次處理一個請求,所以處理完所以的請求可能會花費很長的時間,但由于 IntentService 是另外創建的線程來工作,所以保證不會阻止App 的主線程,防止 App 出現 ANR。
1.2 使用場景
用于處理后臺長時間的耗時操作,如:下載文件、播放音樂...
2. 使用方法
由于 IntentService 是抽象類,所以在實際使用中,我們需要創建一個 IntentService 子類來具體實現。
使用步驟分為兩步:
- 創建 IntentService 子類,并在清單文件中注冊。
- 在 Activity 中通過調用 startService(Intent) 方法發送任務請求。
例子:
我們模擬在后臺進行下載以及讀取文件的耗時操作,觀察打印出來的 Log 信息。
如下所示:
IntentService 的執行流程為:onCreate() -> onStartCommand() -> onStart() -> onHandleIntent -> ···處理完所有請求··· -> onDestroy()
具體代碼如下所示:分 Java & Kotlin 兩個版本展示。
2.1 Java版本
步驟一:創建 MyIntentService
public class MyIntentService extends IntentService {
private static final String TAG = "MyIntentService";
public static final String DOWNLOAD_ACTION = "DOWNLOAD_ACTION";
public static final String READ_ACTION = "READ_ACTION";
public static final String TEST_AUTHOR = "TEST_AUTHOR";
public MyIntentService() {
super("MyIntentService");
}
/**
* 進行一些耗時操作
* @param intent 通過startService(Intent intent)方法傳入
*/
@Override
protected void onHandleIntent(Intent intent) {
Log.e(TAG, "onHandleIntent: ");
if (intent != null) {
final String action = intent.getAction();
String author = intent.getExtras().getString(TEST_AUTHOR);
//模擬下載動作
if (DOWNLOAD_ACTION.equals(action)) {
for (int i = 0; i < 5; i++) {
try {
//線程等待1s,模擬耗時操作
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e(TAG, author + " " + action + " " + i);
}
}
//模擬讀操作
if (READ_ACTION.equals(action)) {
for (int i = 0; i < 5; i++) {
try {
//線程等待2s,模擬耗時操作
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e(TAG, author + " " + action + " " + i);
}
}
}
}
@Override
public void onCreate() {
super.onCreate();
Toast.makeText(this, "onCreate", Toast.LENGTH_SHORT).show();
Log.e(TAG, "onCreate: ");
}
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
Toast.makeText(this, "onStartCommand", Toast.LENGTH_SHORT).show();
Log.e(TAG, "onStartCommand: ");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
super.onStart(intent, startId);
Log.e(TAG, "onStart: ");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.e(TAG, "onDestroy: ");
Toast.makeText(this, "onDestroy", Toast.LENGTH_SHORT).show();
}
}
并記得在清單文件中注冊,這一步你也可以通過 Android Studio 來幫我們完成
<service
android:name=".intentservice.MyIntentService"
android:exported="false">
</service>
步驟二:通過 startService(Intent) 發送任務請求
public class IntentServiceActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_intent_service);
//模擬 Jere 做下載動作
Intent intent = new Intent(this, MyIntentService.class);
intent.setAction(MyIntentService.DOWNLOAD_ACTION);
intent.putExtra(MyIntentService.TEST_AUTHOR, "Jere");
startService(intent);
//模擬 James 做讀取動作
Intent jamesIntent = new Intent(this, MyIntentService.class);
jamesIntent.setAction(MyIntentService.READ_ACTION);
jamesIntent.putExtra(MyIntentService.TEST_AUTHOR, "James");
startService(jamesIntent);
}
}
2.2 Koltin版本
步驟一:創建 MyIntentService
class MyIntentService : IntentService("MyIntentService") {
companion object {
private val TAG: String = "MyIntentService"
val DOWNLOAD_ACTION = "DOWNLOAD_ACTION"
val READ_ACTION = "READ_ACTION"
val TEST_AUTHOR = "TEST_AUTHOR"
}
override fun onHandleIntent(intent: Intent?) {
val action: String? = intent?.action
val author: String? = intent?.extras?.getString(TEST_AUTHOR)
when (intent?.action) {
DOWNLOAD_ACTION -> {
for (i in 0..10) {
Thread.sleep(1000)
Log.e(TAG, "$author $action $I")
}
}
READ_ACTION -> {
for (j in 0..10) {
Thread.sleep(2000)
Log.e(TAG, "$author $action $j")
}
}
}
}
override fun onCreate() {
super.onCreate()
Log.e(TAG, "onCreate")
Toast.makeText(this, "conCreate", Toast.LENGTH_SHORT).show()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.e(TAG, "onStartCommand")
Toast.makeText(this, "onStartCommand", Toast.LENGTH_SHORT).show()
return super.onStartCommand(intent, flags, startId)
}
override fun onDestroy() {
super.onDestroy()
Log.e(TAG, "onDestroy")
Toast.makeText(this, "onDestroy", Toast.LENGTH_SHORT).show()
}
}
在清單文件中注冊:
<service
android:name=".multiplethread.intentservice.MyIntentService"
android:exported="false">
</service>
步驟二:通過 startService(Intent) 發送任務請求
class TestIntentServiceActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test_intent_service)
val intent = Intent()
intent.setClass(this, MyIntentService::class.java)
intent.action = MyIntentService.DOWNLOAD_ACTION
intent.putExtra(MyIntentService.TEST_AUTHOR, "Jere")
startService(intent)
val jamesIntent = Intent()
jamesIntent.setClass(this, MyIntentService::class.java)
jamesIntent.action = MyIntentService.READ_ACTION
jamesIntent.putExtra(MyIntentService.TEST_AUTHOR, "James")
startService(jamesIntent)
}
}
2.3 IntentService更新處理UI工作
思考: 在上面的例子中,我們是通過在打印出來的日志信息來觀察后臺異步任務的執行情況,那 IntentService 可以處理 UI 工作嗎?
答: IntentService 是服務端,肯定是不可以處理 UI 工作的,但是他可以通過其對應的客戶端,也就是 Activity 來更新處理 UI 工作。
客戶端與服務端之間的通信,我們使用 Messenger ,關于 Messenger 內容請看另一篇博客 Android Messenger初探,具體實現如下所示:
例子: 我們在 IntentService 模擬下載任務,通過 ProgressBar 顯示下載進度。
效果圖如下所示:
具體代碼如下所示:分Java & Kotlin 兩個版本展示。
2.3.1 Java版本
- 在 Activity 中發送請求時,新建一個 Messenger 對象,傳遞給 IntentService。
public class IntentServiceActivity extends AppCompatActivity {
private ProgressBar downloadProgressBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_intent_service);
downloadProgressBar = findViewById(R.id.download_progress_bar);
//用于創建 Messenger,接收 IntentService 回復的消息
MessengerHandler messengerHandler = new MessengerHandler(this);
//模擬 Jere 做下載動作
Intent intent = new Intent(this, MyIntentService.class);
intent.setAction(MyIntentService.DOWNLOAD_ACTION);
intent.putExtra(MyIntentService.TEST_AUTHOR, "Jere");
//將 Messenger 傳遞給 IntentService,讓其回復消息回來
intent.putExtra(MyIntentService.TEST_MESSENGER, new Messenger(messengerHandler));
startService(intent);
}
/**
* 用于創建 Messenger 對象
*
* 靜態內部類,防止內存泄漏
*/
public static class MessengerHandler extends Handler {
private WeakReference<IntentServiceActivity> weakReference;
MessengerHandler(IntentServiceActivity activity) {
weakReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
//msg 為 IntentService 回復的消息,包含 Bundle 等信息。
Bundle bundle = msg.getData();
//獲取 IntentService 傳遞過來的 下載進度 參數
int downloadProgressBarValue = bundle.getInt(MyIntentService.DOWNLOAD_PROGRESS_VALUE_KEY);
//將下載進度設置成 ProgressBar 的進度,顯示出來。
IntentServiceActivity activity = weakReference.get();
if (activity != null && !activity.isFinishing()) {
activity.downloadProgressBar.setProgress(downloadProgressBarValue);
}
}
}
}
- 在 IntentService 中獲取 Messenger 對象,并回復消息給 Activity。
public class MyIntentService extends IntentService {
private static final String TAG = "MyIntentService";
public static final String DOWNLOAD_ACTION = "DOWNLOAD_ACTION";
public static final String READ_ACTION = "READ_ACTION";
public static final String TEST_AUTHOR = "TEST_AUTHOR";
public static final String TEST_MESSENGER = "TEST_MESSENGER";
public static final String DOWNLOAD_PROGRESS_VALUE_KEY = "DOWNLOAD_PROGRESS_VALUE";
public MyIntentService() {
super("MyIntentService");
}
/**
* 進行一些耗時操作
* @param intent 通過startService(Intent intent)方法傳入
*/
@Override
protected void onHandleIntent(Intent intent) {
Log.e(TAG, "onHandleIntent: ");
if (intent != null) {
final String action = intent.getAction();
String author = intent.getExtras().getString(TEST_AUTHOR);
//模擬下載動作
if (DOWNLOAD_ACTION.equals(action)) {
for (int i = 0; i <= 6; i++) {
try {
//線程等待1s,模擬耗時操作
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e(TAG, author + " " + action + " " + i);
//獲取從 Activity 傳入的 Messenger
Messenger messenger = (Messenger) intent.getExtras().get(TEST_MESSENGER);
//新建消息,設置下載進度參數
Message msg = new Message();
Bundle bundle = new Bundle();
bundle.putInt(DOWNLOAD_PROGRESS_VALUE_KEY, i);
msg.setData(bundle);
try {
//回復消息
messenger.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
}
@Override
public void onCreate() {
super.onCreate();
Toast.makeText(this, "onCreate", Toast.LENGTH_SHORT).show();
Log.e(TAG, "onCreate: ");
}
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
Toast.makeText(this, "onStartCommand", Toast.LENGTH_SHORT).show();
Log.e(TAG, "onStartCommand: ");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
super.onStart(intent, startId);
Toast.makeText(this, "onStart", Toast.LENGTH_SHORT).show();
Log.e(TAG, "onStart: ");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.e(TAG, "onDestroy: ");
Toast.makeText(this, "onDestroy", Toast.LENGTH_SHORT).show();
}
}
這樣就實現了 IntentService 更新處理 UI 工作,具體效果看上圖 IntentService更新處理UI工作。
2.3.2 Kotlin版本
同樣,Kotlin的實現方式為:
- 在 Activity 中發送請求時,新建一個 Messenger 對象,傳遞給 IntentService。
class TestIntentServiceActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test_intent_service)
val intent = Intent()
intent.setClass(this, MyIntentService::class.java)
intent.action = MyIntentService.DOWNLOAD_ACTION_KEY
intent.putExtra(MyIntentService.TEST_AUTHOR_KEY, "Jere")
//將 Messenger 傳遞給 IntentService, 使其傳遞消息回來,實現客戶端與服務端之間進行溝通
intent.putExtra(MyIntentService.TEST_MESSENGER_KEY, Messenger(MessengerHandler(this)))
startService(intent)
}
/**
* 此 Handler 用于創建 Messenger 對象,接收 IntentService 回復回來的消息
*
* 靜態內部類,防止內存泄漏
*/
class MessengerHandler(activity: TestIntentServiceActivity) : Handler() {
var weakReference: WeakReference<TestIntentServiceActivity> = WeakReference(activity)
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
//msg 為 IntentService 回復的消息,包含 Bundle 等信息。
val bundle: Bundle = msg.data
//獲取 IntentService 傳遞過來的 下載進度 參數
val downloadProgressBarValue: Int =
bundle.get(MyIntentService.DOWNLOAD_PROGRESS_BAR_VALUE_KEY) as Int
val activity: TestIntentServiceActivity? = weakReference.get()
//將下載進度設置成 ProgressBar 的進度,顯示出來。
if (activity != null && !activity.isFinishing) {
activity.intentServiceDownloadProgressBar.progress = downloadProgressBarValue
}
}
}
}
- 在 IntentService 中獲取 Messenger 對象,并回復消息給 Activity。
class MyIntentService : IntentService("MyIntentService") {
companion object {
private val TAG: String = "MyIntentService"
val DOWNLOAD_ACTION_KEY = "DOWNLOAD_ACTION"
val READ_ACTION_KEY = "READ_ACTION"
val TEST_AUTHOR_KEY = "TEST_AUTHOR"
val TEST_MESSENGER_KEY = "TEST_MESSENGER"
val DOWNLOAD_PROGRESS_BAR_VALUE_KEY = "DOWNLOAD_PROGRESS_BAR_VALUE"
}
override fun onHandleIntent(intent: Intent?) {
val action: String? = intent?.action
val author: String? = intent?.extras?.getString(TEST_AUTHOR_KEY)
when (intent?.action) {
DOWNLOAD_ACTION_KEY -> {
for (i in 0..6) {
Thread.sleep(1000)
Log.e(TAG, "$author $action $I")
//獲取從 Activity 中傳入的 Messenger 對象
val messenger: Messenger = intent.extras?.get(TEST_MESSENGER_KEY) as Messenger
//新建一個 Message 對象
val msg: Message = Message()
//為 Message 對象設置 下載進度 參數
val bundle: Bundle = Bundle()
bundle.putInt(DOWNLOAD_PROGRESS_BAR_VALUE_KEY, i)
msg.data = bundle
//Messenger 回復消息給 Activity
messenger.send(msg)
}
}
READ_ACTION_KEY -> {
for (j in 0..10) {
Thread.sleep(2000)
Log.e(TAG, "$author $action $j")
}
}
}
}
override fun onCreate() {
super.onCreate()
Log.e(TAG, "onCreate")
Toast.makeText(this, "onCreate", Toast.LENGTH_SHORT).show()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.e(TAG, "onStartCommand")
Toast.makeText(this, "onStartCommand", Toast.LENGTH_SHORT).show()
return super.onStartCommand(intent, flags, startId)
}
override fun onStart(intent: Intent?, startId: Int) {
super.onStart(intent, startId)
Log.e(TAG, "onStart")
Toast.makeText(this, "onStart", Toast.LENGTH_SHORT).show()
}
override fun onDestroy() {
super.onDestroy()
Log.e(TAG, "onDestroy")
Toast.makeText(this, "onDestroy", Toast.LENGTH_SHORT).show()
}
}
具體效果看上圖 IntentService更新處理UI工作。
3. 源碼分析
上面介紹了 IntentService 的使用方法,接下來,我們來看一下 IntentService 的源碼。如果你理解了 Hander、Looper、MessageQueue三者之間的關系,那么理解 IntentService 的源代碼十分容易。(如果不理解,請看博客 Android Handler深入學習(源碼分析))
根據上面的例子,我們知道了 IntentService 的執行順序為:
onCreate() -> onStartCommand() -> onStart() -> onHandleIntent -> ···處理完所有請求··· -> onDestroy()
IntentService 源碼如下:
public abstract class IntentService extends Service {
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private String mName;
private boolean mRedelivery;
//創建一個內部類,繼承于 Handler 類。
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);
}
}
/**
* IntentService 構造函數,在你的子類構造函數中調用(在子類中通過 super(name) 方法調用)
*
* @param 參數 name 用于命名工作線程
*
*/
public IntentService(String name) {
super();
mName = name;
}
@Override
public void onCreate() {
super.onCreate();
//創建一個 HandlerThread 對象
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
//啟動 HandlerThread 線程
thread.start();
//獲取 HandlerThread 中創建的 Looper 對象,賦值給全局變量 mServiceLooper。
mServiceLooper = thread.getLooper();
//創建一個 Handler 對象,并關聯剛剛通過 HandlerThread 創建的 Looper 對象,即關聯 mServiceLooper
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
//發送消息給 mServiceHanlder,
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
/**
* 你不應該在你的 IntentService 類中復寫 onStartCommand() 方法。
* 相反,你應該復寫 onHandleIntent() 方法,該方法會在 IntentService 接收到啟動請求時被調用。
*/
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
@Override
public void onDestroy() {
//退出 Looper 消息循環
mServiceLooper.quit();
}
/**
* 除非是綁定服務,否則你不需要實現此方法,因為此方法的默認實現返回 null.
*/
@Override
@Nullable
public IBinder onBind(Intent intent) {
return null;
}
/**
* 此方法在工作線程中被調用用于處理請求,一次只能處理一個請求,處理工作運行于獨立于其他應用程序的工作線程中。
* 因此,如果該段代碼花費了很長的一段時間,它會阻塞同一個 IntentService 的其他請求,但不會阻塞其他任何東西。
* 當所有的請求都被處理完,該 IntentService 就會停止運行,所以你不用調用 stopSelf() 方法。
*
* @param intent 通過 startService(Intent) 方法傳入。如果服務在其進程消失后重新啟動,則此值可能為空;
*/
@WorkerThread
protected abstract void onHandleIntent(@Nullable Intent intent);
}
總結:
- 在 onCreate() 方法中,我們新建一個 HandlerThread 對象,然后啟用它,獲取其創建的 Looper 對象,然后新創建一個 Looper 對象關聯該 Looper 對象。關于 HandlerThread 的知識點請看另一篇博客 Android HandlerThread詳解(源碼分析)
- 然后我們在 onStart() 方法中,通過 Message,將 Intent任務請求 發送給 mServiceHandler,Intent任務請求就是我們在 Activity 中通過 startService(Intent) 傳入的 Intent。
- mServiceHanlder 接受到任務請求,調用 onHandleIntent() 方法處理任務請求,處理完所有請求后,調用 stopSelf() 結束 IntentService。
至此 HandlerThread 的源碼也就分析結束了。
其實分享文章的最大目的正是等待著有人指出我的錯誤,如果你發現哪里有錯誤,請毫無保留的指出即可,虛心請教。
另外,如果你覺得文章不錯,對你有所幫助,請給我點個贊,就當鼓勵,謝謝~Peace~!