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是系统帮我们简化了这一过程而已。