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
最后附上运行图
- Andorid中的AIDL(二)
- Andorid中的AIDL(一)
- Andorid中的AIDL(三)
- andorid设计模式系列二:andorid中的MVP模式(上)
- Andorid中的动态加载(二)--加载机制
- andorid aidl详解
- Andorid总结 - AIDL
- Android中的IPC方式(二)—— AIDL
- Andorid--Apache HttpClient(二)
- AIDL基本使用(二)
- Android 深入浅出AIDL(二)
- Android AIDL学习(二)
- Andorid Animation and Graphics系列(二)
- Framework中的AIDL(原)
- Framework中的AIDL(原)
- Framework中的AIDL(原)
- AIDL使用详解 (二)AndroidStudio中 创建 AIDL service
- andorid中的相对布局(Relative Layout)
- 理解Spring+SpringMVC+Hibernate开发流程,附一定源码(一)
- node 使用 import
- java的静态方法可以直接用类名调用的理解
- 面向对象的三个基本特征
- java--final修饰符
- Andorid中的AIDL(二)
- QTcpServer、QTcpSocket、QUdpSocket在聊天程序上的应用
- TCP协议中的三次握手和四次挥手(图解)
- 基于多线程的随机数生成算法
- Struts(一)
- 【算法题】二叉树非递归遍历
- 第二章 人月神话
- java私有构造器
- Linux 中的信号