package com.mogujie.tt.imlib; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import android.content.Intent; import android.os.Handler; import android.os.SystemClock; import com.mogujie.tt.config.SysConstant; import com.mogujie.tt.entity.MessageInfo; import com.mogujie.tt.imlib.db.IMDbManager; import com.mogujie.tt.log.Logger; public class IMUnAckMsgManager extends IMManager { private static IMUnAckMsgManager inst; public static IMUnAckMsgManager instance() { synchronized (IMUnAckMsgManager.class) { if (inst == null) { inst = new IMUnAckMsgManager(); } return inst; } } private class UnAckMsg { public UnAckMsg(MessageInfo msg, long timeoutElapsedRealtime) { this.msg = msg; this.timeoutElapsedRealtime = timeoutElapsedRealtime; } public MessageInfo msg; public long timeoutElapsedRealtime; } private Logger logger = Logger.getLogger(IMUnAckMsgManager.class); // todo eric, after testing ok, make it a longer value private final long TIMEOUT_MILLISECONDS = 6 * 1000; private final long IMAGE_TIMEOUT_MILLISECONDS = 4 * 60 * 1000; // key = msgId private HashMap<String, UnAckMsg> unackMsgList = new HashMap<String, UnAckMsg>(); private Handler timerHandler = new Handler(); private long getTimeoutTolerance(MessageInfo msgInfo) { if (msgInfo.isImage()) { return IMAGE_TIMEOUT_MILLISECONDS; } else { return TIMEOUT_MILLISECONDS; } } public synchronized void handleTimeoutUnAckMsg(String msgId) { if (msgId == null) { return; } logger.d("unack#handleTimeoutUnAckMsg ,msgId:%s", msgId); UnAckMsg unAckMsg = unackMsgList.get(msgId); if (unAckMsg == null) { logger.e("unack#so such message -> msgId:%s", msgId); return; } MessageInfo msg = unAckMsg.msg; handleTimeoutUnAckMsgImpl(msg); removeUnackMsg(msg.msgId); } private synchronized void removeUnackMsg(String msgId) { logger.d("unack#removeUnackMsg msgId:%s", msgId); unackMsgList.remove(msgId); } private void handleTimeoutUnAckMsgImpl(MessageInfo msg) { logger.d("unack#msg is unack timeout -> msg:%s", msg); msg.setMsgLoadState(SysConstant.MESSAGE_STATE_FINISH_FAILED); IMDbManager.instance(ctx).updateMessageStatus(msg); Intent intent = new Intent(IMActions.ACTION_MSG_UNACK_TIMEOUT); intent.putExtra(SysConstant.MSG_ID_KEY, msg.msgId); ctx.sendBroadcast(intent); logger.d("unack#broadcast is ok"); } // todo eric make locker more smaller private synchronized void timerImpl() { logger.d("unack#UnAckMsgTimeoutTimer run"); long currentElapsedRealtime = SystemClock.elapsedRealtime(); List<MessageInfo> toRemovedMsgList = new ArrayList<MessageInfo>(); for (java.util.Map.Entry<String, UnAckMsg> entry : unackMsgList.entrySet()) { UnAckMsg unAckMsg = entry.getValue(); // todo eric optimization if (currentElapsedRealtime >= unAckMsg.timeoutElapsedRealtime) { logger.d("unack#find timeout msg"); handleTimeoutUnAckMsgImpl(unAckMsg.msg); toRemovedMsgList.add(unAckMsg.msg); } } for (MessageInfo msg : toRemovedMsgList) { removeUnackMsg(msg.msgId); } } private void startTimer() { timerHandler.postDelayed(new Runnable() { @Override public void run() { timerImpl(); startTimer(); } }, 10 * 1000); } public synchronized void startUnAckTimeoutTimer() { logger.d("unack#startUnAckMsgTimeoutTimer"); startTimer(); } public synchronized void add(MessageInfo msgInfo) { logger.d("unack#add unack msg -> msgInfo:%s", msgInfo); String msgId = msgInfo.msgId; // todo eric efficiency if (unackMsgList.containsKey(msgId) || msgInfo.isResend()) { // for uploading image msg, it has already been added to the list when // uploading the image, and at the time of sending the msg to the // peer, reset the timer IMDbManager.instance(ctx).deleteMsg(msgId); removeUnackMsg(msgId); } IMDbManager.instance(ctx).saveMsg(msgInfo, true); unackMsgList.put(msgInfo.msgId, new UnAckMsg(msgInfo, SystemClock.elapsedRealtime() + getTimeoutTolerance(msgInfo))); } public synchronized MessageInfo remove(int msgSeqNo) { logger.d("unack#try to remove unack msg -> seqNo:%d", msgSeqNo); logger.d("unack#current unack msg cnt:%d", unackMsgList.size()); for (java.util.Map.Entry<String, UnAckMsg> entry : unackMsgList.entrySet()) { UnAckMsg unAckMsg = entry.getValue(); if (unAckMsg.msg.seqNo == msgSeqNo) { logger.d("unack#remove ok"); MessageInfo msgInfo = unAckMsg.msg; removeUnackMsg(unAckMsg.msg.msgId); return msgInfo; } } return null; } public synchronized MessageInfo get(String msgId) { logger.d("unack#get msgId:%s", msgId); UnAckMsg unAckMsg = unackMsgList.get(msgId); if (unAckMsg == null) { return null; } else { return unAckMsg.msg; } } }