Android aidl Binder框架浅析

08-09 22:56:38.959: E/client(29477): onServiceConnected

可以看到,点击BindService之后,服务端执行了onCreate和onBind的方法,并且客户端执行了onServiceConnected方法,标明服务器与客户端已经联通

然后点击12+12,50-12可以成功的调用服务端的代码并返回正确的结果

下面我们再点击unBindService

08-09 22:59:25.567: E/server(29692): onUnbind

08-09 22:59:25.567: E/server(29692): onDestroy

由于我们当前只有一个客户端绑定了此Service,所以Service调用了onUnbind和onDestory

然后我们继续点击12+12,50-12,通过上图可以看到,依然可以正确执行,也就是说即使onUnbind被调用,连接也是不会断开的,那么什么时候会端口呢?

即当服务端被异常终止的时候,比如我们现在在手机的正在执行的程序中找到该服务:

点击停止,此时查看log

08-09 23:04:21.433: E/client(30146): onServiceDisconnected

可以看到调用了onServiceDisconnected方法,此时连接被断开,现在点击12+12,50-12的按钮,则会弹出Toast服务端断开的提示。

说了这么多,似乎和Binder框架没什么关系,下面我们来具体看一看AIDL为什么做了些什么。

3、分析AIDL生成的代码


1、服务端

先看服务端的代码,可以看到我们服务端提供的服务是由

private final ICalcAIDL.Stub mBinder = new ICalcAIDL.Stub()

{

@Override

public int add(int x, int y) throws RemoteException

{

return x + y;

}

@Override

public int min(int x, int y) throws RemoteException

{

return x - y;

}

};

ICalcAILD.Stub来执行的,让我们来看看Stub这个类的声明:

public static abstract class Stub extends android.os.Binder implements com.zhy.calc.aidl.ICalcAIDL

清楚的看到这个类是Binder的子类,是不是符合我们文章开通所说的服务端其实是一个Binder类的实例

接下来看它的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_add:

{

data.enforceInterface(DESCRIPTOR);

int _arg0;

_arg0 = data.readInt();

int _arg1;

_arg1 = data.readInt();

int _result = this.add(_arg0, _arg1);

reply.writeNoException();

reply.writeInt(_result);

return true;

}

case TRANSACTION_min:

{

data.enforceInterface(DESCRIPTOR);

int _arg0;

_arg0 = data.readInt();

int _arg1;

_arg1 = data.readInt();

int _result = this.min(_arg0, _arg1);

reply.writeNoException();

reply.writeInt(_result);

return true;

}

}

return super.onTransact(code, data, reply, flags);

}

文章开头也说到服务端的Binder实例会根据客户端依靠Binder驱动发来的消息,执行onTransact方法,然后由其参数决定执行服务端的代码。

可以看到onTransact有四个参数

code , data ,replay , flags

code 是一个整形的唯一标识,用于区分执行哪个方法,客户端会传递此参数,告诉服务端执行哪个方法

data客户端传递过来的参数

replay服务器返回回去的值

flags标明是否有返回值,0为有(双向),1为没有(单向)

我们仔细看case TRANSACTION_min中的代码

data.enforceInterface(DESCRIPTOR);与客户端的writeInterfaceToken对用,标识远程服务的名称

int _arg0;

_arg0 = data.readInt();

int _arg1;

_arg1 = data.readInt();

接下来分别读取了客户端传入的两个参数

int _result = this.min(_arg0, _arg1);

reply.writeNoException();

reply.writeInt(_result);

然后执行this.min,即我们实现的min方法;返回result由reply写回。

add同理,可以看到服务端通过AIDL生成Stub的类,封装了服务端本来需要写的代码。

2、客户端

客户端主要通过ServiceConnected与服务端连接

private ServiceConnection mServiceConn = new ServiceConnection()

{

@Override

public void onServiceDisconnected(ComponentName name)

{

Log.e(“client”, “onServiceDisconnected”);

mCalcAidl = null;

}

@Override

public void onServiceConnected(ComponentName name, IBinder service)

{

Log.e(“client”, “onServiceConnected”);

mCalcAidl = ICalcAIDL.Stub.asInterface(service);

}

};

如果你比较敏锐,应该会猜到这个onServiceConnected中的IBinder实例,其实就是我们文章开通所说的Binder驱动,也是一个Binder实例

在ICalcAIDL.Stub.asInterface中最终调用了:

return new com.zhy.calc.aidl.ICalcAIDL.Stub.Proxy(obj);

这个Proxy实例传入了我们的Binder驱动,并且封装了我们调用服务端的代码,文章开头说,客户端会通过Binder驱动的transact()方法调用服务端代码

直接看Proxy中的add方法

@Override public int add(int x, int y) throws android.os.RemoteException

{

android.os.Parcel _data = android.os.Parcel.obtain();

android.os.Parcel _reply = android.os.Parcel.obtain();

int _result;

try {

_data.writeInterfaceToken(DESCRIPTOR);

_data.writeInt(x);

_data.writeInt(y);

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

_reply.readException();

_result = _reply.readInt();

}

finally {

_reply.recycle();

_data.recycle();

}

return _result;

}

首先声明两个Parcel对象,一个用于传递数据,一个用户接收返回的数据

_data.writeInterfaceToken(DESCRIPTOR);与服务器端的enforceInterfac对应

_data.writeInt(x);

_data.writeInt(y);写入需要传递的参数

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

终于看到了我们的transact方法,第一个对应服务端的code,_data,_repay分别对应服务端的data,reply,0表示是双向的

_reply.readException();

_result = _reply.readInt();

最后读出我们服务端返回的数据,然后return。可以看到和服务端的onTransact基本是一行一行对应的。

到此,我们已经通过AIDL生成的代码解释了Android Binder框架的工作原理。Service的作用其实就是为我们创建Binder驱动,即服务端与客户端连接的桥梁。

AIDL其实通过我们写的aidl文件,帮助我们生成了一个接口,一个Stub类用于服务端,一个Proxy类用于客户端调用。那么我们是否可以不通过写AIDL来实现远程的通信呢?下面向大家展示如何完全不依赖AIDL来实现客户端与服务端的通信。

4、不依赖AIDL实现程序间通讯


1、服务端代码

我们新建一个CalcPlusService.java用于实现两个数的乘和除

package com.example.zhy_binder;

import android.app.Service;

import android.content.Intent;

import android.os.Binder;

import android.os.IBinder;

import android.os.Parcel;

import android.os.RemoteException;

import android.util.Log;

public class CalcPlusService extends Service

{

private static final String DESCRIPTOR = “CalcPlusService”;

private static final String TAG = “CalcPlusService”;

public void onCreate()

{

Log.e(TAG, “onCreate”);

}

@Override

public int onStartCommand(Intent intent, int flags, int startId)

{

Log.e(TAG, “onStartCommand”);

return super.onStartCommand(intent, flags, startId);

}

public IBinder onBind(Intent t)

{

Log.e(TAG, “onBind”);

return mBinder;

}

public void onDestroy()

{

Log.e(TAG, “onDestroy”);

super.onDestroy();

}

public boolean onUnbind(Intent intent)

{

Log.e(TAG, “onUnbind”);

return super.onUnbind(intent);

}

public void onRebind(Intent intent)

{

Log.e(TAG, “onRebind”);

super.onRebind(intent);

}

private MyBinder mBinder = new MyBinder();

private class MyBinder extends Binder

{

@Override

protected boolean onTransact(int code, Parcel data, Parcel reply,

int flags) throws RemoteException

{

switch (code)

{

case 0x110:

{

data.enforceInterface(DESCRIPTOR);

int _arg0;

_arg0 = data.readInt();

int _arg1;

_arg1 = data.readInt();

int _result = _arg0 * _arg1;

reply.writeNoException();

reply.writeInt(_result);

return true;

}

case 0x111:

{

data.enforceInterface(DESCRIPTOR);

int _arg0;

_arg0 = data.readInt();

int _arg1;

_arg1 = data.readInt();

int _result = _arg0 / _arg1;

reply.writeNoException();

reply.writeInt(_result);

return true;

}

}

return super.onTransact(code, data, reply, flags);

}

};

}

我们自己实现服务端,所以我们自定义了一个Binder子类,然后复写了其onTransact方法,我们指定服务的标识为CalcPlusService,然后0x110为乘,0x111为除;

记得在AndroidMenifest中注册

服务端代码结束。

2、客户端代码

单独新建了一个项目,代码和上例很类似

首先布局文件:

<LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”

xmlns:tools=“http://schemas.android.com/tools”

android:layout_width=“match_parent”

android:layout_height=“match_parent”

android:orientation=“vertical” >

<Button

android:layout_width=“fill_parent”

android:layout_height=“wrap_content”

android:onClick=“bindService”

android:text=“BindService” />

<Button

android:layout_width=“fill_parent”

android:layout_height=“wrap_content”

android:onClick=“unbindService”

android:text=“UnbindService” />

<Button

android:layout_width=“fill_parent”

android:layout_height=“wrap_content”

android:onClick=“mulInvoked”

android:text=“50*12” />

<Button

android:layout_width=“fill_parent”

android:layout_height=“wrap_content”

android:onClick=“divInvoked”

android:text=“36/12” />

可以看到加入了乘和除

然后是Activity的代码

package com.example.zhy_binder_client03;

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.Toast;

public class MainActivity extends Activity

{

private IBinder mPlusBinder;

private ServiceConnection mServiceConnPlus = new ServiceConnection()

{

@Override

public void onServiceDisconnected(ComponentName name)

{

Log.e(“client”, “mServiceConnPlus onServiceDisconnected”);

}

@Override

public void onServiceConnected(ComponentName name, IBinder service)

{

Log.e(“client”, " mServiceConnPlus onServiceConnected");

mPlusBinder = service;

}

};

@Override

protected void onCreate(Bundle savedInstanceState)

{

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

}

public void bindService(View view)

{

Intent intentPlus = new Intent();

intentPlus.setAction(“com.zhy.aidl.calcplus”);

boolean plus = bindService(intentPlus, mServiceConnPlus,

Context.BIND_AUTO_CREATE);

Log.e(“plus”, plus + “”);

}

public void unbindService(View view)

{

unbindService(mServiceConnPlus);

}

public void mulInvoked(View view)

{

if (mPlusBinder == null)

{

Toast.makeText(this, “未连接服务端或服务端被异常杀死”, Toast.LENGTH_SHORT).show();

} else

{

android.os.Parcel _data = android.os.Parcel.obtain();

android.os.Parcel _reply = android.os.Parcel.obtain();

int _result;

try

{

_data.writeInterfaceToken(“CalcPlusService”);

_data.writeInt(50);

_data.writeInt(12);

mPlusBinder.transact(0x110, _data, _reply, 0);

_reply.readException();

_result = _reply.readInt();

Toast.makeText(this, _result + “”, Toast.LENGTH_SHORT).show();

} catch (RemoteException e)

{

e.printStackTrace();

} finally

{

_reply.recycle();

_data.recycle();

}

}

}

public void divInvoked(View view)

{

if (mPlusBinder == null)

{

Toast.makeText(this, “未连接服务端或服务端被异常杀死”, Toast.LENGTH_SHORT).show();

} else

{

android.os.Parcel _data = android.os.Parcel.obtain();

android.os.Parcel _reply = android.os.Parcel.obtain();

int _result;

try

{

_data.writeInterfaceToken(“CalcPlusService”);

_data.writeInt(36);

_data.writeInt(12);

mPlusBinder.transact(0x111, _data, _reply, 0);

_reply.readException();

_result = _reply.readInt();

Toast.makeText(this, _result + “”, Toast.LENGTH_SHORT).show();

总结:

各行各样都会淘汰一些能力差的,不仅仅是IT这个行业,所以,不要被程序猿是吃青春饭等等这类话题所吓倒,也不要觉得,找到一份工作,就享受安逸的生活,你在安逸的同时,别人正在奋力的向前跑,这样与别人的差距也就会越来越遥远,加油,希望,我们每一个人,成为更好的自己。

  • BAT大厂面试题、独家面试工具包,

  • 资料包括 数据结构、Kotlin、计算机网络、Framework源码、数据结构与算法、小程序、NDK、Flutter

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

xt(this, “未连接服务端或服务端被异常杀死”, Toast.LENGTH_SHORT).show();

} else

{

android.os.Parcel _data = android.os.Parcel.obtain();

android.os.Parcel _reply = android.os.Parcel.obtain();

int _result;

try

{

_data.writeInterfaceToken(“CalcPlusService”);

_data.writeInt(36);

_data.writeInt(12);

mPlusBinder.transact(0x111, _data, _reply, 0);

_reply.readException();

_result = _reply.readInt();

Toast.makeText(this, _result + “”, Toast.LENGTH_SHORT).show();

总结:

各行各样都会淘汰一些能力差的,不仅仅是IT这个行业,所以,不要被程序猿是吃青春饭等等这类话题所吓倒,也不要觉得,找到一份工作,就享受安逸的生活,你在安逸的同时,别人正在奋力的向前跑,这样与别人的差距也就会越来越遥远,加油,希望,我们每一个人,成为更好的自己。

  • BAT大厂面试题、独家面试工具包,

  • 资料包括 数据结构、Kotlin、计算机网络、Framework源码、数据结构与算法、小程序、NDK、Flutter
    [外链图片转存中…(img-qtH2mCjn-1714269076391)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值