Android-Service端和Clinet端通訊-如果client被殺了

  最近做了一個android的語音項目,service端集成了語音服務,該service提供相關的語音SDK,比如語音搜索音樂,然後音樂app service集成這個SDK,從而獲得語音能力。

在實際開發的過程中遇到兩個棘手的問題:

1 Voice Service起來了,如果Music並沒有啓動,有人請求語音搜索音樂,那麼怎麼能告訴你呢?

2 如果Music起來並且通過bind接口綁定了Voice,那麼voice如何知道呢?

針對兩個問題我們進行分析:

1 Voice Service起來而Music沒有起來,如果有人請求語音搜索音樂,觸發了相關的回調

onSeachMusic(MusicBean musicBean)

這個時候怎麼發給music呢?

答案其實不難,就是如果music service沒有啓動,則它也不會利用service提供的aidl向voice service註冊語音監聽服務,這個時候只要判斷這個就可以解決,然後還有一個問題,就是把準備觸發的音樂搜索的回調(AIDL中提供給Music的)給保存起來,等到我啓動了music service讓他向我註冊,再發送給他。

這裏有一個技巧,爲了實現所有方法的統一管理,我這裏使用動態代理,去緩存即將要發送的任意一個client的回調的方法和參數。

public boolean onSearchMusic(MusicModel model)
    {
        DataCenter.getInstance().recordVoiceRequest(DataCenter.IntentDomain.MUSIC);
        IMusicControlCb  callback = (IMusicControlCb) RegisterAIDLManager
                .getInstance()
                .getInterface(ServiceManager.MUSIC_KEY);
        boolean ret = false;
        try {
            if (callback != null && callback.asBinder().pingBinder()) {
                ret = callback.searchMusic(model);//觸發音樂的搜索回調監聽
            } else {
                ret = CallbackProxyManager.getInstance().getMusicCallbackProxy().searchMusic(model);//代理
            }
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        return ret;
    }

這裏有個難點就是AIDL文件如何使用Android的動態代理呢?Interface我們知道可以,但是aidl文件並不知道,廢話不多說直接上代碼,這裏用到了反射

private void initAudioBookCallbackProxy() {
        Class<?> reflectCallback = null;
       //核心部分
        try {
            reflectCallback =  Class.forName("com.byton.vas.vassdk.IOnlineFMControlCb");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        mAudioBookCallbackProxyHandler = new CallbackProxyHandler(ServiceManager.AUDIO_BOOK_KEY);
        mAudioBookCallbackProxy = (IOnlineFMControlCb) Proxy.newProxyInstance(
                CallbackProxyManager.class.getClassLoader(), new Class[] { reflectCallback },//核心
                mAudioBookCallbackProxyHandler);
    }

這裏一旦完成,就可以利用動態代理對任意的aidl回調方法和參數進行緩存,然後去啓動相關的client應用服務。

 public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
        boolean result = ServiceManager.getInstance().startRemoteService(key);
        if (result) {
            InvokeObject invokeObject = new InvokeObject();
            invokeObject.setKey(key);
            invokeObject.setMethod(method);
            invokeObject.setObjects(objects);
            CallbackEventsCache.getInstance().product(invokeObject);
            return true;
        }
       return false;
    }

一旦music的應用啓動了和我們綁定了,則這個時候必然向我們註冊,我們只要在註冊的地方,將所有即將發送給這個應用的緩存指令發送給他就可以了。

 public void registerMusicControlCb(IMusicControlCb callback) {
        RegisterAIDLManager.getInstance().addInterface(ServiceManager.MUSIC_KEY, callback);
        CallbackEventsCache.getInstance().consume();
    }

總結一下就是如下幾點:

  • 判斷你是否活着
  • 如果沒有活?則利用動態代理以及BlockingQuee構建消費者-生產者隊列緩存所有的語音aidl回調方法和參數
  • 啓動相應的client端回調,向voice註冊。
  • 觸發緩存中的回調方法。

第二個問題,如果client端music service被殺了,那麼我的語音指令怎麼辦。

有人可能會說,它不是和你bind了嗎?它死了voice service的unbind回調肯定能知道。對不起錯了,如果有N個client bind servcie,除非所有的client都斷開了,你的service纔會收到unbind回調。

這裏有個方法就是

callback.asBinder().pingBinder()

這個AIDL的方法,可以用來判斷目標client是否和你還在綁定的狀態。一旦語音回調觸發,只要判斷這個就可以知道client是否活着,如果沒有,則可以按照第一個問題的解決方案進行處理。

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