Android 进程通信 (IPC) 之 AIDL 的使用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/afei__/article/details/84594748

一、简介

AIDL 是 Android Interface Definition Language 的缩写,是 Android 定义的一种专门用于进程间通信的接口语言,它的语法也和 java 比较类似。

二、特点

我们知道 Android 中还有一种轻量级的进程通信方式——Messenger

Messenger 类似于一个进程间使用的 Handler,它适用于消息的传递,服务器只能以串行的方式一个一个地处理客户端的消息。无法处理大量的并发请求,也不能直接地调用服务器的方法,若想解决这种问题,则需要使用 AIDL。

关于 Messenger 的介绍可以戳: https://blog.csdn.net/afei__/article/details/83386759

AIDL 可以让我们定义一系列的接口,供客户端和服务端共同调用,区别于 Java 的接口,它只支持定义方法,不支持静态常量。对象本质还是不能跨进程传输的,Binder 会把客户端和服务端传递的对象序列化重新转化生成一个新的对象进行传递。

三、支持的数据类型

  • 基本数据类型
  • StringCharSequence
  • ListMap
  • Parcelable
  • AIDL 接口本身

四、定向 Tag

定向 Tag 有三种,即:

  • in : 表示输入参数,数据只能从客户端流入服务端
  • out : 表示输出参数,数据只能从服务端流入客户端
  • inout : 表示输入输出参数,即数据可以在客户端和服务端双向流通

AIDL 中除基本数据类型和 String 外,其余类型 必须 标注定向 Tag。

建议不要随意使用 inout,因为会加大系统的消耗和降低效率。
我们应该保证,对于输入参数就使用 in,输出参数就使用 out

五、实战

1. 创建一个自定义的序列化对象(可选)

public class TestData implements Parcelable {
    private String mString;
    private int mInt;
    public TestData(String string, int anInt) {
        mString = string;
        mInt = anInt;
    }
    // 省略...
    // 可使用插件直接生成 Parcelable 的实现
}

2. 创建自定义序列化对象对应的 AIDL 文件(可选)

如果上一步做了,那这一步就是必须做的了。
操作步骤:src -> 右键 -> new -> AIDL -> AIDL File,且包名、文件名一定和要类名一致,例如:

// TestData.aidl
package com.afei.androidipc;
parcelable TestData;

3. 创建 AIDL 接口

操作步骤:src -> 右键 -> new -> AIDL -> AIDL File,文件名取一个合适的就行。实例:

// MyAidlInterface.aidl
package com.afei.androidipc;
// 在这里引用非默认的数据类型
import com.afei.androidipc.TestData;
interface MyAidlInterface {
    // 1. 基本数据类型
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble);
    // 2. String类 + CharSequence接口
    void stringType(String aString);
    // ****** 以下类型参数必须指明方向 (in / out / inout)
    // 3. 序列化的类
    void parcelableType(in Bundle aBundle);
    // 4. List 和 Map, 并且里面的元素也是可序列化的
    void listType(in List list);
    void mapType(in Map map);
    // 5. 自定义的序列化类
    void setTestData(in TestData data);
    TestData getTestData();
}

这里有一点要注意的就是,ListMap 的泛型可以不用指定,List 可以写基本数据类型或实现 Parcelable 接口的类,Map 写明泛型反而编译报错(目前我使用中是这样的)。

上述操作,编译器会帮你在 build/generated/source/aidl/{debug,release}/{packagename}/ 目录下帮你生成一个相应的接口类。

4. 服务端

服务端就是创建一个 MyAidlInterface.Stub 对象并实现之前声明的接口:

public class AIDLService extends Service {
    private static final String TAG = "AIDLService";
    private final MyAidlInterface.Stub mStub = new MyAidlInterface.Stub() {
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double
                aDouble) throws RemoteException {
            Log.d(TAG, "basicTypes: anInt: " + anInt);
            Log.d(TAG, "basicTypes: aLong: " + aLong);
            Log.d(TAG, "basicTypes: aBoolean: " + aBoolean);
            Log.d(TAG, "basicTypes: aFloat: " + aFloat);
            Log.d(TAG, "basicTypes: aDouble: " + aDouble);
        }
        @Override
        public void stringType(String aString) throws RemoteException {
            Log.d(TAG, "stringType: " + aString);
        }
        @Override
        public void parcelableType(Bundle aBundle) throws RemoteException {
            int anInt = aBundle.getInt("int");
            Log.d(TAG, "parcelableType: get int: " + anInt);
        }
        @Override
        public void listType(List list) throws RemoteException {
            Log.d(TAG, "listType: list size: " + list.size());
            Log.d(TAG, "listType: " + list.toString());
        }
        @Override
        public void mapType(Map map) throws RemoteException {
            Log.d(TAG, "mapType: map size: " + map.size());
            Log.d(TAG, "mapType: " + map.toString());
        }
        @Override
        public void setTestData(TestData data) throws RemoteException {
            Log.d(TAG, "setTestData: " + data.toString());
        }
        @Override
        public TestData getTestData() throws RemoteException {
            return new TestData("I'm from AIDLService", 666);
        }
    };
    @Override
    public IBinder onBind(Intent intent) {
        return mStub; // 在这里返回 mStub 对象实现绑定
    }
}

注意我们需要将这个服务运行在一个单独的进程,即需要在 AndroidManifest.xml 中给服务添加 process 声明:

<service android:name=".AIDLService" android:process=":aidl" />

5. 客户端

客户端就是绑定服务,并发送和接收数据:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = "MainActivity";
    private MyAidlInterface mAidlInterface;
    private ServiceConnection mAidlServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.d(TAG, "onServiceConnected: " + name);
            mAidlInterface = MyAidlInterface.Stub.asInterface(service);
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {}
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.aidl_btn).setOnClickListener(this);
        // 绑定服务
        Intent intent = new Intent(MainActivity.this, AIDLService.class);
        bindService(intent, mAidlServiceConnection, Context.BIND_AUTO_CREATE);
    }
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.aidl_btn:
                testAidl();
                break;
        }
    }
    private void testAidl() {
        if (mAidlInterface == null) {
            Log.e(TAG, "testAidl: service not connect!");
            return;
        }
        try {
            // 1. 基本数据类型
            mAidlInterface.basicTypes(1, 2, true, 3.0f, 4.0);
            // 2. String类 + CharSequence接口
            mAidlInterface.stringType("test string");
            // 3. 序列化的类
            Bundle bundle = new Bundle();
            bundle.putInt("int", 1);
            mAidlInterface.parcelableType(bundle);
            // 4. List 和 Map, 并且里面的元素也是可序列化的
            List<Integer> list = new ArrayList<>();
            for (int i = 0; i < 5; i++) {
                list.add(i);
            }
            mAidlInterface.listType(list);
            Map<Integer, String> map = new HashMap<>();
            map.put(1, "value1");
            map.put(2, "value2");
            map.put(3, "value3");
            mAidlInterface.mapType(map);
            // 5. 自定义的序列化类
            mAidlInterface.setTestData(new TestData("I'm from MainActivity", 666));
            Log.d(TAG, mAidlInterface.getTestData().toString());
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
    @Override
    protected void onDestroy() {
        unbindService(mAidlServiceConnection); // 解除服务绑定
        super.onDestroy();
    }
}

6. 运行结果

服务端 log:

客户端 log:

可以看到,所有数据都有正常的传递,并且我们可以看到它们的进程 id 不同,说明它们有运行在不同的进程里。

六、完整代码地址

https://github.com/afei-cn/AndroidIPC

猜你喜欢

转载自blog.csdn.net/afei__/article/details/84594748