feat:android show performance waterfall
This commit is contained in:
parent
a021aae3de
commit
8e2ebaf833
@ -17,6 +17,13 @@ package pub.doric.devkit;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import pub.doric.performance.DoricPerformanceProfile;
|
||||
|
||||
/**
|
||||
@ -25,16 +32,57 @@ import pub.doric.performance.DoricPerformanceProfile;
|
||||
* @CreateDate: 2021/7/20
|
||||
*/
|
||||
public class DoricDevPerformanceAnchorHook implements DoricPerformanceProfile.GlobalAnchorHook {
|
||||
|
||||
public static class AnchorNode {
|
||||
public String name;
|
||||
public long prepare;
|
||||
public long start;
|
||||
public long end;
|
||||
|
||||
AnchorNode(String name, long prepare, long start, long end) {
|
||||
this.name = name;
|
||||
this.prepare = prepare;
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
}
|
||||
}
|
||||
|
||||
private static final String TAG = "DoricPerformance";
|
||||
|
||||
private final Map<String, List<AnchorNode>> nodeMap = new HashMap<>();
|
||||
private final Comparator<AnchorNode> comparator = new Comparator<AnchorNode>() {
|
||||
@Override
|
||||
public int compare(AnchorNode o1, AnchorNode o2) {
|
||||
return (int) (o1.prepare - o2.prepare);
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onAnchor(DoricPerformanceProfile profile, String name, long prepare, long start, long end) {
|
||||
Log.d(TAG, String.format("%s: %s prepared %dms, cost %dms.",
|
||||
profile.getName(), name, start - prepare, end - start));
|
||||
List<AnchorNode> list = nodeMap.get(profile.getName());
|
||||
if (list == null) {
|
||||
list = new ArrayList<>();
|
||||
nodeMap.put(profile.getName(), list);
|
||||
}
|
||||
list.add(new AnchorNode(name, prepare, start, end));
|
||||
Collections.sort(list, comparator);
|
||||
if (name.equals(DoricPerformanceProfile.STEP_DESTROY)) {
|
||||
nodeMap.remove(profile.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnchor(String name, long prepare, long start, long end) {
|
||||
//DO nothing
|
||||
}
|
||||
|
||||
public List<AnchorNode> getAnchorNodeList(String name) {
|
||||
List<AnchorNode> ret = nodeMap.get(name);
|
||||
if (ret == null) {
|
||||
ret = new ArrayList<>();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
@ -29,13 +29,13 @@ import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Locale;
|
||||
|
||||
import pub.doric.devkit.DoricDevPerformanceAnchorHook;
|
||||
import pub.doric.devkit.R;
|
||||
import pub.doric.performance.DoricPerformanceProfile;
|
||||
|
||||
/**
|
||||
* @Description: pub.doric.devkit.ui
|
||||
@ -43,6 +43,9 @@ import pub.doric.devkit.R;
|
||||
* @CreateDate: 2021/7/19
|
||||
*/
|
||||
public class DoricDevPerfActivity extends DoricDevBaseActivity {
|
||||
private MyAdapter myAdapter;
|
||||
private TextView tvBtn;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@ -51,7 +54,33 @@ public class DoricDevPerfActivity extends DoricDevBaseActivity {
|
||||
textView.setText(String.format("Doric %s <%s>", doricContext.getSource(), doricContext.getContextId()));
|
||||
RecyclerView recyclerView = findViewById(R.id.list);
|
||||
recyclerView.setLayoutManager(new LinearLayoutManager(this));
|
||||
recyclerView.setAdapter(new MyAdapter());
|
||||
myAdapter = new MyAdapter();
|
||||
recyclerView.setAdapter(myAdapter);
|
||||
tvBtn = findViewById(R.id.tv_button);
|
||||
tvBtn.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (myAdapter.isAllExpanded()) {
|
||||
for (AnchorItem anchorItem : myAdapter.anchorNodes) {
|
||||
anchorItem.expanded = false;
|
||||
}
|
||||
} else {
|
||||
for (AnchorItem anchorItem : myAdapter.anchorNodes) {
|
||||
anchorItem.expanded = true;
|
||||
}
|
||||
}
|
||||
myAdapter.notifyDataSetChanged();
|
||||
updateButton();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateButton() {
|
||||
if (myAdapter.isAllExpanded()) {
|
||||
tvBtn.setText("Collapse[-]");
|
||||
} else {
|
||||
tvBtn.setText("Expand[+]");
|
||||
}
|
||||
}
|
||||
|
||||
private static class PerfCellHolder extends RecyclerView.ViewHolder {
|
||||
@ -59,43 +88,68 @@ public class DoricDevPerfActivity extends DoricDevBaseActivity {
|
||||
private LinearLayout layoutWaterfall;
|
||||
private View waterfallPrepared;
|
||||
private View waterfallWorked;
|
||||
private View waterfallPrefix;
|
||||
private View waterfallSuffix;
|
||||
private LinearLayout layoutExpand;
|
||||
private TextView tvFuncName;
|
||||
private TextView tvParameter;
|
||||
private TextView tvCost;
|
||||
|
||||
public PerfCellHolder(@NonNull View itemView) {
|
||||
private PerfCellHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
}
|
||||
}
|
||||
|
||||
private static class AnchorNode {
|
||||
String name;
|
||||
long prepare;
|
||||
private static class AnchorItem {
|
||||
private String name;
|
||||
private long position;
|
||||
private long prepared;
|
||||
private long worked;
|
||||
private boolean expanded;
|
||||
}
|
||||
|
||||
private class MyAdapter extends RecyclerView.Adapter<PerfCellHolder> {
|
||||
private List<AnchorNode> anchorNodes = new ArrayList<>();
|
||||
private final List<AnchorItem> anchorNodes = new LinkedList<>();
|
||||
private final long duration;
|
||||
|
||||
private MyAdapter() {
|
||||
// Map<String, Long> anchorMap = doricContext.getPerformanceProfile().getAnchorMap();
|
||||
// for (String key : anchorMap.keySet()) {
|
||||
// if (key.endsWith("#prepare")) {
|
||||
// Long prepare = anchorMap.get(key);
|
||||
// if (prepare != null) {
|
||||
// AnchorNode anchorNode = new AnchorNode();
|
||||
// anchorNode.name = key.substring(0, key.lastIndexOf("#prepare"));
|
||||
// anchorNode.prepare = prepare;
|
||||
// anchorNodes.add(anchorNode);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// Collections.sort(anchorNodes, new Comparator<AnchorNode>() {
|
||||
// @Override
|
||||
// public int compare(AnchorNode o1, AnchorNode o2) {
|
||||
// return (int) (o1.prepare - o2.prepare);
|
||||
// }
|
||||
// });
|
||||
DoricPerformanceProfile.GlobalAnchorHook anchorHook
|
||||
= doricContext.getDriver().getRegistry().getGlobalPerformanceAnchorHook();
|
||||
long position = 0;
|
||||
if (anchorHook instanceof DoricDevPerformanceAnchorHook) {
|
||||
DoricDevPerformanceAnchorHook.AnchorNode prevNode = null;
|
||||
for (DoricDevPerformanceAnchorHook.AnchorNode anchorNode :
|
||||
((DoricDevPerformanceAnchorHook) anchorHook)
|
||||
.getAnchorNodeList(doricContext.getContextId())) {
|
||||
AnchorItem anchorItem = new AnchorItem();
|
||||
anchorItem.name = anchorNode.name;
|
||||
long gap = 0;
|
||||
if (prevNode != null) {
|
||||
gap = Math.min(16, anchorNode.prepare - prevNode.end);
|
||||
}
|
||||
position = position + gap;
|
||||
anchorItem.position = position;
|
||||
anchorItem.prepared = anchorNode.start - anchorNode.prepare;
|
||||
anchorItem.worked = anchorNode.end - anchorNode.start;
|
||||
anchorNodes.add(anchorItem);
|
||||
position = position + anchorItem.prepared + anchorItem.worked;
|
||||
prevNode = anchorNode;
|
||||
}
|
||||
duration = position;
|
||||
} else {
|
||||
duration = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAllExpanded() {
|
||||
boolean allExpanded = true;
|
||||
for (AnchorItem anchorItem : myAdapter.anchorNodes) {
|
||||
if (!anchorItem.expanded) {
|
||||
allExpanded = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return allExpanded;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -106,6 +160,8 @@ public class DoricDevPerfActivity extends DoricDevBaseActivity {
|
||||
cellHolder.layoutWaterfall = cell.findViewById(R.id.layout_waterfall);
|
||||
cellHolder.waterfallPrepared = cell.findViewById(R.id.waterfall_prepared);
|
||||
cellHolder.waterfallWorked = cell.findViewById(R.id.waterfall_worked);
|
||||
cellHolder.waterfallPrefix = cell.findViewById(R.id.waterfall_prefix);
|
||||
cellHolder.waterfallSuffix = cell.findViewById(R.id.waterfall_suffix);
|
||||
cellHolder.layoutExpand = cell.findViewById(R.id.layout_expand);
|
||||
cellHolder.tvFuncName = cell.findViewById(R.id.tv_func_name);
|
||||
cellHolder.tvParameter = cell.findViewById(R.id.tv_parameter);
|
||||
@ -116,13 +172,56 @@ public class DoricDevPerfActivity extends DoricDevBaseActivity {
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull PerfCellHolder holder, int position) {
|
||||
holder.itemView.setBackgroundColor(position % 2 == 0 ? Color.parseColor("#ecf0f1") : Color.WHITE);
|
||||
holder.layoutExpand.setVisibility(View.GONE);
|
||||
AnchorNode anchorNode = anchorNodes.get(position);
|
||||
if (anchorNode.name.startsWith("Call")) {
|
||||
final AnchorItem anchorItem = anchorNodes.get(position);
|
||||
holder.layoutExpand.setVisibility(anchorItem.expanded ? View.VISIBLE : View.GONE);
|
||||
if (anchorItem.name.startsWith("Call")) {
|
||||
holder.tvName.setBackgroundColor(0xff3498db);
|
||||
holder.tvName.setText("Call");
|
||||
String extraInfo = anchorItem.name.substring("Call:".length());
|
||||
String[] info = extraInfo.split(",");
|
||||
if (info.length > 1) {
|
||||
holder.tvFuncName.setVisibility(View.VISIBLE);
|
||||
holder.tvFuncName.setText(info[0]);
|
||||
holder.tvParameter.setVisibility(View.VISIBLE);
|
||||
holder.tvParameter.setText(extraInfo.substring(extraInfo.indexOf(",") + 1));
|
||||
} else if (info.length > 0) {
|
||||
holder.tvFuncName.setVisibility(View.VISIBLE);
|
||||
holder.tvFuncName.setText(info[0]);
|
||||
holder.tvParameter.setVisibility(View.GONE);
|
||||
} else {
|
||||
holder.tvFuncName.setVisibility(View.GONE);
|
||||
holder.tvParameter.setVisibility(View.GONE);
|
||||
}
|
||||
} else {
|
||||
holder.tvName.setText(anchorNode.name);
|
||||
if (anchorItem.name.equals("Render")) {
|
||||
holder.tvName.setBackgroundColor(0xffe74c3c);
|
||||
} else {
|
||||
holder.tvName.setBackgroundColor(0xff2ecc71);
|
||||
}
|
||||
holder.tvName.setText(anchorItem.name);
|
||||
holder.tvFuncName.setVisibility(View.GONE);
|
||||
holder.tvParameter.setVisibility(View.GONE);
|
||||
}
|
||||
((LinearLayout.LayoutParams) (holder.waterfallPrefix.getLayoutParams())).weight
|
||||
= anchorItem.position;
|
||||
((LinearLayout.LayoutParams) (holder.waterfallPrepared.getLayoutParams())).weight
|
||||
= anchorItem.prepared;
|
||||
((LinearLayout.LayoutParams) (holder.waterfallWorked.getLayoutParams())).weight
|
||||
= Math.max(anchorItem.worked, 1);
|
||||
((LinearLayout.LayoutParams) (holder.waterfallSuffix.getLayoutParams())).weight
|
||||
= duration - anchorItem.position - anchorItem.prepared - anchorItem.worked;
|
||||
holder.layoutWaterfall.requestLayout();
|
||||
holder.tvCost.setText(String.format(Locale.getDefault(),
|
||||
"%d ms",
|
||||
anchorItem.prepared + anchorItem.worked));
|
||||
holder.itemView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
anchorItem.expanded = !anchorItem.expanded;
|
||||
notifyDataSetChanged();
|
||||
updateButton();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -16,9 +16,20 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="15dp"
|
||||
android:layout_weight="1"
|
||||
android:text="Context"
|
||||
android:textColor="#fff"
|
||||
android:textSize="16dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginRight="15dp"
|
||||
android:gravity="center_vertical"
|
||||
android:text="Expand[+]"
|
||||
android:textColor="#fff"
|
||||
android:textSize="16dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
|
@ -5,7 +5,8 @@
|
||||
android:background="#ecf0f1"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="10dp">
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingRight="10dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
@ -31,16 +32,32 @@
|
||||
android:layout_weight="1">
|
||||
|
||||
<View
|
||||
android:id="@+id/waterfall_prepared"
|
||||
android:layout_width="30dp"
|
||||
android:id="@+id/waterfall_prefix"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="20dp"
|
||||
|
||||
android:layout_weight="1" />
|
||||
|
||||
<View
|
||||
android:id="@+id/waterfall_prepared"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="20dp"
|
||||
|
||||
android:layout_weight="1"
|
||||
android:background="#1abc9c" />
|
||||
|
||||
<View
|
||||
android:id="@+id/waterfall_worked"
|
||||
android:layout_width="60dp"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_weight="1"
|
||||
android:background="#f1c40f" />
|
||||
|
||||
<View
|
||||
android:id="@+id/waterfall_suffix"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_weight="1" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
|
@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright [2021] [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.
|
||||
*/
|
||||
//
|
||||
// Created by pengfei.zhou on 2021/7/20.
|
||||
//
|
||||
|
@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright [2021] [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.
|
||||
*/
|
||||
//
|
||||
// Created by pengfei.zhou on 2021/7/20.
|
||||
//
|
||||
@ -14,4 +29,9 @@ - (void)onAnchorName:(NSString *)name prepare:(NSNumber *)prepare start:(NSNumbe
|
||||
@(end.integerValue - start.integerValue)
|
||||
);
|
||||
}
|
||||
|
||||
- (void)onAnchorName:(NSString *)name prepare:(NSNumber *)prepare start:(NSNumber *)start end:(NSNumber *)end {
|
||||
//Do nothing
|
||||
}
|
||||
|
||||
@end
|
Reference in New Issue
Block a user