http://blog.csdn.net/guolin_blog/article/details/11952435android
http://www.jianshu.com/p/eeb2bd59853f服務器
定義、特色:網絡
Service是能夠在後臺執行長時間(長生命週期)而又不與用戶產生UI交互(沒有用戶界面)的操做app
注意事項:
一、只能在後臺運行,即使用戶切換了其餘應用,啓動的Service仍可在後臺運行。
二、能夠和其餘組件進行Service綁定並與之交互,甚至是跨進程通訊(IPC)。
三、不能運行在一個獨立的進程當中,而是依賴與建立服務時所在的應用組件進程。
四、服務不會自動開啓線程,咱們須要在服務的內部手動建立子線程,並在這裏執行具體的任務。ide
應用場景舉例:
音樂播放:播放多媒體的時候用戶啓動了其餘Activity,此時要在後臺繼續播放。
記錄檢測:好比檢測SD卡上文件的變化;在後臺記錄你的地理信息位置的改變等。
其餘操做:網絡請求、執行文件讀寫操做或者與 content provider交互。佈局
類型:
本地服務與遠程服務this
本地服務依附在主進程上,在必定程度上節約了資源。本地服務由於是在同一進程,所以不須要IPC,也不須要AIDL。相應bindService會方便不少。缺點是主進程被kill後,服務變會終止。spa
遠程服務是獨立的進程,對應進程名格式爲所在包名加上你指定的android:process字符串。因爲是獨立的進程,所以在Activity所在進程被kill的是偶,該服務依然在運行。缺點是該服務是獨立的進程,會佔用必定資源,而且使用AIDL進行IPC稍微麻煩一點。本文第六部分將會簡單的講述這一進程間通訊方式。.net
對於startService來講,無論是本地服務仍是遠程服務,咱們須要作的工做都同樣簡單。線程
生命週期
而後新建一個MyService繼承自Service,並重寫父類的onCreate()、onStartCommand()和onDestroy()方法,以下所示:
public class MyService extends Service { public static final String TAG = "MyService"; @Override public void onCreate() { super.onCreate(); Log.d(TAG, "onCreate() executed"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.d(TAG, "onStartCommand() executed"); return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { super.onDestroy(); Log.d(TAG, "onDestroy() executed"); } @Override public IBinder onBind(Intent intent) { return null; } }
在Activity裏面加入啓動Service和中止Service的邏輯
public void onClick(View v) { switch (v.getId()) { case R.id.start_service: Intent startIntent = new Intent(this, MyService.class); startService(startIntent); break; case R.id.stop_service: Intent stopIntent = new Intent(this, MyService.class); stopService(stopIntent); break; default: break; } }
另外須要注意,項目中的每個Service都必須在AndroidManifest.xml中註冊才行
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.servicetest" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="17" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > …… <service android:name="com.example.servicetest.MyService" > </service> </application> </manifest>
經過startService方式(繼承Service類) 總結
一、啓動服務對象屢次啓動同時只會產生一個,onCreate()方法只會在Service第一次被建立的時候調用,屢次點擊啓動會執行屢次onStartCommand()方法,onDestroy()方法只會在Service第一次被中止的時候調用,屢次點擊中止不會報異常,也再也不執行onDestroy()方法。
二、一旦啓動,Service將一直運行在後臺(run in the background indefinitely)即使啓動Service的組件已被destroy。
三、中止一個started服務有兩種方法:
(1)在外部使用stopService()手動中止。
(2)在服務內部(onStartCommand方法內部)使用stopSelf()方法,使服務執行完畢後自動中止。好比說,一個start的Service執行在後臺下載或上傳一個文件的操做,完成以後,Service應本身中止。
四、onStartCommand方法的返回值:
onStartCommand方法執行時,返回的是一個int型。這個整型能夠有三個返回值:START_NOT_STICKY、START_STICKY、START_REDELIVER_INTENT
START_NOT_STICKY:「非粘性的」。使用這個返回值時,若是在執行完onStartCommand方法後,服務被異常kill掉,系統不會自動重啓該服務。
START_STICKY:若是Service進程被kill掉,保留Service的狀態爲開始狀態,但不保留遞送的intent對象。隨後系統會嘗試從新建立Service,因爲服務狀態爲開始狀態,因此建立服務後必定會調用onStartCommand(Intent,int,int)方法。若是在此期間沒有任何啓動命令被傳遞到Service,那麼參數Intent將爲null。
START_REDELIVER_INTENT:重傳Intent。使用這個返回值時,系統會自動重啓該服務,並將Intent的值傳入。
五、默認狀況下,一個started的Service與啓動他的組件在同一個線程中。上面的實例中,服務就是在主線程中運行的,若是是在服務中完成耗時操做的話,容易形成主線程阻塞。因此咱們能夠在服務中開啓一個子線程來完成耗時操做。
public class MyService extends Service { public static final String TAG = "MyService"; //服務執行的操做 @Override public int onStartCommand(Intent intent, int flags, int startId) { new Thread(new Runnable() { public void run() { //在子線程中處理具體的邏輯 //在這裏咱們只作打印子線程id的操做 Log.i("MyService",Thread.currentThread().getId()+""); stopSelf(); //服務執行完畢後自動中止 } }).start(); return super.onStartCommand(intent, flags, startId); } @Override public IBinder onBind(Intent intent) { // TODO Auto-generated method stub return null; } }
若是咱們不手動開啓線程,I/MyService: 177將會變成它依賴的主線程1,這就不能作耗時操做了。雖然說上面的這種寫法並不複雜,但總會有一些程序猿忘記開啓線程,或者忘記調用stopSelf()方法。
爲了能夠簡單地建立一個可開啓單獨線程、會自動中止的服務,Android專門提供了一個IntentService類,這個類就很好的解決了上面所提到的兩種尷尬。
IntentService的做用:
當咱們須要這樣一次性完成的任務時,就可使用IntentService來完成。
IntentService的用法:
1)新建一個MyIntentService類,繼承自IntentService,並重寫父類的onHandleIntent()方法,代碼以下:
public class MyIntentService extends IntentService{ public MyIntentService() { //第一步:重寫父類的onHandleIntent()方法,這裏首先要提供一個無參的構造方法, //而且必須在其內部調用父類的有參構造方法,這裏咱們手動給服務起個名字爲:MyIntentService super("MyIntentService"); } //第二步:重寫父類的onHandleIntent()方法,該方法在會在一個單獨的線程中執行, //來完成工做任務。任務結束後,該Service自動中止 @Override protected void onHandleIntent(Intent intent) { for(int i = 0;i<3;i++) { //Service要執行的邏輯 //這裏咱們只打印當前線程的id Log.d("MyIntentService","IntentService線程的id是:"+Thread.currentThread().getId()); try { //線程睡眠一秒鐘 Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } @Override public void onDestroy() { super.onDestroy(); Log.d("MyIntentService","onDestroy"); } }
經過startService方式(繼承IntentService類) 總結
一、啓動一個IntentService和啓動一個普通的Service,步驟是類似的。
二、與直接繼承Service不一樣在於:經過繼承IntentService運行,自動開啓了單獨線程,並且完成任務後自動銷燬了Service。
【補充】Service和Thread的關係:
很多Android初學者均可能會有這樣的疑惑,Service和Thread到底有什麼關係呢?何時應該用Service,何時又應該用Thread?答案可能會有點讓你吃驚,由於Service和Thread之間沒有任何關係!
之因此有很多人會把它們聯繫起來,主要就是由於Service的後臺概念。Android的後臺就是指,它的運行是徹底不依賴UI的。即便Activity被銷燬,或者程序被關閉,只要進程還在,Service就能夠繼續運行。Thread咱們你們都知道,是用於開啓一個子線程,在這裏去執行一些耗時操做就不會阻塞主線程的運行。而Service咱們最初理解的時候,總會以爲它是用來處理一些後臺任務的,一些比較耗時的操做也能夠放在這裏運行,這就會讓人產生混淆了。可是,Service實際上是運行在主線程裏的,一些比較耗時的操做須要開啓單獨線程。
Bind Service的引入:
有沒有什麼辦法能讓Service與組件的關聯更多一些呢?好比說在Activity中指揮Service去幹什麼,Service就去幹什麼。固然能夠,只須要讓Activity和Service創建關聯就行了。這時咱們就能夠經過bindService方式定義一個Service。
Bind Service的實現原理:
應用程序組件(客戶端)經過調用bindService()方法可以綁定服務,而後Android系統會調用服務的onBind()回調方法,則個方法會返回一個跟服務器端交互的Binder對象。
bindService()方法當即返回,而且不給客戶端返回IBinder對象。要接收IBinder對象,客戶端必須建立一個ServiceConnection類的實例,而且把這個實例傳遞給bindService()方法。ServiceConnection對象包含了一個系統調用的傳遞IBinder對象的回調方法。
Bind Service實現流程:
1)一直有一個onBind()方法咱們都沒有使用到,這個方法其實就是用於和Activity創建關聯的,修改MyService中的代碼,以下所示:
public class MyBindService01 extends Service { public static final String TAG = "MyBindService01"; private MyBinder mBinder = new MyBinder(); @Override public void onCreate() { super.onCreate(); Log.d(TAG, "onCreate"); } @Override public IBinder onBind(Intent intent) { return mBinder; //在這裏返回新建的MyBinder類 } @Override public boolean onUnbind(Intent intent) { Log.d(TAG, "onUnbind"); return super.onUnbind(intent); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.d(TAG, "onStartCommand"); return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { super.onDestroy(); Log.d(TAG, "onDestroy"); } //MyBinder類,繼承Binder:讓裏面的方法執行下載任務,並獲取下載進度 class MyBinder extends Binder { public void startDownload() { Log.d("TAG", "startDownload() executed"); // 執行具體的下載任務 } public int getProgress(){ Log.d("TAG", "getProgress() executed"); return 0; } } }
2)檢查清單文件,是否已經對Service進行註冊:<service android:name=".MyBindService01" ></service>
3)讓咱們修改MainActivity和MyBindService01之間創建關聯
public class MainActivity extends Activity implements OnClickListener { private Button button1_bind_service; private Button button2_unbind_service; private MyBindService01.MyBinder myBinder; boolean mBound = false; //一開始,並無和Service綁定.這個參數是用來顯示綁定狀態 //匿名內部類:服務鏈接對象 private ServiceConnection connection = new ServiceConnection() { //當服務異常終止時會調用。注意,解除綁定服務時不會調用 @Override public void onServiceDisconnected(ComponentName name) { mBound = false; //服務異常終止時,狀態爲未綁定 //解決了屢次執行unbindService()方法引起的異常問題 } //和服務綁定成功後,服務會回調該方法 @Override public void onServiceConnected(ComponentName name, IBinder service) { myBinder = (MyBindService01.MyBinder) service; //在Activity中調用Service裏面的方法 myBinder.startDownload(); myBinder.getProgress(); mBound = true; //true說明是綁定狀態 } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); button1_bind_service = (Button) findViewById(R.id.button1_bind_service); button2_unbind_service = (Button) findViewById(R.id.button2_unbind_service); button1_bind_service.setOnClickListener(this); button2_unbind_service.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.button1_bind_service: Intent bindIntent = new Intent(this, MyService.class); bindService(bindIntent, connection, BIND_AUTO_CREATE); //這裏傳入BIND_AUTO_CREATE表示在Activity和Service創建關聯後會自動建立Service(即便以前沒有建立 //Service也沒有關係),這會使得MyService中的onCreate()方法獲得執行,但onStartCommand()方法不會執行。 break; case R.id.button2_unbind_service: //若是和Service是綁定的狀態,就解除綁定。 if(mBound){ unbindService(connection); mBound=false; } break; default: break; } } }
說明:這裏咱們首先建立了一個ServiceConnection的匿名類,在裏面重寫了onServiceConnected()方法和onServiceDisconnected()方法,若是當前Activity與服務鏈接成功後,服務會回調onServiceConnected()方法
在onServiceConnected()方法中,咱們又經過向下轉型獲得了MyBinder的實例,有了這個實例,Activity和Service之間的關係就變得很是緊密了。如今咱們能夠在Activity中根據具體的場景來調用MyBinder中的任何public方法,即實現了Activity指揮Service幹什麼Service就去幹什麼的功能。
固然,如今Activity和Service其實還沒關聯起來了呢,這個功能是在Bind Service按鈕的點擊事件裏完成的。能夠看到,這裏咱們仍然是構建出了一個Intent對象,而後調用bindService()方法將Activity和Service進行綁定。bindService()方法接收三個參數,第一個參數就是剛剛構建出的Intent對象,第二個參數是前面建立出的ServiceConnection的實例,第三個參數是一個標誌位,這裏傳入BIND_AUTO_CREATE表示在Activity和Service創建關聯後自動建立Service,這會使得MyService中的onCreate()方法獲得執行,但onStartCommand()方法不會執行。
經過bindService方式 總結
一、onCreate()、onBind()方法只會在Service第一次被建立的時候調用,屢次點擊綁定啓動不會執行任何方法,onUnbind()、onDestroy()方法會在調用者執行unbindService()方法時執行或者Activity退出時自動執行。
二、若是咱們既經過startService()開啓Service,又用經過bindService開啓,必要unbindService()和stopService()都執行一次(沒有前後順序),Service纔會被銷燬。
三、若是屢次執行unbinsService()方法,程序會異常退出,咱們須要在代碼中加一個判斷是否綁定的標記mBound來解決此問題,上面代碼中有說明。
case R.id.button4_unbind_service: //若是和Service是綁定的狀態,就解除綁定。 if(mBound){ unbindService(connection); mBound=false; } break;
四、當在旋轉手機屏幕的時候,當手機屏幕在「橫」「豎」變換時,此時若是你的 Activity 若是會自動旋轉的話,旋轉實際上是 Activity 的從新建立,所以旋轉以前的使用 bindService 創建的鏈接便會斷開(Context 不存在了)。
五、只有Activity、Service、Content Provider可以綁定服務;BroadcastReceiver廣播接收器不能綁定服務。
若是先bindService,再startService,再調用Context.stopService
Service的onDestory方法不會馬上執行,由於有一個與Service綁定的Activity,可是在Activity退出的時候,會執行其(Service的)onDestory方法,若是要馬上執行stopService,就得先解除綁定。
若是先是bind了,那麼start的時候就直接運行Service的onStartCommand()方法,若是先是start,那麼bind的時候就直接運行onBind()方法。
當一個服務沒被onDestory()銷燬以前,只有第一個啓動它的客戶端能調用它的onBind()和onUnbind()。
一、生命週期不一樣。(詳見圖)
二、屢次啓動,前者會屢次執行onStartCommand()方法,後者什麼都不執行。屢次中止,前者只會執行一次onDestroy()方法,後者報異常信息。
三、當啓動Service的組件已被Destroy的時候,前者不中止,後者會中止。
四、前者中止直接執行onDestroy()方法(Service中的),後者則先解除綁onUnbind()定再執行onDestroy()方法(Service中的)。
五、當手機屏幕在「橫」「豎」變換時,前者建立的Service不會中止,後者會隨着Activity的重建而中止。
六、後者的onBind回調方法將返回給客戶端一個IBinder接口實例,IBinder容許客戶端回調服務的方法,好比獲得Service運行的狀態或其餘操做。而這些操做前者啓動的Service是沒有的。
android:name -- 服務類名
android:label -- 服務的名字,若是此項不設置,那麼默認顯示的服務名則爲類名
android:icon -- 服務的圖標
android:permission -- 申明此服務的權限,這意味着只有提供了該權限的應用才能控制或鏈接此服務
android:process -- 表示該服務是否運行在另一個進程,若是設置了此項,那麼將會在包名後面加上這段字符串表示另外一進程的名字
android:enabled --表示是否能被系統實例化,爲true表示能夠,爲false表示不能夠,默認爲true
android:exported -- 表示該服務是否可以被其餘應用程序所控制或鏈接,不設置默認此項爲 false
Service幾乎都是在後臺運行的,一直以來它都是默默地作着辛苦的工做。可是Service的系統優先級仍是比較低的,當系統出現內存不足狀況時,就有可能會回收掉正在後臺運行的Service。若是你但願Service能夠一直保持運行狀態,而不會因爲系統內存不足的緣由致使被回收,就能夠考慮使用前臺Service。前臺Service和普通Service最大的區別就在於,它會一直有一個正在運行的圖標在系統的狀態欄顯示,下拉狀態欄後能夠看到更加詳細的信息,很是相似於通知的效果。固然有時候你也可能不只僅是爲了防止Service被回收才使用前臺Service,有些項目因爲特殊的需求會要求必須使用前臺Service,好比說墨跡天氣,它的Service在後臺更新天氣數據的同時,還會在系統狀態欄一直顯示當前天氣的信息,以下圖所示:
那麼咱們就來看一下如何才能建立一個前臺Service吧,其實並不複雜,修改MyService中的代碼,以下所示:
public class MyService extends Service { public static final String TAG = "MyService"; private MyBinder mBinder = new MyBinder(); @Override public void onCreate() { super.onCreate(); Notification notification = new Notification(R.drawable.ic_launcher, "有通知到來", System.currentTimeMillis()); Intent notificationIntent = new Intent(this, MainActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); notification.setLatestEventInfo(this, "這是通知的標題", "這是通知的內容", pendingIntent); startForeground(1, notification); Log.d(TAG, "onCreate() executed"); } ......... }
這裏只是修改了MyService中onCreate()方法的代碼。能夠看到,咱們首先建立了一個Notification對象,而後調用了它的setLatestEventInfo()方法來爲通知初始化佈局和數據,並在這裏設置了點擊通知後就打開MainActivity。而後調用startForeground()方法就可讓MyService變成一個前臺Service,並會將通知的圖片顯示出來。
如今從新運行一下程序,並點擊Start Service或Bind Service按鈕,MyService就會之前臺Service的模式啓動了,而且在系統狀態欄會彈出一個通欄圖標,下拉狀態欄後能夠看到通知的詳細內容