1.Service簡介android
Service是Android四大組件中最與Activity類似的組件,他們都表明可執行的程序。Service一直運行於後臺,不會與用戶交互,可用來處理一些耗時的任務(好比:後臺播放音樂,I/O操做等)。它的建立、配置與Activity基本類似,下面將詳細介紹Android Service的開發。多線程
2.建立、配置Serviceapp
2.1 定義一個繼承Service類的子類異步
2.2 在AndroidManifest.xml中配置該Serviceide
須要注意的是 Service和Activity都是從Context類派生出來的(建議能夠了解下Context這個類,瞭解以後,學習後面的一些組件、類也是很輕鬆的),所以它們均可以調用Context中getResources()、getContentResolver()等方法;佈局
Service中定義的系列生命週期的方法:學習
IBinder onBind(Intent intent):該方法是Service必須實現的一個方法,該方法返回一個IBinder對象,應用程序能夠經過該對象與Service組件通信測試
void onCreate():當Service第一次被建立後,系統將當即回調該方法ui
void onDestory():當Service被關閉以前會回調該方法this
void onStartCommand(Intent intent,int flags,int startId):該方法的早期版本(pre-2.0)是onStart(Intent intent,int startId)【onStartCommand(intent,flags,startId)裏面已經調用了onStart()方法】,每次客戶端調用startService(Intent intent)啓動Service都會回調該方法。下面是Service類中該方法的源碼:
1 @Override 2 public int onStartCommand(Intent intent, int flags, int startId) { 3 onStart(intent, startId); 4 return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY; 5 }
boolean onUnbind(Intent intent):當該Service上綁定的客戶端都斷開連接時,會回調該方法
下面的類定義了一個Service組件:
1 package com.example.administrator.servicedemo; 2 3 import android.app.Service; 4 import android.content.Intent; 5 import android.os.IBinder; 6 7 /** 8 * Created by Administrator on 2015/2/23. 9 */ 10 public class ServiceTest extends Service{ 11 //Service必須實現的方法 12 @Override 13 public IBinder onBind(Intent intent) { 14 return null; 15 } 16 //Service第一次被建立的時回調該方法 17 @Override 18 public void onCreate() { 19 super.onCreate(); 20 } 21 //Service被啓動時回調該方法 22 @Override 23 public int onStartCommand(Intent intent, int flags, int startId) { 24 return super.onStartCommand(intent, flags, startId); 25 } 26 //Service被關閉以前回調該方法 27 @Override 28 public void onDestroy() { 29 super.onDestroy(); 30 } 31 32 }
定義了Service以後,接下來須要在AndroidManifest.xml中配置該Service,配置Service使用<Service .../>元素
1 <service android:name=".ServiceTest" > 2 <intent-filter> 3 <!-- 爲Service組件的intent-filter配置action --> 4 <action android:name="com.example.administrator.servicedemo.SERVICE_TEST"></action> 5 </intent-filter> 6 </service>
3.啓動和中止Service
Android系統啓動Service有兩種方式:
> 經過Context的startService(Intent intent)方法:這種方法啓動的Service,訪問者和Service之間沒有關聯,即便訪問者退出了,該Service仍然運行。(中止Service:stopService(Intent intent))
> 經過Context的bindService(Intent intent)方法:這種方法啓動的Service,訪問者和Service綁定在了一塊兒,一旦訪問者退出,該Service也會中止。(取消綁定Service:unBind(Intent intent))
4.綁定本地Service並與之通信
當程序經過startService()和stopService()啓動和中止Service時,訪問者和Service之間基本沒有什麼聯繫,所以它們之間也就沒法進行通信、數據交換。
若是訪問者和Service之間須要進行方法調用或數據交換,那麼這時應該使用bindService()和unbindService()進行啓動和關閉Service。
Context 的bindService()方法的完整方法簽名爲:bindService(Intent intent,ServiceConnection conn,int flags),該方法的三個參數解釋以下:
Intent:該參數經過Intent啓動指定的Service
conn:該參數是一個ServiceConnection對象,該對象用於監聽訪問者和Service之間的連接狀況。當訪問者與Service之間連接成功時,將回調該對象的onServiceConnected(ComponentName name,IBinder service)方法,當Service所在的宿主線程因異常終止,或其它異常終止致使訪問者與Service之間斷開連接時回調該對象的onServiceDisconnected(ComponentName name)方法
注意:當訪問者主動經過unBindService()方法斷開與Service的連接時ServiceConnection的onServiceDisconnected(ComponnentName name)並不會被調用。
flags:指定綁定時是否自動建立Service,該參數可指定爲0(不建立)或BIND_AUTO_CREATE(自動建立)
該ServiceConnection的onServiceConnected方法中有一個IBinder對象,該對象能夠實現與被綁定Service之間的通信,下面程序示範如何在本地綁定Service,並和Service進行通信
MainActivity類主要用於啓動、關閉Service和顯示BindService類中的count
1 package com.example.administrator.servicedemo; 2 3 import android.content.ComponentName; 4 import android.content.Intent; 5 import android.content.ServiceConnection; 6 import android.os.IBinder; 7 import android.support.v7.app.ActionBarActivity; 8 import android.os.Bundle; 9 import android.util.Log; 10 import android.view.View; 11 import android.widget.Toast; 12 13 14 public class MainActivity extends ActionBarActivity { 15 private static final String SERVICE_BINDER = "com.example.administrator.servicedemo.SERVICE_BINDER"; 16 public static final String TAG = "MainActivity"; 17 private BindService.MyBind myBind;//聲明BindService類中內部類MyBind 18 @Override 19 protected void onCreate(Bundle savedInstanceState) { 20 super.onCreate(savedInstanceState); 21 setContentView(R.layout.activity_main); 22 } 23 24 //訪問者與Service通訊的橋樑 25 private ServiceConnection conn = new ServiceConnection() { 26 //鏈接成功 27 @Override 28 public void onServiceConnected(ComponentName name, IBinder service) { 29 Log.i(TAG,"connection is succeed"); 30 myBind = (BindService.MyBind)service; 31 } 32 //因異常而斷開連接 33 @Override 34 public void onServiceDisconnected(ComponentName name) { 35 Log.i(TAG,"connection is failed"); 36 } 37 }; 38 //啓動service 39 public void startService(View view){ 40 Intent intent = new Intent(); 41 intent.setAction(SERVICE_BINDER); 42 bindService(intent,conn,BIND_AUTO_CREATE); 43 } 44 //解除綁定 45 public void stopService(View view){ 46 Intent intent = new Intent(); 47 intent.setAction(SERVICE_BINDER); 48 unbindService(conn); 49 } 50 //實時顯示count 51 public void showCount(View view){ 52 Toast.makeText(MainActivity.this,"從BindService獲取到的count:"+myBind.getCount(),Toast.LENGTH_SHORT).show(); 53 } 54 55 }
BindService類中啓動一個新線程對count自增
1 package com.example.administrator.servicedemo; 2 3 import android.app.Service; 4 import android.content.Intent; 5 import android.os.Binder; 6 import android.os.IBinder; 7 8 /** 9 * Created by Administrator on 2015/2/23. 10 */ 11 public class BindService extends Service { 12 private int count; 13 private boolean quit; 14 //自定義一個Binder 15 class MyBind extends Binder{ 16 public int getCount(){ 17 return count;//實時獲取count的值 18 } 19 } 20 @Override 21 public IBinder onBind(Intent intent) { 22 return new MyBind();//返回給訪問者的Binder 23 } 24 25 @Override 26 public void onCreate() { 27 new Thread(new Runnable() { 28 @Override 29 public void run() { 30 while (!quit){ 31 try { 32 Thread.sleep(2000); 33 } catch (InterruptedException e) { 34 e.printStackTrace(); 35 } 36 count++; 37 } 38 } 39 }).start(); 40 } 41 42 @Override 43 public void onDestroy() { 44 super.onDestroy(); 45 this.quit = true; 46 } 47 }
layout佈局文件中添加了啓動、關閉、顯示的按鈕
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" 3 android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" 4 android:paddingRight="@dimen/activity_horizontal_margin" 5 android:paddingTop="@dimen/activity_vertical_margin" 6 android:orientation="vertical" 7 android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"> 8 9 <Button 10 android:layout_width="wrap_content" 11 android:layout_height="wrap_content" 12 android:text="start service" 13 android:onClick="startService"/> 14 <Button 15 android:layout_width="wrap_content" 16 android:layout_height="wrap_content" 17 android:text="stop service" 18 android:onClick="stopService"/> 19 20 <Button 21 android:layout_width="wrap_content" 22 android:layout_height="wrap_content" 23 android:text="show count" 24 android:onClick="showCount"/> 25 </LinearLayout>
程序運行後的結果:
點擊 start service 後將啓動BindService,可經過BindService的MyBind內部類的getCount()方法獲取當前count的值
點擊show count
對於Service中onBind()方法所返回的Ibinder對象來講,他可被當成該Service組件所返回的代理對象,Service容許客戶端經過IBinder來訪問Service內部數據,這樣能夠實現客戶端與Service之間的通訊。
當程序調用unBindService()方法解除對Service的綁定時,系統先回調onUnbind()方法,而後在回調onDestroy()方法.
與屢次調用startService()方法啓動Service不一樣的是,屢次調用bindService()方法並不會執行重複綁定。而前一個程序,只要用戶點擊啓動Service,系統就會回調onStartCommand()方法一次.對於這個示例,無論用戶點擊多少次綁定Service,系統都只會回調一次onBind()方法。
5. Service的聲明週期:
下圖分別是startService()、bindService()兩種方式啓動Service的生命週期
這裏還有一種特殊的狀況:
若是Service已由某個客戶端經過startService()啓動了,接下來其它客戶端再調用bindService()方法綁定到Service上,再調用unbindService()解除綁定,最後有調用bindService()方法再次綁定到這個Service,這個過程所出發的生命週期以下:
onCreate()-->onStartCommand()-->onBind()-->onUnbind()[重寫該方法時返回true]-->onRebind()
要想onRebind()方法被回調,除了已startService()方法啓動Service以外,還要在重寫Service的onUnbind()方法時返回true.
從上面能夠看到整個過程當中並無調用onDestroy()方法,這是由於該Service是以startService()方式啓動的而不是以bindService()方法啓動,所以當Activity調用unBindService()方法取消與Service的綁定時,該Service也不會終止
因而可知,當Activity經過bindService()綁定一個以啓動的Service時,系統只是把該Service的IBinder對象傳給Activity,並不會將Service的生命週期"綁定"到該Activity
,所以當Activity調用unBindService()方法取消與Service的綁定時,也只是切斷該Activity與Service之間的關聯,並不能中止Service組件。
6. 使用IntentService
IntentService 類是Service類的子類,它比普通的Service類增長了額外的功能
先看下Service自己存在的兩個問題.
1>Service不會專門啓動一條單獨的進程,Service與它所在應用位於同一個進程中
2>Service不是專門一條新的線程,所以不能在Service中處理耗時的任務
而IntentService能夠解決上述兩個不足:IntentService會使用隊列來管理請求Intent,每當客戶端經過Intent請求啓動IntentService時,IntentService會將請求放入到隊列,而後開啓一條新的worker線程來處理intent.對於異步的startService()請求,IntentService會按次序依次處理隊列中的Intent請求,該線程保證同一時刻只處理一個Intent。因爲IntentService使用worker線程處理耗時請求,所以不會阻塞主線程。
概括起來,IntentService具備以下特徵:
1>IntentService會建立單獨的worker線程來處理全部的Intent請求
2>IntentService會建立單獨的worker線程來處理onHandlerIntent()方法實現的代碼,開發者無需處理多線程問題
3>當全部請求處理完以後,IntentService會自動中止,所以開發者無需調用stopSelf()方法中止該Service
4>爲Service的onBind()方法提供了默認實現,默認是實現的onBind()方法返回null
5>爲Service的onStartCommand()方法提供了默認實現,該實現會將請求Intent添加到隊列中
所以,在重寫IntentService類時只需重寫onHandlerIntent()方法便可。
下面寫一個demo測試一下
MainActivity類
1 package com.example.administrator.intentservicetest; 2 3 import android.content.Intent; 4 import android.support.v7.app.ActionBarActivity; 5 import android.os.Bundle; 6 import android.view.Menu; 7 import android.view.MenuItem; 8 import android.view.View; 9 import android.widget.Toast; 10 11 12 public class MainActivity extends ActionBarActivity { 13 14 @Override 15 protected void onCreate(Bundle savedInstanceState) { 16 super.onCreate(savedInstanceState); 17 setContentView(R.layout.activity_main); 18 } 19 20 public void startService(View view){ 21 Intent intent = new Intent(this,MyService.class); 22 Toast.makeText(this,"您點擊了--啓動service按鈕",Toast.LENGTH_LONG).show(); 23 startService(intent); 24 } 25 26 public void startIntentService(View view){ 27 Intent intent = new Intent(this,MyIntentService.class); 28 Toast.makeText(this,"您點擊了--啓動intentService按鈕",Toast.LENGTH_LONG).show(); 29 startService(intent); 30 } 31 32 }
MyService類
1 package com.example.administrator.intentservicetest; 2 3 import android.app.Service; 4 import android.content.Intent; 5 import android.os.IBinder; 6 import android.util.Log; 7 8 /** 9 * Created by Administrator on 2015/2/15. 10 */ 11 public class MyService extends Service { 12 @Override 13 public IBinder onBind(Intent intent) { 14 return null; 15 } 16 17 @Override 18 public void onCreate() { 19 super.onCreate(); 20 } 21 22 @Override 23 public int onStartCommand(Intent intent, int flags, int startId) { 24 long currentTime = System.currentTimeMillis(); 25 long endTime = currentTime + 20*1000; 26 //模擬一個耗時操做 27 while (currentTime < endTime){ 28 synchronized (this){ 29 try { 30 wait(endTime - currentTime); 31 } catch (InterruptedException e) { 32 e.printStackTrace(); 33 } 34 } 35 } 36 return super.onStartCommand(intent, flags, startId); 37 } 38 }
MyIntentService類
1 package com.example.administrator.intentservicetest; 2 3 import android.app.IntentService; 4 import android.content.Intent; 5 import android.util.Log; 6 7 /** 8 * Created by Administrator on 2015/2/15. 9 */ 10 public class MyIntentService extends IntentService { 11 public MyIntentService(String name) { 12 super("MyIntentService"); 13 } 14 15 16 17 @Override 18 protected void onHandleIntent(Intent intent) { 19 long currentTime = System.currentTimeMillis(); 20 long endTime = currentTime + 20*1000; 21 //模擬一個耗時操做 22 while (currentTime < endTime){ 23 synchronized (this){ 24 try { 25 wait(endTime - currentTime); 26 } catch (InterruptedException e) { 27 e.printStackTrace(); 28 } 29 } 30 } 31 } 32 }
運行應用程序後: 點擊啓動service,能夠看到出現假死的情況 過會彈出 點擊啓動IntentService
總結:能夠看出當點擊啓動service按鈕時,程序出現了假死情況,這是由於Service中進行了耗時操做,前面說到Service是運行在主線程中的,因此該耗時任務阻塞了主線程。點擊啓動intentService時,未出現阻塞情況,由於IntentService會啓動一個worker線程進行處理耗時操做,並非在主線程中處理。因此,通常遇到耗時狀況時,首先想到的是IntentService,這個類已經幫咱們作了很好的處理,無需本身在Service中建立一個線程去處理耗時任務。