Android進階——AIDL詳解之使用遠程服務AIDL實現進程間帶遠程回調接口和自定義Bean的較複雜通信小結(二)

引言

前一篇Android進階——AIDL詳解之使用遠程服務AIDL實現進程間(IPC)簡單通信小結(一)簡單介紹了AIDL的由來和一些基本的語法知識,結合例子相信對於入門AIDL應該沒有什麼問題了,前篇講到AIDL應用主要可以分爲三大場景:普通AIDL、帶有遠程回調接口的AIDL和需要引用自定義Parcelable的AIDL,由於篇幅問題僅僅完成了第一個最簡單的場景,這篇就完成剩下的兩個場景的應用,系列文章鏈接如下:

一、遠程回調AIDL接口的應用

這裏寫圖片描述

1、封裝基本的父類和一些工具類

package com.crazymo.remoteserver.base;

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

/**
 * Auther: Crazy.Mo on 2018/5/7 13:09
 * Summary:所有遠程服務必須繼承的父類
 */
public abstract class AbstractService extends Service {
    protected IBinder mBinder;

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        if(mBinder==null){
            mBinder=initBinder();
        }
        return mBinder;//與客戶端成功連接上的時候返回給客戶端使用的對象
    }

    protected abstract IBinder initBinder();
}

在Android5.1之後,僅僅通過setAction無法匿名啓動服務了而且還會引發異常,需要做處理。

package com.crazymo.client;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.util.Log;
import java.util.List;

/**
 * Auther: Crazy.Mo on 2018/5/3 9:35
 * Summary:這是爲了處理高版本下僅僅通過action 匿名啓動服務引發的異常
 */
public class AIDLUtil {

    /**
     *
     * @param pContext
     * @param pConnection 實現ServiceConnection接口的類
     * @param action 要啓動服務的action
     * @return
     */
    public static boolean bindAIDLService(Context pContext, ServiceConnection pConnection, String action){
        boolean isBind=false;
        if(pContext!=null && action!=null && pConnection!=null) {
            try {
                Intent intent = new Intent(getExplicitIntent(pContext, new Intent().setAction(action)));
                isBind = pContext.bindService(intent, pConnection, Context.BIND_AUTO_CREATE);
            }catch (Exception e){
                Log.e("AIDL",e.getMessage());
            }
        }
        return isBind;
    }

    private static Intent getExplicitIntent(Context context, Intent implicitIntent) {
        // Retrieve all services that can match the given intent
        PackageManager pm = context.getPackageManager();
        List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);
        // Make sure only one match was found
        if (resolveInfo == null || resolveInfo.size() != 1) {
            return null;
        }
        // Get component info and create ComponentName
        ResolveInfo serviceInfo = resolveInfo.get(0);
        String packageName = serviceInfo.serviceInfo.packageName;
        String className = serviceInfo.serviceInfo.name;
        ComponentName component = new ComponentName(packageName, className);
        // Create a new intent. Use the old one for extras and such reuse
        Intent explicitIntent = new Intent(implicitIntent);
        // Set the component to be explicit
        explicitIntent.setComponent(component);
        return explicitIntent;
    }
}

2、 創建服務端的AIDL

和普通Java 回調接口語法不同,需要單獨使用一個AIDL文件來定義回調接口。

2.1、定義回調AIDL接口

// IRemoteCallback.aidl
package com.crazymo.remoteserver.callback;

interface IRemoteCallback {
    void afterSpeak(String msg);
}

2.2、定義業務AIDL接口

和不需要回調的接口的AIDL不同,這裏必須要定義註冊和反註冊回調的方法,同時通過import 引入對應的aidl回調

  • 定義註冊和反註冊回調的方法
  • 引入遠程回調對應的aidl描述文件
// IRemoteCallbackApi.aidl
package com.crazymo.remoteserver;
//引入回調接口
import com.crazymo.remoteserver.callback.IRemoteCallback;

interface IRemoteCallbackApi {
      void speak(String msg);
      void registerListener(IRemoteCallback callBack);//註冊
      void unRegisterListener(IRemoteCallback callBack);//銷燬
}

3、實現服務端對應AIDL的帶有回調功能的Service

觸發回調,需要藉助**RemoteCallbackList(一些接口集合用於執行列表中對象的回調函數,主要用於爲服務端和客戶端通信)**來實現,所謂註冊就是將回調接口添加到RemoteCallbackList裏,銷燬則是從RemoteCallbackList裏移除,至於用於在回調的AIDL裏做什麼不應該由服務提供者來確定,所以無須實現具體業務邏輯。

  • 繼承Service,重寫onBind方法
  • 繼承對應的Stub類,實現AIDL中定義的方法的具體的邏輯
  • onBind方法中返回我們自定義的Stub子類Binder對象
  • 實現AIDL的註冊及反註冊方法
package com.crazymo.remoteserver.service;

import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
import com.crazymo.remoteserver.IRemoteCallbackApi;
import com.crazymo.remoteserver.base.AbstractService;
import com.crazymo.remoteserver.callback.IRemoteCallback;

/**
 * Auther: Crazy.Mo on 2018/5/7 17:27
 * Summary:
 */
public class RemoteCallApiService extends AbstractService {
    private final static String TAG="RemoteService";
    /**
     * 和普通AIDL的Service實現不同,添加的代碼中比較關鍵的有兩點:註冊和反註冊遠程回調接口
     * 即將回調對象元素加入或移 除mRemoteCallbackList這個對象中的一個集合,執行回調,將回調方法放在需要執行的地方
     **/
    private static RemoteCallbackList<IRemoteCallback> mRemoteCallbackList = new RemoteCallbackList<>();

    @Override
    protected IBinder initBinder() {
        if(mBinder==null){
            mBinder=new RemoteCallBinder();
        }
        return mBinder;
    }

    private static final class RemoteCallBinder extends IRemoteCallbackApi.Stub{

        @Override
        public void speak(String msg) throws RemoteException {
            Log.e(TAG,"【服務端】 線程"+Thread.currentThread().getName()+"接到客戶端的字符串:"+msg);
            try {
                Thread.sleep(100);
                int count = 0;
                Log.e(TAG, "mRemoteCallbackList: " +     mRemoteCallbackList+",mRemoteCallbackList.mCallBack:"+mRemoteCallbackList);
                /**
                 *準備開始調用當前註冊的回調,這將創建一個回調列表的副本,你可以從使用getBroadcastItem檢索條目,而且
                 * 一次只能激活一個廣播,所以你必須確保總是從同一個線程調用這個或者是自己做同步。完成後必須調用finishBroadcast。
                 * 返回值:callbacks的大小;如果 mBroadcastCount > 0 說明之前調用過beginBroadcast(),則會拋出異常;
                 **/
                count = mRemoteCallbackList.beginBroadcast();
                if (count == 0) {
                    return;
                }
                try {
                    for (int i = 0; i < count; i++) {
                        Log.e(TAG,"【服務端】 線程"+Thread.currentThread().getName()+"觸發回調方法");
                        mRemoteCallbackList.getBroadcastItem(i).afterSpeak("今天晴,23℃");
                    }
                } catch (RemoteException e) {
                    e.printStackTrace();
                } finally {
                    mRemoteCallbackList.finishBroadcast();//必須執行否則有可能會出現狀態不合法異常,mBroadcastCount <0 說明之前掉用過finishBroadcast() 則會拋出異常。
                }
            } catch (InterruptedException pE) {
                pE.printStackTrace();
            }
        }

        @Override
        public void registerListener(IRemoteCallback callBack) throws RemoteException {
            if (mRemoteCallbackList == null) {
                return;
            }
            mRemoteCallbackList.register(callBack);//註冊回調添加到mRemoteCallbackList 裏
        }

        @Override
        public void unRegisterListener(IRemoteCallback callBack) throws RemoteException {
            if(mRemoteCallbackList!=null) {
                mRemoteCallbackList.unregister(callBack);
            }
        }
    }
}

  • 清單中聲明自定義的服務,指定相應的Action和進程
 <service android:name=".service.RemoteCallApiService"
                 android:process=":remotecall">
            <intent-filter>
                <action android:name="com.crazymo.aidl.callback"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </service>

4、客戶端調用服務端帶有回調接口AIDL

無論是本地服務還是遠程服務,本質上都是Service機制,按照匿名啓動Service的步驟來就可以了,所以AIDL的是使用步驟都是:先連接——>在連接回調裏初始化遠程接口對象——>再通過遠程接口對象調用遠程方法

  • 引入服務端的AIDL即把服務端AIDL全部文件複製到客戶端的aidl目錄下
  • 聲明遠程服務對象實例(類型名爲AIDL的名稱)
  • 實現並創建遠程回調接口對象
  • 聲明ServiceConnection對象實例,最好通過繼承ServiceConnection實現具體的子類的形式(不要通過匿名內部類的形式創建,因爲取消綁定unbindService(ServiceConnection conn)也需要傳入ServiceConnection)
  • 通過上下文的bindService(@RequiresPermission Intent service,@NonNull ServiceConnection conn, @BindServiceFlags int flags)匿名啓動遠程服務
  • ServiceConnection的onServiceConnected完成遠程服務對象實例的初始化及對應遠程回調接口的註冊
  • ServiceConnection的onServiceDisconnected完成遠程服務對象實例的銷燬及對應遠程回調接口的反註冊或者重連
  • 通過遠程服務對象實例調用遠程接口
package com.crazymo.client;

import android.app.Activity;
import android.content.ComponentName;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import com.crazymo.remoteserver.IRemoteApi;
import com.crazymo.remoteserver.IRemoteCallbackApi;
import com.crazymo.remoteserver.IUserMgrApi;
import com.crazymo.remoteserver.bean.User;
import com.crazymo.remoteserver.callback.IRemoteCallback;

import java.util.List;

//爲了節約篇幅,我沒有做重連處理,也省掉了一些邏輯,實際應用中爲了健壯性應該根據具體情況參照前一篇普通AIDL應用的例子進行完善
public class MainActivity extends Activity {
    private final static String TAG="RemoteService";
    private final static String ACTION_CALLBACK="com.crazymo.aidl.callback";
    private boolean isUnbindRemoteApiConn=false;
    private RemoteApiConn mRemoteApiConn=new RemoteApiConn();//只要是進程通信都需要實現,因爲系統是在這個ServiceConnecttion類的相關方法通過回調返回Ibinder對象的
    private RemoteCallbackApiConn mCallbackApiConn=new RemoteCallbackApiConn();

    private IRemoteCallbackApi mRemoteCallbackApi;

	//實現遠程回調接口
    private IRemoteCallback mIRemoteCallback=new IRemoteCallback.Stub() {

        @Override
        public void afterSpeak(String msg) throws RemoteException {
            Log.e(TAG,"【客戶端】線程"+Thread.currentThread().getName()+"遠程回調返回的信息:"+msg);
        }
    };

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

    public void connCallbackAIDL(View view) {
        boolean isBind;
        isBind=AIDLUtil.bindAIDLService(this,mCallbackApiConn,ACTION_CALLBACK);
        Log.e(TAG,"【客戶端】線程"+Thread.currentThread().getName()+"綁定遠程帶有回調的遠程接口(成功true,失敗false):  "+isBind);
    }

    public void testCallbackAIDL(View view) {
        if(mRemoteCallbackApi!=null){
            try {
                Log.e(TAG,"【客戶端】線程"+Thread.currentThread().getName()+"調用帶有回調的遠程接口,發送:今天天氣怎麼樣");
                mRemoteCallbackApi.speak("今天天氣怎麼樣");
            } catch (RemoteException pE) {
                pE.printStackTrace();
            }
        }
    }

    private final class  RemoteCallbackApiConn implements ServiceConnection{

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mRemoteCallbackApi=IRemoteCallbackApi.Stub.asInterface(service);
            try {
                mRemoteCallbackApi.registerListener(mIRemoteCallback);
            } catch (RemoteException pE) {
                pE.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            if(mRemoteCallbackApi!=null){
                try {
                    mRemoteCallbackApi.unRegisterListener(mIRemoteCallback);
                } catch (RemoteException pE) {
                    pE.printStackTrace();
                }
            }
        }
    }
}

這裏寫圖片描述

二、自定義 Parcelable類型AIDL的應用

這裏寫圖片描述
Android 中進程之間的通信最終都是基於Binder機制的,而AIDL是Binder機制中的一部分,進程之間要想通過AIDL 進行數據傳輸,發送進程方就必須先把數據進行序列化操作,Android 中序列化沒有采用Java 傳統的Serializer 方案,而是使用了一種性能更高的獨有方案Parcelable(因爲傳統的Serializer方案的序列化和反序列化操作都是由JVM去完成的,對於開發者來說是黑盒的,在做反序列化時需要去遍歷根據字節等判斷邊界,而Parcelable 則是直接提供序列化和反序列化接口方法,這些接口直接通過JNI 調用本地方法存儲到Parcel中,最終存儲到Native層),總之AIDL傳輸非基本類型及集合類型數據時都需要通過Parcelable類型

1、定義一個用於引入自定義Parcelable 類型的AIDL

爲了引入實現了Parcelable 的JavaBean到AIDL層,得先建立一個和JavaBean 同名的AIDL文件(一對一的關係),如果需要引入多個JavaBean就得建立多個對應的AIDL文件,這些沒有Interface的AIDL文件在編譯時就不會生成對應的Java文件,僅僅是起描述Parcelable 類型的作用(不能定義Interface,有多少個Parcelable類型就需要有多少個這種類型的描述AIDL文件)。而且必須要先建立AIDL文件,再去Java 同一包名下建立同名的JavanBean

// User.aidl
package com.crazymo.remoteserver.bean;

//爲了引入JavaBean到AIDL層,得先建立一個和JavaBean 同名的aidl接口(一對一的關係),如果需要引入多個JavaBean就得建立對應的aidl接口
//這個文件的作用是引入了一個序列化對象 User 供其他的AIDL文件使用
//注意:User.aidl與User.java的包名應當是一樣的
parcelable User;

2、在Java層定義一個實現了Parcelable接口的JavaBean

package com.crazymo.remoteserver.bean;

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

public class User implements Parcelable {
    private String mName;
    private int mAge;

    public User() {
    }

    public void setName(String pName) {
        mName = pName;
    }

    public void setAge(int pAge) {
        mAge = pAge;
    }

    protected User(Parcel in) {
        mName = in.readString();
        mAge = in.readInt();
    }
	/**
	* 這裏new User 對象和下面writeToParcel方法的對象不在同一個進程內,不是同一個對象實例,
	* 這裏是接收進程的User對象實例
	*/
    public static final Creator<User> CREATOR = new Creator<User>() {
        @Override
        public User createFromParcel(Parcel in) {
            return new User(in);
        }

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

    @Override
    public int describeContents() {
        return 0;
    }
	
	/**
	* 序列化時,寫的順序要和下面讀的順序一致
	*/
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(mName);
        dest.writeInt(mAge);
    }

    public void readFromParcel(Parcel dest) {
        mName = dest.readString();
        mAge = dest.readInt();
    }

    @Override
    public String toString() {
        return "姓名:" + mName + "    " + "年齡:" + mAge;
    }
}

3、定義使用自定義Parcelable 類型的AIDL的AIDL

使用自定義類型前得先通過import 手動引入且作爲參數時必須使用修飾符修飾,一般使用in

// IUserMgrApi.aidl
package com.crazymo.remoteserver;
import com.crazymo.remoteserver.bean.User;//引入
// Declare any non-default types here with import statements

interface IUserMgrApi {
    void registUser(inout User user);
    List<User> getUsers();
}

4、使用自定義Parcelable 類型的AIDL的Service實現

  • 繼承Service,重寫onBind方法
  • 繼承對應的Stub類,實現AIDL中定義的方法的具體的邏輯
  • onBind方法中返回我們自定義的Stub子類Binder對象
package com.crazymo.remoteserver.service;

import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

import com.crazymo.remoteserver.IUserMgrApi;
import com.crazymo.remoteserver.base.AbstractService;
import com.crazymo.remoteserver.bean.User;

import java.util.ArrayList;
import java.util.List;

/**
 * Auther: Crazy.Mo on 2018/5/7 15:44
 * Summary:
 */
public class RemoteUserApiService extends AbstractService {
    private final static String TAG="RemoteService";
    private List<User> mUserList;
    @Override
    protected IBinder initBinder() {
        if(mBinder==null){
            mBinder=new RemoteUserServiceBinder();
        }
        return mBinder;
    }

    /**
     * 繼承AIDL文件裏的Stub封裝IBinder對象,可以理解爲AIDL接口的實現
     */
    private final class RemoteUserServiceBinder extends IUserMgrApi.Stub{

        @Override
        public void registUser(User user) throws RemoteException {
            synchronized (this){
                if(mUserList==null){
                    mUserList=new ArrayList<>();
                }
                if(user!=null){
                    mUserList.add(user);
                    Log.e(TAG, "【服務端】 線程"+Thread.currentThread().getName()+"處理客戶端的註冊請求"+user.toString());
                }
            }
        }

        @Override
        public List<User> getUsers() throws RemoteException {
            synchronized (this){
                if(mUserList!=null){
                    Log.e(TAG, "【服務端】 線程"+Thread.currentThread().getName()+"處理客戶端的獲取用戶集請求返回List集合");
                    return mUserList;
                }else{
                    return null;
                }
            }
        }
    }
}

5、在服務端清單文件註冊對應的Service

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.crazymo.remoteserver">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <service android:name=".service.RemoteApiService"
                 android:process=":remote">
            <intent-filter>
                <action android:name="com.crazymo.aidl.comm"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </service>
        <service android:name=".service.RemoteUserApiService"
                 android:process=":remoteuser">
            <intent-filter>
                <action android:name="com.crazymo.aidl.user"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </service>
        <service android:name=".service.RemoteCallApiService"
                 android:process=":remotecall">
            <intent-filter>
                <action android:name="com.crazymo.aidl.callback"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </service>
    </application>
</manifest>

6、客戶端調用服務端自定義Parcelable的AIDL

無論是本地服務還是遠程服務,本質上都是Service機制,按照匿名啓動Service的步驟來就可以了,所以自定義Parcelable的AIDL的是使用步驟都是:先連接——>在連接回調裏初始化遠程接口對象——>再通過遠程接口對象調用遠程方法

  • 引入服務端的AIDL即把服務端AIDL全部文件複製到客戶端的aidl目錄下
  • 引入服務端定義的Parcelable,把服務端定義的實現了Parcelable接口的JavaBean 複製到客戶端相同的路徑下
  • 聲明遠程服務對象實例(類型名爲AIDL的名稱)
  • 聲明ServiceConnection對象實例,最好通過繼承ServiceConnection實現具體的子類的形式(不要通過匿名內部類的形式創建,因爲取消綁定unbindService(ServiceConnection conn)也需要傳入ServiceConnection)
  • 通過上下文的bindService(@RequiresPermission Intent service,@NonNull ServiceConnection conn, @BindServiceFlags int flags)匿名啓動遠程服務
  • ServiceConnection的onServiceConnected完成遠程服務對象實例的初始化
  • ServiceConnection的onServiceDisconnected完成遠程服務對象實例的銷燬或者重連
  • 通過遠程服務對象實例調用遠程接口
//這裏把三種場景使用的完整代碼全都貼在一起,做個對比
package com.crazymo.client;

import android.app.Activity;
import android.content.ComponentName;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import com.crazymo.remoteserver.IRemoteApi;
import com.crazymo.remoteserver.IRemoteCallbackApi;
import com.crazymo.remoteserver.IUserMgrApi;
import com.crazymo.remoteserver.bean.User;
import com.crazymo.remoteserver.callback.IRemoteCallback;

import java.util.List;

public class MainActivity extends Activity {
    private final static String TAG="RemoteService";
    private final static String ACTION_COMM="com.crazymo.aidl.comm";
    private final static String ACTION_PARCE="com.crazymo.aidl.user";
    private final static String ACTION_CALLBACK="com.crazymo.aidl.callback";
    private boolean isUnbindRemoteApiConn=false;

    private RemoteApiConn mRemoteApiConn=new RemoteApiConn();//只要是進程通信都需要實現,因爲系統是在這個ServiceConnecttion類的相關方法通過回調返回Ibinder對象的
    private RemoteUserApiConn mRemoteUserApiConn=new RemoteUserApiConn();
    private RemoteCallbackApiConn mCallbackApiConn=new RemoteCallbackApiConn();

    private IRemoteApi mRemoteApi;
    private IUserMgrApi mUserMgrApi;
    private IRemoteCallbackApi mRemoteCallbackApi;

    private IRemoteCallback mIRemoteCallback=new IRemoteCallback.Stub() {

        @Override
        public void afterSpeak(String msg) throws RemoteException {
            Log.e(TAG,"【客戶端】線程"+Thread.currentThread().getName()+"遠程回調返回的信息:"+msg);
        }
    };

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

    @Override
    protected void onDestroy() {
        super.onDestroy();
        disconnectRemoteApiComm();
    }

    private void disconnectRemoteApiComm() {
        unbindService(mRemoteApiConn);
        isUnbindRemoteApiConn=true;
    }

    public void connAIDL(View view) {
        connectAIDL();
    }

    private void connectAIDL() {
        boolean isBind;
        isBind= AIDLUtil.bindAIDLService(this,mRemoteApiConn,ACTION_COMM);
        Log.e(TAG,"【客戶端】線程"+Thread.currentThread().getName()+"綁定遠程接口(成功true,失敗false):  "+isBind);
        if(!isBind){
            Toast.makeText(this,"連接遠程服務發生異常",Toast.LENGTH_SHORT).show();
        }
    }

    public void useAIDL(View view) {
        if(mRemoteApi==null){
            connectAIDL();
        }
        try {
            if(mRemoteApi!=null) {//這裏有必要
                Log.e(TAG, "【客戶端】線程"+Thread.currentThread().getName()+"通過AIDL調用遠程接口,客戶端:小雞燉蘑菇");
                String result=mRemoteApi.getRemoteStr();
                Log.e(TAG, "【客戶端】線程"+Thread.currentThread().getName()+"服務端響應請求返回:" +result );
            }
        } catch (RemoteException pE) {
            pE.printStackTrace();
        }
    }

    public void connUserAIDL(View view) {
        boolean isBind;
        isBind= AIDLUtil.bindAIDLService(this,mRemoteUserApiConn,ACTION_PARCE);
        Log.e(TAG,"【客戶端】線程"+Thread.currentThread().getName()+"綁定遠程用戶管理接口(成功true,失敗false):  "+isBind);
        if(!isBind){
            Toast.makeText(this,"連接遠程用戶管理服務發生異常",Toast.LENGTH_SHORT).show();
        }
    }

    public void testUserAIDL(View view) {
        try {
            User user=new User();
            user.setName("CrazyMo");
            user.setAge(1);
            Log.e(TAG, "【客戶端】線程"+Thread.currentThread().getName()+"通過AIDL調用遠程用戶管理接口註冊用戶:"+user.toString());
            mUserMgrApi.registUser(user);
            Log.e(TAG, "【客戶端】線程"+Thread.currentThread().getName()+"通過AIDL調用遠程用戶管理接口查詢用戶集");
            List<User> list=mUserMgrApi.getUsers();
            if(list!=null){
                for (User tmp :list){
                    Log.e(TAG, "【客戶端】線程"+Thread.currentThread().getName()+"遠程用戶管理接口返回的用戶數據:"+tmp.toString());
                }
            }
        } catch (RemoteException pE) {
            pE.printStackTrace();
        }
    }

    public void connCallbackAIDL(View view) {
        boolean isBind;
        isBind=AIDLUtil.bindAIDLService(this,mCallbackApiConn,ACTION_CALLBACK);
        Log.e(TAG,"【客戶端】線程"+Thread.currentThread().getName()+"綁定遠程帶有回調的遠程接口(成功true,失敗false):  "+isBind);
    }

    public void testCallbackAIDL(View view) {
        if(mRemoteCallbackApi!=null){
            try {
                Log.e(TAG,"【客戶端】線程"+Thread.currentThread().getName()+"調用帶有回調的遠程接口,發送:今天天氣怎麼樣");
                mRemoteCallbackApi.speak("今天天氣怎麼樣");
            } catch (RemoteException pE) {
                pE.printStackTrace();
            }
        }
    }

    private final class RemoteApiConn implements ServiceConnection{

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mRemoteApi=IRemoteApi.Stub.asInterface(service);//初始化遠端服務對象實例
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            if(isUnbindRemoteApiConn){
                mRemoteApi=null;//主動斷開直接置爲null
            }else{
                while(mRemoteApi==null) {
                    connectAIDL();//做重連
                }
            }
        }
    }

    private final class RemoteUserApiConn implements ServiceConnection{

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mUserMgrApi= IUserMgrApi.Stub.asInterface(service);
        }

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

    private final class  RemoteCallbackApiConn implements ServiceConnection{

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mRemoteCallbackApi=IRemoteCallbackApi.Stub.asInterface(service);
            try {
                mRemoteCallbackApi.registerListener(mIRemoteCallback);
            } catch (RemoteException pE) {
                pE.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            if(mRemoteCallbackApi!=null){
                try {
                    mRemoteCallbackApi.unRegisterListener(mIRemoteCallback);
                } catch (RemoteException pE) {
                    pE.printStackTrace();
                }
            }
        }
    }
}


這裏寫圖片描述

小結

AIDL的使用到此基本結束了,要記住AIDL本質上也是一種特殊的Service,所以使用它的基本流程和使用本地服務基本一致先連接——>在連接回調裏初始化遠程接口對象——>再通過遠程接口對象調用遠程方法源碼傳送門

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