Android service 和 client的進程通訊和消息回調--AIDL

(一)項目介紹
    Launcher上播放小視屏和獨立的視頻應用。小視屏是視頻應用的裁剪版,只有播放的功能,二者使用相同的底層系統。
系統的初始化會佔用比較長的時間,從Launcher上跳轉進入app,若是先徹底退出結束進程,再從新初始化底層系統,
這樣就會耗費很多時間,用戶體驗會差不少。因此,爲了改善這個狀況,把底層的部分作成service形式,在Launcher上跳轉到其餘應用,只是中止了播放,而不結束底層系統。

(二)AIDL

    ServiceAidl.aidl 文件定義提供客戶端調用的接口
package com.demo.service;

import com.demo.service.Freq;
import  com.demo.service.IServiceCallback;

interface ServiceAidl {

boolean isInited();
void registerCallback(IServiceCallback cb);
        void unregisterCallback(IServiceCallback cb);

        void playerInit();
int setVideoScale(int full, int x, int y, int width, int height);
void play();

/*自定義類*/
          Freq getFreq();
}
    aidl裏面使用自定義類Freq,須要定義Freq.aidl,而後Freq類須要實現接口Parcelable。Freq(Parcel pl)裏面的read的順序,要和writeToParcel裏面write的順序一直。
public Freq(Parcel pl)
 
     mFreq = pl.readInt(); 
     mSymbol = pl.readInt(); 
     mQam = pl.readInt(); 
     }

@Override
    public void writeToParcel(Parcel dest, int flags) {
        // TODO Auto-generated method stub
        dest.writeInt(this.mFreq);
        dest.writeInt(this.mSymbol);
        dest.writeInt(this.mQam);
    }

//添加一個靜態成員,名爲CREATOR,該對象實現了Parcelable.Creator接口   
    public static final Parcelable.Creator<DvbFreq> CREATOR = new Parcelable.Creator<DvbFreq>(){

        @Override
        public DvbFreq createFromParcel(Parcel arg0) {
            // TODO Auto-generated method stub
            return new Freq(arg0);
        }

        @Override
        public DvbFreq[] newArray(int arg0) {
            // TODO Auto-generated method stub
            return new Freq[arg0];  
        }  
        
    };


(三)Service

一、manifest 裏面定義
<service
            android:name="com.demo.service"
            android:enabled="true"
            android:exported="true" >
            
            <intent-filter >
                <action android:name="com.demo.service.START"/>
             <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
</service>

二、定義Stub
private final ServiceAidl.Stub   mBinder = new ServiceAidl.Stub() {

       @Override
        public int setVideoScale(int full, int x, int y, int width, int height)
                throws RemoteException {
            // TODO Auto-generated method stub
            return mService.setVideoScale(full, x, y, width, height);
        }

       @Override
        public boolean isInited() throws RemoteException {
            // TODO Auto-generated method stub
            return  mService.isInited();
        }

        @Override
        public void registerCallback(IServiceCallback cb) throws RemoteException {
            // TODO Auto-generated method stub
             if (cb != null) mCallbacks.register(cb);
        }

        @Override
        public void unregisterCallback(IDvbServiceCallback cb) throws RemoteException {
            // TODO Auto-generated method stub
             if (cb != null) mCallbacks.unregister(cb);
        }

        @Override
        public void playerInit() throws RemoteException {
            // TODO Auto-generated method stub
             mService.playerInit();
        }

        @Override
        public void play() throws RemoteException {
            // TODO Auto-generated method stub
             mService.play();
        }
}

三、重載onBind(Intent intent),客戶端bindservice的時候,返回上面定義的mBinder
    
    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
          return mBinder;
    }
        

(四) Client

1. 導入Service的jar包,或者拷貝service的aidl文件(ServiceAidl.aidl)和aidl裏所用到的自定義類文件(Freq.java 和Freq.aidl)

2. 定義Service的aidl接口和connection,
咱們調用   startDemoService ()以後,service connected,返回的Binder樁接口取得mServiceAidl接口,這樣咱們就能夠調用Service的aidl文件裏面定義的接口了。 注意:咱們在startService()的時候,intent的action須要和service 的manifest裏面定義的action一致。

    private ServiceAidl mService;     // service
    private ServiceConnection mConnection;

    // 鏈接service
    private void initConnection() {
          mConnection = new ServiceConnection(){

            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                // TODO Auto-generated method stub
                  mService = DvbServiceAidl.Stub.asInterface(service);    
                try {
                    mInitFlag = mService.isInited();
                    if(mInitFlag) {
                         mService.playerInit();
                           mService.registerCallback(mCallback);    // 註冊消息回調
                         mService.setVideoScale(0, 272, 150, 930, 598);    // 設置視頻大小和位置
                         mService.play();     // 播放
                    }
                    else {
                        exitService();  // 初始化失敗 退出
                    }
                } catch (RemoteException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                // TODO Auto-generated method stub
                mService = null;
            }
            
        };
    }

    private void startDemoService() {
        Intent intent = new Intent( "com.demo.service.Service.START");
          // remote service ,退出activity以後,也不中止service,因此這裏bind以後再start
        // 具體查看   bindService 、startService以及bindService和startService 同時調用的區別
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);  
        startService (intent);
    }

3. 由於Service會改變狀態,須要去更新ui提示用戶狀態改變,目前咱們已經使用了aidl的方式,因此沒辦法使用Messenger的方式來發送消息更新,這邊使用了回調的方式去更新。
(1)、首先,來看下service端的實現,定義一個RemoteCallbackList和 IServiceCallback的aidl接口由客戶端去實現
final static RemoteCallbackList<IServiceCallback> mCallbacks
        = new RemoteCallbackList<IServiceCallback>();
package com.feiyue.dvbservice;

oneway interface IServiceCallback {
/*
 * handler common message from service
 */
    void handlerCommEvent(int msgID, int param);
    
    /*
 * handler search message from service
 */
    void handlerSearchEvent(int msgID, in List<String> strList);
}

(2)、aidl 定義中有register和unregister兩個接口供客戶端註冊和反註冊回調。
void registerCallback(IServiceCallback cb);
        void unregisterCallback(IServiceCallback cb);

       @Override
        public void registerCallback(IDvbServiceCallback cb) throws RemoteException {
            // TODO Auto-generated method stub
            if (cb != null) mCallbacks.register(cb);
        }

        @Override
        public void unregisterCallback(IDvbServiceCallback cb) throws RemoteException {
            // TODO Auto-generated method stub
            if (cb != null) mCallbacks.unregister(cb);
        }

(3)、service 狀態改變,通知client更新ui,這邊咱們組了一個Message的形式轉給客戶端
     final int N = mCallbacks.beginBroadcast();
            for (int i=0; i<N; i++) {
                try {
                    switch(service.status) {
                        case Msg.SHOW__MSG:
                        case Msg.HIDE__MSG:
                            Bundle b = new Bundle();
                            ArrayList<String> strList = new ArrayList<String>();
                            
                            b = msg.getData();
                            strList.add(b.getString("MSGTIP")); // 提示框消息
                        
                            mCallbacks.getBroadcastItem(i).handlerSearchEvent(msg.what, strList); 
                            break;
                        default:
                            break;
                    }
                    
                } catch (RemoteException e) {
                    // The RemoteCallbackList will take care of removing
                    // the dead object for us.
                }
            }

            mCallbacks.finishBroadcast();

4. Client端實現
(1)、實現 IServiceCallback的接口, mHandler是客戶端實現的消息處理。
private IServiceCallback mCallback = new IServiceCallback.Stub() {

        @Override
        public void handlerCommEvent(int msgID, int param) throws RemoteException {
            // TODO Auto-generated method stub
            Message msg = new Message();
            
            msg.what = msgID;
            msg.arg1 = param;
            
            mHandler.sendMessage(msg);  
        }

        @Override
        public void handlerSearchEvent(int msgID, List<String> strList) throws RemoteException {
            // TODO Auto-generated method stub
            Message msg = new Message();
            Bundle b = new Bundle();
            
            msg.what = msgID;
            
            switch (msgID) {
                case Msg.SHOW_MSG:
                case Msg.HIDE_MSG:
                    b.putString("MSGTIP",strList.get(0));    // ca提示框消息
                    break;
                default:
                    break;
            }
            
            msg.setData(b);
            mHandler.sendMessage(msg);
        }
    };

(2)、在service connected 的時候去註冊這個回調,在退出activity反註冊callback 和unbindservice
mService.unregisterCallback(mCallback);

if(mConnection != null) {
            unbindService(mConnection);
            mConnection = null;
        }

到此,基本就已經完成了,service和client相互間的通訊! 
個人第一次寫記錄,勉之! Demo:  https://github.com/yqb178/AidlDemo
相關文章
相關標籤/搜索