IPC方式
1.使用Bundle
我们知道四大组件中的三大组件(Activity、Service、Receiver)都是支持在Intent中传递Bundle数据的,由于Bundle实现了Parcelable接口,所以他可以很方便的在不同进程中传输。
Bundle传递的数据类型有哪些?
(1)基本类型数据
(2)实现了序列化接口的对象(Parcellable以及Serializable接口)
如下代码:我们在Bundle存储数据,并通过intent发送出去。
Intent intent = new Intent();
intent.setClass(this, ActivityA.class);
Bundle bundle = new Bundle();
bundle.putCharSequence("name", "花花");
intent.putExtras(bundle);
startActivity(intent);
获取Binder对象以及其中的数据。
Bundle b=getIntent().getExtras();
String name= b.getString("name");
使用文件共享
共享文件是通过两个进程通过读/写同一个文件来交换数据,比如A进程把数据写入文件,B进程通过读取这个文件来获取数据。
新建项目,首先我们再MainActivity中,再文件中存储一串数据。
Context 类中提供了一个 openFileOutput ()方法,可以用于将数据存储到指定的文件中。这个方法接收两个参数,第一个参数是文件名,在文件创建的时候使用的就是这个名称,注意这里指定的文件名不可以包含路径,因为所有的文件都是默认存储到/data/data/packagename/files/ 目 录 下 的 。
private void writeInFile() {
String data = "this is my book";
FileOutputStream out = null;
BufferedWriter writer = null;
try {
out = openFileOutput("data", Context.MODE_PRIVATE);
writer = new BufferedWriter(new OutputStreamWriter(out));
writer.write(data);
handler.sendEmptyMessage(0);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
然后新建ActivityA,在此界面中去读取数据:在弹出的Toast中读取到了我们写入的字符串数据。
private void readFile() {
FileInputStream in=null;
BufferedReader reader=null;
StringBuilder content=new StringBuilder();
try {
in=openFileInput("data");
reader=new BufferedReader(new InputStreamReader(in));
String line="";
while ((line=reader.readLine())!=null){
content.append(line);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (reader!=null){
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Toast.makeText(this,content.toString(),Toast.LENGTH_LONG).show();
}
SharedPreference是个特例,它是Android中提供的轻量级存储方案,他通过键值对的方式来存储数据,在底层实现上采用了XML文件来存储键值对。从本质上说SharedPreference也属于文件的一种,但是系统对它的读写有一定的缓存策略,记载内存中会有一份SharedPreference的文件缓存,因此在多进程情况下,系统对它的读/写就变得不可靠。
使用Messenger
Messenger可以翻译为信史,顾名思义,通过它可以再不同进程中传递Message对象,在Message中放入我们需要传递的数据,就可以轻松的实现数据的进程间传递。Messenger是一种轻量级的IPC方案,底层实现为AIDL。Messenger对AIDL做了封装,我们可以更简便的进行进程间通讯。由于它每次只处理一个请求,因此我们再服务端不用考虑线程同步的问题,这是因为在服务端中不存在并发执行的情况。
Messenger的构造方法,一个传入Handler对象,另外一个传入Ibinder对象。
public Messenger(Handler target) {
throw new RuntimeException("Stub!");
}
public Messenger(IBinder target) {
throw new RuntimeException("Stub!");
}
/**次方法用于返回Binder对象*/
public IBinder getBinder() {
throw new RuntimeException("Stub!");
}
/**次方法用于发送一条Message消息*/
public void send(Message message) throws RemoteException {
throw new RuntimeException("Stub!");
}
Messenger的使用方法以及实现步骤
1、服务端进程
首先,我们需要在服务端创建按一个Service用来处理客户端的链接请求,同时创建一个Handler并通过它来创建爱你一个Messenger对象,然后再Service的OnBind中返回这个Messenger对象底层的Binder即可。
2.客户端进程
在客户端进程中,我们首先要绑定到服务端的Service,绑定成功后用服务端返回的IBinder对象创建一个Messenger,通过这个Messenger就可以向服务端发送消息了,发送消息类型为Message对象。
step1:创建服务端项目,并新建MessengerService.java文件
public class MessengerService extends Service {
private static final String TAG = "MessengerService";
private static class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
Log.e(TAG, "receive msg from Client:" + msg.getData().getString("msg"));
break;
default:
super.handleMessage(msg);
}
}
}
private final Messenger messenger=new Messenger(new MessengerHandler());
@Nullable
@Override
public IBinder onBind(Intent intent) {
return messenger.getBinder();
}
}
step2:在Mainfest文件中注册
<!--让服务运行在单独的进程中-->
<service android:name=".service.MessengerService"
android:process=":romote"
>
<intent-filter>
<action android:name="com.example.ipc.service.MessengerService"></action>
</intent-filter>
</service>
step3:创建客户端项目,Client
public class MainActivity extends AppCompatActivity {
private static final String TAG="MainActivity";
private Messenger messengerService;
ServiceConnection conn=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
/**创建Messenger对象*/
messengerService=new Messenger(iBinder);
Message msg=Message.obtain(null,Sign.MSG_FROM_CLIENT);
Bundle data=new Bundle();
data.putString("msg","hello ,this is client.");
msg.setData(data);
try {
/**通过Messenger对象发送Message消息*/
messengerService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
messengerService=null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/**Step1:绑定服务端service*/
Intent intent=new Intent("com.example.ipc.service.MessengerService");
bindService(intent,conn, Context.BIND_AUTO_CREATE);
}
/**解绑Service*/
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(conn);
}
}
然后运行项目,首先运行服务端,然后运行客户端。可以看到收到了客户端发送的消息。
通过以上例子,我们可以看到Messenger传递数据,必须将Message消息放入Messenger中。Messenger和Message都实现了Parcelable接口,因此可以进行跨进程传输。然而这个只是客户端发消息给服务端,这时候我们需要服务端能给客户端发送消息。首先修改服务端代码:
public class MessengerService extends Service {
private static final String TAG = "MessengerService";
private static class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case Sign.MSG_FROM_CLIENT:
Log.e(TAG, "receive msg from Client:" + msg.getData().getString("msg"));
/**获取到Messenger对象*/
Messenger client=msg.replyTo;
/**创建Message*/
Message reply=Message.obtain(null, Sign.MSG_FROM_SERVICE);
Bundle bundle=new Bundle();
bundle.putString("reply","I am receive msg");
reply.setData(bundle);
try {
/**通过Messenger向客户端发送消息*/
client.send(reply);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
super.handleMessage(msg);
}
}
}
private final Messenger messenger=new Messenger(new MessengerHandler());
@Nullable
@Override
public IBinder onBind(Intent intent) {
return messenger.getBinder();
}
}
然后是客户端,既然要接收到服务端返回的消息,我们也定义一个接收消息的Messenger和Handler。如下:
public class MainActivity extends AppCompatActivity {
private static final String TAG="MessengerService";
private Messenger messengerService;
private Messenger mGetReplyMessenger=new Messenger(new MessengerHandler());
private static class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case Sign.MSG_FROM_SERVICE:
Log.e(TAG,"receive msg from Service:"+msg.getData().getString("reply"));
break;
default:
super.handleMessage(msg);
}
}
}
ServiceConnection conn=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
/**创建Messenger对象*/
messengerService=new Messenger(iBinder);
Message msg=Message.obtain(null,Sign.MSG_FROM_CLIENT);
Bundle data=new Bundle();
data.putString("msg","hello ,this is client.");
msg.setData(data);
//注意:
msg.replyTo=mGetReplyMessenger;
try {
/**通过Messenger对象发送Message消息*/
messengerService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
messengerService=null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/**Step1:绑定服务端service*/
Intent intent=new Intent("com.example.ipc.service.MessengerService");
bindService(intent,conn, Context.BIND_AUTO_CREATE);
}
/**解绑Service*/
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(conn);
}
}
结果:我们可以看到,手下服务端接收到了客户端发送的消息,然后服务端作出响应返回给客户端。
AIDL方式
这里就不做具体的例子了,上一篇文章介绍过了。
1、aidl文件支持的数据类型?
(1)基本数据类型(int,long,char,boolean,double)
(2)String 和CharSequence
(3)List,只支持ArrayList,里面每个元素都能被AIDL支持。
(4)Map,只支持HashMap,里面的每个元素都必须被AIDL支持,包括key和value。
(5)Parcelable:所有实现了Parcelable接口的对象。
(6)所有的AIDL接口本身也可以在AIDL文件中使用。
2、服务端可以使用CopyOnWriteArrayList和ConcurrentHashMap来进行自动线程同步,客户端拿到的依然是ArrayList和HashMap;
3、服务端和客户端之间做监听器,服务端需要使用RemoteCallbackList,否则客户端的监听器无法收到通知(因为服务端实质还是一份新的序列化后的监听器实例,并不是客户端那份);
4、客户端通过IBinder.DeathRecipient来监听Binder死亡,也可以在onServiceDisconnected中监听并重连服务端。区别在于前者是在binder线程池中,访问UI需要用Handler,后者则是UI线程。
5、可通过自定义权限在onBind或者onTransact中进行权限验证。
ContentProvider
Android中专门用作不同应用间数据共享的方式,底层实现也是Binder。
Socket
Socket 一般用于网络通信,AIDL用这种方式会过于繁琐,不建议。它在网络通讯中被称为“套接字”,分为流式套接字和用户数据报套接字两种,分别对应网络传输控制层中的TCP和UDP协议。
TCP
面向链接的协议、双向通讯稳定,建立链接需要三次握手,数据传输功能稳定。
UDP
无连接,提供不稳定单项通讯,也可实现双向通讯,效率较高,但传输数据的正确率低。
使用事项
(1)声明权限:
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
Binder连接池
Binder连接池,通过BinderPool的方式将Binder的控制与Service本身解耦,同时只需要维护一份Service即可。这里用到了CountDownLatch,大概解释下用意:线程在await后等待,直到CountDownLatch的计数为0,BinderPool里使用它的目的是为了保证Activity获取BinderPool的时候Service已确定bind完成。
选择合适的IPC方式