Android跨程式元件IPCInvoker用法完全解析

AlbieLiang發表於2018-01-02

接入IPCInvoker

引入元件庫

IPCInvoker元件庫已經提交到jcenter上了,可以直接dependencies中配置引用

dependencies {
    compile 'cc.suitalk.android:ipc-invoker:1.1.7'
}
複製程式碼

定義遠端程式Service

這裡以PushProcessIPCService為示例,程式碼如下:

public class PushProcessIPCService extends BaseIPCService {

    public static final String PROCESS_NAME = "cc.suitalk.ipcinvoker.sample:push";

    @Override
    public String getProcessName() {
        return PROCESS_NAME;
    }
}

複製程式碼

在manifest.xml中配置service

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="cc.suitalk.ipcinvoker.sample">

    <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.PushProcessIPCService"
            android:process=":push"
            android:exported="false"/>
    </application>
</manifest>

複製程式碼

在專案的Application中setup IPCInvoker

這裡需要在你的專案所有需要支援跨程式呼叫的程式中呼叫IPCInvoker.setup(Application, IPCInvokerInitDelegate)方法,並在傳入的IPCInvokerInitDelegate介面實現中,將該程式需要支援訪問的遠端程式相應的Service的class新增到IPCInvoker當中,示例如下:

public class IPCInvokerApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        // Initialize IPCInvoker
        IPCInvokerBoot.setup(this, new DefaultInitDelegate() {
            @Override
            public void onAttachServiceInfo(IPCInvokerInitializer initializer) {
                initializer.addIPCService(PushProcessIPCService.PROCESS_NAME, PushProcessIPCService.class);
            }

            @Override
            public void onAddTypeTransfer(TypeTransferInitializer initializer) {
                super.onAddTypeTransfer(initializer);
                initializer.addTypeTransfer(new TestTypeTransfer());
            }
        });
    }
}
複製程式碼

到此為止IPCInvoker引入已經完成,可以直接在專案中使用IPCInvoker的跨程式呼叫介面了.

用IPCInvoker寫呼叫跨程式呼叫

在使用IPCInvoker編寫跨程式呼叫邏輯前,我們先了解一下IPCInvoker中有哪些介面.

IPCInvoker介面

同步呼叫介面

@WorkerThread
public static <T extends IPCSyncInvokeTask> Bundle invokeSync(@NonNull String process, Bundle data, @NonNull Class<T> taskClass)

複製程式碼
@WorkerThread
public static <T extends IPCRemoteSyncInvoke<InputType, ResultType>, InputType extends Parcelable, ResultType extends Parcelable>
            ResultType invokeSync(String process, InputType data, @NonNull Class<T> taskClass)
複製程式碼

非同步呼叫介面

@AnyThread
public static <T extends IPCAsyncInvokeTask> boolean invokeAsync(
            @NonNull String process, Bundle data, @NonNull Class<T> taskClass, IPCInvokeCallback callback)

複製程式碼
@AnyThread
public static <T extends IPCRemoteAsyncInvoke<InputType, ResultType>, InputType extends Parcelable, ResultType extends Parcelable>
            boolean invokeAsync(String process, InputType data, @NonNull Class<T> taskClass, IPCRemoteInvokeCallback<ResultType> callback)
複製程式碼

IPCInvoker使用示例

同步呼叫

public class IPCInvokeSample_InvokeByType {

    private static final String TAG = "IPCInvokerSample.IPCInvokeSample_InvokeByType";

    public static void invokeSync() {
        Bundle bundle = new Bundle();
        bundle.putString("name", "AlbieLiang");
        bundle.putInt("pid", android.os.Process.myPid());
        IPCString result = IPCInvoker.invokeSync(PushProcessIPCService.PROCESS_NAME,
                    bundle, IPCRemoteInvoke_BuildString.class);
        Log.i(TAG, "invoke result : %s", result);
    }

    private static class IPCRemoteInvoke_BuildString implements IPCRemoteSyncInvoke<Bundle, IPCString> {

        @Override
        public IPCString invoke(Bundle data) {
            String msg = String.format("name:%s|fromPid:%s|curPid:%s", data.getString("name"),
                        data.getInt("pid"), android.os.Process.myPid());
            Log.i(TAG, "build String : %s", msg);
            return new IPCString(msg);
        }
    }
}
複製程式碼

非同步呼叫

public class IPCInvokeSample_InvokeByType {

    private static final String TAG = "IPCInvokerSample.IPCInvokeSample_InvokeByType";

    public static void invokeAsync() {
        Bundle bundle = new Bundle();
        bundle.putString("name", "AlbieLiang");
        bundle.putInt("pid", android.os.Process.myPid());
        IPCInvoker.invokeAsync(PushProcessIPCService.PROCESS_NAME, bundle,
                IPCRemoteInvoke_PrintSomething.class, new IPCRemoteInvokeCallback<IPCString>() {
            @Override
            public void onCallback(IPCString data) {
                Log.i(TAG, "onCallback : %s", data.value);
            }
        });
    }

    private static class IPCRemoteInvoke_PrintSomething implements IPCRemoteAsyncInvoke<Bundle, IPCString> {

        @Override
        public void invoke(Bundle data, IPCRemoteInvokeCallback<IPCString> callback) {
            String result = String.format("name:%s|fromPid:%s|curPid:%s",
                    data.getString("name"), data.getInt("pid"), android.os.Process.myPid());
            callback.onCallback(new IPCString(result));
        }
    }
}

複製程式碼

上述示例中IPCString是IPCInvoker裡面提供的String的Parcelable的包裝類,IPCInvoker支援的跨程式呼叫的資料必須是可序列化的Parcelable(預設支援Bundle)。

當然也可以使用自己實現的Parcelable類作為跨程式呼叫的資料結構,如:

public class IPCSampleData implements Parcelable {

    public String result;

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

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

    public static final Creator<IPCSampleData> CREATOR = new Creator<IPCSampleData>() {
        @Override
        public IPCSampleData createFromParcel(Parcel in) {
            IPCSampleData o = new IPCSampleData();
            o.result = in.readString();
            return o;
        }

        @Override
        public IPCSampleData[] newArray(int size) {
            return new IPCSampleData[size];
        }
    };
}
複製程式碼

跨程式事件監聽與分發

IPCInvoker元件中已經對跨程式事件做了封裝,只需要寫很少的程式碼就可以實現跨程式事件的監聽和分發了.

實現跨程式事件Dispatcher

public class OnClickEventDispatcher extends IPCDispatcher {
}
複製程式碼

因為在做跨程式事件分發時使用的是Dispatcher的class name作為key所以dispatcher只需要繼承IPCDispatcher即可。

使用IPCObservable註冊跨程式事件監聽

註冊跨程式事件監聽

final IPCObserver observer1 = new IPCObserver() {
    @Override
    public void onCallback(final Bundle data) {
        String log = String.format("register observer by Observable, onCallback(%s),
                cost : %s", data.getString("result"),
                (System.nanoTime() - data.getLong("timestamp")) / 1000000.0d);
        Log.i(TAG, log);
    }
};
IPCObservable observable = new IPCObservable("cc.suitalk.ipcinvoker.sample:push", OnClickEventDispatcher.class);
// 註冊跨程式事件監聽
observable.registerIPCObserver(observer1)
複製程式碼

反註冊跨程式事件監聽

// 反註冊跨程式事件監聽
observable.unregisterIPCObserver(observer1)

複製程式碼

釋出跨程式事件

釋出Bundle事件

OnClickEventDispatcher dispatcher = new OnClickEventDispatcher();

Bundle event = new Bundle();
event.putString("result", String.format("processName : %s, pid : %s",
        IPCInvokeLogic.getCurrentProcessName(), android.os.Process.myPid()));
event.putLong("timestamp", System.nanoTime())

dispatcher.dispatch(event);
複製程式碼

釋出自定義資料結構的跨程式事件

釋出自定義資料結構的跨程式事件,該資料結構需要實現IPCData介面(如:IPCSampleData)

實現自定義跨程式事件

public class IPCSampleData implements Parcelable, IPCData {

    public String result;
    public long timestamp;

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(result);
        dest.writeLong(timestamp);
    }

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

    public static final Creator<IPCSampleData> CREATOR = new Creator<IPCSampleData>() {
        @Override
        public IPCSampleData createFromParcel(Parcel in) {
            IPCSampleData o = new IPCSampleData();
            o.result = in.readString();
            o.timestamp = in.readLong();
            return o;
        }

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

    @Override
    public Bundle toBundle() {
        Bundle bundle = new Bundle();
        bundle.putString("result", result);
        bundle.putLong("timestamp", timestamp);
        return bundle;
    }

    @Override
    public void fromBundle(Bundle bundle) {
        result = bundle.getString("result");
        timestamp = bundle.getLong("timestamp");
    }
}
複製程式碼

註冊遠端事件監聽

final IPCObserver observer1 = new IPCObserver() {
    @Override
    public void onCallback(final Bundle data) {
        IPCSampleData result = new IPCSampleData();
        result.fromBundle(data);
        String log = String.format("register observer by client, onCallback(%s), cost : %s",
                result.result, (System.nanoTime() - result.timestamp) / 1000000.0d);
        Log.i(TAG, log);
    }
};
IPCObservable observable = new IPCObservable("cc.suitalk.ipcinvoker.sample:push", OnClickEventDispatcher.class);
// 註冊跨程式事件監聽
observable.registerIPCObserver(observer1)
複製程式碼

釋出IPCSampleData事件

OnClickEventDispatcher dispatcher = new OnClickEventDispatcher();

IPCSampleData event = new IPCSampleData();
event.result = String.format("processName : %s, pid : %s", IPCInvokeLogic.getCurrentProcessName(), android.os.Process.myPid());
event.timestamp = System.nanoTime();

dispatcher.dispatch(event);
複製程式碼

IPCInvoker中的Client?

看到這裡想必你已經大致瞭解IPCInvoker的介面呼叫了,在使用層面,並沒有明顯的Client/Server架構的影子,本節中講到的是IPCInvokerClient。IPCInvokeClient其內部是對IPCInvoker進行了一定的封裝,針對指定的遠端程式,支援IPCInvoker原本的同步和非同步呼叫,實現了跨程式事件監聽邏輯。

IPCInvokeClient的相關介面

@AnyThread
public <T extends IPCAsyncInvokeTask> boolean invokeAsync(Bundle data, @NonNull Class<T> taskClass, IPCInvokeCallback callback)
複製程式碼
@AnyThread
public <T extends IPCRemoteAsyncInvoke<InputType, ResultType>, InputType extends Parcelable, ResultType extends Parcelable>
            boolean invokeAsync(InputType data, @NonNull Class<T> taskClass, IPCRemoteInvokeCallback<ResultType> callback)
複製程式碼
@WorkerThread
public <T extends IPCSyncInvokeTask> Bundle invokeSync(Bundle data, @NonNull Class<T> taskClass)
複製程式碼
@WorkerThread
public <T extends IPCRemoteSyncInvoke<InputType, ResultType>, InputType extends Parcelable, ResultType extends Parcelable>
            ResultType invokeSync(InputType data, @NonNull Class<T> taskClass)
複製程式碼
@AnyThread
public boolean registerIPCObserver(String event, @NonNull IPCObserver observer)
複製程式碼
@AnyThread
public boolean unregisterIPCObserver(String event, @NonNull IPCObserver observer)
複製程式碼

IPCInvokeClient使用示例

建立IPCInvokeClient

IPCInvokeClient client = new IPCInvokeClient("cc.suitalk.ipcinvoker.sample:push");
複製程式碼

IPCInvokeClient在建立時已經指定遠端程式(如示例中指定了遠端程式為cc.suitalk.ipcinvoker.sample:push),IPCInvokeClient調 同步/非同步 呼叫介面及跨程式事件監聽與分發,均與本文前面介紹的IPCInvoker介面是用是保持一致的,這裡不再冗述。

XIPCInvoker

怎麼會有個XIPCInvoker?XIPCInvoker與IPCInvokeClient類似,也是對IPCInvoker進行了一定的封裝。IPCInvoker中的invokeSync和invokeAsync介面只接受Parcelable或Bundle資料型別,而XIPCInvoker的出現是為了支援更豐富的資料型別(支援普通資料型別).

XIPCInvoker呼叫介面

非同步呼叫介面

@AnyThread
public static <T extends IPCRemoteAsyncInvoke<InputType, ResultType>, InputType, ResultType>
            void invokeAsync(String process, InputType data, @NonNull Class<T> taskClass, IPCRemoteInvokeCallback<ResultType> callback)
複製程式碼

同步呼叫介面

@WorkerThread
public static <T extends IPCRemoteSyncInvoke<InputType, ResultType>, InputType, ResultType>
            ResultType invokeSync(String process, InputType data, @NonNull Class<T> taskClass)
複製程式碼

上述兩個介面和IPCInvoker中的同步/非同步呼叫介面不同的地方在於InputTypeResultType不在限定是Parcelable,可以是任意資料型別。

當然需要支援Parcelable之外的資料型別,需要提供該資料型別的BaseTypeTransfer實現類,並在IPCInvoker初始化時將其新增到IPCInvoker的ObjectTypeTransfer中.

支援自定義跨程式資料型別

需實現BaseTypeTransfer介面,並在IPCInvoker初始化時將其新增到ObjectTypeTransfer中。

自定義資料結構TestType

public class TestType {

    public String key;

    public String value;
}
複製程式碼

實現BaseTypeTransfer介面

public class TestTypeTransfer implements BaseTypeTransfer {
    @Override
    public boolean canTransfer(Object o) {
        return o instanceof TestType;
    }

    @Override
    public void writeToParcel(@NonNull Object o, Parcel dest) {
        TestType testTypeObj = (TestType) o;
        dest.writeString(testTypeObj.key);
        dest.writeString(testTypeObj.value);
    }

    @Override
    public Object readFromParcel(Parcel in) {
        TestType testTypeObj = new TestType();
        testTypeObj.key = in.readString();
        testTypeObj.value = in.readString();
        return testTypeObj;
    }
}
複製程式碼

初始化時將TestTypeTransfer新增到ObjectTypeTransfer中

public class IPCInvokerApplication extends Application {

    private static final String TAG = "IPCInvokerSample.IPCInvokerApplication";

    @Override
    public void onCreate() {
        super.onCreate();
        // Initialize IPCInvoker
        IPCInvokerBoot.setup(this, new DefaultInitDelegate() {
            @Override
            public void onAttachServiceInfo(IPCInvokerInitializer initializer) {
                initializer.addIPCService(MainProcessIPCService.PROCESS_NAME, MainProcessIPCService.class);
                initializer.addIPCService(PushProcessIPCService.PROCESS_NAME, PushProcessIPCService.class);
            }

            @Override
            public void onAddTypeTransfer(TypeTransferInitializer initializer) {
                super.onAddTypeTransfer(initializer);
                initializer.addTypeTransfer(new TestTypeTransfer());
            }
        });
    }
}
複製程式碼

在XIPCInvoker中使用TestType

TestType data = new TestType();
data.key = "wx-developer";
data.value = "XIPCInvoker";
final IPCInteger result = XIPCInvoker.invokeSync("cc.suitalk.ipcinvoker.sample:push", data, IPCInvokeTask_getInt.class);

Log.i(TAG, "result : %s", result.value);
複製程式碼

跨程式事件XIPCDispatcher與XIPCObserver

XIPCDispatcher與XIPCObserver是IPCDispatcher與IPCObserver的擴充套件,與XIPCInvoker對IPCInvoker相同,XIPCDispatcher與XIPCObserver同樣是為了支援自定資料型別而設計的,介面使用與原介面一致,這裡就不再冗述了.

相關文章

歡迎使用IPCInvoker,有興趣的同學可以一起維護該專案。(專案地址為github.com/AlbieLiang/…

相關文章