IPC 机制(三)

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网络数据交换










简单易用
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值