Binder是安卓中的一个类,实现了IBinder接口,是安卓中的一种跨进程通信手段。
在安卓开发中,Binder主要应用于服务中,包括AIDL和Messenger,而Messenger的底层实现也是AIDL,所以,这里就借用AIDL来分析一下Binder的工作过程。
AIDL是一种语言。
下面上例子。
首先还是需要一个实现了Parcelable接口的类,User2类
public class User2 implements Parcelable {
int Id;
String name;
public User2(int id,String name){
this.Id = id;
this.name = name;
}
private User2(Parcel in) {
Id = in.readInt();
name = in.readString();
}
public static final Creator<User> CREATOR = new Creator<User>() {
@Override
public User2 createFromParcel(Parcel in) {
return new User2(in);
}
@Override
public User2[] newArray(int size) {
return new User2[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(Id);
dest.writeString(name);
}
}
接下来,由于User这个类不是AIDL中默认支持的数据类型,所以需要单独用一个AIDL文件中声明此类(这里有个坑,两个文件名要一样)
package com.example.tonyn.aidl;
parcelable User2;
然后,还需要写一个IUserManager的AIDL文件,这个是真正要用的AIDL文件,我们在这里可以定义想要的抽象方法
// IUser2Manager.aidl
package com.example.tonyn.aidl;
import com.exanple.tonyn.aidl.User2;
// Declare any non-default types here with import statements
interface IUser2Manager {
//返回值前不加任何东西
List<Book> getUsers();
//若参数不是Java基本类型或者String的话,需要加定向Tag
void addUser(in User2 user2);
}
这里的这个定向Tag就是表示跨进程通讯的时候数据的流向,in表示数据只能从客户端流向服务器端,out同理,inout则是可以双向流通。 接下来系统会给我们自动生成一个IUser2Manager这个Java类,这个类就不在这里展示,把里面比较重要的地方简述一下,这个类里面主要有一个stub抽象类,它继承了Binder,实现了IUser2Manager这个接口,同时,内部还有一个Proxy这个代理类,从stub里面的代码我们可以看出来,当跨进程通信时,会调用Proxy这个代理类。否则,就会直接调用stub这个类。
系统生成了这个java类之后,我们就可以写客户端和服务端的代码了,接下来上服务端
package com.example.tonyn.aidlandparcelable;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
import static android.content.ContentValues.TAG;
public class MyService extends Service {
private List<User2> list = new ArrayList<>();
private final IUser2Manager.Stub manager = new IUser2Manager.Stub() {
@Override
public List<User2> getUsers() throws RemoteException {
synchronized (this) {
Log.e(TAG, "getUsers: "+list.toString());
if (list != null) {
return list;
}
return new ArrayList<>();
}
}
@Override
public void addUser(User2 user) throws RemoteException {
synchronized (this) {
if (list == null) {
list = new ArrayList<>();
}
if (user == null) {
Log.e(TAG, "NO PERSON");
user = new User2(585,"tony");
}
//尝试修改user的参数,主要是为了观察其到客户端的反馈
if (!list.contains(user)) {
list.add(user);
}
//打印mBooks列表,观察客户端传过来的值
Log.e(TAG, "list is : " + list.toString());
}
}
};
public MyService() {
}
@Override
public void onCreate() {
super.onCreate();
//先传进去一个
User2 user = new User2(654,"nkpdqz");
list.add(user);
}
@Override
public IBinder onBind(Intent intent) {
return manager;
}
}
可以看出来,服务端写起来并没有什么太大不同。我们实现的这个抽象类,其实也是binder的一个子类,可以当作onBind函数的返回值,所以我们可以在IUser2Manager.Stub这个类的实例里面重写两个抽象方法。
下面上客户端代码:
package com.example.tonyn.aidlandparcelable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private IUser2Manager manager = null;
private boolean mBound = false;
private List<User2> list;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void xuLieHua(){
User user = new User(1,"nkpdqz");
try {
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("123.txt"));
outputStream.writeObject(user);
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public void fanXuLieHua() {
try {
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("123.txt"));
User user = (User) inputStream.readObject();
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public void add(View view){
if (!mBound) {
attemptToBindService();
Toast.makeText(this, "当前与服务端处于未连接状态,正在尝试重连,请稍后再试", Toast.LENGTH_SHORT).show();
return;
}
if (manager == null)
return;
User2 user2 = new User2(525,"Messi");
try {
manager.addUser(user2);
Log.e(getLocalClassName(), list.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void look(View view){
if (!mBound) {
attemptToBindService();
Toast.makeText(this, "当前与服务端处于未连接状态,正在尝试重连,请稍后再试", Toast.LENGTH_SHORT).show();
return;
}
if (manager == null)
return;
Log.d(getLocalClassName(), list.toString());
}
public void con(View view){
attemptToBindService();
}
private void attemptToBindService() {
Intent intent = new Intent(MainActivity.this,MyService.class);
//intent.setAction("com.example.tontn.aidl");
//intent.setPackage("com.example.tontn.aidlandparcelable");
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
if (mBound) {
unbindService(connection);
mBound = false;
}
}
ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e(getLocalClassName(), "service connected");
manager = IUser2Manager.Stub.asInterface(service);
mBound = true;
if (manager != null) {
try {
list = manager.getUsers();
Log.e(getLocalClassName(), list.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e(getLocalClassName(), "service disconnected");
mBound = false;
}
};
}
客户端的代码依然时bindService这个方法,给里面传三个参数而已。和以前写的有一点不同,就是在重写onServiceConnected这个方法的时候,需要调用到asInterface这个方法,这个方法也是在系统自动生成的那个Stub类里面。最后,可以把不同的事件写在不同的点击事件里面,这样,我们就可以很容易地让客户端与远程服务端通信了。
最后,最重要的一点:在这个全部步骤里面,这个AIDL文件时必需的吗?
答案为不是必需的,如果足够强大,也可以自己实现这个Java类,写AIDL是系统帮我们简化了这一过程而已。