天天看點

初識Android程序間通信之----Binder機制前言簡介對AIDL檔案的了解不使用AIDL實作程序間通信總結

【轉載請注明出處: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();
        }
        boolean r = onTransact(code, data, reply, flags);
        if (reply != null) {
            reply.setDataPosition();
        }
        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 = ( != 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) ? () : ()));
                    _data.writeFloat(aFloat);
                    _data.writeDouble(aDouble);
                    _data.writeString(aString);
                    mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, );
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

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

    /**
     * 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 = ;

    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 = ;
    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(, );
        txtResult.setText("遠端調用結果是:" + result);
    }

    private int remoteAdd(int arg0, int arg1) throws RemoteException {
        if (mBinder == null) {
            Toast.makeText(this, "請先綁定服務!", Toast.LENGTH_SHORT).show();
            return -;
        }
        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, );//遠端調用方法
            reply.readException();
            result = reply.readInt();//讀取遠端調用的結果值
        } finally {
            data.recycle();
            reply.recycle();
        }
        return result;
    }

}
           

分析:

初識Android程式間通信之----Binder機制前言簡介對AIDL檔案的了解不使用AIDL實作程式間通信總結
  1. 值得注意的是data.writeInterfaceToken()方法的參數和服務端data.enforceInterface()方法的參數值必須保持一緻,這相當于用戶端和服務端的接頭暗号。
  2. 用戶端的data.write***()系列方法必須和服務端的data.read***()系列方法順序保持一緻,上一篇部落格中有講到。
  3. 用戶端和服務端的方法區分碼code的值也必須保持一一對應。

運作結果:

總結

如果你對Android程序間通信Binder是一個小白,請自覺使用AIDL語言來編寫程序間通信,那麼即保證不出錯也簡單。如果你覺得利用AIDL程式設計閑代碼有點多餘,那麼你可以自己實作Binder類相應的方法來實作程序間通信。總之通過這幾篇部落格,總算對Android程序間通信之AIDL程式設計和Binder機制有個初步的了解。

繼續閱讀