You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@weex.apache.org by mi...@apache.org on 2018/02/08 08:57:09 UTC

[2/7] incubator-weex git commit: [WEEX-174][android] TemplateList Support Lifecycle and statefull component

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/886d859a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/WXRecyclerTemplateList.java
----------------------------------------------------------------------
diff --git a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/WXRecyclerTemplateList.java b/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/WXRecyclerTemplateList.java
index 51a4874..d8200a2 100644
--- a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/WXRecyclerTemplateList.java
+++ b/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/WXRecyclerTemplateList.java
@@ -22,10 +22,8 @@ import android.annotation.TargetApi;
 import android.content.Context;
 import android.graphics.Point;
 import android.graphics.PointF;
-import android.os.AsyncTask;
 import android.os.Build;
 import android.os.Looper;
-import android.os.MessageQueue;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.v4.util.ArrayMap;
@@ -60,6 +58,7 @@ import com.taobao.weex.dom.flex.CSSLayoutContext;
 import com.taobao.weex.dom.flex.Spacing;
 import com.taobao.weex.el.parse.ArrayStack;
 import com.taobao.weex.ui.component.AppearanceHelper;
+import com.taobao.weex.ui.component.ComponentUtils;
 import com.taobao.weex.ui.component.Scrollable;
 import com.taobao.weex.ui.component.WXBaseRefresh;
 import com.taobao.weex.ui.component.WXComponent;
@@ -84,12 +83,10 @@ import com.taobao.weex.utils.WXViewUtils;
 
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentLinkedQueue;
 
 import static com.taobao.weex.common.Constants.Name.LOADMOREOFFSET;
 
@@ -101,8 +98,15 @@ import static com.taobao.weex.common.Constants.Name.LOADMOREOFFSET;
 @Component(lazyload = false)
 public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> implements
         IRecyclerAdapterListener<TemplateViewHolder>, IOnLoadMoreListener, Scrollable {
+
+    /**
+     * trace log for template list
+     * */
+    public static final boolean ENABLE_TRACE_LOG = false;
+
     public static final String TAG = "WXRecyclerTemplateList";
 
+    private static final String EMPTY_HOLDER_TEMPLATE_KEY  = "";
     private static final String NAME_HAS_FIXED_SIZE = "hasFixedSize";
     private static final String NAME_ITEM_VIEW_CACHE_SIZE = "itemViewCacheSize";
     private static final String NAME_TEMPLATE_CACHE_SIZE = "templateCacheSize";
@@ -134,7 +138,9 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp
     private Point mLastReport = new Point(-1, -1);
     private boolean mHasAddScrollEvent = false;
 
-    private JSONArray listData;
+
+    private CellDataManager cellDataManager;
+
     private String listDataKey = Constants.Name.Recycler.LIST_DATA;
     private String listDataItemKey = null;
     private String listDataIndexKey = null;
@@ -143,13 +149,19 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp
 
 
     private Map<String, WXCell> mTemplateSources;
-    private String  listDataTemplateKey = Constants.Name.Recycler.SLOT_TEMPLATE_TYPE;
+    private String  listDataTemplateKey = Constants.Name.Recycler.SLOT_TEMPLATE_CASE;
     private Runnable listUpdateRunnable;
     private ConcurrentHashMap<String, TemplateCache> mTemplatesCache;
     private int templateCacheSize = 2;
 
 
     /**
+     * case default cell and key
+     * */
+    private WXCell defaultTemplateCell;
+    private String defaultTemplateKey = "@default_template_cell";
+
+    /**
      * scroll start and scroll end event
      * */
     private ScrollStartEndHelper mScrollStartEndHelper;
@@ -175,9 +187,7 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp
      * */
     private ArrayMap<Integer, Map<String,Map<Integer, List<Object>>>> mDisAppearWatchList = new ArrayMap<>();
 
-    private ArrayStack bindIngStackContext = new ArrayStack();
-    private Map bindIngMapContext = new HashMap();
-
+    private CellRenderContext cellRenderContext = new CellRenderContext();
 
     public WXRecyclerTemplateList(WXSDKInstance instance, WXDomObject node, WXVContainer parent) {
         super(instance, node, parent);
@@ -192,18 +202,19 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp
             updateRecyclerAttr();
         }
         mTemplateViewTypes = new ArrayMap<>();
-        mTemplateViewTypes.put("", 0); //empty view, when template was not sended
+        mTemplateViewTypes.put(EMPTY_HOLDER_TEMPLATE_KEY, 0); //empty view, when template was not sended
         mTemplateSources = new HashMap<>();
         mTemplatesCache = new ConcurrentHashMap<>();
         mStickyHelper = new TemplateStickyHelper(this);
         orientation = mDomObject.getOrientation();
-        listDataTemplateKey = WXUtils.getString(getDomObject().getAttrs().get(Constants.Name.Recycler.LIST_DATA_TEMPLATE_KEY), Constants.Name.Recycler.SLOT_TEMPLATE_TYPE);
+        listDataTemplateKey = WXUtils.getString(getDomObject().getAttrs().get(Constants.Name.Recycler.LIST_DATA_TEMPLATE_SWITCH_KEY), Constants.Name.Recycler.SLOT_TEMPLATE_CASE);
         listDataItemKey = WXUtils.getString(getDomObject().getAttrs().get(Constants.Name.Recycler.LIST_DATA_ITEM), listDataItemKey);
         listDataIndexKey = WXUtils.getString(getDomObject().getAttrs().get(Constants.Name.Recycler.LIST_DATA_ITEM_INDEX), listDataIndexKey);
-        if( getDomObject().getAttrs().get(Constants.Name.Recycler.LIST_DATA) instanceof  JSONArray) {
+        cellDataManager = new CellDataManager(this);
+        if(getDomObject().getAttrs().get(Constants.Name.Recycler.LIST_DATA) instanceof  JSONArray) {
             JSONArray array = (JSONArray)getDomObject().getAttrs().get(Constants.Name.Recycler.LIST_DATA);
             if(array.size() > 0) {
-                listData = array;
+                cellDataManager.listData = array;
             }
         }
         /**
@@ -211,23 +222,28 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp
          * list has layout and can archive better user experience and better load time,
          * which reduce waste cell layout when list layout changes.
          * */
-        WXSDKManager.getInstance().getWXDomManager().post(new Runnable() {
-            @Override
-            public void run() {
-                if(isDestoryed()){
-                    return;
-                }
-                long start = System.currentTimeMillis();
-                if(mDomObject != null && mDomObject.getCellList() != null){
-                    for(int i=0; i<mDomObject.getCellList().size(); i++){
-                        addChild(DomTreeBuilder.buildTree(mDomObject.getCellList().get(i),  WXRecyclerTemplateList.this));
+        if(mDomObject != null
+                && mDomObject.getCellList() != null
+                && mDomObject.getCellList().size() > 0){
+            Runnable runnable =  new Runnable() {
+                // @Override
+                public void run() {
+                    if(isDestoryed()){
+                        return;
+                    }
+                    long start = System.currentTimeMillis();
+                    if(mDomObject != null && mDomObject.getCellList() != null){
+                        for(int i=0; i<mDomObject.getCellList().size(); i++){
+                            addChild(ComponentUtils.buildTree(mDomObject.getCellList().get(i),  WXRecyclerTemplateList.this));
+                        }
+                    }
+                    if(WXEnvironment.isOpenDebugLog() && ENABLE_TRACE_LOG){
+                        WXLogUtils.d(TAG, "TemplateList BuildDomTree Used " + (System.currentTimeMillis() - start));
                     }
                 }
-                if(WXEnvironment.isApkDebugable()){
-                    WXLogUtils.d(TAG, "TemplateList BuildDomTree Used " + (System.currentTimeMillis() - start));
-                }
-            }
-        });
+            };
+            WXSDKManager.getInstance().getWXDomManager().post(runnable);
+        }
     }
 
     @Override
@@ -331,15 +347,17 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp
                  * compute sticky position
                  * */
                 if(mStickyHelper != null){
-                    mStickyHelper.getStickyPositions().clear();
-                    if(listData != null){
-                        for(int i=0; i<listData.size(); i++){
-                            WXCell cell = getSourceTemplate(i);
-                            if(cell == null){
-                                continue;
-                            }
-                            if(cell.isSticky()){
-                                mStickyHelper.getStickyPositions().add(i);
+                    if(mStickyHelper.getStickyTypes().size() > 0){
+                        mStickyHelper.getStickyPositions().clear();
+                        if(cellDataManager.listData != null){
+                            for(int i = 0; i< cellDataManager.listData.size(); i++){
+                                WXCell cell = getSourceTemplate(i);
+                                if(cell == null){
+                                    continue;
+                                }
+                                if(cell.isSticky()){
+                                    mStickyHelper.getStickyPositions().add(i);
+                                }
                             }
                         }
                     }
@@ -347,7 +365,7 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp
                 if(getHostView() != null && getHostView().getRecyclerViewBaseAdapter() != null){
                     getHostView().getRecyclerViewBaseAdapter().notifyDataSetChanged();
                 }
-                if(WXEnvironment.isApkDebugable()){
+                if(WXEnvironment.isOpenDebugLog() && ENABLE_TRACE_LOG){
                     WXLogUtils.d(TAG, "WXTemplateList notifyDataSetChanged");
                 }
             }
@@ -390,7 +408,7 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp
         if(template == null){
             return;
         }
-        if(listData == null || mStickyHelper == null){
+        if(cellDataManager.listData == null || mStickyHelper == null){
             return;
         }
         if(!mStickyHelper.getStickyTypes().contains(template.getRef())){
@@ -403,7 +421,7 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp
     public void unbindStickStyle(WXComponent component) {
         WXComponent  template = findParentType(component, WXCell.class);
         if(template == null
-                || listData == null
+                || cellDataManager.listData == null
                 || mStickyHelper == null){
             return;
         }
@@ -425,14 +443,14 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp
     }
 
     private void setAppearanceWatch(WXComponent component, int event, boolean enable) {
-        if(listData == null
+        if(cellDataManager.listData == null
                 || mAppearHelpers == null
                 || TextUtils.isEmpty(component.getRef())){
             return;
         }
-        WXComponent cell = findCell(component);
-        int type = getCellItemType(cell);
-        if(type <= 0){
+        WXCell cell = findCell(component);
+        int type = getCellTemplateItemType(cell);
+        if(type < 0){
             return;
         }
         List<AppearanceHelper>  mAppearListeners = mAppearHelpers.get(type);
@@ -517,9 +535,9 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp
         }
         WXCell cell = findCell(component);
         if(typeIndex >= 0){
-            if(listData != null && component.getRef() != null){
+            if(cellDataManager.listData != null && component.getRef() != null){
                 int typePosition = 0;
-                for(int i=0; i<listData.size(); i++){
+                for(int i = 0; i< cellDataManager.listData.size(); i++){
                     WXCell template = getSourceTemplate(i);
                     if(template == null){
                         continue;
@@ -533,7 +551,7 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp
                     }
                 }
                 if(position < 0){
-                    position = listData.size() - 1;
+                    position = cellDataManager.listData.size() - 1;
                 }
             }
         }
@@ -585,6 +603,9 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp
 
     @Override
     public void addChild(WXComponent child, int index) {
+        /**
+         *  dom object in component is not tree, build tree
+         * */
         if(!(child instanceof  WXCell)) {
             super.addChild(child, index);
         }
@@ -593,8 +614,30 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp
         }
         if(child instanceof WXCell){
             if(child.getDomObject() != null && child.getDomObject().getAttrs() != null){
-                Object templateId = child.getDomObject().getAttrs().get(Constants.Name.Recycler.SLOT_TEMPLATE_TYPE);
+                Object templateId = child.getDomObject().getAttrs().get(Constants.Name.Recycler.SLOT_TEMPLATE_CASE);
                 String key = WXUtils.getString(templateId, null);
+                if(getDomObject().getAttrs().containsKey(Constants.Name.Recycler.LIST_DATA_TEMPLATE_SWITCH_KEY)){
+                    if(defaultTemplateCell == null){
+                        defaultTemplateCell = (WXCell) child;
+                        if(!TextUtils.isEmpty(key)){
+                            defaultTemplateKey = key;
+                        }else{
+                            key = defaultTemplateKey;
+                            child.getDomObject().getAttrs().put(Constants.Name.Recycler.SLOT_TEMPLATE_CASE, key);
+                        }
+                    }
+                }else{
+                    if(defaultTemplateCell == null
+                            || child.getDomObject().getAttrs().containsKey(Constants.Name.Recycler.SLOT_TEMPLATE_DEFAULT)){
+                        defaultTemplateCell = (WXCell) child;
+                        if(!TextUtils.isEmpty(key)){
+                            defaultTemplateKey = key;
+                        }else{
+                            key = defaultTemplateKey;
+                            child.getDomObject().getAttrs().put(Constants.Name.Recycler.SLOT_TEMPLATE_CASE, key);
+                        }
+                    }
+                }
                 if(key != null){
                     if(child.getDomObject() instanceof  WXCellDomObject
                             && getDomObject() instanceof  WXRecyclerDomObject){
@@ -602,7 +645,7 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp
                         domObject.setRecyclerDomObject((WXRecyclerDomObject) getDomObject());
                     }
                     mTemplateSources.put(key, (WXCell) child);
-                    ensureSourceCellRenderWithData((WXCell)child);
+                    renderTemplateCellWithData((WXCell)child);
                     if(mTemplateViewTypes.get(key) == null){
                         mTemplateViewTypes.put(key, mTemplateViewTypes.size());
                     }
@@ -614,6 +657,8 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp
 
 
 
+
+
     /**
      * RecyclerView manage its children in a way that different from {@link WXVContainer}. Therefore,
      * {@link WXVContainer#addSubView(View, int)} is an empty implementation in {@link
@@ -671,9 +716,9 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp
             case Constants.Name.Recycler.LIST_DATA_ITEM_INDEX:
                 listDataIndexKey = WXUtils.getString(param, listDataIndexKey);
                 return true;
-            case Constants.Name.Recycler.LIST_DATA_TEMPLATE_KEY:
-            case Constants.Name.Recycler.SLOT_TEMPLATE_TYPE:
-                listDataTemplateKey = WXUtils.getString(param, Constants.Name.Recycler.SLOT_TEMPLATE_TYPE);
+            case Constants.Name.Recycler.LIST_DATA_TEMPLATE_SWITCH_KEY:
+            case Constants.Name.Recycler.SLOT_TEMPLATE_CASE:
+                listDataTemplateKey = WXUtils.getString(param, Constants.Name.Recycler.SLOT_TEMPLATE_CASE);
                 return true;
             case LOADMOREOFFSET:
                 return true;
@@ -761,68 +806,112 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp
 
     @JSMethod
     public void setListData(Object param){
-        boolean update = listData != null &&  listData != param;
+        boolean update = cellDataManager.listData != param;
         if(param instanceof  JSONArray){
-            listData = (JSONArray) param;
-        }
-        if(update){
-            notifyUpdateList();
+            if(update){
+                cellDataManager.setListData((JSONArray) param);
+                notifyUpdateList();
+            }
         }
     }
 
     @JSMethod
-    public void  appendData(JSONArray arrayObject){
-        if(listData == null){
-            listData = new JSONArray();
+    public void  appendData(JSONArray data){
+        if(data == null || data.size() == 0){
+            return;
+        }
+        if(cellDataManager.listData == null){
+            cellDataManager.listData = new JSONArray();
+        }
+        int position = cellDataManager.listData.size();
+        if(position < 0){
+            position = 0;
         }
-        if(arrayObject instanceof  JSONArray){
-            listData.addAll(arrayObject);
+        if(data instanceof  JSONArray){
+            cellDataManager.listData.addAll(data);
         }
-        notifyUpdateList();
+        getHostView().getRecyclerViewBaseAdapter().notifyItemRangeInserted(position, data.size());
     }
 
     @JSMethod
-    public void  insertData(JSONObject data, int index){
+    public void  insertData(int index, Object data){
         if(data == null){
             return;
         }
-        if(listData == null || index >= listData.size()){
+
+        if(cellDataManager.listData == null || index > cellDataManager.listData.size()){
             return;
         }
-        listData.add(index, data);
-        notifyUpdateList();
+        boolean renderStateChanged = cellDataManager.insertData(index, data);
+        if(renderStateChanged){
+            notifyUpdateList();
+        }else{
+            getHostView().getRecyclerViewBaseAdapter().notifyItemInserted(index);
+        }
+    }
+
+    @JSMethod
+    public void  appendRange(int index, JSONArray data){
+         insertRange(index, data);
     }
 
+
+    /**
+     * when update data, list maybe contains sticky, may use position, when position changed should be rendered
+     * so use notifyUpdateList is better
+     * */
+
     @JSMethod
-    public void  updateData(JSONObject data, int index){
+    public void  insertRange(int index, JSONArray data){
+        if(data == null || data.size() == 0){
+            return;
+        }
+        if(cellDataManager.listData == null || index > cellDataManager.listData.size()){
+            return;
+        }
+        boolean renderStateChange = cellDataManager.insertRange(index, data);
+        if(renderStateChange){
+            notifyUpdateList();
+        }else{
+            getHostView().getRecyclerViewBaseAdapter().notifyItemRangeInserted(index, data.size());
+        }
+
+    }
+
+    @JSMethod
+    public void  updateData(int index, Object data){
         if(data == null){
             return;
         }
-        if(listData == null || index >= listData.size()){
+        if(cellDataManager.listData == null || index >= cellDataManager.listData.size()){
             return;
         }
-        listData.set(index, data);
-        notifyUpdateList();
+        boolean onlyDataChange = cellDataManager.updateData(data, index);
+        if(onlyDataChange) {
+            getHostView().getRecyclerViewBaseAdapter().notifyItemChanged(index, data);
+        }else{
+            notifyUpdateList();
+        }
     }
 
     @JSMethod
-    public void  removeData(List<Integer> array){
-        if(array == null || array.size() == 0){
+    public void  removeData(int index, int count){
+        if(cellDataManager.listData == null
+                || index >= cellDataManager.listData.size()){
             return;
         }
-        int offset = 0;
-        for(Integer index : array){
-            if(listData == null
-                    || index == null){
-                return;
-            }
-            index -= offset;
-            if(index < listData.size()){
-                listData.remove((int)index);
-                offset++;
-            }
+        if(count <= 0){
+            count = 1;
+        }
+        int removeCount = 0;
+        while (count > 0 && index < cellDataManager.listData.size()){
+            cellDataManager.removeData(index);
+            count--;
+            removeCount++;
+        }
+        if(removeCount > 0) {
+            notifyUpdateList();
         }
-        notifyUpdateList();
     }
 
 
@@ -1009,8 +1098,8 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp
                 getHostView().getInnerView().setAdapter(null);
             }
         }
-        if(listData != null){
-            listData = null;
+        if(cellDataManager.listData != null){
+            cellDataManager.setListData(null);
         }
         if(mStickyHelper != null){
             mStickyHelper = null;
@@ -1045,25 +1134,21 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp
             return;
         }
         long start = System.currentTimeMillis();
-        boolean resuse = templateViewHolder.getHolderPosition() >= 0;
         templateViewHolder.setHolderPosition(position);
-        Object data = listData.get(position);
-        if(component.getRenderData() == data){
-            component.setHasLayout(true);
+        Object data = cellDataManager.listData.get(position);
+        CellRenderState cellRenderState = cellDataManager.getRenderState(position);
+        if(component.getRenderData() == data && (cellRenderState == null || !cellRenderState.isDirty())){
+            if(WXEnvironment.isOpenDebugLog() && ENABLE_TRACE_LOG){
+                WXLogUtils.d(TAG,  position + " position "+ getTemplateKey(position) + " onBindViewHolder none data update ");
+            }
+            return;  //none update just return
         }else{
-            List<WXComponent> updates = Statements.doRender(component, getStackContextForPosition(position, data));
+            List<WXComponent> updates = doRenderTemplate(component, position);
             Statements.doInitCompontent(updates);
             component.setRenderData(data);
-            if(WXEnvironment.isApkDebugable()){
-                WXLogUtils.d(TAG, position + getTemplateKey(position) + " onBindViewHolder render used " + (System.currentTimeMillis() - start));
-            }
-            if(component.isHasLayout()){
-                resuse = true;
-            }
             Layouts.doLayoutAsync(templateViewHolder, true);
-            component.setHasLayout(true);
-            if(WXEnvironment.isApkDebugable()){
-                WXLogUtils.d(TAG,  position + getTemplateKey(position) + " onBindViewHolder layout used " + (System.currentTimeMillis() - start) + resuse);
+            if(WXEnvironment.isOpenDebugLog() && ENABLE_TRACE_LOG){
+                WXLogUtils.d(TAG,  position + " position "+ getTemplateKey(position) + " onBindViewHolder used " + (System.currentTimeMillis() - start));
             }
         }
     }
@@ -1077,41 +1162,34 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp
             view.setLayoutParams(new FrameLayout.LayoutParams(0, 0));
             return new TemplateViewHolder(view, viewType);
         }
-        TemplateCache cache = mTemplatesCache.get(template);
-        WXCell component =  null;
+        WXCell component =  getCellTemplateFromCache(template);
         boolean cacheHit = true;
-        if(cache != null && cache.cells != null && cache.cells.size() > 0){
-            component = cache.cells.poll();
-        }
-        if(cache == null ||  !cache.isLoadIng){
-            asyncLoadTemplateCache(template);
-        }
         if(component == null){
             cacheHit = false;
             if(!source.isSourceUsed()){
                 source.setSourceUsed(true);
-                ensureSourceCellRenderWithData(source);
+                renderTemplateCellWithData(source);
                 component = source;
-                if(WXEnvironment.isApkDebugable()) {
+                if(WXEnvironment.isOpenDebugLog() && ENABLE_TRACE_LOG) {
                     WXLogUtils.d(TAG, template + " onCreateViewHolder source");
                 }
             }
         }
         if(component == null) {
             long start = System.currentTimeMillis();
-            component = (WXCell) copyCell(source);
-            if(WXEnvironment.isApkDebugable()) {
+            component = (WXCell) copyComponentFromSourceCell(source);
+            if(WXEnvironment.isOpenDebugLog() && ENABLE_TRACE_LOG) {
                 WXLogUtils.d(TAG, template + " onCreateViewHolder copy used " + (System.currentTimeMillis() - start));
             }
         }
         if(component.isLazy()) {
-            doInitLazyCell(component, template, false);
-            if(WXEnvironment.isApkDebugable()) {
-                WXLogUtils.d(TAG, template + " onCreateViewHolder  cache hit " + cacheHit   + " idle init false ");
+            doCreateCellViewBindData(component, template, false);
+            if(WXEnvironment.isOpenDebugLog() && ENABLE_TRACE_LOG) {
+                WXLogUtils.d(TAG, template + " onCreateViewHolder  cache hit " + cacheHit   + " view not idle init");
             }
         }else{
-            if(WXEnvironment.isApkDebugable()) {
-                WXLogUtils.d(TAG, template + " onCreateViewHolder  cache hit " + cacheHit + " idle init true");
+            if(WXEnvironment.isOpenDebugLog() && ENABLE_TRACE_LOG) {
+                WXLogUtils.d(TAG, template + " onCreateViewHolder  cache hit " + cacheHit + " view idle init");
             }
         }
         TemplateViewHolder templateViewHolder = new TemplateViewHolder(component, viewType);
@@ -1123,8 +1201,8 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp
      * copy cell component from source, init render data, and return source
      * if none data, return null
      * */
-    private WXComponent copyCell(WXCell cell){
-        ensureSourceCellRenderWithData(cell);
+    public WXComponent copyComponentFromSourceCell(WXCell cell){
+        renderTemplateCellWithData(cell);
         WXCell component = (WXCell) Statements.copyComponentTree(cell);
         if(component.getDomObject() instanceof  WXCellDomObject
                 && getDomObject() instanceof  WXRecyclerDomObject){
@@ -1135,15 +1213,20 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp
         return component;
     }
 
-    private void ensureSourceCellRenderWithData(WXCell cell){
+    /**
+     *  render  init with  cell with one data,
+     *  if template has already render with data, done nothing
+     *  @param  cell
+     * */
+    private synchronized void renderTemplateCellWithData(WXCell cell){
         if(cell.getRenderData() == null){
-            if(listData != null && listData.size() > 0){
+            if(cellDataManager.listData != null && cellDataManager.listData.size() > 0){
                 synchronized (this){
                     if(cell.getRenderData() == null){
-                        for(int i=0; i<listData.size(); i++){
+                        for(int i = 0; i< cellDataManager.listData.size(); i++){
                             if(cell == getSourceTemplate(i)){
-                                Object data = listData.get(i);
-                                Statements.doRender(cell, getStackContextForPosition(i, data));
+                                Object data = cellDataManager.listData.get(i);
+                                doRenderTemplate(cell, i);
                                 Layouts.doSafeLayout(cell, new CSSLayoutContext());
                                 cell.setRenderData(data);
                                 break;
@@ -1162,37 +1245,33 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp
      * */
     @Override
     public int getItemViewType(int position) {
-        JSONObject data = safeGetListData(position);
-        String template = data.getString(listDataTemplateKey);
-        if(TextUtils.isEmpty(template)){
-            template = "";
-        }
+        String template = getTemplateKey(position);
         int type =  mTemplateViewTypes.indexOfKey(template);
         if(type < 0){
-            type = 0;
+            type = mTemplateViewTypes.indexOfKey(EMPTY_HOLDER_TEMPLATE_KEY);
         }
         return type;
     }
 
 
     /**
-     * return code context for render component
+     * create code context for render component and do render
      * */
-    private ArrayStack getStackContextForPosition(int position,  Object item){
-        if(!bindIngStackContext.isEmpty()){
-            bindIngStackContext.getList().clear();
-        }
-        if(!bindIngMapContext.isEmpty()){
-            bindIngMapContext.clear();
-        }
-        ArrayStack stack = bindIngStackContext;
-        Map map = bindIngMapContext;
-        if(listData != null){
-            stack.push(listData);
+    private List<WXComponent> doRenderTemplate(WXCell cell, int position){
+        this.cellRenderContext.clear();
+        Object item = cellDataManager.listData.get(position);
+        CellRenderState cellRenderState = cellDataManager.getRenderState(position);
+        cellRenderContext.renderState = cellRenderState;
+        cellRenderContext.templateList = this;
+        cellRenderContext.position = position;
+
+        ArrayStack stack = cellRenderContext.stack;
+        Map map = cellRenderContext.map;
+        if(cellDataManager.listData != null){
             stack.push(map);
-            map.put(listDataKey, listData);
+            map.put(listDataKey, cellDataManager.listData);
             if(!TextUtils.isEmpty(listDataIndexKey)) {
-                map.put(listDataIndexKey, position);
+                map.put(listDataIndexKey, new PositionRef(cellRenderState));
             }
             if(!TextUtils.isEmpty(listDataItemKey)) {
                 map.put(listDataItemKey, item);
@@ -1200,17 +1279,51 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp
                 stack.push(item);
             }
         }
-        return stack;
+        if(cellRenderState.itemId <= 0){
+            getItemId(position);
+        }
+        List<WXComponent> updates = Statements.doRender(cell, this.cellRenderContext);
+        if(cellRenderState.isDirty()){
+            cellRenderState.resetDirty();
+        }
+        return  updates;
+    }
+
+    public ArrayStack  copyStack(CellRenderContext context, ArrayStack stack){
+        ArrayStack  onceStack = new ArrayStack();
+        for(int index=0;  index <  stack.size(); index++) {
+            Object value = stack.get(index);
+            if(value instanceof  Map){
+                value  = new HashMap((Map) value);
+            }
+            onceStack.push(value);
+        }
+        return onceStack;
     }
 
+
     /**
      * return tepmlate key for position
      * */
     public String getTemplateKey(int position){
-        JSONObject data =  safeGetListData(position);
-        String template = data.getString(listDataTemplateKey);
+        Object data =  safeGetListData(position);
+        return getTemplateKey(data);
+    }
+
+    /**
+     * return template key for position never be null
+     * */
+    public String getTemplateKey(Object data){
+        String template = null;
+        if(data instanceof  JSONObject) {
+            template = ((JSONObject)data).getString(listDataTemplateKey);
+        }
         if(TextUtils.isEmpty(template)){
-            template = "";
+            if(defaultTemplateCell != null){
+                template  = defaultTemplateKey;
+            }else {
+                template = "";
+            }
         }
         return  template;
     }
@@ -1226,17 +1339,17 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp
 
 
     /**
-     * get template key from cell; 0  for default type
+     * get template key from cell; -1 for  cann't find  type
      * */
-    private int getCellItemType(WXComponent cell){
+    private int getCellTemplateItemType(WXCell cell){
         if(cell == null){
             return  -1;
         }
         if(cell.getDomObject() != null && cell.getDomObject().getAttrs() != null){
-            Object templateId = cell.getDomObject().getAttrs().get(Constants.Name.Recycler.SLOT_TEMPLATE_TYPE);
+            Object templateId = cell.getDomObject().getAttrs().get(Constants.Name.Recycler.SLOT_TEMPLATE_CASE);
             String template = WXUtils.getString(templateId, null);
-            if(template == null){
-                return  0;
+            if(cell == defaultTemplateCell){
+                template = defaultTemplateKey;
             }
             int type =  mTemplateViewTypes.indexOfKey(template);
             if(type < 0){
@@ -1249,7 +1362,7 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp
 
     @Override
     public int getItemCount() {
-        if(listData == null){
+        if(cellDataManager.listData == null){
             return  0;
         }
         if(mTemplateViewTypes == null || mTemplateViewTypes.size() <= 1){
@@ -1258,7 +1371,7 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp
         if(mTemplateSources == null || mTemplateSources.size() == 0){
             return  0;
         }
-        return listData.size();
+        return cellDataManager.listData.size();
     }
 
     @Override
@@ -1274,18 +1387,22 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp
      * */
     @Override
     public long getItemId(int position) {
-        if(getItemViewType(position) <= 0){
-            return RecyclerView.NO_ID;
-        }
-        JSONObject data = safeGetListData(position);
-        if(data.containsKey(Constants.Name.Recycler.LIST_DATA_ITEM_ID)) {
-            String itemKey = data.getString(Constants.Name.Recycler.LIST_DATA_ITEM_ID);
-            if(TextUtils.isEmpty(itemKey)){
-                return  position;
+        CellRenderState renderState = cellDataManager.getRenderState(position);
+        if(renderState.itemId <=  0){
+            String template = getTemplateKey(position);
+            if(TextUtils.isEmpty(template)){
+                return RecyclerView.NO_ID;
+            }
+            Object data = safeGetListData(position);
+            if(data instanceof  JSONObject && ((JSONObject)data).containsKey("keyItemId")){
+                renderState.itemId =  ((JSONObject)data).getLongValue("keyItemId");
+            }else{
+                long id = Math.abs(data.hashCode());
+                long itemId = (id<< 24) + position;
+                renderState.itemId = itemId;
             }
-            return  itemKey.hashCode();
         }
-        return position;
+        return renderState.itemId;
     }
 
     @Override
@@ -1305,16 +1422,16 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp
             }
             float offsetParsed = WXViewUtils.getRealPxByWidth(Integer.parseInt(offset),getInstance().getInstanceViewPortWidth());
 
-            if (offScreenY <= offsetParsed && listData != null) {
-                if (mListCellCount != listData.size()
+            if (offScreenY <= offsetParsed && cellDataManager.listData != null) {
+                if (mListCellCount != cellDataManager.listData.size()
                         || mForceLoadmoreNextTime) {
                     fireEvent(Constants.Event.LOADMORE);
-                    mListCellCount = listData.size();
+                    mListCellCount = cellDataManager.listData.size();
                     mForceLoadmoreNextTime = false;
                 }
             }
         } catch (Exception e) {
-            WXLogUtils.d(TAG + "onLoadMore :", e);
+            WXLogUtils.d(TAG + " onLoadMore : ", e);
         }
     }
 
@@ -1432,13 +1549,13 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp
     }
 
 
-    private JSONObject safeGetListData(int position){
+    private Object safeGetListData(int position){
         try{
-            return listData.getJSONObject(position);
+            return cellDataManager.listData.get(position);
         }catch (Exception e){return  JSONObject.parseObject("{}");}
     }
 
-    private void  notifyUpdateList(){
+    public void  notifyUpdateList(){
         if(getHostView() == null
                 || getHostView().getInnerView() == null
                 || listUpdateRunnable == null){
@@ -1454,10 +1571,10 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp
 
     private int calcContentSize() {
         int totalHeight = 0;
-        if(listData == null){
+        if(cellDataManager.listData == null){
             return totalHeight;
         }
-        for (int i = 0; i < listData.size(); i++) {
+        for (int i = 0; i < cellDataManager.listData.size(); i++) {
             WXCell child = getSourceTemplate(i);
             if (child != null) {
                 totalHeight += child.getLayoutHeight();
@@ -1567,114 +1684,45 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp
     }
 
 
+
     /**
-     * copy cell async and save to cache
+     * @param  template  template name
+     * get cell template component from cache, if cell component not load
+     * start load cell template
      * */
-    private void asyncLoadTemplateCache(final String template) {
-        if(Thread.currentThread() != Looper.getMainLooper().getThread()){
-            if(listData == null || listData.size() == 0){
-                return;
-            }
-            boolean firstScreenContains = false;
-            for(int i=0; i<listData.size(); i++){
-                if(template.equals(getTemplateKey(i))){
-                    firstScreenContains = true;
-                    break;
-                }
-            }
-            if(!firstScreenContains){
-                return;
-            }
-        }
-        final WXCell source = mTemplateSources.get(template);
-        if(source == null){
-            return;
-        }
-        TemplateCache cellCache = mTemplatesCache.get(template);
-        if(cellCache == null){
-            cellCache = new TemplateCache();
-            mTemplatesCache.put(template, cellCache);
-        }
-        if(cellCache.cells.size() > 0){
-            cellCache.isLoadIng = false;
-            return;
-        }
-        if(cellCache.isLoadIng){
-            return;
+    private WXCell getCellTemplateFromCache(final String template){
+        TemplateCache cache = mTemplatesCache.get(template);
+        WXCell component =  null;
+        if(cache != null && cache.cells != null && cache.cells.size() > 0){
+            component = cache.cells.poll();
         }
-        cellCache.isLoadIng = true;
-        AsyncTask<Void,Void, Void> preloadTask = new AsyncTask<Void, Void, Void>() {
-            @Override
-            protected Void doInBackground(Void... params) {
-                TemplateCache cellCache = mTemplatesCache.get(template);
-                if(cellCache == null || cellCache.cells == null){
-                    return null;
-                }
-                while (cellCache.cells.size() < templateCacheSize){
-                    WXCell component = (WXCell) copyCell(source);
-                    if(component == null){
-                        return null;
-                    }
-                    if(source.getInstance() == null || source.getInstance().isDestroy()){
-                        return null;
-                    }
-                    cellCache.cells.add(component);
-                }
-                return null;
+        if(cache == null ||  !cache.isLoadIng){
+            if(cache == null){
+                cache = new TemplateCache();
+                mTemplatesCache.put(template, cache);
             }
-            @Override
-            protected void onPostExecute(Void aVoid) {
-                if(source.getInstance() == null || source.getInstance().isDestroy()){
-                    return;
+            cache.isLoadIng = true;
+            WXCell source = mTemplateSources.get(template);
+            if(source != null){
+                boolean allowPreload = WXUtils.getBoolean(source.getDomObject().getAttrs().get("preload"), true);
+                if(allowPreload) {
+                    AsyncCellLoadTask asyncCellLoadTask = new AsyncCellLoadTask(template, source, this);
+                    asyncCellLoadTask.startTask();
                 }
-                final TemplateCache cellCache = mTemplatesCache.get(template);
-                if(cellCache == null){
-                    return;
-                }
-                if(cellCache.cells == null
-                        || cellCache.cells.size() == 0){
-                    cellCache.isLoadIng = false;
-                    return;
-                }
-                Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
-                    @Override
-                    public boolean queueIdle() {
-                        if(source.getInstance() == null || source.getInstance().isDestroy()){
-                            return false;
-                        }
-                        ConcurrentLinkedQueue<WXCell> queue =  cellCache.cells;
-                        Iterator<WXCell> iterator =  queue.iterator();
-                        while (iterator.hasNext()){
-                            WXCell  component =  iterator.next();
-                            if(component.isLazy()){
-                                doInitLazyCell(component, template, true);
-                                return iterator.hasNext();
-                            }
-                        }
-                        return false;
-                    }
-                });
-                cellCache.isLoadIng = false;
             }
-        };
-        preloadTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+        }
+        return  component;
     }
 
-    private static void  doInitLazyCell(WXCell component, String template, boolean inPreload){
+    /**
+     * create view for lazy cell and bind data
+     * */
+    public static void doCreateCellViewBindData(WXCell component, String template, boolean inPreload){
         if(component.isLazy()){
             long start = System.currentTimeMillis();
-            component.lazy(false);
-            component.createView();
-            if(WXEnvironment.isApkDebugable()) {
-                WXLogUtils.d(TAG,  "doInitLazyCell idle" + inPreload + template +  " createView used " + (System.currentTimeMillis() - start));
-            }
-            component.applyLayoutAndEvent(component);
-            if(WXEnvironment.isApkDebugable()) {
-                WXLogUtils.d(TAG,  "doInitLazyCell idle" + inPreload  + template +  " apply layout used " + (System.currentTimeMillis() - start));
-            }
-            component.bindData(component);
-            if(WXEnvironment.isApkDebugable()) {
-                WXLogUtils.d(TAG, "doInitLazyCell idle" + inPreload + template + " bindData used " + (System.currentTimeMillis() - start));
+            ComponentUtils.initLazyComponent(component, null);
+            if(WXEnvironment.isOpenDebugLog() && ENABLE_TRACE_LOG) {
+                WXLogUtils.d(TAG, " doCreateCellViewBindData " + template + " in preload "+ inPreload + " used " + (System.currentTimeMillis() - start));
             }
         }
     }
@@ -1685,4 +1733,20 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp
         }
         return mScrollStartEndHelper;
     }
+
+    public int getTemplateCacheSize() {
+        return templateCacheSize;
+    }
+
+    public ConcurrentHashMap<String, TemplateCache> getTemplatesCache() {
+        if(mTemplatesCache == null){
+            mTemplatesCache = new ConcurrentHashMap<>();
+        }
+        return mTemplatesCache;
+    }
+
+
+    public CellDataManager getCellDataManager() {
+        return cellDataManager;
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/886d859a/android/sdk/src/test/java/com/taobao/weex/el/FailedCaseTest.java
----------------------------------------------------------------------
diff --git a/android/sdk/src/test/java/com/taobao/weex/el/FailedCaseTest.java b/android/sdk/src/test/java/com/taobao/weex/el/FailedCaseTest.java
new file mode 100644
index 0000000..f887dca
--- /dev/null
+++ b/android/sdk/src/test/java/com/taobao/weex/el/FailedCaseTest.java
@@ -0,0 +1,50 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.taobao.weex.el;
+
+import com.alibaba.fastjson.JSONObject;
+import com.taobao.weex.el.parse.ArrayStack;
+import com.taobao.weex.el.parse.Parser;
+import com.taobao.weex.el.parse.Token;
+
+import junit.framework.TestCase;
+
+/**
+ * Created by furture on 2018/1/25.
+ */
+
+public class FailedCaseTest extends TestCase {
+
+
+    public void testVElseIf0(){
+        JSONObject data = new JSONObject();
+        JSONObject item = new JSONObject();
+        item.put("number", 0.0);
+        data.put("item", item);
+        ArrayStack stack = new ArrayStack();
+        stack.push(data);
+
+        Token token = Parser.parse("!(item.number%3 === 0) && (item.number%3 === 1)");
+
+        System.out.println(token.toString() + "  " + token.execute(stack));
+
+        Token if2 = Parser.parse("!(!(item.number%3 === 0) && (item.number%3 === 1))");
+        System.out.println(if2 + "   " + if2.execute(stack));
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/886d859a/android/sdk/src/test/java/com/taobao/weex/ui/component/binding/StatementTest.java
----------------------------------------------------------------------
diff --git a/android/sdk/src/test/java/com/taobao/weex/ui/component/binding/StatementTest.java b/android/sdk/src/test/java/com/taobao/weex/ui/component/binding/StatementTest.java
index 3924a3a..2233acc 100644
--- a/android/sdk/src/test/java/com/taobao/weex/ui/component/binding/StatementTest.java
+++ b/android/sdk/src/test/java/com/taobao/weex/ui/component/binding/StatementTest.java
@@ -37,6 +37,7 @@ import com.taobao.weex.ui.component.WXComponentFactory;
 import com.taobao.weex.ui.component.WXDiv;
 import com.taobao.weex.ui.component.WXText;
 import com.taobao.weex.ui.component.list.WXCell;
+import com.taobao.weex.ui.component.list.template.CellRenderContext;
 
 import junit.framework.Assert;
 
@@ -57,7 +58,6 @@ import org.robolectric.annotation.Config;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Stack;
 
 /**
  * Created by furture on 2017/8/18.
@@ -166,7 +166,7 @@ public class StatementTest {
         return cell;
     }
 
-    private ArrayStack createContext(int count){
+    private CellRenderContext createContext(int count){
         JSONObject data = new JSONObject();
         data.put("item", new JSONObject());
         data.getJSONObject("item").put("name", "hello world");
@@ -178,7 +178,9 @@ public class StatementTest {
         data.put("dataList", datas);
         ArrayStack context = new ArrayStack();
         context.push(data);
-        return context;
+        CellRenderContext cellRenderContext = new CellRenderContext();
+        cellRenderContext.stack = context;
+        return cellRenderContext;
     }
 
 }