Android解决进程间通信,线程同步的问题

前一段时间,在工作中遇到一个问题,在网上寻找答案良久,只可惜没能找到,所以只能自己写个小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);
		// A进程进入下一步操作
		Log.d(TAG, "A:进行了下一步操作");
}@Overrideprotected void onDestroy() {super.onDestroy();// 断开AIDL连接unbindService(mConnection);}}



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 isFinished = false;	//B是否完成操作
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();}}}


由于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的优化和注意的细节,我下次会详细介绍,敬请期待~!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值