安卓IPC之利用AIDL分析Binder的工作过程

来源:互联网 发布:php 自动化部署 编辑:程序博客网 时间:2024/06/05 05:24

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.aidlpackage com.example.tonyn.aidl;import com.exanple.tonyn.aidl.User2;// Declare any non-default types here with import statementsinterface 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是系统帮我们简化了这一过程而已。

                                             
0 0