Android多進程系列
經過前幾篇文章,咱們對Binder的使用和工做流程有了必定的瞭解,可是還有幾個問題休要咱們去解決。一個是若是服務端進程意外退出,Binder死亡,那客戶端就會請求失敗;還有一個就是權限校驗問題,就是服務端須要校驗一下客戶端的身份權限,不能誰都能請求服務端的服務
Binder意外死亡的處理
給Binder設置DeathRecipient監聽
- 在綁定Service服務後的onServiceConnected回調中給Binder註冊死亡回調DeathRecipient
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Log.e(TAG, "ServiceConnection-->"+ System.currentTimeMillis());
IBookManager bookManager = BookManagerImpl.asInterface(iBinder);
mRemoteBookManager = bookManager;
try {
//註冊死亡回調
iBinder.linkToDeath(mDeathRecipient,0);
...
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
Log.e(TAG, "onServiceDisconnected-->binder died");
}
};
複製代碼
- 在DeathRecipient中相應的處理,好比從新鏈接服務端
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
Log.e(TAG, "mDeathRecipient-->binderDied-->");
if (mRemoteBookManager == null) {
return;
}
mRemoteBookManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
mRemoteBookManager = null;
//Binder死亡,從新綁定服務
Log.e(TAG, "mDeathRecipient-->bindService");
Intent intent = new Intent(MainActivity.this, BookManagerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
};
複製代碼
@Override
public void onCreate() {
super.onCreate();
Log.e(TAG, "onCreate-->"+ System.currentTimeMillis());
new Thread(new ServiceWorker()).start();
}
private class ServiceWorker implements Runnable {
@Override
public void run() {
while (!mIsServiceDestoryed.get()) {
try {
Thread.sleep(5000);
}catch (InterruptedException e) {
e.printStackTrace();
}
int bookId = mBookList.size() + 1;
if (bookId == 8) {
//結束當前進程,測試Binder死亡回調
android.os.Process.killProcess(android.os.Process.myPid());
return;
}
...
}
}
}
複製代碼
從上面的測試咱們能夠看到客戶端在服務端進程意外退出後,經過從新綁定服務又把服務端進程啓動了。此外,咱們還能夠在ServiceConnection的onServiceDisconnected方法中處理服務端進程意外退出的狀況,方法是同樣的,就不測試了。2種方法的區別就在於onServiceDisconnected方法在客戶端的UI線程中被回調,而binderDied方法在客戶端的Binder線程池中被回調
權限驗證
在onBind中經過自定義權限來驗證
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.xxq2dream.android_ipc">
<permission
android:name="com.xxq2dream.permission.ACCESS_BOOK_SERVICE"
android:protectionLevel="normal"/>
</manifest>
複製代碼
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.e(TAG, "onBind-->"+ System.currentTimeMillis());
int check = checkCallingOrSelfPermission("com.xxq2dream.permission.ACCESS_BOOK_SERVICE");
if (check == PackageManager.PERMISSION_DENIED) {
Log.e(TAG, "PERMISSION_DENIED");
return null;
}
Log.e(TAG, "PERMISSION_GRANTED");
return mBinder;
}
複製代碼
- 從上圖中咱們能夠看到,因爲咱們的應用客戶端沒有聲明服務端校驗的權限,因此服務端校驗不經過,咱們只須要在咱們的客戶端添加相應的權限聲明便可
<uses-permission android:name="com.xxq2dream.permission.ACCESS_BOOK_SERVICE"/>
複製代碼
在onTransact方法中進行權限校驗
private Binder mBinder = new BookManagerImpl(){
@Override
public List<Book> getBookList() throws RemoteException {
Log.e(TAG, "getBookList-->"+ System.currentTimeMillis());
return mBookList;
}
...
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
int check = checkCallingOrSelfPermission("com.xxq2dream.permission.ACCESS_BOOK_SERVICE");
if (check == PackageManager.PERMISSION_DENIED) {
Log.e(TAG, "PERMISSION_DENIED");
return false;
}
String packageName = null;
//獲取客戶端包名
String[] packages = getPackageManager().getPackagesForUid(getCallingUid());
if (packages != null && packages.length > 0) {
packageName = packages[0];
}
//校驗包名
if (!packageName.startsWith("com.xxq2dream")) {
return false;
}
Log.e(TAG, "PERMISSION_GRANTED");
return super.onTransact(code, data, reply,flags);
}
};
複製代碼
結語
- Binder的一個很好的應用就是推送消息和保活。好比咱們能夠建立一個Service運行在一個獨立的進程中,而後和咱們的應用進程中的一個Service綁定。獨立進程的Service每隔必定的時間向咱們的服務端請求查看是否有新的消息,有的話就拉取新的消息,而後通知給應用進程的Service,執行彈出通知之類的操做。應用程序退出後,咱們的Service進程仍是存活的,這樣就能夠一直接收消息。固然,若是用戶一鍵清理或是直接結束應用的話,咱們的Service進程仍然會被幹掉。
歡迎關注個人微信公衆號,期待與你一塊兒學習,一塊兒交流,一塊兒成長!
複製代碼