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

来源:互联网 发布:阿里云os系统电视 编辑:程序博客网 时间:2024/05/18 18:00

先看下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();  }

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

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

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

0 0
原创粉丝点击