最近項目中碰到個問題,應用退出之後(通過調用System.exit(0)),彈出的Toast很久的不消失。
原因是項目中使用了環信的SDK,其中有個處理後臺推送的Service環信專門爲其加了個so文件,目的是爲了防止Service被殺死。
同事將該Service設置爲android:process = "remote" ,即使Service運行的進程與主進程分開,toast確實是消失了,但是後臺就無法接受到推送了。
原因肯定是Service在其他進程了,而之前環信開發時又未做處理,所以已經無法與主進程通信了。
因爲之前很少涉及到多進程的項目,所以就專門去研究了下AIDL的用法。
簡單來說一下AIDL使用步驟:
1.編寫一個AIDL文件。其實就是一個JAVA interface而已,只是文件名需要以.aidl結尾。
如果使用AndroidStudio進行開發,可直接建立AIDL文件,見下圖:
建好之後,目錄中會多一個名爲aidl的文件夾,然後在.aidl文件中實現自己的方法,例如:
// IRemoteServiceStock.aidl
package k.javine.myremoteservice;
interface IRemoteServiceStock {
//自己實現的方法
int getPid();
int getCount();
}
2.建立一個Service類,並在其中實現aidl接口方法,以供客戶端調用。
這個過程跟編寫正常的Service差不多,只是多了一個Stub的實現,直接上代碼吧。
public class MyRemoteService extends Service {
private static int bindCount = 0;
@Override
public void onCreate() {
super.onCreate();
Log.d("Javine", "Create Service pid = "+ Process.myPid());
}
//實現AIDL接口
private final IRemoteServiceStock.Stub mBinder = new IRemoteServiceStock.Stub(){
@Override
public int getPid() throws RemoteException {
return android.os.Process.myPid();
}
@Override
public int getCount() throws RemoteException {
return bindCount;
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
bindCount++;
return mBinder;
}
@Override
public void onDestroy() {
Log.d("Javine", "Destroy Service");
super.onDestroy();
}
}
3.在主進程中調用遠端進程的Service中的內容。
我們都知道,如果要調用Serivce中的函數,需要在Activity中實現ServiceConnection接口,並重寫其中的方法。這裏會將AIDL接口作爲IBinder返回給Activity,這樣就可以在Acitivy中調用RemoteService中的函數了。
代碼如下:
class MyConnection implements ServiceConnection{
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
if (progressDialog.isShowing())
progressDialog.dismiss();
remoteServiceStock = IRemoteServiceStock.Stub.asInterface(service);//獲取遠端進程Service返回的AIDL接口
String info = null;
try {
servicePid = remoteServiceStock.getPid();
info = "Process Id: "+remoteServiceStock.getPid()+"\nBind Count: "+remoteServiceStock.getCount();
} catch (RemoteException e) {
e.printStackTrace();
}
if (info!=null){
textInfo.setText(info);
unBindBtn.setEnabled(true);
killBtn.setEnabled(true);
}else{
textInfo.setText("獲取失敗");
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
remoteServiceStock = null;
}
}
好了,到此三步驟就走完了。
不過有幾個地方需要重要注意一下:
1.在AndroidManifest.xml中聲明Service,並且指定android:process = ":remote"屬性。這裏的remote前面加“:”代表應用內才能調用的遠端進程,若沒有":"代表其他應用也可調用。
2.如何啓動遠程service?
如果在同一個應用內,可通過Intent(this,xxxService.class)的方式,直接start或者onbind。
如果不在同一個應用內,則需通過給Service設置<action>屬性,然後客戶端通過intent.setAction()來隱式啓動。
爲此,我也編寫了一個Demo,並且發現一個有趣的問題。
如果Activity已經綁定了remoteService,然後kill掉remoteService所在的遠端進程,大概1s後系統會爲remoteService新建一個進程,並且Service還是處於bind狀態。
Demo截圖
demo源碼MyRemoteService