您的位置:首页 > 其它

IPC 机制(三)

2016-03-05 18:42 267 查看
4.使用ContentProvider

CotentProvider是Android中提供专门用于不同应用间进行数据共享的方式,从这一点来看,它天生就适合进程间通信。

5.使用Socket



我们也可以通过Socket来实现进程间的通信,Socket也称为“套接字”,是网络通信中的概念,它分为流式套接字和用户数据报套接字两种,分别对应于网络的传输控制层中的TCP和UDP协议。TCP协议是面向连接的协议,提供稳定的双向通信功能,TCP链接的建立需要经过“三次握手”才能完成,为了提供稳定的数据传输功能,其本身提供超时重传机制,因此具有很高的稳定性;而UDP是无连接的,提供不稳定的单向通信功能,当然UDP也可以实现双向通信功能。在性能上,UDP具有很好的效率,确定是不能保证数据一定能够正确传输,尤其是在网络拥堵的情况下。下面是一个跨进程连天的程序,两个进程可以通过Socket来实现信息的传输,Socket本身可以支持传输任意直接流,这里为了简单起见,仅仅传输文本信息,很显然,这事一种IPC方式。

使用Socket来进行通信,需要声明权限:

<uses-permission android:name="android.permission.INTERNET " />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
需要注意的是不能在主线程中访问网络,因为会引起报错,并且访问网络是耗时的,如果放在主线程会影响效率。

先看一下服务端的设计,当Service启动时,会在线程中建立TCP服务,这里监听的是8688端口,然后就可以等待客户端的链接请求了。当有客户端链接时,会生成一个Socket,通过每次新建Socket就可以分别和不同的客户端通信了。服务端每收到一个客户端的消息就会随机回复一句话给客户端。当客户端断开连接时,服务端这边也会响应的关闭对应Socket并结束通话线程,这里通过判断服务端输入流的返回值来确定的,当客户端断开链接后,服务端这边的输入流会返回null,这个时候我们就知道客户端退出了。

服务端代码如下:

public class TCPServerService extends Service {

private boolean mIsServiceDestoryed = false;
private String[] mDefineMessages = new String[] {
"你好啊,哈哈","请问你叫什么名字呀?","今天北京天气不错啊,shy","你知道吗?我可是可以和很多人同时聊天的哦"
,"给你讲个笑话吧:据说爱笑的人运气不会太差,不知道真假。"
};

public TCPServerService() {
}

@Override
public void onCreate() {
new Thread(new TcpServer()).start();

super.onCreate();
}

@Override
public void onDestroy() {

mIsServiceDestoryed = true;

super.onDestroy();
}

@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}

private class TcpServer implements Runnable
{
@Override
public void run() {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(8688);
} catch (IOException e) {

System.err.println("establish tcp setver failed, port:8688");

e.printStackTrace();
return;
}

while(!mIsServiceDestoryed)
{

try {
final Socket client = serverSocket.accept();
System.out.println("accept");
new Thread(){
@Override
public void run() {
try {
responseClient(client);
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
} catch (IOException e) {
e.printStackTrace();
}
}

}
}

private void responseClient(Socket client) throws IOException {

BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));

PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(client.getOutputStream())),true);
out.println("欢迎来到聊天室!");
while (!mIsServiceDestoryed)
{

String str = in.readLine();
System.out.println("msg from client:" + str);
if(str == null)
{
break;
}
int i = new Random().nextInt(mDefineMessages.length);
String msg = mDefineMessages[i];
out.println(msg);
System.out.println("send :" + msg);

}

System.out.println("client quit.");
MyUtils.close(out);
MyUtils.close(in);
client.close();

}

}


下面是客户端的代码:

public class TCPClientActivity extends AppCompatActivity implements View.OnClickListener{
private static final int MESSAGE_RECEIVE_NEW_MSG = 1;
private static final int MESSAGE_SOCKET_CONNECTED = 2;

private Button mSendButton;
private TextView mMessageTextView;
private EditText mMessageEditText;

private PrintWriter mPrintWriter;
private Socket mClientSocket;

private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {

switch (msg.what)
{
case MESSAGE_RECEIVE_NEW_MSG:
{
mMessageTextView.setText(mMessageTextView.getText() + (String)msg.obj);
break;
}
case MESSAGE_SOCKET_CONNECTED :{
mSendButton.setEnabled(true);
break;
}

}

super.handleMessage(msg);
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tcpclient);

mMessageTextView = (TextView) findViewById(R.id.msg_container);
mSendButton = (Button) findViewById(R.id.send);
mSendButton.setOnClickListener(this);
mMessageEditText = (EditText) findViewById(R.id.msg);
Intent service = new Intent(this,TCPServerService.class);
startService(service);
new Thread(new Runnable() {
@Override
public void run() {
connectTCPServer();
}
}).start();

Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);

FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
}

@Override
protected void onDestroy() {
if(mClientSocket != null)
{
try {
mClientSocket.shutdownInput();
mClientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}

super.onDestroy();
}

@Override
public void onClick(View v) {
if(v == mSendButton)
{
final String msg = mMessageEditText.getText().toString();
if(!TextUtils.isEmpty(msg) && mPrintWriter != null)
{
mPrintWriter.println(msg);
mMessageEditText.setText("");
String time = formatDateTime(System.currentTimeMillis());
final String showMsg = "self"+time +":" + msg + "\n";
mMessageTextView.setText(mMessageTextView.getText() + showMsg);
}
}
}

private String formatDateTime(long time)
{
return new SimpleDateFormat("HH:mm:ss").format(new Date(time));
}

private void connectTCPServer()
{
Socket socket = null;
while (socket == null)
{
try {
socket =new Socket("localhost",8688);
mClientSocket = socket;
mPrintWriter = new PrintWriter(
new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())),true);
mHandler.sendEmptyMessage(MESSAGE_SOCKET_CONNECTED);
System.out.println("connect server success");

} catch (IOException e) {
SystemClock.sleep(1000);
System.out.println("connect tcp server failed, retry...");
}
}

try {
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while(!TCPClientActivity.this.isFinishing())
{
String msg = br.readLine();
System.out.println("receive :" + msg);
if(msg != null)
{
String time = formatDateTime(SystemClock.currentThreadTimeMillis());
final String showedMsg = "server " + time + ":" + msg + "\n";
mHandler.obtainMessage(MESSAGE_RECEIVE_NEW_MSG,showedMsg).sendToTarget();
}
}

System.out.println("quit....");
MyUtils.close(mPrintWriter);
MyUtils.close(br);
socket.close();
} catch (IOException e) {
e.printStackTrace();
}

}

}


Binder连接池

当项目中使用很多A
IDL的时候,我第一个想到的是一个个实现AIDL,创建多个Service,但是Service是很占资源,而且会是的应用看起来很重量级。针对上面的问题,需要减少Service的数量,将所有的AIDL放在同一个Service中去管理。


在这种模式下,整个工作机制是这样的:每个业务模块创建自己的AIDL接口并实现此接口,这个时候不同业务模块之间不能有耦合,所有实现细节我们单独开来,然后向服务端提供自己的唯一标识和其对应的Binder现象;对于服务端来说,只需要一个Service就可以了,服务端提供一个queryBinder接口,这个接口能够根据业务模块的特征来返回相应的Binder对象给它们,不同的业务模块拿到所需的Binder对象就可以进行远程方法调用了。由此可见,Binder连接池的主要作用就是将每个业务模块的Binder请求统一转发到远程Service中去执行,从而避免了重复创建Service的过程。

下面是实例:

新建两个业务模块AIDL接口:

ISecurityCenter.aidl



// ISecurityCenter.aidl
package com.app.song.ipc1;

// Declare any non-default types here with import statements

interface ISecurityCenter {
String encrypt(String content);
String decrypt(String password);
}




ICompute.aidl



// ICompute.aidl
package com.app.song.ipc1;

// Declare any non-default types here with import statements

interface ICompute {
int add(int a, int b);
}


下面是AIDL的实现

SecurityCenterImpl.java



package com.app.song.ipc1.binderpool;

import android.os.RemoteException;

import com.app.song.ipc1.ISecurityCenter;

/**
* Created by song on 2016/3/5.
*/
public class SecurityCenterImpl extends ISecurityCenter.Stub {

private static final char SECRET_CODE = '^';

@Override
public String encrypt(String content) throws RemoteException {

char[] chars = content.toCharArray();
for(int i = 0; i < chars.length; i++)
{
chars[i] ^= SECRET_CODE;
}

return new String(chars);
}

@Override
public String decrypt(String password) throws RemoteException {
return encrypt(password);
}

}




ComputeImpl.java



package com.app.song.ipc1.binderpool;

import android.os.RemoteException;

import com.app.song.ipc1.ICompute;

/**
* Created by song on 2016/3/5.
*/
public class ComputeImpl extends ICompute.Stub {
@Override
public int add(int a, int b) throws RemoteException {
return a + b;
}
}


现在业务模块的AIDL接口和实现都已经完成了,注意这里并没有为每个模块的AIDL单独创建Service,接下来就是服务端和Binder连接池的工作了。

首先,为Binder连接池创建AIDL接口IBnderPool.aidl,代码如下:

// IBinderPool.aidl
package com.app.song.ipc1;

// Declare any non-default types here with import statements

interface IBinderPool {
IBinder queryBinder(int binderCode);
}


接着,为Binder连接池创建远程Service并实现IBinderPool

IBinderPool的实现:

public class BinderPool {
public static final int BINDER_NONE = -1;
public static final int BINDER_COMPUTE = 0;
public static final int BINDER_SECURITY_CENTER = 1;

private Context mContext;
private IBinderPool mBinderPool;
private static volatile  BinderPool sInstance;
private CountDownLatch mConnectBinderPoolCountDownLatch;

private BinderPool(Context context)
{
mContext = context.getApplicationContext();
connectBinderPoolService();
}

public static BinderPool getInstance(Context cotext)
{
if(sInstance == null)
{
synchronized (BinderPool.class)
{
sInstance = new BinderPool(cotext);
}
}
return sInstance;
}

private void connectBinderPoolService() {
mConnectBinderPoolCountDownLatch = new CountDownLatch(1);
Intent setvice = new Intent(mContext, BinderPoolService.class);
mContext.bindService(setvice,mBinderPoolConnection,Context.BIND_AUTO_CREATE);
try {
mConnectBinderPoolCountDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}

}

public IBinder queryBinder(int binderCode)//通过调用IBinderPool接口来实现AIDL的调用
{
IBinder binder = null;
if(mBinderPool != null)
{
try {
binder = mBinderPool.queryBinder(binderCode);
} catch (RemoteException e) {
e.printStackTrace();
}

}
return binder;
}

private ServiceConnection mBinderPoolConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mBinderPool = IBinderPool.Stub.asInterface(service);//通过服务端返回的IBinder实现IBinderPool
try {
mBinderPool.asBinder().linkToDeath(mBinderPoolDeathRecipient,0);//死亡监听
} catch (RemoteException e) {
e.printStackTrace();
}
mConnectBinderPoolCountDownLatch.countDown();

}

@Override
public void onServiceDisconnected(ComponentName name) {

}
};

private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient()
{
@Override
public void binderDied() {
mBinderPool.asBinder().unlinkToDeath(mBinderPoolDeathRecipient,0);
mBinderPool = null;
connectBinderPoolService();
}
};

public static class BinderPoolImpl extends  IBinderPool.Stub {//BinderPoolImpl的实现,Service会调用
@Override
public IBinder queryBinder(int binderCode) throws RemoteException {//queryBinder的具体实现
IBinder binder = null;
switch (binderCode) {
case BINDER_SECURITY_CENTER: {
binder = new SecurityCenterImpl();
break;
}
case BINDER_COMPUTE:
{
binder = new ComputeImpl();
break;
}
default:
break;
}

return binder;
}
}

}


Service的实现
public class BinderPoolService extends Service {

private Binder mBinderPool = new BinderPool.BinderPoolImpl();//其实实现的是IBidnerPool

public BinderPoolService() {
}

@Override
public IBinder onBind(Intent intent) {
return mBinderPool;
}

}


下面是客户端的实现:

public class BinderPoolActivity extends AppCompatActivity {

private ISecurityCenter mSecurityCenter;
private ICompute mCompute;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_binder_pool);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);

new Thread(new Runnable() {
@Override
public void run() {
doWork();
}
}).start();

FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
}

private void doWork() {
BinderPool binderPool = BinderPool.getInstance(BinderPoolActivity.this);
IBinder securityBinder = binderPool.queryBinder(BinderPool.BINDER_SECURITY_CENTER);
mSecurityCenter = SecurityCenterImpl.asInterface(securityBinder);

String msg = "helloworld-安卓";
L.d("content:" + msg);
try {
String password = mSecurityCenter.encrypt(msg);
L.d("encrypt:" + password);
L.d("decrypt:" + mSecurityCenter.decrypt(password));

} catch (RemoteException e) {
e.printStackTrace();
}

IBinder computeBinder = binderPool.queryBinder(BinderPool.BINDER_COMPUTE);
mCompute = ComputeImpl.asInterface(computeBinder);

try {
L.d("3+5=" +mCompute.add(3,5));
} catch (RemoteException e) {
e.printStackTrace();
}

}

}


执行后打印的Log:

03-05 11:52:20.182 4051-4530/? D/ipctest: content:helloworld-安卓

03-05 11:52:20.183 4051-4530/? D/ipctest: encrypt:6;221)1,2:s寗匍

03-05 11:52:20.183 4051-4530/? D/ipctest: decrypt:helloworld-安卓

03-05 11:52:20.189 4051-4530/? D/ipctest: 3+5=8



CountDownLatch,一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。

主要方法

public CountDownLatch(int count);

public void countDown();

public void await() throws InterruptedException

构造方法参数指定了计数的次数

countDown方法,当前线程调用此方法,则计数减一

awaint方法,调用此方法会一直阻塞当前线程,直到计时器的值为0


在线程中执行线程池是因为,通过CountDownLatch将bindService这一异步操作转换成可同步操作,这就意味着他有可能是耗时的,然后就是Binder方法调用过程也是可能耗时的,因此不建议放在主线程去执行。注意到BinderPool是一个单例实现,因此在同一个进程中只会初始化一次,所以我们就提前初始化BinderPool,那么可能优化程序的体验,比如可以放在Application中提前初始化BinderPool,虽然不能保证当我们调用BinderPool时它一定是初始化好的,但是在大多数情况下,这种初始化工作(绑定远程服务)的时间开销(比如BinderPool没有提前初始化完成的话)是可以接受的。另外,BinderPool中有断线重连的机制,当远程服务意外终止时,BinderPool重新建立连接,这个时候如果业务模块中Binder调用出现异常,也需要手动去重新获取最新的Binder对象,这个是需要注意的。

使用BinderPool可以大大方便日常的开发工作,比如如果有一个新的业务模块需要添加新的AIDL,那么在它实现了自己的AIDL接口后,只需要修改BinderPoolImpl中的queryBinder方法,给自己添加一个新的BinderCode并返回对应的Binder对象即可,不需要做其他改动,也不需要创建新的Service。由此可见,BinderPool能过极大地提高AIDL的工作效率,并且可以避免大量的Service创建,因此,建议在AIDL开发工作中引入BinderPool机制。

选用合适的IPC

IPC方式的优缺点

名称优点缺点适用场景
Bundle简单易用只能传输Bundle支持的数据类型四大组件的进程间通信
文件共享简单易用不适合高并发场景,并且无法做到

进程间的即使通信
无并发访问情形,交换简单的

数据实时性不高的场景
ADIL功能强大,支持一对多并发通信,

支持实时通信
使用稍微复杂需要处理好线程同步一对多通信且有RPC需求
Messenger功能一般,支持一对多串行通信,

支持实时通信
不能很好处理高并发情形,不支持RPC,

数据通过Message进行传输,因此只能

传输Bundle支持的数据类型
低并发的一对多即时通信,

无RPC需求,或者无须要返回

结果的RPC需要
ContentProvider在数据源访问方面功能强大支持

一对多并发数据共享通过Call方法

扩展其他操作
可以理解为受约束的AIDL,主要提供

数据的

CRUD
一对多的进程间的数据共享
Socket功能强大,可以通过网络传输直接流

,支持一对多并发实时通信
实现细节稍微有点繁琐,不支持直接RPC网络数据交换
简单易用
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: