async layout and preload cell and quick convert parameter
Project: http://git-wip-us.apache.org/repos/asf/incubator-weex/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-weex/commit/a17eb2c6 Tree: http://git-wip-us.apache.org/repos/asf/incubator-weex/tree/a17eb2c6 Diff: http://git-wip-us.apache.org/repos/asf/incubator-weex/diff/a17eb2c6 Branch: refs/heads/website Commit: a17eb2c6859999261889ec08cf519d0b9ed4996e Parents: b9015dd Author: jianbai.gbj <jianbai....@alibaba-inc.com> Authored: Thu Sep 28 16:32:50 2017 +0800 Committer: Hanks <zhanghan...@gmail.com> Committed: Tue Oct 10 21:42:52 2017 +0800 ---------------------------------------------------------------------- .../java/com/taobao/weex/dom/WXDomObject.java | 3 + .../com/taobao/weex/dom/WXTextDomObject.java | 3 +- .../weex/ui/component/binding/Layouts.java | 101 ++++++++++++------ .../weex/ui/component/binding/Statements.java | 26 ++++- .../list/template/TemplateViewHolder.java | 4 + .../list/template/WXRecyclerTemplateList.java | 103 +++++++++++++++++-- .../taobao/weex/utils/WXReflectionUtils.java | 6 ++ 7 files changed, 197 insertions(+), 49 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/a17eb2c6/android/sdk/src/main/java/com/taobao/weex/dom/WXDomObject.java ---------------------------------------------------------------------- diff --git a/android/sdk/src/main/java/com/taobao/weex/dom/WXDomObject.java b/android/sdk/src/main/java/com/taobao/weex/dom/WXDomObject.java index c766e35..001545d 100644 --- a/android/sdk/src/main/java/com/taobao/weex/dom/WXDomObject.java +++ b/android/sdk/src/main/java/com/taobao/weex/dom/WXDomObject.java @@ -438,6 +438,9 @@ public class WXDomObject extends CSSNode implements Cloneable,ImmutableDomObject mAttributes = new WXAttr(); } mAttributes.putAll(attrs); + if(hasNewLayout()){ + markUpdateSeen(); + } super.dirty(); } http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/a17eb2c6/android/sdk/src/main/java/com/taobao/weex/dom/WXTextDomObject.java ---------------------------------------------------------------------- diff --git a/android/sdk/src/main/java/com/taobao/weex/dom/WXTextDomObject.java b/android/sdk/src/main/java/com/taobao/weex/dom/WXTextDomObject.java index 06c4e8a..3456bde 100644 --- a/android/sdk/src/main/java/com/taobao/weex/dom/WXTextDomObject.java +++ b/android/sdk/src/main/java/com/taobao/weex/dom/WXTextDomObject.java @@ -41,6 +41,7 @@ import android.text.style.AlignmentSpan; import android.text.style.ForegroundColorSpan; import com.taobao.weex.WXEnvironment; import com.taobao.weex.common.Constants; +import com.taobao.weex.common.WXThread; import com.taobao.weex.dom.flex.CSSConstants; import com.taobao.weex.dom.flex.CSSNode; import com.taobao.weex.dom.flex.FloatUtil; @@ -185,7 +186,7 @@ public class WXTextDomObject extends WXDomObject { hasBeenMeasured = false; if (layout != null && !layout.equals(atomicReference.get()) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - if(Looper.getMainLooper().getThread().getId() != Thread.currentThread().getId()){ + if(Thread.currentThread() instanceof WXThread){ warmUpTextLayoutCache(layout); } } http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/a17eb2c6/android/sdk/src/main/java/com/taobao/weex/ui/component/binding/Layouts.java ---------------------------------------------------------------------- diff --git a/android/sdk/src/main/java/com/taobao/weex/ui/component/binding/Layouts.java b/android/sdk/src/main/java/com/taobao/weex/ui/component/binding/Layouts.java index f80bc1e..5b8d32c 100644 --- a/android/sdk/src/main/java/com/taobao/weex/ui/component/binding/Layouts.java +++ b/android/sdk/src/main/java/com/taobao/weex/ui/component/binding/Layouts.java @@ -21,60 +21,40 @@ package com.taobao.weex.ui.component.binding; import android.os.AsyncTask; -import android.speech.tts.Voice; +import android.util.Log; +import com.taobao.weex.WXEnvironment; import com.taobao.weex.WXSDKInstance; +import com.taobao.weex.common.Constants; import com.taobao.weex.dom.WXDomObject; import com.taobao.weex.dom.flex.CSSLayoutContext; import com.taobao.weex.ui.component.WXComponent; import com.taobao.weex.ui.component.WXVContainer; import com.taobao.weex.ui.component.list.template.TemplateViewHolder; +import com.taobao.weex.utils.WXLogUtils; /** * Created by furture on 2017/8/21. */ public class Layouts { /** - * do dom layout, and set layout to component + * do dom layout async or sync , and set layout to component on main. + * on first use do sync layout, when compontnet reuse do async layout * */ - public static void doLayout(final TemplateViewHolder templateViewHolder){ - final CSSLayoutContext layoutContext = templateViewHolder.getLayoutContext(); + public static void doLayoutAsync(final TemplateViewHolder templateViewHolder){ final WXComponent component = templateViewHolder.getComponent(); - final WXSDKInstance instance = component.getInstance(); final int position = templateViewHolder.getHolderPosition(); if(templateViewHolder.asyncTask != null){ templateViewHolder.asyncTask.cancel(true); + templateViewHolder.asyncTask = null; } AsyncTask<Void, Void, Void> asyncTask = new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... params) { if(templateViewHolder.getHolderPosition() == position){ - - WXDomObject domObject = (WXDomObject) component.getDomObject(); - domObject.traverseTree(new WXDomObject.Consumer() { - @Override - public void accept(WXDomObject dom) { - if(instance == null || instance.isDestroy()){ - return; - } - if(!dom.hasUpdate()){ - return; - } - dom.layoutBefore(); - } - }); - domObject.calculateLayout(layoutContext); - domObject.traverseTree( new WXDomObject.Consumer() { - @Override - public void accept(WXDomObject dom) { - if(instance == null || instance.isDestroy()){ - return; - } - if (dom.hasUpdate()) { - dom.layoutAfter(); - } - } - }); + if(component.getInstance() != null && !component.getInstance().isDestroy()) { + doSafeLayout(component, templateViewHolder.getLayoutContext()); + } } return null; } @@ -82,14 +62,69 @@ public class Layouts { @Override protected void onPostExecute(Void aVoid) { if(position == templateViewHolder.getHolderPosition()) { - setLayout(component, false); + if(component.getInstance() != null && !component.getInstance().isDestroy()) { + setLayout(component, false); + } } } }; templateViewHolder.asyncTask = asyncTask; - asyncTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); + asyncTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); //serial executor is better + } + /** + * safe layout + * */ + public static void doSafeLayout(WXComponent component, final CSSLayoutContext layoutContext){ + try{ + long start = System.currentTimeMillis(); + doLayout(component, layoutContext); + if(WXEnvironment.isApkDebugable()){ + WXLogUtils.d("WXTemplateList", + component.getDomObject().getAttrs().get(Constants.Name.Recycler.SLOT_TEMPLATE_TYPE) + Thread.currentThread().getName() + " doSafeLayout used " + + (System.currentTimeMillis() - start)); + } + }catch (Exception e){ + if(WXEnvironment.isApkDebugable()){ + WXLogUtils.e("WXTemplateListdoSafeLayout", e); + } + } + } + + private static void doLayout(WXComponent component, final CSSLayoutContext layoutContext){ + WXDomObject domObject = (WXDomObject) component.getDomObject(); + final WXSDKInstance instance = component.getInstance(); + domObject.traverseTree(new WXDomObject.Consumer() { + @Override + public void accept(WXDomObject dom) { + if(instance == null || instance.isDestroy()){ + return; + } + if(!dom.hasUpdate()){ + return; + } + dom.layoutBefore(); + } + }); + if(instance != null && !instance.isDestroy()){ + domObject.calculateLayout(layoutContext); + } + domObject.traverseTree( new WXDomObject.Consumer() { + @Override + public void accept(WXDomObject dom) { + if(instance == null || instance.isDestroy()){ + return; + } + if (dom.hasUpdate()) { + dom.layoutAfter(); + } + } + }); + } + + + /** * recursive set layout to component, http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/a17eb2c6/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 755bae2..6912335 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 @@ -18,21 +18,26 @@ */ 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.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.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; import com.taobao.weex.ui.component.WXComponent; 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.utils.WXLogUtils; import com.taobao.weex.utils.WXUtils; @@ -198,9 +203,11 @@ public class Statements { renderNodeDomObject.getAttrs().setStatement(null); // clear node's statement parentDomObject.add(renderNodeDomObject, renderIndex); parent.addChild(renderNode, renderIndex); - parent.createChildViewAt(renderIndex); - renderNode.applyLayoutAndEvent(renderNode); - renderNode.bindData(renderNode); + if(Thread.currentThread() == Looper.getMainLooper().getThread()) { + parent.createChildViewAt(renderIndex); + renderNode.applyLayoutAndEvent(renderNode); + renderNode.bindData(renderNode); + } } doBindingAttrsEventAndRenderChildNode(renderNode, domObject, context); renderIndex++; @@ -307,8 +314,17 @@ public class Statements { } if(dynamic.size() > 0) { - domObject.updateAttr(dynamic); - component.updateProperties(dynamic); + if(dynamic.size() == 1 + && dynamic.get(Constants.Name.SRC) != null + && component instanceof WXImage){ + //for image avoid dirty layout, only update src attrs + domObject.getAttrs().put(Constants.Name.SRC, dynamic.get(Constants.Name.SRC)); + }else { + domObject.updateAttr(dynamic); //dirty layout + } + if(Thread.currentThread() == Looper.getMainLooper().getThread()) { + component.updateProperties(dynamic); + } } } WXEvent event = domObject.getEvents(); http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/a17eb2c6/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/TemplateViewHolder.java ---------------------------------------------------------------------- diff --git a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/TemplateViewHolder.java b/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/TemplateViewHolder.java index 21930eb..a418e3e 100644 --- a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/TemplateViewHolder.java +++ b/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/TemplateViewHolder.java @@ -67,6 +67,10 @@ public class TemplateViewHolder extends ListBaseViewHolder { return layoutContext; } + public void setLayoutContext(CSSLayoutContext layoutContext){ + this.layoutContext = layoutContext; + } + public int getHolderPosition() { return holderPosition; http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/a17eb2c6/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 0454aba..5d23bf6 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,6 +22,7 @@ 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.support.annotation.NonNull; @@ -53,6 +54,7 @@ import com.taobao.weex.dom.WXCellDomObject; import com.taobao.weex.dom.WXDomObject; import com.taobao.weex.dom.WXEvent; import com.taobao.weex.dom.WXRecyclerDomObject; +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; @@ -82,6 +84,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import static com.taobao.weex.common.Constants.Name.LOADMOREOFFSET; @@ -135,7 +138,8 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp private Map<String, WXCell> mTemplates; private String listDataTemplateKey = Constants.Name.Recycler.SLOT_TEMPLATE_TYPE; private Runnable listUpdateRunnable; - + private ConcurrentHashMap<String, WXCell> mTemplatesCache; + private ConcurrentHashMap<String, Boolean> mTemplateRendered; /** * sticky helper @@ -177,12 +181,20 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp mTemplateViewTypes = new ArrayMap<>(); mTemplateViewTypes.put("", 0); //empty view, when template was not sended mTemplates = new HashMap<>(); + mTemplatesCache = new ConcurrentHashMap<>(); mStickyHelper = new TemplateStickyHelper(this); cellLifecycleManager = new CellLifecycleManager(this); orientation = mDomObject.getOrientation(); listDataTemplateKey = WXUtils.getString(getDomObject().getAttrs().get(Constants.Name.Recycler.LIST_DATA_TEMPLATE_KEY), Constants.Name.Recycler.SLOT_TEMPLATE_TYPE); 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) { + JSONArray array = (JSONArray)getDomObject().getAttrs().get(Constants.Name.Recycler.LIST_DATA); + if(array.size() > 0) { + listData = array; + } + } + mTemplateRendered = new ConcurrentHashMap<>(); } @Override @@ -204,6 +216,7 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp } RecyclerViewBaseAdapter recyclerViewBaseAdapter = new RecyclerViewBaseAdapter<>(this); recyclerViewBaseAdapter.setHasStableIds(true); + bounceRecyclerView.getInnerView().setItemAnimator(null); bounceRecyclerView.getInnerView().setItemViewCacheSize(itemViewCacheSize); bounceRecyclerView.getInnerView().setHasFixedSize(hasFixedSize); bounceRecyclerView.setRecyclerViewBaseAdapter(recyclerViewBaseAdapter); @@ -533,7 +546,13 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp Object templateId = child.getDomObject().getAttrs().get(Constants.Name.Recycler.SLOT_TEMPLATE_TYPE); String key = WXUtils.getString(templateId, null); if(key != null){ + //set visible false, skip layout in dom thread, set visible true in onCreateViewHolder + if(child.getDomObject() != null) { + WXDomObject domObject = (WXDomObject) child.getDomObject(); + domObject.setVisible(false); + } mTemplates.put(key, (WXCell) child); + asyncPreloadCellCopyCache(key); if(mTemplateViewTypes.get(key) == null){ mTemplateViewTypes.put(key, mTemplateViewTypes.size()); } @@ -1017,8 +1036,7 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp if(WXEnvironment.isApkDebugable()){ WXLogUtils.d(TAG, position + getTemplateKey(position) + " onBindViewHolder render used " + (System.currentTimeMillis() - start)); } - - Layouts.doLayout(templateViewHolder); + Layouts.doLayoutAsync(templateViewHolder); cellLifecycleManager.onAttach(position, component); if(WXEnvironment.isApkDebugable()){ WXLogUtils.d(TAG, position + getTemplateKey(position) + " onBindViewHolder layout used " + (System.currentTimeMillis() - start)); @@ -1034,30 +1052,95 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp view.setLayoutParams(new FrameLayout.LayoutParams(0, 0)); return new TemplateViewHolder(view, viewType); } - WXCell component = (WXCell) Statements.copyComponentTree(source); - if(component.getDomObject() instanceof WXCellDomObject - && getDomObject() instanceof WXRecyclerDomObject){ - WXCellDomObject domObject = (WXCellDomObject) component.getDomObject(); - domObject.setRecyclerDomObject((WXRecyclerDomObject) getDomObject()); + long start = System.currentTimeMillis(); + WXCell component = mTemplatesCache.remove(template); + boolean needLayout = false; + if(component == null) { + component = (WXCell) copyCell(source); + needLayout = true; + } + asyncPreloadCellCopyCache(template); + CSSLayoutContext layoutContext = null; + if(needLayout){ + layoutContext = new CSSLayoutContext(); + Layouts.doSafeLayout(component, layoutContext); + if(WXEnvironment.isApkDebugable()){ + WXLogUtils.d(TAG, template + " onCreateViewHolder sync layout used " + (System.currentTimeMillis() - start)); + } } + Layouts.setLayout(component, false); component.lazy(false); - long start = System.currentTimeMillis(); component.createView(); if(WXEnvironment.isApkDebugable()){ WXLogUtils.d(TAG, template + " onCreateViewHolder view used " + (System.currentTimeMillis() - start)); } component.applyLayoutAndEvent(component); if(WXEnvironment.isApkDebugable()) { - WXLogUtils.d(TAG, template + " onCreateViewHolder layout used " + (System.currentTimeMillis() - start)); + WXLogUtils.d(TAG, template + " onCreateViewHolder apply layout used " + (System.currentTimeMillis() - start)); } component.bindData(component); if(WXEnvironment.isApkDebugable()) { WXLogUtils.d(TAG, template + " onCreateViewHolder bindData used " + (System.currentTimeMillis() - start)); } TemplateViewHolder templateViewHolder = new TemplateViewHolder(component, viewType); + templateViewHolder.setLayoutContext(layoutContext); return templateViewHolder; } + private void asyncPreloadCellCopyCache(final String template) { + final WXCell cell = mTemplates.get(template); + if(cell == null){ + return; + } + if(mTemplatesCache.get(template) != null){ + return; + } + AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() { + @Override + public void run() { + if(cell.getInstance() == null || cell.getInstance().isDestroy()){ + return; + } + WXCell component = (WXCell) copyCell(cell); + if(cell.getInstance() == null || cell.getInstance().isDestroy()){ + return; + } + Layouts.doSafeLayout(component, new CSSLayoutContext()); + mTemplatesCache.put(template, component); + } + }); + } + + /** + * copy cell component from source, and return source + * */ + private WXComponent copyCell(WXComponent cell){ + /** pre render for cell */ + Boolean rendered = mTemplateRendered.get(cell.getRef()); + if(rendered == null || !rendered) { + if(listData != null){ + for(int i=0; i<listData.size(); i++){ + WXCell source = getSourceTemplate(i); + if(source == cell){ + Statements.doRender(cell, getStackContextForPosition(i)); + mTemplateRendered.put(source.getRef(), true); + break; + } + } + } + } + WXCell component = (WXCell) Statements.copyComponentTree(cell); + if(component.getDomObject() != null){ + ((WXDomObject)component.getDomObject()).setVisible(true); + } + if(component.getDomObject() instanceof WXCellDomObject + && getDomObject() instanceof WXRecyclerDomObject){ + WXCellDomObject domObject = (WXCellDomObject) component.getDomObject(); + domObject.setRecyclerDomObject((WXRecyclerDomObject) getDomObject()); + } + return component; + } + /** * @param position * when template not send, return an invalid id, use empty view holder. http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/a17eb2c6/android/sdk/src/main/java/com/taobao/weex/utils/WXReflectionUtils.java ---------------------------------------------------------------------- diff --git a/android/sdk/src/main/java/com/taobao/weex/utils/WXReflectionUtils.java b/android/sdk/src/main/java/com/taobao/weex/utils/WXReflectionUtils.java index 930c383..5bef6d3 100644 --- a/android/sdk/src/main/java/com/taobao/weex/utils/WXReflectionUtils.java +++ b/android/sdk/src/main/java/com/taobao/weex/utils/WXReflectionUtils.java @@ -20,6 +20,8 @@ package com.taobao.weex.utils; import android.text.TextUtils; import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; @@ -39,6 +41,10 @@ public class WXReflectionUtils { return value.getClass().isAssignableFrom(double.class) ? value : WXUtils.getDouble(value); } else if (paramClazz == float.class) { return value.getClass().isAssignableFrom(float.class) ? value : WXUtils.getFloat(value); + } else if (paramClazz == JSONArray.class && value != null && value.getClass() == JSONArray.class) { + return value; + } else if (paramClazz == JSONObject.class && value != null && value.getClass() == JSONObject.class) { + return value; } else { return JSON.parseObject(value instanceof String ? (String) value : JSON.toJSONString(value), paramClazz); }