Andorid中的AIDL(二)

来源:互联网 发布:substring mysql 编辑:程序博客网 时间:2024/05/18 00:42

对上一篇的AIDL生成的java文件做了一番改动,分析Binder在CLIENT端回调Server端的过程。这里我把混淆视听的 AIDL文件,和接口文件全部删掉从Binder入手分析。

做成了如下的这个结构的client端和server端,把aidl生成的java文件进行了简化。


1)从服务端开始分析ServerService文件代码如下。


package com.xue.qin.server;import android.os.SystemClock;import android.util.Log;import java.text.SimpleDateFormat;import java.util.Locale;/** * Created by xue.qin on 2017/6/4. */public class ServerBinder extends android.os.Binder implements android.os.IInterface {    private static final String TAG = "QX_ServerBinder";    private static final String DESCRIPTOR = "myDescriptor";   //aidl文件的包名    public ServerBinder() {        //绑定唯一的DESCRIPTOR;        this.attachInterface(this, DESCRIPTOR);    }    @Override    public android.os.IBinder asBinder() {        Log.i(TAG, "asBinder()");        return this;    }    @Override    public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {        Log.i(TAG, "onTransact()");        switch (code) {            case INTERFACE_TRANSACTION: {                Log.i(TAG, "onTransact()code = INTERFACE_TRANSACTION");                reply.writeString(DESCRIPTOR); //来自CLIENT的调用getInterfaceDescriptor()                return true;            }            case TRANSACTION_getCurrentTime: {                Log.i(TAG, "onTransact() code = TRANSACTION_getCurrentTime");                data.enforceInterface(DESCRIPTOR);                int _arg0;                _arg0 = data.readInt();                String _result = this.getCurrentTime(_arg0, 0); //这里调用本地方法                reply.writeNoException();                reply.writeString(_result);                return true;            }            case TRANSACTION_getNewYorkCurrentTime: {                Log.i(TAG, "onTransact() code = TRANSACTION_getCurrentTime");                data.enforceInterface(DESCRIPTOR);                int _arg0;                _arg0 = data.readInt();                String _result = this.getCurrentTime(_arg0, -12); //这里调用本地方法                reply.writeNoException();                reply.writeString(_result);                return true;            }        }        return super.onTransact(code, data, reply, flags);    }    public String getCurrentTime(int which, int jetlag) {        Log.i(TAG, "getCurrentTime()");        StringBuilder builder = new StringBuilder("");        switch (which) {            case 0:                long localtime = System.currentTimeMillis();                SimpleDateFormat sdf = new SimpleDateFormat("", Locale.SIMPLIFIED_CHINESE);                sdf.applyPattern("yyyy年MM月dd日 \nHH时mm分ss秒");                if (jetlag == 0) {                    builder = builder.append("\n本地\n");                    builder.append(sdf.format(localtime));                } else {                    builder = builder.append("\nNewYork\n");                    builder = builder.append(sdf.format(localtime + jetlag * 1000 * 3600));                }                break;            case 1:                builder = builder.append("\n开机之后过去了\n " + formatTime(SystemClock.elapsedRealtime()));                break;            case 2:                //SystemClock.uptimeMillis() 去掉休眠的时间                builder = builder.append("\n实际使用了\n" + formatTime(SystemClock.uptimeMillis()));                break;        }        return builder.toString();    }    public static String formatTime(long time) {        int total = (int) (time / 1000);        int seconds = total % 60;        int minute = total % 3600 / 60;        int hours = total / 3600;        return String.format("%d小时%d分钟 %d秒", hours, minute, seconds);    }//    static final int TRANSACTION_getCurrentTime = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);    static final int TRANSACTION_getCurrentTime = 100;    static final int TRANSACTION_getNewYorkCurrentTime = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);}

重点1、 android.os.IInterface 这个接口的接入,这个接口的回调在自定义的ServerBinder中覆盖为

 @Override    public android.os.IBinder asBinder() {        Log.i(TAG, "asBinder()");        return this;    }

建立了系统与这个Binder的联系,便于系统来获取此Binder实现进程间的通信。

重点2、 通信接口的描述,形如下面的代码

private static final String DESCRIPTOR = "myDescriptor";   //aidl文件的包名

在自动生成的文件中是文件的包名和aidl接口的名字,其实是可以随意命名。

 public ServerBinder() {        //绑定唯一的DESCRIPTOR;        this.attachInterface(this, DESCRIPTOR);    }

调用attachInterface,可以将Bundle和接口描述,绑定在一起。

另外就是数据的传递,另外是进程间数据的传递,必须在一个可共同读写的内存里,在java虚拟机中肯定是不可以了,因此这个DESCRIPTOR就是这块内存的名字。可以通过它来找到这片可供通信的内存,正如第39行的调用一样。

data.enforceInterface(DESCRIPTOR);


进到源码有描述为

/**     * Store or read an IBinder interface token in the parcel at the current     * {@link #dataPosition}.  This is used to validate that the marshalled     * transaction is intended for the target interface.     */    public final void writeInterfaceToken(String interfaceName) {        nativeWriteInterfaceToken(mNativePtr, interfaceName);    }    public final void enforceInterface(String interfaceName) {        nativeEnforceInterface(mNativePtr, interfaceName);    }

大概意思是

 public final void writeInterfaceToken(String interfaceName)  :存数据

public final void enforceInterface(String interfaceName): 读数据

看到这是一个native方法,我终于有点明白了,我猜应该是 用JNI的方式传到C层,进行进程间的数据传递。

JVM有隔离,咱去内存中拿总是可以的。


重点3、

static final int TRANSACTION_getCurrentTime = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);static final int TRANSACTION_getNewYorkCurrentTime = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);


这些值是方法的一个标记,用来标记方法的调用,实现客户端和服务端的统一,调用在第29行如下。


 @Override    public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {        Log.i(TAG, "onTransact()");        switch (code) {            case INTERFACE_TRANSACTION: {                Log.i(TAG, "onTransact()code = INTERFACE_TRANSACTION");                reply.writeString(DESCRIPTOR); //来自CLIENT的调用getInterfaceDescriptor()                return true;            }            case TRANSACTION_getCurrentTime: {                Log.i(TAG, "onTransact() code = TRANSACTION_getCurrentTime");                data.enforceInterface(DESCRIPTOR);                int _arg0;                _arg0 = data.readInt();                String _result = this.getCurrentTime(_arg0, 0); //这里调用本地方法                reply.writeNoException();                reply.writeString(_result);                return true;            }            case TRANSACTION_getNewYorkCurrentTime: {                Log.i(TAG, "onTransact() code = TRANSACTION_getCurrentTime");                data.enforceInterface(DESCRIPTOR);                int _arg0;                _arg0 = data.readInt();                String _result = this.getCurrentTime(_arg0, -12); //这里调用本地方法                reply.writeNoException();                reply.writeString(_result);                return true;            }        }        return super.onTransact(code, data, reply, flags);    }

根据code来映射当前服务端的方法,code来自Client,待将服务端代码贴上。code可以根据自己喜好随意写,保证不重复即可,应该还是要求>=1吧,因为默认的是1。


2)从客户端来分析代码,ClientActivity.java代码如下:

package com.xue.qin.aidldemo;import android.app.Activity;import android.content.ComponentName;import android.content.Context;import android.content.Intent;import android.content.ServiceConnection;import android.os.Bundle;import android.os.IBinder;import android.os.RemoteException;import android.util.Log;import android.view.View;import android.widget.Button;import android.widget.TextView;/** * Created by xue.qin on 2017/6/1. */public class ClientActivity extends Activity implements View.OnClickListener {    private static final String TAG = "QX_ClientActivity";    private TextView mTextView;    private Button mButton;    private Client mService = null;    private ServiceConnection mConntection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            Log.i(TAG, "onServiceConnected() ");            mService = new Client(service);        }        @Override        public void onServiceDisconnected(ComponentName name) {            Log.i(TAG, "onServiceDisconnected()");            mService = null;        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main_activity_layout);        mTextView = (TextView) findViewById(R.id.result);        mButton = (Button) findViewById(R.id.switchmodel);        mButton.setOnClickListener(this);    }    @Override    protected void onResume() {        super.onResume();        Intent intent = new Intent("remoteService");        intent.setPackage("com.xue.qin.server");        Log.i(TAG, "bindService()");        boolean success = bindService(intent, mConntection, Context.BIND_AUTO_CREATE);    }    private volatile int mCount = 0;    @Override    public void onClick(View v) {        switch (v.getId()) {            case R.id.switchmodel:                mCount++;                if (mCount > 100) {                    mCount = 0;                }                try {                    String here = mService.getCurrentTime(mCount % 3);                    String NewYork = mService.getNewYorkCurrentTime(mCount % 3);                    mTextView.setText(here + NewYork);                } catch (RemoteException e) {                    e.printStackTrace();                }                break;        }    }    @Override    protected void onPause() {        super.onPause();        unbindService(mConntection);    }}

先绑定Service,在onServiceConnected,将IBinder接收装饰成为自定义的Client类。代码如下。


package com.xue.qin.aidldemo;import android.os.RemoteException;import android.util.Log;/** * Created by xue.qin on 2017/6/4. */public class Client {    private static final String TAG = "QX_Client";    private static final String DESCRIPTOR = "myDescriptor";   //aidl文件的包名    private android.os.IBinder mRemote;    public Client(android.os.IBinder obj) {        Log.i(TAG, "ClientBinder()");        mRemote = obj;    }    public String getCurrentTime(int which) throws RemoteException {        Log.i(TAG, "getCurrentTime()");        android.os.Parcel _data = android.os.Parcel.obtain();        android.os.Parcel _reply = android.os.Parcel.obtain();        String _result;        try {            _data.writeInterfaceToken(DESCRIPTOR);            _data.writeInt(which);            mRemote.transact(TRANSACTION_getCurrentTime, _data, _reply, 0);            _reply.readException();            _result = _reply.readString();        } finally {            _reply.recycle();            _data.recycle();        }        return _result;    }    public String getNewYorkCurrentTime(int which) throws RemoteException {        Log.i(TAG, "getNewYorkCurrentTime()");        android.os.Parcel _data = android.os.Parcel.obtain();        android.os.Parcel _reply = android.os.Parcel.obtain();        String _result;        try {            _data.writeInterfaceToken(DESCRIPTOR);            _data.writeInt(which);            mRemote.transact(TRANSACTION_getNewYorkCurrentTime, _data, _reply, 0);            _reply.readException();            _result = _reply.readString();        } finally {            _reply.recycle();            _data.recycle();        }        return _result;    }    //    static final int TRANSACTION_getCurrentTime = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);    static final int TRANSACTION_getCurrentTime = 100;    static final int TRANSACTION_getNewYorkCurrentTime = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);}

重点1、首先要将获得的Ibinder接收到,在构造函数中获得,mRemote = obj,用它来调用服务端的方法,使用此类来装饰这个获得的mRemote

public Client(android.os.IBinder obj) {        Log.i(TAG, "ClientBinder()");        mRemote = obj;    }


重点2、需要定义要调用服务端方法的标记,也就是code,以此来形成映射的关系。此code可以定义为任意数字,我把它定为100
static final int TRANSACTION_getCurrentTime = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);


重点3,客户端调用的写法。如下代码


public String getCurrentTime(int which) throws RemoteException {        Log.i(TAG,"getCurrentTime()");        android.os.Parcel _data = android.os.Parcel.obtain();        android.os.Parcel _reply = android.os.Parcel.obtain();        String _result;        try {            _data.writeInterfaceToken(DESCRIPTOR);            _data.writeInt(which);            mRemote.transact(TRANSACTION_getCurrentTime, _data, _reply, 0);            _reply.readException();            _result = _reply.readString();        } finally {            _reply.recycle();            _data.recycle();        }        return _result;    }


注意到
_data.writeInterfaceToken(DESCRIPTOR);
就像服务端一样,这里是调用写入,服务端是调用读取。接下来利用服务端返回这里被接收为mRemote来调用服务端的方法
  mRemote.transact(TRANSACTION_getCurrentTime, _data, _reply, 0);
服务端的onTransact就会被调用,并且利用第一个参数code映射对应方法,数据可以去DESCRIPTOR的内存里去拿处理完了还可以吧结果写到reply。
这样就完成了通信的过程,另外在调用transact时,这个时候没有返回值之前是当前线程是阻塞的。
thinkingInJava中677页,对共享资源的访问,需要在这个访问方法中加上synchronized,这样线程才能安全,访问的数据才能防止资源冲突,因此,这里当前线程肯定是要阻塞的。我想android中原理应该也差不多吧。
有了返回值,这样通信就完成了。来看一下Log


06-05 00:14:00.251 8676-8676/? I/QX_ClientActivity: bindService()06-05 00:14:00.588 8716-8716/? I/QX_ServerService: onCreate()06-05 00:14:00.589 8716-8716/? I/QX_ServerService: onBind()06-05 00:14:00.590 8676-8676/? I/QX_ClientActivity: onServiceConnected() 06-05 00:14:00.591 8676-8676/? I/QX_Client: ClientBinder()06-05 00:14:03.353 8676-8676/com.xue.qin.aidldemo I/QX_Client: getCurrentTime()06-05 00:14:03.358 8716-8731/com.xue.qin.server I/QX_ServerBinder: onTransact()06-05 00:14:03.358 8716-8731/com.xue.qin.server I/QX_ServerBinder: onTransact() code = TRANSACTION_getCurrentTime06-05 00:14:03.359 8716-8731/com.xue.qin.server I/QX_ServerBinder: getCurrentTime()06-05 00:14:03.360 8676-8676/com.xue.qin.aidldemo I/QX_Client: getNewYorkCurrentTime()06-05 00:14:03.360 8716-8728/com.xue.qin.server I/QX_ServerBinder: onTransact()06-05 00:14:03.360 8716-8728/com.xue.qin.server I/QX_ServerBinder: onTransact() code = TRANSACTION_getCurrentTime06-05 00:14:03.361 8716-8728/com.xue.qin.server I/QX_ServerBinder: getCurrentTime()

客户端绑定服务端,
服务端返回IBinder,
客户端接收IBinder,
客户端调用本地方法,
本地方法使用接收到的IBinder调用服务端方法,
服务端Binder执行onTransact回调,
调用服务端方法。


综上来说,IBinder起到了一个中间转发的作用,转发了 1服务端请求的code,2函数的参数,3返回值 三个数据。
Binder自己本身是不保存数据或者方法的,同过bindService这中方式,Binder建立起了两个进程间的通信。
就像android中线程之间通过Handler信息转发类似,相信随着工作的深入,会有更深入的了解。


demo 地址  https://github.com/xueqin123/ClientServer
最后附上运行图



原创粉丝点击