android电池健康获取方式

import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import android.os.Build
import android.util.Log
import org.json.JSONObject
import java.io.File
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.math.abs

private val hasPostedBatteryHealth = AtomicBoolean(false)

/***
 * 发送电池健康度(App启动后发送一次)
 */
private fun postBatteryHealthContent() {
    // 防止重复发送
    if (!hasPostedBatteryHealth.compareAndSet(false, true)) return

    handler.postDelayed({
        runCatching {
            val healthJson = buildBatteryHealthJson(applicationContext)
            mBinder?.sendData(healthJson)
            Log.i("BatteryHealth", "battery health sent: $healthJson")
        }.onFailure {
            Log.e("BatteryHealth", "postBatteryHealthContent failed", it)
        }
    }, 500)
}

private fun buildBatteryHealthJson(context: Context): String {
    val bm = context.getSystemService(Context.BATTERY_SERVICE) as BatteryManager
    val intent = context.registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))

    val manufacturer = Build.MANUFACTURER.orEmpty()
    val model = Build.MODEL.orEmpty()

    val level = intent?.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) ?: -1
    val scale = intent?.getIntExtra(BatteryManager.EXTRA_SCALE, -1) ?: -1
    val batteryPct = if (level >= 0 && scale > 0) level.toDouble() / scale.toDouble() else -1.0

    val tempDeci = intent?.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, Int.MIN_VALUE) ?: Int.MIN_VALUE
    val tempC = if (tempDeci != Int.MIN_VALUE) tempDeci / 10.0 else 25.0

    val chargeCounterUah = bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER)
    val chargeCounterMah = if (chargeCounterUah > 0) chargeCounterUah / 1000.0 else -1.0

    // --- 尝试读取sysfs ---
    val fullMah = readSysfsMah("/sys/class/power_supply/battery/charge_full")
    val designMahSys = readSysfsMah("/sys/class/power_supply/battery/charge_full_design")
    val cycleCount = readSysfsInt("/sys/class/power_supply/battery/cycle_count")
    val asoc = readSysfsDouble("/sys/class/power_supply/battery/fg_asoc")
        ?: readSysfsDouble("/sys/class/power_supply/battery/batt_asoc")
    val samsungFccMah = readSysfsMah("/sys/class/power_supply/battery/batt_full_capacity")

    // 设计容量优先sysfs,再按机型兜底
    val designMah = when {
        designMahSys > 0 -> designMahSys
        else -> getFallbackDesignCapacityMah(model)
    }

    // 满充容量优先sysfs;若无,则在已知电量时用chargeCounter反推(粗估)
    val fullChargeMah = when {
        fullMah > 0 -> fullMah
        chargeCounterMah > 0 && batteryPct in 0.05..1.0 -> chargeCounterMah / batteryPct
        else -> -1.0
    }

    // ---- 子分 ----
    val subScores = mutableMapOf<String, Double>()
    // 容量
    if (fullChargeMah > 0 && designMah > 0) {
        subScores["capacity"] = clip(fullChargeMah / designMah * 100.0)
    }
    // 循环(默认寿命800)
    if (cycleCount != null && cycleCount >= 0) {
        subScores["cycle"] = clip(100.0 - cycleCount.toDouble() / 800.0 * 100.0)
    }
    // 热行为(单点温度简化)
    subScores["thermal"] = clip(
        when {
            tempC <= 35 -> 95.0
            tempC <= 40 -> 85.0
            tempC <= 45 -> 70.0
            else -> 50.0
        }
    )
    // 内阻代理(当前版本没有短窗ΔV/I,给中性分)
    subScores["resistance"] = 70.0
    // 效率(暂无完整充放电周期,给中性偏上)
    subScores["efficiency"] = 80.0

    // 通用动态加权
    val weights = mapOf(
        "capacity" to 0.45,
        "resistance" to 0.15,
        "cycle" to 0.15,
        "thermal" to 0.15,
        "efficiency" to 0.10
    )
    val genericScore = weightedScore(subScores, weights)

    // 三星增强链路
    val isSamsung = manufacturer.equals("samsung", ignoreCase = true)
    val finalScore = if (isSamsung) {
        val samsungSubs = mutableMapOf<String, Double>()
        if (asoc != null) samsungSubs["asoc"] = clip(asoc)
        if (samsungFccMah > 0 && designMah > 0) samsungSubs["fcc"] = clip(samsungFccMah / designMah * 100.0)
        subScores["cycle"]?.let { samsungSubs["cycle"] = it }
        subScores["resistance"]?.let { samsungSubs["resistance"] = it }
        subScores["thermal"]?.let { samsungSubs["thermal"] = it }
        samsungSubs["anomaly"] = 90.0 // 先给默认值,后续可接异常事件惩罚

        val samsungWeights = mapOf(
            "asoc" to 0.35,
            "fcc" to 0.25,
            "cycle" to 0.15,
            "resistance" to 0.10,
            "thermal" to 0.10,
            "anomaly" to 0.05
        )
        weightedScore(samsungSubs, samsungWeights)
    } else genericScore

    val score = clip(finalScore)
    val levelText = levelByScore(score)

    // 置信度:数据源质量 * 覆盖率 * 稳定性(这里先固定0.85)
    val qSource = when {
        isSamsung && (asoc != null || samsungFccMah > 0) -> 0.96
        fullMah > 0 || designMahSys > 0 || cycleCount != null -> 0.90
        else -> 0.70
    }
    val qCoverage = subScores.size / 5.0
    val confidence = clip01(qSource * qCoverage * 0.85)

    val json = JSONObject().apply {
        put("type", "battery_health")
        put("timestamp", System.currentTimeMillis())
        put("manufacturer", manufacturer)
        put("model", model)
        put("healthScore", round1(score))
        put("healthLevel", levelText)
        put("confidence", round2(confidence))
        put("explain", "generic+dynamicWeight${if (isSamsung) "+samsungEnhanced" else ""}")

        put("subScores", JSONObject().apply {
            subScores.forEach { (k, v) -> put(k, round1(v)) }
        })

        put("raw", JSONObject().apply {
            put("batteryPct", if (batteryPct >= 0) round2(batteryPct * 100) else -1)
            put("tempC", round1(tempC))
            put("fullChargeMah", if (fullChargeMah > 0) round1(fullChargeMah) else -1)
            put("designMah", if (designMah > 0) round1(designMah) else -1)
            put("cycleCount", cycleCount ?: -1)
            put("asoc", asoc ?: -1)
            put("samsungFccMah", if (samsungFccMah > 0) round1(samsungFccMah) else -1)
        })
    }

    return json.toString()
}

// ------------------ helpers ------------------

private fun weightedScore(scores: Map<String, Double>, weights: Map<String, Double>): Double {
    val keys = weights.keys.filter { scores[it] != null }
    if (keys.isEmpty()) return 0.0
    val w = keys.sumOf { weights[it] ?: 0.0 }
    if (w <= 0.0) return 0.0
    return keys.sumOf { (weights[it] ?: 0.0) * (scores[it] ?: 0.0) } / w
}

private fun levelByScore(score: Double): String = when {
    score >= 90 -> "A"
    score >= 80 -> "B"
    score >= 70 -> "C"
    score >= 60 -> "D"
    else -> "E"
}

private fun clip(v: Double): Double = v.coerceIn(0.0, 100.0)
private fun clip01(v: Double): Double = v.coerceIn(0.0, 1.0)
private fun round1(v: Double): Double = String.format("%.1f", v).toDouble()
private fun round2(v: Double): Double = String.format("%.2f", v).toDouble()

private fun readText(path: String): String? = runCatching {
    val f = File(path)
    if (f.exists() && f.canRead()) f.readText().trim() else null
}.getOrNull()

private fun readSysfsInt(path: String): Int? = readText(path)?.toIntOrNull()
private fun readSysfsDouble(path: String): Double? = readText(path)?.toDoubleOrNull()

private fun readSysfsMah(path: String): Double {
    val raw = readSysfsDouble(path) ?: return -1.0
    return if (raw > 100_000) raw / 1000.0 else raw // µAh -> mAh
}

private fun getFallbackDesignCapacityMah(model: String): Double {
    // 你后续可以替换为更完整机型表/服务端配置
    return when {
        model.contains("SM-S9", true) -> 3900.0
        model.contains("SM-S91", true) -> 4700.0
        model.contains("SM-S92", true) -> 5000.0
        model.contains("SM-G99", true) -> 5000.0
        else -> 4500.0
    }
}
{"type":"battery_health","timestamp":1780571802331,"manufacturer":"HONOR","model":"LGE-AN00","healthScore":89.7,"healthLevel":"B","confidence":0.48,"explain":"generic+dynamicWeight","subScores":{"capacity":100,"thermal":85,"resistance":70,"efficiency":80},"raw":{"batteryPct":76,"tempC":37,"fullChargeMah":4605.3,"designMah":4500,"cycleCount":-1,"asoc":-1,"samsungFccMah":-1}}

总体健康评估

指标说明
健康评分89.7百分制,整体良好
健康等级B中上水平,有一定老化
置信度0.48偏低,诊断结果不够确定

子项评分分析

维度得分评价
容量 (capacity)100满分,电池实际容量 4605.3mAh > 设计容量 4500mAh,无衰减
温度 (thermal)85当前 37°C,属于正常偏高
内阻 (resistance)70⚠️ 中等偏低,内阻增大说明电池有一定老化
效率 (efficiency)80一般,充放电效率有损耗

关键原始数据

  • 电量:当前 76%
  • 实际容量:4605.3mAh(超设计容量约 2.3%,可能是新电池或标定偏差)
  • 设计容量:4500mAh
  • 循环次数:-1(无法获取,HONOR 未上报此数据)
  • ASOC:-1(无法获取绝对荷电状态)

阅读全文
下载说明:
1、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.dandroid.cn/archives/22935,转载请注明出处。
0

评论0

显示验证码
没有账号?注册  忘记密码?