小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

Android性能優(yōu)化之電量篇

 老匹夫 2015-06-16

android_performance_course_udacity



Google近期在Udacity上發(fā)布了Android性能優(yōu)化的在線課程,分別從渲染,運(yùn)算與內(nèi)存,電量幾個(gè)方面介紹了如何去優(yōu)化性能,這些課程是Google之前在Youtube上發(fā)布的Android性能優(yōu)化典范專題課程的細(xì)化與補(bǔ)充。



下面是電量篇章的學(xué)習(xí)筆記,部分內(nèi)容與前面的性能優(yōu)化典范有重合,歡迎大家一起學(xué)習(xí)交流!



1)Understanding Battery Drain



手機(jī)各個(gè)硬件模塊的耗電量是不一樣的,有些模塊非常耗電,而有些模塊則相對(duì)顯得耗電量小很多。



android_perf_battery_drain



電量消耗的計(jì)算與統(tǒng)計(jì)是一件麻煩而且矛盾的事情,記錄電量消耗本身也是一個(gè)費(fèi)電量的事情。唯一可行的方案是使用第三方監(jiān)測(cè)電量的設(shè)備,這樣才能夠獲取到真實(shí)的電量消耗。



當(dāng)設(shè)備處于待機(jī)狀態(tài)時(shí)消耗的電量是極少的,以N5為例,打開(kāi)飛行模式,可以待機(jī)接近1個(gè)月??墒屈c(diǎn)亮屏幕,硬件各個(gè)模塊就需要開(kāi)始工作,這會(huì)需要消耗很多電量。



使用WakeLock或者JobScheduler喚醒設(shè)備處理定時(shí)的任務(wù)之后,一定要及時(shí)讓設(shè)備回到初始狀態(tài)。每次喚醒蜂窩信號(hào)進(jìn)行數(shù)據(jù)傳遞,都會(huì)消耗很多電量,它比WiFi等操作更加的耗電。






battery_drain_radio



2)Battery Historian



Battery Historian是Android 5.0開(kāi)始引入的新API。通過(guò)下面的指令,可以得到設(shè)備上的電量消耗信息:



1
2
$ adb shell dumpsys batterystats > xxx.txt  //得到整個(gè)設(shè)備的電量消耗信息
$ adb shell dumpsys batterystats > com.package.name > xxx.txt //得到指定app相關(guān)的電量消耗信息



得到了原始的電量消耗數(shù)據(jù)之后,我們需要通過(guò)Google編寫的一個(gè)python腳本把數(shù)據(jù)信息轉(zhuǎn)換成可讀性更好的html文件:



1
$ python historian.py xxx.txt > xxx.html



打開(kāi)這個(gè)轉(zhuǎn)換過(guò)后的html文件,可以看到類似TraceView生成的列表數(shù)據(jù),這里的數(shù)據(jù)信息量很大,這里就不展開(kāi)了。



android_perf_battery_historian



3)Track Battery Status & Battery Manager



我們可以通過(guò)下面的代碼來(lái)獲取手機(jī)的當(dāng)前充電狀態(tài):



1
2
3
4
5
6
7
8
9
// It is very easy to subscribe to changes to the battery state, but you can get the current
// state by simply passing null in as your receiver.  Nifty, isn't that?
IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = this.registerReceiver(null, filter);
int chargePlug = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
boolean acCharge = (chargePlug == BatteryManager.BATTERY_PLUGGED_AC);
if (acCharge) {
    Log.v(LOG_TAG,The phone is charging!);
}



在上面的例子演示了如何立即獲取到手機(jī)的充電狀態(tài),得到充電狀態(tài)信息之后,我們可以有針對(duì)性的對(duì)部分代碼做優(yōu)化。比如我們可以判斷只有當(dāng)前手機(jī)為AC充電狀態(tài)時(shí)
才去執(zhí)行一些非常耗電的操作。



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
 * This method checks for power by comparing the current battery state against all possible
 * plugged in states. In this case, a device may be considered plugged in either by USB, AC, or
 * wireless charge. (Wireless charge was introduced in API Level 17.)
 */
private boolean checkForPower() {
    // It is very easy to subscribe to changes to the battery state, but you can get the current
    // state by simply passing null in as your receiver.  Nifty, isn't that?
    IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
    Intent batteryStatus = this.registerReceiver(null, filter);

    // There are currently three ways a device can be plugged in. We should check them all.
    int chargePlug = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
    boolean usbCharge = (chargePlug == BatteryManager.BATTERY_PLUGGED_USB);
    boolean acCharge = (chargePlug == BatteryManager.BATTERY_PLUGGED_AC);
    boolean wirelessCharge = false;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
        wirelessCharge = (chargePlug == BatteryManager.BATTERY_PLUGGED_WIRELESS);
    }
    return (usbCharge || acCharge || wirelessCharge);
}



4)Wakelock and Battery Drain



高效的保留更多的電量與不斷促使用戶使用你的App會(huì)消耗電量,這是矛盾的選擇題。不過(guò)我們可以使用一些更好的辦法來(lái)平衡兩者。



假設(shè)你的手機(jī)里面裝了大量的社交類應(yīng)用,即使手機(jī)處于待機(jī)狀態(tài),也會(huì)經(jīng)常被這些應(yīng)用喚醒用來(lái)檢查同步新的數(shù)據(jù)信息。Android會(huì)不斷關(guān)閉各種硬件來(lái)延長(zhǎng)手機(jī)的待機(jī)時(shí)間,首先屏幕會(huì)逐漸變暗直至關(guān)閉,然后CPU進(jìn)入睡眠,這一切操作都是為了節(jié)約寶貴的電量資源。但是即使在這種睡眠狀態(tài)下,大多數(shù)應(yīng)用還是會(huì)嘗試進(jìn)行工作,他們將不斷的喚醒手機(jī)。一個(gè)最簡(jiǎn)單的喚醒手機(jī)的方法是使用PowerManager.WakeLock的API來(lái)保持CPU工作并防止屏幕變暗關(guān)閉。這使得手機(jī)可以被喚醒,執(zhí)行工作,然后回到睡眠狀態(tài)。知道如何獲取WakeLock是簡(jiǎn)單的,可是及時(shí)釋放WakeLock也是非常重要的,不恰當(dāng)?shù)氖褂肳akeLock會(huì)導(dǎo)致嚴(yán)重錯(cuò)誤。例如網(wǎng)絡(luò)請(qǐng)求的數(shù)據(jù)返回時(shí)間不確定,導(dǎo)致本來(lái)只需要10s的事情一直等待了1個(gè)小時(shí),這樣會(huì)使得電量白白浪費(fèi)了。這也是為何使用帶超時(shí)參數(shù)的wakelock.acquice()方法是很關(guān)鍵的。



但是僅僅設(shè)置超時(shí)并不足夠解決問(wèn)題,例如設(shè)置多長(zhǎng)的超時(shí)比較合適?什么時(shí)候進(jìn)行重試等等?解決上面的問(wèn)題,正確的方式可能是使用非精準(zhǔn)定時(shí)器。通常情況下,我們會(huì)設(shè)定一個(gè)時(shí)間進(jìn)行某個(gè)操作,但是動(dòng)態(tài)修改這個(gè)時(shí)間也許會(huì)更好。例如,如果有另外一個(gè)程序需要比你設(shè)定的時(shí)間晚5分鐘喚醒,最好能夠等到那個(gè)時(shí)候,兩個(gè)任務(wù)捆綁一起同時(shí)進(jìn)行,這就是非精確定時(shí)器的核心工作原理。我們可以定制計(jì)劃的任務(wù),可是系統(tǒng)如果檢測(cè)到一個(gè)更好的時(shí)間,它可以推遲你的任務(wù),以節(jié)省電量消耗。



alarmmanager_inexact_wakelock



這正是JobScheduler API所做的事情。它會(huì)根據(jù)當(dāng)前的情況與任務(wù),組合出理想的喚醒時(shí)間,例如等到正在充電或者連接到WiFi的時(shí)候,或者集中任務(wù)一起執(zhí)行。我們可以通過(guò)這個(gè)API實(shí)現(xiàn)很多免費(fèi)的調(diào)度算法。



5)Network and Battery Drain



下面內(nèi)容來(lái)自官方Training文檔中高效下載章節(jié)關(guān)于手機(jī)(Radio)蜂窩信號(hào)對(duì)電量消耗的介紹。



通常情況下,使用3G移動(dòng)網(wǎng)絡(luò)傳輸數(shù)據(jù),電量的消耗有三種狀態(tài):




  • Full power: 能量最高的狀態(tài),移動(dòng)網(wǎng)絡(luò)連接被激活,允許設(shè)備以最大的傳輸速率進(jìn)行操作。

  • Low power: 一種中間狀態(tài),對(duì)電量的消耗差不多是Full power狀態(tài)下的50%。

  • Standby: 最低的狀態(tài),沒(méi)有數(shù)據(jù)連接需要傳輸,電量消耗最少。




下圖是一個(gè)典型的3G Radio State Machine的圖示(來(lái)自AT&T,詳情請(qǐng)點(diǎn)擊這里):



mobile_radio_state_machine.png



總之,為了減少電量的消耗,在蜂窩移動(dòng)網(wǎng)絡(luò)下,最好做到批量執(zhí)行網(wǎng)絡(luò)請(qǐng)求,盡量避免頻繁的間隔網(wǎng)絡(luò)請(qǐng)求。



通過(guò)前面學(xué)習(xí)到的Battery Historian我們可以得到設(shè)備的電量消耗數(shù)據(jù),如果數(shù)據(jù)中的移動(dòng)蜂窩網(wǎng)絡(luò)(Mobile Radio)電量消耗呈現(xiàn)下面的情況,間隔很小,又頻繁斷斷續(xù)續(xù)的出現(xiàn),說(shuō)明電量消耗性能很不好:



android_perf_battery_bad.png



經(jīng)過(guò)優(yōu)化之后,如果呈現(xiàn)下面的圖示,說(shuō)明電量消耗的性能是良好的:



android_perf_battery_good



另外WiFi連接下,網(wǎng)絡(luò)傳輸?shù)碾娏肯囊纫苿?dòng)網(wǎng)絡(luò)少很多,應(yīng)該盡量減少移動(dòng)網(wǎng)絡(luò)下的數(shù)據(jù)傳輸,多在WiFi環(huán)境下傳輸數(shù)據(jù)。



android_perf_battery_wifi



那么如何才能夠把任務(wù)緩存起來(lái),做到批量化執(zhí)行呢?下面就輪到Job Scheduler出場(chǎng)了。



6)Using Job Scheduler



使用Job Scheduler,應(yīng)用需要做的事情就是判斷哪些任務(wù)是不緊急的,可以交給Job Scheduler來(lái)處理,Job Scheduler集中處理收到的任務(wù),選擇合適的時(shí)間,合適的網(wǎng)絡(luò),再一起進(jìn)行執(zhí)行。



下面是使用Job Scheduler的一段簡(jiǎn)要示例,需要先有一個(gè)JobService:



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
public class MyJobService extends JobService {
    private static final String LOG_TAG = "MyJobService";

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(LOG_TAG, "MyJobService created");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i(LOG_TAG, "MyJobService destroyed");
    }

    @Override
    public boolean onStartJob(JobParameters params) {
        // This is where you would implement all of the logic for your job. Note that this runs
        // on the main thread, so you will want to use a separate thread for asynchronous work
        // (as we demonstrate below to establish a network connection).
        // If you use a separate thread, return true to indicate that you need a "reschedule" to
        // return to the job at some point in the future to finish processing the work. Otherwise,
        // return false when finished.
        Log.i(LOG_TAG, "Totally and completely working on job " + params.getJobId());
        // First, check the network, and then attempt to connect.
        if (isNetworkConnected()) {
            new SimpleDownloadTask() .execute(params);
            return true;
        } else {
            Log.i(LOG_TAG, "No connection on job " + params.getJobId() + "; sad face");
        }
        return false;
    }

    @Override
    public boolean onStopJob(JobParameters params) {
        // Called if the job must be stopped before jobFinished() has been called. This may
        // happen if the requirements are no longer being met, such as the user no longer
        // connecting to WiFi, or the device no longer being idle. Use this callback to resolve
        // anything that may cause your application to misbehave from the job being halted.
        // Return true if the job should be rescheduled based on the retry criteria specified
        // when the job was created or return false to drop the job. Regardless of the value
        // returned, your job must stop executing.
        Log.i(LOG_TAG, "Whelp, something changed, so I'm calling it on job " + params.getJobId());
        return false;
    }

    /**
     * Determines if the device is currently online.
     */
    private boolean isNetworkConnected() {
        ConnectivityManager connectivityManager =
                (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
        return (networkInfo != null && networkInfo.isConnected());
    }

    /**
     *  Uses AsyncTask to create a task away from the main UI thread. This task creates a
     *  HTTPUrlConnection, and then downloads the contents of the webpage as an InputStream.
     *  The InputStream is then converted to a String, which is logged by the
     *  onPostExecute() method.
     */
    private class SimpleDownloadTask extends AsyncTask<JobParameters, Void, String> {

        protected JobParameters mJobParam;

        @Override
        protected String doInBackground(JobParameters... params) {
            // cache system provided job requirements
            mJobParam = params[0];
            try {
                InputStream is = null;
                // Only display the first 50 characters of the retrieved web page content.
                int len = 50;

                URL url = new URL("https://www.google.com");
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setReadTimeout(10000); //10sec
                conn.setConnectTimeout(15000); //15sec
                conn.setRequestMethod("GET");
                //Starts the query
                conn.connect();
                int response = conn.getResponseCode();
                Log.d(LOG_TAG, "The response is: " + response);
                is = conn.getInputStream();

                // Convert the input stream to a string
                Reader reader = null;
                reader = new InputStreamReader(is, "UTF-8");
                char[] buffer = new char[len];
                reader.read(buffer);
                return new String(buffer);

            } catch (IOException e) {
                return "Unable to retrieve web page.";
            }
        }

        @Override
        protected void onPostExecute(String result) {
            jobFinished(mJobParam, false);
            Log.i(LOG_TAG, result);
        }
    }
}



然后模擬通過(guò)點(diǎn)擊Button觸發(fā)N個(gè)任務(wù),交給JobService來(lái)處理



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
public class FreeTheWakelockActivity extends ActionBarActivity {
    public static final String LOG_TAG = "FreeTheWakelockActivity";

    TextView mWakeLockMsg;
    ComponentName mServiceComponent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_wakelock);

        mWakeLockMsg = (TextView) findViewById(R.id.wakelock_txt);
        mServiceComponent = new ComponentName(this, MyJobService.class);
        Intent startServiceIntent = new Intent(this, MyJobService.class);
        startService(startServiceIntent);

        Button theButtonThatWakelocks = (Button) findViewById(R.id.wakelock_poll);
        theButtonThatWakelocks.setText(R.string.poll_server_button);

        theButtonThatWakelocks.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                    pollServer();
            }
        });
    }

    /**
     * This method polls the server via the JobScheduler API. By scheduling the job with this API,
     * your app can be confident it will execute, but without the need for a wake lock. Rather, the
     * API will take your network jobs and execute them in batch to best take advantage of the
     * initial network connection cost.
     *
     * The JobScheduler API works through a background service. In this sample, we have
     * a simple service in MyJobService to get you started. The job is scheduled here in
     * the activity, but the job itself is executed in MyJobService in the startJob() method. For
     * example, to poll your server, you would create the network connection, send your GET
     * request, and then process the response all in MyJobService. This allows the JobScheduler API
     * to invoke your logic without needed to restart your activity.
     *
     * For brevity in the sample, we are scheduling the same job several times in quick succession,
     * but again, try to consider similar tasks occurring over time in your application that can
     * afford to wait and may benefit from batching.
     */
    public void pollServer() {
        JobScheduler scheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
        for (int i=0; i<10; i++) {
            JobInfo jobInfo = new JobInfo.Builder(i, mServiceComponent)
                    .setMinimumLatency(5000) // 5 seconds
                    .setOverrideDeadline(60000) // 60 seconds (for brevity in the sample)
                    .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) // WiFi or data connections
                    .build();

            mWakeLockMsg.append("Scheduling job " + i + "!\n");
            scheduler.schedule(jobInfo);
        }
    }
}



Notes:關(guān)于更多電量?jī)?yōu)化,還有一篇文章,請(qǐng)點(diǎn)擊這里


    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多