第二章-IPC机制-Binder连接池

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

下面通过一个实际的例子演示(已通过本地代码调试运行)

一、服务端代码:

1、我们提供了两个AIDL接口(ISecurityCenter和ICompute)来模拟上面提到的多个业务模块都要使用AIDL的情况,其中ISecurityCenter接口提供解密功能,声明如下:

// ISecurityCenter.aidl
package com.example.binderpoolserver;

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

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

而ICompute提供了计算加法的功能:

// ICompute.aidl
package com.example.binderpoolserver;

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

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

虽然说上面的两个接口的功能都比较简单,但是用于分析Binder池的工作原理还是足够的,读者可以写出更加复杂的例子。

2、编写上面2个接口的实现
SecurityCenterImpl

package com.example.binderpoolserver;

import android.os.RemoteException;

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

package com.example.binderpoolserver;

import android.os.RemoteException;

public class ComputeImpl extends ICompute.Stub {
    @Override
    public int add(int a, int b) throws RemoteException {
        return a + b;
    }
}

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

3、定义Binder连接池的AIDL并实现
IBinderPool

// IBinderPool.aidl
package com.example.binderpoolserver;

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

interface IBinderPool {
    IBinder queryBinder(int binderCode);
}

BinderPoolImpl

package com.example.binderpoolserver;

import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

public class BinderPoolImpl extends IBinderPool.Stub {
    private static final String TAG = "Server:BinderPoolImpl";

    private static final int BINDER_SECURUITY_CENTER = 0;
    private static final int BINDER_COMPUTE = 1;

    public BinderPoolImpl(){
        super();
    }

    @Override
    public IBinder queryBinder(int binderCode) throws RemoteException {
        IBinder binder = null;
        switch (binderCode){
            case BINDER_SECURUITY_CENTER:
                binder = new SecurityCenterImpl();
                Log.d(TAG,"返回SecurityCenterImpl");
                break;
            case BINDER_COMPUTE:
                binder = new ComputeImpl();
                Log.d(TAG,"返回ComputeImpl");
                break;
        }
        return binder;
    }
}

4、编写服务端的Service

package com.example.binderpoolserver;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

public class BinderPoolService extends Service {
    private  static final String  TAG = "Server:BPS";

    private Binder mBinderPool = new BinderPoolImpl();

    @Override
    public void onCreate() {
        Log.d(TAG,"onCreate");
        super.onCreate();
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG,"onBind");
        return mBinderPool;
    }

    @Override
    public void onDestroy() {
        Log.d(TAG,"onDestroy");
        super.onDestroy();
    }
}

5、服务端的app在AndroidManefest中配置服务(很重要,否则调用失败)

        <service android:name="com.example.binderpoolserver.BinderPoolService"
            android:exported="true">
            <intent-filter >
                <action android:name="com.example.binderpoolserver.BinderPoolService"/>
            </intent-filter>
        </service>

特别注意,android:exported=“true” , 调试的时候遇到了“”“java.lang.SecurityException: Not allowed to bind to service Intent”这个错误。

到这里服务端app的代码已经全部写完了。代码的结构如下:
在这里插入图片描述

二、客户端的代码
1、将服务端的aidl包直接拷贝到客户端的工程中,并且build下。

2、Binder连接池具体实现

package com.example.binderpoolclient;

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.example.binderpoolserver.IBinderPool;

public class BinderPool {
    private static final String TAG = "Client:BinderPool";

    public static final int BINDER_SECURUITY_CENTER = 0;
    public static final int BINDER_COMPUTE = 1;

    private Context mContext;
    private IBinderPool mIBinderPool;
    private static volatile BinderPool sInstance;

    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);
                    Log.d(TAG, "创建Binder连接池");
                }
            }
        }
        return sInstance;
    }

    private synchronized void connectBinderPoolService() {
        final Intent service = new Intent();
        Log.d(TAG, "第一种启动服务方式,推荐使用");
        service.setComponent(new ComponentName("com.example.binderpoolserver", "com.example.binderpoolserver.BinderPoolService"));

//        service.setAction("com.example.binderpoolserver.BinderPoolService");
//        service.setPackage("com.example.binderpoolserver");
        Log.d(TAG, "第二种启动服务的方式");
        Log.d(TAG, "service.setAction(\"com.example.binderpool.service\");");
        Log.d(TAG, "service.setPackage(\"com.example.binderpool.server\");");

        mContext.bindService(service, mBinderPoolConnection, Context.BIND_AUTO_CREATE);
    }

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

    private ServiceConnection mBinderPoolConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.d(TAG, "onServiceConnected");
            mIBinderPool = IBinderPool.Stub.asInterface(service);

            Log.d(TAG, "设置死亡代理");
            try {
                mIBinderPool.asBinder().linkToDeath(mBinderPoolDeathRecipient, 0);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            mIBinderPool.asBinder().unlinkToDeath(mBinderPoolDeathRecipient, 0);
            mIBinderPool = null;
            Log.d(TAG, "重连服务");
            connectBinderPoolService();
        }
    };
}

3、在MainActivity中使用

package com.example.binderpoolclient;

import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;

import androidx.appcompat.app.AppCompatActivity;

import com.example.binderpoolserver.ICompute;
import com.example.binderpoolserver.ISecurityCenter;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "Client:MainActivity";

    BinderPool binderPool;
    private ISecurityCenter mSecurityCenter;
    private ICompute mCompute;

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

        binderPool = BinderPool.getInstance(this);

    }

    public void callSecurity(View v) {
        new Thread() {
            @Override
            public void run() {
                Log.d(TAG, "callSecurity");
                IBinder securityBinder = binderPool.queryBinder(BinderPool.BINDER_SECURUITY_CENTER);
                mSecurityCenter = ISecurityCenter.Stub.asInterface(securityBinder);
                if (mSecurityCenter != null) {
                    String msg = "Android";
                    try {
                        String password = mSecurityCenter.encrypt(msg);
                        Log.d(TAG, "encrypt message = " + password);

                        String decrypt = mSecurityCenter.decrypt(password);
                        Log.d(TAG, "decrypt message = " + decrypt);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                } else {
                    Log.d(TAG, "mSecurityCenter = null");
                }

            }
        }.start();


    }

    public void callCompute(View v) {
        Log.d(TAG, "callCompute");
        IBinder computeBinder = binderPool.queryBinder(BinderPool.BINDER_COMPUTE);
        mCompute = ICompute.Stub.asInterface(computeBinder);
        if (mCompute != null) {
            try {
                int result = mCompute.add(3, 5);
                Log.d(TAG, "compute result = " + result);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        } else {
            Log.d(TAG, "mCompute = null");
        }
    }

}

疑问:主线程中调用远程服务是否会导致ANR?
答案是会的,因为客户端调用了远程的服务,客户端的线程就挂起了,直到服务器返回结果。
例如:
在这里插入图片描述
客户端的打印也是有6秒等待的。
在这里插入图片描述

布局文件为:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context=".MainActivity"
    tools:showIn="@layout/activity_main"
    android:orientation="vertical"
    >

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="callSecurity"
        android:onClick="callSecurity"
        />
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="callCompute"
        android:onClick="callCompute"
        />


</LinearLayout>

到这里,客户端的代码就写完了,不出问题可以正常运行了。
特别注意:运行的时候,先要启动服务端的app,否则绑定不了服务
代码的结构如下:
在这里插入图片描述

看看运行的结果:
在这里插入图片描述
成功调用到了远程的服务方法。

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

三、选择合适的IPC方式

我们介绍了各种各样的IPC方式,那么到底它们有什么不同呢?我到底该使用哪一种呢?本节就为读者解答这些问题,具体内容如表所示。通过表可以明确地看出不同IPC方式的优缺点和适用场景,那么在实际的开发中,只要我们选适的IPC方式就可以轻松完成多进程的开发场景。
在这里插入图片描述

发布了126 篇原创文章 · 获赞 42 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/gaopinqiang/article/details/105106835