blob: 9f59e30cca80b848a41f3e6482b34a080f974b37 [file] [log] [blame]
/*
* 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.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.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 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 int mInstanceViewPortWidth = 750;
private @NonNull
FlatGUIContext mFlatGUIContext =new FlatGUIContext();
public long mRenderStartNanos;
public int mExecJSTraceId = WXTracing.nextId();
/**
*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());
}
});
}
}
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 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;
mNativeInvokeHelper = new NativeInvokeHelper(mInstanceId);
mWXPerformance = new WXPerformance();
mWXPerformance.WXSDKVersion = WXEnvironment.WXSDK_VERSION;
mWXPerformance.JSLibInitTime = WXEnvironment.sJSLibInitTime;
mUserTrackAdapter=WXSDKManager.getInstance().getIWXUserTrackAdapter();
}
/**
* 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) {
((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) {
}
/**
* 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;
}
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;
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);
}
Uri uri = Uri.parse(url);
if (uri != null && TextUtils.equals(uri.getScheme(), "file")) {
render(pageName, WXFileUtils.loadFileOrAsset(assembleFilePath(uri), mContext), 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){
Set<String> componentTypes= WXComponentFactory.getComponentTypesByInstanceId(getInstanceId());
if(componentTypes!=null && componentTypes.contains(WXBasicComponentType.SCROLLER)){
mWXPerformance.useScroller=1;
}
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());
mContext.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(){
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 void onViewAppear(){
WXComponent comp = getRootComponent();
if(comp != null) {
fireEvent( comp.getRef(), Constants.Event.VIEWAPPEAR,null, null);
for(OnInstanceVisibleListener instance:mVisibleListeners){
instance.onAppear();
}
}
}
public void onCreateFinish() {
if (null != mWXPerformance){
mWXPerformance.callCreateFinishTime=System.currentTimeMillis()-mWXPerformance
.renderTimeOrigin;
}
if (mContext != null) {
runOnUiThread(new Runnable() {
@Override
public void run() {
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) {
runOnUiThread(new Runnable() {
@Override
public void run() {
if (mRenderListener != null && mContext != null) {
mRenderListener.onRenderSuccess(WXSDKInstance.this, width, height);
if (mUserTrackAdapter != null) {
WXPerformance performance=new WXPerformance();
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) {
runOnUiThread(new Runnable() {
@Override
public void run() {
if (mRenderListener != null && mContext != null) {
mRenderListener.onRefreshSuccess(WXSDKInstance.this, width, height);
}
}
});
}
}
/**
* when add/rm/mv element
*/
public void onElementChange(){
if (isDestroy() || !mEnd ||null == mRenderContainer || mRenderContainer.isPageHasEvent() ||
mWXPerformance == null){
return;
}
long lazyLoadTime = System.currentTimeMillis()- mWXPerformance.renderTimeOrigin - mWXPerformance
.callCreateFinishTime;
if (lazyLoadTime > 8000){
//bad case
return;
}
getWXPerformance().interactionTime = mWXPerformance.callCreateFinishTime + lazyLoadTime;
}
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 firstScreenRenderFinished() {
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();
}
}
});
}
mWXPerformance.screenRenderTime = System.currentTimeMillis() - mRenderStartTime;
mWXPerformance.fsRenderTime = System.currentTimeMillis();
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 createInstanceFinished(long time) {
}
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()) {
if(mRendered) {
WXSDKManager.getInstance().destroyInstance(mInstanceId);
}
WXComponentFactory.removeComponentTypesByInstanceId(getInstanceId());
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();
}
});
}
}
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.deepInComponentTree=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++;
}
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 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 void setMaxDomDeep(int 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();
}
}
@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) {
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(response.extendParams!=null){
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();
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);
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");
onRenderError(WXErrorCode.WX_DEGRAD_ERR_BUNDLE_CONTENTTYPE_ERROR.getErrorCode(),
"|response.errorMsg==" + response.errorMsg +
"|instance.getTemplateInfo == \n" + instance.getTemplateInfo() +
"|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");
onRenderError(
WXErrorCode.WX_DEGRAD_ERR_NETWORK_CHECK_CONTENT_LENGTH_FAILED.getErrorCode(),
WXErrorCode.WX_DEGRAD_ERR_NETWORK_CHECK_CONTENT_LENGTH_FAILED.getErrorCode() +
"|response.errorMsg==" + response.errorMsg +
"|instance.getTemplateInfo == \n" + instance.getTemplateInfo());
}
else {
onRenderError(WXErrorCode.WX_DEGRAD_ERR_NETWORK_BUNDLE_DOWNLOAD_FAILED.getErrorCode(),
response.errorMsg);
}
}
}
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);
}
}