1. 程式人生 > >Messenger實現Activity與Service的雙向通訊

Messenger實現Activity與Service的雙向通訊

Activity與Service間的單向通訊,相信大家都知道,常用的有下面3種方式:
1. 通過bindService(service, conn, BIND_AUTO_CREATE)開啟Service,然後在ServiceConnection的onServiceConnected回撥方法中獲取到Service引用,然後我們就可以訪問Service中的方法了.
2. 通過broadcast(廣播)的形式,當開啟的Service接收到了對應的廣播,就可以處理預設好的邏輯了.
3. 通過呼叫類名直接呼叫Service中定義的靜態方法.

今天要將的是解決Activity與Service的雙向通訊的問題,即Activity可以呼叫Service中暴露的方法,同時Service也可以呼叫Activity中暴露的方法.
實現的方式:


Messenger+Handler.
原理:
分別將Activity和Service的引用傳送給對方,通過Handler物件來發送引用,在Activity與Service中都定義一個Handler.
關鍵問題,如何拿到對方的Handler來發送引用?
通過Messenger類,他可以把一個Handler轉換成IBinder,也可以把IBinder轉換成Messenger.

下面將通過一個Demo來展示Messenger的使用.

目標Activity

public class AudioPlayerActivity extends BaseActivity {

    private
AudioPlayerService mAudioPlayerService; //Service拿到這個handler就可以與Activity通訊了 private Handler mActivityHandler = new Handler() { @Override public void handleMessage(Message msg) { LogUtils.i(AudioPlayerActivity.this, ">>>>>>>>>>>handleMessage...what="
+ msg.what + " obj=" + msg.obj); switch (msg.what) { case AudioPlayerService.SERVICE_INTERFACE: //獲取AudioPlayerService的引用 mAudioPlayerService = (AudioPlayerService) msg.obj; mAudioPlayerService.testCallService(); break; } } }; private Messenger mActivityMessenger = new Messenger(mActivityHandler); @Override protected void setTitleBar(TitleBar titleBar) { titleBar.setTitleBarBgResource(R.drawable.base_titlebar_bg); titleBar.setLeftIcon(R.drawable.selector_btn_back, new View.OnClickListener() { @Override public void onClick(View v) { onBackPressed(); } }); titleBar.setCenterTv("最炫民族風"); } @Override protected boolean isHomePage() { return false; } @Override public Integer getLayoutResId() { return R.layout.activity_audio_player; } @Override public void initView() { } @Override public void initListener() { } @Override public void initData() { Intent intent = getIntent(); if (null != intent) { Bundle bundle = intent.getExtras(); if (null == bundle && bundle.size() < 0) { mLoadView.setExceptionViewVisible(true); } else { //直接把bundle通過引數傳到服務中,然後在service中取出來 Intent service = new Intent(this, AudioPlayerService.class); service.putExtras(bundle); //先通過start方式開啟服務,保證服務能工作在後臺 startService(service); //再通過bind的方式開啟服務,保證activity能夠和service間互動 bindService(service, conn, BIND_AUTO_CREATE); } } } /** * 服務聯結器 */ private ServiceConnection conn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder binder) { LogUtils.i(AudioPlayerActivity.this, ">>>>>>>>>>>onServiceConnected"); //注意:這裡的binder是由AudioPlayerService的mServiceMessenger.getBinder()返回的,因此這裡使用binder建立Messenger的話,就可以公用AudioPlayerService的Handler傳送訊息了. Messenger messenger = new Messenger(binder); Message msg = Message.obtain(); msg.what = AudioPlayerService.UI_INTERFACE; msg.obj = AudioPlayerActivity.this; //這裡將AudioPlayerActivity的引用傳送給AudioPlayerService msg.replyTo = mActivityMessenger;// 將Activity中的mActivityMessenger傳遞個Service LogUtils.i(AudioPlayerActivity.this, "replyTo>>>>>>>>>>>" + mActivityMessenger); try { messenger.send(msg); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { LogUtils.i(AudioPlayerActivity.this, ">>>>>>>>>>>onServiceDisconnected"); } }; public void updateUI(Audio audio) { LogUtils.i(AudioPlayerActivity.this, ">>>>>>>>>>>updateUI"); } @Override protected void onDestroy() { super.onDestroy(); unbindService(conn); } @Override public void reLoadData() { } @Override public void onClick(View v) { } }

目標Service

public class AudioPlayerService extends Service {
    public static final int UI_INTERFACE = 0; //獲取AudioPlayerActivity引用的message what
    public static final int SERVICE_INTERFACE = 1; //獲取AudioPlayerService引用的message what
    private int mPosition;
    private ArrayList<Parcelable> mAudioList;
    private static AudioPlayerActivity mAudioPlayerActivity;
    private static Messenger mActivityMessenger;

    //Activity拿到這個handler就可以與service通訊了
    private Handler mServiceHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case UI_INTERFACE:
                    //拿到AudioPlayerActivity的引用
                    mAudioPlayerActivity = (AudioPlayerActivity) msg.obj;
                    mAudioPlayerActivity.updateUI(null);

                    //拿到AudioPlayerActivity的mActivityMessenger物件,來發送訊息給mActivityHandler
                    mActivityMessenger = msg.replyTo;
                    Message message = Message.obtain();
                    message.what = SERVICE_INTERFACE;
                    message.obj = AudioPlayerService.this;
                    LogUtils.i(AudioPlayerService.this, "mActivityMessenger>>>>>>>>>>>" + mActivityMessenger);
                    try {
                        mActivityMessenger.send(message);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
            }
        }
    };
    //建立Messenger物件
    Messenger mServiceMessenger = new Messenger(mServiceHandler);

    @Override
    public void onCreate() {
        LogUtils.i(AudioPlayerService.this, ">>>>>>>>>>>onCreate");
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        LogUtils.i(AudioPlayerService.this, ">>>>onBind");
        return mServiceMessenger.getBinder();//返回一個Binder
    }
    public void testCallService() {
        LogUtils.i(AudioPlayerService.this, ">>>>>>>>>>>testCallService");
    }


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        LogUtils.i(AudioPlayerService.this, ">>>>>>>>>>>onStartCommand");
//        //獲取Activity傳遞過來的資料
        Bundle bundle = intent.getExtras();
        mPosition = bundle.getInt(Constant.KEY_POSITION);
        mAudioList = bundle.getParcelableArrayList(Constant.KEY_LIST);
        LogUtils.i(AudioPlayerService.this, "mPosition:" + mPosition + " mAudioList:" + mAudioList);
        return super.onStartCommand(intent, flags, startId);
    }
}

列印的日誌

I/AudioPlayerService: >>>>>>>>>>>onCreate
I/AudioPlayerService: >>>>>>>>>>>onStartCommand
I/AudioPlayerService: >>>>onBind
I/AudioPlayerActivity: >>>>>>>>>>>onServiceConnected
I/AudioPlayerActivity: replyTo>>>>>>>>>>>[email protected]
I/AudioPlayerActivity: >>>>>>>>>>>updateUI
I/AudioPlayerService: mActivityMessenger>>>>>>>>>>>[email protected]
I/AudioPlayerActivity: >>>>>>>>>>>handleMessage…what=1 obj=mchenys[email protected]41b072a8
I/AudioPlayerService: >>>>>>>>>>>testCallService

通過上面的日誌,可以發現AudioPlayerService成功的呼叫了AudioPlayerActivity的updateUI方法,同時AudioPlayerActivity也成功的呼叫了AudioPlayerService的testCallService方法.