Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 從AIDL看Android跨進程通信

從AIDL看Android跨進程通信

編輯:關於Android編程

  AIDL是Android實現IPC的一種重要的方式,理解它的原理對理解Android進程間通信有很大的幫助。AIDL的定義,已經有很多介紹的文章了,這裡就不做詳解了。我們直接從實例入手來分析AIDL實現原理。
  AIDL的使用
  首先需要定義AIDL接口IMyService.aidl:
  

// IMyService.aidl
package com.chuck.aidldemo;

// Declare any non-default types here with import statements

interface IMyService {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
     String getValue();
}

  定義了getValue()方法,返回String。我們知道定義好aidl文件後,IDE在編譯項目時會自動幫我們生成一個文件IMyService.java文件,稍後會詳細介紹。
  接下來需要定義一個service,這裡定義MyService.java
  

public class MyService extends Service {
    @Override
    public void onCreate() {
        super.onCreate();
        Log.e("myService","onCreate"    );
    }

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

    IMyService.Stub ims=new IMyService.Stub() {
        @Override
        public String getValue() throws RemoteException {
            return   "hello AIDL";
        }
    };

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e("myService","onDestroy");
    }
}

  重寫onBind方法,返回IMyService.Stub對象ims,IMyService.Stub是定義在IMyService.java中,其實現了IBinder,所以這裡可以在OnBinder中作為返回對象。同時在AIDL中定義getValue方法的真正實現,就是在這裡。我們僅僅是返回一個”hello AIDL”字符串。
  為了實現跨進程,我們還需要在AndroidMenifast.xml文件中設置process=”:Remote”,這樣service就和client不在同一個進程中了:
  

  最後在定義client,為了方便我們就直接在MainActivity.java實現了:
  

public class MainActivity extends AppCompatActivity {
    private IMyService ims;
    private String text;

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

    public void onClick(View view) {
      if (view.getId() == R.id.btn_bind) {
            Intent intent = new Intent(this, MyService.class);
            bindService(intent, sc, BIND_AUTO_CREATE);
        }else if (view.getId()==R.id.btn_unbind){
            unbindService(sc);
        }
    }

    private ServiceConnection sc = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            ims = IMyService.Stub.asInterface(service);
            try {
                text = ims.getValue();
                Log.e("text",text);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };
}

  這和平時bindService使用是一樣的,需要一個ServiceConnection實例,將該實例作為bindService參數傳入。這樣client就可以啟動並綁定MyService了。
  一般我們使用AIDL的基本步驟就是這些,現在我們需要著重分析自動生成的IMyService.java了,如果不知道文件位置,直接在IDE搜索一下就可以了。先把代碼貼出來:
  

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: /Users/chenkai/Android/codeExcise/aidl/AidlDemo/app/src/main/aidl/com/chuck/aidldemo/IMyService.aidl
 */
package com.chuck.aidldemo;
// Declare any non-default types here with import statements

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

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

        /**
         * Cast an IBinder object into an com.chuck.aidldemo.IMyService interface,
         * generating a proxy if needed.
         */
        public static com.chuck.aidldemo.IMyService asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.chuck.aidldemo.IMyService))) {
                return ((com.chuck.aidldemo.IMyService) iin);
            }
            return new com.chuck.aidldemo.IMyService.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_getValue: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _result = this.getValue();
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.chuck.aidldemo.IMyService {
            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 java.lang.String getValue() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.lang.String _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getValue, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readString();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

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

    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    public java.lang.String getValue() throws android.os.RemoteException;
}

  代碼結構很清晰,先來看一下類圖:
  
  
  1.IInterface,我們定義的Binder接口都需要繼承自它。
  2.我們定義的IMyService接口繼承自接口IInterface。
  3.IBinder 遠程對象的接口。
  4.Binder實現了IBinder,Android通過Binder進行IPC
  5.BinderProxy是在定義在Binder.java中的,也是實現了IBinder接口,這裡只要知道它是Binder的代理就可以了。我們Client中拿到的就是這個類的對象,之後會有講。
  6.IMyService.Stub繼承自Binder並且實現了IMyService。也就是說Stub其實是個Binder。還記得嗎?在前文MyService中我們生成了一個Stub的實例ims,Stub的本意是存根,這裡的用意就是Service的存根,通過它,我們在Service端就擁有了一個Binder。這樣我們就可以通過底層的Binder驅動進行跨進程通信了。
  7.IMyService.Proxy,顧名思義它是一個代理,主要是實現了IMyService接口,客戶端通過它發起遠程請求。
  簡單的介紹了涉及到的類,下面來分析一下請求的流程。
  我們知道,在client bindService(這是一個比較復雜的過程,涉及到AMS,本身也是一個跨進程的通信)之後,如果遠程服務,這裡是MyService,處理完請求之後,通過一系列復雜的操作,client中的onServiceConnected方法將會回調,為了方便我把前邊相關的部分代碼放在這:
  

private ServiceConnection sc = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            ims = IMyService.Stub.asInterface(service);
            try {
                text = ims.getValue();
                Log.e("text",text);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

  第3行當回調onServiceConnected時,會傳過來一個IBinder對象service,client就是需要通過它來發起遠程請求的,那麼service的具體實現到底是Binder還是BinderProxy呢,如果對Binder的機制比較清楚的都會知道其實是BinderProxy的,為了驗證這個我們可以調試一下:
  這裡寫圖片描述

  我們得到BinderProxy後會將其傳給IMyService.Stub.asInterface(service),asInterface接受一個IBinder對象作為參數,這裡就是剛才說的BinderProxy對象。具體看看asInterface做了什麼:
  

 public static com.chuck.aidldemo.IMyService asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.chuck.aidldemo.IMyService))) {
                return ((com.chuck.aidldemo.IMyService) iin);
            }
            return new com.chuck.aidldemo.IMyService.Stub.Proxy(obj);
        }

  看第5行,因為我們設置了MyService的process屬性,也就是說他和client是不在同一個進程的,所以obj.queryLocalInterface(DESCRIPTOR)這個方法返回為空,也就是iin為null,那麼將會執行第9行代碼,對沒錯new了一個IMyService.Proxy對象。回到前邊onServiceConnected方法中第4行ims就是剛剛生成的IMyService.Proxy對象。第6行ims.getValue()方法執行的就是IMyService.Proxy中的getValue方法:
  

/**
             * Demonstrates some basic types that you can use as parameters
             * and return values in AIDL.
             */
            @Override
            public java.lang.String getValue() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.lang.String _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getValue, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readString();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

  第7.8行_data, _reply分別是進行遠程調用時的的請求和相應參數。第11行將DESCRIPTOR = “com.chuck.aidldemo.IMyService”作為Token寫入 _data.第12行mRemote.transact(Stub.TRANSACTION_getValue, _data, _reply, 0);mRemote是在IMyService.Proxy構造方法中被賦值的,也就是我們在new IMyService.Proxy對象時傳進來的BinderProxy對象。所以mRemote.transact(Stub.TRANSACTION_getValue, _data, _reply, 0)的實現應該是在BinderProxy中,這裡需要注意一下第一個參數Stub.TRANSACTION_getValue,他是標識需要調用方法的code,在後邊會有用到。我們看看他的源碼:
  

 public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
        return transactNative(code, data, reply, flags);
    }

  最終會調用transactNative()方法,這是一個native方法,真正的實現是用c實現,他是實現在Android源碼對應/frameworks/base/core/jni/android_util_Binder.cpp中的static jboolean android_os_BinderProxy_transact函數,將java層的transact轉換成c層transact,有興趣的可以去研究一下。
  通過底層Binder的一系列操作,最終會回調到遠程Stub的onTransact方法,至於怎麼調用的,需要去了解Binder機制。這裡只要知道會回調onTransact()方法就好了。不過還是要注意的是,這裡的Stub是在遠程服務端的,他和client不是在同一個進程中的。他是在遠程服務的Binder線程池中。跟進去看onTransact()方法做了什麼處理:
  

 @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_getValue: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _result = this.getValue();
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

  還記得在transact方法時提到的第一個參數Stub.TRANSACTION_getValue嗎,它就是這裡onTransact的第一個參數code。所以會執行8-13行代碼,第10行java.lang.String _result = this.getValue();很關鍵這個this是Stub,還記得我們在哪兒有生成它的實例嗎?沒錯就是在MyService中,我們把它作為onBind方法的返回參數。所以這裡的this.getValue就是MyService中我們寫的那個getValue方法,會返回一個”hello AIDL”的字符串。並把它賦值給_result。在第12行將其寫入_reply中。最後在通過Binder的一系列操作,我們將在client中得到這個字符串。
  總的來說AIDL本身使用比較簡單,理解起來也比較簡單,但是其內部的Binder機制還是比較復雜的,我們先理解Java層的AIDL對學習Binder也是有幫助的。並且Messager通信是基於AIDL的,理解了AIDL也就理解了Messager。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved