上篇我們講述了程序間通信在Android中的一種獨有方式Binder,建議不了解的朋友先去看一下這篇文章,因為我們今天要講程序間的另外一種通信方式Messenger也是基于Binder的,主不過Google給我們封裝好了,可以不使用AIDL就可以簡單快速的實作。上篇文章連結:http://blog.csdn.net/u014366923/article/details/49887515
好,接下來我們就看看如何使用Messenger來實作。首先來看一下我們今天要學習的知識:
**1. Messenger是什麼;
2. Android中Messenger的簡單應用;
3. Messenger原理是什麼,Messenger源碼解析。**
一、Messenger概念:
先來看一張圖
通過這張圖我們可以很清楚的了解到了Messenger的通信模型。可以看到,我們可以在用戶端發送一個Message給服務端,在服務端的handler中會接收到用戶端的消息,然後進行對應的處理,處理完成後,再将結果等資料封裝成Message,發送給用戶端,用戶端的handler中會接收到處理的結果。
其實說白了,它就是封裝了Binder,我們不必關系Binder,通過Handler來通信的一種方式。
二、Messenger在Android中的簡單應用:
下面我們來看一個小例子,模仿微信聊天,做一個智能機器人的聊天demo。在這個例子中我們使用了當下比較流行的RecyclerView代替傳統的ListView。用SwipeRefreshLayout來實作下拉重新整理功能。都是Google原生的控件,還是比較綜合的一個小案例。
好,我們先來看一下服務端的源碼:
public class MyService extends Service{
public static final String SERVICE_KEY = "service_key";
private HandlerThread handlerThread = new HandlerThread("service_handler_thread");
private MyHandler handler;
private Messenger messenger;
@Override
public void onCreate() {
super.onCreate();
handlerThread.start();
handler = new MyHandler(handlerThread.getLooper());
messenger = new Messenger(handler);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return messenger.getBinder();
}
class MyHandler extends Handler{
public MyHandler(Looper looper){
super(looper);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Bundle obj = (Bundle) msg.obj;
obj.setClassLoader(getClass().getClassLoader());
MessageBean messageBeanFromClient = (MessageBean) obj.getParcelable(SERVICE_KEY);
if(messageBeanFromClient == null){
return;
}
Message message = Message.obtain();
Bundle bundle = new Bundle();
MessageBean messageBeanToClient = new MessageBean();
if ("第一次".equals(messageBeanFromClient.getContent())){
messageBeanToClient.setContent("您好,我是小智");
}else {
messageBeanToClient.setContent("您好,"+messageBeanFromClient.getContent());
}
messageBeanToClient.setClient(false);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
messageBeanToClient.setDate(simpleDateFormat.format(new Date()));
bundle.putParcelable(MainActivity.CLIENT_KEY,messageBeanToClient);
message.obj = bundle;
try {
msg.replyTo.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
我們來簡單的讀一下源碼,首先繼承自Android四大元件之一的Service元件,這個沒什麼好說的,别忘了在AndroidManifest.xml檔案中注冊我們的服務,在注冊的時候不要忘記
android:process
屬性,若是沒有這個屬性,那我們的服務就會和用戶端在一個程序中,也就談不上程序間通信了。好,繼續來分析源碼,可以看到,服務端有一個Messenger類型的對象,在構造過程中我們傳遞了一個Handler對象。在Handler對象的handleMessage中我們來處理用戶端發給我們的消息,并且再發回給用戶端。其實就是:服務端中有一個依賴于Handler的Messenger的對象,通過Handler消息循環模型,Messenger可以發送消息給用戶端。在
onBind
方法中可以看到
return messenger.getBinder();
是不是突然明白點什麼了呢。。。。。。
用戶端源碼:
public class MainActivity extends AppCompatActivity implements SwipeRefreshLayout.OnRefreshListener {
public static final String CLIENT_KEY = "client_key";
private SwipeRefreshLayout mSwipeRefreshLayout;
private RecyclerView mRecyclerView;
private EditText et_content;
private Messenger mService;
private ArrayList<MessageBean> list;
private MessageAdapter messageAdapter;
private boolean isConnet = false;
private Messenger mMessenger = new Messenger(new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Bundle obj = (Bundle) msg.obj;
obj.setClassLoader(getClass().getClassLoader());
list.add((MessageBean) obj.getParcelable(CLIENT_KEY));
messageAdapter.notifyDataSetChanged();
mRecyclerView.smoothScrollToPosition(list.size() - );
}
});
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
isConnet = true;
mService = new Messenger(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
private LinearLayoutManager mLayoutManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
StatusBarCompat.compat(this,getResources().getColor(R.color.colorPrimaryDark));
initView();
bindService();
}
private void bindService() {
Intent intent = new Intent(this,MyService.class);
bindService(intent,serviceConnection, Context.BIND_AUTO_CREATE);
}
private void initView() {
mSwipeRefreshLayout = (SwipeRefreshLayout) this.findViewById(R.id.swipe_refresh_layout);
mRecyclerView = (RecyclerView) this.findViewById(R.id.recycler_view);
mSwipeRefreshLayout.setColorSchemeResources(R.color.colorPrimary, R.color.colorPrimaryDark,R.color.colorAccent);
mSwipeRefreshLayout.setOnRefreshListener(this);
mSwipeRefreshLayout.setProgressViewOffset(false, , (int) TypedValue
.applyDimension(TypedValue.COMPLEX_UNIT_DIP, , getResources()
.getDisplayMetrics()));
mSwipeRefreshLayout.setRefreshing(true);
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
// mRecyclerView.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL_LIST));
Toolbar mToolBar = (Toolbar) this.findViewById(R.id.toolbar);
mToolBar.setTitle("智能聊天機器人");
mToolBar.setTitleTextColor(Color.WHITE);
et_content = (EditText) this.findViewById(R.id.edit_text_content);
list = new ArrayList<MessageBean>();
messageAdapter = new MessageAdapter(this,list,mRecyclerView);
mRecyclerView.setAdapter(messageAdapter);
}
@Override
public void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
sendMessage("第一次");
mSwipeRefreshLayout.setRefreshing(false);
}
},);
}
public void sendMessageToService(View view){
String content = et_content.getText().toString().trim();
if (isConnet && !TextUtils.isEmpty(content)) {
MessageBean m = sendMessage(content);
list.add(m);
messageAdapter.notifyDataSetChanged();
et_content.setText("");
mRecyclerView.smoothScrollToPosition(list.size() - );
//InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
// inputManager.hideSoftInputFromWindow(et_content.getWindowToken(),0);
}
}
@Nullable
private MessageBean sendMessage(String content) {
Message message = Message.obtain();
MessageBean messageBean = new MessageBean();
messageBean.setContent(content);
messageBean.setClient(true);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
messageBean.setDate(simpleDateFormat.format(new Date()));
Bundle bundle = new Bundle();
bundle.putParcelable(MyService.SERVICE_KEY, messageBean);
message.obj = bundle;
try {
message.replyTo = mMessenger;
mService.send(message);
return messageBean;
} catch (RemoteException e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onDestroy() {
super.onDestroy();
isConnet = false;
unbindService(serviceConnection);
}
@Override
public void onRefresh() {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
mSwipeRefreshLayout.setRefreshing(false);
}
},);
}
}
用戶端源碼比較簡單了,我們來重點看幾段代碼:
private void bindService() {
Intent intent = new Intent(this,MyService.class);
bindService(intent,serviceConnection, Context.BIND_AUTO_CREATE);
}
綁定我們的服務端Service,相信大家都懂得,在
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
isConnet = true;
mService = new Messenger(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
在這裡我們看到又建立了一個Messenger對象,我滴神額,咋又建立了一個,仔細一看,構造方法參數還不一樣,服務端我們建立的時候是傳入了一個Handler對象,這裡好像是一個IBinder對象诶 ~(≧▽≦)/~ 淚奔中……, 先不要慌,我們繼續來看代碼,細心的朋友可以看到其實我們用戶端是有兩個Messenger對象的,oh my god!!! 。加上服務端的不是又三個對象嗎,是的 确實是有三個Messenger對象。我們來梳理一下邏輯吧,可能有些朋友要瘋…………o(>﹏<)o。
按照上面的模型圖,要想實作這樣的通信方式,服務端和和用戶端必須要各自有一個Messenger對象才行,要不然沒法發送消息啊!用戶端隻有把自己的Messenger對象給服務端,服務端把自己的Messenger對象給用戶端,這樣在雙方中都會有彼此的引用對象,才可以發送消息,是不是呢~,至于為什麼用戶端要有兩個Messenger對象,是因為用戶端是主動發起方,它必須要先拿到服務端Messenger對象才行,這裡你可以把這個Messenger對象了解為服務端的Messenger。
三、源碼解析:
好,我們來看一下Messenger的内部源碼到底什麼怎樣的,首先看一下通過Handler對象建構的構造方法,也就是服務端的Messenger建立過程。
/**
* Create a new Messenger pointing to the given Handler. Any Message
* objects sent through this Messenger will appear in the Handler as if
* {@link Handler#sendMessage(Message) Handler.sendMessage(Message)} had
* been called directly.
*
* @param target The Handler that will receive sent messages.
*/
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
可以看到這裡通過Handler對象傳回了一個mTarget對象,那mTarget對象是什麼呢,我們找到mTarget的聲明
private final IMessenger mTarget;
它是一個IMessenger類型的對象。我們繼續看一下target.getIMessenger();内部是什麼樣的,繼續跟進去:
final IMessenger getIMessenger() {
synchronized (mQueue) {
if (mMessenger != null) {
return mMessenger;
}
mMessenger = new MessengerImpl();
return mMessenger;
}
}
這裡建立了一個 MessengerImpl類型的對象,來看看MessengerImpl是什麼:
private final class MessengerImpl extends IMessenger.Stub {
public void send(Message msg) {
msg.sendingUid = Binder.getCallingUid();
Handler.this.sendMessage(msg);
}
}
它是Handler的一個内部類,實作了
IMessenger.Stub
接口,等等…… 我怎麼看着這麼眼熟呢,沒錯,我們之前用Binder的時候也有一個類實作了xxxxx.Stub接口。莫非~沒錯,你想的沒錯,它和我們之前用的是一樣的。它同樣對應一個AIDL檔案。再回想一下我們服務端的onBinder方法,
messenger.getBinder();
我們看看這個方法内部
/**
* Retrieve the IBinder that this Messenger is using to communicate with
* its associated Handler.
*
* @return Returns the IBinder backing this Messenger.
*/
public IBinder getBinder() {
return mTarget.asBinder();
}
這下明白了吧,和我們之前用的Binder不是一樣嘛,對不對,嘿嘿,是不是有種~。。。。。。,
我們繼續看一下用戶端源碼:
我們重點看一下serviceConnection中的
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
isConnet = true;
mService = new Messenger(service);
}
看一下用IBinder 對象建立的Messenger過程:
/**
* 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);
}
這裡可以看到和我們之前寫的幾乎一模一樣,無非就是Messenger更進一步封裝了而已。
最後我們來看一下發送消息的過程:
無論是服務端還是用戶端發送消息都是用的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);
}
再調用的IMessenger的send方法,其實就是Handler裡面建立的MessengerImpl的send方法,我們看到在MessengerImpl的send方法裡面調用了Handler的sendMessage方法。這樣就可以通過Handler的消息循環模型來接收,發送消息了,進而實作了程序間的通信。說白了,Messenger通信方式就是Handler+Binder。
最後來一張效果圖:
源碼位址:
https://github.com/qqxliufeng/MessengerTestProject