Android四大元件-Service
概念:
android 四大元件之一,提供在後臺執行的服務,屬於計算型元件。
特點:
在後臺執行,無使用者介面,生命週期長。
啟動方式
startservice:
不與Activity繫結,啟動之後會無限期的執行下去,除非遇到記憶體低情況被回收,需要呼叫stopService或stopSelf才會停止。
- **生命週期 :**onCreate(只執行一次)-onStartCommand-服務執行-onDestory()
- onCreate 只調用一次,onStartCommand可以呼叫多次(呼叫的次數是startService的次數),其他方法只能呼叫一次。
- onStartCommand必須返回一個整數= 描述系統在殺死服務後如何繼續執行。
- START_STICK:重建服務,呼叫onStartCommand,但不會傳入上次未傳送完的intent,而是使用null intent (所以需要檢查)。除非還有啟動服務的intent未傳送完會繼續傳送。適用於媒體播放器,不需要執行命令但需要一直執行並隨時待命。
- START_NOT_STICK:不會重建服務,除非還存在未傳送的intent。但服務不再必需的時候,這個是避免重啟服務的最安全的方法。
- START_REDELIVER_INTENT:重建服務,並且任何未傳入的intent的都會被依次送入。 適用於需要立即回覆工作的活躍服務,比如下載檔案。
- **操作:**建立一個Service繼承自service,在onStartCommand操作。在context中通過intent方式是啟動服務。
- 只能開啟或停止服務,無法操作服務。
- 呼叫者退出後,服務仍然存在。
bindservice
與Activity繫結,繫結之後在後臺執行,除非呼叫unBindService或繫結的Context被銷燬。
-
**生命週期:**onCreate(只執行一次)-onBind-onUnbind-onDestory ,如果先呼叫了startservice,已經onCreate,也不會再次呼叫。
-
**操作:**建立一個Binder繼承Binder,通過onBind返回Binder物件,在context中通過serviceConnection取到binder物件並呼叫bindner的方法,bindService中傳入ServiceConnection建立連線。
//在service中自定義Binder class MyBinder extends Binder{ public void startDownload(){ Log.d(TAG, "startDownload: "); } } //在onBind方法中返回Binder private MyBinder myBinder = new MyBinder(); @Override public IBinder onBind(Intent intent) { Log.d(TAG, "onBind: "); // TODO: Return the communication channel to the service. return myBinder; } //在Activity 中建立serviceconnection private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { myBinder = (MyService.MyBinder) service; myBinder.startDownload(); } @Override public void onServiceDisconnected(ComponentName name) { } }; //繫結服務 bindService(new Intent(MainActivity.this,MyService.class),connection,BIND_AUTO_CREATE);
-
除了可以開啟或停止服務,還可以獲得Service物件,對Service進行操作。
-
呼叫者退出後Service隨著呼叫者退出而銷燬。
服務的銷燬方式:
- 如果是startservice啟動的呼叫stop service就可以銷燬,或者在記憶體極低的情況下,被回收銷燬。
- 如果是通過bindservice啟動的服務呼叫unbindservice 銷燬服務;但是同時startservice和bindservice需要unbindservice及再次呼叫stopservice才會銷燬服務,即當service與activity繫結的情況下,service不再繫結且service處於靜止狀態時。 另外當service的呼叫者推出時也會銷燬服務。
前臺服務:
如果需要service一直保持執行狀態(service保活),則可以考慮前臺service。效果類似於通知。在service的onStartCommand方法中修改.(需要新增FOREGROUND_SERVICE許可權)
//8.0 適配通知欄
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
NotificationChannel channel = new NotificationChannel("service","test", NotificationManager.IMPORTANCE_DEFAULT);
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
assert manager != null;
manager.createNotificationChannel(channel);
NotificationCompat.Builder service = new NotificationCompat.Builder(this, "service");
service.setContentTitle("執行前臺服務的通知");
service.setContentText("執行前臺服務的內容");
service.setSmallIcon(R.mipmap.ic_launcher);
notification = service.getNotification();
}else {
Intent intent = new Intent(this,MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,0,intent,0);
Notification.Builder builder = new Notification.Builder(this);
builder.setContentTitle("執行前臺服務的通知");
builder.setContentText("執行前臺服務的內容");
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setContentIntent(pendingIntent);
notification = builder.getNotification();
}
//設定為前臺服務:引數1:唯一的通知標識。引數二:通知
startForeground(1,notification);
service與Thread的區別
兩者無聯絡。雖然都是在後臺執行一下耗時的操作,但是service是執行在主執行緒的,Thread是開啟的子執行緒執行。
遠端服務跨程序通訊
遠端服務的建立
在註冊服務的地方新增屬性:
android:process=":remote"
遠端服務是 執行在另一個程序。此時服務需要與Activity繫結的話需要使用AIDL。
同一個工程下使用:
-
建立一個AIDL檔案,在此檔案中定義方法,構建專案會自動生成一個介面檔案。改檔案是IBinder的子類。
-
在service檔案獲取該子類,並在onBinder方法返回。(程序S)
@Override public IBinder onBind(Intent intent) { Log.d(TAG, "onBind: "); // TODO: Return the communication channel to the service. return mBinder; } IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() { @Override public String toUppercase(String aString) throws RemoteException { if (!TextUtils.isEmpty(aString)){ return aString.toUpperCase(); } return null; } };
-
在activity中(程序C)修改
serviceconnection
-
private IMyAidlInterface iMyAidlInterface; //是IBinder的子類 ,AIDL ,mainactivity 和myservice 是不同的程序,此時實現了跨程序通訊 private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service); try { String hello_world = iMyAidlInterface.toUppercase("hello world"); Log.d(TAG, "onServiceConnected: "+hello_world); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { } };
因此實現了跨程序通訊。
不同工程下使用:
把AIDL檔案和Activity的內容移至另一個工程即可。使用隱式跳轉。在service中新增過濾器,在
Intent intent = new Intent(定義的action);
bindService(intent, connection, BIND_AUTO_CREATE);
Binder機制
概念:
Binder實現IBinder介面,Android 中實現跨程序通訊的機制。
跨程序通訊的原因:
為了資料的獨立性和安全性,一個程序不能訪問另一個程序的資料,即Android的程序是相互獨立、隔離的。如果需要讀取另一個程序的資料就需要IPC機制。
IPC機制基本原理
- 程序的空間分為使用者空間及系統空間,系統空間是全部程序公用的,使用者空間是每個程序私有的,當需要跨程序通訊時,程序1通過系統呼叫,將需要傳遞的資料複製到系統空間,由系統空間喚醒程序2的接收執行緒,通過系統呼叫將資料傳送到程序2的使用者空間(第二次複製),從而完成跨程序通訊。
Binder機制優點:
傳統的跨程序(socket)通訊缺點:1)複製兩次,費時間。 2)接收資料的快取有接收方提供,但接收方不知道需要提供多大合適。
而Binder機制呼叫系統函式mmap()記憶體對映,只需要複製一次即可。
Binder機制原理
利用Binder驅動建立接收快取區並實現地址對映關係:根據需對映的接收程序資訊,實現核心快取區和接收程序使用者空間地址同時對映到同1個共享接收快取區中。
Binder機制模型步驟
- 向驅動申請SM,驅動同意後成為SM,管理service。
- Client與Server與SM的通訊都是通過Binder驅動,他們不可以直接與SM互動。
- 註冊服務:
- Server程序向Binder驅動發起註冊服務請求。
- Binder驅動將註冊請求傳送給service manager程序。
- service manager程序新增該service程序,即註冊服務。
- 獲取服務:
- client程序傳遞需要獲取的服務名稱,向Binder驅動發起獲取服務請求。
- Binder驅動將請求轉發給SM。
- SM查詢到client需要的Server對應的服務資訊。
- 通過Binder驅動將上述資訊返回給client程序。
- 使用服務:
- Binder驅動為實現跨程序做準備(呼叫系統mmap()函式)實現記憶體對映。
- Binder驅動建立一塊接收快取區
- 實現地址對映關係:通過SM程序裡的server資訊找到server程序,實現核心快取區 和 server程序使用者空間地址 同時對映到同一接收快取中。
- client程序將引數資料傳送到server程序:
- client程序通過系統呼叫將資料傳送到核心快取區。 (存在記憶體對映關係,相當於也傳送到了server程序的使用者空間地址)
- Binder驅動通知server程序進行解包。
- server程序根據client程序要求呼叫目標方法:
- 收到Binder驅動通知後,server程序從執行緒池中取出執行緒,進行資料解包和呼叫目標方法。
- 將最終執行結果寫入到自己的共享記憶體中。
- server程序將目標方法結果返回給client程序:
- 由於存在記憶體對映關係,當server將結果寫入自己的記憶體中,Binder驅動通知client程序獲取返回結果(沒有起用新執行緒,之前傳送資料的執行緒被掛起)
- client程序通過系統呼叫從核心快取區接收server程序返回的資料。
- Binder驅動為實現跨程序做準備(呼叫系統mmap()函式)實現記憶體對映。
- 註冊服務:
服務的保活方式
- 在onStartCommand方法中返回START_STICK,在服務被殺死的時候會重新啟動。
- 把service的優先順序(1000)是最高優先順序,也可以把服務改為前臺服務,在系統記憶體不足時不會被回收。
- 使用AIDL跨程序機制雙程序保護。
- 使用JobService
- 使用自定義廣播,在應用退出時(傳送廣播啟動服務)
- 使用系統廣播,比如開機的時候,點選home鍵的時候啟動廣播。
IntentService
定義:
- Intent Service是繼承自service並處理非同步請求的服務。內部有一個工作執行緒處理耗時操作。
- 啟動Intent Service 執行完成後會自動停止,不需要呼叫stopself。
- 可以多次啟動Intent service,每一個耗時操作會以工作佇列的方式在intentservice的onHandleIntent回撥中執行,並且是依次執行。
- 內部封裝了Handler Thread 和Handler實現的。
使用場景:
- 一項任務需要幾個子任務進行,幾個子任務按順序進行才算完成。如在後臺默默進行耗時的上傳和下載操作。
問題
-
啟動intent service不需要建立新的執行緒?
在onCreate方法裡建立 了HandlerThread,這是一個繼承自Thread的類,在onCreate中也獲取了Looper進行工作。本來有一個執行緒,無需建立執行緒。
-
為什麼不建議通過 bindService() 啟動 IntentService?
intent service原始碼中的onBind方法預設返回null,不會回撥到onHanldeIntent方法中,沒有使用到intent service的優點,與普通service無區別。
-
為什麼多次啟動 IntentService 會順序執行事件,停止服務後,後續的事件得不到執行?
內部使用的是handler機制,多次啟動intent service不會重新建立新的執行緒和服務,而是把訊息加到訊息佇列裡,訊息佇列是一個單鏈表,訊息入列時的操作是根據時間入列,所以會按順序執行。停止服務後,會將訊息佇列的訊息清空,因此後續的事件得不到執行。