Android 跨進程通信: AIDL

概念

在Android上一個進程通常是無法訪問另一個進程的內存,而AIDL翻譯過來就是Android 接口定義語言,你可以你利用它定義客戶端和服務端進程間通信相互認可的編程接口。

適用範圍

只有允許不同應用的客戶端用IPC方式訪問服務,並且想要在服務中處理多線程問題時,纔有必要適用AIDL。如果想執行IPC,但不需要處理多線程,可以考慮使用Messenger類實現。如果不需要執行跨不同應用的併發IPC,可以通過實現一個Binder來實現。

使用

考慮這樣一個場景:client 客戶端跨進程訪問 server 客戶端,流程如下

這裏寫圖片描述

server 客戶端接受請求通知,訪問遠程服務器返回數據,並將數據通知給client 客戶端 , 下面我們來看看如何一步步實現該功能

創建server 客戶端

1、創建.aidl文件

新建一個名爲server的工程,這裏面我們創建三個aidl文件
IRomteService.aidl 定義請求接口
ICallBack.aidl 自定義回調接口
MyBean.aidl 自定義的對象
MyBean.java 自定義對象類(必須實現Parcelable接口)

注意:MyBean.aidl和MyBean.java要保證在同一個包路徑下

// IRomteService.aidl
package com.aidlserver.myaidl;
import com.aidlserver.myaidl.MyBean;
import com.aidlserver.myaidl.ICallBack;
// Declare any non-default types here with import statements

interface IRemoteService {
    void requestServer(in MyBean param, in ICallBack callback);
}
// MyBean.aidl
package com.aidlserver.myaidl;
import com.aidlserver.myaidl.MyBean;
// Declare any non-default types here with import statements

parcelable MyBean;
// ICallBack.aidl
package com.aidlserver.myaidl;
// Declare any non-default types here with import statements

interface ICallBack {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void onResult(String result);
}
package com.aidlserver.myaidl;

import android.os.Parcel;
import android.os.Parcelable;


public class MyBean implements Parcelable {

    public int mId;
    public String mName;


    public MyBean() {
    }

    protected MyBean(Parcel in) {
        readFromParcel(in);
    }

    public static final Creator<MyBean> CREATOR = new Creator<MyBean>() {
        @Override
        public MyBean createFromParcel(Parcel in) {
            return new MyBean(in);
        }

        @Override
        public MyBean[] newArray(int size) {
            return new MyBean[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(mId);
        dest.writeString(mName);
    }

    public void readFromParcel(Parcel in) {
        mId = in.readInt();
        mName = in.readString();
    }

    @Override
    public String toString() {
        return "id:" + mId + " name:" + mName;
    }
}

2、創建Service(關鍵部分)

創建一個RemoteService服務,它是連接外部應用和遠程服務器的樞紐,代碼如下

package com.aidlserver.service;

import android.app.Service;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;

import com.aidlserver.myaidl.ICallBack;
import com.aidlserver.myaidl.IRemoteService.Stub;
import com.aidlserver.myaidl.MyBean;

public class RemoteService extends Service {

    private static final String TAG = RemoteService.class.getSimpleName();
    private static final String CLIENT_PACKAGE_NAME = "com.client";
    private static final String MYSELF_PACKAGE_NAME = "com.aidlserver";

    private Object mLock = new Object();

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, TAG + "onCreate");
    }

    private Stub mBinder = new Stub() {
        @Override
        public void requestServer(MyBean param, final ICallBack callback) throws RemoteException {

            synchronized (mLock) {
                //模擬耗時請求開啓線程請求服務端
                Log.d(TAG, "requestServer param:" + param.toString());

                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        if (callback != null) {
                            try {
                                callback.onResult("I was server response . hello client");
                            } catch (RemoteException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }).start();
            }
        }

        //該方法可以對外部應用做認證
        @Override
        public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {

            String[] stringArray = RemoteService.this.getPackageManager().getPackagesForUid(getCallingUid());
            String packageName = null;
            if (stringArray != null && stringArray.length > 0) {
                packageName = stringArray[0];
            }

            boolean result = false;

            //只有"com.client"應用及其自己能訪問
            if (!CLIENT_PACKAGE_NAME.equals(packageName)
                    && !MYSELF_PACKAGE_NAME.equals(packageName)) {
                result = false;
            } else {
                result = super.onTransact(code, data, reply, flags);
            }
            Log.d(TAG, "packageName:" + packageName + " result:" + result);

            return result;
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "=====onBind=====");
        //返回自定義的IRemoteService的存根(Sub)
        return mBinder;
    }

    @Override
    public void unbindService(ServiceConnection conn) {
        super.unbindService(conn);
        Log.d(TAG, "=====unbindService=====");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "=====onDestroy=====");
    }
}

3、註冊Service

在清單文件加入如下代碼

<application>
  <service
    android:name=".service.RemoteService"
    android:enabled="true"
    android:exported="true"
    android:process=":remote"
     ></service>
</application>

4、編寫客戶端調用代碼

新建一個Activity,
完整Activity代碼如下,

package com.aidlserver;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import com.aidlserver.myaidl.ICallBack;
import com.aidlserver.myaidl.IRemoteService;
import com.aidlserver.myaidl.MyBean;
import com.aidlserver.service.RemoteService;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = MainActivity.class.getSimpleName();

    private Button mBtnBindService;
    private Button mBtnUnbindService;
    private TextView mTextView;

    private ServiceConnection mServiceConnection;

    private boolean mIsBinded = false;

    private IRemoteService mRomteService;

    /**
     * 請求回調存根
     */
    private ICallBack.Stub mCallBack = new ICallBack.Stub() {

        //回調通知結果
        @Override
        public void onResult(final String result) throws RemoteException {

            Log.d(TAG, "client onResult function run in " + Process.myPid() + " Process" + "thread:" + Thread.currentThread().getName());

            mTextView.post(new Runnable() {
                @Override
                public void run() {
                    mTextView.setText(result);
                }
            });
        }
    };

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

        //綁定服務按鈕
        mBtnBindService = (Button) findViewById(R.id.btn_bind_service);
        mBtnBindService.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                //綁定服務
                Intent intent = new Intent(MainActivity.this, RemoteService.class);
                bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);

                //5.0以後必須都是顯式綁定,隱式綁定會報錯,如果是外部應用調用可以使用下面註釋代碼,綁定服務
                /**
                 Intent intent = new Intent();
                 intent.setComponent(new ComponentName("com.aidlserver", "com.aidlserver.service.RemoteService"));
                 bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
                 */
            }
        });


        //解綁服務按鈕
        mBtnUnbindService = (Button) findViewById(R.id.btn_unbind);
        mBtnUnbindService.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mIsBinded && mRomteService != null) {
                    //解綁服務
                    unbindService(mServiceConnection);
                    mIsBinded = false;
                    mTextView.setText("unbinded");
                }
            }
        });


        mTextView = (TextView) findViewById(R.id.tv_content);


        mServiceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, final IBinder service) {
                Log.d(TAG, "service connected");

                mTextView.setText("onServiceConnected");

                mIsBinded = true;

                mRomteService = IRemoteService.Stub.asInterface(service);

                MyBean param = new MyBean();
                param.mId = 123;
                param.mName = "I was android client";

                try {
                    //開始請求
                    mRomteService.requestServer(param, mCallBack);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                mIsBinded = false;
                mRomteService = null;
                mTextView.setText("unBind");
                Log.d(TAG, "onServiceDisconnected");
            }
        };
    }
}


現在我們運行server app 效果如下:

這裏寫圖片描述

創建Client 客戶端

client客戶端代碼就比較簡單了,
我們只要把server工程裏面
ICallBack.aidl
IRemoteService.aidl
MyBean.aidl
MyBean.java移到和server同一個包路徑就可以了,
client客戶端調用的activity和server客戶端的activity幾乎一樣

源碼下載:
AIDL Demo

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章