Android中的IPC方式——Binder(二)

栏目: Android · 发布时间: 6年前

内容简介:在开发过程中,实现进程间通信用的最多的就是 AIDL。AIDL(Android Interface Definition Language),也就是接口定义语言,提供接口给远程调用者。

Android中的IPC方式——Binder(二)

在开发过程中,实现进程间通信用的最多的就是 AIDL。

AIDL(Android Interface Definition Language),也就是接口定义语言,提供接口给远程调用者。

当我们定义好 AIDL 文件,在编译时编译器会帮我们生成代码实现 IPC 通信。为了可以更好的理解Binder的过程,从AIDL入手。

服务端

先创建一个服务端,创建一个 IStudentManager.aidl 文件,声明2个方法 getStudentList() 以及 addStudent() IStudentManager.aidl

// IStudentManager.aidl
package com.golden.aidlserver;
import com.golden.aidlserver.Student;
// Declare any non-default types here with import statements

interface IStudentManager {
    List<Student> getStudentList();
    void addStudent(in Student student);
}
复制代码

IStudentManager.javaBuild一下工程,android studio会自动为我们生成一个 java 类:IStudentManager.java,大体结构如下。

package com.golden.aidlserver;
// Declare any non-default types here with import statements

public interface IStudentManager extends android.os.IInterface {
    public static abstract class Stub extends android.os.Binder 
            implements com.golden.aidlserver.IStudentManager {
        private static final java.lang.String DESCRIPTOR = "com.golden.aidlserver.IStudentManager";

        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }
        public static com.golden.aidlserver.IStudentManager asInterface(android.os.IBinder obj) {...}

        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {...}

        private static class Proxy implements com.golden.aidlserver.IStudentManager {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }
            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            @Override
            public java.util.List<com.golden.aidlserver.Student> getStudentList() throws android.os.RemoteException {...}

            @Override
            public void addStudent(com.golden.aidlserver.Student student) throws android.os.RemoteException {...}

        static final int TRANSACTION_getStudentList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_addStudent = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    public java.util.List<com.golden.aidlserver.Student> getStudentList() throws android.os.RemoteException;

    public void addStudent(com.golden.aidlserver.Student student) throws android.os.RemoteException;
}

复制代码

该类首先包含了一个抽象内部类:Stub, 该类继承自Binder并实现了IStudentManager接口。在Stub的内部,又包含了一个静态内部类:Proxy,Proxy类同样实现了IStudentManager接口。

Sercvice

接下来创建一个StudentManagerService,需要实现刚刚我们定义的两个方法,并且在AndroidManifest注册。

public class StudentManagerService extends Service {
    private CopyOnWriteArrayList<Student>  mStudentList = new CopyOnWriteArrayList<>();
    @Override
    public void onCreate() {
        super.onCreate();
        mStudentList.add(1,"张三");
    }

    @Override
    public IBinder onBind(Intent intent) {
        return new MyBinder();
    }

    class MyBinder extends IStudentManager.Stub {
        @Override
        public List<Student> getStudentList() throws RemoteException {
            return mStudentList;
        }

        @Override
        public void addStudent(Student student) throws RemoteException {
            mStudentList.add(student);
        }
    }
}
复制代码
<service android:name=".StudentManagerService"
            android:process=":remote">
      <intent-filter>
          <!--是客户端用于访问AIDL服务的ID  -->
          <action android:name="com.golden.server.StudentManagerService" />
      </intent-filter>
</service>
复制代码

客户端

为了逻辑上区分清晰,另外重新创建一个客户端的应用。 需要将服务端的aidl以及Student.java拷贝到客户端注意与服务端的包名保持一致

Android中的IPC方式——Binder(二)
public class ClientMainActivity extends AppCompatActivity {
    private static final String TAG = "AIDL Client";
    private IStudentManager mRemoteStudentManager;

    private int size = 1;
    private Button mAddBtn;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mAddBtn = findViewById(R.id.mButton);

        Intent intent = new Intent();
        intent.setComponent(new ComponentName("com.golden.aidlserver", "com.golden.aidlserver.StudentManagerService"));
        bindService(intent, mConnection, BIND_AUTO_CREATE);
        initView();
    }

    private void initView() {
        mAddBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    if (mRemoteStudentManager != null) {
                        mRemoteStudentManager.addStudent(new Student(size+1,"李四"+String.valueOf(size+1)));
                        List<Student> studentList = mRemoteStudentManager.getStudentList();
                        size = studentList.size();
                        Log.e(TAG,studentList.toString());
                    }
                } catch (RemoteException e) {

                    e.printStackTrace();
                }
            }
        });
    }

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.e(TAG, "onServiceConnected");
            //获得IStudentManager对象
            mRemoteStudentManager = IStudentManager.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.e(TAG, "onServiceDisconnected");
            mRemoteStudentManager = null;
        }
    };
}

复制代码

client中去 bindserviceonServiceConnected() 中得到服务端获得IStudentManager对象 mRemoteStudentManager ,可以通过 mRemoteStudentManager 来调用服务端service的方法。实现点击button之后服务端添加一个学生然后 getStudentList() 打印出来。

Android中的IPC方式——Binder(二)

Stub

public static abstract class Stub extends android.os.Binder implements com.golden.aidlserver.IStudentManager {
    private static final java.lang.String DESCRIPTOR = "com.golden.aidlserver.IStudentManager";
    /**
     * Construct the stub at attach it to the interface.
     */
    public Stub() {
        this.attachInterface(this, DESCRIPTOR);
    }
    /**
     * Cast an IBinder object into an com.golden.aidlserver.IStudentManager interface,
     * generating a proxy if needed.
     */
    public static com.golden.aidlserver.IStudentManager asInterface(android.os.IBinder obj) {
        if ((obj == null)) {
            return null;
        }
        android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        if (((iin != null) && (iin instanceof com.golden.aidlserver.IStudentManager))) {
            return ((com.golden.aidlserver.IStudentManager) iin);
        }
        return new com.golden.aidlserver.IStudentManager.Stub.Proxy(obj);
    }

    @Override
    public android.os.IBinder asBinder() {
        return this;
    }

    @Override
    public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
        java.lang.String descriptor = DESCRIPTOR;
        switch (code) {
            case INTERFACE_TRANSACTION: {
                reply.writeString(descriptor);
                return true;
            }
            case TRANSACTION_getStudentList: {
                data.enforceInterface(descriptor);
                java.util.List<com.golden.aidlserver.Student> _result = this.getStudentList();
                reply.writeNoException();
                reply.writeTypedList(_result);
                return true;
            }
            case TRANSACTION_addStudent: {
                data.enforceInterface(descriptor);
                com.golden.aidlserver.Student _arg0;
                if ((0 != data.readInt())) {
                    _arg0 = com.golden.aidlserver.Student.CREATOR.createFromParcel(data);
                } else {
                    _arg0 = null;
                }
                this.addStudent(_arg0);
                reply.writeNoException();
                return true;
            }
            default: {
                return super.onTransact(code, data, reply, flags);
            }
        }
    static final int TRANSACTION_getStudentList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_addStudent = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
复制代码
Android中的IPC方式——Binder(二)

其中有一个DESCRIPTOR,它在Stub初始化的时候会绑定这个标识符,就是前面曾经提到的安全性方面。

client与server拥有一样的IStudentManager.java文件,在client端我们可以看到

//client端
private ServiceConnection mConnection = new ServiceConnection() {
	@Override
	public void onServiceConnected(ComponentName name, IBinder service) {
		Log.e(TAG, "onServiceConnected");
		//获得IStudentManager对象
		mRemoteStudentManager = IStudentManager.Stub.asInterface(service);
	}

	@Override
	public void onServiceDisconnected(ComponentName name) {
		Log.e(TAG, "onServiceDisconnected");
		mRemoteStudentManager = null;
	}
};

复制代码

我们在 client端中bindservice中在onServiceConnected()会传给我们一个IBinder,这个 IBinder类型的参数是Binder驱动传给我们的,后面在framework的会说到。方法中会去调用 binder.queryLocalInterface() 去查找 Binder 本地对象,如果找到了就说明 Client 和 Server 在同一进程,那么这个 binder 本身就是 Binder 本地对象,可以直接使用。否则说明是 IBinder是个远程对象,也就是 BinderProxy。因此需要我们创建一个代理对象 Proxy,通过这个代理对象来是实现远程访问。

server端中我们只是创建了stub的对象并且实现了其中定义的两个方法,等待调用 onTransact() 方法。

//server端    
    class MyBinder extends IStudentManager.Stub {
            @Override
            public List<Student> getStudentList() throws RemoteException {
                return mStudentList;
            }

            @Override
            public void addStudent(Student student) throws RemoteException {
                mStudentList.add(student);
            }
    }
复制代码

Proxy

在client中当client 和server处于不同的进程的情况下,client使用的是Proxy对象mRemote。

private static class Proxy implements com.golden.aidlserver.IStudentManager {
    private android.os.IBinder mRemote;

    Proxy(android.os.IBinder remote) {
        mRemote = remote;
    }

    @Override
    public android.os.IBinder asBinder() {
        return mRemote;
    }

    public java.lang.String getInterfaceDescriptor() {
        return DESCRIPTOR;
    }

    @Override
    public java.util.List<com.golden.aidlserver.Student> getStudentList() throws android.os.RemoteException {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        java.util.List<com.golden.aidlserver.Student> _result;
        try {
            _data.writeInterfaceToken(DESCRIPTOR);
            mRemote.transact(Stub.TRANSACTION_getStudentList, _data, _reply, 0);
            _reply.readException();
            _result = _reply.createTypedArrayList(com.golden.aidlserver.Student.CREATOR);
        } finally {
            _reply.recycle();
            _data.recycle();
        }
        return _result;
    }

    @Override
    public void addStudent(com.golden.aidlserver.Student student) throws android.os.RemoteException {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
            _data.writeInterfaceToken(DESCRIPTOR);
            if ((student != null)) {
                _data.writeInt(1);
                student.writeToParcel(_data, 0);
            } else {
                _data.writeInt(0);
            }
            mRemote.transact(Stub.TRANSACTION_addStudent, _data, _reply, 0);
            _reply.readException();
        } finally {
            _reply.recycle();
            _data.recycle();
        }
    }
}
复制代码

单独看下 client调用 getStudentList() 过程就是 proxy中调用。数据包_data需要将标识符DESCRIPTOR写入,远程调用 mRemote.transact(Stub.TRANSACTION_getStudentList, _data, _reply, 0); (因性能考虑需要将方法以int值来标识),然后server端会调用到stub中的 ontranscat() ,最后client中的proxy得到结果。

@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, 
 int flags) throws android.os.RemoteException {
	java.lang.String descriptor = DESCRIPTOR;
	switch (code) {
        case INTERFACE_TRANSACTION: {
            reply.writeString(descriptor);
            return true;
        }
        case TRANSACTION_getStudentList: {
            data.enforceInterface(descriptor);
            java.util.List<com.golden.aidlserver.Student> _result = this.getStudentList();
            reply.writeNoException();
            reply.writeTypedList(_result);
            return true;
        }
        case TRANSACTION_addStudent: {
            data.enforceInterface(descriptor);
            com.golden.aidlserver.Student _arg0;
            if ((0 != data.readInt())) {
            _arg0 = com.golden.aidlserver.Student.CREATOR.createFromParcel(data);
            } else {
            _arg0 = null;
            }
            this.addStudent(_arg0);
            reply.writeNoException();
            return true;
        }
        default: {
            return super.onTransact(code, data, reply, flags);
        }
	}
}
复制代码

将_result 写入 _replay中返回给客户端。如果是在同一个进程中,就将直接调用stub中的 getStudent() 方法不会走 transact()onTransact() 逻辑。

Android中的IPC方式——Binder(二)

以上就是一个简单的进程通信的流程,在不同进程的前提下,在理解的时候可以把client的所有操作都是通过proxy来实现,server端就是stub的具体实现。

下一篇会讲framework层源码。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

High Performance Python

High Performance Python

Micha Gorelick、Ian Ozsvald / O'Reilly Media / 2014-9-10 / USD 39.99

If you're an experienced Python programmer, High Performance Python will guide you through the various routes of code optimization. You'll learn how to use smarter algorithms and leverage peripheral t......一起来看看 《High Performance Python》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

URL 编码/解码
URL 编码/解码

URL 编码/解码

MD5 加密
MD5 加密

MD5 加密工具