1. 程式人生 > >Android系統應用---SystemUI之三:狀態列電池圖示的顯示和Android電池管理的探討

Android系統應用---SystemUI之三:狀態列電池圖示的顯示和Android電池管理的探討

電池圖示顯示

電池圖示是SystemUI顯示中不可缺少的一部分,它顯示在SystemUI的電池和訊號組合區域。


從佈局來看,電池的顯示屬於status_bar.xml,包含了system_icons.xml佈局

<includelayout="@layout/system_icons"/>

從這裡可以看出,電池圖示和訊號圖示屬於同一個區域,所以這一塊一般是兩個佈局,單卡和雙卡,我們可以根據對應的機型選擇msim_system_icons.xml或者system_icons.xml

電池圖示的部分:

<com.android.systemui.BatteryMeterView

android:id="@+id/battery"

        android:layout_height="14.5dp"

        android:layout_width="9.5dp"

        android:layout_marginBottom="@dimen/battery_margin_bottom"

        android:visibility="gone"/>

電池電量百分比的部分:

<com.android.systemui.BatteryLevelTextViewandroid:id="@+id/battery_level_text"

        android:layout_height="match_parent"

        android:layout_width="wrap_content"

        android:gravity="center"

        android:layout_gravity="center_vertical"

        android:textColor="#ffffff"

        android:textSize="@dimen/battery_level_text_size"

        android:layout_marginStart=

"7dp"/>

電池圖示具體的實現顯示和更新分別在兩個自定義View中實現:BatteryMeterView和BatteryLevelTextView

在原生的實現中: BatteryMeterView這個類來監聽電池狀態的變化的,是一個被BatteryController類所管理的ImageView。 BatteryController通過監聽android.intent.action.BATTERY_CHANGED廣播以從 BetteryService中獲取電量資訊,並根據電量資訊畫一個電池圖示在畫布上。原生的方法不需要去替換圖片。

但有時候我們有需求去用圖片來顯示更新電池圖示,最常見的比如有一套五格訊號圖示和充電圖示,替換android原生的畫出來的電池圖示,我們可以將BatteryMeterView設定未GONE,寫一個自定義的ImageView如下:

<com.android.systemui.BatteryIconViewandroid:id="@+id/battery_icons"

        android:layout_height="wrap_content"

        android:layout_width="wrap_content"

        android:layout_marginBottom="@dimen/battery_margin_bottom"

        android:scaleType="fitCenter"/>

自定義View,我們需要一個廣播接收者來監聽Intent.ACTION_BATTERY_CHANGED,用來獲取電池的狀態

private classBatteryStatuteBroadcastReceiver extendsBroadcastReceiver {

        public static final int UNKNOWN_LEVEL = -1;

        int level = UNKNOWN_LEVEL;

        int plugType;

        booleanplugged;

        int health;

        int status;

        String technology;

        int voltage;

        int temperature;

        @Override

        public void onReceive(Context context, Intentintent) {

            final String action = intent.getAction();

            if (action.equals(Intent.ACTION_BATTERY_CHANGED)){

                level = (int)(100f

                        * intent.getIntExtra(BatteryManager.EXTRA_LEVEL,0)

                        / intent.getIntExtra(BatteryManager.EXTRA_SCALE,100));

                plugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED,0);

                plugged = plugType != 0;

                health = intent.getIntExtra(BatteryManager.EXTRA_HEALTH,

                        BatteryManager.BATTERY_HEALTH_UNKNOWN);

                status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,

                        BatteryManager.BATTERY_STATUS_UNKNOWN);

                technology = intent.getStringExtra(BatteryManager.EXTRA_TECHNOLOGY);

                voltage = intent.getIntExtra(BatteryManager.EXTRA_VOLTAGE,0);

                temperature =intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 0);

                updateIcons();

                postInvalidate();

            }

        }

}

這個Receiver只能動態進行註冊,在視窗載入時註冊,在視窗銷燬時解註冊

@Override

    public voidonAttachedToWindow() {

        super.onAttachedToWindow();

        IntentFilterfilter = newIntentFilter();

       filter.addAction(Intent.ACTION_BATTERY_CHANGED);

        finalIntent sticky =getContext().registerReceiver(mReceiver,filter);

        if (sticky !=null) {

            mReceiver.onReceive(getContext(),sticky);

        }

        mBatteryController.addStateChangedCallback(this);

    }

    @Override

    public voidonDetachedFromWindow() {

        super.onDetachedFromWindow();

        getContext().unregisterReceiver(mReceiver);

        mBatteryController.removeStateChangedCallback(this);

    }

我們在接收到電池變化的廣播時,進行updateIcon,選擇需要的圖示組,這裡我們可以用if else語句,也可以用level-list進行選擇,使用if else語句繁瑣且效率底下,比如五格電池顯示,在不同的電量區間顯示不同的電池圖示。一個比較好的方式是使用level-list

步驟

1.        自定義一個ImageView,在ondraw中實現圖片資源的替換

2.        在建構函式中對ImageView進行設定資源

     setImageResource(R.drawable.battery_level_list);

3.        battery_level_list是一個level-list,定義如下

<?xmlversion="1.0"encoding="utf-8"?>

<level-listxmlns:android="http://schemas.android.com/apk/res/android">

<itemandroid:maxLevel="15"android:drawable="@drawable/stat_sys_battery_1"/>

<itemandroid:maxLevel="49"android:drawable="@drawable/stat_sys_battery_2"/>

<itemandroid:maxLevel="75"android:drawable="@drawable/stat_sys_battery_3"/>

<itemandroid:maxLevel="95"android:drawable="@drawable/stat_sys_battery_4"/>

<itemandroid:maxLevel="100"android:drawable="@drawable/stat_sys_battery_5"/>

</level-list>

4.        其中maxLevel表示最大值,也就是說0-15用的是第一個資源,一個level是50的話,就會從第一個開始匹配,匹配到第三個的時候滿足條件,所以就顯示三格訊號,還有minLevel表示最小值,二者結合可以確定一個level範圍

5.        以電池圖示顯示為例,收到電池狀態改變的廣播後,獲取電池電量百分比,設定相應的level

6.        在ondraw中,獲取圖片資源,在畫布中顯示即可

Drawable drawable = getDrawable();

        BatteryStatuteBroadcastReceiverreceivermReceiver;

        final int level = receiver.level;

       drawable.setLevel(level);

        Bitmapb = ((BitmapDrawable) drawable).getBitmap();

        Bitmapbitmap = b.copy(Bitmap.Config.ARGB_8888,true);

       canvas.drawBitmap(bitmap, 0, 0, null);

}

public voidupdateIcons() {

        if (mTracker.status ==BatteryManager.BATTERY_STATUS_CHARGING&&mTracker.level != 100) {

            setImageResource(R.drawable.battery_chargeing_level_list);

        } else {

           setImageResource(R.drawable.battery_level_list);

        }

}

電池資訊管理

我們的操作只是浮於表面,根據收到的廣播,根據廣播中攜帶的幾個狀態進行簡單的判斷顯示,底層電池的狀態如何改變,有什麼測試方法,如何傳送的Framework狀態,Framework層又是如何打包傳送電池狀態改變的廣播,需要我們再進行探索

Android電池管理的在上層功能其實很簡單:無非是1.監測電池電量的變化並更新顯示介面,2.監聽進入充電狀態和退出充電狀態訊息並更新介面

android系統電池部分的驅動程式,繼承了傳統linux系統下的Power Supply驅動程式架構,Battery驅動程式通過Power Supply驅動程式生成相應的sys檔案系統,從而向用戶空間提供電池各種屬性的介面,然後遍歷整個資料夾,查詢各個能源供應裝置的各種屬性

Android的Linux 核心中的電池驅動會提供如下sysfs介面給framework:

/sys/class/power_supply/ac/onlineAC 電源連線狀態

/sys/class/power_supply/usb/onlineUSB電源連線狀態

/sys/class/power_supply/battery/status充電狀態

/sys/class/power_supply/battery/health電池狀態

/sys/class/power_supply/battery/present使用狀態

/sys/class/power_supply/battery/capacity電池 level

/sys/class/power_supply/battery/batt_vol電池電壓

/sys/class/power_supply/battery/batt_temp電池溫度

/sys/class/power_supply/battery/technology電池技術

當供電裝置的狀態發生變化時,driver會更新這些檔案,然後通過jni中的本地方法android_server_BatteryService_update向java層傳送資訊。當監聽到power_supply變化的訊息後, nativeUpdate函式就會重新讀取以上sysfs檔案獲得當前狀態。

而在使用者層則是在BatteryService.java中通過廣播的方式將如下一些電池相關的屬性上報給上層app使用。

frameworks/base/services/java/com/android/server/BatteryService.java

BatteryService 在SystemServer.java 中建立,BatteryService是在系統啟動的時候就跑起來的,為電池及充電相關的服務,主要作了如下幾件事情: 監聽 UEvent、讀取sysfs 中的狀態 、發出廣播Intent.ACTION_BATTERY_CHANGED通知上層

底層上報電池變化的方式一:UEventObserver

在BatteryService定義了UEventObserver, uevent是Linux核心用來向用戶空間主動上報事件的機制

private final UEventObservermInvalidChargerObserver =newUEventObserver() {

           @Override

           public voidonUEvent(UEventObserver.UEvent event) {

               final int invalidCharger = "1".equals(event.get("SWITCH_STATE")) ? 1 : 0;

               synchronized (mLock) {

                   if (mInvalidCharger != invalidCharger) {

                       mInvalidCharger =invalidCharger;

                   }

               }

            }

       };

}

監聽invalid_charger這個屬性,當有狀態變化時就去用onUEvent去處理

       if (new File("/sys/devices/virtual/switch/invalid_charger/state").exists()) {

              mInvalidChargerObserver.startObserving(

            "DEVPATH=/devices/virtual/switch/invalid_charger");

           }

UEvent的實現分為三層,API+JNI+HAL暫且不細表

 

我們可以看到上面的實現方式只監聽了一種狀態,其他的從JNI_OnLoad()得來

底層上報電池變化的方式二:通過AIDL,battery配置改變的時候,呼叫update

 private final class BatteryListener extends IBatteryPropertiesListener.Stub {

           @Override

           public voidbatteryPropertiesChanged(BatteryProperties props) {

               final long identity= Binder.clearCallingIdentity();

                try {

                   BatteryService.this.update(props);

                } finally {

                  Binder.restoreCallingIdentity(identity);

             }

           }

       }

  呼叫到processValuesLocked(),然後傳送廣播給關關係它的應用sendIntentLocked(),通過廣播Intent.ACTION_BATTERY_CHANGED,將電池狀態、電池電量、電池工藝等屬性打包,傳送給其它的使用者,也就是說,只要 在app程式裡監聽了Intent.ACTION_BATTERY_CHANGED這個廣播,就能獲取到電池的各種狀態屬性

應用如果想要接收到BatteryService傳送出來的電池資訊,則需要註冊一個Intent為

Intent.ACTION_BATTERY_CHANGED的BroadcastReceiver,電池相關的屬性就可以直接讀到

附:電池相關的測試知識

◆驅動路徑

/sys/class/power_supply/battery/status

◆讀取電池的狀態

cat /sys/class/power_supply/battery/uevent

◆模擬環境,取出電池配置檔案

adb pull /system/etc/thermal-engine-8909.conf

[BATT_ID_MONITOR]
   algo_type monitor
   sensor batt_id
   sampling 1000
   thresholds 14000
   thresholds_clr 12000
   actions battery
   action_info 3

adb remount
adb push thermal-engine-8909.conf /system/etc/.
adb reboot

◆得到當前電池的溫度,得到有背光和沒有背光的溫度區間

adb shell cat /sys/bus/spmi/devices/qpnp-vadc-*/batt_id

相關推薦

Android系統應用---SystemUI狀態電池圖示顯示Android電池管理探討

電池圖示顯示 電池圖示是SystemUI顯示中不可缺少的一部分,它顯示在SystemUI的電池和訊號組合區域。 從佈局來看,電池的顯示屬於status_bar.xml,包含了system_icons.xml佈局 <includelayout="@layout/

Android系統資訊獲取 IMSI號IMEI解釋

IMSI號: IMSI是國際移動使用者識別碼的簡稱(International Mobile Subscriber Identity)  它是在公眾陸地行動電話網(PLMN)中用於唯一識別移動使用者的一個號碼。在GSM網路,這個號碼通常被存放在SIM卡中 IMSI共有15位,其結構如下:  MCC

Oracle Spatial分區應用研究縣市省不同分區粒度的效率比較

gpo 本地 clas 對比 ora rac mage 比較 src 在《Oracle Spatial分區應用研究之一:分區與分表查詢性能對比》中已經說明:按縣分區+全局空間索引效率要優於按縣分區+本地空間索引,因此在該實驗報告中,將不再考慮按縣分區+本地空間索引的組合,本

C語言學習及應用筆記C語言const關鍵字及其使用

在C語言程式中,const關鍵字也是經常會用到的一個關鍵字,那麼使用const關鍵字的目的是什麼呢?事實上,在程式中使用const關鍵字的主要目的就是為了向使用者傳遞設計者的一些意圖。 事實上,無論我們是使用const關鍵字宣告變數還是宣告引數,其目的都是為了告訴使用者這個

Android問題集錦轉載Javah 常見錯誤記錄-NDK與JNI除錯

測試檔案:hello-jni/src/com/example/hellojni/HelloJni.java/** * 該檔案來自 Android NDK Sample - HelloJni, 為了便於說明問題,我作了一些修改。 */ package com.example

Android系統資訊獲取 系統語言資訊獲取

Android系統的當前系統語言,可以通過Locale類獲取,主要方法:Locale.getDefault().getLanguage(),返回的是es或者zh;通過Locale.getDefault().getCountry()獲取當前國家或地區,返回為CN或US;如果當前手機設定為中文- 中國,則使

比特幣原理入門比特幣私鑰地址

在前面的文章中我們提到,在整個比特幣的系統中,有一個東西在其中扮演了非常重要的角色,就是那個神奇的錢包。關於錢包,它那麼神奇,那我呢準備在下一個視訊中再詳細介紹,在介紹錢包之前,我們先插入今天的這文章,講講比特幣的祕鑰和賬號,為下個文章錢包的隆重出場做個鋪墊。首先,大家來回憶

Spring系列SpringBean的單例構造多例構造(Maven Idea)

在使用Spring的Ioc容器建立物件會使用到單例構造或多例構造;單例構造:整個程式只有一個物件多例構造:程式中有類例項的多個物件,物件之間互相獨立首先在Test包下面建立一個User類,原始碼如下;package com.lydetails.ssm.Test; public

【OpenCV入門教程】 影象的載入,顯示輸出 一站式完全解析

毛星雲,網路ID「淺墨」,90後,熱愛遊戲開發、遊戲引擎、計算機圖形、實時渲染等技術,就職於騰訊互娛。 微軟最有價值專家 著作《Windows遊戲程式設計之從零開始》、《OpenCV3程式設計入門》 碩士就讀於南京航空航天大學航天學院(2013級碩士研究生),已於2016年三月畢業。本科

ANDROID音訊系統散記resample-2

這篇是承接上一篇提到的底層resample處理,以Samsung的tiny alsa-lib為例說明。 tiny alsa-lib 這個tiny alsa-lib位於android2.3.1-gingerbread/device/samsung/crespo/libau

Linux學習文件夾系統的結構相對(絕對)路徑

sharp 二進制 沒有 數據 csharp pan 用戶 ont 臨時 理解每個目錄的作用 bin   二進制文件 boot   系統的啟動文件、內核 dev   設備文件 etc   配置文件 home  用戶的家目錄 lib    鏈接庫文件  l

Linux學習檔案與文件系統的壓縮與打包

常用 etc 存在 filename 目錄 時有 blog 備份工具 restore 將檔案進行壓縮處理是為了使文件更加方便在網絡上傳輸以及降低硬盤使用量。進行壓縮的原理就是檔案在存儲時有很多的空間是無用的,而壓縮就是將這些空間給釋放出來。 Linux下幾種常見的壓縮方式後

Sql語法高級應用存儲過程

while 重新 begin dealloc scom 查詢信息 普通 not null lar 一、存儲過程概述   SQL Server中的存儲過程是使用T_SQL編寫的代碼段。它的目的在於能夠方便的從系統表中查詢信息,或者完成與更新數據庫表相關的管理任務和其他的系統管

Kubernetes系列部署你的第一個應用程式到k8s叢集

部署你的第一個應用程式到k8s叢集 看到這裡,求知慾飢渴難耐的你一定在想,怎麼部署的我們應用程式到叢集裡面去呢?來個簡單的,只需要兩步:(這裡本文使用nginx映象當我們的應用程式,因為nginx 簡單,執行起來後直接可以用瀏覽器訪問網頁了。) 第一步:在master 節點上建立一個

Spring Boot 系統Spring Boot 整合JdbcTemplate

前面兩篇文章我們講了兩件事情: 通過一個簡單例項進行Spring Boot 入門 修改Spring Boot 預設的服務埠號和預設context path 這篇文章我們來看下怎麼通過JdbcTemplate進行資料的持久化。 一、程式碼實現 1、修改pom.xml檔案

Android電源管理PowerManager.WakeLock原始碼詳讀

PowerManager.WakeLock 有加鎖和解鎖兩種狀態,加鎖的方式有兩種,一種是永久的鎖,這樣的鎖除非顯式的放開,是不會解鎖的,所以這種鎖用起來要非常的小心。第二種鎖是超時鎖,這種鎖會在鎖住後一段時間自動解鎖。         在建立了PowerManager.W

Android系統資訊獲取 十一獲取IMEI,IMSI號

對於移動資訊的獲取本不想再寫,只是在開發的過程中還是有同事來討論,因此就單獨再把幾個重要的資訊獲取點給列出來,供查詢使用。 IMEI號,IMSI號的知識點可參考:IMSI號和IMEI解釋 Android系統中IMSI號和IMEI號的獲取一樣可以通過TelephonyManager類來獲取,還有另外一

虛擬機器安裝XP系統圖解安裝VM Tools共享資料夾

     上一篇演示了新建虛擬機器和xp系統的安裝,本篇將是這個系列的結尾階段:安裝VM Tools 和共享資料夾。      安裝VM Tools能夠使的虛擬機器和實體機之間能夠自由拖動檔案,最重要

.NET中那些所謂的新語法系統預定義委託與Lambda表示式

開篇:在上一篇中,我們瞭解了匿名類、匿名方法與擴充套件方法等所謂的新語法,這一篇我們繼續征程,看看系統預定義委託(Action/Func/Predicate)和超愛的Lambda表示式。為了方便碼農們,.Net基類庫針對實際開發中最常用的情形提供了幾個預定義好的委託,這些委託可以直接使用,無需再重頭定義一個自

Android O 前期預研Android Vehicle HAL

Android Automotive Android Automotive 是Android Oreo中的一個新的特色功能,從AOSP的程式碼上來看,Android O中已經包含有了從Application到Framework 到HAL的整體框架,這一章節,我