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文件,我们在这里可以定义想要的抽象方法
这里的这个定向Tag就是表示跨进程通讯的时候数据的流向,in表示数据只能从客户端流向服务器端,out同理,inout则是可以双向流通。// 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); }
接下来系统会给我们自动生成一个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是系统帮我们简化了这一过程而已。