AIDL 总结

1.特点

AIDL定义客户端与服务均认可的编程接口,以便二者使用进程间通信 (IPC) 进行相互通信。在 Android 中,一个进程通常无法访问另一个进程的内存。因此,为进行通信,进程需将其对象分解成可供操作系统理解的原语,并将其编组为可供您操作的对象。编写执行该编组操作的代码较为繁琐,因此 Android 会使用 AIDL 为您处理此问题。

注意:
只有在不同应用的客户端通过 IPC 方式访问服务,且在服务中进行多线程处理时,才有必要使用 AIDL。

如果您无需跨不同应用执行并发 IPC,则应通过实现 Binder来创建接口

如果您想执行 IPC,但需要处理多线程,请[使用 Messenger 来实现接口。无论如何,在实现 AIDL 之前,请您务必理解[绑定服务

在开始设计 AIDL 接口之前,请注意,AIDL 接口的调用是直接函数调用。您无需对发生调用的线程做任何假设。实际情况的差异取决于调用是来自本地进程中的线程,还是远程进程中的线程。具体而言:

  • 来自本地进程的调用和发起调用在同一线程内。如果该线程是您的主界面线程,则其将继续在 AIDL 接口中执行。如果该线程是其他线程,则其便是在服务中执行代码的线程。因此,只有在本地线程访问服务时,您才能完全控制哪些线程在服务中执行(但若出现此情况,您根本无需使用 AIDL,而应通过实现 Binder 类来创建接口)。
  • 远程进程的调用分配来自线程池,且平台会在您自己的进程内部维护该线程池。您必须为来自未知线程,且多次调用同时发生的传入调用做好准备。换言之,AIDL 接口的实现必须基于完全的线程安全。如果调用来自同一远程对象上的某个线程,则该调用将依次抵达接收器端。
  • oneway 关键字用于修改远程调用的行为。使用此关键字后,远程调用不会屏蔽,而只是发送事务数据并立即返回。最终接收该数据时,接口的实现会将其视为来自 Binder 线程池的常规调用(普通的远程调用)。如果 oneway 用于本地调用,则不会有任何影响,且调用仍为同步调用。

1.AIDL可以传递哪些数据

  • Java 编程语言中的所有原语类型(如 intlongcharboolean 等)

  • String CharSequence List

    List 中的所有元素必须是以上列表中支持的数据类型,或者您所声明的由 AIDL 生成的其他接口或 Parcelable 类型。

    Map

    Map 中的所有元素必须是以上列表中支持的数据类型,或者您所声明的由 AIDL 生成的其他接口或 Parcelable 类型。不支持泛型 Map(如 Map<String,Integer> 形式的 Map)

如果你要使用上方未列出的附加类型,如Book对象,要添加一条 import 语句。也是为什么创建Book.aidl文件的原因

请注意:

  • 方法可有参数和返回值或空值。

  • 所有非原语参数均需要指示数据走向的方向标记(in out inout)。这类标记可以是原语类型默认为 in,不能是其他方向。

    注意:您应将方向限定为真正需要的方向,因为编组参数的开销较大

  • 生成的 IBinder 接口内包含 .aidl 文件中的所有代码注释(import 和 package 语句之前的注释除外)。

  • 您可以在 ADL 接口中定义 String 常量和 int 字符串常量。例如:const int VERSION = 1;

  • 方法调用由 transact() 代码分派,该代码通常基于接口中的方法索引。由于这会增加版本控制的难度,因此您可以向方法手动配置事务代码:void method() = 10;

  • 使用 @nullable 注释可空参数或返回类型。

2.in out inout

所有非基本类型的参数都需要一个定向tag来表明数据是如何走向的,要不是in,out或者inout。基本数据类型默认是in,而且不能是其他tag。
其中 in 表示数据只能由客户端流向服务端, out 表示数据只能由服务端流向客户端,而 inout 则表示数据可以在服务端与客户端之间双向流通。

2.服务端

a. 创建 module_server,然后右键添加aidl文件,注意包名

ServerInterface.aidl

interface ServerInterface {

    /** client调用server ,传递数据json**/
    boolean sendMsgToServer(String packageName,String json);
    /** 注册一个callback ,用于回调给client数据**/
    void registerCallbackToServer(String packageName,in ClientCallback clientCallback);	
    void unRegisterCallbackToServer(String packageName,in ClientCallback clientCallback);

}

ClientCallback.aidl

import com.example.aidl.Book;

interface ClientCallback {
    /** 发送json格式数据给client**/
      boolean onServerAction(String json);
    
}

如果内部需要传递对象Book,就需要序列化。注意导包Book

Book.java

public class Book implements Parcelable {
    String name;
...

同时还需要创建Book.aidl文件,注意要和aidl文件放到一起

Book.aidl

// Book.aidl
package com.example.aidl;

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

parcelable Book;

创建完毕aidl文件,记得执行build 编译一下.会自动构建相对应的接口文件。ServerInterface.aidl生成的文件名是ServerInterface.java

b. 创建一个ServerService

ServerService.java

 
public class ServerService extends Service {
    private static final String TAG = "AidlService";
    private BindManager mBindManager;
    private ServerInterface.Stub serverInterface = new ServerInterface.Stub() {
        @Override
        public boolean sendMsgToServer(String packageName, String json) throws RemoteException {
            Log.d(TAG, "-------------sendMsgToServer: " + json);
            if (!TextUtils.isEmpty(json)) {
                mBindManager.receiverClientMsg(json);
                return true;
            }
            return false;
        }

        @Override
        public void registerCallbackToServer(String packageName, ClientCallback clientCallback) throws RemoteException {
            Log.d(TAG, "----------------registerCallbackToServer: ");
            if (!TextUtils.isEmpty(packageName) && clientCallback != null) {
                mBindManager.registerClientCallback(packageName, clientCallback);
            }
        }

        @Override
        public void unRegisterCallbackToServer(String packageName, ClientCallback clientCallback) throws RemoteException {
            if (clientCallback != null) {
                mBindManager.unRegisterClientCallback(getPackageName(),clientCallback);
            }
        }

    };

    @Override
    public void onCreate() {
        super.onCreate();
        mBindManager = BindManager.getInstance(this);
        Log.d(TAG, "------------ ----------------onCreate: ");
    }

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

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

然后注册清单文件

        <service
            android:name="com.example.aidl.AidlService"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="qqq.aaa.zzz"></action>
            </intent-filter>
        </service>

为了有一个统一对接外部的接口,需要创建一个Manager。

BindManager.java

 
public class BindManager {
    private static final String TAG = "BindManager";
    private static BindManager mBindManager = null;
    private static Context context;
    private static RemoteCallbackList<ClientCallback> remoteCallbackList = new RemoteCallbackList<>();

    private BindManager(Context context) {
        this.context = context;
    }

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

    public static void killRemoteBackList() {
        Log.d(TAG, "------------------------------clearCallbacks: ");
        remoteCallbackList.kill();

    }

    public static void registerClientCallback(String packageName, ClientCallback clientCallback) {
        Log.d(TAG, "------------------------------registerClientCallback: " + packageName);
        remoteCallbackList.register(clientCallback, packageName);

    }

    public static void unRegisterClientCallback(String packageName,ClientCallback clientCallback) {
        Log.d(TAG, "------------------------------unregisterClientCallback: ");
        remoteCallbackList.unregister(clientCallback);
        remoteCallbackList.onCallbackDied(clientCallback,packageName);
    }

    /**
     * 服务端接受客户端的信息
     *
     * @param json
     */
    public static void receiverClientMsg(String json) {
        // TODO 接受到客户端发来的信息,未来要通知小程序框架,执行某操作
        Log.d(TAG, "receiverClientMsg: " + json);
    }

    /**
     * 服务端发送信息给客户端
     *
     * @param json
     */
    public static void sendMsgToClient(String packageName, String json) {
        try {
            int i = remoteCallbackList.beginBroadcast();
            Log.d(TAG, "------------------sendMsgToClient: (i="+i+") json:" + json+"  packageName:"+packageName);
            while (i > 0) {
                i--;
                String cookie = (String) remoteCallbackList.getBroadcastCookie(i);
                if (packageName.equals(cookie)) {
                    ClientCallback callback = remoteCallbackList.getBroadcastItem(i);
                    callback.onServerAction(json);
                }
            }
        } catch (RemoteException e) {
            e.printStackTrace();
        }finally {
            remoteCallbackList.finishBroadcast();
        }
    }
}

3.客户端

1.创建客户端的module,并且创建aidl文件,注意此处aidl的包名和文件名要和服务端保持一致。

2.开启绑定服务

创建一个界面,首先绑定service ,然后创建serviceConnection负责连接服务对象,同时创建一个callback对象和一个DeathRecipient对象,还有对接服务端的接口对象。

ClientCallback:负责数据的回调,接收服务端的数据
DeathRecipient:当服务端被杀死的,该方法会触发,然后再次启动服务。其实就是避免服务端停止。

ServerInterface:接口对象,里面包含接口的api,负责客户端和服务端交互


public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = "MainActivity";
    private ServerInterface serverInterface;
    private boolean connected;
    private TextView result;

    private ClientCallback clientCallback = new ClientCallback.Stub() {
        @Override
        public boolean onServerAction(String json) throws RemoteException {
            Log.d(TAG, "-----------1----------sendMsgToClient: " + json);
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Log.d(TAG, "-----------2---------sendMsgToClient: " + json);
            return true;
        }
    };

    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            try {
                Log.d(TAG, "------------------------------onServiceConnected: "+getPackageName());
                serverInterface = ServerInterface.Stub.asInterface(service);
                serverInterface.registerCallbackToServer(getPackageName(), clientCallback);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            connected = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            connected = false;
            try {
                Log.d(TAG, "------------------------------onServiceDisconnected: ");
                serverInterface.unRegisterCallbackToServer(getPackageName(),clientCallback);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    };


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

        findViewById(R.id.click1).setOnClickListener(this);
        findViewById(R.id.click2).setOnClickListener(this);
        findViewById(R.id.click3).setOnClickListener(this);
        result = findViewById(R.id.result);

        bindService();
    }

    private void bindService() {
        Log.d(TAG, "------------------------------bindService: ");
        Intent intent = new Intent();
        intent.setPackage("com.example.aidlserver");
//        intent.setComponent(new ComponentName("com.example.aidl","com.example.aidl.ServerService"));
        intent.setAction("qqq.aaa.zzz");
        intent.putExtra("packageName", getPackageName());
        boolean status = bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);

        Log.d(TAG, "------------------------------bindService: " + status);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.click1:

                Log.d(TAG, "---------1---------------------onClick: ");
                bindService();
                break;
            case R.id.click2:
                Log.d(TAG, "---------2---------------------onClick: ");
                try {
                    serverInterface.sendMsgToServer(getPackageName(), "this is client msg,server please receiver");
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
            case R.id.click3:
                Log.d(TAG, "-----------3-------------------onClick: ");
                throw new NullPointerException("client 崩溃");
        }
    }
}

常见问题

1.java.lang.SecurityException: Binder invocation to an incorrect interface

客户端和服务端的包名要保持一致。不然找不到对应的资源。

2.beginBroadcast() called while already in a broadcast

参考:https://blog.csdn.net/chentaishan/article/details/124526994?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22124526994%22%2C%22source%22%3A%22chentaishan%22%7D&ctrtid=oNDqU

3.client绑定server后,杀死server的情况

这种情况下,默认会触发client的onServiceDisconnected方法,但是如果在onServiceConnected方法绑定过deathRecipient监听的话,还会触发DeathRecipient 的binderDied()方法。

  override fun onServiceConnected(name: ComponentName, service: IBinder?) {
            try {
                ...
                service?.asBinder()?.linkToDeath(deathRecipient, 0)
                ...
            } catch (e: RemoteException) {
                e.printStackTrace()
            }
        }
public interface DeathRecipient {
    public void binderDied();
}

4.client绑定server后,杀死client的情况

如果当前只有一个client 绑定server,那server会触发onUnbind() onDestory()
如果多个client绑定服务,server无其他相应,当然会移除client的callback回调,这是server的RemoteCallbacklist 自动完成的。因为内部也有对client的监听。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值