本文不分析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); } } }