content: /**
   * 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 org.apache.weex.performance;
  
  import java.util.HashMap;
  import java.util.Map;
  import java.util.Set;
  import java.util.concurrent.ConcurrentHashMap;
  import java.util.concurrent.CopyOnWriteArraySet;
  
  import android.graphics.Rect;
  import android.os.Handler;
  import android.os.Looper;
  import android.text.TextUtils;
  import android.util.Log;
  import org.apache.weex.WXEnvironment;
  import org.apache.weex.WXSDKInstance;
  import org.apache.weex.WXSDKManager;
  import org.apache.weex.bridge.WXBridgeManager;
  import org.apache.weex.common.WXErrorCode;
  import org.apache.weex.common.WXPerformance;
  import org.apache.weex.common.WXRenderStrategy;
  import org.apache.weex.ui.component.WXComponent;
  import org.apache.weex.utils.WXExceptionUtils;
  import org.apache.weex.utils.WXLogUtils;
  import org.apache.weex.utils.WXUtils;
  
  public class WXInstanceApm {
  
      public static final String WEEX_PAGE_TOPIC = "weex_page";
  
      /************** properties *****************/
      public static final String KEY_PROPERTIES_ERROR_CODE = "wxErrorCode";
      //public static final String KEY_PAGE_PROPERTIES_LAUNCH_ID = "wxLaunchId";
      public static final String KEY_PAGE_PROPERTIES_BIZ_ID = "wxBizID";
      public static final String KEY_PAGE_PROPERTIES_BUBDLE_URL = "wxBundleUrl";
      public static final String KEY_PAGE_PROPERTIES_JSLIB_VERSION = "wxJSLibVersion";
      public static final String KEY_PAGE_PROPERTIES_WEEX_VERSION = "wxSDKVersion";
      public static final String KEY_PAGE_PROPERTIES_REQUEST_TYPE = "wxRequestType";
      public static final String KEY_PAGE_PROPERTIES_CACHE_TYPE = "wxCacheType";
      public static final String KEY_PAGE_PROPERTIES_CACHE_INFO = "wxZCacheInfo";
      public static final String KEY_PAGE_PROPERTIES_JS_FM_INI = "wxJsFrameworkInit";
      public static final String KEY_PAGE_PROPERTIES_CONTAINER_NAME = "wxContainerName";
      public static final String KEY_PAGE_PROPERTIES_INSTANCE_TYPE = "wxInstanceType";
      public static final String KEY_PAGE_PROPERTIES_PARENT_PAGE = "wxParentPage";
      public static final String KEY_PAGE_PROPERTIES_BUNDLE_TYPE = "wxBundleType";
      public static final String KEY_PAGE_PROPERTIES_RENDER_TYPE = "wxRenderType";
      public static final String KEY_PAGE_PROPERTIES_UIKIT_TYPE = "wxUIKitType";
  
      /************** stages *****************/
      public static final String KEY_PAGE_STAGES_CONTAINER_READY = "wxContainerReady";
      public static final String KEY_PAGE_STAGES_DOWN_BUNDLE_START = "wxStartDownLoadBundle";
      public static final String KEY_PAGE_STAGES_DOWN_BUNDLE_END = "wxEndDownLoadBundle";
      public static final String KEY_PAGE_STAGES_RENDER_ORGIGIN = "wxRenderTimeOrigin";
      public static final String KEY_PAGE_STAGES_LOAD_BUNDLE_START = "wxStartLoadBundle";
      public static final String KEY_PAGE_STAGES_LOAD_BUNDLE_END = "wxEndLoadBundle";
      public static final String KEY_PAGE_STAGES_FIRST_INTERACTION_VIEW = "wxFirstInteractionView";
      public static final String KEY_PAGE_STAGES_CREATE_FINISH= "wxJSBundleCreateFinish";
      public static final String KEY_PAGE_STAGES_FSRENDER = "wxFsRender";
      public static final String KEY_PAGE_STAGES_NEW_FSRENDER = "wxNewFsRender";
      public static final String KEY_PAGE_STAGES_END_EXCUTE_BUNDLE = "wxEndExecuteBundle";
      public static final String KEY_PAGE_STAGES_INTERACTION = "wxInteraction";
      public static final String KEY_PAGE_STAGES_DESTROY = "wxDestroy";
  
      //Custom preprocessing start, called when activity created or other time. Called by other activity
      public static final String KEY_PAGE_STAGES_CUSTOM_PREPROCESS_START = "wxCustomPreprocessStart";
      //Custom preprocessing end, called when you'are able to start weex render. Called by other activity
      public static final String KEY_PAGE_STAGES_CUSTOM_PREPROCESS_END = "wxCustomPreprocessEnd";
  
      /************** stats *****************/
      public static final String KEY_PAGE_STATS_BUNDLE_SIZE = "wxBundleSize";
      public static final String KEY_PAGE_STATS_FS_CALL_JS_TIME = "wxFSCallJsTotalTime";
      public static final String KEY_PAGE_STATS_FS_CALL_JS_NUM = "wxFSCallJsTotalNum";
      public static final String KEY_PAGE_STATS_FS_TIMER_NUM = "wxFSTimerCount";
      public static final String KEY_PAGE_STATS_FS_CALL_NATIVE_TIME = "wxFSCallNativeTotalTime";
      public static final String KEY_PAGE_STATS_FS_CALL_NATIVE_NUM = "wxFSCallNativeTotalNum";
      public static final String KEY_PAGE_STATS_FS_CALL_EVENT_NUM = "wxFSCallEventTotalNum";
      public static final String KEY_PAGE_STATS_FS_REQUEST_NUM = "wxFSRequestNum";
      public static final String KEY_PAGE_STATS_CELL_EXCEED_NUM = "wxCellExceedNum";
      public static final String KEY_PAGE_STATS_MAX_DEEP_VIEW = "wxMaxDeepViewLayer";
      public static final String KEY_PAGE_STATS_MAX_DEEP_DOM = "wxMaxDeepVDomLayer";
      public static final String KEY_PAGE_STATS_MAX_COMPONENT_NUM = "wxMaxComponentCount";
      public static final String KEY_PAGE_STATS_WRONG_IMG_SIZE_COUNT = "wxWrongImgSizeCount";
      public static final String KEY_PAGE_STATS_EMBED_COUNT = "wxEmbedCount";
      public static final String KEY_PAGE_STATS_LARGE_IMG_COUNT = "wxLargeImgMaxCount";
      public static final String KEY_PAGE_STATS_BODY_RATIO = "wxBodyRatio";
      public static final String KEY_PAGE_STATS_SCROLLER_NUM = "wxScrollerCount";
      public static final String KEY_PAGE_STATS_CELL_DATA_UN_RECYCLE_NUM = "wxCellDataUnRecycleCount";
      public static final String KEY_PAGE_STATS_CELL_UN_RE_USE_NUM = "wxCellUnReUseCount";
      public static final String KEY_PAGE_STATS_IMG_UN_RECYCLE_NUM = "wxImgUnRecycleCount";
  
      public static final String KEY_PAGE_STATS_I_SCREEN_VIEW_COUNT = "wxInteractionScreenViewCount";
      public static final String KEY_PAGE_STATS_I_ALL_VIEW_COUNT = "wxInteractionAllViewCount";
      public static final String KEY_PAGE_STATS_I_COMPONENT_CREATE_COUNT = "wxInteractionComponentCreateCount";
      public static final String KEY_PAGE_ANIM_BACK_NUM = "wxAnimationInBackCount";
      public static final String KEY_PAGE_TIMER_BACK_NUM = "wxTimerInBackCount";
      public static final String KEY_PAGE_STATS_ACTUAL_DOWNLOAD_TIME = "wxActualNetworkTime";
  
      public static final String KEY_PAGE_STATS_IMG_LOAD_NUM = "wxImgLoadCount";
      public static final String KEY_PAGE_STATS_IMG_LOAD_SUCCESS_NUM = "wxImgLoadSuccessCount";
      public static final String KEY_PAGE_STATS_IMG_LOAD_FAIL_NUM = "wxImgLoadFailCount";
      public static final String KEY_PAGE_STATS_NET_NUM = "wxNetworkRequestCount";
      public static final String KEY_PAGE_STATS_NET_SUCCESS_NUM = "wxNetworkRequestSuccessCount";
      public static final String KEY_PAGE_STATS_NET_FAIL_NUM = "wxNetworkRequestFailCount";
      public static final String KEY_PAGE_STATS_JSLIB_INIT_TIME = "wxJSLibInitTime";
      public static final String KEY_PAGE_STATS_VIEW_CREATE_COST = "wxViewCost";
      public static final String KEY_PAGE_STATS_COMPONENT_CREATE_COST = "wxComponentCost";
      public static final String KEY_PAGE_STATS_EXECUTE_JS_CALLBACK_COST = "wxExecJsCallBack";
      public static final String KEY_PAGE_STATS_LAYOUT_TIME = "wxLayoutTime";
  
  
      /************** value *****************/
      public static final String VALUE_ERROR_CODE_DEFAULT = "0";
      public static final String VALUE_BUNDLE_LOAD_LENGTH = "wxLoadedLength";
  
      private String mInstanceId;
      private IWXApmMonitorAdapter apmInstance;
      private Map<String, Double> recordStatsMap;
      public final Map<String, Long> stageMap;
      private Map<String,Object> mPropertiesMap;
      private boolean isFSEnd;
      private boolean mHasInit = false;
      private boolean mEnd = false;
      private boolean hasRecordFistInteractionView =false;
      public final Map<String,Object> extInfo;
      public boolean forceStopRecordInteraction = false;
      public Rect instanceRect;
      public String reportPageName;
      public boolean hasReportLayerOverDraw = false;
      public boolean hasAddView;
      private Handler mUIHandler;
  
      public Set<String> exceptionRecord = new CopyOnWriteArraySet<String>();
  
      private double interactionLayoutTime;
      public long componentCreateTime;
      private long interactionComponentCreateTime;
      public long viewCreateTime;
      private long interactionViewCreateTime;
      //next version
      private long wxExecJsCallBackTime;
      private long interactionJsCallBackTime;
  
  
      private boolean mHasRecordDetailData = false;
  
      /**
       * send performance value to js
       **/
      private boolean hasSendInteractionToJS = false;
      public volatile boolean isReady = true;
  
      public WXInstanceApm(String instanceId) {
          mInstanceId = instanceId;
          extInfo = new ConcurrentHashMap<>();
          stageMap = new ConcurrentHashMap<>();
          mUIHandler = new Handler(Looper.getMainLooper());
          recordStatsMap = new ConcurrentHashMap<>();
          mPropertiesMap = new ConcurrentHashMap<>();
          IApmGenerator generator = WXSDKManager.getInstance().getApmGenerater();
          if (null != generator) {
              apmInstance = generator.generateApmInstance(WEEX_PAGE_TOPIC);
          }
      }
  
      public void onInstanceReady(boolean isPreDownLoad){
          this.isReady = true;
          if (isPreDownLoad){
              onStage(KEY_PAGE_STAGES_DOWN_BUNDLE_START);
          }
          doInit();
          for (Map.Entry<String,Long> stage:stageMap.entrySet()){
              sendStageInfo(stage.getKey(),stage.getValue());
          }
          for (Map.Entry<String,Double> stats:recordStatsMap.entrySet()){
             sendStats(stats.getKey(),stats.getValue());
          }
          for (Map.Entry<String,Object> p:mPropertiesMap.entrySet()){
             sendProperty(p.getKey(),p.getValue());
          }
      }
  
      /**
       * record event
       */
      public void onEvent(String name, Object value) {
          if (null == apmInstance) {
              return;
          }
          apmInstance.onEvent(name, value);
      }
  
      /**
       * record stage
       */
      public void onStage(String name) {
          long time =  WXUtils.getFixUnixTime();
          onStageWithTime(name,time);
      }
  
      /**
       *
       * @param name stage
       * @param time unixTime ,plz use WXUtils.getFixUnixTime
       */
      public void onStageWithTime(String name,long time){
          if (mEnd){
              return;
          }
          if (null == name){
              return;
          }
          stageMap.put(name,time);
          if (isReady){
              sendStageInfo(name,time);
          }
      }
  
      private void sendStageInfo(String name,long time){
          if(WXAnalyzerDataTransfer.isOpenPerformance){
              WXAnalyzerDataTransfer.transferPerformance(mInstanceId,"stage",name,time);
          }
  
          if (KEY_PAGE_STAGES_RENDER_ORGIGIN.equalsIgnoreCase(name)){
              mUIHandler.postDelayed(jsPerformanceCallBack,8000);
          }
  
          if (null == apmInstance) {
              return;
          }
  
          apmInstance.onStage(name, time);
      }
  
      private Runnable jsPerformanceCallBack = new Runnable() {
          @Override
          public void run() {
              sendPerformanceToJS();
          }
      };
  
      /**
       * record property
       */
      public void addProperty(String key, Object value) {
          if (mEnd){
              return;
          }
          if (null == key || null == value){
              return;
          }
          mPropertiesMap.put(key,value);
          if (isReady){
              sendProperty(key,value);
          }
      }
  
      private void sendProperty(String key,Object value){
          if(WXAnalyzerDataTransfer.isOpenPerformance){
              WXAnalyzerDataTransfer.transferPerformance(mInstanceId,"properties",key,value);
          }
  
          if (null == apmInstance) {
              return;
          }
          apmInstance.addProperty(key, value);
      }
  
      /**
       * record statistic
       */
      public void addStats(String key, double value) {
          if (mEnd){
              return;
          }
          if (null == key){
              return;
          }
          recordStatsMap.put(key,value);
          if (isReady){
              sendStats(key,value);
          }
      }
  
      private void sendStats(String key,double value){
          if(WXAnalyzerDataTransfer.isOpenPerformance){
              WXAnalyzerDataTransfer.transferPerformance(mInstanceId,"stats",key,value);
          }
  
          if (null == apmInstance) {
              return;
          }
          apmInstance.addStats(key, value);
      }
  
  
      public boolean hasInit(){
          return mHasInit;
      }
  
      /**
       * start record
       */
      public void doInit() {
          if (!isReady){
              return;
          }
          if (mHasInit){
              return;
          }
          mHasInit = true;
          if (null == apmInstance) {
              return;
          }
          apmInstance.onStart(mInstanceId);
          WXSDKInstance instance = WXSDKManager.getInstance().getAllInstanceMap().get(mInstanceId);
          String url = null == instance ? "unKnowUrl" : instance.getBundleUrl();
          addProperty(KEY_PAGE_PROPERTIES_BUBDLE_URL, url);
          addProperty(KEY_PROPERTIES_ERROR_CODE, VALUE_ERROR_CODE_DEFAULT);
          addProperty(KEY_PAGE_PROPERTIES_JSLIB_VERSION, WXEnvironment.JS_LIB_SDK_VERSION);
          addProperty(KEY_PAGE_PROPERTIES_WEEX_VERSION, WXEnvironment.WXSDK_VERSION);
          addStats("wxReInitCount", WXBridgeManager.reInitCount);
          if (null != instance){
              addProperty(KEY_PAGE_PROPERTIES_UIKIT_TYPE, instance.getRenderType());
          }
  
          addProperty("wxUseRuntimeApi",WXEnvironment.sUseRunTimeApi);
          if (instance != null && (instance.isUsingEaglePlugin()
              || instance.getRenderStrategy() == WXRenderStrategy.DATA_RENDER_BINARY //todo only for compat.
              || instance.getRenderStrategy() == WXRenderStrategy.DATA_RENDER
          )) {
              addProperty(KEY_PAGE_PROPERTIES_RENDER_TYPE, WXEnvironment.EAGLE);
          }
          if (null != instance) {
              for (Map.Entry<String, String> entry : instance.getContainerInfo().entrySet()) {
                  addProperty(entry.getKey(), entry.getValue());
              }
          }
      }
  
      public void setPageName(String pageName) {
          if (TextUtils.isEmpty(pageName)) {
              WXSDKInstance instance = WXSDKManager.getInstance().getAllInstanceMap().get(mInstanceId);
              if (null != instance) {
                  pageName = instance.getContainerInfo().get(KEY_PAGE_PROPERTIES_CONTAINER_NAME);
              }
          }
          reportPageName = null == apmInstance?pageName:apmInstance.parseReportUrl(pageName);
          reportPageName = TextUtils.isEmpty(reportPageName) ? "emptyPageName" : reportPageName;
          addProperty(KEY_PAGE_PROPERTIES_BIZ_ID, reportPageName);
      }
  
      public void onAppear(){
          if (null == apmInstance) {
              return;
          }
          apmInstance.onAppear();
      }
  
      public void onDisAppear(){
          if (null == apmInstance) {
              return;
          }
          apmInstance.onDisappear();
      }
  
      /**
       * end record
       */
      public void onEnd() {
          if (null == apmInstance || mEnd) {
              return;
          }
          new Handler(Looper.getMainLooper()).removeCallbacks(delayCollectDataTask);
          recordPerformanceDetailData();
          exceptionRecord.clear();
          mUIHandler.removeCallbacks(jsPerformanceCallBack);
          onStage(KEY_PAGE_STAGES_DESTROY);
          if (mHasInit && null != apmInstance){
              apmInstance.onEnd();
          }
          mEnd = true;
          if (WXEnvironment.isApkDebugable()){
              printLog();
          }
      }
  
      public void doDelayCollectData(){
          new Handler(Looper.getMainLooper()).postDelayed(delayCollectDataTask,8000);
      }
  
      private Runnable delayCollectDataTask = new Runnable() {
          @Override
          public void run() {
              recordPerformanceDetailData();
          }
      };
  
  
      private void printLog(){
          Long startDownLoad = stageMap.get(KEY_PAGE_STAGES_DOWN_BUNDLE_START);
          Long endDownLoad = stageMap.get(KEY_PAGE_STAGES_DOWN_BUNDLE_END);
          Long interaction = stageMap.get(KEY_PAGE_STAGES_INTERACTION);
          Long containerReady = stageMap.get(KEY_PAGE_STAGES_CONTAINER_READY);
          if (null != endDownLoad && null != startDownLoad){
              WXLogUtils.d("test->", "downLoadTime: "+ (endDownLoad - startDownLoad));
          }
          if (null != endDownLoad && null != interaction){
              WXLogUtils.d("test->", "renderTime: "+ (interaction - endDownLoad));
          }
          if (null != containerReady && null !=interaction){
              WXLogUtils.d("test->", "showTime: "+ (interaction - containerReady));
          }
  
      }
  
      public void arriveNewFsRenderTime(){
          if (null == apmInstance){
              return;
          }
          onStage(WXInstanceApm.KEY_PAGE_STAGES_NEW_FSRENDER);
      }
  
      public void arriveFSRenderTime() {
          if (null == apmInstance){
              return;
          }
          isFSEnd = true;
          onStage(WXInstanceApm.KEY_PAGE_STAGES_FSRENDER);
      }
  
      public void arriveInteraction(WXComponent targetComponent) {
          if (null == apmInstance || null == targetComponent || targetComponent.getInstance() == null) {
              return;
          }
  
          if (WXAnalyzerDataTransfer.isOpenPerformance){
              WXAnalyzerDataTransfer.transferInteractionInfo(targetComponent);
          }
  
  
          if (null == apmInstance){
              return;
          }
  
          WXPerformance performanceRecord = targetComponent.getInstance().getWXPerformance();
          if (null == performanceRecord){
              return;
          }
  
          long curTime = WXUtils.getFixUnixTime();
  
          if (WXAnalyzerDataTransfer.isInteractionLogOpen()){
              Log.d(WXAnalyzerDataTransfer.INTERACTION_TAG, "[client][wxinteraction]"
                  + targetComponent.getInstance().getInstanceId()
                  +","+targetComponent.getComponentType()
                  +","+targetComponent.getRef()
                  +","+targetComponent.getStyles()
                  +","+targetComponent.getAttrs()
              );
          }
  
          if (!hasRecordFistInteractionView){
              onStage(KEY_PAGE_STAGES_FIRST_INTERACTION_VIEW);
              hasRecordFistInteractionView = true;
          }
          if (forceStopRecordInteraction){
              return;
          }
          long now = WXUtils.getFixUnixTime();
          if (now - preUpdateTime > 50){
              //for performance, reduce jni calls
              WXBridgeManager.getInstance().onInteractionTimeUpdate(mInstanceId);
              preUpdateTime = now;
          }
  
          interactionComponentCreateTime = componentCreateTime;
          interactionViewCreateTime = viewCreateTime;
          Double layoutTime = recordStatsMap.get("wxLayoutTime");
          interactionLayoutTime = layoutTime ==null? 0:layoutTime;
  
          performanceRecord.interactionTime = curTime - performanceRecord.renderUnixTimeOrigin;
          performanceRecord.interactionRealUnixTime = System.currentTimeMillis();
          onStageWithTime(KEY_PAGE_STAGES_INTERACTION,curTime);
  
          updateDiffStats(KEY_PAGE_STATS_I_SCREEN_VIEW_COUNT, 1);
          updateMaxStats(KEY_PAGE_STATS_I_ALL_VIEW_COUNT, performanceRecord.localInteractionViewAddCount);
          WXSDKInstance instance = WXSDKManager.getInstance().getSDKInstance(mInstanceId);
          if (null != instance) {
              updateMaxStats(KEY_PAGE_STATS_I_COMPONENT_CREATE_COUNT, instance.getWXPerformance().componentCount);
          }
      }
  
      private long preUpdateTime = 0;
  
      public void updateFSDiffStats(String name, double diffValue) {
          if (null == apmInstance || isFSEnd) {
              return;
          }
          updateDiffStats(name, diffValue);
      }
  
      public void recordPerformanceDetailData(){
          if (mHasRecordDetailData){
              return;
          }
          mHasRecordDetailData = true;
          addStats(KEY_PAGE_STATS_VIEW_CREATE_COST,interactionViewCreateTime);
          addStats(KEY_PAGE_STATS_COMPONENT_CREATE_COST,interactionComponentCreateTime);
          addStats(KEY_PAGE_STATS_EXECUTE_JS_CALLBACK_COST,interactionJsCallBackTime);
          addStats(KEY_PAGE_STATS_LAYOUT_TIME,interactionLayoutTime);
      }
  
      public void updateDiffStats(String name, double diffValue) {
          if (null == apmInstance) {
              return;
          }
          Double preVal = recordStatsMap.containsKey(name) ? recordStatsMap.get(name) : 0;
          //fix by use ConcurrentHashMap,but not sure,so report if error still happen
          if (null == preVal){
              WXExceptionUtils.commitCriticalExceptionRT(
                  "",
                  WXErrorCode.WX_ERR_HASH_MAP_TMP,
                  "updateDiffStats",
                  "key : "+name,
                  null
              );
              return;
          }
  
          double currentValue = preVal + diffValue;
          addStats(name, currentValue);
      }
  
      public void updateMaxStats(String name, double currentVal) {
          if (null == apmInstance) {
              return;
          }
          Double maxValue = recordStatsMap.containsKey(name) ? recordStatsMap.get(name) : 0;
          //fix by use ConcurrentHashMap,but not sure,so report if error still happen
          if (null == maxValue){
              WXExceptionUtils.commitCriticalExceptionRT(
                  "",
                  WXErrorCode.WX_ERR_HASH_MAP_TMP,
                  "updateMaxStats",
                  "key : "+name,
                  null
              );
              return;
          }
          if (maxValue < currentVal) {
              maxValue = currentVal;
              addStats(name, maxValue);
          }
      }
  
      public void updateRecordInfo(Map<String, Object> extParams) {
          if (null == apmInstance || null == extParams) {
              return;
          }
  
          addPropeyFromExtParms(KEY_PAGE_PROPERTIES_REQUEST_TYPE, KEY_PAGE_PROPERTIES_REQUEST_TYPE, extParams);
          addPropeyFromExtParms("cacheType", KEY_PAGE_PROPERTIES_CACHE_TYPE, extParams);
          addPropeyFromExtParms("zCacheInfo", KEY_PAGE_PROPERTIES_CACHE_INFO, extParams);
  
          addStats(KEY_PAGE_STATS_JSLIB_INIT_TIME, WXEnvironment.sJSLibInitTime);
          addProperty(KEY_PAGE_PROPERTIES_JS_FM_INI, WXEnvironment.JsFrameworkInit);
  
          Object wxNetLibDownBundleTime = extParams.get("actualNetworkTime");
          if (wxNetLibDownBundleTime instanceof Long) {
              updateDiffStats(KEY_PAGE_STATS_ACTUAL_DOWNLOAD_TIME, ((Long)wxNetLibDownBundleTime).doubleValue());
          }
      }
  
      private void addPropeyFromExtParms(String fromKey, String toKey, Map<String, Object> extParams) {
          Object value = extParams.get(fromKey);
          if (value instanceof String) {
              addProperty(toKey, value);
          }
      }
  
      /************** called by IWXHttpAdapter implementer *****************/
  
      public void actionNetRequest() {
          if (!isFSEnd) {
              updateFSDiffStats(WXInstanceApm.KEY_PAGE_STATS_FS_REQUEST_NUM, 1);
          }
          updateDiffStats(WXInstanceApm.KEY_PAGE_STATS_NET_NUM, 1);
      }
  
      public void actionNetResult(boolean succeed, String errorCode) {
          if (succeed) {
              updateDiffStats(WXInstanceApm.KEY_PAGE_STATS_NET_SUCCESS_NUM, 1);
          } else {
              updateDiffStats(WXInstanceApm.KEY_PAGE_STATS_NET_FAIL_NUM, 1);
          }
      }
  
      /************** called by IWXImgLoaderAdapter implementer *****************/
  
      public void actionLoadImg() {
          updateDiffStats(WXInstanceApm.KEY_PAGE_STATS_IMG_LOAD_NUM, 1);
      }
  
      public void actionLoadImgResult(boolean succeed, String errorCode) {
          if (succeed) {
              updateDiffStats(WXInstanceApm.KEY_PAGE_STATS_IMG_LOAD_SUCCESS_NUM, 1);
          } else {
              updateDiffStats(WXInstanceApm.KEY_PAGE_STATS_IMG_LOAD_FAIL_NUM, 1);
          }
      }
  
      public void sendPerformanceToJS() {
          if (hasSendInteractionToJS) {
              return;
          }
          hasSendInteractionToJS = true;
          WXSDKInstance instance = WXSDKManager.getInstance().getAllInstanceMap().get(mInstanceId);
          if (null == instance) {
              return;
          }
  
          Map<String,String> sendProperties = new HashMap<>(2);
          sendProperties.put(KEY_PAGE_PROPERTIES_BIZ_ID,reportPageName);
          sendProperties.put(KEY_PAGE_PROPERTIES_BUBDLE_URL,instance.getBundleUrl());
  
          Map<String,Long> sendStage = new HashMap<>(1);
          sendStage.put(KEY_PAGE_STAGES_INTERACTION,instance.getWXPerformance().interactionRealUnixTime);
  
          Map<String, Object> data = new HashMap<>(2);
          data.put("stage",sendStage);
          data.put("properties",sendProperties);
  
          instance.fireGlobalEventCallback("wx_apm", data);
      }
  
  
      public String toPerfString() {
          Long start = stageMap.get(KEY_PAGE_STAGES_RENDER_ORGIGIN);
          Long end = stageMap.get(KEY_PAGE_STAGES_INTERACTION);
          Long wxNewFsRender = stageMap.get(KEY_PAGE_STAGES_NEW_FSRENDER);
          StringBuilder builder = new StringBuilder();
          if(start != null && end != null){
              builder.append("interactiveTime " + (end - start) + "ms");
          }
          if(wxNewFsRender != null){
              builder.append(" wxNewFsRender " + (wxNewFsRender) + "ms");
          }
          return builder.toString();
     }
  
     public void updateNativePerformanceData(Map<String,String> nativePerformanceData){
          if (null == nativePerformanceData){
              return;
          }
          for (Map.Entry<String,String> entry : nativePerformanceData.entrySet()){
              double value = -1;
              try {
                  value = Double.valueOf(entry.getValue());
              }catch (Exception e){
                  e.printStackTrace();
              }
              if (value != -1){
                  recordStatsMap.put(entry.getKey(),value);
              }
          }
     }
   }
  