本文不分析AutoRefreshListView内部源码,从数据适配角度分析如何适配上文讲到的多种聊天消息;
既然从AutoRefreshListView开始,那先来了解下通常使用ListView的步骤:html
BaseAdapter<UIMessage>
,BaseAdapter<T>
泛型类重点分析下getView(int position, View convertView, ViewGroup parent)
方法;@Override public View getView(int position, View convertView, ViewGroup parent) { View view; if (convertView != null) { view = convertView; } else { view = this.newView(mContext, position, parent); } this.bindView(view, position, this.getItem(position)); return view; } protected abstract View newView(Context context, int pos, ViewGroup parent); protected abstract void bindView(View convertView, int pos, T t);
final View view = holder.contentView.inflate((IContainerItemProvider)provider); ((IContainerItemProvider)provider).bindView(view, position, data);
if(data != null) { final MessageListAdapter.ViewHolder holder = (MessageListAdapter.ViewHolder)v.getTag(); if(holder == null) { RLog.e("MessageListAdapter", "view holder is null !"); } else { Object provider;//声明provider对象 ProviderTag tag; //判断是不是评论消息 if(this.getNeedEvaluate(data)) { provider = RongContext.getInstance().getEvaluateProvider(); tag = RongContext.getInstance().getMessageProviderTag(data.getContent().getClass()); } else { if(RongContext.getInstance() == null || data == null || data.getContent() == null) { RLog.e("MessageListAdapter", "Message is null !"); return; } provider = RongContext.getInstance().getMessageTemplate(data.getContent().getClass()); if(provider == null) { provider = RongContext.getInstance().getMessageTemplate(UnknownMessage.class); tag = RongContext.getInstance().getMessageProviderTag(UnknownMessage.class); } else { tag = RongContext.getInstance().getMessageProviderTag(data.getContent().getClass()); } if(provider == null) { RLog.e("MessageListAdapter", data.getObjectName() + " message provider not found !"); return; } }
getMessageTemplate
方法,看是如何经过消息对象获取对应的消息provider*(MessageProvider)((MessageProvider)this.mTemplateMap.get(type)).clone();
这段代码发现与上面有什么不一样的地方;一个是HashMap对象换成了mTemplateMap,另外一个是调用了clone(因为实现了cloneable接口);Why?为何须要两个HashMap对象以及clone方法调用的缘由。
下面一步一步分析看,首先mTemplateMap对象数据哪里来的?这个是在融云创建链接成功回调之后添加的消息模板;算法
public MessageProvider getMessageTemplate(Class<? extends MessageContent> type) { MessageProvider provider = (MessageProvider)this.mWeakTemplateMap.get(type); if(provider == null) { try { if(this.mTemplateMap != null && this.mTemplateMap.get(type) != null) { provider = (MessageProvider)((MessageProvider)this.mTemplateMap.get(type)).clone(); this.mWeakTemplateMap.put(type, provider); } else { RLog.e("RongContext", "The template of message can\'t be null. type :" + type); } } catch (CloneNotSupportedException var4) { var4.printStackTrace(); } } return provider; }
public <T extends IContainerItemProvider> View inflate(T t)
与两个HashMap:mViewCounterMap--记录使用频率与mContentViewMap--缓存控件public <T extends IContainerItemProvider> View inflate(T t) { View result = null; if(this.mInflateView != null) { this.mInflateView.setVisibility(GONE); } if(this.mContentViewMap.containsKey(t.getClass())) { result = (View)this.mContentViewMap.get(t.getClass()); this.mInflateView = result; ((AtomicInteger)this.mViewCounterMap.get(t.getClass())).incrementAndGet(); } if(result != null) { if(result.getVisibility() == GONE) { result.setVisibility(VISIBLE); } return result; } else { this.recycle(); result = t.newView(this.getContext(), this); if(result != null) { super.addView(result); this.mContentViewMap.put(t.getClass(), result); this.mViewCounterMap.put(t.getClass(), new AtomicInteger()); } this.mInflateView = result; return result; } }
inflate方法
,根据传入的消息处理者类型,若是mContentViewMap中存在了对应控件,mViewCounterMap找到对应键并自动+1(这里的键的类型是AtomicInteger,自增或者自是减线程安全的),那数值大的表明最近刚刚使用过;若是mContentViewMap不存在的话,则把消息处理器添加到mContentViewMap与mViewCounterMap两个HashMap中,起到缓存做用。经过以上两步,使缓存效率获得优化。private void recycle() { if(this.mInflateView != null) { int count = this.getChildCount(); if(count >= this.mMaxContainSize) { Map.Entry min = null; Map.Entry item; for(Iterator view = this.mViewCounterMap.entrySet().iterator(); view.hasNext(); min = ((AtomicInteger)min.getValue()).get() > ((AtomicInteger)item.getValue()).get()?item:min) { item = (Map.Entry)view.next(); if(min == null) { min = item; } } this.mViewCounterMap.remove(min.getKey()); View view1 = (View)this.mContentViewMap.remove(min.getKey()); this.removeView(view1); } } }