原創(chuàng)作品,轉(zhuǎn)載請(qǐng)注明出處O
Service是Android四大組件之一,它主要是去執(zhí)行耗時(shí)操作(不需要與用戶交互并且要長(zhǎng)期運(yùn)行的任務(wù))。
Service不依賴于用戶界面,但依賴于創(chuàng)建服務(wù)的應(yīng)用進(jìn)程,同時(shí)也不會(huì)自動(dòng)開(kāi)啟線程,所以我們需要在服務(wù)內(nèi)部手動(dòng)創(chuàng)建線程,否則就可能會(huì)出現(xiàn)線程阻塞。
在具體講解Service之前,我們先引入幾個(gè)關(guān)于Android多線程的知識(shí)點(diǎn):
1.Android多線程基本用法
多線程的作用就是避免在主線程做耗時(shí)操作而導(dǎo)致線程阻塞,從而影響用戶體驗(yàn)。
1.繼承Thread,重寫(xiě)run方法
public class MyThread extends Thread {
@Override
public void run() {
//具體實(shí)現(xiàn)代碼
}
}
調(diào)用 new MyThread().start();
2.實(shí)現(xiàn)Runnable接口
public class MyThread implements Runnable {
@Override
public void run() {
//具體實(shí)現(xiàn)代碼
}
}
調(diào)用 MyThread myThread = new MyThread();
new Thread(myThread).start();
3.匿名類方式
new Thread(new Runnable() {
@Override
public void run() {
//具體實(shí)體
}
}).start();
4.切記不可在子線程更新UI
2.異步消息處理
Android中的異步消息處理主要有4部分:
1.Message
Message是在線程之間傳遞的消息,它可以在內(nèi)部攜帶少量信息,用于在不同線程之間交換數(shù)據(jù)。
2.Handler
Handler主要用于發(fā)送和處理消息。
3.MessageQueue
MessageQueue是消息隊(duì)列的意思,主要用于存放所有通過(guò)Handler發(fā)送的消息。這部分消息會(huì)一直存在于消息隊(duì)列中,等待處理。
4.Looper
Looper是每個(gè)線程中的MessageQueue的管家,調(diào)用Looper的loop()方法后,就會(huì)進(jìn)入到一個(gè)無(wú)限循環(huán)中,每當(dāng)MessageQueue中存在一條消息,就將它取出,傳遞到Handler的handleMessage()方法中。
整個(gè)流程如圖所示(請(qǐng)忽略我清新脫俗的配圖)

3.AsyncTask
AsyncTask的實(shí)現(xiàn)原理也是基于異步消息處理機(jī)制
由于AsyncTask是一個(gè)抽象類,所以必須創(chuàng)建一個(gè)子類繼承它。在繼承時(shí)可以為AsyncTask類指定3個(gè)泛型參數(shù):
- Params 在執(zhí)行AsyncTask時(shí)需要傳入的參數(shù),可用于后臺(tái)任務(wù)。
- Progress 后臺(tái)任務(wù)執(zhí)行時(shí),可以使用這里指定的泛型作為進(jìn)度單位
- Result 任務(wù)執(zhí)行完成時(shí),可以使用這里指定的泛型作為返回值類型
public class MyTask extends AsyncTask<Void,Integer,Boolean>{
}
現(xiàn)在創(chuàng)建完MyTask如果要使用還需要重寫(xiě)里面的方法,常用的有以下4個(gè):
1.onPreExecute()
后臺(tái)任務(wù)執(zhí)行前調(diào)用,用于界面上的一些初始化操作。
2.doInBackground(Params...)
代碼執(zhí)行在子線程,處理耗時(shí)操作。返回類型根據(jù)AsyncTask類的第三個(gè)泛型參數(shù)決定(上文中提到的Result)。
不可進(jìn)行UI操作,可通過(guò)publishProgress() 從而調(diào)用下一個(gè)方法onProgressUpdate()操作UI
3.onProgressUpdate(Progress...)
在這個(gè)方法中對(duì)UI進(jìn)行操作
4.onPostExecute(Result)
后臺(tái)任務(wù)執(zhí)行完成時(shí)調(diào)用此方法
- 重寫(xiě)方法后變成這樣
public class MyTask extends AsyncTask<Void,Integer,Boolean>{
@Override
protected void onPreExecute() {
//顯示對(duì)話框等UI操作
}
@Override
protected Boolean doInBackground(Void... voids) {
//執(zhí)行后臺(tái)任務(wù)
//不可操作UI
publishProgress(integer);//通過(guò)此方法調(diào)用onProgressUpdate
return ture;
}
@Override
protected void onProgressUpdate(Integer... values) {
//獲取后臺(tái)執(zhí)行進(jìn)度
//可操作UI
}
@Override
protected void onPostExecute(Boolean aBoolean) {
//后臺(tái)任務(wù)執(zhí)行完成
//可操作UI
}
}
啟動(dòng)AsyncTask,只需要簡(jiǎn)單的一句
new MyAsyncTask().execute();
上面我們介紹了Android多線程編程的幾個(gè)知識(shí)點(diǎn),接下來(lái)一起學(xué)習(xí)Service的基本用法。
- 我們先來(lái)定義一個(gè)Service
public class MyService extends Service{
public MyService(){
}
@Override
public IBinder onBind(Intent intent){
throw new UnsupportedOperationException("Not yet implemented");
}
}
可以看到MyService繼承自Service類,里面只有一個(gè)onBind()方法,onBind()是Service中唯一的一個(gè)抽象方法,所以子類中必須要實(shí)現(xiàn)。
- 此時(shí)的服務(wù)還是空的,如果我們要在里面處理一些事情,還需要實(shí)現(xiàn)幾個(gè)服務(wù)中常用的方法
public class MyService extends Service{
public MyService(){
}
@Override
public IBinder onBind(Intent intent){
throw new UnsupportedOperationException("Not yet implemented");
}
public void onCreate(){
super.onCreate();
}
@Override
public int onStartCommand(Intent intent,int flags,int startId){
return super.onStartCommand(intent,flags,startId);
}
@Override
public void onDestroy(){
super.onDestroy();
}
}
可以看到MyService中又重寫(xiě)了onCreate()、onStartCommand()、onDestroy()
onCreate() 會(huì)在服務(wù)創(chuàng)建的時(shí)候調(diào)用
onStartCommand() 會(huì)在每次服務(wù)啟動(dòng)的時(shí)候調(diào)用
onDestroy() 會(huì)在服務(wù)銷毀的時(shí)候調(diào)用
- 此時(shí)MyService類已經(jīng)創(chuàng)建完成,但要讓其生效還需要在AndroidManifest.xml文件中注冊(cè)
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".MyService"
android:enabled="true"
android:exported="true"></service>
</application>
細(xì)心的盆友可能看到注冊(cè)的MyService還附帶有兩個(gè)屬性
enabled 是否啟動(dòng)此服務(wù)
exported 是否允許其它程序訪問(wèn)此服務(wù)
- 現(xiàn)在,服務(wù)已經(jīng)定義好了,如何啟動(dòng)呢(啟動(dòng)完別忘記關(guān)掉啊)
//啟動(dòng)
Intent startIntent = new Intent(context,MyService.class);
startService(startIntent);
//停止
Intent stopIntent = new Intent(context,MyService.class);
stopService(stopIntent);
看到這里是不是覺(jué)得很簡(jiǎn)單,不過(guò)有一點(diǎn)要注意下onCreate()只在服務(wù)第一次創(chuàng)建的時(shí)候調(diào)用,而onStartCommand()會(huì)在每次啟動(dòng)的時(shí)候都調(diào)用。
- 既然Service已經(jīng)定義完成,那么怎么讓它起作用呢?這時(shí)候onBind()就樣登場(chǎng)了
public class MyService extends Service{
···
private DownloadBinder mBinder = new DownloadBinder();
class DownloadBinder extends Binder{
public void startDownload(){
Log.d("MyService","startDownload executed");
}
public int getProgress(){
Log.d("MyService","getProgress executed");
return 0;
}
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
···
}
我們新建了DownloadBinder類,繼承Binder,并提供了兩個(gè)方法startDownload()啟動(dòng)下載,getProgress()獲取下載進(jìn)度(當(dāng)然只是執(zhí)行打印日志,并沒(méi)有實(shí)現(xiàn)具體方法)
- 萬(wàn)事俱備只欠調(diào)用,直接上代碼
···
private MyService.DownloadBinder downloadBinder;
private ServiceConnection connection = new ServiceConnection() {//創(chuàng)建一個(gè)ServiceConnection
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
//服務(wù)成功綁定時(shí)調(diào)用
downloadBinder = (MyService.DownloadBinder) iBinder;
downloadBinder.startDownload();
downloadBinder.getProgress();
}
@Override
public void onServiceDisconnected(ComponentName componentName) {//斷開(kāi)連接時(shí)調(diào)用
}
};
···
//bind
Intent bindIntent = new Intent(this, MyService.class);
bindService(bindIntent, connection, BIND_AUTO_CREATE);
···
//unbind
unbindService(connection);
上文中我想細(xì)心的朋友已經(jīng)注意到了,服務(wù)也有自己的生命周期
官網(wǎng)解釋
官方文檔
服務(wù)可以由系統(tǒng)運(yùn)行有兩個(gè)原因。如果有人調(diào)用Context.startService(),系統(tǒng)將檢索服務(wù)(創(chuàng)建它并調(diào)用它的onCreate()方法),然后使用客戶機(jī)提供的參數(shù)調(diào)用onStartCommand(Intent,int,int)方法。此服務(wù)將繼續(xù)運(yùn)行到Context.stopService()或stopSelf()。注意,多個(gè)調(diào)用Context.startService()不嵌入(盡管他們導(dǎo)致多個(gè)相應(yīng)的調(diào)用onStartCommand()),所以不管多少次啟動(dòng)服務(wù)將停止一次Context.stopService()或stopSelf();然而,服務(wù)可以使用他們stopSelf(int)方法,以確保服務(wù)不停止,直到開(kāi)始意圖已經(jīng)處理。
開(kāi)始服務(wù),主要有兩個(gè)額外的操作模式,他們可以決定在運(yùn)行,這取決于他們從onStartCommand返回值():START_STICKY用于顯式地根據(jù)需要啟動(dòng)和停止服務(wù),而START_NOT_STICKY或START_REDELIVER_INTENT應(yīng)該只用于服務(wù)仍然運(yùn)行在處理任何命令發(fā)送給他們。有關(guān)語(yǔ)義的更多細(xì)節(jié),請(qǐng)參閱相關(guān)文檔。
客戶端還可以使用Context.bindService()來(lái)獲得對(duì)服務(wù)的持久連接。同樣,如果服務(wù)尚未運(yùn)行(調(diào)用onCreate()),但不調(diào)用onStartCommand(),也會(huì)創(chuàng)建服務(wù)??蛻舳藢⒔邮誌Binder對(duì)象,該對(duì)象將從其onBind(Intent)方法返回服務(wù),允許客戶端調(diào)用該服務(wù)。只要建立連接(不管客戶機(jī)是否保留對(duì)服務(wù)的IBinder的引用),服務(wù)就會(huì)繼續(xù)運(yùn)行。通常IBinder返回的是在aidl中編寫(xiě)的復(fù)雜接口。
服務(wù)既可以啟動(dòng),也可以連接到它。在這種情況下,系統(tǒng)將保持服務(wù)運(yùn)行,只要它已經(jīng)啟動(dòng),或者有一個(gè)或多個(gè)與上下文相關(guān)的連接。BIND_AUTO_CREATE標(biāo)記。一旦這些情況都不存在,服務(wù)的onDestroy()方法被調(diào)用,服務(wù)實(shí)際上被終止。從onDestroy()返回時(shí),所有清除(停止線程、未注冊(cè)的接收者)都應(yīng)該完成。
- 了解了服務(wù)的基本使用和生命周期,我們來(lái)學(xué)習(xí)一些新的技巧
1.前臺(tái)服務(wù)
服務(wù)的系統(tǒng)優(yōu)先級(jí)比較低,當(dāng)系統(tǒng)內(nèi)存不足時(shí),可能會(huì)回收掉正在后臺(tái)運(yùn)行的服務(wù)。前臺(tái)服務(wù)會(huì)一直有一個(gè)正在運(yùn)行的圖標(biāo)顯示在系統(tǒng)的狀態(tài)欄顯示,已避免被后臺(tái)回收。相信這樣的例子在你的手機(jī)上很常見(jiàn)。
現(xiàn)在就讓我們的服務(wù)具有前臺(tái)能力,直接上代碼
@Override
public void onCreate() {
super.onCreate();
Log.d("MyService","onCreate executed");
Intent intent = new Intent(this,MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,0,intent,0);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("This is content title")
.setContentText("This is content text")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher))
.setContentIntent(pendingIntent)
.build();
startForeground(1,notification);
}
看看是不是覺(jué)得很簡(jiǎn)單,只需用修改onCreate中的代碼,調(diào)用* startForeground()*后,我們的MyService就進(jìn)化成了一個(gè)前臺(tái)服務(wù),并在系統(tǒng)狀態(tài)欄顯示出來(lái)

上文我們提到,Service中的代碼是默認(rèn)運(yùn)行在主線程中的,如果直接在服務(wù)中進(jìn)行耗時(shí)操作,就準(zhǔn)備迎接ANR吧
現(xiàn)在我們上一篇講到的多線程就起到作用了
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("MyService","onStartCommand executed");
new Thread(new Runnable() {
@Override
public void run() {
//處理邏輯代碼
stopSelf();
}
}).start();
return super.onStartCommand(intent, flags, startId);
}
在要進(jìn)行耗時(shí)操作的時(shí)候,我們new Thread()在run()方法內(nèi)處理耗時(shí)操作,其中stopSelf()是為了讓服務(wù)在執(zhí)行完之后停止下來(lái),和stopService()同理。
- 問(wèn)題來(lái)了,每次都要開(kāi)啟線程,關(guān)閉線程,不僅麻煩,忘了豈不是很尷尬。但是,Android還提供了一個(gè)類IntentService,這個(gè)類就能很好的解決這兩個(gè)問(wèn)題,下面我們就來(lái)創(chuàng)建一個(gè)IntentService類
public class MyIntentService extends IntentService {
public MyIntentService() {
super("MyIntentService");
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
//打印當(dāng)前線程的Id
Log.d("MyIntentService","Thread id is" + Thread.currentThread().getId());
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("MyIntentService","onDestroy executed");
}
}
我們?cè)趏nHandleIntent()方法中打印當(dāng)前線程id和Activity所在線程id進(jìn)行對(duì)比

可以看到不僅所在線程不同,而且在執(zhí)行完打印操作后自動(dòng)停止了,完美解決問(wèn)題。
好了,服務(wù)的基本使用講到這里,后續(xù)會(huì)結(jié)合源碼來(lái)具體分析四大組件之一的Service.
我寫(xiě)了個(gè)Demo,涵蓋這本文涉及到的所有知識(shí)點(diǎn)
Demo下載 密碼:gxuc
每星期至少一篇跟新本系列,感興趣可以關(guān)注。
一起學(xué)習(xí),一起進(jìn)步。