欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android Messenger實(shí)現(xiàn)進(jìn)程間通信及其原理

 更新時(shí)間:2021年05月21日 10:36:12   作者:aspook  
這篇文章主要為大家詳細(xì)介紹了Android Messenger實(shí)現(xiàn)進(jìn)程間通信及其原理,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

前言

之前分析Android消息機(jī)制的源碼時(shí),曾遇到過replyTo、IMessenger等屬性字段,當(dāng)時(shí)只是說這些字段用于進(jìn)程間通信,并未作深入分析。今天這篇文字就來演示一下使用Messenger如何進(jìn)行進(jìn)程間通信并分析其源碼實(shí)現(xiàn)。

Messenger進(jìn)程間通信的流程

Messenger顧名思義,即信使,那么它的作用就是滿足不同進(jìn)程兩邊的通信需要了。通常我們會(huì)寫AIDL來實(shí)現(xiàn)進(jìn)程間通信,其實(shí)簡單的IPC可以用Messenger來實(shí)現(xiàn),需要知道的是Messenger也是基于AIDL的,只不過Messenger幫我們做了封裝而已,其進(jìn)程間通信框架是這樣的:

如上圖,假設(shè)兩個(gè)進(jìn)程分別為Client Process和Server Process,首先Server端需要將自己這邊的Messenger引用傳給Client,然后Client使用Server端傳過來的Messenger來發(fā)消息給Server端,這樣就實(shí)現(xiàn)了一個(gè)單向通信。同理,如果想要實(shí)現(xiàn)雙向通信,則需要Client端也發(fā)送一個(gè)自己的Messenger到Server端,那么Server端也就可以利用該Messenger向Client發(fā)消息了。雖然Messenger是基于AIDL的,但它們最底層都是基于Binder的。

Messenger進(jìn)程間雙向通信示例

創(chuàng)建一個(gè)Service模擬Server進(jìn)程

一般的進(jìn)程間通信多是在兩個(gè)App之間,但一個(gè)App中也可以有多進(jìn)程,這個(gè)很常見,如應(yīng)用中的推送服務(wù)一般位于單獨(dú)的進(jìn)程。當(dāng)然我們可以把這個(gè)Service創(chuàng)建到另一個(gè)App中,但為了方便測試,這里只是將該Service注冊(cè)為另一個(gè)進(jìn)程,但還是在同一個(gè)應(yīng)用中。

該Service的實(shí)現(xiàn)很簡單,如下:

public class RemoteService extends Service {
    private WorkThread mWorkThread = new WorkThread();
    private Messenger mMessenger;

    @Override
    public void onCreate() {
        super.onCreate();
        mWorkThread.start();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mWorkThread.quit();
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mMessenger.getBinder();
    }

    private void prepareMessenger() {
        mMessenger = new Messenger(mWorkThread.mHandler);
    }

    private class WorkThread extends Thread {
        Handler mHandler;

        @Override
        public void run() {
            Looper.prepare();
            mHandler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    switch (msg.what) {
                        case MessageConstant.CLIENT_TO_SERVER:
                            Toast.makeText(RemoteService.this, "Hello Server:" + msg.arg1 + "," + msg.arg2, Toast.LENGTH_SHORT).show();
                            if (msg.replyTo != null) {
                                try {
                                    msg.replyTo.send(Message.obtain(null, MessageConstant.SERVER_TO_CLIENT, 0, msg.arg1 + msg.arg2));
                                } catch (RemoteException e) {
                                    e.printStackTrace();
                                }
                            }
                            break;
                        default:
                            break;
                    }

                }
            };
            prepareMessenger();
            Looper.loop();
        }

        public void quit() {
            mHandler.getLooper().quit();
        }
    }

上述代碼雖然簡單,但有幾點(diǎn)需要注意:

1、為什么Service中要開一個(gè)工作線程?因?yàn)镾ervice作為四大組件之一,它是運(yùn)行在主線程的,所以不能執(zhí)行耗時(shí)操作,一旦進(jìn)程間交互是耗時(shí)操作,那么Service所在進(jìn)程就會(huì)阻塞,而Client端進(jìn)程則不會(huì)阻塞。
2、該Service中創(chuàng)建了一個(gè)Messenger對(duì)象,并在onBind中返回了IBinder對(duì)象,這里是進(jìn)程間通信的關(guān)鍵,在后面會(huì)詳細(xì)分析。
3、該Service的子線程中創(chuàng)建了一個(gè)Handler,并關(guān)聯(lián)給Messenger,用于進(jìn)程間通信的消息處理。Handler消息處理跟我們平時(shí)用的一樣,但有一點(diǎn)提一下,子線程是沒有默認(rèn)Looper的,因此需要自己創(chuàng)建并啟動(dòng),否則子線程的Handler無法收到Message。
4、Server端收到消息后,Toast一下“hello server”并顯示Cient傳過來的兩個(gè)整數(shù)值。如果Client端也將自己的Messenger傳過來了,則向Client端回復(fù)消息,將兩個(gè)整數(shù)之和返回。

另外該Service在AndroidManifest.xml中的注冊(cè)如下:

<service
    android:name=".messenger.RemoteService"
    android:enabled="true"
    android:exported="true"
    android:process=":remote">
    <intent-filter>
        <action android:name="com.aspook.remote.ACTION_BIND" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</service>

核心一句為android:process=":remote",將該Service置于另一個(gè)進(jìn)程之中,從而可以在同一個(gè)App中模擬進(jìn)程間通信。

創(chuàng)建一個(gè)Activity模擬Client進(jìn)程

該Activity默認(rèn)就是該App所在進(jìn)程了,具體實(shí)現(xiàn)如下:

/**
 * demo for IPC by Messenger
 */
public class MessengerActivity extends AppCompatActivity {

    private Button btn_start;
    private Button btn_bind;
    private Button btn_send;
    private boolean mBound = false;
    private Messenger mRemoteMessenger = null;

    private ServiceConnection mRemoteConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mRemoteMessenger = new Messenger(service);
            mBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mRemoteMessenger = null;
            mBound = false;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_messenger);

        findViews();
        setListeners();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(mRemoteConnection);
    }

    public void findViews() {
        btn_start = (Button) findViewById(R.id.btn_start);
        btn_bind = (Button) findViewById(R.id.btn_bind);
        btn_send = (Button) findViewById(R.id.btn_send);
    }

    public void setListeners() {
        btn_start.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // start Remote Service first
                Intent intent = new Intent(MessengerActivity.this, RemoteService.class);
                startService(intent);
                btn_start.setEnabled(false);
            }
        });

        btn_bind.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // bind the Remote Service, if the Remote service run in another App, you should run the App and start the service first
                try {
                    bindRemoteService();
                    btn_bind.setEnabled(false);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });

        btn_send.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mBound) {
                    Handler mClientHandler = new Handler() {
                        @Override
                        public void handleMessage(Message msg) {
                            super.handleMessage(msg);
                            switch (msg.what) {
                                case MessageConstant.SERVER_TO_CLIENT:
                                    Toast.makeText(MessengerActivity.this, "Hello Client:" + msg.arg2, Toast.LENGTH_SHORT).show();

                                    break;
                                default:
                                    break;
                            }
                        }
                    };

                    try {
                        Message msg = Message.obtain(null, MessageConstant.CLIENT_TO_SERVER, 66, 88);
                        // Messenger of client sended to server is used for sending message to client
                        msg.replyTo = new Messenger(mClientHandler);
                        mRemoteMessenger.send(msg);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                } else {
                    Toast.makeText(MessengerActivity.this, "Service not bind", Toast.LENGTH_SHORT).show();
                }
            }
        });
    }

    /**
     * bind service
     */
    public void bindRemoteService() {
        // Method one
        Intent intent = new Intent("com.aspook.remote.ACTION_BIND");// 5.0+ need explicit intent
        intent.setPackage("com.aspook.androidnotes"); // the package name of Remote Service
        bindService(intent, mRemoteConnection, BIND_AUTO_CREATE);
    }
}

代碼邏輯也很簡單,界面有3個(gè)按鈕,操作如下:

1、先啟動(dòng)Server端的Service,暫且叫做啟動(dòng)遠(yuǎn)程Service
2、綁定遠(yuǎn)程Service
3、Client向Servcie端發(fā)送消息,并接收返回的消息

需要注意的有如下幾點(diǎn):

1、綁定遠(yuǎn)程Service后,Client端才拿到了Server端的Messenger引用。
2、Client端的Messenger需要關(guān)聯(lián)自己的Handler,用來處理從Server端收到的消息。這里也需要注意,理論上如果Server端與Client端交互也是耗時(shí)的話,也需要開子線程,這個(gè)例子中由于只是顯示下消息,直接放在UI線程了。
3、如果需要雙向通信,Client端需要通過Message的replyTo參數(shù)將自己的Messenger發(fā)到Server端。
4、Android 5.0+要求綁定Service時(shí)必須使用顯式Intent,可以通過設(shè)置包名的方式來解決,注意我是在同一個(gè)App中開的兩個(gè)進(jìn)程,因此包名相同,但如果遠(yuǎn)程Service位于另一個(gè)App,則應(yīng)該填寫其所在App的包名。
5、Client端收到回復(fù)消息后,Toast“Hello client”及兩個(gè)整數(shù)之和。

示例效果演示

以上示例的進(jìn)程間通信效果演示如下:

Messenger進(jìn)程間通信原理分析

關(guān)于Service的啟動(dòng)、綁定不必多說,先從Client端通過綁定遠(yuǎn)程Service獲取Server端的Messenger入手,代碼如下:

private ServiceConnection mRemoteConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        mRemoteMessenger = new Messenger(service);
        mBound = true;
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        mRemoteMessenger = null;
        mBound = false;
    }
};

接著來看mRemoteMessenger = new Messenger(service);的源碼實(shí)現(xiàn):

/**
 * Create a Messenger from a raw IBinder, which had previously been
 * retrieved with {@link #getBinder}.
 * 
 * @param target The IBinder this Messenger should communicate with.
 */
public Messenger(IBinder target) {
    mTarget = IMessenger.Stub.asInterface(target);
}

注意到該構(gòu)造方法的參數(shù)IBinder,就是遠(yuǎn)程Service中onBind返回的,具體代碼如下:

@Nullable
@Override
public IBinder onBind(Intent intent) {
    return mMessenger.getBinder();
}

再來看這一句代碼:

mTarget = IMessenger.Stub.asInterface(target);

mTarget是IMessenger對(duì)象,看起來越來越像AIDL的寫法了,其實(shí)不能說像,本來就是AIDL。于是猜想源碼必定中有一個(gè)名為IMessenger.aidl的文件,它應(yīng)該定義了發(fā)送消息的相關(guān)接口。果然在源碼目錄 “/frameworks/base/core/java/android/os/”下找到了IMessenger.aidl文件,其內(nèi)容如下:

package android.os;
import android.os.Message;

/** @hide */
oneway interface IMessenger {
    void send(in Message msg);
}

因此可知Messenger只是幫我們省去了寫AIDL的工作而已,底層還是AIDL。

再來看Messenger是如何發(fā)送消息的,即Messenger的send方法:

/**
 * Send a Message to this Messenger's Handler.
 * 
 * @param message The Message to send.  Usually retrieved through
 * {@link Message#obtain() Message.obtain()}.
 * 
 * @throws RemoteException Throws DeadObjectException if the target
 * Handler no longer exists.
 */
public void send(Message message) throws RemoteException {
    mTarget.send(message);
}

通過注釋可知,Messenger會(huì)將消息發(fā)送到其關(guān)聯(lián)的Handler,且Handler不存在時(shí)會(huì)報(bào)異常,這就是我們無論是創(chuàng)建客戶端還是服務(wù)端Messenger時(shí)都為其創(chuàng)建了一個(gè)Handler的原因。

另外上述示例中為了簡便,只是在進(jìn)程間傳遞了基本類型的值,其實(shí)類似單進(jìn)程的消息機(jī)制,也可以傳遞Bundle數(shù)據(jù),但注意需要序列化,具體說明可參考Message源碼的基本字段支撐。

以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論