[Feat]新增性能监控工具

This commit is contained in:
chenfufeng
2022-08-25 17:04:40 +08:00
parent 6d6b14f36e
commit 437535837e
25 changed files with 1678 additions and 10 deletions

View File

@@ -82,7 +82,6 @@ dependencies {
implementation rootProject.ext.dependencies.androidxconstraintlayout
implementation rootProject.ext.dependencies.androidxrecyclerview
implementation rootProject.ext.dependencies.flexbox
if (Boolean.valueOf(USE_MAVEN_PACKAGE)) {
implementation rootProject.ext.dependencies.mogoserviceapi
implementation rootProject.ext.dependencies.modulecommon

View File

@@ -12,6 +12,8 @@ import com.mogo.eagle.core.data.deva.scene.SceneTAG
import com.mogo.eagle.core.function.api.devatools.IDevaToolsProvider
import com.zhjt.mogo_core_function_devatools.badcase.BadCaseManager
import com.zhjt.mogo_core_function_devatools.logcatch.MogoLogCatchManager
import com.zhjt.mogo_core_function_devatools.monitor.MonitorManager
import com.zhjt.mogo_core_function_devatools.monitor.remote.UserServiceManager
import com.zhjt.mogo_core_function_devatools.scene.SceneManager.Companion.sceneManager
import com.zhjt.mogo_core_function_devatools.status.*
import com.zhjt.mogo_core_function_devatools.trace.TraceManager.Companion.traceManager
@@ -110,4 +112,12 @@ class DevaToolsProvider : IDevaToolsProvider {
override fun hideStatusBar() {
StatusManager.hide()
}
override fun startMonitor() {
mContext?.let { MonitorManager.getInstance(it)?.startMonitor() }
}
override fun stopMonitor() {
mContext?.let { MonitorManager.getInstance(it)?.stopMonitor() }
}
}

View File

@@ -0,0 +1,109 @@
package com.zhjt.mogo_core_function_devatools.monitor
import android.annotation.SuppressLint
import android.content.Context
import android.os.Process
import android.util.Log
import com.zhjt.mogo_core_function_devatools.monitor.db.CpuInfo
import com.zhjt.mogo_core_function_devatools.monitor.db.MonitorDb
import com.zhjt.mogo_core_function_devatools.monitor.remote.UserServiceManager
import com.zhjt.mogo_core_function_devatools.monitor.utils.CpuUtils
import java.text.SimpleDateFormat
class CpuMonitor private constructor(var context: Context) {
var isRunning = false
private val pid by lazy {
Process.myPid()
}
private val cpuInfoList by lazy {
ArrayList<CpuInfo>()
}
private val dateFormat by lazy {
SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
}
companion object {
private const val TAG = "CpuMonitor"
const val INTERVAL_PERF = 1000L
@SuppressLint("StaticFieldLeak")
@Volatile
private var sInstance: CpuMonitor? = null
fun getInstance(context: Context): CpuMonitor? {
if (sInstance == null) {
synchronized(CpuMonitor::class.java) {
if (sInstance == null) {
sInstance = CpuMonitor(context)
}
}
}
return sInstance
}
}
/**
* 请先运行Shizuku的服务
*/
@Synchronized
fun startRun() {
if (!isRunning) {
Thread(CpuRunnable()).start()
isRunning = true
}
}
@Synchronized
fun isStarted() = isRunning
@Synchronized
fun stop() {
isRunning = false
}
fun saveCpuInfos() {
synchronized(this) {
MonitorDb.getDb(context).monitorDao().saveAllCpuInfos(*cpuInfoList.toTypedArray())
cpuInfoList.clear()
}
}
private fun getCpuValue(): Double {
var value = 0.0
try {
// 应用获取当前进程CPU使用时间Binder服务端获取系统CPU时间中间用耗时不如使用top命令
// value = UserServiceManager.getCpuUsage(Process.myPid())
value = CpuUtils.getCpuUsageForO()
return value
} catch (e: Exception) {
Log.e(TAG, e.message ?: "未知异常!")
}
return value
}
inner class CpuRunnable : Runnable {
override fun run() {
while (isRunning) {
val cpu = getCpuValue()
if (cpu > 0) {
synchronized(this@CpuMonitor) {
cpuInfoList.add(
CpuInfo(
saveTime = dateFormat.format(System.currentTimeMillis()),
cpuPercent = cpu
)
)
}
}
try {
Thread.sleep(INTERVAL_PERF)
} catch (e: InterruptedException) {
e.printStackTrace()
}
}
}
}
}

View File

@@ -0,0 +1,121 @@
package com.zhjt.mogo_core_function_devatools.monitor
import android.annotation.SuppressLint
import android.content.Context
import android.os.Process.myPid
import android.util.Log
import com.zhjt.mogo_core_function_devatools.monitor.db.MemInfo
import com.zhjt.mogo_core_function_devatools.monitor.db.MonitorDb
import com.zhjt.mogo_core_function_devatools.monitor.utils.HookUtils
import com.zhjt.mogo_core_function_devatools.monitor.utils.MemUtils
import java.text.SimpleDateFormat
class MemMonitor private constructor(var context: Context) {
var isRunning = false
private val memInfoList by lazy {
ArrayList<MemInfo>()
}
private val dateFormat by lazy {
SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
}
companion object {
private const val TAG = "MemMonitor"
const val INTERVAL_PERF = 1000L
@SuppressLint("StaticFieldLeak")
@Volatile
private var sInstance: MemMonitor? = null
fun getInstance(context: Context): MemMonitor? {
if (sInstance == null) {
synchronized(MemMonitor::class.java) {
if (sInstance == null) {
sInstance = MemMonitor(context)
}
}
}
return sInstance
}
}
@Synchronized
fun startRun() {
if (!isRunning) {
Thread(MemoryRunnable()).start()
isRunning = true
}
}
@Synchronized
fun isStarted() = isRunning
@Synchronized
fun stop() {
isRunning = false
}
fun saveMemInfos() {
synchronized(this) {
MonitorDb.getDb(context).monitorDao().saveAllMemInfos(*memInfoList.toTypedArray())
memInfoList.clear()
}
}
/**
* 单位MB
*/
private fun getMemoryValue(): Double {
var value = 0.0
try {
value = MemUtils.getPssMemValue(context, intArrayOf(myPid()))
return value
} catch (e: Exception) {
Log.e(TAG, e.message ?: "未知异常!")
}
return value
}
/**
* 单位MB
*/
private fun getMemoryArray(): DoubleArray {
var value = doubleArrayOf()
try {
value = MemUtils.getPssArray(context, intArrayOf(myPid()))
return value
} catch (e: Exception) {
Log.e(TAG, e.message ?: "未知异常!")
}
return value
}
inner class MemoryRunnable : Runnable {
override fun run() {
while (isRunning) {
// 0: 总PSS1: dalvik总Pss, 2: native总Pss, 3: other总Pss
val pssArray = getMemoryArray()
if (pssArray[0] > 0) {
synchronized(this@MemMonitor) {
memInfoList.add(
MemInfo(
saveTime = dateFormat.format(System.currentTimeMillis()),
totalPss = pssArray[0],
dalvikPss = pssArray[1],
nativePss = pssArray[2],
otherPss = pssArray[3]
)
)
}
}
try {
Thread.sleep(INTERVAL_PERF)
} catch (e: InterruptedException) {
e.printStackTrace()
}
}
}
}
}

View File

@@ -0,0 +1,64 @@
package com.zhjt.mogo_core_function_devatools.monitor
import android.annotation.SuppressLint
import android.content.Context
import android.os.Looper
import com.mogo.eagle.core.utilcode.util.ToastUtils
import java.util.*
class MonitorManager private constructor(var context: Context) {
private var timer: Timer? = null
private var isStarted = false
companion object {
@SuppressLint("StaticFieldLeak")
@Volatile
private var sInstance: MonitorManager? = null
fun getInstance(context: Context): MonitorManager? {
if (sInstance == null) {
synchronized(MonitorManager::class.java) {
if (sInstance == null) {
sInstance = MonitorManager(context)
}
}
}
return sInstance
}
}
/**
* 主线程中执行
*/
fun startMonitor() {
if (Thread.currentThread() == Looper.getMainLooper().thread) {
if (!isStarted) {
CpuMonitor.getInstance(context)?.startRun()
MemMonitor.getInstance(context)?.startRun()
if (timer == null) {
timer = Timer()
}
timer!!.schedule(object : TimerTask() {
override fun run() {
CpuMonitor.getInstance(context)?.saveCpuInfos()
MemMonitor.getInstance(context)?.saveMemInfos()
}
}, 1000, 60000)
isStarted = true
ToastUtils.showShort("性能监控启动成功!")
} else {
ToastUtils.showShort("性能监控已启动,请勿重复启动!")
}
}
}
fun stopMonitor() {
CpuMonitor.getInstance(context)?.stop()
MemMonitor.getInstance(context)?.stop()
timer?.cancel()
timer = null
isStarted = false
ToastUtils.showShort("性能监控停止成功!")
}
}

View File

@@ -0,0 +1,18 @@
package com.zhjt.mogo_core_function_devatools.monitor.db
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "t_cpu")
data class CpuInfo(
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "id")
val uuid: Long = 0,
@ColumnInfo(name = "save_time")
val saveTime: String?,
@ColumnInfo(name = "cpu_percent")
val cpuPercent: Double
)

View File

@@ -0,0 +1,27 @@
package com.zhjt.mogo_core_function_devatools.monitor.db
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "t_memory")
data class MemInfo(
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "id")
val uuid: Long = 0,
@ColumnInfo(name = "save_time")
val saveTime: String,
@ColumnInfo(name = "total_pss")
val totalPss: Double,
@ColumnInfo(name = "dalvik_pss")
val dalvikPss: Double,
@ColumnInfo(name = "native_pss")
val nativePss: Double,
@ColumnInfo(name = "other_pss")
val otherPss: Double
)

View File

@@ -0,0 +1,24 @@
package com.zhjt.mogo_core_function_devatools.monitor.db
import androidx.room.*
@Dao
interface MonitorDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun saveCpu(info: CpuInfo)
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun saveAllCpuInfos(vararg cpuInfo: CpuInfo)
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun saveMemory(info: MemInfo)
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun saveAllMemInfos(vararg memInfo: MemInfo)
@Query("SELECT * FROM t_cpu WHERE id =:id")
fun getAllCPUById(id: Long): List<CpuInfo>
@Query("SELECT * FROM t_memory WHERE id =:id")
fun getAllMemById(id: Long): List<MemInfo>
}

View File

@@ -0,0 +1,27 @@
package com.zhjt.mogo_core_function_devatools.monitor.db
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
@Database(entities = [CpuInfo::class, MemInfo::class], version = 1, exportSchema = false)
abstract class MonitorDb: RoomDatabase() {
abstract fun monitorDao(): MonitorDao
companion object {
const val INTERNAL_DB_NAME = "mogo_monitor.db"
private var db: MonitorDb? = null
@JvmStatic
fun getDb(context: Context): MonitorDb {
if (db == null) {
db = Room.databaseBuilder(context.applicationContext, MonitorDb::class.java, INTERNAL_DB_NAME)
.fallbackToDestructiveMigration()
.build()
}
return db!!
}
}
}

View File

@@ -0,0 +1,17 @@
package com.zhjt.mogo_core_function_devatools.monitor.remote;
import android.os.IBinder;
import android.os.IInterface;
import android.os.RemoteException;
public interface IUserInterface extends IInterface {
String DESCRIPTOR = "rikka.shizuku.demo.IUserInterface";
int TRANSACTION_exec = IBinder.FIRST_CALL_TRANSACTION;
int TRANSACTION_getCpuUsage = IBinder.FIRST_CALL_TRANSACTION + 1;
String exec(String cmd) throws RemoteException;
double getCpuUsage(int pid) throws RemoteException;
}

View File

@@ -0,0 +1,141 @@
package com.zhjt.mogo_core_function_devatools.monitor.remote;
import android.os.Binder;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Parcel;
import android.os.RemoteException;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.zhjt.mogo_core_function_devatools.monitor.utils.CpuUtils;
/**
* Server端Binder类
*/
public class RemoteUserServiceImp extends Binder implements IUserInterface {
public RemoteUserServiceImp() {
attachInterface(this, DESCRIPTOR);
}
public static IUserInterface asInterface(IBinder obj) {
if ((obj == null)) {
return null;
}
IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (iin instanceof IUserInterface) {
return ((IUserInterface) iin);
}
return new Proxy(obj);
}
@Override
public String exec(String cmd) throws RemoteException {
return "";
}
@Override
public double getCpuUsage(int pid) throws RemoteException {
return CpuUtils.getCpuUsagePercentWithPid(pid);
}
/**
* 处理Client端请求
*
* @param code
* @param data
* @param reply
* @param flags
* @return
* @throws RemoteException
*/
@Override
protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
final String descriptor = DESCRIPTOR;
switch (code) {
case TRANSACTION_exec:
data.enforceInterface(descriptor);
String _arg0;
_arg0 = data.readString();
String _result = this.exec(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true;
default:
return super.onTransact(code, data, reply, flags);
}
}
@Nullable
@Override
public String getInterfaceDescriptor() {
return super.getInterfaceDescriptor();
}
/**
* 返回给Client端并被封装成BinderProxy
*
* @return
*/
@Override
public IBinder asBinder() {
return this;
}
public static class Proxy implements IUserInterface {
private IBinder mRemote;
public Proxy(IBinder remote) {
mRemote = remote;
}
public String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public IBinder asBinder() {
// 返回Server端Binder对象
return mRemote;
}
@Override
public String exec(String cmd) throws RemoteException {
// 参数传递给Server端并挂起当前线程等待Server端执行完返回结果
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(cmd);
mRemote.transact(TRANSACTION_exec, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public double getCpuUsage(int pid) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
double _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(pid);
mRemote.transact(TRANSACTION_getCpuUsage, _data, _reply, 0);
_reply.readException();
_result = _reply.readDouble();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
}

View File

@@ -0,0 +1,182 @@
package com.zhjt.mogo_core_function_devatools.monitor.remote
import rikka.shizuku.Shizuku
import android.content.ServiceConnection
import android.content.ComponentName
import android.os.IBinder
import rikka.shizuku.Shizuku.UserServiceArgs
import android.content.pm.PackageManager
import android.util.Log
import com.mogo.eagle.core.utilcode.util.AppUtils
import com.mogo.eagle.core.utilcode.util.ToastUtils
import rikka.shizuku.Shizuku.OnBinderReceivedListener
import rikka.shizuku.Shizuku.OnBinderDeadListener
import rikka.shizuku.Shizuku.OnRequestPermissionResultListener
import java.lang.StringBuilder
object UserServiceManager {
private const val TAG = "UserServiceManager"
private const val REQUEST_CODE_BIND = 1
private const val REQUEST_CODE_UNBIND = 2
private var userServiceProxy: IUserInterface? = null
private var isFirst = true
fun init() {
if (isFirst) {
addListener()
isFirst = false
}
bindUserService()
}
fun stopService() {
removeListener()
unbindUserService()
}
fun isServiceActive(): Boolean {
var isActive = false
val service = userServiceProxy
if (service != null && service is IBinder && service.isBinderAlive) {
isActive = true
}
return isActive
}
private fun addListener() {
Shizuku.addBinderReceivedListenerSticky(BINDER_RECEIVED_LISTENER)
Shizuku.addBinderDeadListener(BINDER_DEAD_LISTENER)
Shizuku.addRequestPermissionResultListener(REQUEST_PERMISSION_RESULT_LISTENER)
}
private fun removeListener() {
Shizuku.removeBinderReceivedListener(BINDER_RECEIVED_LISTENER)
Shizuku.removeBinderDeadListener(BINDER_DEAD_LISTENER)
Shizuku.removeRequestPermissionResultListener(REQUEST_PERMISSION_RESULT_LISTENER)
}
private fun bindUserService() {
if (checkPermission(REQUEST_CODE_BIND)) {
val res = StringBuilder()
try {
if (Shizuku.getVersion() < 10) {
res.append("requires Shizuku API 10")
} else {
Shizuku.bindUserService(userServiceArgs, userServiceConnection)
}
} catch (tr: Throwable) {
tr.printStackTrace()
res.append(tr.toString())
}
}
}
private fun unbindUserService() {
if (checkPermission(REQUEST_CODE_UNBIND)) {
val res = StringBuilder()
try {
if (Shizuku.getVersion() < 10) {
res.append("requires Shizuku API 10")
} else {
Shizuku.unbindUserService(userServiceArgs, userServiceConnection, true)
}
} catch (tr: Throwable) {
tr.printStackTrace()
res.append(tr.toString())
}
}
}
fun exec(cmd: String): String {
return userServiceProxy?.exec(cmd) ?: ""
}
/**
* 单位:%保留1位小数
*/
fun getCpuUsage(pid: Int): Double {
return userServiceProxy?.getCpuUsage(pid) ?: -1.0
}
private val userServiceConnection: ServiceConnection = object : ServiceConnection {
override fun onServiceConnected(componentName: ComponentName, binder: IBinder?) {
if (binder != null && binder.pingBinder()) {
val service = RemoteUserServiceImp.asInterface(binder)
userServiceProxy = service
ToastUtils.showShort("监控权限获取成功!")
}
}
override fun onServiceDisconnected(componentName: ComponentName) {}
}
private val userServiceArgs by lazy {
val appInfo = AppUtils.getAppInfo()
UserServiceArgs(
ComponentName(
appInfo?.packageName ?: "com.mogo.launcher.f",
RemoteUserServiceImp::class.java.name
)
)
.daemon(false)
.processNameSuffix("service")
.debuggable(true)
.version(appInfo?.versionCode ?: 1)
}
private fun checkPermission(code: Int): Boolean {
if (Shizuku.isPreV11()) {
Log.w(TAG, "Version is preV11!")
return false
}
try {
return if (Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED) {
true
} else if (Shizuku.shouldShowRequestPermissionRationale()) {
Log.e(TAG, "User denied permission (shouldShowRequestPermissionRationale=true)")
false
} else {
Shizuku.requestPermission(code)
false
}
} catch (e: Throwable) {
Log.e(TAG, Log.getStackTraceString(e))
ToastUtils.showLong("请先打开Shizuku并启动它")
}
return false
}
private fun onRequestPermissionsResult(requestCode: Int, grantResult: Int) {
if (grantResult == PackageManager.PERMISSION_GRANTED) {
when (requestCode) {
REQUEST_CODE_BIND -> {
bindUserService()
}
REQUEST_CODE_UNBIND -> {
unbindUserService()
}
else -> {}
}
} else {
Log.e(TAG, "User denied permission")
}
}
private val BINDER_RECEIVED_LISTENER = OnBinderReceivedListener {
if (Shizuku.isPreV11()) {
Log.w(TAG, "Shizuku pre-v11 is not supported")
} else {
Log.d(TAG, "Binder received")
}
}
private val BINDER_DEAD_LISTENER = OnBinderDeadListener { Log.w(TAG, "Binder dead") }
private val REQUEST_PERMISSION_RESULT_LISTENER =
OnRequestPermissionResultListener { requestCode: Int, grantResult: Int ->
onRequestPermissionsResult(
requestCode,
grantResult
)
}
}

View File

@@ -0,0 +1,298 @@
package com.zhjt.mogo_core_function_devatools.monitor.utils;
import android.os.Process;
import android.text.TextUtils;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
/**
* CPU相关工具类。
*/
public class CpuUtils {
private static boolean initCpu = true;
private static double oCpu = 0.0;
private static double oIdle = 0.0;
private static double pJif = 0.0;
private static double pCpu = 0.0;
private static double aCpu = 0.0;
private static double opCpu = 0.0;
private static double oaCpu = 0.0;
/**
* 8.0以上获取cpu的方式
*
* @return
*/
public static double getCpuUsageForO() {
java.lang.Process process = null;
try {
process = Runtime.getRuntime().exec("top -n 1");
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
int cpuIndex = -1;
while ((line = reader.readLine()) != null) {
line = line.trim();
if (TextUtils.isEmpty(line)) {
continue;
}
int tempIndex = getCPUIndex(line);
if (tempIndex != -1) {
cpuIndex = tempIndex;
continue;
}
if (line.startsWith(String.valueOf(Process.myPid()))) {
if (cpuIndex == -1) {
continue;
}
String[] param = line.split("\\s+");
if (param.length <= cpuIndex) {
continue;
}
String cpu = param[cpuIndex];
if (cpu.endsWith("%")) {
cpu = cpu.substring(0, cpu.lastIndexOf("%"));
}
return Double.parseDouble(cpu) / Runtime.getRuntime().availableProcessors();
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (process != null) {
process.destroy();
}
}
return 0;
}
private static int getCPUIndex(String line) {
if (line.contains("CPU")) {
String[] titles = line.split("\\s+");
for (int i = 0; i < titles.length; i++) {
if (titles[i].contains("CPU")) {
return i;
}
}
}
return -1;
}
/**
* 获取应用的CPU占用率单位%保留1位小数
* @param pid
* @return
*/
public static double getCpuUsagePercentWithPid(int pid) {
double usage = 0.0;
String[] result1;
String[] result2;
if (pid >= 0) {
result1 = getCpuWithPid(pid);
if (null != result1) {
pCpu = Double.parseDouble(result1[1])
+ Double.parseDouble(result1[2]);
}
result2 = getSystemCpu();
if (null != result2) {
aCpu = 0.0;
for (int i = 2; i < result2.length; i++) {
if (result2[i] != null) {
aCpu += Double.parseDouble(result2[i]);
}
}
}
if ((aCpu - oaCpu) != 0) {
usage = DoubleUtils.div(((pCpu - opCpu) * 100.00),
(aCpu - oaCpu), 1);
if (usage < 0) {
usage = 0;
}
else if (usage > 100)
{
usage = 100;
}
}
opCpu = pCpu;
oaCpu = aCpu;
}
pJif = pCpu;
return usage;
}
/**
* 获取CPU使用率
*
* @return CPU使用率单位%
*/
public static double getSysCpuUsagePercent() {
double usage = 0.0;
if (initCpu) {
initCpu = false;
RandomAccessFile reader = null;
try {
reader = new RandomAccessFile("/proc/stat",
"r");
String load;
load = reader.readLine();
String[] toks = load.split(" ");
oIdle = Double.parseDouble(toks[5]);
oCpu = Double.parseDouble(toks[2])
+ Double.parseDouble(toks[3])
+ Double.parseDouble(toks[4])
+ Double.parseDouble(toks[6])
+ Double.parseDouble(toks[8])
+ Double.parseDouble(toks[7]);
} catch (IOException e) {
e.printStackTrace();
}
finally
{
FileUtil.closeRandomAccessFile(reader);
}
} else {
RandomAccessFile reader = null;
try {
reader = new RandomAccessFile("/proc/stat", "r");
String load;
load = reader.readLine();
String[] toks = load.split(" ");
double cIdle = Double.parseDouble(toks[5]);
double cCpu = Double.parseDouble(toks[2])
+ Double.parseDouble(toks[3])
+ Double.parseDouble(toks[4])
+ Double.parseDouble(toks[6])
+ Double.parseDouble(toks[8])
+ Double.parseDouble(toks[7]);
if (0 != ((cCpu + cIdle) - (oCpu + oIdle))) {
usage = DoubleUtils.div((100.00 * ((cCpu - oCpu))),
((cCpu + cIdle) - (oCpu + oIdle)), 1);
if (usage < 0) {
usage = 0;
}
else if (usage > 100)
{
usage = 100;
}
}
oCpu = cCpu;
oIdle = cIdle;
} catch (IOException e) {
e.printStackTrace();
} finally {
FileUtil.closeRandomAccessFile(reader);
}
}
return usage;
}
private static String[] getCpuWithPid(int pid) {
String cpuPath = "/proc/" + pid + "/stat";
String cpu = "";
String[] result = new String[3];
File f = new File(cpuPath);
// 部分进程信息无法读取
if (!f.exists() || !f.canRead())
{
return result;
}
FileReader fr = null;
BufferedReader localBufferedReader = null;
try {
fr = new FileReader(f);
localBufferedReader = new BufferedReader(fr, 8192);
cpu = localBufferedReader.readLine();
if (null != cpu) {
String[] cpuSplit = cpu.split(" ");
result[0] = cpuSplit[1];
result[1] = cpuSplit[13];
result[2] = cpuSplit[14];
}
}catch (IOException e) {
e.printStackTrace();
}
FileUtil.closeReader(localBufferedReader);
return result;
}
private static String[] getSystemCpu() {
String cpuPath = "/proc/stat";
String cpu = "";
String[] result = new String[7];
File f = new File(cpuPath);
if (!f.exists() || !f.canRead())
{
return result;
}
FileReader fr = null;
BufferedReader localBufferedReader = null;
try {
fr = new FileReader(f);
localBufferedReader = new BufferedReader(fr, 8192);
cpu = localBufferedReader.readLine();
if (null != cpu) {
result = cpu.split(" ");
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
FileUtil.closeReader(localBufferedReader);
return result;
}
public static String getCpuUsageByCmd() {
java.lang.Process process;
StringBuilder sb = new StringBuilder();
String line;
String cmd = "dumpsys cpuinfo";
try {
process = Runtime.getRuntime().exec(
new String[] { "sh", "-c", cmd });
BufferedReader br = new BufferedReader(new InputStreamReader(
process.getInputStream()));
while (((line = br.readLine()) != null)) {
// 去掉空白行数据
line = line.trim();
if (line.equals("")) {
continue;
}
sb.append(line);
sb.append("\r\n");
}
try {
process.waitFor();
} catch (InterruptedException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
return sb.toString();
}
/**
* 获取进程的CPU使用时间片
*
* @return 进程的CPU使用时间片
*/
public long getJif() {
return (long)pJif;
}
}

View File

@@ -0,0 +1,44 @@
package com.zhjt.mogo_core_function_devatools.monitor.utils
import java.lang.Exception
import java.math.BigDecimal
object DoubleUtils {
fun mul(d1: Double, d2: Double): Double {
val bd1 = BigDecimal(d1.toString())
val bd2 = BigDecimal(d2.toString())
return try {
bd1.multiply(bd2).toDouble()
} catch (e: Exception) {
e.printStackTrace()
0.0
}
}
@JvmStatic
fun div(d1: Double, d2: Double, scale: Int): Double {
val bd1 = BigDecimal(d1.toString())
val bd2 = BigDecimal(d2.toString())
return try {
// 直接向下取整
bd1.divide(bd2, scale, BigDecimal.ROUND_DOWN).toDouble()
} catch (e: Exception) {
e.printStackTrace()
0.0
}
}
/**
* 四舍五入
* scale:保留多少个小数位
*/
@JvmStatic
fun keepDecimal(v: Double, scale: Int = 1): Double {
if (scale < 0) {
throw IllegalArgumentException("The scale must be a positive integer or zero")
}
return BigDecimal(v).setScale(scale, BigDecimal.ROUND_HALF_UP).toDouble()
}
}

View File

@@ -0,0 +1,304 @@
package com.zhjt.mogo_core_function_devatools.monitor.utils
import android.net.LocalSocket
import android.util.Log
import java.io.*
import java.lang.Exception
import java.net.Socket
import java.nio.channels.FileChannel
object FileUtil {
private const val TAG = "FileUtil"
const val separator = "/"
fun isPathStringValid(path: String?): Boolean {
if (null == path || path.isEmpty()) {
return false
}
if (path.contains(":") || path.contains("*") || path.contains("?")
|| path.contains("\"") || path.contains("<")
|| path.contains(">") || path.contains("|")
) {
Log.w(TAG, "filename can not contains:*:?\"<>|")
return false
}
return true
}
fun isPath(path: String): Boolean {
return path.contains(separator) || path.contains("\\")
}
fun getPath(path: String): String {
val la = path.lastIndexOf(separator)
return path.substring(0, la)
}
fun convertValidFilePath(path: String, defPosfix: String): String {
var filePath = path
if (path.contains(separator) || path.contains("\\")) {
val la = filePath.lastIndexOf(".")
if (la < 0) {
filePath = path + defPosfix
} else {
val temp = filePath.substring(la)
if (temp.contains(separator) || temp.contains("\\")) {
// "."是目录名的一部分而不是后缀名的情况
filePath = path + defPosfix
}
// else fileName = fileName
}
} else {
if (!path.contains(".")) // 没有有后缀
{
filePath = filePath + defPosfix
}
}
return filePath
}
fun isFileExists(file: String?): Boolean {
try {
val f = File(file)
if (!f.exists()) {
return false
}
} catch (e: Exception) {
e.printStackTrace()
return false
}
return true
}
fun isFileValid(f: File): Boolean {
if (!f.exists()) {
try {
f.createNewFile()
} catch (e: IOException) {
return false
}
f.delete()
}
return true
}
fun isFileValid(parent: File?, name: String?): Boolean {
val f = File(parent, name)
return isFileValid(f)
}
/**
* 删除存在的文件
*
* @param filePath
*/
fun delExistFile(filePath: String?) {
val f = File(filePath)
if (f.exists()) f.delete()
}
/**
* 关闭bufferReader
*
* @param br
*/
@JvmStatic
fun closeReader(br: Reader?) {
if (br != null) {
try {
br.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
}
/**
* 关闭Writer
*
* @param wr
*/
fun closeWriter(wr: Writer?) {
if (wr != null) {
try {
wr.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
}
/**
* flush Writer
*
* @param wr
*/
fun flushWriter(wr: Writer?) {
if (wr != null) {
try {
wr.flush()
} catch (e: IOException) {
e.printStackTrace()
}
}
}
/**
* 输入流的关闭
*
* @param in
*/
fun closeInputStream(`in`: InputStream?) {
if (`in` != null) {
try {
`in`.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
}
/**
* 输出流的关闭
*
* @param out
*/
fun closeOutputStream(out: OutputStream?) {
if (out != null) {
try {
out.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
}
/**
* 文件管道的关闭
*
* @param chl
*/
fun closeFileChannel(chl: FileChannel?) {
if (chl != null) {
try {
chl.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
}
/**
* RandomAccessFile的关闭
*
* @param f RandomAccessFile对象
*/
@JvmStatic
fun closeRandomAccessFile(f: RandomAccessFile?) {
if (f != null) {
try {
f.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
}
/**
* Socket的关闭
*
* @param s Socket对象
*/
fun colseSocket(s: Socket?) {
if (s != null) {
try {
s.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
}
/**
* LocalSocket的关闭
*
* @param s Socket对象
*/
fun colseLocalSocket(s: LocalSocket?) {
if (s != null) {
try {
s.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
}
fun deleteFile(file: File) {
if (file.exists()) { // 判断文件是否存在
if (file.isFile) { // 判断是否是文件
file.delete() // delete()方法 你应该知道 是删除的意思;
} else if (file.isDirectory) { // 否则如果它是一个目录
val files = file.listFiles() // 声明目录下所有的文件 files[];
for (i in files.indices) { // 遍历目录下所有的文件
deleteFile(files[i]) // 把每个文件 用这个方法进行迭代
}
}
file.delete()
} else {
Log.e(TAG, "文件不存在!" + "n")
}
}
/**
* 拷贝文件
*
* @param s 源文件
* @param t 目标文件
*/
fun copyFile(s: File?, t: File) {
var fi: FileInputStream? = null
var fo: FileOutputStream? = null
var `in`: FileChannel? = null
var out: FileChannel? = null
try {
if (!t.exists()) {
t.createNewFile()
}
fi = FileInputStream(s)
fo = FileOutputStream(t)
`in` = fi.channel
out = fo.channel
// 连接两个通道并且从in通道读取然后写入out通道
`in`.transferTo(0, `in`.size(), out)
} catch (e: IOException) {
e.printStackTrace()
} finally {
closeOutputStream(fo)
closeInputStream(fi)
closeFileChannel(`in`)
closeFileChannel(out)
}
}
fun copyInputToFile(`in`: InputStream?, path: String?) {
var bis: BufferedInputStream? = null
var fos: FileOutputStream? = null
try {
val buffer = ByteArray(10 * 1024)
bis = BufferedInputStream(`in`)
fos = FileOutputStream(path)
var a = bis.read(buffer, 0, buffer.size)
while (a != -1) {
fos.write(buffer, 0, a)
fos.flush()
a = bis.read(buffer, 0, buffer.size)
}
} catch (e: Exception) {
e.printStackTrace()
} finally {
closeOutputStream(fos)
closeInputStream(bis)
closeInputStream(`in`)
}
}
}

View File

@@ -0,0 +1,54 @@
package com.zhjt.mogo_core_function_devatools.monitor.utils;
import android.os.Debug;
import android.util.Log;
import java.lang.reflect.Field;
public class HookUtils {
private static final String TAG = "HookUtils";
/**
* 单位为MB
* 0: 总PSS1: dalvik总Pss, 2: native总Pss, 3: other总Pss
* @param memoryInfo
* @return
*/
public static double[] getPssArray(Debug.MemoryInfo memoryInfo) {
double[] pssArray = new double[4];
if (memoryInfo != null) {
double totalPss = DoubleUtils.keepDecimal(memoryInfo.getTotalPss() / 1024.0, 3);
// Log.d(TAG, "===========Hook PSS 开始:===========\n");
// Log.d(TAG, "总PSS内存为" + totalPss);
pssArray[0] = totalPss;
try {
Field dalvikField = memoryInfo.getClass().getField("dalvikSwappedOutPss");
double dalvikPss = DoubleUtils.keepDecimal(memoryInfo.dalvikPss / 1024.0, 3);
double dalvikSwappedOutPss = DoubleUtils.keepDecimal((int) dalvikField.get(memoryInfo) / 1024.0, 3);
pssArray[1] = dalvikPss + dalvikSwappedOutPss;
// Log.d(TAG, "dalvikPss is:" + dalvikSwappedOutPss);
// Log.d(TAG, "dalvikSwappedOutPss is:" + dalvikPss + "\n\n");
Field nativeField = memoryInfo.getClass().getField("nativeSwappedOutPss");
double nativePss = DoubleUtils.keepDecimal(memoryInfo.nativePss / 1024.0, 3);
double nativeSwappedOutPss = DoubleUtils.keepDecimal((int) nativeField.get(memoryInfo) / 1024.0, 3);
pssArray[2] = nativePss + nativeSwappedOutPss;
// Log.d(TAG, "nativePss is:" + nativePss);
// Log.d(TAG, "nativeSwappedOutPss is:" + nativeSwappedOutPss + "\n\n");
Field otherField = memoryInfo.getClass().getField("otherSwappedOutPss");
double otherPss = DoubleUtils.keepDecimal(memoryInfo.otherPss / 1024.0, 3);
double otherSwappedOutPss = DoubleUtils.keepDecimal((int) otherField.get(memoryInfo) / 1024.0, 3);
pssArray[3] = otherPss + otherSwappedOutPss;
// Log.d(TAG, "otherPss is:" + otherPss);
// Log.d(TAG, "otherSwappedOutPss is:" + otherSwappedOutPss + "\n\n");
//
// Log.d(TAG, "===========Hook PSS 结束!===========\n");
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, e.getMessage());
}
}
return pssArray;
}
}

View File

@@ -0,0 +1,180 @@
package com.zhjt.mogo_core_function_devatools.monitor.utils;
import android.app.ActivityManager;
import android.content.Context;
import android.os.Debug;
import java.lang.reflect.Method;
/**
* 内存信息工具类。
*/
public class MemUtils {
public static double[] getPssArray(Context context, int[] pids) {
Debug.MemoryInfo[] memoryArray = getMemoryInfo(context, pids);
if (memoryArray.length > 0) {
return HookUtils.getPssArray(memoryArray[0]);
} else {
return new double[4];
}
}
/**
* 单位MB
*/
public static double getPssMemValue(Context context, int[] pids) {
double value = 0.0;
Debug.MemoryInfo[] memoryArray = getMemoryInfo(context, pids);
if (memoryArray.length > 0) {
value = DoubleUtils.keepDecimal(memoryArray[0].getTotalPss() / 1024.0, 1);
}
return value;
}
/**
* 只能获取本应用的PSS内存占系统百分比单位%保留1位小数
* @return
*/
public static double getPidMemPercent() {
double percent = 0.0;
Debug.MemoryInfo memInfo = new Debug.MemoryInfo();
Debug.getMemoryInfo(memInfo);
int pidTotalPss = memInfo.getTotalPss();
long systemTotalMem = getTotalMem();
percent = DoubleUtils.keepDecimal(pidTotalPss * 100.0 / systemTotalMem, 1);
return percent;
}
/**
* 获取本应用进程总pss内存单位MB
*/
public static double getPssMem() {
Debug.MemoryInfo memInfo = new Debug.MemoryInfo();
Debug.getMemoryInfo(memInfo);
return DoubleUtils.keepDecimal(memInfo.getTotalPss() / 1024.0, 1);
}
/**
* 获取系统空闲内存占用百分比: 单位%
* @return
*/
public static double getSystemMemPercent() {
double percent = 0.0;
long[] memInfo = getMemInfo();
long free = memInfo[1] + memInfo[2] + memInfo[3];
long total = memInfo[0];
percent = DoubleUtils.keepDecimal(free * 100.0 / total, 1);
return percent;
}
/**
* 获取空闲内存
*
* @return 空闲内存单位KB
*/
public static long getFreeMem() {
long[] memInfo = getMemInfo();
return memInfo[1] + memInfo[2] + memInfo[3];
}
/**
* 获取总内存
*
* @return 总内存单位KB
*/
public static long getTotalMem() {
long[] memInfo = getMemInfo();
return memInfo[0];
}
/**
* 获取内存信息total、free、buffers、cached单位KB
*
* @return 内存信息
*/
public static long[] getMemInfo() {
long memInfo[] = new long[4];
try {
Class<?> procClazz = Class.forName("android.os.Process");
Class<?> paramTypes[] = new Class[] { String.class, String[].class,
long[].class };
Method readProclines = procClazz.getMethod("readProcLines",
paramTypes);
Object args[] = new Object[3];
final String[] memInfoFields = new String[] { "MemTotal:",
"MemFree:", "Buffers:", "Cached:" };
long[] memInfoSizes = new long[memInfoFields.length];
memInfoSizes[0] = 30;
memInfoSizes[1] = -30;
args[0] = "/proc/meminfo";
args[1] = memInfoFields;
args[2] = memInfoSizes;
if (null != readProclines) {
readProclines.invoke(null, args);
for (int i = 0; i < memInfoSizes.length; i++) {
memInfo[i] = memInfoSizes[i];
}
}
} catch (Exception e) {
e.printStackTrace();
}
return memInfo;
}
public static long[] getHeapNative() {
int Native_HeapSize = 0;
int Native_HeapAlloc = 1;
long[] value = new long[2];
value[Native_HeapSize] = Debug.getNativeHeapSize() >> 10;
value[Native_HeapAlloc] = Debug.getNativeHeapAllocatedSize() >> 10;
return value;
}
public static long[] getHeapDalvik() {
int Total_HeapSize = 0;
int Total_HeapAlloc = 1;
long[] value_total = new long[2];
value_total[Total_HeapSize] = Runtime.getRuntime().totalMemory() >> 10;
value_total[Total_HeapAlloc] = (Runtime.getRuntime().totalMemory() - Runtime
.getRuntime().freeMemory()) >> 10;
long[] value_native = getHeapNative();
int Dalvik_HeapSize = 0;
int Dalvik_HeapAlloc = 1;
long[] value_dalvik = new long[2];
value_dalvik[Dalvik_HeapSize] = value_total[Total_HeapSize]
- value_native[0];
value_dalvik[Dalvik_HeapAlloc] = value_total[Total_HeapAlloc]
- value_native[1];
return value_dalvik;
}
/**
* 获取堆内存数据精确到KB Get VM Heap Size by calling:
* Runtime.getRuntime().totalMemory(); Get Allocated VM Memory by calling:
* Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
* Get VM Heap Size Limit by calling: Runtime.getRuntime().maxMemory() Get
* Native Allocated Memory by calling: Debug.getNativeHeapAllocatedSize();
*/
public static long[] getVM() {
long[] value = new long[5];
value[0] = (Runtime.getRuntime().totalMemory() - Runtime.getRuntime()
.freeMemory()) >> 10;
value[1] = Runtime.getRuntime().totalMemory() >> 10;
value[2] = Debug.getNativeHeapAllocatedSize() >> 10;
value[3] = Debug.getNativeHeapSize() >> 10;
value[4] = Debug.getGlobalAllocSize() >> 10;
return value;
}
private static Debug.MemoryInfo[] getMemoryInfo(Context context, int[] pids) {
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
return am.getProcessMemoryInfo(pids);
}
}

View File

@@ -160,6 +160,8 @@ import java.util.*
private var busOperationStatus: IOchBusView? = null
private var isStarted = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
@@ -182,10 +184,17 @@ import java.util.*
}
}
/*ivCameraIcon?.setOnLongClickListener {
activity?.let { it1 -> CarcorderPreviewView.show(it1) }
ivCameraIcon?.setOnLongClickListener {
if (!isStarted) {
CallerDevaToolsManager.startMonitor()
isStarted = true
} else {
CallerDevaToolsManager.stopMonitor()
isStarted = false
}
// activity?.let { it1 -> CarcorderPreviewView.show(it1) }
true
}*/
}
ivToolsIcon?.setOnClickListener {
if (toolsViewFloat == null) {

View File

@@ -28,8 +28,12 @@ import com.mogo.module.common.MogoModule;
import com.mogo.module.common.MogoModulePaths;
import com.mogo.module.service.ServiceConst;
import com.zhidao.support.obu.ami.AmiClientManager;
import com.zhjt.mogo_core_function_devatools.monitor.db.CpuInfo;
import com.zhjt.mogo_core_function_devatools.monitor.db.MemInfo;
import com.zhjt.mogo_core_function_devatools.monitor.db.MonitorDb;
import java.lang.reflect.Field;
import java.util.List;
/**
* 默认初始化一些基础服务配置
@@ -58,6 +62,7 @@ public abstract class MainMoGoApplication extends AbsMogoApplication {
//查询是否有版本的更新
queryAppUpgrade();
checkMonitorDb();
}
@Override
@@ -75,6 +80,18 @@ public abstract class MainMoGoApplication extends AbsMogoApplication {
},5000);
}
private void checkMonitorDb() {
new Thread(() -> {
long limitId = 50001;
List<CpuInfo> cpuList = MonitorDb.getDb(this).monitorDao().getAllCPUById(limitId);
List<MemInfo> memList = MonitorDb.getDb(this).monitorDao().getAllMemById(limitId);
// 大于5w条清除
if (cpuList.size() > 0 || memList.size() > 0) {
this.deleteDatabase(MonitorDb.INTERNAL_DB_NAME);
}
}).start();
}
/**
* 初始化异常采集配置
*/