前一段时间,在工作中遇到一个问题,在网上寻找答案良久,只可惜没能找到,所以只能自己写个小demo,来验证自己的想法了。
需求是这样:两个应用程序(进程)A和B之间通信,A需要通知B完成某一个操作,然后等待B的结果,只有等待B执行成功了,A才能进行下一步的操作。
进程间通信嘛,当仁不让AIDL,但是一旦选择了AIDL,那么也就意味着线程同步机制完全失效。也就是说,可能B的执行还没结束,或者B执行失败,A就已经进行下一步的操作了,这样和我们当初的想法就相违背了。有的人会说,可以使用线程休眠啊,A中睡眠一段时间,等待B执行的结束不就欧了嘛,但是,那假如B执行失败,或者执行了一个耗时操作,A无法预估B的成功与否呢?所以,这种做法并不能保证100%的“同步”,这种做法是不可靠的。那么,有没有一种解决办法,能实现进程间通信线程“同步”的问题呢?
答案肯定是有的,废话不多说,首先说下项目结构:
一:有两个进程,我这里创建了两个项目,分别 为IPC_A 和 IPC_B,代表进程A和进程B:
二:进程间通讯,需要用到AIDL的相关知识,这里不做详细介绍,由上面的项目结构可以看到,A进程相当于客户端,B进程相当于服务端,这两个进程定义的AIDL文件如下:
package com.example.aidl;
interface AidlCommunication {
void todoSomeThing();
}
文件很简单,只是定义了一个接口,表示去做一些操作
三:进程A的代码:
package com.example.ipc_a; import com.example.aidl.AidlCommunication; import com.example.aidl.ResponseSuccessListener; import com.example.ipc.R; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.util.Log; import android.view.View; public class MainActivity extends Activity { protected static final String TAG = "MainActivityA"; private static boolean isFinished = false; private static boolean isWait = true; private AidlCommunication mService; private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { Log.d(TAG, "AIDL未连接"); } @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.d(TAG, "AIDL已连接"); mService = AidlCommunication.Stub.asInterface(service); try { Log.d(TAG, "A:通知进程B,执行某一任务"); mService.todoSomeThing(); } catch (RemoteException e) { e.printStackTrace(); } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void Aidl(View view) { Log.d(TAG, "A:绑定服务,连接AIDL"); Intent intent = new Intent(); intent.setAction("android.intent.action.ServiceB"); intent.setPackage("com.example.ipc_b"); bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}@Overrideprotected void onDestroy() {super.onDestroy();// 断开AIDL连接unbindService(mConnection);}}// A进程进入下一步操作 Log.d(TAG, "A:进行了下一步操作");
A程序效果图:
由效果图可知,点击按钮,会启动AIDL,连接成功后,A进程会让B进行某些操作
四,进程B的代码:
package com.example.ipc_b; import android.os.Bundle; import android.app.Activity; import android.content.Intent; import android.widget.Toast; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); startService(new Intent(MainActivity.this, ServiceB.class)); Toast.makeText(MainActivity.this, "ServiceB启动了...", Toast.LENGTH_SHORT) .show(); } @Override protected void onDestroy() { super.onDestroy(); } }
ServiceB:package com.example.ipc_b; import java.util.concurrent.CopyOnWriteArrayList; import com.example.aidl.AidlCommunication; import com.example.aidl.ResponseSuccessListener; import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.os.SystemClock; import android.util.Log; public class ServiceB extends Service { private static final String TAG = "ServiceB"; @Override public void onCreate() { super.onCreate(); } @Override public IBinder onBind(Intent intent) { return mBinder; } private Binder mBinder = new AidlCommunication.Stub() { @Override public void todoSomeThing() throws RemoteException { // 通知B去执行某一操作 new Thread() { public void run() { for (int i = 1; i < 6; i++) { SystemClock.sleep(1000); Log.d(TAG, "B:处理中...进行了" + i + "秒,请等待"); } Log.d(TAG, "B:处理完成~!"); for (int j = 0; j < listenerList.size(); j++) { ResponseSuccessListener listener = listenerList.get(j); try { //B处理完,就告知A Log.d(TAG, "B:已经处理完,A可以进行下一步操作"); listener.ResponseSuccess(); } catch (RemoteException e) { e.printStackTrace(); } } }; }.start(); } }; }
B程序效果图:
打开程序B,后台服务ServiceB就会自动启动
另外,程序B的清单文件添加ServiceB时,需要配置下:
<service android:name="com.example.ipc_b.ServiceB" > <intent-filter> <action android:name="android.intent.action.ServiceB" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </service>
五:现在,进行测试。如果按照普通的AIDL连接方式,我们希望A通知B进行一个耗时的操作,然后A需要根据B操作的结果,去做不同的处理。先手动开启B进程的ServiceB,服务ServiceB启动(如上图),然后打开A进程,连接AIDL,连接成功后我们来看Log:
D/MainActivityA( 4823): A:绑定服务,连接AIDL D/MainActivityA( 4823): A:进行下了一步操作 D/MainActivityA( 4823): AIDL已连接 D/MainActivityA( 4823): A:通知进程B,执行某一任务 D/ServiceB( 4808): B:处理中...进行了1秒,请等待 D/ServiceB( 4808): B:处理中...进行了2秒,请等待 D/ServiceB( 4808): B:处理中...进行了3秒,请等待 D/ServiceB( 4808): B:处理中...进行了4秒,请等待 D/ServiceB( 4808): B:处理中...进行了5秒,请等待 D/ServiceB( 4808): B:处理完成~!
可以看到,确实进程间通讯,同步机制已经失效,B还没来得及处理完A的请求,A就已经进入下一步的操作了,这不是我们想看到的。如何处理?其实我们只用在AIDL里面定义监听就OK了,这个类似Android里面的监听器。只需要在原有代码的基础上增加一些细节就够了:
六,先在AIDL文件目录下定义AIDL监听,文件名为ResponseSuccessListener.aidl,顾名思义,响应成功的监听,与AIDL文件定义类似,A和B都需要在AIDL目录下定义该文件:
package com.example.aidl; interface ResponseSuccessListener { void ResponseSuccess(); }
定义好之后,就是引用了。需要注意,我们要在原先的AIDL文件中增加两个方法,分别为注册监听、注销监听(注意导包):package com.example.aidl; import com.example.aidl.ResponseSuccessListener; interface AidlCommunication { void todoSomeThing(); void registerListener(ResponseSuccessListener listener); void unregisterListener(ResponseSuccessListener listener); }
文件定义好后,接下来,就是代码的添加了A进程做如下修改:
package com.example.ipc_a; import com.example.aidl.AidlCommunication; import com.example.aidl.ResponseSuccessListener; import com.example.ipc.R; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.util.Log; import android.view.View; public class MainActivity extends Activity { protected static final String TAG = "MainActivityA";
private static boolean isWait = true; //是否等待private AidlCommunication mService;private ServiceConnection mConnection = new ServiceConnection() {@Overridepublic void onServiceDisconnected(ComponentName name) {Log.d(TAG, "AIDL未连接");}@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {Log.d(TAG, "AIDL已连接");mService = AidlCommunication.Stub.asInterface(service);try {Log.d(TAG, "A:通知进程B,执行某一任务");mService.registerListener(listener);mService.todoSomeThing();} catch (RemoteException e) {e.printStackTrace();}}};// 注册监听private ResponseSuccessListener listener = new ResponseSuccessListener.Stub() {@Overridepublic void ResponseSuccess() throws RemoteException {isFinished = true;}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}public void Aidl(View view) {Log.d(TAG, "A:绑定服务,连接AIDL");Intent intent = new Intent();intent.setAction("android.intent.action.ServiceB");intent.setPackage("com.example.ipc_b");bindService(intent, mConnection, Context.BIND_AUTO_CREATE);new Thread() {public void run() {while (isWait) {if (isFinished) {isWait = false;}}isWait = true;isFinished = false;// A进程进入下一步操作Log.d(TAG, "A:进行了下一步操作");};}.start();}@Overrideprotected void onDestroy() {super.onDestroy();// 断开AIDL连接unbindService(mConnection);try {mService.unregisterListener(listener);} catch (RemoteException e) {e.printStackTrace();}}}private static boolean isFinished = false; //B是否完成操作
由于B是耗时操作,相应地,A也需要在子线程开启阻塞式的循环等待B执行的结束。一旦B执行成功,会告知A,走ResponseSuccess()的方法,表示相应成功,此时A才会继续下一步的操作。
另外ServiceB也做一些改动:
package com.example.ipc_b; import java.util.concurrent.CopyOnWriteArrayList; import com.example.aidl.AidlCommunication; import com.example.aidl.ResponseSuccessListener; import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.os.SystemClock; import android.util.Log; public class ServiceB extends Service { private static final String TAG = "ServiceB"; @Override public void onCreate() { super.onCreate(); } @Override public IBinder onBind(Intent intent) { return mBinder; } private CopyOnWriteArrayList<ResponseSuccessListener> listenerList = new CopyOnWriteArrayList<ResponseSuccessListener>(); private Binder mBinder = new AidlCommunication.Stub() { @Override public void todoSomeThing() throws RemoteException { // 通知B去执行某一操作 new Thread() { public void run() { for (int i = 1; i < 6; i++) { SystemClock.sleep(1000); Log.d(TAG, "B:处理中...进行了" + i + "秒,请等待"); } Log.d(TAG, "B:处理完成~!"); for (int j = 0; j < listenerList.size(); j++) { ResponseSuccessListener listener = listenerList.get(j); try { //B处理完,就告知A Log.d(TAG, "B:已经处理完,A可以进行下一步操作"); listener.ResponseSuccess(); } catch (RemoteException e) { e.printStackTrace(); } } }; }.start(); } @Override public void registerListener(ResponseSuccessListener listener) throws RemoteException { if (!listenerList.contains(listener)) { listenerList.add(listener); } } @Override public void unregisterListener(ResponseSuccessListener listener) throws RemoteException { } }; }
注意到,我们这里用到了CopyOnWriteArrayList,CopyOnWriteArrayList也是继承自ArrayList,但是它可以支持多并发的读/写,如果有多个进程同时请求B进行操作,这里能保证进程B的线程同步。注册监听用到添加到集合的方式,也是考虑到多个客户端(A)同时与服务端(B)通信时的情况。
七,验证:
代码都完善了,现在来检验下。启动ServiceB,再打开A,点击按钮连接AIDL,最终打印的Log:
D/MainActivityA( 4576): A:绑定服务,连接AIDL D/MainActivityA( 4576): AIDL已连接 D/MainActivityA( 4576): A:通知进程B,执行某一任务 D/ServiceB( 4558): B:处理中...进行了1秒,请等待 D/ServiceB( 4558): B:处理中...进行了2秒,请等待 D/ServiceB( 4558): B:处理中...进行了3秒,请等待 D/ServiceB( 4558): B:处理中...进行了4秒,请等待 D/ServiceB( 4558): B:处理中...进行了5秒,请等待 D/ServiceB( 4558): B:处理完成~! D/ServiceB( 4558): B:已经处理完,A可以进行下一步操作 D/MainActivityA( 4576): A:进行了下一步操作
对比第五步,可以明显看出执行顺序的不同。A在B成功执行完成后,才进入了下一步的操作。所以,进程间通信,其实也能够实现“线程同步”的,只是我们换了一种方式!办法总是有的,是的,有人可能会有疑问,干嘛这么复杂?其实完全可以利用广播啊,进程间通信,广播不是更简单吗,为毛还要用到AIDL注册什么监听?
广播其实也是可以的,不过我只是简化了开头的需求,如果我们需要用到B执行完后的结果呢?换句话说,B执行成功后,需要返回给A数据,A利用接受的数据做下一步处理呢?这个时候单纯的用广播肯定时不行的,因为广播只能起到通知的作用,而AIDL不仅能起到通知作用(AIDL监听),而且能支持序列化的对象的传递,这也是AIDL的优势所在。之所以这样处理,也是为了保证进程间通信的健壮性和可维护性,如果后期有需求有变动,也仅仅是添加一些逻辑完善下罢了。
关于AIDL序列化对象的传递,我这里就不做过多介绍啦,后面的blog会有更新。另外,今天介绍的AIDL监听,你以为到这里结束了?其实远不止这些,但是已经能满足开头的需求了。关于一些AIDL的优化和注意的细节,我下次会详细介绍,敬请期待~!