Android中的多进程通信

本文详细探讨了Android中实现多进程通信的各种方式,包括Serialization与Parcelable的比较、Binder的使用及工作原理、Messenger的实现步骤和工作流程,以及AIDL在权限验证中的应用。此外,还介绍了ContentProvider、Socket通信和Binder连接池的概念与实践,强调了在选择IPC方式时的考量因素。

Android中的多进程

在androidManifest中可以通过android:process=”“的方式让四大组建运行在指定在指定的进程中。android:process=”:remote”和android:process=”com.develop.demo.remote”的两种含义不同,“:”表示运行进程名字自动添加应用的包名,比如应用程序包名是”com.one.app”,那么”:remote”运行的进程是”com.one.app:remote”,它是应用的一个私有进程,也就是不允许其它的应用程序使用该进程。第二种方式运行的进程是”com.develop.demo.remote”,该进程是以完整包名的形式指定了进程名,而且它可以通过androidmanifest中的ShareUid的标签来和其它应用程序共享进程。但是ShareUid的使用是由限制的,只有两个应用ShareUid的值相同,并且两个应用拥有相同的签名才可以,这时两个应用可以共享data下目录的数据和组建信息,但是还不能共享内存数据,因为共享内存数据两个组建必须还需要他们运行在同一个进程中,也就是都指定了相同的android:process。

所有运行在不同进程的四大组件,它们希望通过共享内存的方式恭喜数据都会失败,原因是Android中每个进程都运行在单独的虚拟机中,运行的类都有自己的拷贝。

使用多进程会造成如下几个问题:
1. 静态成员和单例模式完全失效
2. 线程同步机制的完全失效
3. SharedPreference的可靠性下降
4. Application创建多次

Serialization和Parcelable接口对比

Serialization是Java中的序列化操作,使用简单但是开销很大,序列化和反序列化过程需要大量的IO操作。

Parcelable是Androd中的序列化操作,更加适合Android平台,缺点是使用起来稍微麻烦点,但是它的效率很高,这是Android推荐的序列化方式,因此首选Parcelable,它可以主要用在内存序列化上,通过Parcelable将对象序列化到存储设备中或者将对象序列化后通过网络传输也都是可以的,就是稍微复杂,这是由建议使用Serialization。

Binder

通过AIDL实现跨进程通信,系统会自动在gen下生成相同命名的java类,其中其关键作用的是它的内部类Stub和Stub的内部代理类Proxy。

DESCRIPTOR
Binder的唯一标识,一般用当前Binder的类名表示。

asInterface(android.os.IBinder obj)
用于将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象,这种转换过程是区分进程的,如果客户端和服务端位于统一进程,那么此方法返回的就是服务端的Stub对象本身,否则返回的是系统封装后的Stub.proxy对象。

asBinder
此方法用于返回当前Binder对象。

onTransact
这个方法运行在服务端的Binder线程池中,当客户端发起跨进程请求时,Proxy类会在接口方法实现中,先使用Parcel封装请求参数,再通过mRemote.transact将请求和请求参数通过系统底层封装后发送出去,远程请求通过系统底层封装后最终交由onTransact方法来处理。该方法的原型是public Boolean onTransact(int code,android.os.Parcel data,android.os.Parcel reply,int flags)。服务端通过code可以确定客户端所请求的目标方法是什么,接着从data中取出目标方法所需的参数(存入和取出都是按照固定的顺序),然后执行目标方法。当目标执行完毕后,再向reply中写入返回值。

Proxy#AIDL中定义的接口方法
这个方法运行在客户端,当客户端远程调用此方法时,它的内部实现是:首先创建该方法所需要的输入型Parcel对象_data、输出型Parcel对象_reply和返回值对象List;使用transact方法来发起RPC(远程过程调用)请求,同时当前线程挂起;然后服务端的onTract方法会被调用,直到RPC过程返回后,当前线程继续执行。并从_reply中取出RPC过程的返回结果,最后返回_reply中的数据。

Binder使用的注意事项

当客户端发起远程请求时,由于当前线程会被挂起直到服务端进程返回数据,所以如果一个远程方法是很耗时的,那么不能再UI线程中发起远程请求。

由于服务端的Binder方法运行在Binder的线程池中,所以Binder方法不管是否耗时都应该采用同步的方式去实现,因为它已经运行在一个线程中了。

Binder的工作原理

Binder在Linux中是使用共享内存的方式实现进程间通信的,这里共享内存的部分是Service中onTransact中的实现部分,也就是AIDL中实现AIDL接口的Binder对象。

这里写图片描述

手动实现一个Binder可以通过如下方法:
1. 声明一个AIDL性质的接口,只需要继承IInterface接口即可,IInterface接口中有一个asBinder方法。这个接口实现包括声明Binder描述符和各个方法的唯一标识。
2. 实现Stub类和Stub类中的Proxy代理类,实现基本和系统生成的代码一致。

AIDL文件的本质是系统提供一种快速实现Binder的工具。

Binder中的两个重要方法:

linkToDeath:设置死亡代理,当Binder死亡时,客户端会受到通知。

unlinkToDeath

通过Binder的isBinderAlive也可以判断Binder是否死亡。

Messenger

Messenger的底层是AIDL,它的特点是一次处理一个请求,服务端不需要考虑线程同步的问题。

public Messenger(Handler target){
mTarget = target.getIMessenger();
}

public Messenger(IBinder target){
mTarget = IMessenger.Strub.asInterface(target)
}

实现步骤:
1. 服务端进程:在服务端创建一个Service来处理客户端的连接请求,同时创建一个Handler并通过它来创建一个Messenger对象,然后在ServiceonBinder中返回这个Messenger对象底层的Binder即可。
2. 客户端进程:首先要绑定服务端的Service,绑定成功后用服务端返回的IBinder对象创建一个Messenger,通过这个Messenger就可以向服务端发送消息了,发消息类型为Message对象。如果需要服务端能够回应客户端,就和服务端一样,创建一个Handler并创建一个新的Messenger,并把这个Messenger对象通过MessagereplyTo参数传递给服务端,服务端通过这个replyTo参数就可以回应客户端。

Messenger的工作原理

如下所示:

这里写图片描述

听一个应用的不同组件,如果它们运行在不同的进程中,那么和它们分别属于两个应用没有本质区别。

AIDL

通过AIDL实现进程间通信步骤:
1. 服务端:首先创建一个Service用来监听客户端的连接请求,然后创建一个AIDL文件,将暴露给客户端的接口在这个AIDL文件中声明,最后在Serivce中实现这个AIDL接口即可。
2. 客户端:绑定服务端的Service,绑定成功后,将服务端返回的Binder对象转成AIDL接口所属的类型,接口就可以调用AIDL中的方法了。

其中支持的数据类型:

  • 基本数据类型(int、long、char、boolean、double等)
  • String和CharSequence
  • List:只支持ArrayList,里面每个元素都必须能够被AIDL支持
  • Map:只支持HashMap,里面每个元素都必须能够被AIDL支持,包括key和value
  • Parcelable:所有实现了Parcelable接口的对象
  • AIDL:所有AIDL接口本身也可以在AIDL文件中使用

如果AIDL文件中用到了自定义的Parcelable对象,那么必须新建一个和它同名的AIDL文件,并在其中声明它为Parcelable类型。除此之外,AIDL中除了基本数据类型,其它类型的参数必须表上方向:in、out或者inout,in表示输入型参数,out表示输出型参数,inout表示输入输出型参数。它们是有区别的,不能一概适用out或者inout,因为这在底层实现是有开销的。

AIDL接口中只支持方法,不支持声明静态常量,这一点区别传统的接口。

parcelable Book;

使用AIDL就必须要考虑同步的问题,因为可能有多个客户端同时连接并调用服务端的方法。

在AIDL中,对象是不能跨进程直接传输的,对象的跨进程传输本质上都是反序列化的过程。

如果实现Lisenter的反注册呢,需要使用RemoteCallbackList
RemoteCallbackList是系统专门提供的用于删除跨进程listener的接口。RemoteCallbackList是一个泛型,支持管理任意的AIDL接口。

public class RemoteCallbackList<E extends IInterface>

它的工作原理是它内部有一个Map接口专门用来保存所有AIDL回调,这个Map的key是IBinder类型,value是Callback类型,如下所示:

ArrayMap<IBinder,Callback> mCallBack =  new ArrayMap<IBinder,CallBack>();

其中Callback中封装了真正的远程listener。当客户端注册listener的时候,它会把这个listener的信息存入mCallBack中,其中key和value分别通过下面的方式获得:

IBinder key = listener.asBinder()
Callback value =new Callback(listener,cookie)

虽然跨进程通信同一个对象会生成新的对象,但是两个对象有一个共同点,它们底层的Binder对象是同一个。

所以当客户端解注册的时候,我们只要遍历服务端所有的listener,找出那个和解注册listener具有相同Binder对象的服务端listener并把它删掉即可。这就是RemoteCallbackList为我们做的事情。同时RemoteCallbackList还有一个很有用的功能,那就是当客户端进程终止后,它能自动移除客户端所注册的listener。了另外,RemoteCallbackList内部自动实现了线程同步的功能,所以我们是用它来注册和解注册的时候,不需要做额外的线程同步功能。

RemoteCallbackList有一点需要注意,它并不是一个List,遍历RemoteCallbackList必须按照下面的方式进行:

    RemoteCallbackList remoteCallbackList = new RemoteCallbackList();
        final int N = remoteCallbackList.beginBroadcast();
        for (int i = 0; i < N; i++) {
            IOnNewBookArrayListener l = remoteCallbackList.getBroadcastItem(i);
            if (i != null) {
                // TODO
            }
        }

一定要注意,客户端的onServiceConnected和onServiceDisconnected的方法都运行在UI线程中,所以不可以在里面直接调用服务端的耗时方法。另外由于服务端的方法本身就运行在服务端的Binder线程池中,所以服务端方法本身就可以执行大量耗时操作,这个时候切记不要在服务端方法中开线程去执行异步任务,除非你明确知道自己在干什么。

注意:不要在主线程中直接通过跨进程的方式调用服务端的方法,而需要启动一个新的线程,在线程中调用执行。

注意:当远程服务端需要调用客户端的listener中的方法时,被调用的方法也运行在Binder线程池中,只不过是客户端的线程池。所以,我们同样不可以在服务端中调用客户端的耗时方法。

为了程序的健壮性,Binder是可能意外死亡的,往往是因为服务端的进程意外停止了,这时我们需要重新连接服务,有两中方法,一种是给Binder设置DeathRecipient监听,当Binder死亡时,我们会收到binderDied方法的回调,在binderDied方法中我们可以重新连接远程服务。另一种方法是在onServiceDisconnected中重连远程服务。这两种方法可以随便选择一种使用,区别是onServiceDisconnected在客户端的UI线程中被回调,binderDied在客户端的Binder线程池中被回调。

如何在AIDL中使用权限验证功能

第一种方法,可以再onBinder中进行验证,验证不通过就直接返回Null,这样验证失败的客户端就无法绑定服务,至于验证方式可以有很多种,比如使用permission验证,使用这种验证方式,首先需要在AndroidManifest中声明所需的权限。


    <permission
        android:name="com.pwrd.permission.ACCESS"
        android:protectionLevel="normal"></permission>
public IBinder onBind(Intent intent) {
        int check = checkCallingOrSelfPermission("com.pwrd.permission.ACCESS");
        if(check== PackageManager.PERMISSION_DENIED){
            return null;
        }
        return mMessenger.getBinder();
    }

第二种方法,可以再服务端的onTransact方法中进行权限验证,如果验证失败就直接返回false,这样服务端就不会中止执行AIDL中的方法而达到保护服务端的效果。同样可以采用permission验证,具体实现与方法一相同。还可以采用UidPid来验证,通过getCallingUidgetCallingPid可以拿到客户端所属的应用的UidPid,通过这两个参数可以进行一些验证工作,比如验证包名。

int check = checkCallingOrSelfPermission("com.pwrd.permission.ACCESS");
        if(check== PackageManager.PERMISSION_DENIED){
            return null;
        }
        String packgeName = null;
        String[] packages =getPackageManager().getPackagesForUid(getCallingUid());
        if(packages!=null&&packages.length>0){
            packgeName=packages[0];
        }
        if(!packgeName.startsWith("com.pwrd")){
            return null;
        }

ContentProvider

ContentProvider的一些重要细节:
防止SQL注入和权限控制、CRUD(指的是增删改查操作)操作。

CRUD操作运行在ContentProvider的进程中。除了Create操作由系统回调并运行在主线程中,其他五个方法均由外界回调并运行在Binder线程池中。

需要注意的是:query、update、insert、delete四大方法是存在多线程并发访问的,因为方法内部要做好线程同步。采用的SQLite并且只有一个SQLiteDatabase连接时,SQLiteDatabase内部对数据库的操作是有同步处理的,但是如果通过多个SQLiteDatabase对象来才做数据库就无法保证线程同步,因为SQLiteDatabase对象之间无法进行线程同步。如果ContentProvider的底层数据集是一块内存的话,比如List,这个时候同List的遍历、插入、删除操作就需要进行线程同步了。

Socket

在本节中,通过Socket实现进程间通信。Socket是“套接字”,是网络通信中的概念,分为流式套接字和用户数据套接字两种,分别应对与网络的传输控制层的TCP和UDP协议。TCP协议是面向连接的协议,提供稳定的双向通信功能,TCP连接的建立需要经过“三次握手”才能完成,为了提供稳定的数据传输功能,其本身提供了超时重传机制,因为具有很高的稳定性;而UDP是无连接的,提供不稳定的单向通信功能,当然UDP也可以实现双向通信。

简单的Socket聊天服务端代码如下所示:

public class TCPServerService extends Service {

    private static final String TAG = "TCPServerService";

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


    public TCPServerService() {
    }

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

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

    @Override
    public void onDestroy() {
        mIsServiceDestoryed = true;
        super.onDestroy();
    }

    private class TcpServer implements Runnable {

        @Override
        public void run() {
            ServerSocket serverSocket = null;
            try {
                serverSocket = new ServerSocket(8688);
            } catch (IOException e) {
                Log.d(TAG, "establish tcp server failed,port:8688");
                e.printStackTrace();
                return;
            }
            while (!mIsServiceDestoryed) {
                try {
                    final Socket clent = serverSocket.accept();
                    System.out.println("accept");
                    Log.d(TAG, "accept");
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                responseClient(clent);
                            } 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();
            Log.d(TAG, "msg from client:" + str);
            if (str == null) {
                // 客户端断开连接
                break;
            }
            int i = new Random().nextInt(mDefinedMessages.length);
            String msg = mDefinedMessages[i];
            out.println(msg);
            Log.d(TAG, "send :" + msg);
        }
        Log.d(TAG, "client quit.");
        out.close();
        in.close();
    }

}

客户端代码如下所示:

public class SocketActivity extends AppCompatActivity implements View.OnClickListener {

    private static final int MESSAGE_RECEIVE_NEW_MSG = 1;
    private static final int MESSAGE_SOCKET_CONNECTED = 2;
    private static final String TAG = "SocketActivity";

    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;
                default:
                    break;
            }

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_socket);
        mSendButton = (Button) findViewById(R.id.send_button);
        mMessageTextView = (TextView) findViewById(R.id.message_text);
        mMessageEditText = (EditText) findViewById(R.id.message_edit);
        mSendButton.setOnClickListener(this);
        Intent service = new Intent(this, TCPServerService.class);
        startService(service);
        new Thread(new Runnable() {
            @Override
            public void run() {
                connectTCPServer();
            }
        }).start();
    }

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

        super.onDestroy();
    }

    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);
                Log.d(TAG, "connect server success");
            } catch (IOException e) {
                SystemClock.sleep(1000);
                Log.d(TAG, "connect tcp server failed,retry...");
                e.printStackTrace();
            }
        }

        try {
            BufferedReader br = new BufferedReader(new BufferedReader(new InputStreamReader(socket.getInputStream())));
            while (!SocketActivity.this.isFinishing()) {
                String msg = br.readLine();
                System.out.println("receive :" + msg);
                Log.d(TAG, "receive :" + msg);
                if (msg != null) {
                    String time = formateDataTime(System.currentTimeMillis());
                    final String showedMsg = "server " + time + ":" + msg + "\n";
                    mHandler.obtainMessage(MESSAGE_RECEIVE_NEW_MSG, showedMsg).sendToTarget();
                }
            }
            System.out.println("quit... ");
            Log.d(TAG, "quit... ");
            mPrintWriter.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.send_button:
                final String msg = mMessageEditText.getText().toString();
                if (!TextUtils.isEmpty(msg) && mPrintWriter != null) {
                    mPrintWriter.println(msg);
                    mMessageEditText.setText("");
                    String time = formateDataTime(System.currentTimeMillis());
                    final String showedMsg = "self " + time + ":" + msg + "\n";
                    mMessageTextView.setText(mMessageTextView.getText() + showedMsg);
                }
                break;
        }

    }

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

}

Binder连接池

Binder连接池的作用是将众多因为AIDL建立的Service放在一个Service中进行管理。

在这种模式下,每个业务模块不能有耦合,所有实现细节要单独开来,然后向服务端提供一个Binder的唯一标识和其Binder对象,Service中实现各个AIDLStub接口的Binder对象,不同业务模块调用服务端功能时先通过该模块的唯一标识调用服务端提供的queryBinder接口来让服务端返回对应的Binder,然后不同业务模块拿到对应的Binder就可以调用远程方法了。

Binder连接池的主要作用是将每个模块的Binder请求统一转发到远程Service中去执行,从而避免了重复创建Service的过程。它的工作原理如下所示:

..这里写图片描述

Binder连接池的实现过程如下所示:
首先为Binder连接池创建AIDL接口的IBinderPool.aidl文件,代码如下所示:

package com.derek.leakcheck;

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

interface IBinderPool {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    IBinder queryBinder(int binderCode);
}

接着,为Binder连接池创建远程Service并实现IBinderPool,下面是queryBinder的具体实现,可以看到请求转发的实现方法,当Binder连接池连接上远程服务时,会根据不同模块标识即binderCode返回不同的Binder对象,通过这个Binder对象所执行的操作全部发生在远程服务端。

 @Override
        public IBinder queryBinder(int binderCode) throws RemoteException {
            IBinder binder = null;
            switch (binderCode) {
                case BINDER_SECURITY_CENTER:
                    binder = new SecurityCenterImpl();
                    break;
                case BINDER_COMPUTE:
                    binder = new ComputeImpl();
                    break;
                default:
                    break;
            }
            return binder;
        }

SecurityCenterImplComputeImpl是实现了ISecurityCenterImpl.aidlIComputeImpl.aidl接口的实现类。

完整的BinderPool 的实现代码如下所示:

package com.derek.leakcheck.binder;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

import com.derek.leakcheck.IBinderPool;

import java.util.concurrent.CountDownLatch;

/**
 * Created with Intellij IDEA
 * Author:LiuXuedong
 * Date:2017/7/5
 */
public class BinderPool {
    private static final String TAG = "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 IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            Log.d(TAG, "binder died.");
            mBinderPool.asBinder().unlinkToDeath(mBinderPoolDeathRecipient, 0);
            mBinderPool = null;
            connectBinderPoolService();
        }
    };


    private ServiceConnection mBinderPoolConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mBinderPool = IBinderPool.Stub.asInterface(service);
            try {
                mBinderPool.asBinder().linkToDeath(mBinderPoolDeathRecipient, 0);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            mConnectBinderPoolCountDownLatch.countDown();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };


    public static class BinderPoolImpl extends IBinderPool.Stub {

        public BinderPoolImpl() {
            super();
        }


        @Override
        public IBinder queryBinder(int binderCode) throws RemoteException {
            IBinder binder = null;
            switch (binderCode) {
                case BINDER_SECURITY_CENTER:
                    binder = new SecurityCenterImpl();
                    break;
                case BINDER_COMPUTE:
                    binder = new ComputeImpl();
                    break;
                default:
                    break;
            }
            return binder;
        }
    }

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

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


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

    public IBinder queryBinder(int binderCode) {
        IBinder binder = null;
        if (mBinderPool != null) {
            try {
                binder = mBinderPool.queryBinder(binderCode);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        return binder;
    }


}

这样只需要创建一个BinderPoolService 即可, 客户端通过绑定这个Service来获取不同业务的Binder,具体实现代码如下所示:

public class BinderPoolService extends Service {

    private static final String TAG = "BinderPoolService";

    private Binder mBinderPool = new BinderPool.BinderPoolImpl();


    public BinderPoolService() {
    }


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


    @Override
    public void onDestroy() {
        super.onDestroy();
    }
}

客户端调用代码如下所示:

public class BinderPoolActivity extends AppCompatActivity {

    private static final String TAG = "BinderPoolActivity";
    private ISecurityCenterImpl mSecurityCenter;
    private IComputeImpl mComputer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_binder_pool);
        new Thread(new Runnable() {
            @Override
            public void run() {
                doWork();
            }
        }).start();
    }

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

        Log.d(TAG, "visit  ISecurityCenterImpl");
        String msg = "hello-android";

        Log.d(TAG, "content:" + msg);

        try {
            String password = mSecurityCenter.encrypt(msg);
            Log.d(TAG, "encrypt:" + password);

            Log.d(TAG, "decrypt:" + mSecurityCenter.decrypt(password));
        } catch (RemoteException e) {
            e.printStackTrace();
        }


        Log.d(TAG, "visit  ICompute");

        IBinder computeBinder = binderPool.queryBinder(BinderPool.BINDER_COMPUTE);
        mComputer = IComputeImpl.Stub.asInterface(computeBinder);


        try {
            Log.d(TAG, "3+15=" + mComputer.add(3, 15));
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
}

需要额外说明BinderPool 需要再线程中执行是因为Binder连接池的实现中,通过CountDownLatch将bindService这一异步操作转换成了同步操作,这就意味着它可能是耗时的,另外就是Binder方法的调用过程也可能是耗时的。

BinderPool有断线重连机制,当远程服务意外终止时,BinderPool会中先建立链接,这个时候如果业务模块的Binder调用出现了异常,也需要手动去获取最新的Binder对象。

这样再增加业务添加新的AIDL后,只需要实现AIDL接口后,修改BinderPoolIml中的queryBinder方法,给自己添加一个新的binderCode并返回相应的Binder对象即可,不需要创建新的Service。

选用合适的IPC方式

除了一下进程间通信的方式外,还可以通过BroadCast发送发送广播的方式和对方通信,只不过对方必须要通过BroadCastReceiver来进行接收,而且传递的数据只能放在Bundle中,由于BroadCastReceiver的生命周期只有10s,所以它在接收到消息后如果需要处理耗时的任务则需要启动Service来处理。

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值