在我们的应用开发中,我们常常有跨进程通信的需求,如果使用AIDL方式,就是用Binder进行通信,往往会新建AIDL文件来定义好服务,服务端实现这些服务,而客户端会具体调用这些服务。
关于怎么新建AIDL文件,以及如何实现客户端与服务端,这里就不做讨论了,网上由于大把的教程,这里主要讨论下需要开发中注意的事项。
需要注意的事项如下:
当我们在studio中新建AIDL文件时,由于Server与Client需要共享原始的Bean与AIDL文件,并且两端需要有共同的包名,因此,我们往往通过studio中的如下命令来新建AIDL文件:
这样我们往往在src/main/目录下会新建一个aidl包,如下:
这个包与java包同级,同时为了避免aidl包中的类我们在编译的时候找不到,应该在build.gradle中添加如下配置:
sourceSets {
main {
java.srcDirs = ['src/main/java', 'src/main/aidl']
aidl.srcDirs = ['src/main/aidl']
}
}
不管是客户端还是服务器,都需要以上的配置,这样就能保证进行AIDL部分在客户端与服务端都具有相同的包名,便于序列化与反序列化。
private CopyOnWriteArrayList mBookList = new CopyOnWriteArrayList<>();
......
/**
* 运行在Binder线程池中
*/
private Binder mBinder = new IBookManager.Stub(){
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public List getBookList() throws RemoteException {
Log.d(TAG,"thread :" + Thread.currentThread().getName());
// try {
// Thread.sleep(10000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
return mBookList;
}
@Override
public void addBook(Book book) throws RemoteException {
mBookList.add(book);
}
......
}
客户端作为观察者,服务端作为被观察者,客户端注册某个监听到服务端,服务端当数据准备好或者条件满足,通过监听的回调通知客户端。
由于Binder通信是跨进程的,一般来说监听是需要跨进程传输,为了便于客户端的注册与反注册监听器,需要使用RemoteCallbackList< E >辅助类来实现监听的注册与反注册。
一个简单的例子如下:
服务端与客户端AIDL文件:
interface IBookManager {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
/*
* 获取所有的book
* */
List getBookList();
/*
* 添加一本书
* */
void addBook(in Book book);
void registerListener(in IOnNewBookArrivedListener listener);
void unRegisterListener(in IOnNewBookArrivedListener listener);
}
interface IOnNewBookArrivedListener {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
//新书到来
void onNewBookArrived(in Book book);
}
BookManagerService实现:
/**
* Created by qiyei2015 on 2017/4/7.
* [email protected]
*/
public class BookManagerService extends Service {
private static final String TAG = "BMS";
//需要同步
private AtomicBoolean mIsServiceDestroy = new AtomicBoolean(false);
private CopyOnWriteArrayList mBookList = new CopyOnWriteArrayList<>();
//无法取消注册
//private CopyOnWriteArrayList mListenerList = new CopyOnWriteArrayList<>();
//使用这个来取消注册
private RemoteCallbackList mRemoteCallbackList = new RemoteCallbackList<>();
/**
* 运行在Binder线程池中
*/
private Binder mBinder = new IBookManager.Stub(){
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public List getBookList() throws RemoteException {
Log.d(TAG,"getBookList thread :" + Thread.currentThread().getName());
return mBookList;
}
@Override
public void addBook(Book book) throws RemoteException {
mBookList.add(book);
}
@Override
public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
mRemoteCallbackList.register(listener);
Log.d(TAG,"mListenerList size:" + mRemoteCallbackList.beginBroadcast());
mRemoteCallbackList.finishBroadcast();
}
@Override
public void unRegisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
mRemoteCallbackList.unregister(listener);
Log.d(TAG,"mListenerList size:" + mRemoteCallbackList.beginBroadcast());
mRemoteCallbackList.finishBroadcast();
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG,"onBind");
return mBinder;
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG,"onCreate");
init();
new Thread(new ServiceWorker()).start();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG,"onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public boolean onUnbind(Intent intent) {
Log.d(TAG,"onUnbind");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
mIsServiceDestroy.set(true);
Log.d(TAG,"onDestroy");
super.onDestroy();
}
private void init(){
for (int i = 0;i < 10;i++){
Book book = new Book(i+1,"book_" + (i+1));
mBookList.add(book);
}
}
public class ServiceWorker implements Runnable{
@Override
public void run() {
while (!mIsServiceDestroy.get()){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int bookId = mBookList.size() + 1;
Book book = new Book(bookId,"book_" + bookId);
try {
notifyClientNewBook(book);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
/**
* 通知客户端新书到来
* @param book
* @throws Exception
*/
private void notifyClientNewBook(Book book) throws Exception{
mBookList.add(book);
int size = mRemoteCallbackList.beginBroadcast();
Log.d(TAG,"notifyClientNewBook: "+ book.toString());
for (int i = 0;i "notify listener: "+ listener.toString());
listener.onNewBookArrived(book);
}
mRemoteCallbackList.finishBroadcast();
}
/**
* 检查BookServiceDe调用权限
* @return
*/
private boolean checkBookServicePermission(){
int check = checkCallingOrSelfPermission("com.qiyei.ipc.book.ACCESS_BOOK_SERVICE");
return check == PackageManager.PERMISSION_DENIED;
}
}
客户端实现:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "Client";
private static final int MESSAGE_NEW_BOOK = 1;
private Context mContext;
private TextView mTextView;
private Button mButton;
private Button mClear;
private Button mButton2;
private IBookManager mBookManager;
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
if (msg.what == MESSAGE_NEW_BOOK){
Book book = (Book) msg.obj;
mTextView.setText(book.toString());
}
}
};
/**
* Binder死亡代理 ,Binder死亡时会回调该方法
*/
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
if (mBookManager == null){
return;
}
mBookManager.asBinder().unlinkToDeath(mDeathRecipient,0);
mBookManager = null;
//重新连接服务
bindBookService();
}
};
/**
* 运行在主线程中
*/
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG,"onServiceConnected thread :" + Thread.currentThread().getName());
mBookManager = IBookManager.Stub.asInterface(service);
try {
mBookManager.asBinder().linkToDeath(mDeathRecipient,0);
mBookManager.registerListener(mListener);
} catch (RemoteException e) {
e.printStackTrace();
}
try {
mTextView.setText(mBookManager.getBookList().toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
//bindBookService();
}
};
/**
* Service调用,运行在Client端的Binder线程中
*/
private IOnNewBookArrivedListener mListener = new IOnNewBookArrivedListener.Stub(){
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public void onNewBookArrived(Book book) throws RemoteException {
Log.d(TAG,"onNewBookArrived thread :" + Thread.currentThread().getName());
Message message = Message.obtain();
message.what = MESSAGE_NEW_BOOK;
message.obj = book;
mHandler.sendMessage(message);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = this;
initView();
}
private void initView(){
mTextView = (TextView) findViewById(R.id.tv1);
mClear = (Button) findViewById(R.id.btn0);
mClear.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mTextView.setText("");
}
});
mButton = (Button) findViewById(R.id.btn1);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
bindBookService();
}
});
mButton2 = (Button) findViewById(R.id.btn2);
mButton2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
unbindService(mServiceConnection);
startActivity(new Intent(mContext,TestActivity.class));
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mBookManager != null && mBookManager.asBinder().isBinderAlive()){
try {
mBookManager.unRegisterListener(mListener);
Log.d(TAG,"unRegister listener ");
} catch (RemoteException e) {
e.printStackTrace();
}
}
unbindService(mServiceConnection);
}
/**
* 绑定BookService
*/
private void bindBookService(){
Intent intent = new Intent();
//这里需要指定Action 与packageName
intent.setAction("android.intent.action.BOOK_MANAGER_SERVICE");
intent.setPackage("com.qiyei.ipcserver");
bindService(intent,mServiceConnection, BIND_AUTO_CREATE);
}
}
客户端直接在MainActivty去连接服务,并且在绑定成功后的onServiceConnected(ComponentName name, IBinder service)去注册IOnNewBookArrivedListener ,客户端自己实现该监听器,并在Activity的onDestroy中去取消注册与解绑服务。
对于服务端来说,客户端调用服务端的服务,具体的服务方法运行在服务端的Binder线程池中,并没有运行在服务端的主线程中。并且调用会阻塞。
在上面的服务BookManagerService中:
/**
* 运行在Binder线程池中
*/
private Binder mBinder = new IBookManager.Stub(){
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public List getBookList() throws RemoteException {
Log.d(TAG,"getBookList thread :" + Thread.currentThread().getName());
return mBookList;
}
@Override
public void addBook(Book book) throws RemoteException {
mBookList.add(book);
}
@Override
public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
mRemoteCallbackList.register(listener);
Log.d(TAG,"mListenerList size:" + mRemoteCallbackList.beginBroadcast());
mRemoteCallbackList.finishBroadcast();
}
@Override
public void unRegisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
mRemoteCallbackList.unregister(listener);
Log.d(TAG,"mListenerList size:" + mRemoteCallbackList.beginBroadcast());
mRemoteCallbackList.finishBroadcast();
}
};
当运行时,可以看到如下输出:
可以看到,确实运行在服务端的Binder线程中
对于客户端来说, 绑定服务成功后的回调ServiceConnection是运行在主线程(UI线程中的),同时由于调用服务端的服务可能会阻塞,因此尽量避免在主线程中去调用服务端的服务。另外,服务端回调客户端的监听Listener中的方法却是运行在客户端的Binder线程池中,因此不能直接更新UI,我们运行客户端的,可以看到如下:
/**
* Binder死亡代理 ,Binder死亡时会回调该方法
*/
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
if (mBookManager == null){
return;
}
mBookManager.asBinder().unlinkToDeath(mDeathRecipient,0);
mBookManager = null;
//重新连接服务
bindBookService();
}
};
/**
* 运行在主线程中
*/
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG,"onServiceConnected thread :" + Thread.currentThread().getName());
mBookManager = IBookManager.Stub.asInterface(service);
try {
mBookManager.asBinder().linkToDeath(mDeathRecipient,0);
mBookManager.registerListener(mListener);
} catch (RemoteException e) {
e.printStackTrace();
}
try {
mTextView.setText(mBookManager.getBookList().toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
//bindBookService();
}
};
这样当Binder意外死亡时,会回调binderDied,我们在里面去重连服务即可。
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG,"onBind");
if (checkBookServicePermission()){
return mBinder;
}else {
return null;
}
}
/**
* 检查BookServiceDe调用权限
* @return
*/
private boolean checkBookServicePermission(){
int check = checkCallingOrSelfPermission("com.qiyei.ipc.book.ACCESS_BOOK_SERVICE");
return check == PackageManager.PERMISSION_DENIED;
}
其中com.qiyei.ipc.book.ACCESS_BOOK_SERVICE定义在服务端的mainfest文件中:
<permission android:name="com.qiyei.ipc.book.ACCESS_BOOK_SERVICE"
android:protectionLevel="normal"/>
......
<service android:name=".service.BinderPoolService"
android:exported="true"
android:permission="com.qiyei.ipc.book.ACCESS_BOOK_SERVICE">
<intent-filter>
<action android:name="android.intent.action.BINDER_POOL_SERVICE"/>
intent-filter>
service>
这样只有客户端有这个权限时才能访问这个服务。
(2)在服务端的Binder对象中onTransact()根据Uid与pid来验证客户端的权限
比如,我们在实现返回的Binder对象中时,重写onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException方法,通过getCallingUid和getCallingPid方法来得到客户端的包名,根据具体的包名来决定是否给予访问的权限,注意,这一条也可以在IBinder onBind(Intent intent)中使用。
/**
* 运行在Binder线程池中
*/
private Binder mBinder = new IBookManager.Stub(){
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public List getBookList() throws RemoteException {
Log.d(TAG,"getBookList thread :" + Thread.currentThread().getName());
return mBookList;
}
@Override
public void addBook(Book book) throws RemoteException {
mBookList.add(book);
}
@Override
public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
mRemoteCallbackList.register(listener);
Log.d(TAG,"mListenerList size:" + mRemoteCallbackList.beginBroadcast());
mRemoteCallbackList.finishBroadcast();
}
@Override
public void unRegisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
mRemoteCallbackList.unregister(listener);
Log.d(TAG,"mListenerList size:" + mRemoteCallbackList.beginBroadcast());
mRemoteCallbackList.finishBroadcast();
}
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
String packageName = null;
String[] packages = getPackageManager().getPackagesForUid(getCallingUid());
if (packages != null && packages.length > 0){
packageName = packages[0];
}
Log.d(TAG,"packageName:" + packageName);
if (!packageName.startsWith("com.qiyei.ipc")){
return false;
}
return super.onTransact(code, data, reply, flags);
}
};
上面就是判断包名是否以”com.qiyei.ipc”开头,符合才给予权限访问。
本篇的介绍就到此结束,后续会继续补充,相关代码下载见我的github
源代码下载