Android省電開發

被問到啊哈哈哈,宛如一個智障的我為什么沒有想到!
性能優化肯定會省電啊啊啊
其他的沒說,這個怎么……

憋說了,來來來,整理一波

轉載自享受技術帶來的快樂

(一)性能優化

Android開發除了NDK之外,使用的都是Java語言,而Java語言是一種基于虛擬機JVM運行的語言,所以相比C/C++語言來說,效率是比較低的。Java需要占用大量內存來換取執行速度,而不定期的GC機制,直接導致Android界面的卡頓現象。

Java語言優化

1. 使用非阻塞I/O

版本較低的JDK不支持非阻塞I/O API。為了避免I/O阻塞,一些應用采用了創建大量線程的方法(在較好的情況下,會使用一個緩沖池)。這種技術可以在許多必須支持并發I/O流的應用中見到,如web服務器等。然而,創建Java線程需要相當可觀的開銷。Java在JDK 1.4及以后版本中提供了一套API來專門操作非阻塞I/O,我們可以再java.nio包及其子包中找到相關的類和接口。由于這套API是JDK新提供的I/O API,因此,也叫New I/O,這就是包名nio的由來。這套API由3個主要的部分組成:緩沖區(Buffers)、通道(Channels)和非阻塞I/O的核心類。

2. 慎用異常

異常對性能不利。拋出異常首先要創建一個新的對象。 Throwable 接口的構造函數調用名為 fillnStackTrace()的本地(Native)方法,fillnStackTrace() 方法檢查堆棧,收集調用跟蹤信息。只要有異常被拋出,VM就必須調整調用堆棧,因為在處理過程中創建了一個新的對象。異常只能用于錯誤處理,不應該用來控制程序流程。

3. 變量和修飾符

  • 不要重復初始化變量。默認情況下,調用類的構造函數時,Java會把變量初始化成確定的值:所有的對象被設置成null,整數變量(byte、short、int、long)設置成0,float和double設置成0.0,邏輯值設置成false。所有盡量不要重復初始化變量。

  • 盡量使用局部變量,調用方法時傳遞的參數以及在調用中創建的臨時變量都保存在棧(stack)中,速度較快,并且隨所在線程的死亡而自動銷毀。其他變量,如靜態變量、實例變量等,都在堆(heap)中創建,速度較慢,垃圾回收是的耗能會導致APP出現卡頓現象。

  • 盡量指定類的final修飾符。帶有final修飾符的類是不可派生的。另外,如果指定一個類為final,則該類所有的方法都是final。Java編譯器會尋找機會內聯(inline)所有的final方法。此舉能夠使性能平均提高50%。

  • 私有內部類要訪問外部類的field或方法時,其成員變量不要用private,因為在編譯時會生成setter/getter影響性能。可以把外部類的field或者方法聲明為包訪問權限。

  • 如果方法用不到成員變量,可以把方法申明為static,性能會提高到15%到20%。

4. 其他方面:

  • 用位操作代替乘除法;
  • 用StringBuilder代替拼接操作;
  • 對算法進行復雜度的改進(必要的時候可以空間換時間);
  • 合理利用浮點數,浮點數比整形慢兩倍。
  • 網絡優化、異步線程、圖片使用緩存等。

UI布局優化

  • 1.布局層數盡量少,RelativeLayout來代替LinearLayout,因為RelativeLayout性能更優,且可以簡單實現LinearLayout嵌套才能實現的布局。

  • 2.采用<merge>標簽優化合并布局層數:系統在編譯XML布局文件時不會為<merge>生成任何節點,通過合并可以大大減少標簽的生成。

  • 3.采用<include>標簽共享重用其他布局:并用android:id屬性覆蓋被引用布局文件中頂層節點的android:id屬性值。

<!—引用mylayout.xml文件>  
<include android:id=”@+id/layout1” layout=”@layout/mylayout”/>  
    1. <viewstub> 標簽:viewstub標簽同include標簽一樣可以用來引入一個外部布局,不同的是,viewstub引入的布局默認不會擴張,即既不會占用顯示也不會占用位置,從而在解析layout時節省cpu和內存。 <viewstub> 常用來引入那些默認不會顯示,只在特殊情況下顯示的布局,如進度布局、網絡失敗顯示的刷新布局、信息出錯出現的提示布局等。
  • 5.通過Android SDK中tools目錄下的 layoutopt 命令查看你的布局是否需要優化。

  • 6.將Activity中的window的背景圖設置為空。 .getWindow().setBackgroundDrawable(null) ;android默認的背景不為空。

  • 7.View中設置緩存屬性 lsetDrawingCache l為true。動態加載View時采用ViewStub避免一些不經常的視圖長期握住引用。

  • 8.采用SurfaceView在子線程刷新UI,避免手勢的處理和繪制在同一UI線程(普通View都這樣做)。

  • 9.ListView的優化

    • item盡可能的減少使用的控件和布局的層次;
    • RelativeLayout是絕對的利器,通過它可以減少布局的層次。
    • 盡可能的復用控件,這樣可以減少 ListView的內存使用,減少滑動時GC次數。
    • ListView的背景色與cacheColorHint設置相同顏色,可以提高滑動時的渲染性能。
    • ListView中getView是性能是關鍵,這里要盡可能的優化。
    • getView方法中要重用view;
    • getView方法中不能做復雜的邏輯計算, 特別是數據庫操作,否則會嚴重影響滑動時的性能。

數據庫優化

  1. 有些能用文件操作的,盡量使用文件操作,文件操作的速度比數據庫的操作要快10倍左右。

  2. Cursor的使用,管理好cursor,不要每次打開關閉cursor,因為打開關閉cursor非常耗時。Cursor.require用于刷cursor。同時由于SQLiteDatabase對象較為耗費資源,所以我們在使用完SQLiteDatabase對象之后,必須立即關閉它,避免它繼續占用資源,否則我們繼續程序可能會導致OOM或者其他異常。

  3. SQLite使用事務,也可以自定義事務。使用事務好處是原子提交和更優性能。

Db.beginTransaction();  
Db.setTransactionSuccessful();  
Db.end Transaction();  
  1. 可以建立索引,增加檢索的速度。(當某字段數據更新頻率較低,查詢頻率較高,經常有范圍查詢(>,<, =, >=, <=)或orderby、group by發生時建議使用索引; 經常同時存取多列,且每列都含有重復值可考慮建立復合索引)。

  2. 批量插入、更新使用原子操作,采用事務等方式。

  3. 查詢時返回更少的結果集及更少的字段

  4. 少用cursor.getColumnIndex(可以在建表的時候用static變量記住某列的index,直接調用相應index而不是每次查詢。)

  5. 優化sql語句字符串等,語句的拼接使用StringBuilder代替String。


新機制或者借用工具

  • 采用JNI技術,將耗時間的處理放到c/c++層處理。適當的采用NDK編程可以提高效率,但是內存回收不穩定,所以說適當。
  1. 懶加載和緩存機制:訪問網絡的耗時操作啟動一個新線程來做,而不要在UI線程做。還有從網絡上獲取大量圖片的時候,可以本地緩存,下次獲取同樣圖片的時候,可以先從本地獲取,本地無再從網絡獲取。

  2. 通過Android SDK中tools目錄下的layoutopt命令查看你的布局是否需要優化;hierarchy viewer可以方便的查看Activity的布局,各個View的屬性、measure、layout、draw的時間,如果耗時較多會用紅色標記,否則顯示綠色;利用android自帶的性能跟蹤工具TraceView查看跟蹤函數調用,跟蹤方法跟蹤各部分的執行效率。

  3. 可以在方法執行前后各使用system.currentTimeMillis方法獲取當前系統的時間(毫秒),兩個時間之差可以計算出方法的執行時間,進而可以改進優化。

  4. 獲取系統的內存信息

//獲取系統內存總數  
Long total = Runtime.getRuntime().totalMemory();  
//獲取剩余內存  
Long free = Runtime.getRuntime().freeMemory();  
//獲取已經使用內存  
Long used = total – free;

(二)Android省電開發之CPU降頻

轉載自Matrixxu博客專欄

在Android系統的耗電量排行里,cpu的耗電占了比較大的一部分比例,也就是說,cpu的使用率和使用頻率將直接或間接的影響電量的分配和使用,但很遺憾,android-sdk中沒有為android的開發者提供類似cpu管理的功能,但是當下很多省電類應用或專業的cpu管理軟件都提供了cpu的降頻甚至是超頻的功能,那么這樣的功能是如何實現的,本文將詳細說明在android環境下調整cpu頻率的一些方法,僅供參考。

  • CPU的工作頻率
    單位赫茲或者兆赫茲。CPU的工作頻率越高,耗電量越大,反之亦然。
  • CPU的調控模式
    英文詞為:Governor,解釋為調速器,控制器。android的framework是基于Linux平臺的,那么cpu的管理體系這塊也跟linux基本上一樣,其中包括cat命令,和一些文件的讀寫配置都是基本上差不多的。Linux在管理CPU方面,提供了如下幾種調控模式,分別為:
調控模式 效果
performance 將CPU的工作頻率調整到最大模式,讓CPU充分工作
powersave 將cpu的工作頻率調整到節能模式,這個模式下的CPU頻率最低
ondemand 定期檢查負載。當負荷超越了閾值,設置的CPU運行以最高的頻率。當負載低于相同的閾值,設置的CPU運行在下一個的最低頻率。導致更少的延遲比。ondemand從字面翻譯是“根據需求,按照需要”,cpu在工作的時候頻率會在一個最大值和最小值之間波動,當負載提高時,該調控期會自動提高cpu的頻率,反之亦然。“Causes less latency than the conservative governor.”這句話的意思是,該模式跟conservative相比,會導致更少的延遲。ok,那讓我們再看看conservative是如何解釋的。
conservative 該模式與ondemand的最大區別在于:conservative模式不會立刻在負載增加的情況下將cpu頻率調整到最大,會調整到比目前頻率稍微大的頻段去工作。所以在某種極端情況下,該模式的延遲會大于ondemand。
usersapce 將cpu的掌控權交給了用戶態,也就是交給了應用程序,應用程序可以通過配置文件的方式修改cpu的頻率信息。
public class CPUFreqSetting {  
    /** 
     * cpu cat命令大全 
     * cat [%cpuFreqPath%]/cpuinfo_cur_freq   (當前cpu頻率) 
     * cat [%cpuFreqPath%]/cpuinfo_max_freq     (最大cpu頻率) 
     * cat [%cpuFreqPath%]/cpuinfo_min_freq     (最小cpu頻率) 
     * cat [%cpuFreqPath%]/related_cpus     (cpu數量標號,從0開始,如果是雙核,結果為0,1) 
     * cat [%cpuFreqPath%]/scaling_available_frequencies    (cpu所有可用頻率) 
     * cat [%cpuFreqPath%]/scaling_available_governors  (cpu所有可用調控模式) 
     * cat [%cpuFreqPath%]/scaling_available_governors  (cpu所有可用調控模式) 
     * cat [%cpuFreqPath%]/scaling_cur_freq     (?????) 
     * cat [%cpuFreqPath%]/scaling_driver   (?????) 
     * cat [%cpuFreqPath%]/scaling_governor (?????) 
     * cat [%cpuFreqPath%]/scaling_max_freq (?????) 
     * cat [%cpuFreqPath%]/scaling_min_freq (?????) 
     * cat [%cpuFreqPath%]/scaling_setspeed (?????) 
     * cat [%cpuFreqPath%]/cpuinfo_transition_latency   (?????) 
     */  
    private final String TAG = "SetCPU";  
    private final String cpuFreqPath = "/sys/devices/system/cpu/cpu0/cpufreq";  
  
    private final static String PERFORMANCE_GOVERNOR = "performance";  
    private final static String POWER_SAVE_GOVERNOR = "performance";  
    private final static String ONDEMAND_GOVERNOR = "performance";  
    private final static String CONSERVATIVE_GOVERNOR = "performance";  
    private final static String USERSAPCE_GOVERNOR = "performance";  
  
//  public void powerSaveGovernor() {  
//      List<String> governors = readCpuGovernors();  
//      if (governors.contains(object)) {  
//  
//      }  
//  }  
  
    /** 
     * 獲得當前CPU調控模式 
     */  
    public void getCpuCurGovernor() {  
        try {  
            DataInputStream is = null;  
            Process process = Runtime.getRuntime().exec("cat " + cpuFreqPath + "/scaling_governor");  
            is = new DataInputStream(process.getInputStream());  
            String line = is.readLine();  
        } catch (IOException e) {  
            e.printStackTrace();  
        }  
    }  
  
    /** 
     * 重寫CPU調控模式 
     * @param governor 
     * @return 
     */  
    private boolean writeCpuGovernor(String governor) {  
        DataOutputStream os = null;  
        byte[] buffer = new byte[256];  
        String command = "echo " + governor + " > " + cpuFreqPath + "/scaling_governor";  
        Log.i(TAG, "command: " + command);  
        try {  
            Process process = Runtime.getRuntime().exec("su");  
            os = new DataOutputStream(process.getOutputStream());  
            os.writeBytes(command + "\n");  
            os.writeBytes("exit\n");  
            os.flush();  
            process.waitFor();  
            Log.i(TAG, "exit value = " + process.exitValue());  
        } catch (IOException e) {  
            Log.i(TAG, "writeCpuGovernor: write CPU Governor(" + governor + ") failed!");  
            return false;  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
        return true;  
    }  
  
    /** 
     * 獲得CPU所有調控模式 
     * @return 
     */  
    private List<String> readCpuGovernors() {  
        List<String> governors = new ArrayList<String>();  
        DataInputStream is = null;  
        try {  
            Process process = Runtime.getRuntime().exec("cat " + cpuFreqPath + "/scaling_available_governors");  
            is = new DataInputStream(process.getInputStream());  
            String line = is.readLine();  
  
            String[] strs = line.split(" ");  
            for (int i = 0; i < strs.length; i++)  
                governors.add(strs[i]);  
        } catch (IOException e) {  
            Log.i(TAG, "readCpuGovernors: read CPU Governors failed!");  
        }  
        return governors;  
    }  
}

(三)Android省電開發之service

Android應用開發中,難免會遇到service開發。android中服務是運行在后臺的東西,級別與activity差不多。既然說service是運行在后臺的服務,那么它就是不可見的,沒有界面的東西。Service和其他組件一樣,都是運行在主線程中,因此不能用它來做耗時的請求或者動作。你可以在服務中開一個線程,在線程中做耗時動作。

** 我們從三個方面來淺析一下service的省電開發**

  • ** 查看service是否存活以及降低優先級:**
    假如一個service工作完成,但是來不及關掉或者kill掉,用戶又看不見,所以這個service將會一直在后臺運行,勢必耗電。所以我們可以降低某些不常用service進程的優先級,在系統內存吃緊的情況下, 進程優先級低的service容易被系統kill掉。除此之外,可以利用監聽系統廣播來判斷service狀態是否存活,死亡即可手動kill掉。

  • ** 用IntentService代替Service開發:**
    普通服務一旦啟動之后,就會一直處于運行狀態,必須調用stopService()或者stopSelf()方法才能讓服務停止下來。為了簡單的創建一個異步的、會自動挺值得服務,Android專門提供了一個IntentService類。IntentService在運行完畢后自動停止,減少耗電量。

  • ** 后臺執行的定時任務Alarm機制:**
    Service沒必要一直在后臺運行,這時候的定時任務顯得很重要。
    Android的定時任務有兩種實現方式,Timer類和Alarm機制。

  • Timer有一個明顯的短板,不適合長期后臺運行的定時任務。為了能讓電池更加耐用,每種手機都會有自己的休眠策略,Android手機就會在長時間不操作的情況下自動讓CPU進入到睡眠狀態,這就有可能導致Timer中的定時任務無法正常運行。
  • Alarm機制則不存在這種情況,它具有喚醒CPU的功能,即可以保證每次需要執行定時任務的時候CPU能正常工作。

? PS:Alarm機制

從Android 4.4之后,Alarm任務的觸發時間將會變得不準確,有可能會延遲一段時間后任務才能得到執行。這不是bug,而是系統在耗電性方面進行的優化。系統會自動檢測目前有多少Alarm任務存在,然后將觸發時間將近的幾個任務放在一起執行,這就可以大幅度的減少CPU被喚醒的次數,從而有效延長電池的使用時間。

但是,Android提供了解決方案,使用AlarmManager的 setExact() 來代替 set() 方法,基本可以保證任務準時執行。

public class MyService extends Service {
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        new Thread() {
            @Override
            public void run() {
                //耗時操作
            }
        }.run();
        AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
        long triggerAtTime = SystemClock.elapsedRealtime() + 10 * 1000;
        Intent intent = new Intent(this, MyService.class);
        PendingIntent pendingIntent = PendingIntent.getService(this, 0, intent, 0);
        manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pendingIntent);
        return super.onStartCommand(intent, flags, startId);
    }
}
// 讓定時任務的觸發時間從系統開機算起,不會喚醒CPU
AlarmManager.ELAPSED_REALTIME;        
//讓定時任務的觸發時間從系統開機算起,會喚醒CPU
AlarmManager.ELAPSED_REALTIME_WAKEUP; 
 // 讓定時任務的觸發時間從1970年1月1日0點算起,不會喚醒CPU
AlarmManager.RTC;    
 // 讓定時任務的觸發時間從1970年1月1日0點算起,會喚醒CPU                
AlarmManager.RTC_WAKEUP;             
                
// 獲取系統開機至今所經歷時間的毫秒數                
SystemClock.elapsedRealtime();    
// 獲取從1970年1月1日0點至今所經歷時間的毫秒數    
SystemClock.currentThreadTimeMillis();

(四)Android省電開發之網絡

除了常規的異步網絡、多線程技術、本地緩存等等之外,Android省電開發還有一個重要的方面是: WIFI 比蜂窩數據更省電:

  • a)盡量在Wi-Fi下傳輸數據,當然這是廢話,不過可以考慮在有Wi-Fi的時候做預加載,比如應用中心的zip包、手Q web類應用的離線資源等;
  • b)非Wi-Fi下,盡量減少網絡訪問,每一次后臺交互都要考慮是否必須。雖然WiFi接入方式已經占到移動互聯網用戶的50%,但是是有些手機設置為待機關閉WiFi連接,即便有Wi-Fi信號也只能切換到蜂窩數據;

一篇博文中的數據測試
滅屏情況:滅屏傳輸,高負載download的時候WiFi最省電(70mA),3G(270mA)和2G(280mA)相當,是WiFi的4倍左右;
亮屏情況:亮屏傳輸,高負載download的時候WiFi最省電(280mA),3G(360mA)和2G(370mA)相當,是WiFi的1.3倍左右;
所以在Android應用省電開發中,我們可以在獲取網絡方式的方面加以考慮。


(五)Android省電開發之Android L5.0(ART)登場

  • 1. 默認的ART運行模式
    安卓4.4系統中引入了全新的ART模式嗎,相比之前流行已久的Dalvik模式有了很大的改變。

在Dalvik中,應用每次運行,字節碼都需要通過即時編譯器轉換為機器碼,這回拖慢應用的運行效率,而在ART環境中,應用在第一次安裝時,字節碼就會預先編譯成機器碼,使其成為了真正的本地應用。這個過程叫做預編譯。這樣的話,應用的啟動和執行都會變得更加快速。但是ART的缺點是預編譯完的機器人占用了更大的存儲空間,應用的安裝需要更長的時間。但是犧牲空間時間換取省電速度,在Android應用中還是可以接受的,畢竟性能的提升,運行速度的變快,體驗更流暢,電池續航更久顯得更重要。

  • 2.利用JobScheduler API

過去,如果開發人員想通過后臺調取服務器數據,或完成某些處理工作,應用程序必須先監聽是否有事件正在發生,并為自己設定一個喚醒時間,一旦應用程序開始運行,他需要檢查各種環境條件,以確定是否具備條件讓它完成工作,還是需要稍后再試,這種方式不僅復雜,而且容易出錯,它會不斷的浪費資源,比如當 一個應用程序被喚醒后,發現條件不符合就只能去睡覺并為下次喚醒再次設定時間,這是一個反復的過程。

這個問題,將引用 JobScheduler 來修復,它作為一個調度應用程序,負責當應用程序被喚醒時,提供適當的運行環境,所以開發者不用再讓程序檢測環境是否符合需求,開發人員只需要按照標準的流程來,調度程序會自動為喚醒的程序,準備好運行環境。應用程序可以使用這個調度程序,來喚醒他們,比如當設備連接到充電器后,調度程序將喚醒那些需要處理器工作的程序,讓他們進行工作,或者在設備連接至WiFi網絡的時候上傳下載照片,更新內容等。該調度程序還支持一個時間窗口,以便它可以喚醒一組應用程序,這將使那些不需要精確喚醒時間,但每隔一兩小時需要運行一次的程序能在同一時間點運行,這樣就能讓處理器保持更長時間的休眠。

JobScheduler 的優勢相當巨大,它不僅可以幫助手機節省電量, 實際由于不在需要監聽,更改和設置報警,還可以幫助開發人員減少代碼書寫量。目前該JobScheduler類,已經加入Android L開發者預覽版。

  • 3.各種省電新模式和新技術
    (1) 全新的Material Design風格用戶界面
    新的用戶界面更加簡潔、色彩更加豐富。動畫效果更加合理生動,同時加入實時陰影的3D視圖,更多的使用卡片風格的顯示效果。全平臺風格也變得更為統一。

    (2) 新的通知消息系統
    允許用戶管理通知中心中的通知消息。原先的Android通知欄幾乎是處于無法控制的狀態,所有的應用通知都會彈出,要想關閉只能進到每一個應用中去單獨設置,或是使用第三方軟件來實現統一管理。但是現在不用了,Android 5.0已經自帶了通知管理界面。

    (3) 多任務界面有著全新的Google Now卡片風格
    通知中心融入更多的卡片式風格,即使是在鎖屏狀態下也可以進行多種功能操作。同時用戶可以自定義通知的優先級別,使得用戶不會錯過任何重要的通知。還可以設置特定的通知權限,只有被允許的通知消息才會推送。同時還具有操作性,比如用戶在游戲時有電話打入,不會以全屏顯示,而是彈出可操作的通知卡片,用戶可選擇接聽或拒接,不影響游戲繼續進行。

    (4) Project Volta省電模式、BatterySaver省電模式
    Android 5.0的省電模式是通過 JobSchedulerAPI 以及自動調節屏幕亮度、刷新率來達到省電的效果,而且還使用了Project Volta量化每個應用的耗電量,在手機快沒電的情況下主動降低CPU的主頻或者關閉通訊模塊,以獲得更長的待機時間。還配備了一個 Battery Bistorian 電池歷史記錄功能,可讓詳細顯示設備的耗電情況。

梳理一下~

以上整理自網絡~
如有錯誤還請大家多多指教啊哈哈

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,292評論 25 708
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,923評論 18 139
  • 導讀:Android面試中高頻率出現的題都在這了。試題大部分從互聯網收集,博主下了一番功夫進行梳理總結,難免有不足...
    Maat紅飛閱讀 55,852評論 61 844
  • 6月18日,口袋記賬與OPPO可可商店合作發布獨家首發1.5.0版本,界面UI更精致,操作使用更流暢,細節設計更貼...
    小浮生閱讀 637評論 0 1
  • 1.《塵緣》 花季般的流年 墜落萬丈紅塵 青春碎了 塵緣未了 2.《城堡》 漫天的霞光 化作一道道秩序的神鏈 沖入...
    雨韓閱讀 267評論 12 7