【oschina android源码分析】聊天页面(私信)的设计

来源:互联网 发布:编程显示九九乘法表 编辑:程序博客网 时间:2024/05/21 10:09

一.总结

1.如何支持连续的消息发送,并且不会产生线程安全的问题

//存放正在发送的消息,key 为生成的一个临时messageID(msgTag),value为Message实体//当消息发送成功后,从mSendingMsgs删除对应的Message实体private SparseArray<MessageDetail> mSendingMsgs = new SparseArray<MessageDetail>();

2.一些分析

本设计应该是较为常见的聊天设计方案。其实近一年来,我也主要参与了公司聊天页面的开发和设计,本方案和我们公司的设计细节还是不太相符,不过整体的架构有很大的参考价值。
其实原理非常简单:就是一个listview,然后动态的修改listview中某个item的状态。

二.具体流程

1.点击发送按钮:

        if (!AppContext.getInstance().isLogin()) {            UIHelper.showLoginActivity(getActivity());            return;        }        if (StringUtils.isEmpty(str)) {            AppContext.showToastShort(R.string.tip_content_empty);            return;        }        MessageDetail message = new MessageDetail();        User user = AppContext.getInstance().getLoginUser();        int msgTag = mMsgTag++;        message.setId(msgTag);        message.setPortrait(user.getPortrait());        message.setAuthor(user.getName());        message.setAuthorId(user.getId());        message.setContent(str.toString());        sendMessage(message);

说明:
即每个消息都是有一个本地的id的,作为区分

2.发送消息调用的具体方法分析:

    /**     * 发送消息     * @param msg     */    private void sendMessage(MessageDetail msg){        msg.setStatus(MessageDetail.MessageStatus.SENDING);        Date date = new Date();        msg.setPubDate(net.oschina.app.util.StringUtils.getDateString(date));        //如果此次发表的时间距离上次的时间达到了 TIME_INTERVAL 的间隔要求,则显示时间        if(isNeedShowDate(date.getTime(),mLastShowDate)) {            msg.setShowDate(true);            mLastShowDate = date.getTime();        }        //如果待发送列表没有此条消息,说明是新消息,不是发送失败再次发送的,不需要再次添加        if(mSendingMsgs.indexOfKey(msg.getId())<0) {            mSendingMsgs.put(msg.getId(), msg);            mAdapter.addItem(0, msg);            mListView.setSelection(mListView.getBottom());        }else{            mAdapter.notifyDataSetChanged();        }        OSChinaApi.publicMessage(msg.getAuthorId(), mFid, msg.getContent(), new SendMessageResponseHandler(msg.getId()));    }

说明:

  • 如果待发送消息列表中没有这条消息,则把消息加入到待发送消息列表中,并刷新聊天列表。
  • 调用发送消息的网络请求接口。

3.发送接口具体实现操作:

 class SendMessageResponseHandler extends AsyncHttpResponseHandler{        private int msgTag;        public SendMessageResponseHandler(int msgTag) {            this.msgTag = msgTag;        }        @Override        public void onSuccess(int arg0, Header[] arg1, byte[] arg2) {            try {                ResultBean resb = XmlUtils.toBean(ResultBean.class,                        new ByteArrayInputStream(arg2));                Result res = resb.getResult();                if (res.OK()) {                    //从mSendingMsgs获取发送时放入的MessageDetail实体                    MessageDetail message = mSendingMsgs.get(this.msgTag);                    MessageDetail serverMsg = resb.getMessage();                    //把id设置为服务器返回的id                    message.setId(serverMsg.getId());                    message.setStatus(MessageDetail.MessageStatus.NORMAL);                    //从待发送列表移除                    mSendingMsgs.remove(this.msgTag);                    mAdapter.notifyDataSetChanged();                } else {                    error();                    AppContext.showToastShort(res.getErrorMessage());                }                emojiFragment.clean();            } catch (Exception e) {                e.printStackTrace();                onFailure(arg0, arg1, arg2, e);            }        }        @Override        public void onFailure(int arg0, Header[] arg1, byte[] arg2, Throwable arg3) {            error();        }        private void error(){            mSendingMsgs.get(this.msgTag).setStatus(MessageDetail.MessageStatus.ERROR);            mAdapter.notifyDataSetChanged();        }    }

说明:

  • 需要把消息的tag传递进来。
  • 发送成功的话,从mSendingMsgs获取发送时放入的MessageDetail实体,把id设置为服务器返回的id,并改变本地消息的状态,并从待发送列表中删除本地消息,刷新列表。是不会从服务端再拉取一遍的。
  • 发送失败的话,设置发送的消息的状态为失败,并刷新列表。

4.失败点击重发的实现方案:

采用的是接口回调的方案。
Adapter:

    public interface OnRetrySendMessageListener {        void onRetrySendMessage(int msgId);    }
        /**         * 重试发送         *         * @param v         */        @OnClick(R.id.itv_error)        void retry(View v) {            if (v.getTag() != null && mOnRetrySendMessageListener != null) {                mOnRetrySendMessageListener.onRetrySendMessage((int) v.getTag());            }        }    }

Fragment:该fragment实现了Adapter的重发接口

public class MessageDetailFragment extends BaseListFragment<MessageDetail> implements        OnItemLongClickListener, OnSendClickListener,MessageDetailAdapter.OnRetrySendMessageListener
adapter.setOnRetrySendMessageListener(this);
    @Override    public void onRetrySendMessage(int msgId) {        MessageDetail message = mSendingMsgs.get(msgId);        if (message != null) {            sendMessage(message);        }    }

5.每条消息的状态:

在实体中有一个发送状态的字段,通过不断修改该值来实现消息发送状态的切换。

0 0
原创粉丝点击