服務(service)是Android中實現程序後臺運行的解決方案。服務並不是運行在一個獨立的進程中,而是依賴於創建服務所在的應用程序的進程。實際上,服務並不會自動開啓線程。
android多線程編程
線程的基本用法
- 新建一個類繼承自Thread,重寫父類的run()方法,在裏面寫耗時的邏輯。
class MyThread extends Thread{
@override
public void run(){
//寫耗時的邏輯
}
}
new MyThread().start(); - 實現Runnable接口,匿名內部類的形式。
new Thread(new Runnable() {
@Override
public void run() {
}
}).start();
在子線程中更新UI
想要更新應用程序裏的UI,必須在主線程中進行。android提供了一套異步消息處理機制,完美地解決了在子線程中進行UI操作的問題。
- 異步消息處理機制
- 用匿名內部類的形式,實例化一個重寫了handleMessage(Message msg)方法的Handler對象,在handleMessage(Message msg)方法中利用傳過來的Message更新UI。
- 當子線程有更新的需要時,將數據存在Message對象當中,調用Handler的sendMessage(message)發送到主線程。
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case UPDATA_TEXT:
textView.setText("Hello Android!");
break;
default:
break;
}
}
};
new Thread(new Thread() {
@Override
public void run() {
//textView.setText("Hello Android!");
Message msg = new Message();
msg.what = UPDATA_TEXT;
handler.sendMessage(msg);
new downtask().execute();
}
}).start();
深入異步消息處理機制
1、組成部分:
何爲handler:異步消息處理者。handler扮演了往MessageQueue上添加消息和處理消息的角色(只處理由自己發出的,由looper分發過來(dispatch)的消息),即通知MQ它要執行一個任務(sendMessage),並在loop到自己的時候執行該任務(handleMessage),整個過程是異步的。Handler的構造方法,會首先得到當前線程中保存的Looper實例,進而與Looper實例中的MessageQueue想關聯。Handler的sendMessage方法,會給msg的target賦值爲handler自身,然後加入MessageQueue中。在構造Handler實例時,我們會重寫handleMessage方法,也就是msg.target.dispatchMessage(msg)最終調用的方法。
何爲message:後臺進程返回的數據,裏面可以存儲bundle等數據格式。
何爲messageQueue:是線程對應looper的一部分,以隊列的形式負責存儲從後臺進程中拋回的和當前handler綁定的message。
何爲looper:looper相當於一個messageQueue的管理人員,同時還是“電報分發員”,它會不停的循環的遍歷隊列,然後將符合條件的message一個個的拿出來分發特定的handler進行處理。首先Looper.prepare()在本線程中保存一個Looper實例,然後該實例中保存一個MessageQueue對象;因爲Looper.prepare()在一個線程中只能調用一次,所以MessageQueue在一個線程中只會存在一個。Looper.loop()會讓當前線程進入一個無限循環,不端從MessageQueue的實例中讀取消息,然後回調msg.target.dispatchMessage(msg)方法。
2、過程:我們通常在UI線程中創建一個handler。這裏要明確Handler、線程、looper的關係,每一個線程都綁定有一個looper,而每個Handler都要關聯一個looper,所以Handler就間接綁定到了一個線程上。一個線程只能有一個looper,但可以有多個Handler。此外,線程中looper負責從其內部的messageQueue中拿出一個個的message,調用dispatchMessage()方法把Message分發給特定的Handler進行處理。handler可以在任意線程中,往它所綁定的looper內部的MQ添加消息,handler在它所綁定的線程中處理消息。因爲我們這裏handler是在UI線程中實現的,所以經過這麼一個handler異步消息處理大師之手,就實現了跨線程間的通信了,那麼UI線程更新UI就不是什麼問題了。
另外有幾個值得關注的點:
1、調用handler的post(Runnable r)或者postDelayed(Runnable r,long t)方法裏傳進去一個Runnable的匿名類,其實這樣寫並不能創建一個線程,Runnable內部重寫的run()方法也只是在主線程中執行。因爲handler只負責發送消息和處理消息,並不是負責新建一個線程。而且,所謂的子線程的start()自始至終都沒有被調用。怎樣解決這個問題呢?
上文提到,handler創建時會關聯一個looper,默認的構造方法將關聯當前線程的looper,當然handler也可以關聯到其他線程的looper啦。
可以寫成如下代碼:
//創建一個包含Looper的線程,這裏如果沒有HandlerThread的調用,會直接將後邊的MyThread放到UI線程隊列
HandlerThread myHandlerThread = new HandlerThread("chenzheng_java");
// 啓動新線程
myHandlerThread.start();
// 將handler綁定到新線程,自定義MyHandler類需要定義自己的構造方法
handler = new MyHandler(myHandlerThread.getLooper());
// 在新線程中執行任務,MyRunnableTask是實現了Runnable接口的自定義類
handler.post(new MyRunnableTask());
2、有了handler之後,我們就可以使用 post(Runnable), postAtTime(Runnable, long), postDelayed(Runnable, long), sendEmptyMessage(int), sendMessage(Message), sendMessageAtTime(Message, long)和 sendMessageDelayed(Message, long)這些方法向MQ上發送消息了。光看這些API你可能會覺得handler能發兩種消息,一種是Runnable對象,一種是message對象,這是直觀的理解,但其實post發出的Runnable對象最後都被封裝成message對象了。
最後來張總結性的好圖:
要真正理解android的異步消息處理機制,《第一行代碼》並沒有做很詳細的講解,畢竟是入門書,但這個知識點是比較重要的,面試當中也會經常考到。另外推薦兩篇結合android框架源碼分析異步消息處理機制的博文,寫得非常好的。
Android 異步消息處理機制 讓你深入理解 Looper、Handler、Message三者關係:
http://blog.csdn.net/lmj623565791/article/details/38377229
android的消息處理機制(圖+源碼分析)——Looper,Handler,Message:
http://www.cnblogs.com/codingmyworld/archive/2011/09/14/2174255.html
使用AsyncTask
爲了更加方便我們在子線程中對UI進行操作,android提供了一個工具類AsyncTask,把異步消息處理機制封裝其中。
- 由於AsyncTask是一個抽象類,我們需要創建一個子類去繼承它。繼承時,我們可以指定三個泛型參數。
1.Params 在執行AsyncTask時需要傳入的參數,doInBackground(Params,…)。
2.Progress 在後臺任務執行時,如需要在界面上顯示當前的進度,則使用這裏指定的泛型作爲進度單位
3.Result 指定返回值類型 - 重寫onPreExecute(),doInBackground(Params,…),onPrpgressUpdate(Progress,…),onPostExecute(Result,…)方法
- 啓動自定義任務,new DownloadTask().execute();
- 由於AsyncTask是一個抽象類,我們需要創建一個子類去繼承它。繼承時,我們可以指定三個泛型參數。
class downtask extends AsyncTask<Void,Integer,Boolean>{
@Override
protected void onPreExecute() {//後臺任務開始執行前調用,用於進行一些界面上的初始化操作
super.onPreExecute();
ProgressDialog.show(MainActivity.this,"Downloading","Loading...");
}
@Override
protected Boolean doInBackground(Void... params) {//在子線程中進行一些耗時的操作
int downloadProgress=0;
publishProgress(downloadProgress);//傳入更新ui的數據
return true;
}
@Override
protected void onProgressUpdate(Integer... values) {//更新ui操作
super.onProgressUpdate(values);
}
@Override
protected void onPostExecute(Boolean aBoolean) {//任務結束
super.onPostExecute(aBoolean);
}
}
服務的基本用法
- 當然要自定義一個子類繼承Service類,重寫
public IBinder onBind(Intent intent),與活動通信的關鍵方法,與活動綁定的時候調用。
public void onCreate(),當服務被創建的時候調用
public int onStartCommand(Intent intent, int flags, int startId),當服務被啓動的時候被調用
public void onDestroy(),當服務被銷燬的時候被調用。 - 作爲android四大組件之一,要啓動和停止服務當然要藉助intent了,
Intent startIntent=new Intent(this,MyService.class);
startService(startIntent);
Intent stopIntent=new Intent(this,MyService.class);
stopService(stopIntent);
- 最後一定要在AndroidManifest文件中進行註冊。
服務與活動通信
- 在自定義的Service子類中,定義一個內部類,繼承自Binder。在子類內部,定義給關聯組件訪問的接口方法。我把它比喻成電話線。
- 實例化這個內部類,在onBind()方法中返回Binder子類實例。
public IBinder onBind(Intent intent) {
return downloadBinder;
} - 在活動中,建造一條橋樑,創建一個ServiceConnection的匿名類,重寫onServiceDisconnected()和onServiceConnected(),並在onServiceConnected()中得到Binder實例,即當橋樑架好後在onServiceConnected()可獲得來自Service的電話線實例。
private MyService.DownloadBinder downloadBinder;
private ServiceConnection serviceConnection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
downloadBinder=(MyService.DownloadBinder)service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
- 橋樑建好了,接下來就可調用bindService()和unbindService()綁定和解綁服務了,把橋樑架在Activity與Service之間。
Intent bindIntent=new Intent(this,MyService.class);
bindService(bindIntent,serviceConnection,BIND_AUTO_CREATE);
//BIND_AUTO_CREATE表示綁定服務後自動啓動服務
- 接下來在活動中就可以調用返回的在Service內部的Binder子類實例的方法了。任何一個服務在整個應用程序都是通用的,它可以與其他活動綁定。不管服務是如何被創建的,它都允許客戶綁定它。所以一個通過onStartCommand()方法啓動的服務(started service)依然會收到onBind()調用(當客戶調用了bindService())。
服務的生命週期
活躍的生命時間,開始於onStartCommand()或onBind()函數被調用。如果是被啓動的服務,他的活躍生命時間會和整個生命時間一同結束(在onStartCommand()函數返回後,服務依然是活躍狀態)。如果是被綁定的服務,活躍生命時間會在onUnbind()函數返回後結束。當調用了startService()又去調用bindService()方法,要調用unbindService()和stopService()方法,onDestroy()方法纔會執行。
服務的更多技巧
前臺服務
如果希望服務一直保持運行狀態,而不會由於系統的內存不足而被回收,可以考慮使用前臺服務。
方法很簡單,在Service的onCreate()方法中,使用通知(Notification),所不同的是調用startForground()啓動通知。
@Override
public void onCreate() {
super.onCreate();
Intent intent=new Intent(this,MainActivity.class);
PendingIntent pendingIntent=PendingIntent.getActivity(this,0,intent,0);
Notification notification=new Notification.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("Front Service")
.setContentText("this is content")
.setWhen(System.currentTimeMillis())
.setContentIntent(pendingIntent)
.getNotification();
startForeground(1,notification);
Log.d("MyService","onCreate");
}
使用IntentService
不要被後臺服務的後臺所迷惑,其實服務中的代碼都是默認運行渣主線程當中的,如果直接在服務中去處理一些耗時的邏輯就很容易出現ANR(Application Not Response)。
我們應該在服務的每個具體方法中開啓一個子線程,在子線程中處理那些耗時的邏輯。
服務一旦啓動之後,就會一直處於運行狀態,必須調用StopService()或stopSelf()方法才能停止服務。爲了方便創建一個異步的,會自動停止的服務,Android提供了一個IntentService類(方便的服務),它有點像Activity,能夠響應intent,從onHandleIntent(Intent intent)方法可以看出。
public class MyIntentService extends IntentService {
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* @param name Used to name the worker thread, important only for debugging.
*/
public MyIntentService(String name) {
super(name);
}
public MyIntentService() {
super("MyIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
//子線程代碼
}
}
AlarmManager
AlarmManager是可以發送Intent的系統服務,既然是系統服務,當然調用(AlarmManager)getSystemService(ALARM_SERVICE);方法得到Manager實例啦。發送什麼樣的intent呢,我們使用PendingIntent打包Intent。
AlarmManager的set()方法可以設置一個定時任務。(涉及PendingIntent和BroadcastReceiver)
Intent intent1=new Intent(this,AlarmReceiver.class);
PendingIntent pendingIntent=PendingIntent.getBroadcast(this,0,intent1,0);
AlarmManager alarmManager=(AlarmManager)getSystemService(ALARM_SERVICE);
long triggerAtTime= SystemClock.elapsedRealtime()+30*1000;
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,triggerAtTime,pendingIntent);
參考博客和文獻:
Android管理服務(Service)的生命週期(lifecycle)
http://blog.csdn.net/oracleot/article/details/18818575