码迷,mamicode.com
首页 > 移动开发 > 详细

初识Android进程间通信之----Binder机制

时间:2016-05-18 19:26:12      阅读:313      评论:0      收藏:0      [点我收藏+]

标签:

【转载请注明出处:http://blog.csdn.net/feiduclear_up/article/details/51405967 CSDN废墟的树】

前言

前面两篇博客分别介绍了Android进程间通信之AIDL的使用,以及使用AIDL传递复杂对象以及Bitmap对象。所谓AIDL:Android Interface Definition Language,是一种Android接口定义语言,用于编写Android进程间通信代码。也就是说AIDL只是一个实现进程间通信的一个工具,真正实现Android进程间通信机制的其实是幕后“主谋”Binder机制。所有有关AIDL实现进程间通信都是依赖于Android的Binder机制,那么这个Binder机制到底是个什么东西呢?在这篇博客中我只能说初始Binder机制,在下实在不敢造次,因为Android的Binder机制实在挺复杂的,要想深入了解Binder机制的实现原理的朋友们请绕道而走,出门左拐去看老罗的博客,我可不敢误人子弟啊。

简介

我们从整个抽象层面来理解Android的进程间通信Binder机制,在进程间通信有三个角色分别为:客户端(Client),Binder驱动,服务端(Service)。客户端通过Binder驱动向服务端发送请求,服务端又将请求的结果通过Binder驱动回送给客户端,这就是一个简单的进程间通信的描述。由此可以看到Binder驱动是两者的中间桥梁,是客户端和服务端的信使。

  • 服务端:定义了一个Binder对象,并且重写其中的onTransact方法来处理来自客户端的请求,该Binder对象会注册到系统中,以便客户端绑定使用。
  • Binder驱动:设备文件驱动,提供相应的接口给客户端和服务端,以便两者通信。
  • 客户端:通过绑定指定的服务来获取服务端的Binder对象,然后调用IBinder接口类中的transact方法进行远程调用。

IBinder是一个接口对象,其定义了一系列方法,其中一个方法transact至关重要,客户端就通过该方法来进行远程通信。而Binder对象继承IBinder接口,并实现了里面的方法。现在我们来看看Binder对象中的transact方法的实现:

/**
     * Default implementation rewinds the parcels and calls onTransact.  On
     * the remote side, transact calls into the binder to do the IPC.
     */
    public final boolean transact(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException {
        if (false) Log.v("Binder", "Transact: " + code + " to " + this);
        if (data != null) {
            data.setDataPosition(0);
        }
        boolean r = onTransact(code, data, reply, flags);
        if (reply != null) {
            reply.setDataPosition(0);
        }
        return r;
    }

分析:该方法有四个参数,分别是 code,data,reply,flag。

  • code:整形的一个识别码,客户端传入,用于区分服务端执行哪个方法。
  • data:客户端传入的Parcel类型的参数。
  • reply:服务端返回的结果值,也是Parcel类型数据。
  • flag:整形的一个标记,用于标记是否是否有返回值,0表示有返回值,1表示没有。

一般客户端需要调用transact方法来将客户端相应的参数传递给服务端。

我们知道transact方法是Binder类提供给客户端来实现远程调用的一个方法,而该方法中真正做事情的代码却是调用了Binder类中的onTransact方法。而该方法正是服务端用来实现客户端请求的方法,那么我们来看看onTransact方法:

 /**
     * Default implementation is a stub that returns false.  You will want
     * to override this to do the appropriate unmarshalling of transactions.
     *
     * <p>If you want to call this, call transact().
     */
    protected boolean onTransact(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException {
        if (code == INTERFACE_TRANSACTION) {
            reply.writeString(getInterfaceDescriptor());
            return true;
        } else if (code == DUMP_TRANSACTION) {
            ParcelFileDescriptor fd = data.readFileDescriptor();
            String[] args = data.readStringArray();
            if (fd != null) {
                try {
                    dump(fd.getFileDescriptor(), args);
                } finally {
                    try {
                        fd.close();
                    } catch (IOException e) {
                        // swallowed, not propagated back to the caller
                    }
                }
            }
            // Write the StrictMode header.
            if (reply != null) {
                reply.writeNoException();
            } else {
                StrictMode.clearGatheredViolations();
            }
            return true;
        }
        return false;
    }

以上是onTransact方法的默认实现,其参数和transact方法一一对应。
一般服务端需要重写该方法来实现客户端的请求。

对AIDL文件的理解

通过上一小节我们知道,基于Binder机制的Android进程间通信的一个简易过程就是:服务端定义个Binder对象,并且重写其中的onTransact方法来实现客户端的请求,然后通过Service将该Binder注册到系统中。客户端通过绑定指定的服务来获得远程的IBinder对象,之后调用其中的transact方法来进行远程调用。

而标准的AIDL编程其实很简单,无须开发者自己主动去调用transact和实现onTransact方法,只需实现相应的一些功能即可。由此也方便了开发者,尽量避免让开发者去关注Binder工作的一个过程,而仅仅在意其功能就好了。不过我们本着学习的心态了解一点进程间通信的原理还是有帮助的。接下来我们简单分析一下一个aidl文件:

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: G:\\WorkPlace\\ServiceAidl\\app\\src\\main\\aidl\\com\\xjp\\serviceaidl\\IMyAidlInterface.aidl
 */
package com.xjp.serviceaidl;
// Declare any non-default types here with import statements

public interface IMyAidlInterface extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.xjp.serviceaidl.IMyAidlInterface {
        private static final java.lang.String DESCRIPTOR = "com.xjp.serviceaidl.IMyAidlInterface";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.xjp.serviceaidl.IMyAidlInterface interface,
         * generating a proxy if needed.
         */
        public static com.xjp.serviceaidl.IMyAidlInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.xjp.serviceaidl.IMyAidlInterface))) {
                return ((com.xjp.serviceaidl.IMyAidlInterface) iin);
            }
            return new com.xjp.serviceaidl.IMyAidlInterface.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 {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_basicTypes: {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    long _arg1;
                    _arg1 = data.readLong();
                    boolean _arg2;
                    _arg2 = (0 != data.readInt());
                    float _arg3;
                    _arg3 = data.readFloat();
                    double _arg4;
                    _arg4 = data.readDouble();
                    java.lang.String _arg5;
                    _arg5 = data.readString();
                    int _result = this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.xjp.serviceaidl.IMyAidlInterface {
            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;
            }

            /**
             * Demonstrates some basic types that you can use as parameters
             * and return values in AIDL.
             */
            @Override
            public int basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                int _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(anInt);
                    _data.writeLong(aLong);
                    _data.writeInt(((aBoolean) ? (1) : (0)));
                    _data.writeFloat(aFloat);
                    _data.writeDouble(aDouble);
                    _data.writeString(aString);
                    mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }

    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    public int basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
}

分析:

其中抽象类Stub是继承自Binder对象,且继承了开发者自己定义的接口IMyAidlInterface类,且该Stub类中实现了onTransact方法,也就是Stub类帮服务端实现了远程调用方法,所以服务端只需要实现IMyAidlInterface类中的接口方法即可。

其中代理类Proxy也是继承自Binder对象,且继承了开发者自己定义的接口IMyAidlInterface类,并且Proxy类实现了IMyAidlInterface类中的接口方法,在该方法中调用了Binder类的transact方法,也就是Proxy代理类帮客户端调用了transact方法,所以客户端直接调用IMyAidlInterface接口类中的方法。

由此我们知道客户端通过Binder调用IMyAidlInterface接口类中的方法,恰好服务端正好通过Binder来实现了IMyAidlInterface接口类中的方法,从而实现了客户端远程调用了服务端的接口方法来完成一次进程间的通信。

由此也看出上面的AIDL文件其实客户端只是使用了其中的Proxy类代码,服务端只是使用了其中的Stub类代码,但是在客户端和服务端都会生成相同的AIDL文件。那么我们可不可以再客户端把Stub类中代码去掉,然后再服务端把Proxy类代码去掉呢?答案是可以得。由于AIDL编程是idea自动生成的,它区分不了哪个是客户端,哪个是服务端,故而自动生成的AIDL文件即包括了客户端的代码,也包括了服务端的代码,看着有点冗余。既然我们知道了Android进程间通信Binder机制,那么接下来不使用AIDL编程语言来实现进程间通信试试!!!!

不使用AIDL实现进程间通信

其实上面几个小节已经讲解的很清楚了,只要服务端定义一个Binder对象并且实现其中的onTransact方法,客户端获取一个远程服务的IBinder对象并且调用其中的transact方法即可实现不实用AIDL实现进程间的通信。

示例代码

服务端

package com.xjp.serviceaidl;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.support.annotation.Nullable;

/**
 * Created by xjp on 2016/5/14.
 */
public class CustomService extends Service {
    private static final String DESCRIPTOR = "com.xjp.serviceaidl.service";
    private static final int TRANSACTION_add = 0;

    private Binder mIBinder = new Binder(){
        @Override
        protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
            switch (code){
                case TRANSACTION_add://方法标识码
                    data.enforceInterface(DESCRIPTOR);//读取系列化令牌,和客户端的data.writeInterfaceToken对应
                    int arg0 = data.readInt();//读取参数
                    int arg1 = data.readInt();//读取参数
                    int result = this.add(arg0,arg1);//本地方法
                    reply.writeNoException();
                    reply.writeInt(result);//回写结果值
                    return true;
            }
            return super.onTransact(code, data, reply, flags);
        }
        //远程调用的方法
        public int add(int arg0, int arg1){
            return arg0 + arg1;
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mIBinder;
    }
}

分析:服务端只是定义了Binder对象并且重写了onTransact方法,很简单。记得在AndroidManifest.xml配置该服务。

客户端代码

package com.xjp.clientbinder;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    private static final String SERVICE_ACTION = "com.xjp.serviceaidl_service";
    private static final String SERVICE_PKG_NAME = "com.xjp.serviceaidl";
    private static final String DESCRIPTOR = "com.xjp.serviceaidl.service";
    private static final int TRANSACTION_add = 0;
    private IBinder mBinder;

    private TextView txtResult;

    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            if (service != null) {
                mBinder = service;
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            serviceConnection = null;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        txtResult = (TextView) findViewById(R.id.result);
    }

    public void bindService(View v) {
        Intent intent = new Intent(SERVICE_ACTION);
        intent.setPackage(SERVICE_PKG_NAME);
        bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
    }

    public void unbindService(View v) {
        unbindService(serviceConnection);
    }

    public void remoteAdd(View v) throws RemoteException {
        int result = remoteAdd(4, 9);
        txtResult.setText("远程调用结果是:" + result);
    }

    private int remoteAdd(int arg0, int arg1) throws RemoteException {
        if (mBinder == null) {
            Toast.makeText(this, "请先绑定服务!", Toast.LENGTH_SHORT).show();
            return -1;
        }
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        int result;
        try {
            data.writeInterfaceToken(DESCRIPTOR);//往序列化中写一个令牌,和服务端的data.enforceInterface对应,相当于接头暗号。
            data.writeInt(arg0);//往序列化参数中写值
            data.writeInt(arg1);//往序列化参数中写值,值得注意是此处的写顺序必须和服务端的读顺序一致。
            mBinder.transact(TRANSACTION_add, data, reply, 0);//远程调用方法
            reply.readException();
            result = reply.readInt();//读取远程调用的结果值
        } finally {
            data.recycle();
            reply.recycle();
        }
        return result;
    }

}

分析:
技术分享

  1. 值得注意的是data.writeInterfaceToken()方法的参数和服务端data.enforceInterface()方法的参数值必须保持一致,这相当于客户端和服务端的接头暗号。
  2. 客户端的data.write***()系列方法必须和服务端的data.read***()系列方法顺序保持一致,上一篇博客中有讲到。
  3. 客户端和服务端的方法区分码code的值也必须保持一一对应。

运行结果:

总结

如果你对Android进程间通信Binder是一个小白,请自觉使用AIDL语言来编写进程间通信,那么即保证不出错也简单。如果你觉得利用AIDL编程闲代码有点多余,那么你可以自己实现Binder类相应的方法来实现进程间通信。总之通过这几篇博客,总算对Android进程间通信之AIDL编程和Binder机制有个初步的了解。

初识Android进程间通信之----Binder机制

标签:

原文地址:http://blog.csdn.net/feiduclear_up/article/details/51405967

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!