From 6406b664abb68ab943738a68feed6d1765e89c4f Mon Sep 17 00:00:00 2001 From: zhongcang Date: Wed, 19 Jun 2019 18:08:33 +0800 Subject: [PATCH] [Android] add new white screen check logic fix fix --- .../java/com/alibaba/weex/WXApplication.java | 2 + .../extend/adapter/DefaultConfigAdapter.java | 32 ++++ .../java/com/taobao/weex/WXSDKInstance.java | 25 +++ .../java/com/taobao/weex/WXSDKManager.java | 4 + .../java/com/taobao/weex/bridge/WXBridge.java | 2 + .../taobao/weex/bridge/WXBridgeManager.java | 5 + .../com/taobao/weex/common/WXErrorCode.java | 3 + .../weex/performance/WXStateRecord.java | 128 +++++++++++++++ .../weex/performance/WhiteScreenUtils.java | 151 ++++++++++++++++++ .../ui/action/GraphicActionAddElement.java | 2 + .../taobao/weex/utils/WXExceptionUtils.java | 2 + 11 files changed, 356 insertions(+) create mode 100644 android/playground/app/src/main/java/com/alibaba/weex/extend/adapter/DefaultConfigAdapter.java create mode 100644 android/sdk/src/main/java/com/taobao/weex/performance/WXStateRecord.java create mode 100644 android/sdk/src/main/java/com/taobao/weex/performance/WhiteScreenUtils.java diff --git a/android/playground/app/src/main/java/com/alibaba/weex/WXApplication.java b/android/playground/app/src/main/java/com/alibaba/weex/WXApplication.java index e3cebf1472..ec714ed05a 100644 --- a/android/playground/app/src/main/java/com/alibaba/weex/WXApplication.java +++ b/android/playground/app/src/main/java/com/alibaba/weex/WXApplication.java @@ -30,6 +30,7 @@ import com.alibaba.weex.commons.adapter.PicassoBasedDrawableLoader; import com.alibaba.weex.extend.adapter.ApmGenerator; import com.alibaba.weex.extend.adapter.DefaultAccessibilityRoleAdapter; +import com.alibaba.weex.extend.adapter.DefaultConfigAdapter; import com.alibaba.weex.extend.adapter.InterceptWXHttpAdapter; import com.alibaba.weex.extend.adapter.WXAnalyzerDemoListener; import com.alibaba.weex.extend.component.WXComponentSyncTest; @@ -83,6 +84,7 @@ public void onCreate() { .setApmGenerater(new ApmGenerator()) .build() ); + WXSDKManager.getInstance().setWxConfigAdapter(new DefaultConfigAdapter()); WXSDKManager.getInstance().addWXAnalyzer(new WXAnalyzerDemoListener()); WXAnalyzerDataTransfer.isOpenPerformance = false; diff --git a/android/playground/app/src/main/java/com/alibaba/weex/extend/adapter/DefaultConfigAdapter.java b/android/playground/app/src/main/java/com/alibaba/weex/extend/adapter/DefaultConfigAdapter.java new file mode 100644 index 0000000000..fa31138238 --- /dev/null +++ b/android/playground/app/src/main/java/com/alibaba/weex/extend/adapter/DefaultConfigAdapter.java @@ -0,0 +1,32 @@ +/** + * 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.alibaba.weex.extend.adapter; + +import com.taobao.weex.adapter.IWXConfigAdapter; + +/** + * @author zhongcang + * @date 2019/6/19 + */ +public class DefaultConfigAdapter implements IWXConfigAdapter { + @Override + public String getConfig(String nameSpace, String key, String defaultValue) { + return defaultValue; + } +} diff --git a/android/sdk/src/main/java/com/taobao/weex/WXSDKInstance.java b/android/sdk/src/main/java/com/taobao/weex/WXSDKInstance.java index 761d7a256b..b5af9e0052 100644 --- a/android/sdk/src/main/java/com/taobao/weex/WXSDKInstance.java +++ b/android/sdk/src/main/java/com/taobao/weex/WXSDKInstance.java @@ -71,6 +71,8 @@ import com.taobao.weex.instance.InstanceOnFireEventInterceptor; import com.taobao.weex.layout.ContentBoxMeasurement; import com.taobao.weex.performance.WXInstanceApm; +import com.taobao.weex.performance.WXStateRecord; +import com.taobao.weex.performance.WhiteScreenUtils; import com.taobao.weex.tracing.WXTracing; import com.taobao.weex.ui.action.GraphicActionAddElement; import com.taobao.weex.ui.component.NestedContainer; @@ -862,6 +864,7 @@ private void renderInternal(String pageName, WXSDKManager.getInstance().postOnUiThread(new Runnable() { @Override public void run() { + checkWhiteScreen(); if(isDestroy || hasException || isRenderSuccess) { return; } @@ -880,6 +883,28 @@ public void run() { } } + + private void checkWhiteScreen(){ + if (isDestroy ||!WhiteScreenUtils.doWhiteScreenCheck()){ + return; + } + + boolean isWS = WhiteScreenUtils.isWhiteScreen(this); + if (!isWS){ + return; + } + WXErrorCode errorCode = WXErrorCode.WX_ERROR_WHITE_SCREEN; + Map args = new HashMap<>(1); + String vieTreeMsg = WhiteScreenUtils.getViewMsg(this); + args.put("viewTree",null == vieTreeMsg?"null viewTreeMsg":vieTreeMsg); + + for (Map.Entry entry: WXStateRecord.getInstance().getStateInfo().entrySet()){ + args.put(entry.getKey(),entry.getValue()); + } + WXExceptionUtils.commitCriticalExceptionRT(getInstanceId(),errorCode,"checkEmptyScreen",errorCode.getErrorMsg(),args); + } + + public boolean skipFrameworkInit(){ return isDataRender() && !mDisableSkipFrameworkInit; } diff --git a/android/sdk/src/main/java/com/taobao/weex/WXSDKManager.java b/android/sdk/src/main/java/com/taobao/weex/WXSDKManager.java index 0bde6c88c0..1f70920c19 100644 --- a/android/sdk/src/main/java/com/taobao/weex/WXSDKManager.java +++ b/android/sdk/src/main/java/com/taobao/weex/WXSDKManager.java @@ -236,6 +236,10 @@ public void postOnUiThread(Runnable runnable, long delayMillis) { mWXRenderManager.postOnUiThread(WXThread.secure(runnable), delayMillis); } + public void removeUITask(Runnable runnable){ + mWXRenderManager.removeTask(runnable); + } + public Map getAllInstanceMap() { return mAllInstanceMap; } diff --git a/android/sdk/src/main/java/com/taobao/weex/bridge/WXBridge.java b/android/sdk/src/main/java/com/taobao/weex/bridge/WXBridge.java index 87a54fd8ed..36c6ecb6cf 100644 --- a/android/sdk/src/main/java/com/taobao/weex/bridge/WXBridge.java +++ b/android/sdk/src/main/java/com/taobao/weex/bridge/WXBridge.java @@ -34,6 +34,7 @@ import com.taobao.weex.dom.CSSShorthand; import com.taobao.weex.layout.ContentBoxMeasurement; import com.taobao.weex.performance.WXInstanceApm; +import com.taobao.weex.performance.WXStateRecord; import com.taobao.weex.utils.WXExceptionUtils; import com.taobao.weex.utils.WXJsonUtils; import com.taobao.weex.utils.WXLogUtils; @@ -385,6 +386,7 @@ public void setJSFrmVersion(String version) { if (version != null) { WXEnvironment.JS_LIB_SDK_VERSION = version; } + WXStateRecord.getInstance().onJSFMInit(); } @Override diff --git a/android/sdk/src/main/java/com/taobao/weex/bridge/WXBridgeManager.java b/android/sdk/src/main/java/com/taobao/weex/bridge/WXBridgeManager.java index 8d3e0d98f9..27fead214c 100644 --- a/android/sdk/src/main/java/com/taobao/weex/bridge/WXBridgeManager.java +++ b/android/sdk/src/main/java/com/taobao/weex/bridge/WXBridgeManager.java @@ -61,6 +61,7 @@ import com.taobao.weex.dom.CSSShorthand; import com.taobao.weex.layout.ContentBoxMeasurement; import com.taobao.weex.performance.WXInstanceApm; +import com.taobao.weex.performance.WXStateRecord; import com.taobao.weex.ui.WXComponentRegistry; import com.taobao.weex.ui.action.ActionReloadPage; import com.taobao.weex.ui.action.BasicGraphicAction; @@ -609,6 +610,8 @@ public Object callNativeModule(String instanceId, String module, String method, return IWXBridge.INSTANCE_RENDERING_ERROR; } + WXStateRecord.getInstance().recordAction(instanceId,"callNativeModule:"+module+"."+method); + if (WXEnvironment.isApkDebugable() && BRIDGE_LOG_SWITCH) { mLodBuilder.append("[WXBridgeManager] callNativeModule >>>> instanceId:").append(instanceId) .append(", module:").append(module).append(", method:").append(method).append(", arguments:").append(arguments); @@ -848,8 +851,10 @@ public int callReportCrashReloadPage(String instanceId, String crashFile) { } catch (Throwable e) { e.printStackTrace(); } + WXStateRecord.getInstance().onJSCCrash(); callReportCrash(crashFile, instanceId, url); } else { + WXStateRecord.getInstance().onJSEngineReload(); commitJscCrashAlarmMonitor(IWXUserTrackAdapter.JS_BRIDGE, WXErrorCode.WX_ERR_RELOAD_PAGE, "reboot jsc Engine", instanceId, url); } diff --git a/android/sdk/src/main/java/com/taobao/weex/common/WXErrorCode.java b/android/sdk/src/main/java/com/taobao/weex/common/WXErrorCode.java index 913ec8565c..0556879074 100644 --- a/android/sdk/src/main/java/com/taobao/weex/common/WXErrorCode.java +++ b/android/sdk/src/main/java/com/taobao/weex/common/WXErrorCode.java @@ -123,6 +123,9 @@ public enum WXErrorCode { */ WX_ERR_RELOAD_PAGE("-2111", "reloadPage",ErrorType.NATIVE_ERROR,ErrorGroup.NATIVE), + WX_ERROR_WHITE_SCREEN("-2116", "WHITE_SCREEN",ErrorType.RENDER_ERROR,ErrorGroup.JS), + + /** * */ diff --git a/android/sdk/src/main/java/com/taobao/weex/performance/WXStateRecord.java b/android/sdk/src/main/java/com/taobao/weex/performance/WXStateRecord.java new file mode 100644 index 0000000000..ec2b7c313d --- /dev/null +++ b/android/sdk/src/main/java/com/taobao/weex/performance/WXStateRecord.java @@ -0,0 +1,128 @@ +package com.taobao.weex.performance; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +import com.taobao.weex.utils.WXUtils; + +/** + * @author zhongcang + * @date 2019/6/19 + */ +public class WXStateRecord { + + private RecordList mExceptionHistory; + private RecordList mActionHistory; + private RecordList mJsfmInitHistory; + private RecordList mJscCrashHistory; + private RecordList mJscReloadHistory; + + private static class SingleTonHolder { + private static final WXStateRecord S_INSTANCE = new WXStateRecord(); + } + + public static WXStateRecord getInstance() { + return SingleTonHolder.S_INSTANCE; + } + + private WXStateRecord() { + mExceptionHistory = new RecordList<>(5); + mActionHistory = new RecordList<>(10); + mJsfmInitHistory = new RecordList<>(3); + mJscCrashHistory = new RecordList<>(3); + mJscReloadHistory = new RecordList<>(5); + } + + /** + * check history exception (may be cause ws) + */ + public void recordException(String instanceId, String exception) { + String shortException = exception.length() > 200 ?exception.substring(0,200) : exception; + mExceptionHistory.add(new Info(WXUtils.getFixUnixTime(), instanceId, shortException)); + } + + /** + * check history action (may be occupy cpu by preInstance or some task) + */ + public void recordAction(String instanceId, String action) { + mActionHistory.add(new Info(WXUtils.getFixUnixTime(), instanceId, action)); + } + + /** + * check onJSFMInit time,and we know when jsfm is init sucess in reloadJsEngine case + */ + public void onJSFMInit() { + mJsfmInitHistory.add(new Info(WXUtils.getFixUnixTime(), "JSFM", "onJsfmInit")); + } + + /** + * check onJSEngineReload time,and we know how many times reload and each reload time + */ + public void onJSEngineReload() { + mJscReloadHistory.add(new Info(WXUtils.getFixUnixTime(), "", "onJSEngineReload")); + } + + /** + * check jsc crash time,and we know how many times jscCrash and each crash time + */ + public void onJSCCrash() { + mJscCrashHistory.add(new Info(WXUtils.getFixUnixTime(), "", "onJSCCrash")); + } + + public Map getStateInfo() { + Map stateInfo = new HashMap<>(5); + stateInfo.put("exceptionHistory", mExceptionHistory.toString()); + stateInfo.put("actionHistory", mActionHistory.toString()); + stateInfo.put("jsfmInitHistory", mJsfmInitHistory.toString()); + stateInfo.put("jscCrashHistory", mJscCrashHistory.toString()); + stateInfo.put("jscReloadHistory", mJscReloadHistory.toString()); + return stateInfo; + } + + private static class RecordList extends ArrayList { + private int maxSize; + + public RecordList(int maxSize) { + super(maxSize); + this.maxSize = maxSize; + } + + @Override + public boolean add(E e) { + if (this.size()>0 && this.size() >= maxSize){ + remove(this.size() - 1); + } + return super.add(e); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + int size = size(); + for (int i = 0; i < size; i++) { + builder.append('[').append(get(i).toString()).append(']').append("->"); + } + return builder.toString(); + } + } + + private static class Info { + private long time; + private String instanceId; + private String msg; + + public Info(long time, String instance, String msg) { + this.time = time; + this.instanceId = instance; + this.msg = msg; + } + + @Override + public String toString() { + return new StringBuilder() + .append(instanceId).append(',').append(time).append(',').append(msg) + .toString(); + } + } +} diff --git a/android/sdk/src/main/java/com/taobao/weex/performance/WhiteScreenUtils.java b/android/sdk/src/main/java/com/taobao/weex/performance/WhiteScreenUtils.java new file mode 100644 index 0000000000..80ab64a855 --- /dev/null +++ b/android/sdk/src/main/java/com/taobao/weex/performance/WhiteScreenUtils.java @@ -0,0 +1,151 @@ +/** + * 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.performance; + +import android.text.TextUtils; +import android.view.View; +import android.view.ViewGroup; +import com.taobao.weex.WXSDKInstance; +import com.taobao.weex.WXSDKManager; +import com.taobao.weex.adapter.IWXConfigAdapter; +import com.taobao.weex.ui.component.WXComponent; +import com.taobao.weex.ui.component.WXVContainer; +import org.json.JSONObject; + +/** + * @author zhongcang + * @date 2019/5/30 + */ +public class WhiteScreenUtils { + + public static boolean doWhiteScreenCheck() { + IWXConfigAdapter configAdapter = WXSDKManager.getInstance().getWxConfigAdapter(); + if (null == configAdapter) { + return false; + } + double randomValue = Math.random() * 100; + double max = 100; + try { + String configValue = configAdapter.getConfig("wxapm", "new_ws_sampling", "100"); + max = Double.valueOf(configValue); + } catch (Exception e) { + e.printStackTrace(); + } + return randomValue < max; + } + + public static boolean isWhiteScreen(WXSDKInstance instance) { + if (null == instance) { + return false; + } + View v = instance.getContainerView(); + if (!(v instanceof ViewGroup)) { + return false; + } + ViewGroup group = (ViewGroup)v; + if (group.getChildCount() == 0) { + return true; + } + return !hasLeafView(group); + } + + private static boolean hasLeafView(View v) { + if (!(v instanceof ViewGroup)) { + return true; + } + + ViewGroup group = (ViewGroup)v; + for (int i = 0; i < group.getChildCount(); i++) { + View child = group.getChildAt(i); + boolean res = hasLeafView(child); + if (res) { + return true; + } + } + return false; + } + + /** + * get instance viewTree && component tree msg + */ + public static String getViewMsg(WXSDKInstance instance) { + if (null == instance) { + return "nullInstance"; + } + View v = instance.getContainerView(); + StringBuilder builder = new StringBuilder(); + WXComponent component = instance.getRootComponent(); + if (null != component) { + builder.append("componentMsg:").append(getComponentTreeMsg(component)).append("-----"); + } + if (null != v) { + builder.append("viewTreeMsg:").append(geViewDetailTreeMsg(v)); + } + return builder.toString(); + } + + private static String getComponentTreeMsg(WXComponent component) { + if (null == component) { + return "nullComponent"; + } + + if (!(component instanceof WXVContainer)) { + return component.getRef(); + } + WXVContainer container = (WXVContainer)component; + StringBuilder builder = new StringBuilder(); + builder.append(" _start_ "); + for (int i = 0; i < container.getChildCount(); i++) { + builder.append(getComponentTreeMsg(container.getChild(i))).append(","); + } + builder.append(" _end_ "); + return builder.toString(); + } + + private static JSONObject geViewDetailTreeMsg(View view) { + if (null == view) { + return null; + } + JSONObject node = new JSONObject(); + try { + node.put("width", view.getWidth()); + node.put("height", view.getHeight()); + int[] location = new int[2]; + location[0] = -1; + location[1] = -1; + view.getLocationOnScreen(location); + node.put("x", location[0]); + node.put("y", location[1]); + + if (view instanceof ViewGroup) { + node.put("type", "ViewGroup"); + ViewGroup group = (ViewGroup)view; + for (int i = 0; i < group.getChildCount(); i++) { + node.put("child", geViewDetailTreeMsg(group.getChildAt(i))); + } + } else { + node.put("type", view.getClass().getSimpleName()); + } + + } catch (Exception e) { + e.printStackTrace(); + } + return node; + } +} diff --git a/android/sdk/src/main/java/com/taobao/weex/ui/action/GraphicActionAddElement.java b/android/sdk/src/main/java/com/taobao/weex/ui/action/GraphicActionAddElement.java index eee66889d0..85dd99e449 100644 --- a/android/sdk/src/main/java/com/taobao/weex/ui/action/GraphicActionAddElement.java +++ b/android/sdk/src/main/java/com/taobao/weex/ui/action/GraphicActionAddElement.java @@ -31,6 +31,7 @@ import com.taobao.weex.common.WXErrorCode; import com.taobao.weex.dom.transition.WXTransition; import com.taobao.weex.performance.WXAnalyzerDataTransfer; +import com.taobao.weex.performance.WXStateRecord; import com.taobao.weex.ui.component.WXComponent; import com.taobao.weex.ui.component.WXVContainer; import com.taobao.weex.utils.WXExceptionUtils; @@ -95,6 +96,7 @@ public GraphicActionAddElement(@NonNull WXSDKInstance instance, String ref, child.isIgnoreInteraction = true; } } + WXStateRecord.getInstance().recordAction(instance.getInstanceId(),"addElement"); } catch (ClassCastException e) { Map ext = new ArrayMap<>(); diff --git a/android/sdk/src/main/java/com/taobao/weex/utils/WXExceptionUtils.java b/android/sdk/src/main/java/com/taobao/weex/utils/WXExceptionUtils.java index afeadec8c2..70acf60375 100644 --- a/android/sdk/src/main/java/com/taobao/weex/utils/WXExceptionUtils.java +++ b/android/sdk/src/main/java/com/taobao/weex/utils/WXExceptionUtils.java @@ -41,6 +41,7 @@ import com.taobao.weex.common.WXPerformance; import com.taobao.weex.performance.WXAnalyzerDataTransfer; import com.taobao.weex.performance.WXInstanceApm; +import com.taobao.weex.performance.WXStateRecord; /** * Created on 2017/10/13. @@ -108,6 +109,7 @@ public static void commitCriticalExceptionRT(@Nullable final String instanceId, @Nullable final Map extParams ) { try { + WXStateRecord.getInstance().recordException(instanceId,exception); IWXConfigAdapter configAdapter = WXSDKManager.getInstance().getWxConfigAdapter(); boolean doCheck = true; if (null != configAdapter){