参考 View、Window、WindowManager—vsync信号 View、Window、WindowManager—Choreographer源码阅读
Matrix提供了fps检测的功能, 该功能由 FrameTracer 完成, 这里围绕FrameTracer分析
public void doFrameAsync(String visibleScene
,
long taskCost
,
long frameCostMs
,
int droppedFrames
,
boolean isContainsFrame
);
涉及到的主要method:
1. new Matrix: Matric初始化
-> 1 Plugin
.init
: 插件初始化
2. Plugin
.init
: 插件初始化
-> 1. new AnrTracer
-> 2. new FrameTracer
-> 3. new EvilMethodTracer
-> 4. new StartupTracer
3. TracePlugin
.start
: 启动TracePlugin插件
-> 1. UIThreadMonitor
.getMonitor().init
: UIThreadMonitor初始化
-> 2. UIThreadMonitor
.getMonitor().onStart()
-> 3. frameTracer
.onStartTrace()
4. UIThreadMonitor
.init
: UIThreadMonitor初始化
-> 1. LooperMonitor初始化
-> 2. 获取Choreographer实例
-> 3. 反射获取Choreographer
.mLock
-> 4. 返回获取Choreographer
.mCallbackQueues
-> 5. 反射获取addInputQueue
= Choreographer
.callbackQueues
[0]
-> 6. 反射获取addAnimationQueue
= Choreographer
.callbackQueues
[1]
-> 7. 反射获取addTraversalQueue
= Choreographer
.callbackQueues
[2]
-> 8. 返回获取Choreographer
.mFrameIntervalNanos
-> 9. LooperMonitor
.register注册Message事件分发的监听
5. LooperMonitor初始化
-> 1. LooperMonitor
.resetPrinter
: hook Looper中的Printer
-> 2. LooperMonitor
.dispatch
: Message事件分发
-> 3. UIThreadMonitor
.dispatchBegin
: Message事件开始分发
-> 4. UIThreadMonitor
.dispatchEnd
: Message事件结束分发
6. UIThreadMonitor
.onStart
-> 1. callbackExist
= new boolean[CALLBACK_LAST
+ 1]
-> 2. queueStatus
= new int[CALLBACK_LAST
+ 1]
-> 3. queueCost
= new long[CALLBACK_LAST
+ 1]
-> 4. UIThreadMonitor
.addFrameCallback
-> 5. UIThreadMonitor
.run
-> 6. UIThreadMonitor
.doFrameBegin
-> 7. UIThreadMonitor
.doQueueBegin
-> 8. UIThreadMonitor
.doQueueEnd
7. frameTracer
.onStartTrace
-> 1. onAlive
-> UIThreadMonitor
.getMonitor().addObserver(this);
8. 帧刷新回调
, Message事件回调
-> 1. FrameTracer
.dispatchBegin
-> 2. FrameTracer
.doFrame
-> 3. FrameTracer
.dispatchEnd
9. FrameTracer
.doFrame
-> 1. FrameTracer
.notifyListener
-> 2. FPSCollector
.doFrameAsync
接下来围绕这些方法进行分析
文章目录
一、Matrix初始化二、TracePlugin初始化2.1 TracePlugin.init
三、FrameTracer初始化3.1 new FrameTrace3.2 TracePlugin.start3.3 UIThreadMonitor.init初始化3.4 LooperMonitor初始化3.5 LooperPrinter监听Message事件的开始与结束
四、UIThreadMonitor 启动4.1 UIThreadMonitor.onStart4.2 UIThreadMonitor.addFrameCallback4.3 事件分发与UIThreadMonitor.run的流程
五、帧率FPS检测5.1 LooperMonitor.dispatch事件分发5.2 关于frameIntervalNanos与事件分发5.3 UIThreadMonitor.dispatchBegin Message事件分发开始5.4 FrameTracer.dispatchBegin5.5 UIThreadMonitor.run5.5.1 UIThreadMonitor.doFrameBegin5.5.2 UIThreadMonitor.doQueueBegin5.5.3 UIThreadMonitor.doQueueEnd
5.6 UIThreadMonitor.dispatchEnd Message事件分发结束
六、Frame帧刷新事件处理6.1 FrameTracer.doFrame6.2 FrameTracer.notifyListener6.3 FPSCollector.doFrameAsync6.4 FrameCollectItem.collect6.5 FrameCollectItem.report 上报数据
一、Matrix初始化
private Matrix(Application app
, PluginListener listener
, HashSet
<Plugin> plugins
) {
this.application
= app
;
this.pluginListener
= listener
;
this.plugins
= plugins
;
AppActiveMatrixDelegate
.INSTANCE
.init(application
);
for (Plugin plugin
: plugins
) {
plugin
.init(application
, pluginListener
);
pluginListener
.onInit(plugin
);
}
}
1.Matrix构造函数会对所有插件Plugin进行初始化, 这些插件就是MAtrix-demo在Application.onCreate中添加的Plugin2.这里主要分析FrameTracer, 而FrameTracer又与TracePlugin相关, 所以这里只分析TracePlugin.init方法
二、TracePlugin初始化
2.1 TracePlugin.init
@Override
public void init(Application app
, PluginListener listener
) {
super.init(app
, listener
);
anrTracer
= new AnrTracer(traceConfig
);
frameTracer
= new FrameTracer(traceConfig
);
evilMethodTracer
= new EvilMethodTracer(traceConfig
);
startupTracer
= new StartupTracer(traceConfig
);
}
这里主要做了以下四件事:1、AnrTracer初始化: 监控ANR2、FrameTracer初始化: 监控帧率3、EvilMethodTracer初始化: 方法打点, 记录方法时长4、StartupTracer初始化
三、FrameTracer初始化
3.1 new FrameTrace
public FrameTracer(TraceConfig config
) {
this.config
= config
;
this.frameIntervalMs
= TimeUnit
.MILLISECONDS
.convert(UIThreadMonitor
.getMonitor().getFrameIntervalNanos(), TimeUnit
.NANOSECONDS
) + 1;
this.timeSliceMs
= config
.getTimeSliceMs();
this.isFPSEnable
= config
.isFPSEnable();
this.frozenThreshold
= config
.getFrozenThreshold();
this.highThreshold
= config
.getHighThreshold();
this.normalThreshold
= config
.getNormalThreshold();
this.middleThreshold
= config
.getMiddleThreshold();
MatrixLog
.i(TAG
, "[init] frameIntervalMs:%s isFPSEnable:%s", frameIntervalMs
, isFPSEnable
);
if (isFPSEnable
) {
addListener(new FPSCollector());
}
}
根据Matrix-demo提供的流程, 在Application.onCreate中对TracePlugin进行初始化(TracePlugin初始化时会初始化FrameTracer), 之后通过TracePlugin.start开始帧率的检测
3.2 TracePlugin.start
@Override
public void start() {
super.start();
Runnable runnable
= new Runnable() {
@Override
public void run() {
if (!UIThreadMonitor
.getMonitor().isInit()) {
UIThreadMonitor
.getMonitor().init(traceConfig
);
}
AppMethodBeat
.getInstance().onStart();
UIThreadMonitor
.getMonitor().onStart();
anrTracer
.onStartTrace();
frameTracer
.onStartTrace();
evilMethodTracer
.onStartTrace();
startupTracer
.onStartTrace();
}
};
if (Thread
.currentThread() == Looper
.getMainLooper().getThread()) {
runnable
.run();
} else {
MatrixHandlerThread
.getDefaultMainHandler().post(runnable
);
}
}
3.3 UIThreadMonitor.init初始化
public void init(TraceConfig config
) {
if (Thread
.currentThread() != Looper
.getMainLooper().getThread()) {
throw new AssertionError("must be init in main thread!");
}
this.config
= config
;
choreographer
= Choreographer
.getInstance();
callbackQueueLock
= reflectObject(choreographer
, "mLock");
callbackQueues
= reflectObject(choreographer
, "mCallbackQueues");
addInputQueue
= reflectChoreographerMethod(callbackQueues
[CALLBACK_INPUT
], ADD_CALLBACK
, long.class, Object
.class, Object
.class);
addAnimationQueue
= reflectChoreographerMethod(callbackQueues
[CALLBACK_ANIMATION
], ADD_CALLBACK
, long.class, Object
.class, Object
.class);
addTraversalQueue
= reflectChoreographerMethod(callbackQueues
[CALLBACK_TRAVERSAL
], ADD_CALLBACK
, long.class, Object
.class, Object
.class);
frameIntervalNanos
= reflectObject(choreographer
, "mFrameIntervalNanos");
LooperMonitor
.register(new LooperMonitor.LooperDispatchListener() {
@Override
public boolean isValid() {
return isAlive
;
}
@Override
public void dispatchStart() {
super.dispatchStart();
UIThreadMonitor
.this.dispatchBegin();
}
@Override
public void dispatchEnd() {
super.dispatchEnd();
UIThreadMonitor
.this.dispatchEnd();
}
});
this.isInit
= true;
}
1、在 Choreographer源码分析 时, mFrameIntervalNanos为vsync信号回调时的时间戳
3.4 LooperMonitor初始化
public LooperMonitor(Looper looper
) {
this.looper
= looper
;
resetPrinter();
addIdleHandler(looper
);
}
3.5 LooperPrinter监听Message事件的开始与结束
public void println(String x
) {
if (null
!= origin
) {
origin
.println(x
);
}
if (!isHasChecked
) {
isValid
= x
.charAt(0) == '>' || x
.charAt(0) == '<';
isHasChecked
= true;
if (!isValid
) {
MatrixLog
.e(TAG
, "[println] Printer is inValid! x:%s", x
);
}
}
if (isValid
) {
dispatch(x
.charAt(0) == '>', x
);
}
}
public class Looper {
public static void loop() {
if (logging
!= null
) {
logging
.println(">>>>> Dispatching to " + msg
.target
+ " " +
msg
.callback
+ ": " + msg
.what
);
}
...
if (logging
!= null
) {
logging
.println("<<<<< Finished to " + msg
.target
+ " " + msg
.callback
);
}
}
}
当Message事件开始和结束时会分别回调Printer.println方法, 并带上开始/结束的标识, 这里最终会调用到LooperMonitor.dispatch方法
四、UIThreadMonitor 启动
4.1 UIThreadMonitor.onStart
public synchronized void onStart() {
if (!isAlive
) {
this.isAlive
= true;
synchronized (this) {
callbackExist
= new boolean[CALLBACK_LAST
+ 1];
}
queueStatus
= new int[CALLBACK_LAST
+ 1];
queueCost
= new long[CALLBACK_LAST
+ 1];
addFrameCallback(CALLBACK_INPUT
, this, true);
}
}
4.2 UIThreadMonitor.addFrameCallback
private synchronized void addFrameCallback(int type
, Runnable callback
, boolean isAddHeader
) {
if (callbackExist
[type
]) {
MatrixLog
.w(TAG
, "[addFrameCallback] this type %s callback has exist! isAddHeader:%s", type
, isAddHeader
);
return;
}
if (!isAlive
&& type
== CALLBACK_INPUT
) {
MatrixLog
.w(TAG
, "[addFrameCallback] UIThreadMonitor is not alive!");
return;
}
synchronized (callbackQueueLock
) {
Method method
= null
;
switch (type
) {
case CALLBACK_INPUT
:
method
= addInputQueue
;
break;
case CALLBACK_ANIMATION
:
method
= addAnimationQueue
;
break;
case CALLBACK_TRAVERSAL
:
method
= addTraversalQueue
;
break;
}
if (null
!= method
) {
method
.invoke(callbackQueues
[type
], !isAddHeader
? SystemClock
.uptimeMillis() : -1, callback
, null
);
callbackExist
[type
] = true;
}
}
}
1、将UIThreadMonitor添加到Choreographer.mCallbackQueues对应的CallbackQueue中2、底层回调vsync信号会触发FrameDisplayEventReceiver.onVsync的执行, 最终调用CallbackRecord.run方法的执行, 将UIThreadMonitor添加到CallbackRecord, 绘制之前触发UIThreadMonitor.run的执行
4.3 事件分发与UIThreadMonitor.run的流程
UIThreadMonitor
.onStart()
-> CallbackRecord
.enqueue(UIThreadMonitor
)
FrameDisplayEventReceiver
.onVsync() -> FrameHandler
.sendMessageAtTime()
-> Printer
.println(">>>>> Dispatching to...")
-> FrameDisplayEventReceiver
.run()
-> Choreographer
.doFrame()
-> CallbackRecord
.doCallbacks()
-> CallbackRecord
.run()
-> UIThreadMonitor
.run()
-> mTraversalRunnable
.run()
-> ViewRootImpl
.performTraversal()
-> Printer
.println("<<<<< Finished to...")
五、帧率FPS检测
5.1 LooperMonitor.dispatch事件分发
private void dispatch(boolean isBegin
, String log
) {
for (LooperDispatchListener listener
: listeners
) {
if (listener
.isValid()) {
if (isBegin
) {
if (!listener
.isHasDispatchStart
) {
listener
.onDispatchStart(log
);
}
} else {
if (listener
.isHasDispatchStart
) {
listener
.onDispatchEnd(log
);
}
}
} else if (!isBegin
&& listener
.isHasDispatchStart
) {
listener
.dispatchEnd();
}
}
}
1、Message事件分发开始时回调listener.onDispatchStart方法2、Message事件分发结束时回调listener.onDispatchEnd方法3、LooperDispatchListener只考虑UIThreadMonitor.init中通过LooperMoniter.register注册的回调4、所以listener.onDispatchStart、listener.onDispatchEnd会分别回调UIThreadMonitor.dispatchBegin与UIThreadMonitor.dispatchEnd方法.
5.2 关于frameIntervalNanos与事件分发
FrameTracer为何通过这种方式进行hook检测帧率(FPS)
public void onVsync(long timestampNanos
, long physicalDisplayId
, int frame
) {
long now
= System
.nanoTime();
if (timestampNanos
> now
) {
timestampNanos
= now
;
}
mTimestampNanos
= timestampNanos
;
mFrame
= frame
;
Message msg
= Message
.obtain(mHandler
, this);
msg
.setAsynchronous(true);
mHandler
.sendMessageAtTime(msg
, timestampNanos
/ TimeUtils
.NANOS_PER_MS
);
}
1、在进行UI绘制之前, 先向natice层发送vsync信号, 然后native层收到该信号之后会回调vsync信号给应用层, 应用层收到该信号之后, 才开始进行measure、layout、draw绘制操作, 上层回调的入口是 FrameDisplayEventReceiver.onVsync, 在onVsync通过Handler.sendMessageAtTime向Looper发送事件, 然后调用FrameDisplayEventReceiver.run方法2、FrameTracer基于这一点对Printer进行hook, 完成对FPS的检测
5.3 UIThreadMonitor.dispatchBegin Message事件分发开始
private void dispatchBegin() {
token
= dispatchTimeMs
[0] = SystemClock
.uptimeMillis();
dispatchTimeMs
[2] = SystemClock
.currentThreadTimeMillis();
synchronized (observers
) {
for (LooperObserver observer
: observers
) {
if (!observer
.isDispatchBegin()) {
observer
.dispatchBegin(dispatchTimeMs
[0], dispatchTimeMs
[2], token
);
}
}
}
}
1.Message执行之前, 记录了从开机到现在的毫秒数, 当前线程运行的毫秒数, 并将该参数通过observer.dispatchBegin回调时传入2.通过UIThreadMonitor.addObserver添加LooperObserver, 方法调用链可知FrameTracer.onStart时会向UITHreadMonitor注册一个LooperObserver.
5.4 FrameTracer.dispatchBegin
public void dispatchBegin(long beginMs
, long cpuBeginMs
, long token
) {
isDispatchBegin
= true;
}
结合 4.3事件分发与hook的流程 事件分发开始时将标识位置为true, 并且输出日志, 当View绘制结束之后, 输出日志, 并调用事件分发结束的接口dispatchEnd
5.5 UIThreadMonitor.run
结合流程 4.3 如果当前事件(Message)对应的是vsync信号的事件, 既从FrameDisplayEventReceiver.onVsync()发出来的消息, 则在UIThreadMonitor.dispatchEnd调用之前会先执行UIThreadMonitor.run
public void run() {
final long start
= System
.nanoTime();
doFrameBegin(token
);
doQueueBegin(CALLBACK_INPUT
);
addFrameCallback(CALLBACK_ANIMATION
, new Runnable() {
@Override
public void run() {
doQueueEnd(CALLBACK_INPUT
);
doQueueBegin(CALLBACK_ANIMATION
);
}
}, true);
addFrameCallback(CALLBACK_TRAVERSAL
, new Runnable() {
@Override
public void run() {
doQueueEnd(CALLBACK_ANIMATION
);
doQueueBegin(CALLBACK_TRAVERSAL
);
}
}, true);
}
5.5.1 UIThreadMonitor.doFrameBegin
private void doFrameBegin(long token
) {
this.isBelongFrame
= true;
}
5.5.2 UIThreadMonitor.doQueueBegin
private void doQueueBegin(int type
) {
queueStatus
[type
] = DO_QUEUE_BEGIN
;
queueCost
[type
] = System
.nanoTime();
}
5.5.3 UIThreadMonitor.doQueueEnd
private void doQueueEnd(int type
) {
queueStatus
[type
] = DO_QUEUE_END
;
queueCost
[type
] = System
.nanoTime() - queueCost
[type
];
synchronized (this) {
callbackExist
[type
] = false;
}
}
注: 当前执行到这里, 只记录了input、animation事件的结束状态, 并没有记录traversal结束的状态, traversal结束的状态在dispatchEnd的doFrameEnd中执行
5.6 UIThreadMonitor.dispatchEnd Message事件分发结束
当前Message事件执行完成, 回调dispatchEnd方法
private void dispatchEnd() {
if (isBelongFrame
) {
doFrameEnd(token
);
}
long start
= token
;
long end
= SystemClock
.uptimeMillis();
synchronized (observers
) {
for (LooperObserver observer
: observers
) {
if (observer
.isDispatchBegin()) {
observer
.doFrame(AppMethodBeat
.getVisibleScene(), token
, SystemClock
.uptimeMillis(), isBelongFrame
? end
- start
: 0, queueCost
[CALLBACK_INPUT
], queueCost
[CALLBACK_ANIMATION
], queueCost
[CALLBACK_TRAVERSAL
]);
}
}
}
dispatchTimeMs
[3] = SystemClock
.currentThreadTimeMillis();
dispatchTimeMs
[1] = SystemClock
.uptimeMillis();
synchronized (observers
) {
for (LooperObserver observer
: observers
) {
if (observer
.isDispatchBegin()) {
observer
.dispatchEnd(dispatchTimeMs
[0], dispatchTimeMs
[2], dispatchTimeMs
[1], dispatchTimeMs
[3], token
, isBelongFrame
);
}
}
}
}
dispatchTimeMs[0]: 从开机到Message执行开始的时间 dispatchTimeMs[1]: 从开机到Message执行完毕的时间 dispatchTimeMs[2]: Message执行开始时当前线程消耗的时间 dispatchTimeMs[3]: Message执行结束时当前线程消耗的时间
有一个地方没看明白, 如果isBelongFrame = true, 则会进入到doFrameEnd中将isBelongFrame置为false, 如果为false, 则跳过doFrameEnd, 因此observer.doFrame(isBelongFrame? end - start : 0)这个参数一定只能是0, 那这样的意义何在呢? 这个问题留待手撸Matrix-FrameTracer代码之后再确定是否自己理解有误
六、Frame帧刷新事件处理
6.1 FrameTracer.doFrame
public void doFrame(String focusedActivityName
, long start
, long end
, long frameCostMs
, long inputCostNs
, long animationCostNs
, long traversalCostNs
) {
if (isForeground()) {
notifyListener(focusedActivityName
, end
- start
, frameCostMs
, frameCostMs
>= 0);
}
}
6.2 FrameTracer.notifyListener
private void notifyListener(final String visibleScene
, final long taskCostMs
, final long frameCostMs
, final boolean isContainsFrame
) {
long start
= System
.currentTimeMillis();
synchronized (listeners
) {
for (final IDoFrameListener listener
: listeners
) {
if (config
.isDevEnv()) {
listener
.time
= SystemClock
.uptimeMillis();
}
final int dropFrame
= (int) (taskCostMs
/ frameIntervalMs
);
listener
.doFrameSync(visibleScene
, taskCostMs
, frameCostMs
, dropFrame
, isContainsFrame
);
if (null
!= listener
.getExecutor()) {
listener
.getExecutor().execute(new Runnable() {
@Override
public void run() {
listener
.doFrameAsync(visibleScene
, taskCostMs
, frameCostMs
, dropFrame
, isContainsFrame
);
}
});
}
}
}
}
dropFrame: 理论上一次Message需要的时间与一帧的时间相同为16ms, 因此dropFrame理论上取值应该为1, 因此dropFrame的值越大, 表明Message花费的时间越多, 掉帧也就越多.
6.3 FPSCollector.doFrameAsync
public void doFrameAsync(String visibleScene
, long taskCost
, long frameCostMs
, int droppedFrames
, boolean isContainsFrame
) {
super.doFrameAsync(visibleScene
, taskCost
, frameCostMs
, droppedFrames
, isContainsFrame
);
FrameCollectItem item
= map
.get(visibleScene
);
if (null
== item
) {
item
= new FrameCollectItem(visibleScene
);
map
.put(visibleScene
, item
);
}
item
.collect(droppedFrames
, isContainsFrame
);
if (item
.sumFrameCost
>= timeSliceMs
) {
map
.remove(visibleScene
);
item
.report();
}
}
6.4 FrameCollectItem.collect
void collect(int droppedFrames
, boolean isContainsFrame
) {
long frameIntervalCost
= UIThreadMonitor
.getMonitor().getFrameIntervalNanos();
sumFrameCost
+= (droppedFrames
+ 1) * frameIntervalCost
/ Constants
.TIME_MILLIS_TO_NANO
;
sumDroppedFrames
+= droppedFrames
;
sumFrame
++;
if (!isContainsFrame
) {
sumTaskFrame
++;
}
if (droppedFrames
>= frozenThreshold
) {
dropLevel
[DropStatus
.DROPPED_FROZEN
.index
]++;
dropSum
[DropStatus
.DROPPED_FROZEN
.index
] += droppedFrames
;
} else if (droppedFrames
>= highThreshold
) {
dropLevel
[DropStatus
.DROPPED_HIGH
.index
]++;
dropSum
[DropStatus
.DROPPED_HIGH
.index
] += droppedFrames
;
} else if (droppedFrames
>= middleThreshold
) {
dropLevel
[DropStatus
.DROPPED_MIDDLE
.index
]++;
dropSum
[DropStatus
.DROPPED_MIDDLE
.index
] += droppedFrames
;
} else if (droppedFrames
>= normalThreshold
) {
dropLevel
[DropStatus
.DROPPED_NORMAL
.index
]++;
dropSum
[DropStatus
.DROPPED_NORMAL
.index
] += droppedFrames
;
} else {
dropLevel
[DropStatus
.DROPPED_BEST
.index
]++;
dropSum
[DropStatus
.DROPPED_BEST
.index
] += (droppedFrames
< 0 ? 0 : droppedFrames
);
}
}
6.5 FrameCollectItem.report 上报数据
每个页面监控的总时间超过预设阈值就进行上报
void report() {
float fps
= Math
.min(60.f, 1000.f * sumFrame
/ sumFrameCost
);
MatrixLog
.i(TAG
, "[report] FPS:%s %s", fps
, toString());
try {
TracePlugin plugin
= Matrix
.with().getPluginByClass(TracePlugin
.class);
if (null
== plugin
) {
return;
}
JSONObject dropLevelObject
= new JSONObject();
dropLevelObject
.put(DropStatus
.DROPPED_FROZEN
.name(), dropLevel
[DropStatus
.DROPPED_FROZEN
.index
]);
dropLevelObject
.put(DropStatus
.DROPPED_HIGH
.name(), dropLevel
[DropStatus
.DROPPED_HIGH
.index
]);
dropLevelObject
.put(DropStatus
.DROPPED_MIDDLE
.name(), dropLevel
[DropStatus
.DROPPED_MIDDLE
.index
]);
dropLevelObject
.put(DropStatus
.DROPPED_NORMAL
.name(), dropLevel
[DropStatus
.DROPPED_NORMAL
.index
]);
dropLevelObject
.put(DropStatus
.DROPPED_BEST
.name(), dropLevel
[DropStatus
.DROPPED_BEST
.index
]);
JSONObject dropSumObject
= new JSONObject();
dropSumObject
.put(DropStatus
.DROPPED_FROZEN
.name(), dropSum
[DropStatus
.DROPPED_FROZEN
.index
]);
dropSumObject
.put(DropStatus
.DROPPED_HIGH
.name(), dropSum
[DropStatus
.DROPPED_HIGH
.index
]);
dropSumObject
.put(DropStatus
.DROPPED_MIDDLE
.name(), dropSum
[DropStatus
.DROPPED_MIDDLE
.index
]);
dropSumObject
.put(DropStatus
.DROPPED_NORMAL
.name(), dropSum
[DropStatus
.DROPPED_NORMAL
.index
]);
dropSumObject
.put(DropStatus
.DROPPED_BEST
.name(), dropSum
[DropStatus
.DROPPED_BEST
.index
]);
JSONObject resultObject
= new JSONObject();
resultObject
= DeviceUtil
.getDeviceInfo(resultObject
, plugin
.getApplication());
resultObject
.put(SharePluginInfo
.ISSUE_SCENE
, visibleScene
);
resultObject
.put(SharePluginInfo
.ISSUE_DROP_LEVEL
, dropLevelObject
);
resultObject
.put(SharePluginInfo
.ISSUE_DROP_SUM
, dropSumObject
);
resultObject
.put(SharePluginInfo
.ISSUE_FPS
, fps
);
resultObject
.put(SharePluginInfo
.ISSUE_SUM_TASK_FRAME
, sumTaskFrame
);
Issue issue
= new Issue();
issue
.setTag(SharePluginInfo
.TAG_PLUGIN_FPS
);
issue
.setContent(resultObject
);
plugin
.onDetectIssue(issue
);
} catch (JSONException e
) {
MatrixLog
.e(TAG
, "json error", e
);
} finally {
sumFrame
= 0;
sumDroppedFrames
= 0;
sumFrameCost
= 0;
sumTaskFrame
= 0;
}
}