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进程仍然会被干掉。
欢迎关注个人微信公众号,期待与你一块儿学习,一块儿交流,一块儿成长!
复制代码