diff --git a/doric-android/doric/src/main/java/pub/doric/DoricContext.java b/doric-android/doric/src/main/java/pub/doric/DoricContext.java index 3a267c47..54fd82ea 100644 --- a/doric-android/doric/src/main/java/pub/doric/DoricContext.java +++ b/doric-android/doric/src/main/java/pub/doric/DoricContext.java @@ -19,8 +19,6 @@ import android.animation.AnimatorSet; import android.content.Context; import android.content.Intent; import android.text.TextUtils; -import android.view.View; -import android.view.ViewGroup; import androidx.fragment.app.Fragment; @@ -37,6 +35,7 @@ import java.util.concurrent.Callable; import pub.doric.async.AsyncResult; import pub.doric.navbar.IDoricNavBar; import pub.doric.navigator.IDoricNavigator; +import pub.doric.performance.DoricPerformanceProfile; import pub.doric.plugin.DoricJavaPlugin; import pub.doric.shader.RootNode; import pub.doric.shader.ViewNode; @@ -60,6 +59,7 @@ public class DoricContext { private JSONObject initParams; private IDoricDriver doricDriver; private final Map> mHeadNodes = new HashMap<>(); + private final DoricPerformanceProfile performanceProfile; public Collection allHeadNodes(String type) { return mHeadNodes.get(type).values(); @@ -107,6 +107,11 @@ public class DoricContext { this.mContextId = contextId; this.source = source; this.extra = extra; + this.performanceProfile = new DoricPerformanceProfile(contextId); + } + + public DoricPerformanceProfile getPerformanceProfile() { + return this.performanceProfile; } public String getSource() { diff --git a/doric-android/doric/src/main/java/pub/doric/DoricNativeDriver.java b/doric-android/doric/src/main/java/pub/doric/DoricNativeDriver.java index c39c06fb..c4b2031e 100644 --- a/doric-android/doric/src/main/java/pub/doric/DoricNativeDriver.java +++ b/doric-android/doric/src/main/java/pub/doric/DoricNativeDriver.java @@ -21,6 +21,8 @@ import android.util.Log; import com.github.pengfeizhou.jscore.JSDecoder; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -28,6 +30,7 @@ import java.util.concurrent.Executors; import pub.doric.async.AsyncCall; import pub.doric.async.AsyncResult; import pub.doric.engine.DoricJSEngine; +import pub.doric.performance.DoricPerformanceProfile; import pub.doric.utils.DoricConstant; import pub.doric.utils.ThreadMode; @@ -82,7 +85,6 @@ public class DoricNativeDriver implements IDoricDriver { @Override public void onFinish() { - } }); return asyncResult; @@ -90,10 +92,38 @@ public class DoricNativeDriver implements IDoricDriver { @Override public AsyncResult invokeDoricMethod(final String method, final Object... args) { + DoricPerformanceProfile profile = null; + Object contextId = args.length > 0 ? args[0] : null; + if (contextId instanceof String) { + DoricContext context = DoricContextManager.getContext((String) contextId); + if (context != null) { + profile = context.getPerformanceProfile(); + } + } + StringBuilder stringBuilder = new StringBuilder(DoricPerformanceProfile.STEP_Call) + .append(":"); + for (Object object : args) { + if (object == contextId) { + continue; + } + stringBuilder.append(object.toString()).append(","); + } + final String anchorName = stringBuilder.toString(); + final DoricPerformanceProfile finalProfile = profile; + if (finalProfile != null) { + finalProfile.prepare(anchorName); + } return AsyncCall.ensureRunInHandler(mJSHandler, new Callable() { @Override public JSDecoder call() { - return doricJSEngine.invokeDoricMethod(method, args); + if (finalProfile != null) { + finalProfile.start(anchorName); + } + JSDecoder decoder = doricJSEngine.invokeDoricMethod(method, args); + if (finalProfile != null) { + finalProfile.end(anchorName); + } + return decoder; } }); } @@ -113,11 +143,15 @@ public class DoricNativeDriver implements IDoricDriver { @Override public AsyncResult createContext(final String contextId, final String script, final String source) { + final DoricPerformanceProfile performanceProfile = DoricContextManager.getContext(contextId).getPerformanceProfile(); + performanceProfile.prepare(DoricPerformanceProfile.STEP_CREATE); return AsyncCall.ensureRunInHandler(mJSHandler, new Callable() { @Override public Boolean call() { try { + performanceProfile.start(DoricPerformanceProfile.STEP_CREATE); doricJSEngine.prepareContext(contextId, script, source); + performanceProfile.end(DoricPerformanceProfile.STEP_CREATE); return true; } catch (Exception e) { doricJSEngine.getRegistry().onException(DoricContextManager.getContext(contextId), e); @@ -130,11 +164,15 @@ public class DoricNativeDriver implements IDoricDriver { @Override public AsyncResult destroyContext(final String contextId) { + final DoricPerformanceProfile profile = DoricContextManager.getContext(contextId).getPerformanceProfile(); + profile.prepare(DoricPerformanceProfile.STEP_DESTROY); return AsyncCall.ensureRunInHandler(mJSHandler, new Callable() { @Override public Boolean call() { try { + profile.start(DoricPerformanceProfile.STEP_DESTROY); doricJSEngine.destroyContext(contextId); + profile.end(DoricPerformanceProfile.STEP_DESTROY); return true; } catch (Exception e) { doricJSEngine.getRegistry().onException(DoricContextManager.getContext(contextId), e); diff --git a/doric-android/doric/src/main/java/pub/doric/engine/DoricJSEngine.java b/doric-android/doric/src/main/java/pub/doric/engine/DoricJSEngine.java index fc2d9356..8feaaa88 100644 --- a/doric-android/doric/src/main/java/pub/doric/engine/DoricJSEngine.java +++ b/doric-android/doric/src/main/java/pub/doric/engine/DoricJSEngine.java @@ -44,6 +44,7 @@ import pub.doric.DoricRegistry; import pub.doric.IDoricMonitor; import pub.doric.extension.bridge.DoricBridgeExtension; import pub.doric.extension.timer.DoricTimerExtension; +import pub.doric.performance.DoricPerformanceProfile; import pub.doric.utils.DoricConstant; import pub.doric.utils.DoricLog; import pub.doric.utils.DoricUtils; @@ -62,9 +63,11 @@ public class DoricJSEngine implements Handler.Callback, DoricTimerExtension.Time private boolean initialized = false; private final DoricRegistry mDoricRegistry; private final Map mEnvironmentMap = new ConcurrentHashMap<>(); + private final DoricPerformanceProfile globalProfile = new DoricPerformanceProfile("JSEngine"); public DoricJSEngine() { mDoricRegistry = new DoricRegistry(this); + globalProfile.prepare(DoricPerformanceProfile.PART_INIT); handlerThread = new HandlerThread(this.getClass().getSimpleName()); handlerThread.start(); Looper looper = handlerThread.getLooper(); @@ -72,10 +75,12 @@ public class DoricJSEngine implements Handler.Callback, DoricTimerExtension.Time mJSHandler.post(new Runnable() { @Override public void run() { + globalProfile.start(DoricPerformanceProfile.PART_INIT); initJSEngine(); injectGlobal(); initDoricRuntime(); initialized = true; + globalProfile.end(DoricPerformanceProfile.PART_INIT); } }); mTimerExtension = new DoricTimerExtension(looper, this); @@ -285,7 +290,7 @@ public class DoricJSEngine implements Handler.Callback, DoricTimerExtension.Time values.add(DoricUtils.toJavaValue(arg)); } return mDoricJSE.invokeMethod(DoricConstant.GLOBAL_DORIC, method, - values.toArray(new JavaValue[values.size()]), false); + values.toArray(new JavaValue[0]), false); } @Override diff --git a/doric-android/doric/src/main/java/pub/doric/performance/DoricPerformanceProfile.java b/doric-android/doric/src/main/java/pub/doric/performance/DoricPerformanceProfile.java new file mode 100644 index 00000000..2a4150fb --- /dev/null +++ b/doric-android/doric/src/main/java/pub/doric/performance/DoricPerformanceProfile.java @@ -0,0 +1,97 @@ +/* + * Copyright [2019] [Doric.Pub] + * + * Licensed 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 pub.doric.performance; + +import android.util.Log; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @Description: pub.doric.performance + * @Author: pengfei.zhou + * @CreateDate: 3/29/21 + */ +public class DoricPerformanceProfile { + private static final String TAG = DoricPerformanceProfile.class.getSimpleName(); + public static final String PART_INIT = "Init"; + public static final String STEP_CREATE = "Create"; + public static final String STEP_Call = "Call"; + public static final String STEP_DESTROY = "Destroy"; + public static final String STEP_RENDER = "Render"; + private static final String MARK_PREPARE = "prepare"; + private static final String MARK_START = "start"; + private static final String MARK_END = "end"; + private final String name; + + private static final boolean DEBUG = true; + + public DoricPerformanceProfile(String name) { + this.name = name; + } + + private final Map anchorMap = new ConcurrentHashMap<>(); + + private void markAnchor(String eventName) { + if (DEBUG) { + anchorMap.put(eventName, System.currentTimeMillis()); + } + } + + private String getPrepareAnchor(String anchorName) { + return anchorName + "#" + MARK_PREPARE; + } + + private String getStartAnchor(String anchorName) { + return anchorName + "#" + MARK_START; + } + + private String getEndAnchor(String anchorName) { + return anchorName + "#" + MARK_END; + } + + public void prepare(String anchorName) { + markAnchor(getPrepareAnchor(anchorName)); + } + + public void start(String anchorName) { + markAnchor(getStartAnchor(anchorName)); + } + + public void end(String anchorName) { + markAnchor(getEndAnchor(anchorName)); + if (DEBUG) { + print(anchorName); + } + } + + private void print(String anchorName) { + Long prepare = anchorMap.get(getPrepareAnchor(anchorName)); + Long start = anchorMap.get(getStartAnchor(anchorName)); + Long end = anchorMap.get(getEndAnchor(anchorName)); + if (end == null) { + end = System.currentTimeMillis(); + } + if (start == null) { + start = end; + } + if (prepare == null) { + prepare = start; + } + Log.d(TAG, String.format("%s: %s prepared %dms, cost %dms.", + name, anchorName, start - prepare, end - start)); + } +} diff --git a/doric-android/doric/src/main/java/pub/doric/plugin/ShaderPlugin.java b/doric-android/doric/src/main/java/pub/doric/plugin/ShaderPlugin.java index d6df0c99..b5a211a5 100644 --- a/doric-android/doric/src/main/java/pub/doric/plugin/ShaderPlugin.java +++ b/doric-android/doric/src/main/java/pub/doric/plugin/ShaderPlugin.java @@ -34,6 +34,7 @@ import pub.doric.async.AsyncResult; import pub.doric.extension.bridge.DoricMethod; import pub.doric.extension.bridge.DoricPlugin; import pub.doric.extension.bridge.DoricPromise; +import pub.doric.performance.DoricPerformanceProfile; import pub.doric.shader.RootNode; import pub.doric.shader.SuperNode; import pub.doric.shader.ViewNode; @@ -54,9 +55,13 @@ public class ShaderPlugin extends DoricJavaPlugin { @DoricMethod public void render(final JSObject jsObject, final DoricPromise promise) { + final DoricPerformanceProfile profile = getDoricContext().getPerformanceProfile(); + profile.prepare(DoricPerformanceProfile.STEP_RENDER); getDoricContext().getDriver().asyncCall(new Callable() { @Override public Object call() throws Exception { + profile.start(DoricPerformanceProfile.STEP_RENDER); + if (getDoricContext().getContext() instanceof Activity) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && ((Activity) getDoricContext().getContext()).isDestroyed()) { @@ -110,6 +115,7 @@ public class ShaderPlugin extends DoricJavaPlugin { @Override public void onFinish() { + profile.end(DoricPerformanceProfile.STEP_RENDER); } }); }