您的位置:首页 > 移动开发

oschina-app源码分析-提醒标签BadgeView使用逻辑流程

2014-03-12 15:03 597 查看
先看下oschina-app里实现标签的效果图:



功能需求比较较简单,就是服务器有新的消息(文章、公告、评论等)就要通知客户端,并在相应的模块tab上显示标签,标签的显示方法上节已经讲过,这里主要讲标签实现逻辑。

主要流程:

1、初始化BadgeView:在标签显示页面初始化BadgeView控件。

2、获取提醒数据:起定时器,轮询请求服务器,获取需要提醒的消息数据;

3、发送消息广播:获取到消息数据后发送消息广播,通知需要显示标签的页面刷新。

4、标签页面刷新:广播接收者接收到广播后,刷新标签页面,显示状态栏通知。

5、更新服务器消息数据:查看某类信息的最新数据后,发请求通知服务器,该信息已经查看过,可标记为非新消息。

以前弄的一个项目里,设计有个漏洞,就是缺少第5步,在第2部返回数据的时候就把服务器的数据标记成非新信息。其实是一种概念性错误,消息发给给用户了,不能证明就是旧的了,只有用户看过了,对用户来说才是旧的消息。

1、初始化BadgeView

oschina里,显示标签的页面是主界面,在主界面创建的时候就要初始化BadgeView控件:

public static BadgeView bv_active;
	public static BadgeView bv_message;
	public static BadgeView bv_atme;
	public static BadgeView bv_review;
	/**
	 * 初始化通知信息标签控件
	 */
	private void initBadgeView() {
		bv_active = new BadgeView(this, fbactive);
		bv_active.setBackgroundResource(R.drawable.widget_count_bg);
		bv_active.setIncludeFontPadding(false);
		bv_active.setGravity(Gravity.CENTER);
		bv_active.setTextSize(8f);
		bv_active.setTextColor(Color.WHITE);

		bv_atme = new BadgeView(this, framebtn_Active_atme);
		bv_atme.setBackgroundResource(R.drawable.widget_count_bg);
		bv_atme.setIncludeFontPadding(false);
		bv_atme.setGravity(Gravity.CENTER);
		bv_atme.setTextSize(8f);
		bv_atme.setTextColor(Color.WHITE);

		bv_review = new BadgeView(this, framebtn_Active_comment);
		bv_review.setBackgroundResource(R.drawable.widget_count_bg);
		bv_review.setIncludeFontPadding(false);
		bv_review.setGravity(Gravity.CENTER);
		bv_review.setTextSize(8f);
		bv_review.setTextColor(Color.WHITE);

		bv_message = new BadgeView(this, framebtn_Active_message);
		bv_message.setBackgroundResource(R.drawable.widget_count_bg);
		bv_message.setIncludeFontPadding(false);
		bv_message.setGravity(Gravity.CENTER);
		bv_message.setTextSize(8f);
		bv_message.setTextColor(Color.WHITE);
	}

共定义了4个标签,对应四个提醒模块;初始化完成后,只要控制他的显示与否和内容就可以了。

标签定义成静态的,目的是在广播接收者里更方便的访问到,4个标签控件,例如显示一个标签:Main.bv_active.show();

2、获取提醒数据

oschina,是在主界面里,启动一个线程定时请求消息数据,通过hander消息机制传递给UI主线程,发送消息广播,看下代码:

/**
	 * 轮询通知信息
	 */
	private void foreachUserNotice() {
		final int uid = appContext.getLoginUid();
		final Handler handler = new Handler() {
			public void handleMessage(Message msg) {
				if (msg.what == 1) {
					UIHelper.sendBroadCast(Main.this, (Notice) msg.obj);
				}
				foreachUserNotice();// 回调
			}
		};
		new Thread() {
			public void run() {
				Message msg = new Message();
				try {
					sleep(60 * 1000);
					if (uid > 0) {
						Notice notice = appContext.getUserNotice(uid);
						msg.what = 1;
						msg.obj = notice;
					} else {
						msg.what = 0;
					}
				} catch (AppException e) {
					e.printStackTrace();
					msg.what = -1;
				} catch (Exception e) {
					e.printStackTrace();
					msg.what = -1;
				}
				handler.sendMessage(msg);
			}
		}.start();
	}

Notice notice = appContext.getUserNotice(uid);是公共http请求获取网络消息数据,这个不在赘述。

这个地方我有点疑问,这种后台运行的操作,为什么不放在service里?这样在Main里起线程,等Main退出以后,这个不是很容易被搞死吗?

除了上面的轮询请求消息数据意外,oschina里其他获得数据的接口、提交数据的接口,也返回来Notice消息并发送消息广播,目的是提高消息的及时性。

我认为这种消息提醒功能,最好还是做成主动推的方式,类似apple提供的推送服务,由于google的服务在大陆不稳定,所以有时间可以研究下开源的xmpp。

3、发送广播

* 发送通知广播
	 * 
	 * @param context
	 * @param notice
	 */
	public static void sendBroadCast(Context context, Notice notice) {
		if (!((AppContext) context.getApplicationContext()).isLogin()
				|| notice == null)
			return;
		Intent intent = new Intent("net.oschina.app.action.APPWIDGET_UPDATE");
		intent.putExtra("atmeCount", notice.getAtmeCount());
		intent.putExtra("msgCount", notice.getMsgCount());
		intent.putExtra("reviewCount", notice.getReviewCount());
		intent.putExtra("newFansCount", notice.getNewFansCount());
		context.sendBroadcast(intent);
	}



4、标签页面刷新

在广播接收这里实现页面刷新,主要是通过消息的数量来控制主页面标签的显示和隐藏,还有状态栏的通知是否显示:

/**
 * 通知信息广播***
 * @author liux (http://my.oschina.net/liux)
 * @version 1.0
 * @created 2012-4-16
 */
public class BroadCast extends BroadcastReceiver {

	private final static int NOTIFICATION_ID = R.layout.main;
	
	private static int lastNoticeCount;
	
	@Override
	public void onReceive(Context context, Intent intent) {
		String ACTION_NAME = intent.getAction();
		if("net.oschina.app.action.APPWIDGET_UPDATE".equals(ACTION_NAME))
		{	
			int atmeCount = intent.getIntExtra("atmeCount", 0);//@我
			int msgCount = intent.getIntExtra("msgCount", 0);//留言
			int reviewCount = intent.getIntExtra("reviewCount", 0);//评论
			int newFansCount = intent.getIntExtra("newFansCount", 0);//新粉丝
			int activeCount = atmeCount + reviewCount + msgCount + newFansCount;//信息总数
			
			//动态-总数
			if(Main.bv_active != null){
				if(activeCount > 0){
					Main.bv_active.setText(activeCount+"");
					Main.bv_active.show();
				}else{
					Main.bv_active.setText("");
					Main.bv_active.hide();
				}
			}
			//@我
			if(Main.bv_atme != null){
				if(atmeCount > 0){
					Main.bv_atme.setText(atmeCount+"");
					Main.bv_atme.show();
				}else{
					Main.bv_atme.setText("");
					Main.bv_atme.hide();
				}
			}
			//评论
			if(Main.bv_review != null){
				if(reviewCount > 0){
					Main.bv_review.setText(reviewCount+"");
					Main.bv_review.show();
				}else{
					Main.bv_review.setText("");
					Main.bv_review.hide();
				}
			}
			//留言
			if(Main.bv_message != null){
				if(msgCount > 0){
					Main.bv_message.setText(msgCount+"");
					Main.bv_message.show();
				}else{
					Main.bv_message.setText("");
					Main.bv_message.hide();
				}
			}
			
			//通知栏显示
			this.notification(context, activeCount);
		}
	}

	private void notification(Context context, int noticeCount){		
		//创建 NotificationManager
		NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
		
		String contentTitle = "开源中国";
		String contentText = "您有 " + noticeCount + " 条最新信息";
		int _lastNoticeCount;
		
		//判断是否发出通知信息
		if(noticeCount == 0)
		{
			notificationManager.cancelAll();
			lastNoticeCount = 0;
			return;
		}
		else if(noticeCount == lastNoticeCount)
		{
			return; 
		}
		else
		{
			_lastNoticeCount = lastNoticeCount;
			lastNoticeCount = noticeCount;
		}
		
		//创建通知 Notification
		Notification notification = null;
		
		if(noticeCount > _lastNoticeCount) 
		{
			String noticeTitle = "您有 " + (noticeCount-_lastNoticeCount) + " 条最新信息";
			notification = new Notification(R.drawable.icon, noticeTitle, System.currentTimeMillis());
		}
		else
		{
			notification = new Notification();
		}
		
		//设置点击通知跳转
		Intent intent = new Intent(context, Main.class);
		intent.putExtra("NOTICE", true);
		intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_NEW_TASK); 
		
		PendingIntent contentIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
		
		//设置最新信息
		notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);
		
		//设置点击清除通知
		notification.flags = Notification.FLAG_AUTO_CANCEL;
		
		if(noticeCount > _lastNoticeCount) 
		{
			//设置通知方式
			notification.defaults |= Notification.DEFAULT_LIGHTS;
			
			//设置通知音-根据app设置是否发出提示音
			if(((AppContext)context.getApplicationContext()).isAppSound())
				notification.sound = Uri.parse("android.resource://" + context.getPackageName() + "/" + R.raw.notificationsound);
			
			//设置振动 <需要加上用户权限android.permission.VIBRATE>
			//notification.vibrate = new long[]{100, 250, 100, 500};
		}
		
		//发出通知
		notificationManager.notify(NOTIFICATION_ID, notification);		
	}
	
}

每次刷新页面除了确保标签显示,还要确保改隐藏的标签隐藏掉。

当没有消息提醒时要取消状态栏通知:notificationManager.cancelAll();



5、更新服务器消息数据

当用户已经看过新的消息后,则要通知服务器更新数据。一般是在第一次加载数据和刷新数据的时候,去更新服务器消息数据,因为这两个操作是获得当前最新的该类信息。



/**
	 * 通知信息处理
	 * 
	 * @param type
	 *            1:@我的信息 2:未读消息 3:评论个数 4:新粉丝个数
	 */
	private void ClearNotice(final int type) {
		final int uid = appContext.getLoginUid();
		final Handler handler = new Handler() {
			public void handleMessage(Message msg) {
				if (msg.what == 1 && msg.obj != null) {
					Result res = (Result) msg.obj;
					if (res.OK() && res.getNotice() != null) {
						UIHelper.sendBroadCast(Main.this, res.getNotice());
					}
				} else {
					((AppException) msg.obj).makeToast(Main.this);
				}
			}
		};
		new Thread() {
			public void run() {
				Message msg = new Message();
				try {
					Result res = appContext.noticeClear(uid, type);
					msg.what = 1;
					msg.obj = res;
				} catch (AppException e) {
					e.printStackTrace();
					msg.what = -1;
					msg.obj = e;
				}
				handler.sendMessage(msg);
			}
		}.start();
	}

更新服务器消息数据,其实也是一个获取消息数据的过程,因为要获得更新后的消息数据,并发出消息广播。

更新数据时候,只需更新当前类别的数据即可,如果你刷新了新闻页,你只要告诉服务器你已经看过了最新的新闻。

整个消息标签显示流程就讲完了,一般自己写也是这个思路,就是有的地方想的不够全面,在这里记录下容易忽略的地方。



完整源码下载:http://download.csdn.net/detail/xiangxue336/7023661
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: