本文不分析AutoRefreshListView内部源码,从数据适配角度分析如何适配上文讲到的多种聊天消息; 既然从AutoRefreshListView开始,那先来了解下一般使用ListView的步骤:
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);
}
}
}