[Feat]新增性能监控工具
This commit is contained in:
@@ -243,6 +243,9 @@ dependencies {
|
|||||||
debugImplementation rootProject.ext.dependencies.debugleakcanary
|
debugImplementation rootProject.ext.dependencies.debugleakcanary
|
||||||
releaseImplementation rootProject.ext.dependencies.releaseleakcanary
|
releaseImplementation rootProject.ext.dependencies.releaseleakcanary
|
||||||
implementation rootProject.ext.dependencies.android_start_up
|
implementation rootProject.ext.dependencies.android_start_up
|
||||||
|
|
||||||
|
// // 暂不使用Shizuku-API
|
||||||
|
// implementation rootProject.ext.dependencies.shizuku_provider
|
||||||
if (Boolean.valueOf(USE_MAVEN_PACKAGE)) {
|
if (Boolean.valueOf(USE_MAVEN_PACKAGE)) {
|
||||||
implementation rootProject.ext.dependencies.ttspad
|
implementation rootProject.ext.dependencies.ttspad
|
||||||
implementation rootProject.ext.dependencies.mogo_core_function_hmi
|
implementation rootProject.ext.dependencies.mogo_core_function_hmi
|
||||||
|
|||||||
@@ -31,6 +31,15 @@
|
|||||||
android:value="android.startup.provider.config" />
|
android:value="android.startup.provider.config" />
|
||||||
</provider>
|
</provider>
|
||||||
|
|
||||||
|
<!-- <!– 暂不使用Shizuku-API –>-->
|
||||||
|
<!-- <provider-->
|
||||||
|
<!-- android:name="rikka.shizuku.ShizukuProvider"-->
|
||||||
|
<!-- android:authorities="${applicationId}.shizuku"-->
|
||||||
|
<!-- android:enabled="true"-->
|
||||||
|
<!-- android:exported="true"-->
|
||||||
|
<!-- android:multiprocess="false"-->
|
||||||
|
<!-- android:permission="android.permission.INTERACT_ACROSS_USERS_FULL" />-->
|
||||||
|
|
||||||
<!-- 配置APP ID -->
|
<!-- 配置APP ID -->
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="BUGLY_APPID"
|
android:name="BUGLY_APPID"
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package com.mogo.launcher;
|
|||||||
|
|
||||||
import com.mogo.eagle.core.function.main.MainMoGoApplication;
|
import com.mogo.eagle.core.function.main.MainMoGoApplication;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import com.mogo.eagle.core.utilcode.mogo.logger.LogLevel;
|
import com.mogo.eagle.core.utilcode.mogo.logger.LogLevel;
|
||||||
@@ -11,11 +10,7 @@ import com.mogo.launcher.crash.CrashSystem;
|
|||||||
import com.mogo.thread.ext.core.ThreadManager;
|
import com.mogo.thread.ext.core.ThreadManager;
|
||||||
import com.mogo.thread.ext.core.config.ThreadConfig;
|
import com.mogo.thread.ext.core.config.ThreadConfig;
|
||||||
|
|
||||||
import java.util.concurrent.LinkedBlockingDeque;
|
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
|
||||||
import java.util.concurrent.RejectedExecutionHandler;
|
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author congtaowang
|
* @author congtaowang
|
||||||
|
|||||||
@@ -240,7 +240,11 @@ ext {
|
|||||||
//========================= DataStore ======================
|
//========================= DataStore ======================
|
||||||
androidx_datastore_preferences : "androidx.datastore:datastore-preferences:1.0.0",
|
androidx_datastore_preferences : "androidx.datastore:datastore-preferences:1.0.0",
|
||||||
androidx_datastore : "androidx.datastore:datastore:1.0.0",
|
androidx_datastore : "androidx.datastore:datastore:1.0.0",
|
||||||
android_start_up : "com.mogo:android-startup:1.0.1"
|
android_start_up : "com.mogo:android-startup:1.0.1",
|
||||||
|
|
||||||
|
//========================= Shizuku ======================
|
||||||
|
shizuku_api : "dev.rikka.shizuku:api:12.1.0",
|
||||||
|
shizuku_provider : "dev.rikka.shizuku:provider:12.1.0"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -82,7 +82,6 @@ dependencies {
|
|||||||
implementation rootProject.ext.dependencies.androidxconstraintlayout
|
implementation rootProject.ext.dependencies.androidxconstraintlayout
|
||||||
implementation rootProject.ext.dependencies.androidxrecyclerview
|
implementation rootProject.ext.dependencies.androidxrecyclerview
|
||||||
implementation rootProject.ext.dependencies.flexbox
|
implementation rootProject.ext.dependencies.flexbox
|
||||||
|
|
||||||
if (Boolean.valueOf(USE_MAVEN_PACKAGE)) {
|
if (Boolean.valueOf(USE_MAVEN_PACKAGE)) {
|
||||||
implementation rootProject.ext.dependencies.mogoserviceapi
|
implementation rootProject.ext.dependencies.mogoserviceapi
|
||||||
implementation rootProject.ext.dependencies.modulecommon
|
implementation rootProject.ext.dependencies.modulecommon
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import com.mogo.eagle.core.data.deva.scene.SceneTAG
|
|||||||
import com.mogo.eagle.core.function.api.devatools.IDevaToolsProvider
|
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.badcase.BadCaseManager
|
||||||
import com.zhjt.mogo_core_function_devatools.logcatch.MogoLogCatchManager
|
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.scene.SceneManager.Companion.sceneManager
|
||||||
import com.zhjt.mogo_core_function_devatools.status.*
|
import com.zhjt.mogo_core_function_devatools.status.*
|
||||||
import com.zhjt.mogo_core_function_devatools.trace.TraceManager.Companion.traceManager
|
import com.zhjt.mogo_core_function_devatools.trace.TraceManager.Companion.traceManager
|
||||||
@@ -110,4 +112,12 @@ class DevaToolsProvider : IDevaToolsProvider {
|
|||||||
override fun hideStatusBar() {
|
override fun hideStatusBar() {
|
||||||
StatusManager.hide()
|
StatusManager.hide()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun startMonitor() {
|
||||||
|
mContext?.let { MonitorManager.getInstance(it)?.startMonitor() }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun stopMonitor() {
|
||||||
|
mContext?.let { MonitorManager.getInstance(it)?.stopMonitor() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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: 总PSS,1: 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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("性能监控停止成功!")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
)
|
||||||
@@ -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
|
||||||
|
)
|
||||||
@@ -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>
|
||||||
|
}
|
||||||
@@ -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!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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: 总PSS,1: 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -160,6 +160,8 @@ import java.util.*
|
|||||||
|
|
||||||
private var busOperationStatus: IOchBusView? = null
|
private var busOperationStatus: IOchBusView? = null
|
||||||
|
|
||||||
|
private var isStarted = false
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
}
|
}
|
||||||
@@ -182,10 +184,17 @@ import java.util.*
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*ivCameraIcon?.setOnLongClickListener {
|
ivCameraIcon?.setOnLongClickListener {
|
||||||
activity?.let { it1 -> CarcorderPreviewView.show(it1) }
|
if (!isStarted) {
|
||||||
|
CallerDevaToolsManager.startMonitor()
|
||||||
|
isStarted = true
|
||||||
|
} else {
|
||||||
|
CallerDevaToolsManager.stopMonitor()
|
||||||
|
isStarted = false
|
||||||
|
}
|
||||||
|
// activity?.let { it1 -> CarcorderPreviewView.show(it1) }
|
||||||
true
|
true
|
||||||
}*/
|
}
|
||||||
|
|
||||||
ivToolsIcon?.setOnClickListener {
|
ivToolsIcon?.setOnClickListener {
|
||||||
if (toolsViewFloat == null) {
|
if (toolsViewFloat == null) {
|
||||||
|
|||||||
@@ -28,8 +28,12 @@ import com.mogo.module.common.MogoModule;
|
|||||||
import com.mogo.module.common.MogoModulePaths;
|
import com.mogo.module.common.MogoModulePaths;
|
||||||
import com.mogo.module.service.ServiceConst;
|
import com.mogo.module.service.ServiceConst;
|
||||||
import com.zhidao.support.obu.ami.AmiClientManager;
|
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.lang.reflect.Field;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 默认初始化一些基础服务配置
|
* 默认初始化一些基础服务配置
|
||||||
@@ -58,6 +62,7 @@ public abstract class MainMoGoApplication extends AbsMogoApplication {
|
|||||||
|
|
||||||
//查询是否有版本的更新
|
//查询是否有版本的更新
|
||||||
queryAppUpgrade();
|
queryAppUpgrade();
|
||||||
|
checkMonitorDb();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -75,6 +80,18 @@ public abstract class MainMoGoApplication extends AbsMogoApplication {
|
|||||||
},5000);
|
},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();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化异常采集配置
|
* 初始化异常采集配置
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -113,4 +113,8 @@ interface IDevaToolsProvider : IProvider {
|
|||||||
* 隐藏状态栏
|
* 隐藏状态栏
|
||||||
*/
|
*/
|
||||||
fun hideStatusBar()
|
fun hideStatusBar()
|
||||||
|
|
||||||
|
fun startMonitor()
|
||||||
|
|
||||||
|
fun stopMonitor()
|
||||||
}
|
}
|
||||||
@@ -164,4 +164,12 @@ object CallerDevaToolsManager {
|
|||||||
devaToolsProviderApi?.hideStatusBar()
|
devaToolsProviderApi?.hideStatusBar()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun startMonitor() {
|
||||||
|
devaToolsProviderApi?.startMonitor()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stopMonitor() {
|
||||||
|
devaToolsProviderApi?.stopMonitor()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user