Java tutorial
/* * 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; import android.app.Activity; import android.app.AlertDialog; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.graphics.Color; import android.graphics.Paint; import android.net.Uri; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.RestrictTo; import android.support.annotation.RestrictTo.Scope; import android.support.annotation.WorkerThread; import android.support.v4.util.ArrayMap; import android.text.TextUtils; import android.view.Menu; import android.view.View; import android.view.ViewGroup; import android.widget.ScrollView; import com.alibaba.fastjson.JSONObject; import com.taobao.weex.adapter.IDrawableLoader; import com.taobao.weex.adapter.IWXHttpAdapter; import com.taobao.weex.adapter.IWXImgLoaderAdapter; import com.taobao.weex.adapter.IWXUserTrackAdapter; import com.taobao.weex.adapter.URIAdapter; import com.taobao.weex.appfram.websocket.IWebSocketAdapter; import com.taobao.weex.bridge.EventResult; import com.taobao.weex.bridge.NativeInvokeHelper; import com.taobao.weex.bridge.SimpleJSCallback; import com.taobao.weex.bridge.WXBridgeManager; import com.taobao.weex.bridge.WXModuleManager; import com.taobao.weex.common.Constants; import com.taobao.weex.common.Destroyable; import com.taobao.weex.common.IWXDebugProxy; import com.taobao.weex.common.OnWXScrollListener; import com.taobao.weex.common.WXErrorCode; import com.taobao.weex.common.WXModule; import com.taobao.weex.common.WXPerformance; import com.taobao.weex.common.WXPerformance.Dimension; import com.taobao.weex.common.WXRefreshData; import com.taobao.weex.common.WXRenderStrategy; import com.taobao.weex.common.WXRequest; import com.taobao.weex.common.WXResponse; import com.taobao.weex.dom.WXEvent; import com.taobao.weex.http.WXHttpUtil; import com.taobao.weex.layout.ContentBoxMeasurement; import com.taobao.weex.performance.WXAnalyzerDataTransfer; import com.taobao.weex.performance.WXInstanceApm; import com.taobao.weex.performance.WXInstanceExceptionRecord; import com.taobao.weex.tracing.WXTracing; import com.taobao.weex.ui.action.GraphicActionAddElement; import com.taobao.weex.ui.component.NestedContainer; import com.taobao.weex.ui.component.WXBasicComponentType; import com.taobao.weex.ui.component.WXComponent; import com.taobao.weex.ui.component.WXComponentFactory; import com.taobao.weex.ui.component.WXEmbed; import com.taobao.weex.ui.flat.FlatGUIContext; import com.taobao.weex.ui.view.WXScrollView; import com.taobao.weex.utils.Trace; import com.taobao.weex.utils.WXExceptionUtils; import com.taobao.weex.utils.WXFileUtils; import com.taobao.weex.utils.WXJsonUtils; import com.taobao.weex.utils.WXLogUtils; import com.taobao.weex.utils.WXReflectionUtils; import com.taobao.weex.utils.WXUtils; import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.PriorityQueue; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import static com.taobao.weex.http.WXHttpUtil.KEY_USER_AGENT; /** * Each instance of WXSDKInstance represents an running weex instance. * It can be a pure weex view, or mixed with native view */ public class WXSDKInstance implements IWXActivityStateListener, View.OnLayoutChangeListener { private static final String SOURCE_TEMPLATE_BASE64_MD5 = "templateSourceBase64MD5"; //Performance public boolean mEnd = false; public boolean mHasCreateFinish = false; public boolean mHasSetCreateFinishFsTime = false; public static final String BUNDLE_URL = "bundleUrl"; private IWXUserTrackAdapter mUserTrackAdapter; private IWXRenderListener mRenderListener; private IWXStatisticsListener mStatisticsListener; /** package **/ Context mContext; private final String mInstanceId; private RenderContainer mRenderContainer; private WXComponent mRootComp; private boolean mRendered; private WXRefreshData mLastRefreshData; private NestedInstanceInterceptor mNestedInstanceInterceptor; private String mBundleUrl = ""; public static String requestUrl = "requestUrl"; private boolean isDestroy = false; private Map<String, Serializable> mUserTrackParams; private NativeInvokeHelper mNativeInvokeHelper; private boolean isCommit = false; private WXGlobalEventReceiver mGlobalEventReceiver = null; private boolean trackComponent; private boolean enableLayerType = true; private boolean mNeedValidate = false; private boolean mNeedReLoad = false; private boolean mUseScroller = false; private int mInstanceViewPortWidth = 750; private WXInstanceApm mApmForInstance; private @NonNull FlatGUIContext mFlatGUIContext = new FlatGUIContext(); private Map<String, String> mContainerInfo; private WXInstanceExceptionRecord mExceptionRecorder; /** * bundle type */ public WXBridgeManager.BundType bundleType; public long mRenderStartNanos; public int mExecJSTraceId = WXTracing.nextId(); private boolean isViewDisAppear = false; /** *for network tracker */ public String mwxDims[] = new String[5]; public long measureTimes[] = new long[5]; public WeakReference<String> templateRef; public Map<String, List<String>> responseHeaders = new HashMap<>(); /** * Render strategy. */ private WXRenderStrategy mRenderStrategy = WXRenderStrategy.APPEND_ASYNC; /** * Render start time */ public long mRenderStartTime; /** * Refresh start time */ private long mRefreshStartTime; private WXPerformance mWXPerformance; private ScrollView mScrollView; private WXScrollView.WXScrollViewListener mWXScrollViewListener; private List<OnWXScrollListener> mWXScrollListeners; private List<String> mLayerOverFlowListeners; public List<String> getLayerOverFlowListeners() { return mLayerOverFlowListeners; } public void addLayerOverFlowListener(String ref) { if (mLayerOverFlowListeners == null) mLayerOverFlowListeners = new ArrayList<>(); mLayerOverFlowListeners.add(ref); } public void removeLayerOverFlowListener(String ref) { if (mLayerOverFlowListeners != null) mLayerOverFlowListeners.remove(ref); } /** * whether we are in preRender mode * */ private volatile boolean isPreRenderMode; private boolean mCurrentGround = false; private ComponentObserver mComponentObserver; private Map<String, GraphicActionAddElement> inactiveAddElementAction = new ArrayMap<>(); private Map<Long, ContentBoxMeasurement> mContentBoxMeasurements = new ArrayMap<>(); /** * set make weexCore run in single process mode * @param flag true means weexCore run in single process mode or multi process mode */ public void setUseSingleProcess(boolean flag) { WXBridgeManager.getInstance().setUseSingleProcess(flag); } /** * set open SandBox * @param flag */ public void setUseSandBox(boolean flag) { WXBridgeManager.getInstance().setSandBoxContext(flag); } public PriorityQueue<WXEmbed> hiddenEmbeds; private int maxHiddenEmbedsNum = -1; //max hidden embed num, -1 standard for ulimit public int getMaxHiddenEmbedsNum() { return maxHiddenEmbedsNum; } public void setMaxHiddenEmbedsNum(int maxHiddenEmbedsNum) { this.maxHiddenEmbedsNum = maxHiddenEmbedsNum; } @WorkerThread @RestrictTo(Scope.LIBRARY) public void addInActiveAddElementAction(String ref, GraphicActionAddElement action) { inactiveAddElementAction.put(ref, action); } @WorkerThread @RestrictTo(Scope.LIBRARY) public void removeInActiveAddElmentAction(String ref) { inactiveAddElementAction.remove(ref); } @WorkerThread @RestrictTo(Scope.LIBRARY) public GraphicActionAddElement getInActiveAddElementAction(String ref) { return inactiveAddElementAction.get(ref); } /** * If anchor is created manually(etc. define a layout xml resource ), * be aware do not add it to twice when {@link IWXRenderListener#onViewCreated(WXSDKInstance, View)}. * @param a */ public void setRenderContainer(RenderContainer a) { if (a != null) { a.setSDKInstance(this); a.addOnLayoutChangeListener(this); } mRenderContainer = a; if (mRenderContainer != null && mRenderContainer.getLayoutParams() != null && mRenderContainer.getLayoutParams().width == ViewGroup.LayoutParams.WRAP_CONTENT) { WXBridgeManager.getInstance().post(new Runnable() { @Override public void run() { WXBridgeManager.getInstance().setRenderContentWrapContentToCore(true, getInstanceId()); } }); } else { WXBridgeManager.getInstance().post(new Runnable() { @Override public void run() { WXBridgeManager.getInstance().setRenderContentWrapContentToCore(false, getInstanceId()); } }); } } private int mMaxDeepLayer; public boolean isTrackComponent() { return trackComponent; } public void setTrackComponent(boolean trackComponent) { this.trackComponent = trackComponent; } /** * Tell whether it is enabled to change the layerType * {@link android.view.View#setLayerType(int, Paint)} * @return True for enable to change the layerType of component, false otherwise. The default * is True */ public boolean isLayerTypeEnabled() { return enableLayerType; } /** * Enable the ability of changing layerType. e.g. {@link android.view.View#setLayerType(int, Paint)} * Disable the ability of changing layerType will have tremendous <strong>performance * punishment</strong>. * * <strong>Do not</strong> set this to false unless you know exactly what you are doing. * @param enable True for enable to change the layerType of component, false otherwise. The default * is True */ public void enableLayerType(boolean enable) { enableLayerType = enable; } @RestrictTo(RestrictTo.Scope.LIBRARY) public @NonNull FlatGUIContext getFlatUIContext() { return mFlatGUIContext; } public boolean isNeedValidate() { return mNeedValidate; } public boolean isNeedReLoad() { return mNeedReLoad; } public void setNeedLoad(boolean load) { mNeedReLoad = load; } public boolean isUseScroller() { return mUseScroller; } public void setUseScroller(boolean use) { mUseScroller = use; } public void setInstanceViewPortWidth(int instanceViewPortWidth) { this.mInstanceViewPortWidth = instanceViewPortWidth; } public int getInstanceViewPortWidth() { return mInstanceViewPortWidth; } public interface OnInstanceVisibleListener { void onAppear(); void onDisappear(); } private List<OnInstanceVisibleListener> mVisibleListeners = new ArrayList<>(); public WXSDKInstance(Context context) { mInstanceId = WXSDKManager.getInstance().generateInstanceId(); init(context); } /** * For unittest only. */ @RestrictTo(Scope.TESTS) WXSDKInstance(Context context, String id) { mInstanceId = id; init(context); } public WXComponent getRootComponent() { return mRootComp; } public void setNestedInstanceInterceptor(NestedInstanceInterceptor interceptor) { mNestedInstanceInterceptor = interceptor; } public final WXSDKInstance createNestedInstance(NestedContainer container) { WXSDKInstance sdkInstance = newNestedInstance(); if (mNestedInstanceInterceptor != null) { mNestedInstanceInterceptor.onCreateNestInstance(sdkInstance, container); } if (sdkInstance != null) { sdkInstance.setComponentObserver(this.getComponentObserver()); } return sdkInstance; } protected WXSDKInstance newNestedInstance() { return new WXSDKInstance(mContext); } public void addOnInstanceVisibleListener(OnInstanceVisibleListener l) { mVisibleListeners.add(l); } public void removeOnInstanceVisibleListener(OnInstanceVisibleListener l) { mVisibleListeners.remove(l); } public void init(Context context) { mContext = context; mContainerInfo = new HashMap<>(4); mNativeInvokeHelper = new NativeInvokeHelper(mInstanceId); mWXPerformance = new WXPerformance(mInstanceId); mApmForInstance = new WXInstanceApm(mInstanceId); mExceptionRecorder = new WXInstanceExceptionRecord(mInstanceId); mWXPerformance.WXSDKVersion = WXEnvironment.WXSDK_VERSION; mWXPerformance.JSLibInitTime = WXEnvironment.sJSLibInitTime; mUserTrackAdapter = WXSDKManager.getInstance().getIWXUserTrackAdapter(); WXSDKManager.getInstance().getAllInstanceMap().put(mInstanceId, this); mContainerInfo.put(WXInstanceApm.KEY_PAGE_PROPERTIES_CONTAINER_NAME, context instanceof Activity ? context.getClass().getSimpleName() : "unKnowContainer"); mContainerInfo.put(WXInstanceApm.KEY_PAGE_PROPERTIES_INSTANCE_TYPE, "page"); } /** * Set a Observer for component. * This observer will be called in each component, should not doing * anything will impact render performance. * * @param observer */ public void setComponentObserver(ComponentObserver observer) { mComponentObserver = observer; } public ComponentObserver getComponentObserver() { return mComponentObserver; } public NativeInvokeHelper getNativeInvokeHelper() { return mNativeInvokeHelper; } @Deprecated public void setBizType(String bizType) { if (!TextUtils.isEmpty(bizType)) { mWXPerformance.bizType = bizType; } } public ScrollView getScrollView() { return mScrollView; } public void setRootScrollView(ScrollView scrollView) { mScrollView = scrollView; if (mWXScrollViewListener != null && mScrollView instanceof WXScrollView) { ((WXScrollView) mScrollView).addScrollViewListener(mWXScrollViewListener); } } @Deprecated public void registerScrollViewListener(WXScrollView.WXScrollViewListener scrollViewListener) { mWXScrollViewListener = scrollViewListener; } @Deprecated public WXScrollView.WXScrollViewListener getScrollViewListener() { return mWXScrollViewListener; } @Deprecated public void setIWXUserTrackAdapter(IWXUserTrackAdapter adapter) { } public void setContainerInfo(String key, String val) { mContainerInfo.put(key, val); } public Map<String, String> getContainerInfo() { return mContainerInfo; } /** * Render template asynchronously, use {@link WXRenderStrategy#APPEND_ASYNC} as render strategy * @param template bundle js * @param options os iphone/android/ipad * weexversion Weex version(like 1.0.0) * appversion App version(like 1.0.0) * devid Device id(like Aqh9z8dRJNBhmS9drLG5BKCmXhecHUXIZoXOctKwFebH) * sysversion Device system version(like 5.4.4?7.0.4, should be used with os) * sysmodel Device model(like iOS:"MGA82J/A", android:"MI NOTE LTE") * Time UNIX timestamp, UTC+08:00 * TTID(Optional) * MarkertId * Appname(Optional) tm,tb,qa * Bundleurl(Optional) template url * @param jsonInitData Initial data for rendering */ public void render(String template, Map<String, Object> options, String jsonInitData) { render(template, options, jsonInitData, WXRenderStrategy.APPEND_ASYNC); } /** * Render template asynchronously * @param template bundle js * @param options os iphone/android/ipad * weexversion Weex version(like 1.0.0) * appversion App version(like 1.0.0) * devid Device id(like Aqh9z8dRJNBhmS9drLG5BKCmXhecHUXIZoXOctKwFebH) * sysversion Device system version(like 5.4.4?7.0.4, should be used with os) * sysmodel Device model(like iOS:"MGA82J/A", android:"MI NOTE LTE") * Time UNIX timestamp, UTC+08:00 * TTID(Optional) * MarkertId * Appname(Optional) tm,tb,qa * Bundleurl(Optional) template url * @param jsonInitData Initial data for rendering * @param flag RenderStrategy {@link WXRenderStrategy} */ @Deprecated public void render(String template, Map<String, Object> options, String jsonInitData, WXRenderStrategy flag) { render(WXPerformance.DEFAULT, template, options, jsonInitData, flag); } /** * Render template asynchronously * * @param pageName, used for performance log. * @param template bundle js * @param options os iphone/android/ipad * weexversion Weex version(like 1.0.0) * appversion App version(like 1.0.0) * devid Device id(like Aqh9z8dRJNBhmS9drLG5BKCmXhecHUXIZoXOctKwFebH) * sysversion Device system version(like 5.4.4?7.0.4, should be used with os) * sysmodel Device model(like iOS:"MGA82J/A", android:"MI NOTE LTE") * Time UNIX timestamp, UTC+08:00 * TTID(Optional) * MarkertId * Appname(Optional) tm,tb,qa * Bundleurl(Optional) template url * @param jsonInitData Initial data for rendering * @param flag RenderStrategy {@link WXRenderStrategy} */ public void render(String pageName, String template, Map<String, Object> options, String jsonInitData, WXRenderStrategy flag) { mWXPerformance.beforeInstanceRender(mInstanceId); if (WXEnvironment.isApkDebugable() && WXPerformance.DEFAULT.equals(pageName)) { WXLogUtils.e("WXSDKInstance", "Please set your pageName or your js bundle url !!!!!!!"); if (getUIContext() != null) { new AlertDialog.Builder(getUIContext()).setTitle("Error: Missing pageName") .setMessage("We highly recommend you to set pageName. Call" + "\nWXSDKInstance#render(String pageName, String template, Map<String, Object> options, String jsonInitData, WXRenderStrategy flag)\n" + "to fix it.") .show(); } return; } renderInternal(pageName, template, options, jsonInitData, flag); } private void ensureRenderArchor() { if (mRenderContainer == null) { if (getContext() != null) { setRenderContainer(new RenderContainer(getContext())); mRenderContainer.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); mRenderContainer.setBackgroundColor(Color.TRANSPARENT); mRenderContainer.setSDKInstance(this); mRenderContainer.addOnLayoutChangeListener(this); } } } private void renderInternal(String pageName, String template, Map<String, Object> options, String jsonInitData, WXRenderStrategy flag) { if (mRendered || TextUtils.isEmpty(template)) { return; } //some case ,from render(template),but not render (url) if (!mApmForInstance.hasInit()) { mApmForInstance.doInit(); } mApmForInstance.setPageName(pageName); mApmForInstance.onStage(WXInstanceApm.KEY_PAGE_STAGES_RENDER_ORGIGIN); mWXPerformance.pageName = (TextUtils.isEmpty(pageName) ? "defaultBundleUrl" : pageName); if (TextUtils.isEmpty(mBundleUrl)) { mBundleUrl = mWXPerformance.pageName; } WXLogUtils.d("WXSDKInstance", "Start render page: " + pageName); if (WXTracing.isAvailable()) { WXTracing.TraceEvent traceEvent = WXTracing.newEvent("executeBundleJS", mInstanceId, -1); traceEvent.traceId = mExecJSTraceId; traceEvent.iid = mInstanceId; traceEvent.tname = "JSThread"; traceEvent.ph = "B"; traceEvent.submit(); mRenderStartNanos = System.nanoTime(); } ensureRenderArchor(); Map<String, Object> renderOptions = options; if (renderOptions == null) { renderOptions = new HashMap<>(); } if (WXEnvironment.sDynamicMode && !TextUtils.isEmpty(WXEnvironment.sDynamicUrl) && renderOptions.get("dynamicMode") == null) { renderOptions.put("dynamicMode", "true"); renderByUrl(pageName, WXEnvironment.sDynamicUrl, renderOptions, jsonInitData, flag); return; } mWXPerformance.JSTemplateSize = template.length() / 1024f; mApmForInstance.addStats(WXInstanceApm.KEY_PAGE_STATS_BUNDLE_SIZE, mWXPerformance.JSTemplateSize); mRenderStartTime = System.currentTimeMillis(); mRenderStrategy = flag; WXSDKManager.getInstance().setCrashInfo(WXEnvironment.WEEX_CURRENT_KEY, pageName); WXSDKManager.getInstance().createInstance(this, template, renderOptions, jsonInitData); mRendered = true; } private void renderByUrlInternal(String pageName, final String url, Map<String, Object> options, final String jsonInitData, final WXRenderStrategy flag) { ensureRenderArchor(); pageName = wrapPageName(pageName, url); mBundleUrl = url; if (WXSDKManager.getInstance().getValidateProcessor() != null) { mNeedValidate = WXSDKManager.getInstance().getValidateProcessor().needValidate(mBundleUrl); } Map<String, Object> renderOptions = options; if (renderOptions == null) { renderOptions = new HashMap<>(); } if (!renderOptions.containsKey(BUNDLE_URL)) { renderOptions.put(BUNDLE_URL, url); } mApmForInstance.doInit(); Uri uri = Uri.parse(url); if (uri != null && TextUtils.equals(uri.getScheme(), "file")) { mApmForInstance.onStage(WXInstanceApm.KEY_PAGE_STAGES_DOWN_BUNDLE_START); String template = WXFileUtils.loadFileOrAsset(assembleFilePath(uri), mContext); mApmForInstance.onStage(WXInstanceApm.KEY_PAGE_STAGES_DOWN_BUNDLE_END); render(pageName, template, renderOptions, jsonInitData, flag); return; } IWXHttpAdapter adapter = WXSDKManager.getInstance().getIWXHttpAdapter(); WXRequest wxRequest = new WXRequest(); wxRequest.url = rewriteUri(Uri.parse(url), URIAdapter.BUNDLE).toString(); if (wxRequest != null && !TextUtils.isEmpty(wxRequest.url)) { requestUrl = wxRequest.url; } else { requestUrl = pageName; } if (wxRequest.paramMap == null) { wxRequest.paramMap = new HashMap<String, String>(); } wxRequest.instanceId = getInstanceId(); wxRequest.paramMap.put(KEY_USER_AGENT, WXHttpUtil.assembleUserAgent(mContext, WXEnvironment.getConfig())); WXHttpListener httpListener = new WXHttpListener(pageName, renderOptions, jsonInitData, flag, System.currentTimeMillis()); httpListener.setSDKInstance(this); adapter.sendRequest(wxRequest, (IWXHttpAdapter.OnHttpListener) httpListener); } /** * Use {@link #render(String, String, Map, String, WXRenderStrategy)} instead. * @param pageName * @param template * @param options * @param jsonInitData * @param width * @param height * @param flag */ @Deprecated public void render(String pageName, String template, Map<String, Object> options, String jsonInitData, int width, int height, WXRenderStrategy flag) { render(pageName, template, options, jsonInitData, flag); } /** * Render template asynchronously, use {@link WXRenderStrategy#APPEND_ASYNC} as render strategy * @param template bundle js */ public void render(String template) { render(WXPerformance.DEFAULT, template, null, null, mRenderStrategy); } /** * Use {@link #render(String)} instead. * @param template * @param width * @param height */ @Deprecated public void render(String template, int width, int height) { render(template); } /** * Use {@link #renderByUrl(String, String, Map, String, WXRenderStrategy)} instead. * @param pageName * @param url * @param options * @param jsonInitData * @param width * @param height * @param flag */ @Deprecated public void renderByUrl(String pageName, final String url, Map<String, Object> options, final String jsonInitData, final int width, final int height, final WXRenderStrategy flag) { renderByUrl(pageName, url, options, jsonInitData, flag); } public void renderByUrl(String pageName, final String url, Map<String, Object> options, final String jsonInitData, final WXRenderStrategy flag) { renderByUrlInternal(pageName, url, options, jsonInitData, flag); } private String wrapPageName(String pageName, String url) { if (TextUtils.equals(pageName, WXPerformance.DEFAULT)) { pageName = url; WXExceptionUtils.degradeUrl = pageName; try { Uri uri = Uri.parse(url); if (uri != null) { Uri.Builder builder = new Uri.Builder(); builder.scheme(uri.getScheme()); builder.authority(uri.getAuthority()); builder.path(uri.getPath()); pageName = builder.toString(); } } catch (Exception e) { } } return pageName; } private String assembleFilePath(Uri uri) { if (uri != null && uri.getPath() != null) { return uri.getPath().replaceFirst("/", ""); } return ""; } public void reloadPage(boolean reloadThis) { WXSDKEngine.reload(); if (reloadThis) { if (mContext != null) { Intent intent = new Intent(); intent.setAction(IWXDebugProxy.ACTION_INSTANCE_RELOAD); intent.putExtra("url", mBundleUrl); mContext.sendBroadcast(intent); } // mRendered = false; // destroy(); // renderInternal(mPackage, mTemplate, mOptions, mJsonInitData, mFlag); // refreshInstance("{}"); } } /** * Refresh instance asynchronously. * @param data the new data */ public void refreshInstance(Map<String, Object> data) { if (data == null) { return; } refreshInstance(WXJsonUtils.fromObjectToJSONString(data)); } /** * Refresh instance asynchronously. * @param jsonData the new data */ public void refreshInstance(String jsonData) { if (jsonData == null) { return; } mRefreshStartTime = System.currentTimeMillis(); //cancel last refresh message if (mLastRefreshData != null) { mLastRefreshData.isDirty = true; } mLastRefreshData = new WXRefreshData(jsonData, false); WXSDKManager.getInstance().refreshInstance(mInstanceId, mLastRefreshData); } public WXRenderStrategy getRenderStrategy() { return mRenderStrategy; } public Context getUIContext() { return mContext; } public String getInstanceId() { return mInstanceId; } public Context getContext() { if (mContext == null) { WXLogUtils.e("WXSdkInstance mContext == null"); } return mContext; } public int getWeexHeight() { return mRenderContainer == null ? 0 : mRenderContainer.getHeight(); } public int getWeexWidth() { return mRenderContainer == null ? 0 : mRenderContainer.getWidth(); } public IWXImgLoaderAdapter getImgLoaderAdapter() { return WXSDKManager.getInstance().getIWXImgLoaderAdapter(); } public IDrawableLoader getDrawableLoader() { return WXSDKManager.getInstance().getDrawableLoader(); } public URIAdapter getURIAdapter() { return WXSDKManager.getInstance().getURIAdapter(); } public Uri rewriteUri(Uri uri, String type) { return getURIAdapter().rewrite(this, type, uri); } public IWXHttpAdapter getWXHttpAdapter() { return WXSDKManager.getInstance().getIWXHttpAdapter(); } public IWXStatisticsListener getWXStatisticsListener() { return mStatisticsListener; } public @Nullable IWebSocketAdapter getWXWebSocketAdapter() { return WXSDKManager.getInstance().getIWXWebSocketAdapter(); } @Deprecated public void reloadImages() { if (mScrollView == null) { return; } } public boolean isPreRenderMode() { return this.isPreRenderMode; } public void setPreRenderMode(final boolean isPreRenderMode) { WXSDKManager.getInstance().getWXRenderManager().postOnUiThread(new Runnable() { @Override public void run() { WXSDKInstance.this.isPreRenderMode = isPreRenderMode; } }, 0); } public void setContext(@NonNull Context context) { this.mContext = context; } /******************************** * begin register listener ********************************************************/ public void registerRenderListener(IWXRenderListener listener) { mRenderListener = listener; } @Deprecated public void registerActivityStateListener(IWXActivityStateListener listener) { } public void registerStatisticsListener(IWXStatisticsListener listener) { mStatisticsListener = listener; } /**set render start time*/ public void setRenderStartTime(long renderStartTime) { this.mRenderStartTime = renderStartTime; } /******************************** * end register listener ********************************************************/ /******************************** * begin hook Activity life cycle callback ********************************************************/ @Override public void onActivityCreate() { // module listen Activity onActivityCreate WXModuleManager.onActivityCreate(getInstanceId()); if (mRootComp != null) { mRootComp.onActivityCreate(); } else { WXLogUtils.w("Warning :Component tree has not build completely,onActivityCreate can not be call!"); } mGlobalEventReceiver = new WXGlobalEventReceiver(this); getContext().registerReceiver(mGlobalEventReceiver, new IntentFilter(WXGlobalEventReceiver.EVENT_ACTION)); } @Override public void onActivityStart() { // module listen Activity onActivityCreate WXModuleManager.onActivityStart(getInstanceId()); if (mRootComp != null) { mRootComp.onActivityStart(); } else { WXLogUtils.w("Warning :Component tree has not build completely,onActivityStart can not be call!"); } } public boolean onCreateOptionsMenu(Menu menu) { WXModuleManager.onCreateOptionsMenu(getInstanceId(), menu); if (mRootComp != null) { mRootComp.onCreateOptionsMenu(menu); } else { WXLogUtils.w("Warning :Component tree has not build completely,onActivityStart can not be call!"); } return true; } @Override public void onActivityPause() { onViewDisappear(); if (!isCommit) { if (mUseScroller) { mWXPerformance.useScroller = 1; } mWXPerformance.maxDeepViewLayer = getMaxDeepLayer(); mWXPerformance.wxDims = mwxDims; mWXPerformance.measureTimes = measureTimes; if (mUserTrackAdapter != null) { mUserTrackAdapter.commit(mContext, null, IWXUserTrackAdapter.LOAD, mWXPerformance, getUserTrackParams()); } WXAnalyzerDataTransfer.transferPerformance(mWXPerformance, getInstanceId()); isCommit = true; } // module listen Activity onActivityPause WXModuleManager.onActivityPause(getInstanceId()); if (mRootComp != null) { mRootComp.onActivityPause(); } else { WXLogUtils.w("Warning :Component tree has not build completely,onActivityPause can not be call!"); } WXLogUtils.i("Application onActivityPause()"); if (!mCurrentGround) { WXLogUtils.i("Application to be in the backround"); Intent intent = new Intent(WXGlobalEventReceiver.EVENT_ACTION); intent.putExtra(WXGlobalEventReceiver.EVENT_NAME, Constants.Event.PAUSE_EVENT); intent.putExtra(WXGlobalEventReceiver.EVENT_WX_INSTANCEID, getInstanceId()); /** * Fix NPE just like {@link #onActivityResume()} */ if (null != mContext) { mContext.sendBroadcast(intent); } else { WXEnvironment.getApplication().sendBroadcast(intent); } this.mCurrentGround = true; } } @Override public void onActivityResume() { // notify onActivityResume callback to module WXModuleManager.onActivityResume(getInstanceId()); if (mRootComp != null) { mRootComp.onActivityResume(); } else { WXLogUtils.w("Warning :Component tree has not build completely, onActivityResume can not be call!"); } if (mCurrentGround) { WXLogUtils.i("Application to be in the foreground"); Intent intent = new Intent(WXGlobalEventReceiver.EVENT_ACTION); intent.putExtra(WXGlobalEventReceiver.EVENT_NAME, Constants.Event.RESUME_EVENT); intent.putExtra(WXGlobalEventReceiver.EVENT_WX_INSTANCEID, getInstanceId()); //todo tmp solution for gray version if (null != mContext) { mContext.sendBroadcast(intent); } else { WXEnvironment.getApplication().sendBroadcast(intent); } this.mCurrentGround = false; } onViewAppear(); } @Override public void onActivityStop() { // notify onActivityResume callback to module WXModuleManager.onActivityStop(getInstanceId()); if (mRootComp != null) { mRootComp.onActivityStop(); } else { WXLogUtils.w("Warning :Component tree has not build completely, onActivityStop can not be call!"); } } @Override public void onActivityDestroy() { WXModuleManager.onActivityDestroy(getInstanceId()); if (mRootComp != null) { mRootComp.onActivityDestroy(); } else { WXLogUtils.w("Warning :Component tree has not build completely, onActivityDestroy can not be call!"); } destroy(); } @Override public boolean onActivityBack() { WXModuleManager.onActivityBack(getInstanceId()); if (mRootComp != null) { return mRootComp.onActivityBack(); } else { WXLogUtils.w("Warning :Component tree has not build completely, onActivityBack can not be call!"); } return false; } public boolean onBackPressed() { WXComponent comp = getRootComponent(); if (comp != null) { WXEvent events = comp.getEvents(); boolean hasNativeBackHook = events.contains(Constants.Event.NATIVE_BACK); if (hasNativeBackHook) { EventResult result = comp.fireEventWait(Constants.Event.NATIVE_BACK, null); if (WXUtils.getBoolean(result.getResult(), false)) { return true; } } boolean hasBackPressed = events.contains(Constants.Event.CLICKBACKITEM); if (hasBackPressed) { fireEvent(comp.getRef(), Constants.Event.CLICKBACKITEM, null, null); } return hasBackPressed; } return false; } public void onActivityResult(int requestCode, int resultCode, Intent data) { WXModuleManager.onActivityResult(getInstanceId(), requestCode, resultCode, data); if (mRootComp != null) { mRootComp.onActivityResult(requestCode, resultCode, data); } else { WXLogUtils.w("Warning :Component tree has not build completely, onActivityResult can not be call!"); } } public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { WXModuleManager.onRequestPermissionsResult(getInstanceId(), requestCode, permissions, grantResults); if (mRootComp != null) { mRootComp.onRequestPermissionsResult(requestCode, permissions, grantResults); } else { WXLogUtils.w( "Warning :Component tree has not build completely, onRequestPermissionsResult can not be call!"); } } /******************************** * end hook Activity life cycle callback ********************************************************/ public void onViewDisappear() { isViewDisAppear = false; mApmForInstance.onDisAppear(); WXComponent comp = getRootComponent(); if (comp != null) { fireEvent(comp.getRef(), Constants.Event.VIEWDISAPPEAR, null, null); //call disappear of nested instances for (OnInstanceVisibleListener instance : mVisibleListeners) { instance.onDisappear(); } } } public boolean isViewDisAppear() { return isViewDisAppear; } public void onViewAppear() { isViewDisAppear = true; mApmForInstance.onAppear(); WXComponent comp = getRootComponent(); if (comp != null) { fireEvent(comp.getRef(), Constants.Event.VIEWAPPEAR, null, null); for (OnInstanceVisibleListener instance : mVisibleListeners) { instance.onAppear(); } } } public void onCreateFinish() { if (mContext != null) { onViewAppear(); View wxView = mRenderContainer; if (mRenderListener != null) { mRenderListener.onViewCreated(WXSDKInstance.this, wxView); } if (mStatisticsListener != null) { mStatisticsListener.onFirstView(); } } } /** * call back when update finish */ public void onUpdateFinish() { WXLogUtils.d("Instance onUpdateSuccess"); } public void runOnUiThread(Runnable action) { WXSDKManager.getInstance().postOnUiThread(action, 0); } public void onRenderSuccess(final int width, final int height) { firstScreenRenderFinished(); long time = System.currentTimeMillis() - mRenderStartTime; long[] renderFinishTime = WXBridgeManager.getInstance().getRenderFinishTime(getInstanceId()); WXLogUtils.renderPerformanceLog("onRenderSuccess", time); WXLogUtils.renderPerformanceLog(" invokeCreateInstance", mWXPerformance.communicateTime); WXLogUtils.renderPerformanceLog(" onRenderSuccessCallBridgeTime", renderFinishTime[0]); WXLogUtils.renderPerformanceLog(" onRenderSuccessCssLayoutTime", renderFinishTime[1]); WXLogUtils.renderPerformanceLog(" onRenderSuccessParseJsonTime", renderFinishTime[2]); mWXPerformance.callBridgeTime = renderFinishTime[0]; mWXPerformance.cssLayoutTime = renderFinishTime[1]; mWXPerformance.parseJsonTime = renderFinishTime[2]; mWXPerformance.totalTime = time; if (mWXPerformance.screenRenderTime < 0.001) { mWXPerformance.screenRenderTime = time; } WXLogUtils.d(WXLogUtils.WEEX_PERF_TAG, "mComponentNum:" + mWXPerformance.componentCount); if (mRenderListener != null && mContext != null) { mRenderListener.onRenderSuccess(WXSDKInstance.this, width, height); if (mUserTrackAdapter != null) { WXPerformance performance = new WXPerformance(mInstanceId); performance.errCode = WXErrorCode.WX_SUCCESS.getErrorCode(); performance.args = getBundleUrl(); mUserTrackAdapter.commit(mContext, null, IWXUserTrackAdapter.JS_BRIDGE, performance, getUserTrackParams()); } WXLogUtils.d(WXLogUtils.WEEX_PERF_TAG, mWXPerformance.toString()); } if (!WXEnvironment.isApkDebugable()) { WXLogUtils.e("weex_perf", mWXPerformance.getPerfData()); } } public void onRefreshSuccess(final int width, final int height) { WXLogUtils.renderPerformanceLog("onRefreshSuccess", (System.currentTimeMillis() - mRefreshStartTime)); if (mRenderListener != null && mContext != null) { mRenderListener.onRefreshSuccess(WXSDKInstance.this, width, height); } } public void onChangeElement(WXComponent component, boolean isOutOfScreen) { if (isDestroy() || null == mRenderContainer || mWXPerformance == null) { return; } if (mRenderContainer.hasConsumeEvent()) { return; } long lastElementChangeTime = System.currentTimeMillis(); long lazyLoadTime; if (mHasCreateFinish) { lazyLoadTime = lastElementChangeTime - mWXPerformance.renderTimeOrigin - mWXPerformance.callCreateFinishTime; if (lazyLoadTime > 8000) { //bad case return; } } if (component.mIsAddElementToTree) { getWXPerformance().localInteractionViewAddCount++; if (!isOutOfScreen) getWXPerformance().interactionViewAddLimitCount++; component.mIsAddElementToTree = false; } if (!isOutOfScreen) { getWXPerformance().interactionViewAddCount = getWXPerformance().localInteractionViewAddCount; getWXPerformance().interactionTime = lastElementChangeTime - mWXPerformance.renderTimeOrigin; // WXLogUtils.renderPerformanceLog(" interactionViewAddCount", getWXPerformance().interactionViewAddCount); // WXLogUtils.renderPerformanceLog(" interactionViewAddLimitCount", getWXPerformance().interactionViewAddLimitCount); // WXLogUtils.renderPerformanceLog(" interactionTime", getWXPerformance().interactionTime); mApmForInstance.arriveInteraction(getWXPerformance().interactionViewAddLimitCount, getWXPerformance().interactionViewAddCount); } } public void onRenderError(final String errCode, final String msg) { if (mRenderListener != null && mContext != null) { runOnUiThread(new Runnable() { @Override public void run() { if (mRenderListener != null && mContext != null) { mRenderListener.onException(WXSDKInstance.this, errCode, msg); } } }); } } public void onJSException(final String errCode, final String function, final String exception) { if (mRenderListener != null && mContext != null) { runOnUiThread(new Runnable() { @Override public void run() { if (mRenderListener != null && mContext != null) { StringBuilder builder = new StringBuilder(); builder.append(function); builder.append(exception); mRenderListener.onException(WXSDKInstance.this, errCode, builder.toString()); } } }); } } @Override public final void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { if (left != oldLeft || top != oldTop || right != oldRight || bottom != oldBottom) { onLayoutChange(v); } } /** * Subclass should override this method to get notifications of layout change of GodView. * @param godView the godView. */ public void onLayoutChange(View godView) { } private boolean mCreateInstance = true; public void firstScreenCreateInstanceTime(long time) { if (mCreateInstance) { mWXPerformance.firstScreenJSFExecuteTime = time - mRenderStartTime; mCreateInstance = false; } } public void callJsTime(final long time) { if (!mEnd) { mWXPerformance.fsCallJsTotalTime += time; mWXPerformance.fsCallJsTotalNum++; } } public void onComponentCreate(WXComponent component, long createTime) { mWXPerformance.mActionAddElementCount++; mWXPerformance.mActionAddElementSumTime += createTime; if (!mEnd) { mWXPerformance.fsComponentCreateTime += createTime; mWXPerformance.fsComponentCount++; } mWXPerformance.componentCount++; mWXPerformance.componentCreateTime += createTime; } public void callActionAddElementTime(long time) { mWXPerformance.mActionAddElementSumTime += time; } public void onOldFsRenderTimeLogic() { if (mEnd) { return; } mEnd = true; if (mStatisticsListener != null && mContext != null) { runOnUiThread(new Runnable() { @Override public void run() { if (mStatisticsListener != null && mContext != null) { Trace.beginSection("onFirstScreen"); mStatisticsListener.onFirstScreen(); Trace.endSection(); } } }); } mApmForInstance.arriveFSRenderTime(); mWXPerformance.fsRenderTime = System.currentTimeMillis(); mWXPerformance.screenRenderTime = System.currentTimeMillis() - mRenderStartTime; long[] fitstScreenPerformance = WXBridgeManager.getInstance().getFirstScreenRenderTime(getInstanceId()); WXLogUtils.renderPerformanceLog("firstScreenRenderFinished", mWXPerformance.screenRenderTime); WXLogUtils.renderPerformanceLog(" firstScreenJSFExecuteTime", mWXPerformance.firstScreenJSFExecuteTime); WXLogUtils.renderPerformanceLog(" firstScreenCallBridgeTime", fitstScreenPerformance[0]); WXLogUtils.renderPerformanceLog(" firstScreenCssLayoutTime", fitstScreenPerformance[1]); WXLogUtils.renderPerformanceLog(" firstScreenParseJsonTime", fitstScreenPerformance[2]); } public void firstScreenRenderFinished() { mApmForInstance.arriveFSRenderTime(); mWXPerformance.screenRenderTime = System.currentTimeMillis() - mRenderStartTime; if (!mHasSetCreateFinishFsTime) { mWXPerformance.fsRenderTime = System.currentTimeMillis(); if (mHasCreateFinish) mHasSetCreateFinishFsTime = true; } mWXPerformance.newFsRenderTime = System.currentTimeMillis() - mRenderStartTime; } private void destroyView(View rootView) { try { if (rootView instanceof ViewGroup) { ViewGroup cViewGroup = ((ViewGroup) rootView); for (int index = 0; index < cViewGroup.getChildCount(); index++) { destroyView(cViewGroup.getChildAt(index)); } cViewGroup.removeViews(0, ((ViewGroup) rootView).getChildCount()); // Ensure that the viewgroup's status to be normal WXReflectionUtils.setValue(rootView, "mChildrenCount", 0); } if (rootView instanceof Destroyable) { ((Destroyable) rootView).destroy(); } } catch (Exception e) { WXLogUtils.e("WXSDKInstance destroyView Exception: ", e); } } public synchronized void destroy() { if (!isDestroy()) { mApmForInstance.onEnd(); getExceptionRecorder().checkEmptyScreenAndReport(); if (mRendered) { WXSDKManager.getInstance().destroyInstance(mInstanceId); } if (mGlobalEventReceiver != null) { getContext().unregisterReceiver(mGlobalEventReceiver); mGlobalEventReceiver = null; } if (mRootComp != null) { mRootComp.destroy(); destroyView(mRenderContainer); mRootComp = null; } if (mGlobalEvents != null) { mGlobalEvents.clear(); } if (mComponentObserver != null) { mComponentObserver = null; } if (mLayerOverFlowListeners != null) { mLayerOverFlowListeners.clear(); } getFlatUIContext().destroy(); mFlatGUIContext = null; mWXScrollListeners = null; mRenderContainer = null; mNestedInstanceInterceptor = null; mUserTrackAdapter = null; mScrollView = null; mContext = null; mRenderListener = null; isDestroy = true; mStatisticsListener = null; if (responseHeaders != null) { responseHeaders.clear(); } if (templateRef != null) { templateRef = null; } if (null != mContentBoxMeasurements) { mContentBoxMeasurements.clear(); } mWXPerformance.afterInstanceDestroy(mInstanceId); WXBridgeManager.getInstance().post(new Runnable() { @Override public void run() { WXBridgeManager.getInstance().onInstanceClose(getInstanceId()); inactiveAddElementAction.clear(); } }); //when report error in @WXExceptionUtils // instance may had destroy and remove, // so we delay remove from allInstanceMap WXBridgeManager.getInstance().postDelay(new Runnable() { @Override public void run() { WXSDKManager.getInstance().getAllInstanceMap().remove(mInstanceId); } }, 5000); } } public boolean isDestroy() { return isDestroy; } /** * @return If you use render () the return value may be empty */ public @Nullable String getBundleUrl() { return mBundleUrl; } public View getRootView() { if (mRootComp == null) return null; return mRootComp.getRealView(); } public View getContainerView() { return mRenderContainer; } @Deprecated public void setBundleUrl(String url) { mBundleUrl = url; if (WXSDKManager.getInstance().getValidateProcessor() != null) { mNeedValidate = WXSDKManager.getInstance().getValidateProcessor().needValidate(mBundleUrl); } } public void onRootCreated(WXComponent root) { this.mRootComp = root; this.mRootComp.mDeepInComponentTree = 1; mRenderContainer.addView(root.getHostView()); setSize(mRenderContainer.getWidth(), mRenderContainer.getHeight()); } /** * Move fixed view to container ,except it's already moved. * @param fixedChild */ public void moveFixedView(View fixedChild) { if (mRenderContainer != null) { ViewGroup parent; if ((parent = (ViewGroup) fixedChild.getParent()) != null) { if (parent != mRenderContainer) { parent.removeView(fixedChild); mRenderContainer.addView(fixedChild); } } else { mRenderContainer.addView(fixedChild); } } } public void removeFixedView(View fixedChild) { if (mRenderContainer != null) { mRenderContainer.removeView(fixedChild); } } public int getRenderContainerPaddingLeft() { if (mRenderContainer != null) { return mRenderContainer.getPaddingLeft(); } return 0; } public int getRenderContainerPaddingTop() { if (mRenderContainer != null) { return mRenderContainer.getPaddingTop(); } return 0; } public synchronized List<OnWXScrollListener> getWXScrollListeners() { return mWXScrollListeners; } public synchronized void registerOnWXScrollListener(OnWXScrollListener wxScrollListener) { if (mWXScrollListeners == null) { mWXScrollListeners = new ArrayList<>(); } mWXScrollListeners.add(wxScrollListener); } public void setSize(int width, int height) { if (width > 0 && height > 0 & !isDestroy && mRendered && mRenderContainer != null) { ViewGroup.LayoutParams layoutParams = mRenderContainer.getLayoutParams(); if (layoutParams != null) { final float realWidth = width; final float realHeight = height; if (mRenderContainer.getWidth() != width || mRenderContainer.getHeight() != height) { layoutParams.width = width; layoutParams.height = height; mRenderContainer.setLayoutParams(layoutParams); } if (mRootComp != null && layoutParams != null) { final boolean isWidthWrapContent = layoutParams.width == ViewGroup.LayoutParams.WRAP_CONTENT; final boolean isHeightWrapContent = layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT; WXBridgeManager.getInstance().post(new Runnable() { @Override public void run() { WXBridgeManager.getInstance().setDefaultRootSize(getInstanceId(), realWidth, realHeight, isWidthWrapContent, isHeightWrapContent); } }); } } } } /*Global Event*/ private HashMap<String, List<String>> mGlobalEvents = new HashMap<>(); public void fireGlobalEventCallback(String eventName, Map<String, Object> params) { List<String> callbacks = mGlobalEvents.get(eventName); if (callbacks != null) { for (String callback : callbacks) { WXSDKManager.getInstance().callback(mInstanceId, callback, params, true); } } } /** * Fire event callback on a element. * @param elementRef * @param type * @param data * @param domChanges */ public void fireEvent(String elementRef, final String type, final Map<String, Object> data, final Map<String, Object> domChanges, List<Object> eventArgs) { fireEvent(elementRef, type, data, domChanges, eventArgs, null); } public void fireEvent(String elementRef, final String type, final Map<String, Object> data, final Map<String, Object> domChanges, List<Object> eventArgs, EventResult callback) { if (null != mWXPerformance && mWXPerformance.fsCallEventTotalNum < Integer.MAX_VALUE) { mWXPerformance.fsCallEventTotalNum++; } mApmForInstance.updateFSDiffStats(WXInstanceApm.KEY_PAGE_STATS_FS_CALL_EVENT_NUM, 1); WXBridgeManager.getInstance().fireEventOnNode(getInstanceId(), elementRef, type, data, domChanges, eventArgs, callback); } /** * Fire event callback on a element. * @param elementRef * @param type * @param data * @param domChanges */ public void fireEvent(String elementRef, final String type, final Map<String, Object> data, final Map<String, Object> domChanges) { fireEvent(elementRef, type, data, domChanges, null); } public void fireEvent(String elementRef, final String type, final Map<String, Object> data) { fireEvent(elementRef, type, data, null); } public void fireEvent(String ref, String type) { fireEvent(ref, type, new HashMap<String, Object>()); } protected void addEventListener(String eventName, String callback) { if (TextUtils.isEmpty(eventName) || TextUtils.isEmpty(callback)) { return; } List<String> callbacks = mGlobalEvents.get(eventName); if (callbacks == null) { callbacks = new ArrayList<>(); mGlobalEvents.put(eventName, callbacks); } callbacks.add(callback); } protected void removeEventListener(String eventName, String callback) { if (TextUtils.isEmpty(eventName) || TextUtils.isEmpty(callback)) { return; } List<String> callbacks = mGlobalEvents.get(eventName); if (callbacks != null) { callbacks.remove(callback); } } protected void removeEventListener(String eventName) { if (TextUtils.isEmpty(eventName)) { return; } mGlobalEvents.remove(eventName); } /** * Notifies WEEX that this event has occurred * @param eventName WEEX register event * @param module Events occur in this Module * @param params The parameters to be notified to WEEX are required */ public void fireModuleEvent(String eventName, WXModule module, Map<String, Object> params) { if (TextUtils.isEmpty(eventName) || module == null) { return; } Map<String, Object> event = new HashMap<>(); event.put("type", eventName); event.put("module", module.getModuleName()); event.put("data", params); List<String> callbacks = module.getEventCallbacks(eventName); if (callbacks != null) { for (String callback : callbacks) { SimpleJSCallback jsCallback = new SimpleJSCallback(mInstanceId, callback); if (module.isOnce(callback)) { jsCallback.invoke(event); } else { jsCallback.invokeAndKeepAlive(event); } } } } /** * Check whether the current module registered the event * @param eventName EventName register in weex * @param module Events occur in this Module * @return register->true */ public boolean checkModuleEventRegistered(String eventName, WXModule module) { if (module != null) { List<String> events = module.getEventCallbacks(eventName); if (events != null && events.size() > 0) { return true; } } return false; } public WXPerformance getWXPerformance() { return mWXPerformance; } public WXInstanceApm getApmForInstance() { return mApmForInstance; } public WXInstanceExceptionRecord getExceptionRecorder() { return mExceptionRecorder; } public Map<String, Serializable> getUserTrackParams() { return mUserTrackParams; } public void addUserTrackParameter(String key, Serializable value) { if (this.mUserTrackParams == null) { this.mUserTrackParams = new ConcurrentHashMap<>(); } mUserTrackParams.put(key, value); } public void clearUserTrackParameters() { if (this.mUserTrackParams != null) { this.mUserTrackParams.clear(); } } public void removeUserTrackParameter(String key) { if (this.mUserTrackParams != null) { this.mUserTrackParams.remove(key); } } public int getMaxDeepLayer() { return mMaxDeepLayer; } public void setMaxDeepLayer(int maxDeepLayer) { mMaxDeepLayer = maxDeepLayer; mApmForInstance.updateMaxStats(WXInstanceApm.KEY_PAGE_STATS_MAX_DEEP_VIEW, maxDeepLayer); } public void setMaxDomDeep(int maxDomDeep) { mApmForInstance.updateMaxStats(WXInstanceApm.KEY_PAGE_STATS_MAX_DEEP_DOM, maxDomDeep); if (null == mWXPerformance) { return; } if (mWXPerformance.maxDeepVDomLayer <= maxDomDeep) { mWXPerformance.maxDeepVDomLayer = maxDomDeep; } } public void onHttpStart() { if (!mEnd) { mWXPerformance.fsRequestNum++; } } /** * load bundle js listener */ class WXHttpListener implements IWXHttpAdapter.OnHttpListener { private String pageName; private Map<String, Object> options; private String jsonInitData; private WXRenderStrategy flag; private WXSDKInstance instance; private long startRequestTime; private int traceId; private WXHttpListener(String pageName, Map<String, Object> options, String jsonInitData, WXRenderStrategy flag, long startRequestTime) { this.pageName = pageName; this.options = options; this.jsonInitData = jsonInitData; this.flag = flag; this.startRequestTime = startRequestTime; this.traceId = WXTracing.nextId(); if (WXTracing.isAvailable()) { WXTracing.TraceEvent event = WXTracing.newEvent("downloadBundleJS", mInstanceId, -1); event.iid = mInstanceId; event.tname = "Network"; event.ph = "B"; event.traceId = traceId; event.submit(); } } public void setSDKInstance(WXSDKInstance instance) { this.instance = instance; } @Override public void onHttpStart() { if (this.instance != null && this.instance.getWXStatisticsListener() != null) { this.instance.getWXStatisticsListener().onHttpStart(); } mApmForInstance.onStage(WXInstanceApm.KEY_PAGE_STAGES_DOWN_BUNDLE_START); } @Override public void onHeadersReceived(int statusCode, Map<String, List<String>> headers) { if (this.instance != null && this.instance.getWXStatisticsListener() != null) { this.instance.getWXStatisticsListener().onHeadersReceived(); this.instance.onHttpStart(); } if (this.instance != null && this.instance.responseHeaders != null && headers != null) { this.instance.responseHeaders.putAll(headers); } } @Override public void onHttpUploadProgress(int uploadProgress) { } @Override public void onHttpResponseProgress(int loadedLength) { } @Override public void onHttpFinish(WXResponse response) { mApmForInstance.onStage(WXInstanceApm.KEY_PAGE_STAGES_DOWN_BUNDLE_END); if (this.instance != null && this.instance.getWXStatisticsListener() != null) { this.instance.getWXStatisticsListener().onHttpFinish(); } if (WXTracing.isAvailable()) { WXTracing.TraceEvent event = WXTracing.newEvent("downloadBundleJS", mInstanceId, -1); event.traceId = traceId; event.tname = "Network"; event.ph = "E"; event.extParams = new HashMap<>(); if (response != null && response.originalData != null) { event.extParams.put("BundleSize", response.originalData.length); } event.submit(); } mWXPerformance.networkTime = System.currentTimeMillis() - startRequestTime; if (null != response && response.extendParams != null) { mApmForInstance.updateExtInfo(response.extendParams); Object actualNetworkTime = response.extendParams.get("actualNetworkTime"); mWXPerformance.actualNetworkTime = actualNetworkTime instanceof Long ? (long) actualNetworkTime : 0; WXLogUtils.renderPerformanceLog("actualNetworkTime", mWXPerformance.actualNetworkTime); Object pureNetworkTime = response.extendParams.get("pureNetworkTime"); mWXPerformance.pureNetworkTime = pureNetworkTime instanceof Long ? (long) pureNetworkTime : 0; WXLogUtils.renderPerformanceLog("pureNetworkTime", mWXPerformance.pureNetworkTime); Object connectionType = response.extendParams.get("connectionType"); mWXPerformance.connectionType = connectionType instanceof String ? (String) connectionType : ""; Object packageSpendTime = response.extendParams.get("packageSpendTime"); mWXPerformance.packageSpendTime = packageSpendTime instanceof Long ? (long) packageSpendTime : 0; Object syncTaskTime = response.extendParams.get("syncTaskTime"); mWXPerformance.syncTaskTime = syncTaskTime instanceof Long ? (long) syncTaskTime : 0; Object requestType = response.extendParams.get("requestType"); mWXPerformance.requestType = requestType instanceof String ? (String) requestType : "none"; Object cacheType = response.extendParams.get(Dimension.cacheType.toString()); if (cacheType instanceof String) { mWXPerformance.cacheType = (String) cacheType; } Object zCacheInfo = response.extendParams.get("zCacheInfo"); mWXPerformance.zCacheInfo = zCacheInfo instanceof String ? (String) zCacheInfo : ""; if (isNet(mWXPerformance.requestType) && mUserTrackAdapter != null) { WXPerformance performance = new WXPerformance(mInstanceId); if (!TextUtils.isEmpty(mBundleUrl)) { try { performance.args = Uri.parse(mBundleUrl).buildUpon().clearQuery().toString(); } catch (Exception e) { performance.args = pageName; } } if (!"200".equals(response.statusCode)) { performance.errCode = WXErrorCode.WX_ERR_JSBUNDLE_DOWNLOAD.getErrorCode(); performance.appendErrMsg(response.errorCode); performance.appendErrMsg("|"); performance.appendErrMsg(response.errorMsg); } else if ("200".equals(response.statusCode) && (response.originalData == null || response.originalData.length <= 0)) { performance.errCode = WXErrorCode.WX_ERR_JSBUNDLE_DOWNLOAD.getErrorCode(); performance.appendErrMsg(response.statusCode); performance.appendErrMsg("|template is null!"); } else { performance.errCode = WXErrorCode.WX_SUCCESS.getErrorCode(); } if (mUserTrackAdapter != null) { mUserTrackAdapter.commit(getContext(), null, IWXUserTrackAdapter.JS_DOWNLOAD, performance, null); } } } WXLogUtils.renderPerformanceLog("networkTime", mWXPerformance.networkTime); String wxErrorCode = WXInstanceApm.VALUE_ERROR_CODE_DEFAULT; if (response != null && response.originalData != null && TextUtils.equals("200", response.statusCode)) { String template = new String(response.originalData); render(pageName, template, options, jsonInitData, flag); // check content-type } else if (TextUtils.equals(WXErrorCode.WX_DEGRAD_ERR_BUNDLE_CONTENTTYPE_ERROR.getErrorCode(), response.statusCode)) { WXLogUtils.e("user intercept: WX_DEGRAD_ERR_BUNDLE_CONTENTTYPE_ERROR"); wxErrorCode = WXErrorCode.WX_DEGRAD_ERR_BUNDLE_CONTENTTYPE_ERROR.getErrorCode(); onRenderError(wxErrorCode, "|response.errorMsg==" + response.errorMsg + "|instance bundleUrl = \n" + instance.getBundleUrl() + "|instance requestUrl = \n" + Uri.decode(WXSDKInstance.requestUrl)); // check content-length } else if (response != null && response.originalData != null && TextUtils.equals("-206", response.statusCode)) { WXLogUtils.e("user intercept: WX_DEGRAD_ERR_NETWORK_CHECK_CONTENT_LENGTH_FAILED"); wxErrorCode = WXErrorCode.WX_DEGRAD_ERR_NETWORK_CHECK_CONTENT_LENGTH_FAILED.getErrorCode(); onRenderError(wxErrorCode, WXErrorCode.WX_DEGRAD_ERR_NETWORK_CHECK_CONTENT_LENGTH_FAILED.getErrorCode() + "|response.errorMsg==" + response.errorMsg); } else { wxErrorCode = WXErrorCode.WX_DEGRAD_ERR_NETWORK_BUNDLE_DOWNLOAD_FAILED.getErrorCode(); onRenderError(wxErrorCode, response.errorMsg); } if (!WXInstanceApm.VALUE_ERROR_CODE_DEFAULT.equals(wxErrorCode)) { mApmForInstance.addProperty(WXInstanceApm.KEY_PROPERTIES_ERROR_CODE, wxErrorCode); } } } private boolean isNet(String requestType) { return "network".equals(requestType) || "2g".equals(requestType) || "3g".equals(requestType) || "4g".equals(requestType) || "wifi".equals(requestType) || "other".equals(requestType) || "unknown".equals(requestType); } /** * return md5, and bytes length * */ public String getTemplateInfo() { String template = getTemplate(); if (template == null) { return " template md5 null " + JSONObject.toJSONString(responseHeaders); } if (TextUtils.isEmpty(template)) { return " template md5 length 0 " + JSONObject.toJSONString(responseHeaders); } try { byte[] bts = template.getBytes("UTF-8"); String sourceMD5 = WXFileUtils.md5(bts); String sourceBase64MD5 = WXFileUtils.base64Md5(bts); ArrayList<String> sourceMD5List = new ArrayList<>(); ArrayList<String> sourceBase64MD5List = new ArrayList<>(); sourceMD5List.add(sourceMD5); sourceBase64MD5List.add(sourceBase64MD5); responseHeaders.put("templateSourceMD5", sourceMD5List); responseHeaders.put(SOURCE_TEMPLATE_BASE64_MD5, sourceBase64MD5List); return " template md5 " + sourceMD5 + " length " + bts.length + " base64 md5 " + sourceBase64MD5 + " response header " + JSONObject.toJSONString(responseHeaders); } catch (UnsupportedEncodingException e) { return "template md5 getBytes error"; } } /** * check template header md5 match with header content-md5 * */ public boolean isContentMd5Match() { if (responseHeaders == null) { return true; } List<String> contentMD5s = responseHeaders.get("Content-Md5"); if (contentMD5s == null) { contentMD5s = responseHeaders.get("content-md5"); } if (contentMD5s == null || contentMD5s.size() <= 0) { return true; } String md5 = contentMD5s.get(0); List<String> sourceBase64Md5 = responseHeaders.get(SOURCE_TEMPLATE_BASE64_MD5); if (sourceBase64Md5 == null) { getTemplateInfo(); sourceBase64Md5 = responseHeaders.get(SOURCE_TEMPLATE_BASE64_MD5); } if (sourceBase64Md5 == null || sourceBase64Md5.size() == 0) { return true; } return md5.equals(sourceBase64Md5.get(0)); } public String getTemplate() { if (templateRef == null) { return null; } return templateRef.get(); } public void setTemplate(String template) { this.templateRef = new WeakReference<String>(template); } public interface NestedInstanceInterceptor { void onCreateNestInstance(WXSDKInstance instance, NestedContainer container); } public void OnVSync() { boolean forceLayout = WXBridgeManager.getInstance().notifyLayout(getInstanceId()); if (forceLayout) { WXBridgeManager.getInstance().post(new Runnable() { @Override public void run() { WXBridgeManager.getInstance().forceLayout(getInstanceId()); } }); } } public void addContentBoxMeasurement(long renderObjectPtr, ContentBoxMeasurement contentBoxMeasurement) { mContentBoxMeasurements.put(renderObjectPtr, contentBoxMeasurement); } public ContentBoxMeasurement getContentBoxMeasurement(long renderObjectPtr) { return mContentBoxMeasurements.get(renderObjectPtr); } }