您的位置:首页 > 移动开发 > Android开发

android之Service之Binder学习

2015-09-14 23:51 866 查看
一、Binder框架

Binder用于完成进程间通信(IPC),比如普通应用程可以调用音乐服务,它工作在内核态,属于一个驱动,只是这个驱动要用的“硬件”是内存。

Binder架构由三个模块构成:服务端接口,Binder驱动,客户端接口。我们分开来看:

服务端

一个Binber服务端实际上是一个Binder类的对象,且一旦创建,内部就启动一个隐藏线程,用来接收Binder驱动发送的消息,收到消息后,会执行Binder对象中的onTransact()方法,并执照不同的参数执行不同的服务代码,所以实现一个Binder对象一定要重载onTransact()方法,onTransact()方法的参数来自于客户端调用transact()方法时的输入,两者的格式和顺序一定要一样。

Binder驱动

当服务端Binder对象创建时,同时会在Binder驱动中创建一个mRemote对象(这里不会再有新的线程),mRemote也是Binder类。当客户端访问远程服务时,是通过 mRemote对象 。

客户端

要访问服务端,先得到远程对象对应的mRemote对象,然后就可以调用其transact()方法,而在Binder驱动中,mRemote对象也重载了transact()方法,重载的内容重要是:

1.以线程间通信的模式,向服务端发送客户端传递过来的参数。

2.挂起当前线程(客户端线程),并等待服务端后的通知(notify)。

3.接收到服务端线程的能通知,然后继续执行客户端线程。



二、Server端的设计

只要基于Binder类新建一个Server类就可以了,如下

public class MusicPlayerService extends Binder {
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
return super.onTransact(code, data, reply, flags);
}

public void start(String filePath){

}
public void stop (){

}
}


要启动服务,只要初始化一个MusicPlayerService就可,如要Activity中初始化,会在ddms中看到多了一个线程。

  定义了服务类后,就要重载onTransact()方法,并从data中读取出客户端发送来的参数,比如start()中的filePath,这里有一个服务端与客户端约定的问题。

  假如客户端传入的第一个参数是filePath,则

@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
//code表示想调用服务端的那个方法
switch (code) {
case 1000:
//一种校验
data.enforceInterface("MusicPlayerService");
String filePath = data.readString();
start(filePath);
break;
}
return super.onTransact(code, data, reply, flags);
}


如果IPC调用希望返回一 些结果,可以在返回的paracel reply中写入结果。

三、Binder客户端设计

要想使用服务端,先要得到服务端在Binder驱动中对应的mRemote引用(怎么得到见后面),然后再调用mRemote变量的transact()方法。原型如下

public final boolean transact(int code, Parcel data, Parcel reply, int flags)


调用这个方法后,客户端线程进入Binder驱动,Binder驱动会挂起客户端线程,并向远程服务发送一个消息,消息中有客户端传递的参数,服务端执行完后,向Binder驱动发送一个notify消息,从而使客户端线程从Binder驱动代码区返回到客户端代码区。最后客户端就可以从reply中解析返回的数据了。

四、使用Service类

手写Binder服务端和客户端的过程有两个问题:

第一,客户端如何得到服务端的Binder引用(使用Service)

第二,客户端与服务端一定要事先约好两件事情(用aidl工具)

(1)服务端函数的参数的顺序。

(2)服务端不同的函数的int标识。

使用Binder是想提一个全局的服务,也就是在系统中的任何地方都可以访问。我们还有一个更傻瓜的方法:Service .

如何得到Binder对象

对于高手 ,完全可以不使用Service类,而只要使用Binder编写服务程序,但只是一部分。因为可以基于Binder类扩展系统服务,而客户端服务则一定要基于Service.这里的系统服务是指可以使用getSystemService()得到的服务,客户端服务是指app自定义的服务。

AmS提供了startService()来启动客户服务,对于客户端,可以使用下面的两个方法来与一个服务建立连接:

public Component startService(Intent intent )

这里只是启动了intent指定的服务,还没有得到服务端的Binder引用,所以还不能调用服务端的功能。

public boolean bindService (Intent service, ServiceConnection conn, int flags)

可以用于绑定一个服务,这是第一个问题的关键所在,里面的第二个参数是一个interface.

public interface ServiceConnection {
/**
* Called when a connection to the Service has been established, with
* the {@link android.os.IBinder} of the communication channel to the
* Service.
*
* @param name The concrete component name of the service that has
* been connected.
*
* @param service The IBinder of the Service's communication channel,
* which you can now make calls on.
*/
public void onServiceConnected(ComponentName name, IBinder service);

public void onServiceDisconnected(ComponentName name);
}


在客户端中,可以在onServiceConnected()方法中将参数service保存为一个全局变量,从而在客户端的任何地方都可以调用远程服务。  

五、例子

工程目录如



先创建Fruit类

/**
* Created by lsj on 2015/9/15.
*/
public class Fruit implements Parcelable{
private String name ;
private String color ;
private int number ;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getColor() {
return color;
}

public void setColor(String color) {
this.color = color;
}

public int getNumber() {
return number;
}

public void setNumber(int number) {
this.number = number;
}

public static final Creator<Fruit> CREATOR = new Creator<Fruit>() {
@Override
public Fruit createFromParcel(Parcel source) {
Fruit fruit = new Fruit();
fruit.name = source.readString() ;
fruit.color = source.readString();
fruit.number= source.readInt() ;
return fruit;
}

@Override
public Fruit[] newArray(int size) {
return new Fruit[size];
}
};

@Override
public int describeContents() {
return 0;
}

@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeString(color);
dest.writeInt(number);
}
}


Fruit.aidl如下


 

IFruitBinder.aidl如下



在点击build-->make project后,会在如下的目录中生成代码 IFuitBinder.java



分析如下



Service类如下

public class FruitAidlService extends Service {
private Fruit mFruit ;

@Override
public void onCreate() {
super.onCreate();
mFruit = new Fruit();
mFruit.setName("apple");
mFruit.setColor("red");
mFruit.setNumber(10);
}

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

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}

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

/**
* 分析IFruitBinder.java文件中的:
* (1)Stub :基于Binder类,实现了AIDL接口,主要同服务端来使用,定义为一个abstract类,因为具体的服务
* 函数由程序员实现,所以aidl文件中定义的接口在stub类中并没有实现,同时Stub类中重载了onTransact
* 方法,由于transact()方法内部给包裹写入参数的顺序是由aidl工具定义的,所以在onTransact()方法中,
* AIDL工具自然知道按什么顺序从包裹中取出数据。
* (2)Proxy类:客户端访问服务端的代理,所谓的代理主要 就是为了前面所提到的第二个重要的问题:统一包裹
* 内写入数据的顺序。
*
* 这是是Server端,要实现真正的功能。
*/
private IFruitBinder.Stub serviceBinder = new IFruitBinder.Stub() {
@Override
public String getInfo() throws RemoteException {
return "I am a server";
}

@Override
public Fruit getFruit() throws RemoteException {
return mFruit;
}
} ;
}


定义一个测试类Activity

/**
* Created by lsj on 2015/9/16.
*/
public class FruitAidlActivity extends BaseActivity {
private Button btn ;
private IFruitBinder iFruitBinder;

private ServiceConnection sc = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iFruitBinder = IFruitBinder.Stub.asInterface(service);

}

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

@Override
protected void findView() {
setContentView(R.layout.activity_aidl);
}

@Override
protected void initView() {
btn = (Button)findViewById(R.id.aidl_button);
// Intent serviceIntent = new Intent(this, FruitAidlService.class);
//bindService(serviceIntent, sc,BIND_AUTO_CREATE);
}

@Override
protected void setOnClickListener() {
btn.setOnClickListener(this);
}

@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.aidl_button:
if (iFruitBinder == null){
Log.d(TAG, "ibinder == null");
}else {
try{
String str = "getFruit:"+ iFruitBinder.getFruit().getName()+
iFruitBinder.getFruit().getColor()+ iFruitBinder.getFruit().getNumber();
Log.d(TAG, str);
}catch (RemoteException e ){
e.printStackTrace();
}
}
break;
default:
break;
}
}

@Override
protected void onStart() {
super.onStart();
Intent serviceIntent = new Intent(this, FruitAidlService.class);
bindService(serviceIntent, sc, BIND_AUTO_CREATE);
}

@Override
protected void onStop() {
super.onStop();
unbindService(sc);
}
}


备注

在aidl中使用自定义的类后,要修改app下的build.gradle文件,在android添加如下

//加了后消除aidl中自定义类时找不到的问题
//参考 http://blog.csdn.net/wuyuxing24/article/details/46948961 //http://stackoverflow.com/questions/16596352/android-studio-cant-find-an-aidl-interface-for-use-in-class
//里面有google的文档
sourceSets {
main {
//Manifest.srcFile 'src/main/AndroidManifest.xml'
java.srcDirs = ['src/main/java', 'src/main/aidl']
resources.srcDirs = ['src/main/java', 'src/main/aidl']
aidl.srcDirs = ['src/main/aidl']
res.srcDirs = ['src/main/res']
assets.srcDirs = ['src/main/assets']
}
}


为什么android选用Binder来实现进程间通信?

一、可靠性。在移动设备上,通常采用基于Client-Server的通信方式来实现互联网与设备间的内部通信。目前linux支持IPC包括传统的管道,System V IPC,即消息队列/共享内存/信号量,以及socket中只有socket支持Client-Server的通信方式。Android系统为开发者提供了丰富进程间通信的功能接口,媒体播放,传感器,无线传输。这些功能都由不同的server来管理。开发都只关心将自己应用程序的client与server的通信建立起来便可以使用这个服务。毫无疑问,如若在底层架设一套协议来实现Client-Server通信,增加了系统的复杂性。在资源有限的手机 上来实现这种复杂的环境,可靠性难以保证。

二、传输性能。socket主要用于跨网络的进程间通信和本机上进程间的通信,但传输效率低,开销大。消息队列和管道采用存储-转发方式,即数据先从发送方缓存区拷贝到内核开辟的一块缓存区中,然后从内核缓存区拷贝到接收方缓存区,其过程至少有两次拷贝。虽然共享内存无需拷贝,但控制复杂。比较各种IPC方式的数据拷贝次数。共享内存:0次。Binder:1次。Socket/管道/消息队列:2次。

三、安全性。Android是一个开放式的平台,所以确保应用程序安全是很重要的。Android对每一个安装应用都分配了UID/PID,其中进程的UID是可用来鉴别进程身份。传统的只能由用户在数据包里填写UID/PID,这样不可靠,容易被恶意程序利用。而我们要求由内核来添加可靠的UID。

所以,出于可靠性、传输性、安全性。android建立了一套新的进程间通信方式。

 

相关参考

binder机制的简要理解
http://www.linuxidc.com/Linux/2012-07/66195.htm
binder机制的设计分析(1)
http://www.linuxidc.com/Linux/2015-01/111148.htm
binder机制的设计分析(2)
http://www.linuxidc.com/Linux/2012-07/66196p2.htm
binder的讲解(综合)
http://www.cnblogs.com/angeldevil/p/3296381.html
aidl中出现自定义类时
http://www.cnblogs.com/xilinch/archive/2012/07/16/2593236.html
  

  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: