全栈工场实训13---Android---消息总线机制

昨天发表的博文讲述了Android中,采用异步任务进行网络请求的内容,在异步任务结束时,采用Handler机制通知原来的Activity进行界面更新,网友 traburiss指出,异步任务的onPostExecute已经在UI线程中了,再用Handler等于要到下一个UI运行周期才能执行,效率会降低不少,而且违反了异步任务的本意。感谢 traburiss的意见,他说得非常正确,我之所以用到Handler,是因为要在Android应用开发中引入消息总线的概念,想基于Handler来做,所以才使用了这个技术,看来是不恰当的。所以在本篇博文中,我把涉及消息总线实现部分,一起讲出来,这样就避免了网友的提出的问题。

首先介绍一下消息总线,消息总线指系统发生的事件,如收到用户注册成功的异步任务完成消息,系统将消息放到消息总线上,对这个消息感兴趣的应用组件,可以订阅这个消息总线,这样当消息发生时,这些组件会得到通知,从而完成消应的操作。引入消息总线技术,其主要优点是可以实现组件间的松耦合,消息生产者不用关心哪个组件会使用这个消息,只需将产生的消息放到消息总线上即可。而消息消费者订阅这个消息总线,当消息发生时,就可以进行相应的处理了。

我们先来看消息总线的实现机制,代码如下所示:

public class WkyMessageBus {
	public static void prepareEventBus() {
		messageBus = new HashMap<String, HashMap<String, Handler>>();
		// 将所有消息类型加到消息总线上
		HashMap<String, Handler> registerUserListeners = new HashMap<String, Handler>();
		messageBus.put("" + WkyConstants.MSG_WHAT_REGISTER_USER, registerUserListeners);
	}
	
	public static void registerToMessageBus(int messageTypeId, String listenerName, Handler handler) {
		HashMap<String, Handler> listeners = messageBus.get("" + messageTypeId);
		listeners.put(listenerName, handler);
	}
	
	public static void unregisterToMessageBus(int messageTypeId, String listenerName) {
		HashMap<String, Handler> listeners = messageBus.get("" + messageTypeId);
		listeners.remove(listenerName);
	}
	
	public static void postMessage(Message msg) {
		HashMap<String, Handler> handlers = messageBus.get("" + msg.what);
		for (Handler handler : handlers.values()) {
			handler.sendMessage(msg);
		}
	}
	private static HashMap<String, HashMap<String ,Handler>> messageBus = null;
}


如上面代码所示,用messageBus来代表消息总线的集合,Android的Message对象的what值变为字符串作为key,其值为一个列表,列表元素为Handler,通过该handler可以向Activity发送消息,通知相应Activity进行相关操作。

Activity通过registerToMessageBus方法,订阅到消息总线。在Activity销毁时调用unregisterToMessageBus方法,从消息总线上注销。

消息生产者产生消息后,通过postMessage将消息发布到消息总线上来。

在应用启动时,即应用的Application对象的onCreate方法中,初始化消息总线。

WkyMessageBus.prepareEventBus();

如上篇文章所述,当异步任务结束时,会发送消息到消息总线:

	/**
	 * 异步任务结束时要调用的方法,通知页面进行更新
	 * 【闫涛 2015.09.24】初始版本
	 */
	@Override
	protected void onPostExecute(String result) {
		JSONObject json = null;
		long userId = 0;
		try {
			json = new JSONObject(result);
			userId = json.getLong("userId");
		} catch (JSONException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		WkyRegisterLoginModel model = (WkyRegisterLoginModel)activity.getModel();
		model.setUserId(userId);
		activity.onAsyncTaskResult();
		Message msg = handler.obtainMessage();
		msg.what = WkyConstants.MSG_WHAT_REGISTER_USER;
		Bundle params = new Bundle();
		params.putString(WkyConstants.MSG_DATA_NAME, result);
		msg.setData(params);
		WkyMessageBus.postMessage(msg);
	}

这段代码有三个部分,第一部分是更新对应的Model对象中的数据,第二个部分是调用WkyActivity基类所定义JysRegisterLoginActivity重载的onAsyncTaskResult方法,实现界面的更新,第三个部分是产生一个Message对象,并发送到消息总线上去。

JysRegisterLoginActivity在启动时,注册到消息总线上去,在销毁时从消息总线注销,代码如下所示:

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(com.weikangyun.wkylib.R.layout.activity_register_login);
		handler = new JysRegisterLoginHandler(this);
		messageBusListenerName = this.getClass().getCanonicalName() + System.currentTimeMillis();
		WkyMessageBus.registerToMessageBus(WkyConstants.MSG_WHAT_REGISTER_USER, 
				messageBusListenerName, handler);
		getViewObjects();
		setupGuis();
		setupActionListeners();
	}	
	
	@Override
	public void onDestroy() {
		super.onDestroy();
		WkyMessageBus.unregisterToMessageBus(WkyConstants.MSG_WHAT_REGISTER_USER, messageBusListenerName);
	}

当系统产生消息,会向Activity发送消息,其消息处理如下所示:

	static protected class JysRegisterLoginHandler extends WkyRegisterLoginHandler {
		public JysRegisterLoginHandler(JysRegisterLoginActivity activity) {
			this.activity = activity;
		}
		
		public void handleMessage(Message msg) {
			super.handleMessage(msg);
			// 自己额外的处理
			switch (msg.what) {
			case WkyConstants.MSG_WHAT_REGISTER_USER:
				activity.processRegisterUserResult(msg);
				break;
			}
		}
		private JysRegisterLoginActivity activity = null;
	}

上面其实还遗留了一个问题,就是异步消息结束时更新界面的问题。我们在应用的公共基Activity类WkyActivity中,定义了异步任务回调函数onAsyncTaskResult方法,如下所示:

	/**
	 * 当异步任务完成后,会回调本方法,执行具体的页面更新操作。需要实现两部分功能:
	 * 1. 异步任务:在onPostExecute函数中,将结果放到Activity对应的Model中
	 * 2. Activity中:从Model中取出数据,更新界面
	 * 【闫涛 2015.12.04】初始版本
	 */
	public void onAsyncTaskResult() {
	}

在具体的Activity类里,编写界面更新函数。注意,在异步任务结束的方法onPostExecute方法中,我们已经将所需数据更新到Model中,所以onAsyncTaskResult方法只需从Model中读出数据进行显示就可以了。

这里在补充一个问题,我们的网络请求为什么采用异步任务,而不是直接采用线程技术呢?是因为异步任务封装了线程与UI线程之间的交互吗?其实这只是其中的一个方面。因为在异步任务背后,是系统管理的线程池,系统会根据CPU核数,当前负载等因素,给出合适的线程解决方案(启动新线程还是复用老线程)。而自己启动线程的方案,由于不能获取上述信息,所以不可能进行任何系统级的优化。因此,建议在能用异步任务的情况下,还是尽量用异步任务来解决问题。


华丽的分隔线
******************************************************************************************************************************************************************************
希望大家多支持,有大家的支持,我才能走得更远,谢谢!
银行账号:622202 0200 1078 56128 闫涛
我的支付宝:yt7589@hotmail.com










  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值