Android:Add PerformanceProfile

This commit is contained in:
pengfei.zhou 2021-03-29 17:53:11 +08:00 committed by osborn
parent 800365645c
commit 51719d3fc7
5 changed files with 156 additions and 5 deletions

View File

@ -19,8 +19,6 @@ import android.animation.AnimatorSet;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
@ -37,6 +35,7 @@ import java.util.concurrent.Callable;
import pub.doric.async.AsyncResult; import pub.doric.async.AsyncResult;
import pub.doric.navbar.IDoricNavBar; import pub.doric.navbar.IDoricNavBar;
import pub.doric.navigator.IDoricNavigator; import pub.doric.navigator.IDoricNavigator;
import pub.doric.performance.DoricPerformanceProfile;
import pub.doric.plugin.DoricJavaPlugin; import pub.doric.plugin.DoricJavaPlugin;
import pub.doric.shader.RootNode; import pub.doric.shader.RootNode;
import pub.doric.shader.ViewNode; import pub.doric.shader.ViewNode;
@ -60,6 +59,7 @@ public class DoricContext {
private JSONObject initParams; private JSONObject initParams;
private IDoricDriver doricDriver; private IDoricDriver doricDriver;
private final Map<String, Map<String, ViewNode>> mHeadNodes = new HashMap<>(); private final Map<String, Map<String, ViewNode>> mHeadNodes = new HashMap<>();
private final DoricPerformanceProfile performanceProfile;
public Collection<ViewNode> allHeadNodes(String type) { public Collection<ViewNode> allHeadNodes(String type) {
return mHeadNodes.get(type).values(); return mHeadNodes.get(type).values();
@ -107,6 +107,11 @@ public class DoricContext {
this.mContextId = contextId; this.mContextId = contextId;
this.source = source; this.source = source;
this.extra = extra; this.extra = extra;
this.performanceProfile = new DoricPerformanceProfile(contextId);
}
public DoricPerformanceProfile getPerformanceProfile() {
return this.performanceProfile;
} }
public String getSource() { public String getSource() {

View File

@ -21,6 +21,8 @@ import android.util.Log;
import com.github.pengfeizhou.jscore.JSDecoder; import com.github.pengfeizhou.jscore.JSDecoder;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@ -28,6 +30,7 @@ import java.util.concurrent.Executors;
import pub.doric.async.AsyncCall; import pub.doric.async.AsyncCall;
import pub.doric.async.AsyncResult; import pub.doric.async.AsyncResult;
import pub.doric.engine.DoricJSEngine; import pub.doric.engine.DoricJSEngine;
import pub.doric.performance.DoricPerformanceProfile;
import pub.doric.utils.DoricConstant; import pub.doric.utils.DoricConstant;
import pub.doric.utils.ThreadMode; import pub.doric.utils.ThreadMode;
@ -82,7 +85,6 @@ public class DoricNativeDriver implements IDoricDriver {
@Override @Override
public void onFinish() { public void onFinish() {
} }
}); });
return asyncResult; return asyncResult;
@ -90,10 +92,38 @@ public class DoricNativeDriver implements IDoricDriver {
@Override @Override
public AsyncResult<JSDecoder> invokeDoricMethod(final String method, final Object... args) { public AsyncResult<JSDecoder> 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<JSDecoder>() { return AsyncCall.ensureRunInHandler(mJSHandler, new Callable<JSDecoder>() {
@Override @Override
public JSDecoder call() { 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 @Override
public AsyncResult<Boolean> createContext(final String contextId, final String script, final String source) { public AsyncResult<Boolean> 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<Boolean>() { return AsyncCall.ensureRunInHandler(mJSHandler, new Callable<Boolean>() {
@Override @Override
public Boolean call() { public Boolean call() {
try { try {
performanceProfile.start(DoricPerformanceProfile.STEP_CREATE);
doricJSEngine.prepareContext(contextId, script, source); doricJSEngine.prepareContext(contextId, script, source);
performanceProfile.end(DoricPerformanceProfile.STEP_CREATE);
return true; return true;
} catch (Exception e) { } catch (Exception e) {
doricJSEngine.getRegistry().onException(DoricContextManager.getContext(contextId), e); doricJSEngine.getRegistry().onException(DoricContextManager.getContext(contextId), e);
@ -130,11 +164,15 @@ public class DoricNativeDriver implements IDoricDriver {
@Override @Override
public AsyncResult<Boolean> destroyContext(final String contextId) { public AsyncResult<Boolean> destroyContext(final String contextId) {
final DoricPerformanceProfile profile = DoricContextManager.getContext(contextId).getPerformanceProfile();
profile.prepare(DoricPerformanceProfile.STEP_DESTROY);
return AsyncCall.ensureRunInHandler(mJSHandler, new Callable<Boolean>() { return AsyncCall.ensureRunInHandler(mJSHandler, new Callable<Boolean>() {
@Override @Override
public Boolean call() { public Boolean call() {
try { try {
profile.start(DoricPerformanceProfile.STEP_DESTROY);
doricJSEngine.destroyContext(contextId); doricJSEngine.destroyContext(contextId);
profile.end(DoricPerformanceProfile.STEP_DESTROY);
return true; return true;
} catch (Exception e) { } catch (Exception e) {
doricJSEngine.getRegistry().onException(DoricContextManager.getContext(contextId), e); doricJSEngine.getRegistry().onException(DoricContextManager.getContext(contextId), e);

View File

@ -44,6 +44,7 @@ import pub.doric.DoricRegistry;
import pub.doric.IDoricMonitor; import pub.doric.IDoricMonitor;
import pub.doric.extension.bridge.DoricBridgeExtension; import pub.doric.extension.bridge.DoricBridgeExtension;
import pub.doric.extension.timer.DoricTimerExtension; import pub.doric.extension.timer.DoricTimerExtension;
import pub.doric.performance.DoricPerformanceProfile;
import pub.doric.utils.DoricConstant; import pub.doric.utils.DoricConstant;
import pub.doric.utils.DoricLog; import pub.doric.utils.DoricLog;
import pub.doric.utils.DoricUtils; import pub.doric.utils.DoricUtils;
@ -62,9 +63,11 @@ public class DoricJSEngine implements Handler.Callback, DoricTimerExtension.Time
private boolean initialized = false; private boolean initialized = false;
private final DoricRegistry mDoricRegistry; private final DoricRegistry mDoricRegistry;
private final Map<String, Object> mEnvironmentMap = new ConcurrentHashMap<>(); private final Map<String, Object> mEnvironmentMap = new ConcurrentHashMap<>();
private final DoricPerformanceProfile globalProfile = new DoricPerformanceProfile("JSEngine");
public DoricJSEngine() { public DoricJSEngine() {
mDoricRegistry = new DoricRegistry(this); mDoricRegistry = new DoricRegistry(this);
globalProfile.prepare(DoricPerformanceProfile.PART_INIT);
handlerThread = new HandlerThread(this.getClass().getSimpleName()); handlerThread = new HandlerThread(this.getClass().getSimpleName());
handlerThread.start(); handlerThread.start();
Looper looper = handlerThread.getLooper(); Looper looper = handlerThread.getLooper();
@ -72,10 +75,12 @@ public class DoricJSEngine implements Handler.Callback, DoricTimerExtension.Time
mJSHandler.post(new Runnable() { mJSHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
globalProfile.start(DoricPerformanceProfile.PART_INIT);
initJSEngine(); initJSEngine();
injectGlobal(); injectGlobal();
initDoricRuntime(); initDoricRuntime();
initialized = true; initialized = true;
globalProfile.end(DoricPerformanceProfile.PART_INIT);
} }
}); });
mTimerExtension = new DoricTimerExtension(looper, this); mTimerExtension = new DoricTimerExtension(looper, this);
@ -285,7 +290,7 @@ public class DoricJSEngine implements Handler.Callback, DoricTimerExtension.Time
values.add(DoricUtils.toJavaValue(arg)); values.add(DoricUtils.toJavaValue(arg));
} }
return mDoricJSE.invokeMethod(DoricConstant.GLOBAL_DORIC, method, return mDoricJSE.invokeMethod(DoricConstant.GLOBAL_DORIC, method,
values.toArray(new JavaValue[values.size()]), false); values.toArray(new JavaValue[0]), false);
} }
@Override @Override

View File

@ -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<String, Long> 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));
}
}

View File

@ -34,6 +34,7 @@ import pub.doric.async.AsyncResult;
import pub.doric.extension.bridge.DoricMethod; import pub.doric.extension.bridge.DoricMethod;
import pub.doric.extension.bridge.DoricPlugin; import pub.doric.extension.bridge.DoricPlugin;
import pub.doric.extension.bridge.DoricPromise; import pub.doric.extension.bridge.DoricPromise;
import pub.doric.performance.DoricPerformanceProfile;
import pub.doric.shader.RootNode; import pub.doric.shader.RootNode;
import pub.doric.shader.SuperNode; import pub.doric.shader.SuperNode;
import pub.doric.shader.ViewNode; import pub.doric.shader.ViewNode;
@ -54,9 +55,13 @@ public class ShaderPlugin extends DoricJavaPlugin {
@DoricMethod @DoricMethod
public void render(final JSObject jsObject, final DoricPromise promise) { public void render(final JSObject jsObject, final DoricPromise promise) {
final DoricPerformanceProfile profile = getDoricContext().getPerformanceProfile();
profile.prepare(DoricPerformanceProfile.STEP_RENDER);
getDoricContext().getDriver().asyncCall(new Callable<Object>() { getDoricContext().getDriver().asyncCall(new Callable<Object>() {
@Override @Override
public Object call() throws Exception { public Object call() throws Exception {
profile.start(DoricPerformanceProfile.STEP_RENDER);
if (getDoricContext().getContext() instanceof Activity) { if (getDoricContext().getContext() instanceof Activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1
&& ((Activity) getDoricContext().getContext()).isDestroyed()) { && ((Activity) getDoricContext().getContext()).isDestroyed()) {
@ -110,6 +115,7 @@ public class ShaderPlugin extends DoricJavaPlugin {
@Override @Override
public void onFinish() { public void onFinish() {
profile.end(DoricPerformanceProfile.STEP_RENDER);
} }
}); });
} }