http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/886d859a/android/sdk/src/main/java/com/taobao/weex/ui/component/binding/Statements.java ---------------------------------------------------------------------- diff --git a/android/sdk/src/main/java/com/taobao/weex/ui/component/binding/Statements.java b/android/sdk/src/main/java/com/taobao/weex/ui/component/binding/Statements.java index 2ed3fae..b0b383e 100644 --- a/android/sdk/src/main/java/com/taobao/weex/ui/component/binding/Statements.java +++ b/android/sdk/src/main/java/com/taobao/weex/ui/component/binding/Statements.java @@ -21,19 +21,19 @@ package com.taobao.weex.ui.component.binding; import android.os.Looper; import android.support.v4.util.ArrayMap; import android.text.TextUtils; -import android.util.Log; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.taobao.weex.WXEnvironment; -import com.taobao.weex.annotation.Component; +import com.taobao.weex.bridge.EventResult; +import com.taobao.weex.bridge.WXBridgeManager; import com.taobao.weex.common.Constants; import com.taobao.weex.dom.WXAttr; import com.taobao.weex.dom.WXDomObject; import com.taobao.weex.dom.WXEvent; +import com.taobao.weex.dom.WXStyle; import com.taobao.weex.dom.binding.ELUtils; import com.taobao.weex.dom.binding.WXStatement; -import com.taobao.weex.dom.flex.CSSLayoutContext; import com.taobao.weex.el.parse.ArrayStack; import com.taobao.weex.el.parse.Operators; import com.taobao.weex.el.parse.Token; @@ -42,6 +42,9 @@ import com.taobao.weex.ui.component.WXComponentFactory; import com.taobao.weex.ui.component.WXImage; import com.taobao.weex.ui.component.WXVContainer; import com.taobao.weex.ui.component.list.WXCell; +import com.taobao.weex.ui.component.list.template.CellRenderContext; +import com.taobao.weex.ui.component.list.template.CellDataManager; +import com.taobao.weex.ui.component.list.template.VirtualComponentLifecycle; import com.taobao.weex.ui.component.list.template.WXRecyclerTemplateList; import com.taobao.weex.utils.WXLogUtils; import com.taobao.weex.utils.WXUtils; @@ -63,11 +66,7 @@ public class Statements { * recursive copy component, none parent connect * */ public static WXComponent copyComponentTree(WXComponent component){ - long start = System.currentTimeMillis(); WXComponent copy = copyComponentTree(component, component.getParent()); - if(WXEnvironment.isApkDebugable()){ - WXLogUtils.d(WXRecyclerTemplateList.TAG, Thread.currentThread() + component.getRef() + "copyComponentTree " + "used " + (System.currentTimeMillis() - start)); - } return copy; } @@ -91,6 +90,10 @@ public class Statements { } } } + //copy info need be sync + if(source.isWaste()){ + component.setWaste(true); + } return component; } @@ -104,7 +107,7 @@ public class Statements { * may be next render it can be used. * after statement has executed, render component's binding attrs in context and bind it to component. * */ - public static final List<WXComponent> doRender(WXComponent component, ArrayStack stack){ + public static final List<WXComponent> doRender(WXComponent component, CellRenderContext stack){ List<WXComponent> updates = new ArrayList<>(4); try{ doRenderComponent(component, stack, updates); @@ -143,7 +146,7 @@ public class Statements { * may be next render it can be used. * after statement has executed, render component's binding attrs in context and bind it to component. * */ - private static final int doRenderComponent(WXComponent component, ArrayStack context, + private static final int doRenderComponent(WXComponent component, CellRenderContext context, List<WXComponent> updates){ WXVContainer parent = component.getParent(); WXDomObject domObject = (WXDomObject) component.getDomObject(); @@ -168,7 +171,7 @@ public class Statements { String itemKey = vfor.getString(WXStatement.WX_FOR_ITEM); Object data = null; if(listBlock != null) { - data = listBlock.execute(context); + data = listBlock.execute(context.stack); } if((data instanceof List || data instanceof Map)){ @@ -199,15 +202,15 @@ public class Statements { if(itemKey != null){ loop.put(itemKey, value); }else{ - context.push(value); + context.stack.push(value); } if(loop.size() > 0){ - context.push(loop); + context.stack.push(loop); } if(vif != null){ - if(!Operators.isTrue(vif.execute(context))){ + if(!Operators.isTrue(vif.execute(context.stack))){ continue; } } @@ -229,8 +232,10 @@ public class Statements { if(renderNode == null){ long start = System.currentTimeMillis(); renderNode = copyComponentTree(component, parent); + renderNode.setWaste(false); WXDomObject renderNodeDomObject = (WXDomObject) renderNode.getDomObject(); - renderNodeDomObject.getAttrs().setStatement(null); // clear node's statement + renderNodeDomObject.getAttrs().getStatement().remove(WXStatement.WX_FOR); + renderNodeDomObject.getAttrs().getStatement().remove(WXStatement.WX_IF); //FIXME clear node's statement parentDomObject.add(renderNodeDomObject, renderIndex); parent.addChild(renderNode, renderIndex); updates.add(renderNode); @@ -238,13 +243,13 @@ public class Statements { WXLogUtils.d(WXRecyclerTemplateList.TAG, Thread.currentThread().getName() + renderNode.getRef() + renderNode.getDomObject().getType() + "statements copy component tree used " + (System.currentTimeMillis() - start)); } } - doBindingAttrsEventAndRenderChildNode(renderNode, domObject, context, updates); + doBindingAttrsEventAndRenderChildNode(renderNode, (WXDomObject) renderNode.getDomObject(), context, updates); renderIndex++; if(loop.size() > 0){ - context.push(loop); + context.stack.push(loop); } if(itemKey == null) { - context.pop(); + context.stack.pop(); } } } @@ -264,45 +269,112 @@ public class Statements { //execute v-if context if(vif != null){ - if(!Operators.isTrue(vif.execute(context))){ + if(!Operators.isTrue(vif.execute(context.stack))){ component.setWaste(true); - if(Thread.currentThread() == Looper.getMainLooper().getThread()) { - return 1; - } + return 1; }else{ component.setWaste(false); } + } } doBindingAttrsEventAndRenderChildNode(component, domObject, context, updates); return 1; } + /** * bind attrs and doRender component child * */ - private static void doBindingAttrsEventAndRenderChildNode(WXComponent component, WXDomObject domObject, ArrayStack context, + private static void doBindingAttrsEventAndRenderChildNode(WXComponent component, WXDomObject domObject, CellRenderContext context, List<WXComponent> updates){ - WXAttr attr = component.getDomObject().getAttrs(); + WXAttr attr = component.getDomObject().getAttrs(); + /** * sub component supported, sub component new stack * */ + ArrayStack stack = context.stack; + + + if(attr.get(ELUtils.IS_COMPONENT_ROOT) != null && WXUtils.getBoolean(attr.get(ELUtils.IS_COMPONENT_ROOT), false)){ if(attr.get(ELUtils.COMPONENT_PROPS) != null && attr.get(ELUtils.COMPONENT_PROPS) instanceof JSONObject){ - Map<String, Object> props = renderProps((JSONObject) attr.get(ELUtils.COMPONENT_PROPS), context); - context = new ArrayStack(); - context.push(props); + String compoentId = (String) attr.get(CellDataManager.SUB_COMPONENT_TEMPLATE_ID); + Object compoentData = null; + if(!TextUtils.isEmpty(compoentId)){ + String virtualComponentId = context.getRenderState().getVirtualComponentIds().get(component.getViewTreeKey()); + if(virtualComponentId == null){ //none virtualComponentId, create and do attach + virtualComponentId = CellDataManager.createVirtualComponentId(context.templateList.getRef(), + component.getViewTreeKey(), context.templateList.getItemId(context.position)); + Map<String, Object> props = renderProps((JSONObject) attr.get(ELUtils.COMPONENT_PROPS), context.stack); + EventResult result = WXBridgeManager.getInstance().syncCallJSEventWithResult(WXBridgeManager.METHD_COMPONENT_HOOK_SYNC, component.getInstanceId(), null, compoentId, VirtualComponentLifecycle.LIFECYCLE, VirtualComponentLifecycle.CREATE, new Object[]{ + virtualComponentId, + props + }, null); + if(result != null + && result.getResult() != null + && result.getResult() instanceof Map){ + props.putAll((Map<? extends String, ?>) result.getResult()); + } + compoentData = props; + context.getRenderState().getVirtualComponentIds().put(component.getViewTreeKey(), virtualComponentId); + context.templateList.getCellDataManager().createVirtualComponentData(context.position, virtualComponentId, compoentData); + //create virtual componentId + WXBridgeManager.getInstance().asyncCallJSEventVoidResult(WXBridgeManager.METHD_COMPONENT_HOOK_SYNC, component.getInstanceId(), null, virtualComponentId, VirtualComponentLifecycle.LIFECYCLE, VirtualComponentLifecycle.ATTACH, null); + }else{ // get virtual component data check has dirty's update + compoentData = context.getRenderState().getVirtualComponentDatas().get(virtualComponentId); + if(context.getRenderState().isHasDataUpdate()){ + Map<String, Object> props = renderProps((JSONObject) attr.get(ELUtils.COMPONENT_PROPS), context.stack); + EventResult result = WXBridgeManager.getInstance().syncCallJSEventWithResult(WXBridgeManager.METHD_COMPONENT_HOOK_SYNC, component.getInstanceId(), null, virtualComponentId, VirtualComponentLifecycle.LIFECYCLE, VirtualComponentLifecycle.SYNSTATE, new Object[]{ + virtualComponentId, + props + }, null); + + if(result != null + && result.getResult() != null + && result.getResult() instanceof Map){ + props.putAll((Map<? extends String, ?>) result.getResult()); + context.templateList.getCellDataManager().updateVirtualComponentData(virtualComponentId, props); + compoentData = props; + } + } + } + component.getDomObject().getAttrs().put(CellDataManager.VIRTUAL_COMPONENT_ID, virtualComponentId); + }else{ //stateless component + Map<String, Object> props = renderProps((JSONObject) attr.get(ELUtils.COMPONENT_PROPS), context.stack); + compoentData = props; + } + //virtual component is new context + context.stack = new ArrayStack(); + if(compoentData != null){ + context.stack.push(compoentData); + } } } + + /** + * check node is render only once, if render once, and has rendered, just return + * */ + Object vonce = null; + if(attr.getStatement() != null){ + vonce = attr.getStatement().get(WXStatement.WX_ONCE); + } + if(vonce != null){ + ArrayStack onceStack = context.getRenderState().getOnceComponentStates().get(component.getViewTreeKey()); + if(onceStack == null){ + onceStack = context.templateList.copyStack(context, stack); + context.getRenderState().getOnceComponentStates().put(component.getViewTreeKey(), onceStack); + } + context.stack = onceStack; + } + doRenderBindingAttrsAndEvent(component, domObject, context); if(component instanceof WXVContainer){ if(!domObject.isShow()){ if(!(component instanceof WXCell)){ - if(Thread.currentThread() == Looper.getMainLooper().getThread()){ - return; - } + return; } } WXVContainer container = (WXVContainer) component; @@ -311,6 +383,9 @@ public class Statements { k += doRenderComponent(next, context, updates); } } + if(stack != context.stack){ + context.stack = stack; + } } @@ -325,14 +400,15 @@ public class Statements { /** * render dynamic binding attrs and bind them to component node. * */ - private static void doRenderBindingAttrsAndEvent(WXComponent component, WXDomObject domObject, ArrayStack context){ + private static void doRenderBindingAttrsAndEvent(WXComponent component, WXDomObject domObject, CellRenderContext context){ + ArrayStack stack = context.stack; component.setWaste(false); WXAttr attr = domObject.getAttrs(); if(attr != null && attr.getBindingAttrs() != null && attr.getBindingAttrs().size() > 0){ ArrayMap<String, Object> bindAttrs = domObject.getAttrs().getBindingAttrs(); - Map<String, Object> dynamic = renderBindingAttrs(bindAttrs, context); + Map<String, Object> dynamic = renderBindingAttrs(bindAttrs, stack); Set<Map.Entry<String, Object>> entries = dynamic.entrySet(); /** * diff attrs, see attrs has update, remove none update attrs @@ -364,23 +440,60 @@ public class Statements { }else { domObject.updateAttr(dynamic); //dirty layout } - if(Thread.currentThread() == Looper.getMainLooper().getThread()) { + if(isMainThread()) { component.updateProperties(dynamic); } dynamic.clear(); } } + + + WXStyle style = domObject.getStyles(); + if(style != null && style.getBindingStyle() != null){ + ArrayMap<String, Object> bindStyle = style.getBindingStyle(); + Map<String, Object> dynamic = renderBindingAttrs(bindStyle, stack); + Set<Map.Entry<String, Object>> entries = dynamic.entrySet(); + /** + * diff attrs, see attrs has update, remove none update attrs + * */ + Iterator<Map.Entry<String, Object>> iterator = entries.iterator(); + while (iterator.hasNext()){ + Map.Entry<String, Object> entry = iterator.next(); + String key = entry.getKey(); + Object value = entry.getValue(); + Object oldValue = style.get(key); + if(value == null){ + if(oldValue == null){ + iterator.remove(); + continue; + } + }else{ + if(value.equals(oldValue)){ + iterator.remove(); + } + } + } + if(dynamic.size() > 0) { + domObject.updateStyle(dynamic, false); + domObject.applyStyle(dynamic); + if(isMainThread()) { + component.updateProperties(dynamic); + } + } + } + WXEvent event = domObject.getEvents(); if(event == null || event.getEventBindingArgs() == null){ return; } Set<Map.Entry<String, Object>> eventBindArgsEntrySet = event.getEventBindingArgs().entrySet(); for(Map.Entry<String, Object> eventBindArgsEntry : eventBindArgsEntrySet){ - List<Object> values = getBindingEventArgs(context, eventBindArgsEntry.getValue()); + List<Object> values = getBindingEventArgs(stack, eventBindArgsEntry.getValue()); if(values != null){ event.putEventBindingArgsValue(eventBindArgsEntry.getKey(), values); } } + } @@ -390,7 +503,7 @@ public class Statements { * return binding attrs rended value in context * */ private static final ThreadLocal<Map<String, Object>> dynamicLocal = new ThreadLocal<>(); - public static Map<String, Object> renderBindingAttrs(ArrayMap bindAttrs, ArrayStack context){ + public static Map<String, Object> renderBindingAttrs(ArrayMap bindAttrs, ArrayStack stack){ Set<Map.Entry<String, Object>> entrySet = bindAttrs.entrySet(); Map<String, Object> dynamic = dynamicLocal.get(); if(dynamic == null) { @@ -407,7 +520,7 @@ public class Statements { && (((JSONObject) value).get(ELUtils.BINDING) instanceof Token)){ JSONObject binding = (JSONObject) value; Token block = (Token) (binding.get(ELUtils.BINDING)); - Object blockValue = block.execute(context); + Object blockValue = block.execute(stack); dynamic.put(key, blockValue); }else if(value instanceof JSONArray){ JSONArray array = (JSONArray) value; @@ -422,7 +535,7 @@ public class Statements { && (((JSONObject) element).get(ELUtils.BINDING) instanceof Token)){ JSONObject binding = (JSONObject) element; Token block = (Token) (binding.get(ELUtils.BINDING)); - Object blockValue = block.execute(context); + Object blockValue = block.execute(stack); if(blockValue == null){ blockValue = ""; } @@ -442,7 +555,7 @@ public class Statements { } - public static Map<String, Object> renderProps(JSONObject props, ArrayStack context){ + public static Map<String, Object> renderProps(JSONObject props, ArrayStack stack){ Set<Map.Entry<String, Object>> entrySet = props.entrySet(); Map<String, Object> renderProps = new ArrayMap<>(4); for(Map.Entry<String, Object> entry : entrySet){ @@ -452,7 +565,7 @@ public class Statements { && (((JSONObject) value).get(ELUtils.BINDING) instanceof Token)){ JSONObject binding = (JSONObject) value; Token block = (Token) (binding.get(ELUtils.BINDING)); - Object blockValue = block.execute(context); + Object blockValue = block.execute(stack); renderProps.put(key, blockValue); }else{ renderProps.put(key, value); @@ -461,7 +574,7 @@ public class Statements { return renderProps; } - public static List<Object> getBindingEventArgs(ArrayStack context, Object bindings){ + public static List<Object> getBindingEventArgs(ArrayStack stack, Object bindings){ List<Object> params = new ArrayList<>(4); if(bindings instanceof JSONArray){ JSONArray array = (JSONArray) bindings; @@ -470,7 +583,7 @@ public class Statements { if(value instanceof JSONObject && (((JSONObject) value).get(ELUtils.BINDING) instanceof Token)){ Token block = (Token)(((JSONObject) value).get(ELUtils.BINDING)); - Object blockValue = block.execute(context); + Object blockValue = block.execute(stack); params.add(blockValue); }else{ params.add(value); @@ -480,7 +593,7 @@ public class Statements { JSONObject binding = (JSONObject) bindings; if(binding.get(ELUtils.BINDING) instanceof Token){ Token block = (Token) binding.get(ELUtils.BINDING); - Object blockValue = block.execute(context); + Object blockValue = block.execute(stack); params.add(blockValue); }else{ params.add(bindings.toString()); @@ -490,4 +603,33 @@ public class Statements { } return params; } + + private static volatile int componentIdNext = 0; + + + private static boolean isMainThread(){ + return Thread.currentThread() == Looper.getMainLooper().getThread(); + } + + + + public static String getComponentId(WXComponent component){ + if(component instanceof WXCell || component == null){ + return null; + } + WXDomObject domObject = (WXDomObject) component.getDomObject(); + WXAttr attr = domObject.getAttrs(); + if(attr.get(ELUtils.IS_COMPONENT_ROOT) != null + && WXUtils.getBoolean(attr.get(ELUtils.IS_COMPONENT_ROOT), false)){ + if(attr.get(ELUtils.COMPONENT_PROPS) != null + && attr.get(ELUtils.COMPONENT_PROPS) instanceof JSONObject){ + Object componentId = attr.get(CellDataManager.VIRTUAL_COMPONENT_ID); + if(componentId == null){ + return null; + } + return componentId.toString(); + } + } + return getComponentId(component.getParent()); + } }
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/886d859a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/WXCell.java ---------------------------------------------------------------------- diff --git a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/WXCell.java b/android/sdk/src/main/java/com/taobao/weex/ui/component/list/WXCell.java index 36909fa..935f7e6 100644 --- a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/WXCell.java +++ b/android/sdk/src/main/java/com/taobao/weex/ui/component/list/WXCell.java @@ -52,7 +52,6 @@ public class WXCell extends WidgetContainer<WXFrameLayout> { private ViewGroup mRealView; private View mTempStickyView; private View mHeadView; - private boolean mLazy = true; /** used in list sticky detect **/ private int mScrollPositon = -1; @@ -63,16 +62,16 @@ public class WXCell extends WidgetContainer<WXFrameLayout> { private boolean isSourceUsed = false; - private boolean hasLayout = false; - @Deprecated public WXCell(WXSDKInstance instance, WXDomObject dom, WXVContainer parent, String instanceId, boolean isLazy) { super(instance, dom, parent); + lazy(true); } public WXCell(WXSDKInstance instance, WXDomObject dom, WXVContainer parent, boolean isLazy) { super(instance, dom, parent); + lazy(true); if(Build.VERSION.SDK_INT< VERSION_CODES.LOLLIPOP) { try { //TODO a WTF is necessary if anyone try to change the flat flag during update attrs. @@ -88,12 +87,10 @@ public class WXCell extends WidgetContainer<WXFrameLayout> { @Override public boolean isLazy() { - return mLazy && !isFixed(); + return super.isLazy() && !isFixed(); } - public void lazy(boolean lazy) { - mLazy = lazy; - } + @Override @RestrictTo(Scope.LIBRARY) @@ -236,11 +233,4 @@ public class WXCell extends WidgetContainer<WXFrameLayout> { isSourceUsed = sourceUsed; } - public boolean isHasLayout() { - return hasLayout; - } - - public void setHasLayout(boolean hasLayout) { - this.hasLayout = hasLayout; - } } http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/886d859a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/AsyncCellLoadTask.java ---------------------------------------------------------------------- diff --git a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/AsyncCellLoadTask.java b/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/AsyncCellLoadTask.java new file mode 100644 index 0000000..eb864bf --- /dev/null +++ b/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/AsyncCellLoadTask.java @@ -0,0 +1,128 @@ +/** + * 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.ui.component.list.template; + +import android.os.AsyncTask; +import android.os.Looper; +import android.os.MessageQueue; + +import com.taobao.weex.WXEnvironment; +import com.taobao.weex.ui.component.list.WXCell; +import com.taobao.weex.utils.WXLogUtils; + +import java.util.Iterator; +import java.util.concurrent.ConcurrentLinkedQueue; + +/** + * Created by furture on 2018/1/25. + * async template cell component copy in background and init component when idle + */ +class AsyncCellLoadTask extends AsyncTask<Void,Void, Void> { + + private String template; + private WXCell source; + private WXRecyclerTemplateList templateList; + + public AsyncCellLoadTask(String template, WXCell source, WXRecyclerTemplateList templateList) { + this.template = template; + this.source = source; + this.templateList = templateList; + } + + /** + * async template cell component copy in background + * */ + @Override + protected Void doInBackground(Void... params) { + TemplateCache cellCache = templateList.getTemplatesCache().get(template); + if(cellCache == null || cellCache.cells == null){ + return null; + } + while (cellCache.cells.size() < templateList.getTemplateCacheSize()){ + long start = System.currentTimeMillis(); + WXCell component = (WXCell) templateList.copyComponentFromSourceCell(source); + if(WXEnvironment.isOpenDebugLog() && WXRecyclerTemplateList.ENABLE_TRACE_LOG){ + WXLogUtils.d(WXRecyclerTemplateList.TAG, " AsyncCellLoadTask load " + template + + " used " + (System.currentTimeMillis() - start)); + } + if(component == null){ + return null; + } + if(isDestory()){ + return null; + } + cellCache.cells.add(component); + } + return null; + } + + /** + * init component view when main thread idle + * */ + @Override + protected void onPostExecute(Void aVoid) { + if(isDestory()){ + return; + } + final TemplateCache cellCache = templateList.getTemplatesCache().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(isDestory()){ + return false; + } + ConcurrentLinkedQueue<WXCell> queue = cellCache.cells; + Iterator<WXCell> iterator = queue.iterator(); + while (iterator.hasNext()){ + WXCell component = iterator.next(); + if(component.isLazy()){ + templateList.doCreateCellViewBindData(component, template, true); + return iterator.hasNext(); + } + } + return false; + } + }); + cellCache.isLoadIng = false; + } + + private boolean isDestory(){ + if(source.getInstance() == null + || source.getInstance().isDestroy()){ + return true; + } + return templateList.isDestoryed(); + } + + /** + * start cell load task on THREAD_POOL_EXECUTOR + * */ + public void startTask(){ + executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/886d859a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/CellDataManager.java ---------------------------------------------------------------------- diff --git a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/CellDataManager.java b/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/CellDataManager.java new file mode 100644 index 0000000..3982117 --- /dev/null +++ b/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/CellDataManager.java @@ -0,0 +1,272 @@ +/** + * 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.ui.component.list.template; + + +import android.support.v4.util.ArrayMap; +import android.text.TextUtils; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.taobao.weex.WXEnvironment; +import com.taobao.weex.bridge.WXBridgeManager; +import com.taobao.weex.utils.WXUtils; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +/** + * Created by furture on 2018/1/23. + * template data manager, manage data and render state + */ +public class CellDataManager { + + private static final String VIRTUAL_COMPONENT_SEPRATOR = "@"; + + public static final String SUB_COMPONENT_TEMPLATE_ID = "@templateId"; + + public static final String VIRTUAL_COMPONENT_ID = "@virtualComponentId"; + + /** + * template list data, update should vis data manager interface, this field is read only + * */ + JSONArray listData; + + /** + * current template list + * */ + public final WXRecyclerTemplateList templateList; + + /** + * data render state for position, itemIdãdirtyãand other info + * */ + private Map<Integer, CellRenderState> renderStates = new ArrayMap<>(); + + + /** + * list virtual component data, rendet state + * key virtual component id, value is cell render state. + * */ + private Map<String, CellRenderState> virtualComponentRenderStates; + + + + /** + * template list data manager + * */ + public CellDataManager(WXRecyclerTemplateList templateList) { + this.templateList = templateList; + } + + /** + * get current render state for current position, never return null. + * */ + public CellRenderState getRenderState(int position){ + CellRenderState renderState = renderStates.get(position); + if(renderState == null){ + renderState = new CellRenderState(); + renderState.position = position; + renderStates.put(position, renderState); + } + if(position != renderState.position) { + renderState.position = position; + renderState.hasPositionChange = true; + } + return renderState; + } + + /** + * @param virutalComponentId virutalComponentId + * @param data update virutalComponent's data + * update virtual component data + * */ + public void updateVirtualComponentData(String virutalComponentId, Object data){ + if(virtualComponentRenderStates != null){ + CellRenderState cellRenderState = virtualComponentRenderStates.get(virutalComponentId); + if(cellRenderState != null){ + cellRenderState.getVirtualComponentDatas().put(virutalComponentId, data); + cellRenderState.hasVirtualCompoentUpdate = true; + }else{ + if(WXEnvironment.isApkDebugable()) { + throw new IllegalArgumentException("virtualComponentDatas illegal state empty render state" + virutalComponentId); + } + } + }else{ + if(WXEnvironment.isApkDebugable()){ + throw new IllegalArgumentException("virtualComponentDatas illegal state " + virutalComponentId); + } + } + } + + /** + * @param position current position virtual component + * @param virutalComponentId virutalComponentId + * @param data update virutalComponent's data + * create virtual component data + * */ + public void createVirtualComponentData(int position, String virutalComponentId, Object data){ + if(virtualComponentRenderStates == null){ + virtualComponentRenderStates = new HashMap<>(8); + } + CellRenderState renderState = renderStates.get(position); + renderState.getVirtualComponentDatas().put(virutalComponentId, data); + virtualComponentRenderStates.put(virutalComponentId, renderState); + + } + + + + + /** + * @param listData setList data + * clear render state for current cell, and virtual component data + * */ + public void setListData(JSONArray listData) { + if(this.listData != listData) { + if(this.listData != null){ + if(WXUtils.getBoolean(templateList.getDomObject().getAttrs().get("exitDetach"), true)){ + for(int i=0; i<this.listData.size(); i++){ + cleanRenderState(renderStates.remove(i)); + } + } + } + this.listData = listData; + renderStates.clear(); + if (virtualComponentRenderStates != null) { + virtualComponentRenderStates.clear(); + } + } + } + + /** + * @param index insert index + * @param data data object + * insert data at current index + * */ + public boolean insertData(int index, Object data){ + listData.add(index, data); + boolean renderStateChange = false; + for(int i=listData.size(); i>= index; i--){ + CellRenderState state = renderStates.remove(i); + if(state != null){ + renderStates.put(i + 1, state); + renderStateChange = true; + } + } + return renderStateChange; + } + + /** + * + * @param index insert index + * @param data data object + * insert data at current index + * */ + public boolean insertRange(int index, JSONArray data){ + listData.addAll(index, data); + boolean renderStateChange = false; + for(int i = listData.size()-1; i >= index; i--){ + CellRenderState state = renderStates.remove(i); + if(state != null){ + renderStates.put(i + 1, state); + renderStateChange = true; + } + } + return renderStateChange; + } + + /** + * @param data data object + * @param index insert index + * update data, reset new render state, + * return true if only data changed, false if viewType changed + * */ + public boolean updateData(Object data, int index){ + boolean onlyDataChange = TextUtils.equals(templateList.getTemplateKey(index), templateList.getTemplateKey(data)); + listData.set(index, data); + if(!onlyDataChange){ + //structure changed, reset render state + cleanRenderState(renderStates.remove(index)); + }else{ + CellRenderState renderState = renderStates.get(index); + if(renderState != null){ + renderState.hasDataUpdate = true; + } + } + return onlyDataChange; + } + + /** + * @param index + * remove data, and its render state and virtual's data + * */ + public void removeData(Integer index){ + listData.remove((int)index); //remove by index + cleanRenderState(renderStates.remove(index)); + int count = listData.size() + 1; + for(int i=index + 1; i < count; i++){ + CellRenderState state = renderStates.remove(i); + if(state != null){ + renderStates.put(i-1, state); + } + } + } + + /** + * clean render state, if has virtual component, call virtual component detach lifecycle + * */ + private void cleanRenderState(CellRenderState renderState){ + if(renderState == null){ + return; + } + if(renderState.hasVirtualComponents()){ + Collection<String> virtualComponentIds = renderState.getVirtualComponentIds().values(); + for(String virtualComponentId : virtualComponentIds){ + if(virtualComponentRenderStates != null){ + virtualComponentRenderStates.remove(virtualComponentId); + } + WXBridgeManager.getInstance().asyncCallJSEventVoidResult(WXBridgeManager.METHD_COMPONENT_HOOK_SYNC, + templateList.getInstanceId(), + null, + virtualComponentId, VirtualComponentLifecycle.LIFECYCLE, VirtualComponentLifecycle.DETACH, null); + + } + } + } + + + /** + * create virtualComponentId + * */ + public static String createVirtualComponentId(String listRef, String viewTreeKey, long itemId){ + return listRef + VIRTUAL_COMPONENT_SEPRATOR + viewTreeKey + VIRTUAL_COMPONENT_SEPRATOR + itemId; + } + + /** + * get list ref from virtualComponentId + * */ + public static String getListRef(String virtualComponentId){ + if(virtualComponentId == null){ + return null; + } + return virtualComponentId.split(VIRTUAL_COMPONENT_SEPRATOR)[0]; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/886d859a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/CellRenderContext.java ---------------------------------------------------------------------- diff --git a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/CellRenderContext.java b/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/CellRenderContext.java new file mode 100644 index 0000000..7d59848 --- /dev/null +++ b/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/CellRenderContext.java @@ -0,0 +1,79 @@ +/** + * 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.ui.component.list.template; + +import com.taobao.weex.el.parse.ArrayStack; + +import java.util.HashMap; +import java.util.Map; + +/** + * Created by furture on 2018/1/23. + * render context for cell template + */ +public class CellRenderContext { + /** + * code execute stack + * */ + public ArrayStack stack = new ArrayStack(); + /** + * init context data + * */ + public Map map = new HashMap(8); + + + /** + * component data context + * */ + public CellRenderState renderState; + + /** + * current position + * */ + public int position; + + /** + * current list component + * */ + public WXRecyclerTemplateList templateList; + + + /** + * get current render state + * */ + public CellRenderState getRenderState() { + if(renderState != null){ + renderState = templateList.getCellDataManager().getRenderState(position); + } + return renderState; + } + + + public void clear(){ + if(stack.getList().size() > 0) { + stack.getList().clear(); + } + if(map.size() > 0) { + map.clear(); + } + renderState = null; + position = 0; + templateList = null; + } +} http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/886d859a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/CellRenderState.java ---------------------------------------------------------------------- diff --git a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/CellRenderState.java b/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/CellRenderState.java new file mode 100644 index 0000000..2f71a81 --- /dev/null +++ b/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/CellRenderState.java @@ -0,0 +1,122 @@ +/** + * 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.ui.component.list.template; + +import android.support.v7.widget.RecyclerView; + +import com.taobao.weex.el.parse.ArrayStack; + +import java.util.HashMap; +import java.util.Map; + +/** + * Created by furture on 2018/1/24. + * render state for one cell, manage it's render state + */ +public class CellRenderState { + + /** + * dirty for current position, for example virtual component has update + * */ + boolean hasVirtualCompoentUpdate = false; + + /** + * has date update + * */ + boolean hasDataUpdate = false; + + /** + * position changed + * */ + boolean hasPositionChange = false; + + /** + * may use position, when position changed should be rendered + * */ + int position; + + /** + * unique itemId for current position, generate via key core + * */ + long itemId = RecyclerView.NO_ID; + + + + /** + * virtualCompoentId for cell, key is viewTreeKey, value is virutalComponentId. + * lazy init, + * */ + private Map<String, String> virtualComponentIds; + private Map<String, Object> virtualComponentDatas; + + + /** + * mark once statements has rendered + * */ + private Map<String,ArrayStack> onceComponentStates; + + + public Map<String, String> getVirtualComponentIds() { + if(virtualComponentIds == null){ + virtualComponentIds = new HashMap<>(8); + } + return virtualComponentIds; + } + + /** + * return current cell has virtual component + * */ + public boolean hasVirtualComponents(){ + return virtualComponentIds != null && virtualComponentIds.size() > 0; + } + + public Map<String, Object> getVirtualComponentDatas() { + if(virtualComponentDatas == null){ + virtualComponentDatas = new HashMap<>(4); + } + return virtualComponentDatas; + } + + public Map<String, ArrayStack> getOnceComponentStates() { + if(onceComponentStates == null){ + onceComponentStates = new HashMap<>(); + } + return onceComponentStates; + } + + public boolean isDirty() { + return hasDataUpdate + || hasVirtualCompoentUpdate + || hasPositionChange; + } + + public boolean isHasDataUpdate() { + return hasDataUpdate; + } + + + + public void resetDirty(){ + hasDataUpdate = false; + hasVirtualCompoentUpdate = false; + hasPositionChange = false; + } + + +} http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/886d859a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/DomTreeBuilder.java ---------------------------------------------------------------------- diff --git a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/DomTreeBuilder.java b/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/DomTreeBuilder.java deleted file mode 100644 index 0aa6cde..0000000 --- a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/DomTreeBuilder.java +++ /dev/null @@ -1,100 +0,0 @@ -/** - * 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.ui.component.list.template; - -import com.taobao.weex.WXEnvironment; -import com.taobao.weex.WXSDKManager; -import com.taobao.weex.dom.DOMActionContext; -import com.taobao.weex.dom.WXDomObject; -import com.taobao.weex.dom.action.TraceableAction; -import com.taobao.weex.ui.component.WXComponent; -import com.taobao.weex.ui.component.WXComponentFactory; -import com.taobao.weex.ui.component.WXVContainer; -import com.taobao.weex.utils.WXLogUtils; - -/** - * Created by furture on 2017/10/2. - */ - -class DomTreeBuilder extends TraceableAction { - - - - - public WXComponent generateComponentTree(DOMActionContext context, WXDomObject dom, WXVContainer parent) { - if (dom == null) { - return null; - } - long startNanos = System.nanoTime(); - dom.setCloneThis(true); - WXComponent component = WXComponentFactory.newInstance(context.getInstance(), dom, parent); - if (component != null) { - component.mTraceInfo.domThreadStart = dom.mDomThreadTimestamp; - component.mTraceInfo.rootEventId = mTracingEventId; - component.mTraceInfo.domQueueTime = mDomQueueTime; - } - context.registerComponent(dom.getRef(), component); - if (component instanceof WXVContainer) { - WXVContainer container = (WXVContainer) component; - WXDomObject parentDom = (WXDomObject) container.getDomObject(); - for (int i = 0; i < dom.childCount(); ++i) { - WXDomObject child = dom.getChild(i); - if (child != null) { - WXComponent childComponent = generateComponentTree(context, child, container); - container.addChild(childComponent); - WXDomObject childDomObject = (WXDomObject) childComponent.getDomObject(); - if(childDomObject != child) { - int index = parentDom.index(child); - parentDom.add(childDomObject, index); - if(index >= 0) { - parentDom.remove(child); - i--; - } - RuntimeException exception = new IllegalArgumentException(childDomObject.getClass().getName() - + " not support clone this"); - WXLogUtils.e("weex", exception); - if(WXEnvironment.isApkDebugable()){ - throw exception; - } - } - } - } - } - dom.setCloneThis(false); - if (component != null) { - component.mTraceInfo.domThreadNanos = System.nanoTime() - startNanos; - } - return component; - } - - public static final WXComponent buildTree(WXDomObject domObject, WXVContainer parent){ - DOMActionContext domActionContext = WXSDKManager.getInstance().getWXDomManager().getDomContext(parent.getInstanceId()); - if(domActionContext == null){ - return null; - } - DomTreeBuilder builder = new DomTreeBuilder(); - domObject.traverseTree( - domActionContext.getAddDOMConsumer(), - domActionContext.getApplyStyleConsumer() - ); - return builder.generateComponentTree(domActionContext, domObject, parent); - - } - -} http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/886d859a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/PositionRef.java ---------------------------------------------------------------------- diff --git a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/PositionRef.java b/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/PositionRef.java new file mode 100644 index 0000000..f25c9a9 --- /dev/null +++ b/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/PositionRef.java @@ -0,0 +1,64 @@ +/** + * 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.ui.component.list.template; + +/** + * position render state, when render state change, position changed + * Created by furture on 2018/2/2. + */ +public class PositionRef extends Number{ + + private CellRenderState renderState; + + public PositionRef(CellRenderState renderState) { + this.renderState = renderState; + } + + @Override + public int intValue() { + return getPosition(); + } + + @Override + public long longValue() { + return getPosition(); + } + + @Override + public float floatValue() { + return getPosition(); + } + + @Override + public double doubleValue() { + return getPosition(); + } + + private int getPosition(){ + if(renderState == null){ + return -1; + } + return renderState.position; + } + + @Override + public String toString() { + return String.valueOf(getPosition()); + } +} http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/886d859a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/VirtualComponentLifecycle.java ---------------------------------------------------------------------- diff --git a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/VirtualComponentLifecycle.java b/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/VirtualComponentLifecycle.java new file mode 100644 index 0000000..5466621 --- /dev/null +++ b/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/VirtualComponentLifecycle.java @@ -0,0 +1,45 @@ +/** + * 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.ui.component.list.template; + + +/** + * Created by furture on 2018/2/1. + */ + +public class VirtualComponentLifecycle { + + /** + * lifecycle + * */ + public static final String LIFECYCLE = "lifecycle"; + + + /** + * virtual component lifecycle + * */ + public static final String CREATE = "create"; + + public static final String ATTACH = "attach"; + + public static final String SYNSTATE = "syncState"; + + public static final String DETACH = "detach"; + +}
