RemoteService之AIDL進程間通信 ---基於AndroidStudio

最近項目中碰到個問題,應用退出之後(通過調用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

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