Android进程间通信机制详解-AIDL

本文主要总结一下AIDL的基本使用和底层实现,对这方面感兴趣的可以看一下。

基本用法:

1、创建.aidl文件:IMyAidlInterface.aidl

// IMyAidlInterface.aidl
package aidl;

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

interface IMyAidlInterface {
    String getRemoteMessage();
}

aidl文件主要描述了服务端提供了哪些功能,为简单起见这里的服务端进程只有返回一条信息的功能。
2、编写服务端代码RemoteService.java

package demo.zkp.com.aidldemo;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;

import aidl.IMyAidlInterface;

/**
 * Created by zkp on 2017/2/14.
 */

public class RemoteService extends Service {

    private IMyAidlInterface.Stub stub = new IMyAidlInterface.Stub() {
        @Override
        public String getRemoteMessage() throws RemoteException {
            return "hello,i am remote!";
        }
    };

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

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

自定义服务需要继承自Service,可以看到服务端拥有一个IMyAidlInterface.Stub类型的成员变量stub,在onBind方法中返回了这个stub,stub是个什么东西后面会介绍(可以看出stub是个IBinder类型)。
3、编写客户端代码MainActivity.Java

package demo.zkp.com.aidldemo;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import aidl.IMyAidlInterface;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button btnBindService, btnUnBindService, btnCallMethod;
    private IMyAidlInterface iMyAidlInterface;
    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            iMyAidlInterface = null;
        }
    };

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

    private void initView() {
        btnBindService = (Button) findViewById(R.id.bind);
        btnUnBindService = (Button) findViewById(R.id.unbind);
        btnCallMethod = (Button) findViewById(R.id.call);

        btnBindService.setOnClickListener(this);
        btnUnBindService.setOnClickListener(this);
        btnCallMethod.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.bind:
                bindService();
                break;
            case R.id.unbind:
                unbindService();
                break;
            case R.id.call:
                call();
                break;
        }
    }

    private void bindService() {
        Intent intent = new Intent(this, RemoteService.class);
        bindService(intent, serviceConnection, BIND_AUTO_CREATE);
    }

    private void unbindService() {
        unbindService(serviceConnection);
        iMyAidlInterface = null;
    }

    private void call() {
        try {
            String remoteMsg = iMyAidlInterface.getRemoteMessage();
            Toast.makeText(this, remoteMsg, Toast.LENGTH_SHORT).show();
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onDestroy() {
        Log.d("dddddddddddddddddd", "dddddddddddddddddddd");
        super.onDestroy();
    }
}

客户端主要是绑定远程服务端然后调用服务端提供的方法。
aidl的基本用法介绍完了,下面深入了解下aidl的底层是如何实现进程间通信的。

原理浅析:

编译一下我们写的.aidl文件,会自动生成一个.java文件IMyAidlInterface.java,代码如下所示

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: G:\\AidlDemo\\app\\src\\main\\aidl\\aidl\\IMyAidlInterface.aidl
 */
package aidl;
// Declare any non-default types here with import statements

public interface IMyAidlInterface extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements aidl.IMyAidlInterface {
        private static final java.lang.String DESCRIPTOR = "aidl.IMyAidlInterface";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an aidl.IMyAidlInterface interface,
         * generating a proxy if needed.
         */
        public static aidl.IMyAidlInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof aidl.IMyAidlInterface))) {
                return ((aidl.IMyAidlInterface) iin);
            }
            return new aidl.IMyAidlInterface.Stub.Proxy(obj);
        }

        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_getRemoteMessage: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _result = this.getRemoteMessage();
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements aidl.IMyAidlInterface {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            @Override
            public java.lang.String getRemoteMessage() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.lang.String _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getRemoteMessage, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readString();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        static final int TRANSACTION_getRemoteMessage = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }

    public java.lang.String getRemoteMessage() throws android.os.RemoteException;
}

我们主要看一下Stub这个内部抽像类里面的代码

public static abstract class Stub extends android.os.Binder implements aidl.IMyAidlInterface

抽象类Stub继承自Binder并且实现了IMyAidlInterface里面的方法(不是真正的实现),服务端进程需要有一个Stub类型的对象并且实现它的方法。
接着看下asInterface方法:

public static aidl.IMyAidlInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof aidl.IMyAidlInterface))) {
                return ((aidl.IMyAidlInterface) iin);
            }
            return new aidl.IMyAidlInterface.Stub.Proxy(obj);
        }

asInterface方法需要一个IBinder类型的参数,这里分两种情况:
1、当调用asInterface方法的进程和服务端Service进程是同一进程时,不涉及进程间通信问题,此时传入的是BBinder类型的参数,最后直接将参数转化成IMyAidlInterface接口返回,即:

android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof aidl.IMyAidlInterface))) {
                return ((aidl.IMyAidlInterface) iin);
            }

2、当调用asInterface方法的进程和服务端Service进程不是同一进程时,涉及进程间通信问题,此时传入的是BpBinder类型的参数,最后返回Stub的代理,即:

return new aidl.IMyAidlInterface.Stub.Proxy(obj);

看一下这个代理类:

private static class Proxy implements aidl.IMyAidlInterface {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            @Override
            public java.lang.String getRemoteMessage() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.lang.String _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getRemoteMessage, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readString();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

这个代理类也实现了IMyAidlInterface接口,我们看下他是怎么实现的:


@Override
            public java.lang.String getRemoteMessage() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.lang.String _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getRemoteMessage, _data, _reply, 0);
                     _reply.readException();
                    _result = _reply.readString();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

方法中定义了两个Parcel类型的变量_data、_reply,前一个是客户端进程往服务端传递参数用的,后一个是服务端往客户端返回数据用的,函数最终调用的是:

mRemote.transact(Stub.TRANSACTION_getRemoteMessage, _data, _reply, 0);

其中第一个参数用来表示要调用服务端进程中的哪个方法。
这个方法经过Binder驱动最终会调用到服务端stub的onTransact方法:

@Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_getRemoteMessage: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _result = this.getRemoteMessage();
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

在onTransact方法里面根据code来决定调用服务端的哪个方法,并将要返回的数据写到reply里面。这个过程比较复杂,感兴趣的可以看下老罗的这一系列的文章Android进程间通信(IPC)机制Binder简要介绍和学习计划

回想下最上面aidl的基本用法,我们在远程service进程里面new了一个Stub类型的变量并实现了它里面的方法,当客户端绑定远程service时服务端将这个stub返回给客户端进程,客户端拿到这个stub以后将它作为参数调用了Stub的静态方法asInterface,最终得到了一个服务端Stub的代理对象,所以客户端才能够像调用自己的方法一样调用服务端的方法。

演示Demo下载链接
如果大家有什么疑问或文章中有讲的不对的地方,欢迎大家提出来讨论,谢谢大家。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值