Battery Historian based on V2.0Battery Historian installation and use

The horizontal coordinate is a time range. In our example, the statistical data starts from reset and ends at the time when the bugreport content is obtained. How long have we collected data

(2). The vertical

Key data points are summarized in a table.

Parameter names role
CPU running Running status of the CPU and whether it is woken up. If you hover your mouse over it, you can see more information, such as why the CPU wakes up.
Screen When the screen is on, it can be seen that the coloring of this item in the chart has an interval, because I turned off the screen during the experiment, and the coloring was interrupted every time I turned off the screen.
Top app The current topmost app
Mobile network type Network type, which needs to be noted, “free network may include wifi, Bluetooth network sharing, USB network sharing”
Mobile radio active Mobile cellular signal BP side power consumption, usually refers to SIM card, data link. This column has too many colors and too many intervals. It means the power consumption is also high.
WiFi supplicant Wifi enabled or not
WiFi signal strength The intensity of wifi
Wifi Running Power consumption under wifi connection
Audio Audio enabled or not
Battery Level electricity
Plugged Whether it is charging, and the charging type can be seen when the mouse is over it, including AC (charger), USB, and others (such as wireless charging).
Battery Level The charge at the beginning of the test, as you can see from the graph I grabbed before, is 100, full charge.
Top app The foreground application, if you want to analyze the power consumption of the application, should always be in the foreground during testing.
Userspace wakelock Record the working time of the wake_lock module

Ps: In order to save power, the CPU will automatically sleep when there is no busy task. Wake_lock is added to the CPU when a task needs to be woken up for efficient execution.

Electric quantity optimization suggestions:

When the Android device is idle, the screen darkens, turns off the screen, and finally stops the CPU, which prevents the battery from draining too quickly. During hibernation, custom timers, handlers, threads, and services are suspended. But sometimes we need to change the Android system’s default state: for example, when playing a game, we need to keep the screen on, for example, some download operations do not require the screen to be on but require the CPU to run until the task is complete. So you don’t have to use any more power for the moment of arousal.

1. Judge the charging state

Here we need to think, according to our own business, in order to save electricity, can be put when the phone is plugged in to do. This is often the case. Actions such as these that do not require immediate user interaction can be dealt with later. For example, 360 mobile phone assistant will automatically clean up mobile phone garbage and automatically back up and upload pictures and contacts to the cloud only when it is charged. Another example is our own APP, which has a service of album backup. At this time, there is an option control to let users choose whether to continue to backup when the battery is lower than 15%, so as to avoid continuing to consume power when the battery is low.

We can obtain the current charging status of the phone by using the following code:


// 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!" ); }Copy the code

  private boolean checkForPower(a) {
        // Get the charging status of the battery (register a broadcast)
        IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
        Intent res = this.registerReceiver(null, filter);

        // Determine the charging status by using BatteryManager parameters
        if(res ! =null) {
            int chargePlug = res.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
            boolean usb = chargePlug == BatteryManager.BATTERY_PLUGGED_USB;/ / usb charging
            boolean ac = chargePlug == BatteryManager.BATTERY_PLUGGED_AC;/ / ac
            // Wireless charging, this requires API>=17
            boolean wireless = false;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                wireless = chargePlug == BatteryManager.BATTERY_PLUGGED_WIRELESS;
            }
            return (usb || ac || wireless);
        } else {
            return false; }}Copy the code

Invoke the sample


    private void applyFilter(a) {
        // Whether the battery is being charged
        if(! checkForPower()){ mPowerMsg.setText("Please charge it up and do it again!");
            return;
        }
        mCheyennePic.setImageResource(R.drawable.pink_cheyenne);
        mPowerMsg.setText(R.string.photo_filter);
    }
Copy the code

2. Keep the screen steady on

In order to avoid excessive power consumption when the screen wakes up, some applications, such as games and payment pages, need to keep the screen on to save power:


getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
Copy the code

It can also be used in layout files, but with less flexibility:


android:keepScreenOn="true"
Copy the code

Note: There is no need to manually remove the FLAG_KEEP_SCREEN_ON flag, windowManager will manage the application into the background and back into the foreground. If you really need to manually clear the flag that is on, run the


getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
Copy the code
Android :keepScreenOn = “true” works the same as FLAG_KEEP_SCREEN_ON. The advantage of using code is that you allow you to close the screen where you need to.

Note: There is no need to manually remove the FLAG_KEEP_SCREEN_ON flag, windowManager will manage the application into the background and back into the foreground. If you really need to manually clear out normally on flag, use getWindow (). ClearFlags (WindowManager. LayoutParams. FLAG_KEEP_SCREEN_ON)

3.1 use wake_lock

To save power, the CPU automatically goes to sleep when no task is busy. Wake_lock is added to the CPU when a task needs to be woken up for efficient execution. The wake_lock lock is related to the sleep of the system. It means that my program will not sleep if I add this lock to the CPU. The purpose of this lock is to fully cooperate with the operation of our program. In some cases, failure to do so can lead to problems, such as the heartbeat packets of instant communication, such as wechat, stopping Internet access shortly after the screen goes down. Therefore, wake_lock is widely used in wechat. PowerManager The Wake locks feature of the system service to keep the CPU awake. Wakeup locks allow an application to control the power status of the host device. Creating and holding a wakeup lock has a significant impact on battery life, so use it only when you really need it to complete tasks that can be done in the background in the shortest possible time. For example in the Acitivity you don’t have to use it. A typical example is a background service that keeps the CPU running after the screen is off. If you do not use wakeup locks to perform background services, there is no guarantee that tasks will stop at some point in the future due to CPU sleep, which is not what we want. Some people may think that the background service has not dropped the chain before it runs well, 1. Maybe you have a short assignment; 2. Maybe the CPU is being woken up by many other applications in the phone). Wakeup locks have the following types:

Wake_lock two types of locks (from the point of view of release and use) : count locks and non-count locks (locks that are locked many times and can be released only once)
Please note that starting with API level 17, FULL_WAKE_LOCK is deprecated and should be FLAG_KEEP_SCREEN_ON instead.

In order to prevent the CPU from waking up and consuming too much power, we need to use wakelock to save power to prevent the CPU from sleeping when executing critical code:

**

// Create a wakeup lock
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "partial_lock");

// Get the wakeup lock
wakeLock.acquire();

// Some key code

// Release wakelock
wakeLock.release();
Copy the code

Need to add permissions:


<uses-permission android:name="android.permission.WAKE_LOCK"/>
Copy the code

Tips: need paired with wake lock release for Tips: there are some unexpected situation, such as millet mobile phone is made synchronous heartbeat packets aligned (heart) (if more than the synchronous frequency would be to block or frequency), all of the app backstage awakening frequency can’t is too high, then you need to drop the frequency, such as in every other 2 s request.

3.2. Use WakefulBroadcastReceiver

As mentioned above, the typical usage scenario is for a background Service to keep the CPU running, but WakefulBroadcastReceiver is recommended: a combination of broadcast and Service (typically IntentService) allows you to manage the life cycle of the background Service.

WakefulBroadcastReceiver is a special case of BroadcastReceiver. It creates and manages a WakeLock of type PARTIAL_WAKE_LOCK for your APP. A WakeBroadcastReceiver receives the broadcast and passes the work to a Service (typically an IntentService) until it is sure that the device is not sleeping. If you don’t keep a wakeup lock when handing off work to a service and allow the device to sleep before the work is done, you will have some undesirable situations.


public class MyIntentService extends IntentService {

    public MyIntentService(String name) {
        super(name);
    }

    public MyIntentService(a) {
        super(MyIntentService.class.getSimpleName());
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        if(intent ! =null) {
            // Get parameters
            Bundle extras = intent.getExtras();

            // Execute some code that requires the CPU to stay awake

            // The wakeup lock is releasedMyWakefulReceiver.completeWakefulIntent(intent); }}}Copy the code

Broadcast Receiver:


public class MyWakefulReceiver extends WakefulBroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Intent service = newIntent(context, MyIntentService.class); startWakefulService(context, service); }}Copy the code

When you need to use the service, do so in the usual way:


Intent intent = new Intent(this, MyIntentService.class);
// Pass parameters
intent.setData(Uri.parse("xxx"));
Copy the code
WakefulBroadcastReceiver (WakefulBroadcastReceiver)

public abstract class WakefulBroadcastReceiver extends BroadcastReceiver {
    private static final String EXTRA_WAKE_LOCK_ID = "android.support.content.wakelockid";

    private static final SparseArray<PowerManager.WakeLock> mActiveWakeLocks
            = new SparseArray<PowerManager.WakeLock>();
    private static int mNextId = 1;

    /**
     * Do a {@link android.content.Context#startService(android.content.Intent)
     * Context.startService}, but holding a wake lock while the service starts.
     * This will modify the Intent to hold an extra identifying the wake lock;
     * when the service receives it in {@link android.app.Service#onStartCommand
     * Service.onStartCommand}, it should pass back the Intent it receives there to
     * {@link #completeWakefulIntent(android.content.Intent)} in order to release
     * the wake lock.
     *
     * @param context The Context in which it operate.
     * @param intent The Intent with which to start the service, as per
     * {@link android.content.Context#startService(android.content.Intent)
     * Context.startService}.
     */
    public static ComponentName startWakefulService(Context context, Intent intent) {
        synchronized (mActiveWakeLocks) {
            int id = mNextId;
            mNextId++;
            if (mNextId <= 0) {
                mNextId = 1;
            }

            intent.putExtra(EXTRA_WAKE_LOCK_ID, id);
            ComponentName comp = context.startService(intent);
            if (comp == null) {
                return null;
            }

            PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
            PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
                    "wake:" + comp.flattenToShortString());
            wl.setReferenceCounted(false);
            wl.acquire(60*1000);
            mActiveWakeLocks.put(id, wl);
            returncomp; }}/**
     * Finish the execution from a previous {@link #startWakefulService}.  Any wake lock
     * that was being held will now be released.
     *
     * @param intent The Intent as originally generated by {@link #startWakefulService}.
     * @return Returns true if the intent is associated with a wake lock that is
     * now released; returns false if there was no wake lock specified for it.
     */
    public static boolean completeWakefulIntent(Intent intent) {
        final int id = intent.getIntExtra(EXTRA_WAKE_LOCK_ID, 0);
        if (id == 0) {
            return false;
        }
        synchronized (mActiveWakeLocks) {
            PowerManager.WakeLock wl = mActiveWakeLocks.get(id);
            if(wl ! =null) {
                wl.release();
                mActiveWakeLocks.remove(id);
                return true;
            }
            // We return true whether or not we actually found the wake lock
            // the return code is defined to indicate whether the Intent contained
            // an identifier for a wake lock that it was supposed to match.
            // We just log a warning here if there is no wake lock found, which could
            // happen for example if this function is called twice on the same
            // intent or the process is killed and restarted before processing the intent.
            Log.w("WakefulBroadcastReceiver"."No active wake lock id #" + id);
            return true; }}}Copy the code
WakefulBroadcastReceiver is essentially based on PowerManager.wakelock
Note:
1. Add permissions
2. Pay attention to service and broadcast registration
3. Use broadcasts to design for decoupling

Some problems collected online and solved are as follows:

1. The code polling to the server is not executed.

I used to make an application that used Timer and TimerTask to set up periodic polling for the server, but found that polling stopped after the machine was in a certain period of time. It took a long time before it turned out to be dormancy. A later solution was to use the system’s AlarmService to perform polling. Although the system puts the machine to sleep to save electricity, it is not completely shut down. Some programs with high priority are still running in the system, such as alarm clock. AlarmService can be used to start its own program periodically, make the CPU start up, and then sleep after execution.

Solution: Use AlarmService to perform polling instead of Timer and TimerTask
2. The rear platform manager is disconnected.

Recent problems. Use the Socket long connection to achieve QQ similar chat function, found that after the screen is off for a period of time, the Socket is disconnected. When the screen is on, it needs to be reconnected, but every time you look at the Log, you find that the network is connected. Later, it is found that the LINK is disconnected due to CPU sleep. When you plug in the data line to look at the Log, the network CPU recovers, and you see that the network is indeed connected. Finally, PARTIAL_WAKE_LOCK is used to keep the CPU from sleeping.

Solution: PARTIAL_WAKE_LOCK is used to keep the CPU from sleeping.
3. It does not sleep during debugging.

To my dismay, when DEBUGGING 2, I found that sometimes the Socket would break and sometimes it would not break. I later figured out that sometimes I was debugging with the data cable plugged in and sometimes unplugged, and the sleep state of Android was different. And different machines also have different performance, such as some machines, plug in the data line will charge, some will not, some of the machine set when charging screen is not dark and so on, make yourself confused. In fact, understand this sleep mechanism, everything is easy.

The Wakelock Detector (WLD) software can see the Wakelock in the phone:

3.3. A large number of high frequency CPU wakes and operations use JobScheduler/GCM

Since the release of Android 5.0, JobScheduler has become the preferred way to perform background work in a user-friendly way. Applications can schedule jobs while allowing the system to be optimized based on memory, power, and connectivity. The purpose of JobSchedule is to batch less urgent tasks at a more appropriate time. This has two advantages:

Avoid frequent awakenings of hardware modules, resulting in unnecessary power consumption. Avoid performing too many tasks at inappropriate times (e.g. under low battery conditions, weak network or mobile network conditions) and consuming too much power;

A simple use of JobScheduler starts with a custom Service class that inherits from JobService


public class JobSchedulerService extends JobService{
    private String TAG = JobSchedulerService.class.getSimpleName();

    @Override
    public boolean onStartJob(JobParameters jobParameters) {
        Log.d(TAG, "onStartJob:" + jobParameters.getJobId());

        if(true) {
            // The JobService is running on the main thread. If we need to process some time-consuming business logic, we need to start a separate thread to process it and return true.
            // Notify the system when the task is complete by calling jobFinished(JobParameters Params, Boolean needsRescheduled).

            // Suppose you start a thread to download the file
            new DownloadTask().execute(jobParameters);

            return true;

        }else {
            Return false if you just perform some simple logic in this method
            return false; }}/** * For example, if the constraint of our service is set to run in WIFI state, the system will notify us to stop running by calling onStopJob(). This method will not be returned under normal circumstances **@param jobParameters
     * @return* /
    @Override
    public boolean onStopJob(JobParameters jobParameters) {
        Log.d(TAG, "onStopJob:" + jobParameters.getJobId());

        Return true if the required service is executed again when the specified convention condition is met, false otherwise
        return true;
    }

    class DownloadTask extends AsyncTask<JobParameters.Object.Object> {
        JobParameters mJobParameters;

        @Override
        protected Object doInBackground(JobParameters... jobParameterses) {
            mJobParameters = jobParameterses[0];

            // Let's say we're dealing with a download task
            // Or to handle some more complex operation logic
            / /...

            try {
                Thread.sleep(30*1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            return null;
        }

        @Override
        protected void onPostExecute(Object o) {
            super.onPostExecute(o);
            // If onStartJob() returns true, be sure to execute jobFinished() after processing the logic to tell the system that it is finished,
            // If the service needs to be rescheduled please true, otherwise false
            jobFinished(mJobParameters, false); }}}Copy the code

Remember to configure the Service in the Manifest file


 <service android:name=".JobSchedulerService" android:permission="android.permission.BIND_JOB_SERVICE"/>
Copy the code

Create a Work Plan


public class MainActivity extends Activity{
    private JobScheduler mJobScheduler;
    private final int JOB_ID = 1;

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

        mJobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE );

        // Use jobinfo. Builder to set the constraint that triggers the service, at least one condition
        JobInfo.Builder jobBuilder = new JobInfo.Builder(JOB_ID, new ComponentName(this, JobSchedulerService.class));

        // Trigger a loop to set the task to run periodically every three seconds
        jobBuilder.setPeriodic(3000);

        // Single timer trigger, set to trigger after 3 seconds. This is incompatible with setPeriodic(long time),
        // And an exception will be thrown if both functions are used together.
        jobBuilder.setMinimumLatency(3000);

        // The trigger will start after 3 seconds if all conditions set in the specified time are not triggered. Similar to setMinimumLatency(long time),
        // This function is mutually exclusive with setPeriodic(long time) and will throw an exception if both functions are used together.
        jobBuilder.setOverrideDeadline(3000);

        // Whether the trigger condition set after the device is restarted is still valid
        jobBuilder.setPersisted(false);

        It fires only when the device is in a specific network state.
        // JobInfo.NETWORK_TYPE_NONE, which can be triggered whether there is a network or not, is the default value;
        // JobInfo.NETWORK_TYPE_ANY, triggered when there is a network connection;
        // JobInfo.NETWORK_TYPE_UNMETERED, not triggered in cellular networks;
        // JobInfo.NETWORK_TYPE_NOT_ROAMING, this task can be triggered only in non-roaming networks.
        jobBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);


         /** Sets the retry/fallback policy, and what measure to take when a task fails to be scheduled. InitialBackoffMillis: wait interval for the first retry ms *backoffPolicy: corresponding retreat policy. Wait times, for example, grow exponentially. * /
      // jobBuilder .setBackoffCriteria(long initialBackoffMillis, int backoffPolicy)
        jobBuilder .setBackoffCriteria(JobInfo.MAX_BACKOFF_DELAY_MILLIS, JobInfo.BACKOFF_POLICY_LINEAR)
        // Set the trigger when the phone is charging
        jobBuilder.setRequiresCharging(true);

        // Set the trigger when the phone is idle
        jobBuilder.setRequiresDeviceIdle(true);

        // Get the JobInfo object
        JobInfo jobInfo = jobBuilder.build();

        // Set to start scheduling tasks, which will return a status code
        / / JobScheduler RESULT_SUCCESS, success
        / / JobScheduler RESULT_FAILURE, failure
        if (mJobScheduler.schedule(jobInfo) == JobScheduler.RESULT_FAILURE) {
            // Failed to schedule the task
        }

        // Stop the working service for the specified JobId
        mJobScheduler.cancel(JOB_ID);
        // Stop all working services
        mJobScheduler.cancelAll();
    }
Copy the code

High frequency CPU wake up and operations, we better concentrate these operations. There are algorithms we can use to solve this. JobScheduler/GCM is the quintessence of Google.

4. Use AlarmManager to wake up

When the machine does not operate for a period of time, it will enter a sleep state. Polling to the server will stop and long connections will be disconnected. To prevent this, use AlarmManager:


Intent intent = new Intent(this, TestService.class);
PendingIntent pi = PendingIntent.getService(this.0, intent, 0);

AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
am.cancel(pi);

// The alarm clock will wake up the system and perform the prompt function when the system is asleep
// Fuzzy time, in API-19 and before, setRepeating is inaccurate
am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 1000.2000, pi);
// Exact time, but need to be used after API-17
am.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 1000, pi);
am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 1000, pi);
Copy the code

This timer can start services, send broadcasts, jump activities, and wake up the system while it is sleeping. Therefore, this method does not need to obtain and release the power lock. Refer to other articles for more information about AlarmManager. In versions 19 and above, the frequency set in setRepeating is recommended (60s is the minimum in the 6.0 source code). Use setWindow or setExact to be more precise.

5. Other optimizations

Of course, battery optimization includes many aspects, such as:

Rendering optimization
Optimization of positioning strategy
Network optimization, such as network cache processing, request mode, times optimization, setting timeout time and so on
Code execution efficiency optimization
Preventing memory leaks
Wait, battery optimization is everywhere.

deepen

First, Android phones have two processors, one is called Application Processor (AP) and the other is called Baseband Processor (BP). AP is a processor based on ARM architecture, used to run Linux+Android system. BP is used to run real-time operating system (RTOS), and the communication protocol stack runs on BP RTOS. Non-talk time, the energy consumption of BP is basically around 5mA, while the energy consumption of AP is at least over 50mA as long as the AP is in non-sleep state, and the graph calculation will be higher. In addition, LCD works at about 100mA power consumption, WIFI is also about 100mA. Generally, when the phone is on standby, the AP, LCD, and WIFI go to sleep, and the Android application code stops executing.
To ensure the correct execution of critical code in an application, Android provides the Wake Lock API, which gives an application the right to prevent an AP from going to sleep through code. But if you misunderstand the Android designers and misuse the Wake Lock API, preventing the AP from going to sleep for long periods of time in order for your application to work properly in the background, you can be a stand-by battery killer. Let’s say some app that was a while ago, let’s say some app that’s still doing this.

AlarmManager is a module packaged by the Android operating system to manage the RTC. The Real Time Clock (RTC) is an independent hardware Clock that runs normally when the CPU is hibernated and wakes up the CPU by interrupting when the preset Time arrives. (This is what aurora push does.)

Conclusion:

1. Wake Lock is required to protect the execution process of key logic. Such as disconnection and re-login
2. How to wake up to perform tasks when sleeping? Use AlarmManager. For example, get push messages

Other resources: Problem with alarmManager failing to wake up Service while the phone is asleep? (Some companies have developed heartbeat alignment to combat “rogue” apps that wake up frequently.) www.zhihu.com/question/36… Why does wechat Android version 6.2 set a large number of random wake-up locks for a long time? www.zhihu.com/question/31…