AIDL (Android Interface Definition Language)
近期做了一個(gè)進(jìn)程間通信的需求,這里總結(jié)一下。具體的需求是這樣的:
B應(yīng)用在詳情頁需要調(diào)起A應(yīng)用的播放功能,但播放地址是必須在播放器頁面再去獲取的,但A應(yīng)用不能調(diào)取B應(yīng)用的請求,只能通過B應(yīng)用中的請求拿到播放地址傳給A應(yīng)用,A然后進(jìn)行播放。
- 這里就需要用到進(jìn)程間通信(icp:inter process communication:內(nèi)部進(jìn)程通信。)了。 業(yè)務(wù)邏輯大體如下:B在詳情頁點(diǎn)擊播放的時(shí)候通過普通的Intent或者scheme方式調(diào)起A應(yīng)用對應(yīng)的activity,之后A應(yīng)用會啟動B中定義的service,然后通過aidl的方法傳遞請求參數(shù)數(shù)據(jù)到B中,B拿到請求參數(shù)后起線程獲取播放地址,然后回傳給A應(yīng)用,A拿到播放地址后再進(jìn)行播放。
流程圖如下(B應(yīng)用就叫main應(yīng)用,A應(yīng)用就叫l(wèi)ite應(yīng)用):

- 下面是具體實(shí)現(xiàn):
1.先在main應(yīng)用中建立對應(yīng)的aidl文件,

這樣會在我們包名下建出aidl文件,這時(shí),我們需要在build下重新make project一下,這樣在build文件夾中就會生成對應(yīng)的接口文件,如下圖:

這樣,我們才能在代碼中引用這個(gè)接口。
這時(shí)在IRemoteService.aidl文件中,還只是默認(rèn)的方法,這里我們需要加上我們自己的方法,這個(gè)文件其實(shí)就是一個(gè)接口文件,所以我們只能寫上方法的定義,具體的實(shí)現(xiàn)還得再接口的實(shí)現(xiàn)中完成。
在IRemoteService.aidl我們加上
/**
* @param couldId 視頻id
* @param callback 獲取playurl的回調(diào)
*/String getPlayInfo(String couldId, LiteappCallback callback);
注意:上面方法中的參數(shù)LIteappCallback是又lite中需要傳給main的回調(diào)類,我們需要再次建一個(gè)aidl文件,就叫LiteappCallback.aidl,代碼如下:
// LiteappCallback.aidlpackage
com.main.mainapplication;
// Declare any non-default types here with import statements
interface LiteappCallback {
/**
* Demonstrates some basic types that you can use as parameters * and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString);
//獲取url成功
void getPlayUrlSuccess(String playUrl);
//獲取url失敗
void getPlayUrlFail(String message);
}
其中兩個(gè)方法的具體實(shí)現(xiàn)在需要在lite應(yīng)用中做的。
---別忘了添加aidl之后需要make project一下,否則你會發(fā)現(xiàn)在IRemoteService中用不了。這里還需要注意的是,在aidl中如果引用其他aidl接口類型,需要import,即使是在相同的包結(jié)構(gòu)下。所以我們在IRemoteService的方法中引用LiteappCallback需要導(dǎo)包,在IRemoteService.aidl文件中加入import com.main.mainapplication.LiteappCallback;
至此,aidl基本定義完畢。
2.創(chuàng)建main中的service。
這里我們創(chuàng)建叫l(wèi)iteservice的服務(wù),其中需要實(shí)現(xiàn)IRemoteService。
package com.main.mainapplication.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import com.main.mainapplication.IRemoteService;
import com.main.mainapplication.LiteappCallback;
/**
* Date: 2016-04-15
* Time: 12:58
* Description: FIXME
*/
public class LiteService extends Service{
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException { }
@Override
public String getPlayInfo(String couldId, LiteappCallback callback) throws RemoteException {
requestPlayUrl(couldId, callback);
return "http://";
}
};
private void requestPlayUrl(String couldId, LiteappCallback callback) { //TODO 進(jìn)行異步操作
//在異步請求里如果成功執(zhí)行callback.getPlayUrlSuccess(playurl); //如果失敗之執(zhí)行callback.getPlayUrlFail(message);
}
}
其中具體的異步請求就不寫了,總之會回調(diào)callback的兩個(gè)方法。由于是異步的處理,所以這里getPlayInfo方法的直接返回結(jié)果并沒有什么卵用。
別忘了我們需要在main應(yīng)用的清單文件中注冊service:
<service android:name="com.main.mainapplication.service.LiteService">
<intent-filter>
<action android:name="com.main.mainapplication.intent.action.FORURL"/>
</intent-filter>
</service>
這里采用隱式的調(diào)起方式。
3.回到lite應(yīng)用中
首先需要將main中創(chuàng)建的aidl文件copy到lite中,這里需要注意的是aidl的包名也一并copy過來:

同樣需要make project生成對應(yīng)接口文件。
由于只是事例代碼,所以我就寫在liteApplication的MainActivity中了,
private IRemoteService mRemoteService;
ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mRemoteService = IRemoteService.Stub.asInterface(service);
System.out.println("bind success" + mRemoteService.toString());
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
private final LiteappCallback.Stub callback = new LiteappCallback.Stub() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public void getPlayUrlSuccess(String playUrl) throws RemoteException {
System.out.println("playUrl: " + playUrl);
}
@Override
public void getPlayUrlFail(String message) throws RemoteException {
System.out.println("message: " + message);
}
};
這里是先創(chuàng)建ServiceConnection,并且實(shí)現(xiàn)LiteappCallback接口,getPlayUrlSuccess功能只是打印該url。這個(gè)方法會在main應(yīng)用中的異步處理中調(diào)用。
這里就直接在mainactivity的oncreate方法中綁定服務(wù)了,場景不一樣,需要另外處理:
Intent intent = new Intent("com.main.mainapplication.intent.action.FORURL");
intent.setPackage("com.main.mainapplication");
System.out.println("開始bind");
bindService(intent, conn, Context.BIND_AUTO_CREATE);
這里注意:Android 5.0一出來后,其中有個(gè)特性就是Service Intent must be explitict,也就是說從Lollipop開始,service服務(wù)必須采用顯式意圖方式啟動.解決辦法就是增加intent.setPackage("com.main.mainapplication");設(shè)置包名。才可以綁定service。然后在需要的地方直接調(diào)用getPlayInfo方法。
try {
mRemoteService.getPlayInfo("23456789", callback);
} catch (RemoteException e) {
e.printStackTrace();
}
此時(shí),同一個(gè)手機(jī)上都裝了main應(yīng)用和lite應(yīng)用后,main應(yīng)用帶數(shù)據(jù)到lite之后,lite通過綁定service,調(diào)用getPlayInfo方法,然后會在service中異步請求獲取后調(diào)用了lite中的getPlayUrlSuccess方法后,成功在lite中拿到了main中請求的url數(shù)據(jù),然后lite中再處理播放的邏輯。
至此,aidl實(shí)現(xiàn)基本完成。