Android Battery 开发(三) BatteryService

android 4.4 电池电量管理底层分析(C\C++层) (http://blog.csdn.net/daweibalang717/article/details/41446993)

先贴一张类与类之间的关系图:


    android开机过程中会加载系统BatteryService ,说一下电池电量相关的,本文主要讲述关于JAVA 层代码。文件路径:\frameworks\base\services\java\com\android\server\BatteryService.java   下面贴出源码。我把注释加上。个人理解,仅参考。

 
[java]  view plain  copy
  1. /* 
  2.  * Copyright (C) 2006 The Android Open Source Project 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  *      http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */  
  16.   
  17. package com.android.server;  
  18.   
  19. import android.os.BatteryStats;  
  20. import com.android.internal.app.IBatteryStats;  
  21. import com.android.server.am.BatteryStatsService;  
  22.   
  23. import android.app.ActivityManagerNative;  
  24. import android.content.ContentResolver;  
  25. import android.content.Context;  
  26. import android.content.Intent;  
  27. import android.content.pm.PackageManager;  
  28. import android.os.BatteryManager;  
  29. import android.os.BatteryProperties;  
  30. import android.os.Binder;  
  31. import android.os.FileUtils;  
  32. import android.os.Handler;  
  33. import android.os.IBatteryPropertiesListener;  
  34. import android.os.IBatteryPropertiesRegistrar;  
  35. import android.os.IBinder;  
  36. import android.os.DropBoxManager;  
  37. import android.os.RemoteException;  
  38. import android.os.ServiceManager;  
  39. import android.os.SystemClock;  
  40. import android.os.UEventObserver;  
  41. import android.os.UserHandle;  
  42. import android.provider.Settings;  
  43. import android.util.EventLog;  
  44. import android.util.Slog;  
  45.   
  46. import java.io.File;  
  47. import java.io.FileDescriptor;  
  48. import java.io.FileOutputStream;  
  49. import java.io.IOException;  
  50. import java.io.PrintWriter;  
  51.   
  52.   
  53. /** 
  54.  * <p>BatteryService monitors the charging status, and charge level of the device 
  55.  * battery.  When these values change this service broadcasts the new values 
  56.  * to all {@link android.content.BroadcastReceiver IntentReceivers} that are 
  57.  * watching the {@link android.content.Intent#ACTION_BATTERY_CHANGED 
  58.  * BATTERY_CHANGED} action.</p> 
  59.  * <p>The new values are stored in the Intent data and can be retrieved by 
  60.  * calling {@link android.content.Intent#getExtra Intent.getExtra} with the 
  61.  * following keys:</p> 
  62.  * <p>"scale" - int, the maximum value for the charge level</p> 
  63.  * <p>"level" - int, charge level, from 0 through "scale" inclusive</p> 
  64.  * <p>"status" - String, the current charging status.<br /> 
  65.  * <p>"health" - String, the current battery health.<br /> 
  66.  * <p>"present" - boolean, true if the battery is present<br /> 
  67.  * <p>"icon-small" - int, suggested small icon to use for this state</p> 
  68.  * <p>"plugged" - int, 0 if the device is not plugged in; 1 if plugged 
  69.  * into an AC power adapter; 2 if plugged in via USB.</p> 
  70.  * <p>"voltage" - int, current battery voltage in millivolts</p> 
  71.  * <p>"temperature" - int, current battery temperature in tenths of 
  72.  * a degree Centigrade</p> 
  73.  * <p>"technology" - String, the type of battery installed, e.g. "Li-ion"</p> 
  74.  * 
  75.  * <p> 
  76.  * The battery service may be called by the power manager while holding its locks so 
  77.  * we take care to post all outcalls into the activity manager to a handler. 
  78.  * 
  79.  * FIXME: Ideally the power manager would perform all of its calls into the battery 
  80.  * service asynchronously itself. 
  81.  * </p> 
  82.  */  
  83. public final class BatteryService extends Binder {  
  84.     private static final String TAG = BatteryService.class.getSimpleName();  
  85.   
  86.     private static final boolean DEBUG = false;  
  87.   
  88.     private static final int BATTERY_SCALE = 100;    // battery capacity is a percentage  
  89.   
  90.     // Used locally for determining when to make a last ditch effort to log  
  91.     // discharge stats before the device dies.  
  92.     private int mCriticalBatteryLevel;  
  93.   
  94.     private static final int DUMP_MAX_LENGTH = 24 * 1024;  
  95.     private static final String[] DUMPSYS_ARGS = new String[] { "--checkin""--unplugged" };  
  96.   
  97.     private static final String DUMPSYS_DATA_PATH = "/data/system/";  
  98.   
  99.     // This should probably be exposed in the API, though it's not critical  
  100.     private static final int BATTERY_PLUGGED_NONE = 0;  
  101.   
  102.     private final Context mContext;  
  103.     private final IBatteryStats mBatteryStats;  
  104.     private final Handler mHandler;  
  105.   
  106.     private final Object mLock = new Object();  
  107.   
  108.     private BatteryProperties mBatteryProps;  
  109.     private boolean mBatteryLevelCritical;  
  110.     private int mLastBatteryStatus;  
  111.     private int mLastBatteryHealth;  
  112.     private boolean mLastBatteryPresent;  
  113.     private int mLastBatteryLevel;  
  114.     private int mLastBatteryVoltage;  
  115.     private int mLastBatteryTemperature;  
  116.     private boolean mLastBatteryLevelCritical;  
  117.   
  118.     private int mInvalidCharger;  
  119.     private int mLastInvalidCharger;  
  120.   
  121.     private int mLowBatteryWarningLevel;  
  122.     private int mLowBatteryCloseWarningLevel;  
  123.     private int mShutdownBatteryTemperature;  
  124.   
  125.     private int mPlugType;  
  126.     private int mLastPlugType = -1// Extra state so we can detect first run  
  127.   
  128.     private long mDischargeStartTime;  
  129.     private int mDischargeStartLevel;  
  130.   
  131.     private boolean mUpdatesStopped;  
  132.   
  133.     private Led mLed;  
  134.   
  135.     private boolean mSentLowBatteryBroadcast = false;  
  136.   
  137.     private BatteryListener mBatteryPropertiesListener;  
  138.     private IBatteryPropertiesRegistrar mBatteryPropertiesRegistrar;  
  139. //构造函数  
  140.     public BatteryService(Context context, LightsService lights) {  
  141.         mContext = context;  
  142.         mHandler = new Handler(true /*async*/);  
  143.         mLed = new Led(context, lights);//这个应该是指示灯,没实验  
  144.         mBatteryStats = BatteryStatsService.getService();  
  145.   
  146.  //低电量临界值,这个数我看的源码版本值是4(在这个类里只是用来写日志)  
  147.         mCriticalBatteryLevel = mContext.getResources().getInteger(  
  148.                 com.android.internal.R.integer.config_criticalBatteryWarningLevel);  
  149.   
  150. //低电量告警值,值15,下面会根据这个变量发送低电量的广播Intent.ACTION_BATTERY_LOW(这个跟系统低电量提醒没关系,只是发出去了)  
  151.         mLowBatteryWarningLevel = mContext.getResources().getInteger(  
  152.                 com.android.internal.R.integer.config_lowBatteryWarningLevel);  
  153.   
  154. //电量告警取消值,值20 , 就是手机电量大于等于20的话发送Intent.ACTION_BATTERY_OKAY  
  155.         mLowBatteryCloseWarningLevel = mContext.getResources().getInteger(  
  156.                 com.android.internal.R.integer.config_lowBatteryCloseWarningLevel);  
  157.   
  158. //值是680 ,温度过高,超过这个值就发送广播,跳转到将要关机提醒。  
  159.        mShutdownBatteryTemperature = mContext.getResources().getInteger(  
  160.                 com.android.internal.R.integer.config_shutdownBatteryTemperature);  
  161.   
  162.         // watch for invalid charger messages if the invalid_charger switch exists  
  163.         if (new File("/sys/devices/virtual/switch/invalid_charger/state").exists()) {  
  164.             mInvalidChargerObserver.startObserving(  
  165.                     "DEVPATH=/devices/virtual/switch/invalid_charger");  
  166.         }  
  167. //电池监听,这个应该是注册到底层去了。当底层电量改变会调用此监听。然后执行update(BatteryProperties props);  
  168.         mBatteryPropertiesListener = new BatteryListener();  
  169.   
  170.         IBinder b = ServiceManager.getService("batterypropreg");  
  171.         mBatteryPropertiesRegistrar = IBatteryPropertiesRegistrar.Stub.asInterface(b);  
  172.   
  173.         try {  
  174. //这里注册  
  175.           mBatteryPropertiesRegistrar.registerListener(mBatteryPropertiesListener);  
  176.         } catch (RemoteException e) {  
  177.             // Should never happen.  
  178.         }  
  179.     }  
  180. //开机后先去看看是否没电了或者温度太高了。如果是,就关机提示(关机提示我等会介绍)。  
  181.     void systemReady() {  
  182.         // check our power situation now that it is safe to display the shutdown dialog.  
  183.         synchronized (mLock) {  
  184.             shutdownIfNoPowerLocked();  
  185.             shutdownIfOverTempLocked();  
  186.         }  
  187.     }  
  188. //返回是否在充电,这个函数在PowerManagerService.java 中调用  
  189.     /** 
  190.      * Returns true if the device is plugged into any of the specified plug types. 
  191.      */  
  192.     public boolean isPowered(int plugTypeSet) {  
  193.         synchronized (mLock) {  
  194.             return isPoweredLocked(plugTypeSet);  
  195.         }  
  196.     }  
  197. //就是这里,通过充电器类型判断是否充电  
  198.     private boolean isPoweredLocked(int plugTypeSet) {  
  199. //我这英语小白猜着翻译下:就是开机后,电池状态不明了,那我们就认为就在充电,以便设备正常工作。  
  200.         // assume we are powered if battery state is unknown so  
  201.         // the "stay on while plugged in" option will work.  
  202.         if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) {  
  203.             return true;  
  204.         }  
  205. //充电器  
  206.         if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_AC) != 0 && mBatteryProps.chargerAcOnline) {  
  207.             return true;  
  208.         }  
  209. //USB,插电脑上充电  
  210.       if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_USB) != 0 && mBatteryProps.chargerUsbOnline) {  
  211.             return true;  
  212.         }  
  213. //电源是无线的。 (我没见过...)  
  214.         if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_WIRELESS) != 0 && mBatteryProps.chargerWirelessOnline) {  
  215.             return true;  
  216.         }  
  217.         return false;  
  218.     }  
  219.   
  220.     /** 
  221.      * Returns the current plug type. 
  222.      */  
  223. //充电器类型  
  224.    public int getPlugType() {  
  225.         synchronized (mLock) {  
  226.             return mPlugType;  
  227.         }  
  228.     }  
  229.   
  230.     /** 
  231.      * Returns battery level as a percentage. 
  232.      */  
  233. //电池属性:电量等级(0-100)  
  234.     public int getBatteryLevel() {  
  235.         synchronized (mLock) {  
  236.             return mBatteryProps.batteryLevel;  
  237.         }  
  238.     }  
  239.   
  240.     /** 
  241.      * Returns true if battery level is below the first warning threshold. 
  242.      */  
  243. //低电量  
  244.      public boolean isBatteryLow() {  
  245.         synchronized (mLock) {  
  246.             return mBatteryProps.batteryPresent && mBatteryProps.batteryLevel <= mLowBatteryWarningLevel;  
  247.         }  
  248.     }  
  249.   
  250.     /** 
  251.      * Returns a non-zero value if an  unsupported charger is attached. 
  252.      */  
  253. //不支持的充电器类型    
  254.     public int getInvalidCharger() {  
  255.         synchronized (mLock) {  
  256.             return mInvalidCharger;  
  257.         }  
  258.     }  
  259.   
  260. //这里就是没电了,要关机的提示。  
  261.     private void shutdownIfNoPowerLocked() {  
  262.         // shut down gracefully if our battery is critically low and we are not powered.  
  263.         // wait until the system has booted before attempting to display the shutdown dialog.  
  264.         if (mBatteryProps.batteryLevel == 0 && (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING)) {  
  265.             mHandler.post(new Runnable() {  
  266.                 @Override  
  267.                 public void run() {  
  268.                     if (ActivityManagerNative.isSystemReady()) {  
  269.                         Intent intent = new Intent("android.intent.action.ACTION_REQUEST_SHUTDOWN_LOWBATTERY");//ACTION_REQUEST_SHUTDOWN  
  270.                         intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);  
  271.                         intent.putExtra("cant_be_cancel_by_button"true);  
  272.                         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
  273.                         mContext.startActivityAsUser(intent, UserHandle.CURRENT);  
  274.                     }  
  275.                 }  
  276.             });  
  277.         }  
  278.     }  
  279.   
  280. //温度过高,关机提示(个人感觉这里有问题,温度过高为啥子跳转到没电关机提示界面)   
  281.     private void shutdownIfOverTempLocked() {  
  282.         // shut down gracefully if temperature is too high (> 68.0C by default)  
  283.         // wait until the system has booted before attempting to display the  
  284.         // shutdown dialog.  
  285.         if (mBatteryProps.batteryTemperature > mShutdownBatteryTemperature) {  
  286.             mHandler.post(new Runnable() {  
  287.                 @Override  
  288.                 public void run() {  
  289.                     if (ActivityManagerNative.isSystemReady()) {  
  290.                         Intent intent = new Intent("android.intent.action.ACTION_REQUEST_SHUTDOWN_LOWBATTERY");//ACTION_REQUEST_SHUTDOWN  
  291.                         intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);  
  292.                         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
  293.                         mContext.startActivityAsUser(intent, UserHandle.CURRENT);  
  294.                     }  
  295.                 }  
  296.             });  
  297.         }  
  298.     }  
  299. //这个方法就是被JNI回调的。用来更新上层状态的方法。  
  300.     private void update(BatteryProperties props) {  
  301.         synchronized (mLock) {  
  302.             if (!mUpdatesStopped) {  
  303.                 mBatteryProps = props;  
  304.                 // Process the new values.  
  305.                 processValuesLocked();  
  306.             }  
  307.         }  
  308.     }  
  309. //嗯。这个就是最主要的方法了。  
  310.     private void processValuesLocked() {  
  311.         boolean logOutlier = false;  
  312.         long dischargeDuration = 0;  
  313.   
  314.         mBatteryLevelCritical = (mBatteryProps.batteryLevel <= mCriticalBatteryLevel);  
  315. //充电器类型   
  316.        if (mBatteryProps.chargerAcOnline) {  
  317.             mPlugType = BatteryManager.BATTERY_PLUGGED_AC;  
  318.         } else if (mBatteryProps.chargerUsbOnline) {  
  319.             mPlugType = BatteryManager.BATTERY_PLUGGED_USB;  
  320.         } else if (mBatteryProps.chargerWirelessOnline) {  
  321.             mPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS;  
  322.         } else {  
  323.             mPlugType = BATTERY_PLUGGED_NONE;  
  324.         }  
  325.   
  326.         if (DEBUG) {//日志,略过  
  327.             Slog.d(TAG, "Processing new values: "  
  328.                     + "chargerAcOnline=" + mBatteryProps.chargerAcOnline  
  329.                     + ", chargerUsbOnline=" + mBatteryProps.chargerUsbOnline  
  330.                     + ", chargerWirelessOnline=" + mBatteryProps.chargerWirelessOnline  
  331.                     + ", batteryStatus=" + mBatteryProps.batteryStatus  
  332.                     + ", batteryHealth=" + mBatteryProps.batteryHealth  
  333.                     + ", batteryPresent=" + mBatteryProps.batteryPresent  
  334.                     + ", batteryLevel=" + mBatteryProps.batteryLevel  
  335.                     + ", batteryTechnology=" + mBatteryProps.batteryTechnology  
  336.                     + ", batteryVoltage=" + mBatteryProps.batteryVoltage  
  337.                     + ", batteryCurrentNow=" + mBatteryProps.batteryCurrentNow  
  338.                     + ", batteryChargeCounter=" + mBatteryProps.batteryChargeCounter  
  339.                     + ", batteryTemperature=" + mBatteryProps.batteryTemperature  
  340.                     + ", mBatteryLevelCritical=" + mBatteryLevelCritical  
  341.                     + ", mPlugType=" + mPlugType);  
  342.         }  
  343.   
  344.         // Let the battery stats keep track of the current level.  
  345.         try {  
  346. //把电池属性放到状态里面  
  347.            mBatteryStats.setBatteryState(mBatteryProps.batteryStatus, mBatteryProps.batteryHealth,  
  348.                     mPlugType, mBatteryProps.batteryLevel, mBatteryProps.batteryTemperature,  
  349.                     mBatteryProps.batteryVoltage);  
  350.         } catch (RemoteException e) {  
  351.             // Should never happen.  
  352.         }  
  353. //没电了  
  354.         shutdownIfNoPowerLocked();  
  355. //温度过高了  
  356.        shutdownIfOverTempLocked();  
  357.   
  358.         if (mBatteryProps.batteryStatus != mLastBatteryStatus ||  
  359.                 mBatteryProps.batteryHealth != mLastBatteryHealth ||  
  360.                 mBatteryProps.batteryPresent != mLastBatteryPresent ||  
  361.                 mBatteryProps.batteryLevel != mLastBatteryLevel ||  
  362.                 mPlugType != mLastPlugType ||  
  363.                 mBatteryProps.batteryVoltage != mLastBatteryVoltage ||  
  364.                 mBatteryProps.batteryTemperature != mLastBatteryTemperature ||  
  365.                 mInvalidCharger != mLastInvalidCharger) {  
  366.   
  367.             if (mPlugType != mLastPlugType) {//当前充电器类型与上次的不一样  
  368. //并且上次充电器类型是no one ,那就可以知道,现在是插上充电器了。  
  369.                if (mLastPlugType == BATTERY_PLUGGED_NONE) {  
  370.                     // discharging -> charging  
  371.   
  372.                     // There's no value in this data unless we've discharged at least once and the  
  373.                     // battery level has changed; so don't log until it does.  
  374.                     if (mDischargeStartTime != 0 && mDischargeStartLevel != mBatteryProps.batteryLevel) {  
  375.                         dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;  
  376.                         logOutlier = true;  
  377.                         EventLog.writeEvent(EventLogTags.BATTERY_DISCHARGE, dischargeDuration,  
  378.                                 mDischargeStartLevel, mBatteryProps.batteryLevel);  
  379.                         // make sure we see a discharge event before logging again  
  380.                         mDischargeStartTime = 0;  
  381.                     }  
  382. //并且本次充电器类型是no one ,那就可以知道,现在是拔掉充电器了。  
  383.                 } else if (mPlugType == BATTERY_PLUGGED_NONE) {  
  384.                     // charging -> discharging or we just powered up  
  385.                     mDischargeStartTime = SystemClock.elapsedRealtime();  
  386.                     mDischargeStartLevel = mBatteryProps.batteryLevel;  
  387.                 }  
  388.             }  
  389.             if (mBatteryProps.batteryStatus != mLastBatteryStatus ||//写日志,略过  
  390.                     mBatteryProps.batteryHealth != mLastBatteryHealth ||  
  391.                     mBatteryProps.batteryPresent != mLastBatteryPresent ||  
  392.                     mPlugType != mLastPlugType) {  
  393.                 EventLog.writeEvent(EventLogTags.BATTERY_STATUS,  
  394.                         mBatteryProps.batteryStatus, mBatteryProps.batteryHealth, mBatteryProps.batteryPresent ? 1 : 0,  
  395.                         mPlugType, mBatteryProps.batteryTechnology);  
  396.             }  
  397.             if (mBatteryProps.batteryLevel != mLastBatteryLevel) {  
  398.                 // Don't do this just from voltage or temperature changes, that is  
  399.                 // too noisy.  
  400.                 EventLog.writeEvent(EventLogTags.BATTERY_LEVEL,  
  401.                         mBatteryProps.batteryLevel, mBatteryProps.batteryVoltage, mBatteryProps.batteryTemperature);  
  402.             }  
  403.             if (mBatteryLevelCritical && !mLastBatteryLevelCritical &&  
  404.                     mPlugType == BATTERY_PLUGGED_NONE) {  
  405.                 // We want to make sure we log discharge cycle outliers  
  406.                 // if the battery is about to die.  
  407.                 dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;  
  408.                 logOutlier = true;  
  409.             }  
  410. //本次调用,当前的充电状态  
  411.             final boolean plugged = mPlugType != BATTERY_PLUGGED_NONE;  
  412. //本次调用,上次调用的充电状态    
  413.             final boolean oldPlugged = mLastPlugType != BATTERY_PLUGGED_NONE;  
  414.   
  415.             /* The ACTION_BATTERY_LOW broadcast is sent in these situations: 
  416.              * - is just un-plugged (previously was plugged) and battery level is 
  417.              *   less than or equal to WARNING, or 
  418.              * - is not plugged and battery level falls to WARNING boundary 
  419.              *   (becomes <= mLowBatteryWarningLevel). 
  420.              */  
  421. //用于发送低电量广播的判断  
  422.             final boolean sendBatteryLow = !plugged//(按sendBatteryLow = true 来说) 当前没有充电  
  423.                     && mBatteryProps.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN//充电状态不是UNKNOWN  
  424.                     && mBatteryProps.batteryLevel <= mLowBatteryWarningLevel//当前电量小于告警值 15  
  425.                     && (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);//上次状态是充电或者上次电量等级大于告警值 15  
  426.   
  427.             sendIntentLocked();//发送电池电量改变的广播Intent.ACTION_BATTERY_CHANGED  
  428.   
  429.             // Separate broadcast is sent for power connected / not connected  
  430.             // since the standard intent will not wake any applications and some  
  431.             // applications may want to have smart behavior based on this.  
  432.             if (mPlugType != 0 && mLastPlugType == 0) {//插上充电器了  
  433.                 mHandler.post(new Runnable() {  
  434.                     @Override  
  435.                     public void run() {  
  436.                         Intent statusIntent = new Intent(Intent.ACTION_POWER_CONNECTED);  
  437.                         statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);  
  438.                         mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);  
  439.                     }  
  440.                 });  
  441.             }  
  442.             else if (mPlugType == 0 && mLastPlugType != 0) {//断开充电器了  
  443.                 mHandler.post(new Runnable() {  
  444.                     @Override  
  445.                     public void run() {  
  446.                         Intent statusIntent = new Intent(Intent.ACTION_POWER_DISCONNECTED);  
  447.                         statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);  
  448.                         mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);  
  449.                     }  
  450.                 });  
  451.             }  
  452. //发送低电量提醒(这个跟系统低电量提醒没关系,只是发出去了)  
  453.             if (sendBatteryLow) {  
  454.                 mSentLowBatteryBroadcast = true;  
  455.                 mHandler.post(new Runnable() {  
  456.                     @Override  
  457.                     public void run() {  
  458.                         Intent statusIntent = new Intent(Intent.ACTION_BATTERY_LOW);  
  459.                         statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);  
  460.                         mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);  
  461.                     }  
  462.                 });  
  463.             } else if (mSentLowBatteryBroadcast && mLastBatteryLevel >= mLowBatteryCloseWarningLevel) {//电量超过20了。电池状态OK了  
  464.                 mSentLowBatteryBroadcast = false;  
  465.                 mHandler.post(new Runnable() {  
  466.                     @Override  
  467.                     public void run() {  
  468.                         Intent statusIntent = new Intent(Intent.ACTION_BATTERY_OKAY);  
  469.                         statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);  
  470.                         mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);  
  471.                     }  
  472.                 });  
  473.             }  
  474.   
  475.             // Update the battery LED  
  476.             mLed.updateLightsLocked();  
  477.   
  478.             // This needs to be done after sendIntent() so that we get the lastest battery stats.  
  479.             if (logOutlier && dischargeDuration != 0) {  
  480.                 logOutlierLocked(dischargeDuration);  
  481.             }  
  482.   
  483.             mLastBatteryStatus = mBatteryProps.batteryStatus;  
  484.             mLastBatteryHealth = mBatteryProps.batteryHealth;  
  485.             mLastBatteryPresent = mBatteryProps.batteryPresent;  
  486.             mLastBatteryLevel = mBatteryProps.batteryLevel;  
  487.             mLastPlugType = mPlugType;  
  488.             mLastBatteryVoltage = mBatteryProps.batteryVoltage;  
  489.             mLastBatteryTemperature = mBatteryProps.batteryTemperature;  
  490.             mLastBatteryLevelCritical = mBatteryLevelCritical;  
  491.             mLastInvalidCharger = mInvalidCharger;  
  492.         }  
  493.     }  
  494. //电池电量改变,把属性发出去(系统低电量提醒接收的是这个广播)  
  495.     private void sendIntentLocked() {  
  496.         //  Pack up the values and broadcast them to everyone  
  497.         final Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);  
  498.         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY  
  499.                 | Intent.FLAG_RECEIVER_REPLACE_PENDING);  
  500.   
  501.         int icon = getIconLocked(mBatteryProps.batteryLevel);  
  502.   
  503.         intent.putExtra(BatteryManager.EXTRA_STATUS, mBatteryProps.batteryStatus);  
  504.         intent.putExtra(BatteryManager.EXTRA_HEALTH, mBatteryProps.batteryHealth);  
  505.         intent.putExtra(BatteryManager.EXTRA_PRESENT, mBatteryProps.batteryPresent);  
  506.         intent.putExtra(BatteryManager.EXTRA_LEVEL, mBatteryProps.batteryLevel);  
  507.         intent.putExtra(BatteryManager.EXTRA_SCALE, BATTERY_SCALE);  
  508.         intent.putExtra(BatteryManager.EXTRA_ICON_SMALL, icon);  
  509.         intent.putExtra(BatteryManager.EXTRA_PLUGGED, mPlugType);  
  510.         intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mBatteryProps.batteryVoltage);  
  511.         intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mBatteryProps.batteryTemperature);  
  512.         intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mBatteryProps.batteryTechnology);  
  513.         intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger);  
  514.   
  515.         if (DEBUG) {  
  516.             Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED.  level:" + mBatteryProps.batteryLevel +  
  517.                     ", scale:" + BATTERY_SCALE + ", status:" + mBatteryProps.batteryStatus +  
  518.                     ", health:" + mBatteryProps.batteryHealth +  ", present:" + mBatteryProps.batteryPresent +  
  519.                     ", voltage: " + mBatteryProps.batteryVoltage +  
  520.                     ", temperature: " + mBatteryProps.batteryTemperature +  
  521.                     ", technology: " + mBatteryProps.batteryTechnology +  
  522.                     ", AC powered:" + mBatteryProps.chargerAcOnline + ", USB powered:" + mBatteryProps.chargerUsbOnline +  
  523.                     ", Wireless powered:" + mBatteryProps.chargerWirelessOnline +  
  524.                     ", icon:" + icon  + ", invalid charger:" + mInvalidCharger);  
  525.         }  
  526.   
  527.         mHandler.post(new Runnable() {  
  528.             @Override  
  529.             public void run() {  
  530.                 ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);  
  531.             }  
  532.         });  
  533.     }  
  534.   
  535.     private void logBatteryStatsLocked() {  
  536.         IBinder batteryInfoService = ServiceManager.getService(BatteryStats.SERVICE_NAME);  
  537.         if (batteryInfoService == nullreturn;  
  538.   
  539.         DropBoxManager db = (DropBoxManager) mContext.getSystemService(Context.DROPBOX_SERVICE);  
  540.         if (db == null || !db.isTagEnabled("BATTERY_DISCHARGE_INFO")) return;  
  541.   
  542.         File dumpFile = null;  
  543.         FileOutputStream dumpStream = null;  
  544.         try {  
  545.             // dump the service to a file  
  546.             dumpFile = new File(DUMPSYS_DATA_PATH + BatteryStats.SERVICE_NAME + ".dump");  
  547.             dumpStream = new FileOutputStream(dumpFile);  
  548.             batteryInfoService.dump(dumpStream.getFD(), DUMPSYS_ARGS);  
  549.             FileUtils.sync(dumpStream);  
  550.   
  551.             // add dump file to drop box  
  552.             db.addFile("BATTERY_DISCHARGE_INFO", dumpFile, DropBoxManager.IS_TEXT);  
  553.         } catch (RemoteException e) {  
  554.             Slog.e(TAG, "failed to dump battery service", e);  
  555.         } catch (IOException e) {  
  556.             Slog.e(TAG, "failed to write dumpsys file", e);  
  557.         } finally {  
  558.             // make sure we clean up  
  559.             if (dumpStream != null) {  
  560.                 try {  
  561.                     dumpStream.close();  
  562.                 } catch (IOException e) {  
  563.                     Slog.e(TAG, "failed to close dumpsys output stream");  
  564.                 }  
  565.             }  
  566.             if (dumpFile != null && !dumpFile.delete()) {  
  567.                 Slog.e(TAG, "failed to delete temporary dumpsys file: "  
  568.                         + dumpFile.getAbsolutePath());  
  569.             }  
  570.         }  
  571.     }  
  572.   
  573.     private void logOutlierLocked(long duration) {  
  574.         ContentResolver cr = mContext.getContentResolver();  
  575.         String dischargeThresholdString = Settings.Global.getString(cr,  
  576.                 Settings.Global.BATTERY_DISCHARGE_THRESHOLD);  
  577.         String durationThresholdString = Settings.Global.getString(cr,  
  578.                 Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD);  
  579.   
  580.         if (dischargeThresholdString != null && durationThresholdString != null) {  
  581.             try {  
  582.                 long durationThreshold = Long.parseLong(durationThresholdString);  
  583.                 int dischargeThreshold = Integer.parseInt(dischargeThresholdString);  
  584.                 if (duration <= durationThreshold &&  
  585.                         mDischargeStartLevel - mBatteryProps.batteryLevel >= dischargeThreshold) {  
  586.                     // If the discharge cycle is bad enough we want to know about it.  
  587.                     logBatteryStatsLocked();  
  588.                 }  
  589.                 if (DEBUG) Slog.v(TAG, "duration threshold: " + durationThreshold +  
  590.                         " discharge threshold: " + dischargeThreshold);  
  591.                 if (DEBUG) Slog.v(TAG, "duration: " + duration + " discharge: " +  
  592.                         (mDischargeStartLevel - mBatteryProps.batteryLevel));  
  593.             } catch (NumberFormatException e) {  
  594.                 Slog.e(TAG, "Invalid DischargeThresholds GService string: " +  
  595.                         durationThresholdString + " or " + dischargeThresholdString);  
  596.                 return;  
  597.             }  
  598.         }  
  599.     }  
  600.   
  601.     private int getIconLocked(int level) {  
  602.         if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_CHARGING) {  
  603.             return com.android.internal.R.drawable.stat_sys_battery_charge;  
  604.         } else if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING) {  
  605.             return com.android.internal.R.drawable.stat_sys_battery;  
  606.         } else if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING  
  607.                 || mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_FULL) {  
  608.             if (isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)  
  609.                     && mBatteryProps.batteryLevel >= 100) {  
  610.                 return com.android.internal.R.drawable.stat_sys_battery_charge;  
  611.             } else {  
  612.                 return com.android.internal.R.drawable.stat_sys_battery;  
  613.             }  
  614.         } else {  
  615.             return com.android.internal.R.drawable.stat_sys_battery_unknown;  
  616.         }  
  617.     }  
  618.   
  619.     @Override  
  620.     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {  
  621.         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)  
  622.                 != PackageManager.PERMISSION_GRANTED) {  
  623.   
  624.             pw.println("Permission Denial: can't dump Battery service from from pid="  
  625.                     + Binder.getCallingPid()  
  626.                     + ", uid=" + Binder.getCallingUid());  
  627.             return;  
  628.         }  
  629.   
  630.         synchronized (mLock) {  
  631.             if (args == null || args.length == 0 || "-a".equals(args[0])) {  
  632.                 pw.println("Current Battery Service state:");  
  633.                 if (mUpdatesStopped) {  
  634.                     pw.println("  (UPDATES STOPPED -- use 'reset' to restart)");  
  635.                 }  
  636.                 pw.println("  AC powered: " + mBatteryProps.chargerAcOnline);  
  637.                 pw.println("  USB powered: " + mBatteryProps.chargerUsbOnline);  
  638.                 pw.println("  Wireless powered: " + mBatteryProps.chargerWirelessOnline);  
  639.                 pw.println("  status: " + mBatteryProps.batteryStatus);  
  640.                 pw.println("  health: " + mBatteryProps.batteryHealth);  
  641.                 pw.println("  present: " + mBatteryProps.batteryPresent);  
  642.                 pw.println("  level: " + mBatteryProps.batteryLevel);  
  643.                 pw.println("  scale: " + BATTERY_SCALE);  
  644.                 pw.println("  voltage: " + mBatteryProps.batteryVoltage);  
  645.   
  646.                 if (mBatteryProps.batteryCurrentNow != Integer.MIN_VALUE) {  
  647.                     pw.println("  current now: " + mBatteryProps.batteryCurrentNow);  
  648.                 }  
  649.   
  650.                 if (mBatteryProps.batteryChargeCounter != Integer.MIN_VALUE) {  
  651.                     pw.println("  charge counter: " + mBatteryProps.batteryChargeCounter);  
  652.                 }  
  653.   
  654.                 pw.println("  temperature: " + mBatteryProps.batteryTemperature);  
  655.                 pw.println("  technology: " + mBatteryProps.batteryTechnology);  
  656.             } else if (args.length == 3 && "set".equals(args[0])) {  
  657.                 String key = args[1];  
  658.                 String value = args[2];  
  659.                 try {  
  660.                     boolean update = true;  
  661.                     if ("ac".equals(key)) {  
  662.                         mBatteryProps.chargerAcOnline = Integer.parseInt(value) != 0;  
  663.                     } else if ("usb".equals(key)) {  
  664.                         mBatteryProps.chargerUsbOnline = Integer.parseInt(value) != 0;  
  665.                     } else if ("wireless".equals(key)) {  
  666.                         mBatteryProps.chargerWirelessOnline = Integer.parseInt(value) != 0;  
  667.                     } else if ("status".equals(key)) {  
  668.                         mBatteryProps.batteryStatus = Integer.parseInt(value);  
  669.                     } else if ("level".equals(key)) {  
  670.                         mBatteryProps.batteryLevel = Integer.parseInt(value);  
  671.                     } else if ("invalid".equals(key)) {  
  672.                         mInvalidCharger = Integer.parseInt(value);  
  673.                     } else {  
  674.                         pw.println("Unknown set option: " + key);  
  675.                         update = false;  
  676.                     }  
  677.                     if (update) {  
  678.                         long ident = Binder.clearCallingIdentity();  
  679.                         try {  
  680.                             mUpdatesStopped = true;  
  681.                             processValuesLocked();  
  682.                         } finally {  
  683.                             Binder.restoreCallingIdentity(ident);  
  684.                         }  
  685.                     }  
  686.                 } catch (NumberFormatException ex) {  
  687.                     pw.println("Bad value: " + value);  
  688.                 }  
  689.             } else if (args.length == 1 && "reset".equals(args[0])) {  
  690.                 long ident = Binder.clearCallingIdentity();  
  691.                 try {  
  692.                     mUpdatesStopped = false;  
  693.                 } finally {  
  694.                     Binder.restoreCallingIdentity(ident);  
  695.                 }  
  696.             } else {  
  697.                 pw.println("Dump current battery state, or:");  
  698.                 pw.println("  set ac|usb|wireless|status|level|invalid <value>");  
  699.                 pw.println("  reset");  
  700.             }  
  701.         }  
  702.     }  
  703.   
  704.     private final UEventObserver mInvalidChargerObserver = new UEventObserver() {  
  705.         @Override  
  706.         public void onUEvent(UEventObserver.UEvent event) {  
  707.             final int invalidCharger = "1".equals(event.get("SWITCH_STATE")) ? 1 : 0;  
  708.             synchronized (mLock) {  
  709.                 if (mInvalidCharger != invalidCharger) {  
  710.                     mInvalidCharger = invalidCharger;  
  711.                 }  
  712.             }  
  713.         }  
  714.     };  
  715.   
  716.     private final class Led {  
  717.         private final LightsService.Light mBatteryLight;  
  718.   
  719.         private final int mBatteryLowARGB;  
  720.         private final int mBatteryMediumARGB;  
  721.         private final int mBatteryFullARGB;  
  722.         private final int mBatteryLedOn;  
  723.         private final int mBatteryLedOff;  
  724.   
  725.         public Led(Context context, LightsService lights) {  
  726.             mBatteryLight = lights.getLight(LightsService.LIGHT_ID_BATTERY);  
  727.   
  728.             mBatteryLowARGB = context.getResources().getInteger(  
  729.                     com.android.internal.R.integer.config_notificationsBatteryLowARGB);  
  730.             mBatteryMediumARGB = context.getResources().getInteger(  
  731.                     com.android.internal.R.integer.config_notificationsBatteryMediumARGB);  
  732.             mBatteryFullARGB = context.getResources().getInteger(  
  733.                     com.android.internal.R.integer.config_notificationsBatteryFullARGB);  
  734.             mBatteryLedOn = context.getResources().getInteger(  
  735.                     com.android.internal.R.integer.config_notificationsBatteryLedOn);  
  736.             mBatteryLedOff = context.getResources().getInteger(  
  737.                     com.android.internal.R.integer.config_notificationsBatteryLedOff);  
  738.         }  
  739.   
  740.         /** 
  741.          * Synchronize on BatteryService. 
  742.          */  
  743.         public void updateLightsLocked() {  
  744.             final int level = mBatteryProps.batteryLevel;  
  745.             final int status = mBatteryProps.batteryStatus;  
  746.             if (level < mLowBatteryWarningLevel) {  
  747.                 if (status == BatteryManager.BATTERY_STATUS_CHARGING) {  
  748.                     // Solid red when battery is charging  
  749.                     mBatteryLight.setColor(mBatteryLowARGB);  
  750.                 } else {  
  751.                     // Flash red when battery is low and not charging  
  752.                     mBatteryLight.setFlashing(mBatteryLowARGB, LightsService.LIGHT_FLASH_TIMED,  
  753.                             mBatteryLedOn, mBatteryLedOff);  
  754.                 }  
  755.             } else if (status == BatteryManager.BATTERY_STATUS_CHARGING  
  756.                     || status == BatteryManager.BATTERY_STATUS_FULL) {  
  757.                 if (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90) {  
  758.                     // Solid green when full or charging and nearly full  
  759.                     mBatteryLight.setColor(mBatteryFullARGB);  
  760.                 } else {  
  761.                     // Solid orange when charging and halfway full  
  762.                     mBatteryLight.setColor(mBatteryMediumARGB);  
  763.                 }  
  764.             } else {  
  765.                 // No lights if not charging and not low  
  766.                 mBatteryLight.turnOff();  
  767.             }  
  768.         }  
  769.     }  
  770.   
  771.     private final class BatteryListener extends IBatteryPropertiesListener.Stub {  
  772.         public void batteryPropertiesChanged(BatteryProperties props) {  
  773.             BatteryService.this.update(props);  
  774.        }  
  775.     }  
  776. }  



 
总结如下: 
此服务构造时会注册监听到系统JNI层。 当电池电量改变的时候会调用update(BatteryProperties props) -----》processValuesLocked() 。 而processValuesLocked() 函数会把电池状态把广播发送出去。其他类再接收广播进行处理 
 

下一个类就是低电量提醒了,文件目录:\frameworks\base\packages\SystemUI\src\com\android\systemui\power\PowerUI.java

[java]  view plain  copy
  1. /* 
  2.  * Copyright (C) 2008 The Android Open Source Project 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  *      http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */  
  16.   
  17. package com.android.systemui.power;  
  18.   
  19. import android.app.AlertDialog;  
  20. import android.content.BroadcastReceiver;  
  21. import android.content.ContentResolver;  
  22. import android.content.Context;  
  23. import android.content.DialogInterface;  
  24. import android.content.Intent;  
  25. import android.content.IntentFilter;  
  26. import android.media.AudioManager;  
  27. import android.media.Ringtone;  
  28. import android.media.RingtoneManager;  
  29. import android.net.Uri;  
  30. import android.os.BatteryManager;  
  31. import android.os.Handler;  
  32. import android.os.PowerManager;  
  33. import android.os.SystemClock;  
  34. import android.os.UserHandle;  
  35. import android.provider.Settings;  
  36. import android.util.Slog;  
  37. import android.view.View;  
  38. import android.view.WindowManager;  
  39. import android.widget.TextView;  
  40.   
  41. import com.android.systemui.R;  
  42. import com.android.systemui.SystemUI;  
  43.   
  44. import java.io.FileDescriptor;  
  45. import java.io.PrintWriter;  
  46. import java.util.Arrays;  
  47.   
  48. public class PowerUI extends SystemUI { //总体说一下,这里才是处理低电量提醒的地方,他接收的广播是Intent.ACTION_BATTERY_CHANGED  
  49.     static final String TAG = "PowerUI";  
  50.   
  51.     static final boolean DEBUG = false;  
  52.   
  53.     Handler mHandler = new Handler();  
  54.   
  55.     int mBatteryLevel = 100;  
  56.     int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;  
  57.     int mPlugType = 0;  
  58.     int mInvalidCharger = 0;  
  59.   
  60.     int mLowBatteryAlertCloseLevel;  
  61.     int[] mLowBatteryReminderLevels = new int[2];  
  62.   
  63.     AlertDialog mInvalidChargerDialog;  
  64.     AlertDialog mLowBatteryDialog;  
  65.     TextView mBatteryLevelTextView;  
  66.   
  67.     private long mScreenOffTime = -1;  
  68.   
  69.     public void start() {  //这个类会在手机启动后,在SystemUI 里启动(就是系统界面)。  
  70.   
  71.         mLowBatteryAlertCloseLevel = mContext.getResources().getInteger( //电量告警取消值,值20   
  72.                 com.android.internal.R.integer.config_lowBatteryCloseWarningLevel);  
  73.         mLowBatteryReminderLevels[0] = mContext.getResources().getInteger( //低电量提醒 15  
  74.                 com.android.internal.R.integer.config_lowBatteryWarningLevel);  
  75.         mLowBatteryReminderLevels[1] = mContext.getResources().getInteger( //低电量临界值 4  
  76.                 com.android.internal.R.integer.config_criticalBatteryWarningLevel);  
  77.   
  78.         final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);  
  79.         mScreenOffTime = pm.isScreenOn() ? -1 : SystemClock.elapsedRealtime();  
  80.   
  81.         // Register for Intent broadcasts for...  
  82.         IntentFilter filter = new IntentFilter();  
  83.         filter.addAction(Intent.ACTION_BATTERY_CHANGED);  
  84.         filter.addAction(Intent.ACTION_SCREEN_OFF);  
  85.         filter.addAction(Intent.ACTION_SCREEN_ON);  
  86.         mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);  
  87.     }  
  88.   
  89.     /** 
  90.      * Buckets the battery level. 
  91.      * 
  92.      * The code in this function is a little weird because I couldn't comprehend 
  93.      * the bucket going up when the battery level was going down. --joeo 
  94.      * 
  95.      * 1 means that the battery is "ok" 
  96.      * 0 means that the battery is between "ok" and what we should warn about. 
  97.      * less than 0 means that the battery is low 
  98.      */  
  99.     private int findBatteryLevelBucket(int level) {   //这个方法是用来警告判断用的。  
  100.         if (level >= mLowBatteryAlertCloseLevel) {  
  101.             return 1;  
  102.         }  
  103.         if (level >= mLowBatteryReminderLevels[0]) {  
  104.             return 0;  
  105.         }  
  106.         final int N = mLowBatteryReminderLevels.length;  
  107.         for (int i=N-1; i>=0; i--) {  
  108.             if (level <= mLowBatteryReminderLevels[i]) {  
  109.                 return -1-i;  
  110.             }  
  111.         }  
  112.         throw new RuntimeException("not possible!");  
  113.     }  
  114.   
  115.     private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { //重点,广播接收处理  
  116.         @Override  
  117.         public void onReceive(Context context, Intent intent) {  
  118.             String action = intent.getAction();  
  119.             if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {  //电池电量改变  
  120.                 final int oldBatteryLevel = mBatteryLevel; //下边就是根据Intent获取BatteryService传过来的电池属性  
  121.                 mBatteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 100); /  
  122.                 final int oldBatteryStatus = mBatteryStatus;  
  123.                 mBatteryStatus = intent.getIntExtra(BatteryManager.EXTRA_STATUS,  
  124.                         BatteryManager.BATTERY_STATUS_UNKNOWN);  
  125.                 final int oldPlugType = mPlugType;  
  126.                 mPlugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 1);  
  127.                 final int oldInvalidCharger = mInvalidCharger;  
  128.                 mInvalidCharger = intent.getIntExtra(BatteryManager.EXTRA_INVALID_CHARGER, 0);  
  129.   
  130.                 final boolean plugged = mPlugType != 0;  
  131.                 final boolean oldPlugged = oldPlugType != 0;  
  132.   
  133.                 int oldBucket = findBatteryLevelBucket(oldBatteryLevel); //这两个值特别有意思,就是说记录下老的电量,记录一下新的电量,比较电量是增加了,还是减小了  
  134.                 int bucket = findBatteryLevelBucket(mBatteryLevel);  
  135.   
  136.                 if (DEBUG) {  
  137.                     Slog.d(TAG, "buckets   ....." + mLowBatteryAlertCloseLevel  
  138.                             + " .. " + mLowBatteryReminderLevels[0]  
  139.                             + " .. " + mLowBatteryReminderLevels[1]);  
  140.                     Slog.d(TAG, "level          " + oldBatteryLevel + " --> " + mBatteryLevel);  
  141.                     Slog.d(TAG, "status         " + oldBatteryStatus + " --> " + mBatteryStatus);  
  142.                     Slog.d(TAG, "plugType       " + oldPlugType + " --> " + mPlugType);  
  143.                     Slog.d(TAG, "invalidCharger " + oldInvalidCharger + " --> " + mInvalidCharger);  
  144.                     Slog.d(TAG, "bucket         " + oldBucket + " --> " + bucket);  
  145.                     Slog.d(TAG, "plugged        " + oldPlugged + " --> " + plugged);  
  146.                 }  
  147.   
  148.                 if (oldInvalidCharger == 0 && mInvalidCharger != 0) {  
  149.                     Slog.d(TAG, "showing invalid charger warning");  
  150.                     showInvalidChargerDialog(); //就是充电器不识别的弹窗  
  151.                     return;  
  152.                 } else if (oldInvalidCharger != 0 && mInvalidCharger == 0) {  
  153.                     dismissInvalidChargerDialog();  
  154.                 } else if (mInvalidChargerDialog != null) {  
  155.                     // if invalid charger is showing, don't show low battery  
  156.                     return;  
  157.                 }  
  158.   
  159.                 if (!plugged  
  160.                         && (bucket < oldBucket || oldPlugged)  
  161.                         && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN  
  162.                         && bucket < 0) {  
  163.                     showLowBatteryWarning(); <span style="color:#FF0000;">//这里哈,低电量提醒的弹窗</span>  
  164.   
  165.                     // only play SFX when the dialog comes up or the bucket changes  
  166.                     if (bucket != oldBucket || oldPlugged) {  
  167.                         playLowBatterySound();  
  168.                     }  
  169.                 } else if (plugged || (bucket > oldBucket && bucket > 0)) { //插上充电器或者充电电池电量超过20取消弹窗  
  170.                     dismissLowBatteryWarning();  
  171.                 } else if (mBatteryLevelTextView != null) {  
  172.                     showLowBatteryWarning();  
  173.                 }  
  174.             } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {  
  175.                 mScreenOffTime = SystemClock.elapsedRealtime();  
  176.             } else if (Intent.ACTION_SCREEN_ON.equals(action)) {  
  177.                 mScreenOffTime = -1;  
  178.             } else {  
  179.                 Slog.w(TAG, "unknown intent: " + intent);  
  180.             }  
  181.         }  
  182.     };  
  183.   
  184.     void dismissLowBatteryWarning() {  
  185.         if (mLowBatteryDialog != null) {  
  186.             Slog.i(TAG, "closing low battery warning: level=" + mBatteryLevel);  
  187.             mLowBatteryDialog.dismiss();  
  188.         }  
  189.     }  
  190.   
  191.     void showLowBatteryWarning() {  
  192.         Slog.i(TAG,  
  193.                 ((mBatteryLevelTextView == null) ? "showing" : "updating")  
  194.                 + " low battery warning: level=" + mBatteryLevel  
  195.                 + " [" + findBatteryLevelBucket(mBatteryLevel) + "]");  
  196.   
  197.         CharSequence levelText = mContext.getString(  
  198.                 R.string.battery_low_percent_format, mBatteryLevel);  
  199.   
  200.         if (mBatteryLevelTextView != null) {  
  201.             mBatteryLevelTextView.setText(levelText);  
  202.         } else {  
  203.             View v = View.inflate(mContext, R.layout.battery_low, null);  
  204.             mBatteryLevelTextView = (TextView)v.findViewById(R.id.level_percent);  
  205.   
  206.             mBatteryLevelTextView.setText(levelText);  
  207.   
  208.             AlertDialog.Builder b = new AlertDialog.Builder(mContext);  
  209.                 b.setCancelable(true);  
  210.                 b.setTitle(R.string.battery_low_title);  
  211.                 b.setView(v);  
  212.                 b.setIconAttribute(android.R.attr.alertDialogIcon);  
  213.                 b.setPositiveButton(android.R.string.ok, null);  
  214.   
  215.             final Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);  
  216.             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK  
  217.                     | Intent.FLAG_ACTIVITY_MULTIPLE_TASK  
  218.                     | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS  
  219.                     | Intent.FLAG_ACTIVITY_NO_HISTORY);  
  220.             if (intent.resolveActivity(mContext.getPackageManager()) != null) {  
  221.                 b.setNegativeButton(R.string.battery_low_why,  
  222.                         new DialogInterface.OnClickListener() {  
  223.                     @Override  
  224.                     public void onClick(DialogInterface dialog, int which) {  
  225.                         mContext.startActivityAsUser(intent, UserHandle.CURRENT);  
  226.                         dismissLowBatteryWarning();  
  227.                     }  
  228.                 });  
  229.             }  
  230.   
  231.             AlertDialog d = b.create();  
  232.             d.setOnDismissListener(new DialogInterface.OnDismissListener() {  
  233.                     @Override  
  234.                     public void onDismiss(DialogInterface dialog) {  
  235.                         mLowBatteryDialog = null;  
  236.                         mBatteryLevelTextView = null;  
  237.                     }  
  238.                 });  
  239.             d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);  
  240.             d.getWindow().getAttributes().privateFlags |=  
  241.                     WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;  
  242.             d.show();  
  243.             mLowBatteryDialog = d;  
  244.         }  
  245.     }  
  246.   
  247.     void playLowBatterySound() {  
  248.         final ContentResolver cr = mContext.getContentResolver();  
  249.   
  250.         final int silenceAfter = Settings.Global.getInt(cr,  
  251.                 Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, 0);  
  252.         final long offTime = SystemClock.elapsedRealtime() - mScreenOffTime;  
  253.         if (silenceAfter > 0  
  254.                 && mScreenOffTime > 0  
  255.                 && offTime > silenceAfter) {  
  256.             Slog.i(TAG, "screen off too long (" + offTime + "ms, limit " + silenceAfter  
  257.                     + "ms): not waking up the user with low battery sound");  
  258.             return;  
  259.         }  
  260.   
  261.         if (DEBUG) {  
  262.             Slog.d(TAG, "playing low battery sound. pick-a-doop!"); // WOMP-WOMP is deprecated  
  263.         }  
  264.   
  265.         if (Settings.Global.getInt(cr, Settings.Global.POWER_SOUNDS_ENABLED, 1) == 1) {  
  266.             final String soundPath = Settings.Global.getString(cr,  
  267.                     Settings.Global.LOW_BATTERY_SOUND);  
  268.             if (soundPath != null) {  
  269.                 final Uri soundUri = Uri.parse("file://" + soundPath);  
  270.                 if (soundUri != null) {  
  271.                     final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);  
  272.                     if (sfx != null) {  
  273.                         sfx.setStreamType(AudioManager.STREAM_SYSTEM);  
  274.                         sfx.play();  
  275.                     }  
  276.                 }  
  277.             }  
  278.         }  
  279.     }  
  280.   
  281.     void dismissInvalidChargerDialog() {  
  282.         if (mInvalidChargerDialog != null) {  
  283.             mInvalidChargerDialog.dismiss();  
  284.         }  
  285.     }  
  286.   
  287.     void showInvalidChargerDialog() {  
  288.         Slog.d(TAG, "showing invalid charger dialog");  
  289.   
  290.         dismissLowBatteryWarning();  
  291.   
  292.         AlertDialog.Builder b = new AlertDialog.Builder(mContext);  
  293.             b.setCancelable(true);  
  294.             b.setMessage(R.string.invalid_charger);  
  295.             b.setIconAttribute(android.R.attr.alertDialogIcon);  
  296.             b.setPositiveButton(android.R.string.ok, null);  
  297.   
  298.         AlertDialog d = b.create();  
  299.             d.setOnDismissListener(new DialogInterface.OnDismissListener() {  
  300.                     public void onDismiss(DialogInterface dialog) {  
  301.                         mInvalidChargerDialog = null;  
  302.                         mBatteryLevelTextView = null;  
  303.                     }  
  304.                 });  
  305.   
  306.         d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);  
  307.         d.show();  
  308.         mInvalidChargerDialog = d;  
  309.     }  
  310.   
  311.     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {  
  312.         pw.print("mLowBatteryAlertCloseLevel=");  
  313.         pw.println(mLowBatteryAlertCloseLevel);  
  314.         pw.print("mLowBatteryReminderLevels=");  
  315.         pw.println(Arrays.toString(mLowBatteryReminderLevels));  
  316.         pw.print("mInvalidChargerDialog=");  
  317.         pw.println(mInvalidChargerDialog == null ? "null" : mInvalidChargerDialog.toString());  
  318.         pw.print("mLowBatteryDialog=");  
  319.         pw.println(mLowBatteryDialog == null ? "null" : mLowBatteryDialog.toString());  
  320.         pw.print("mBatteryLevel=");  
  321.         pw.println(Integer.toString(mBatteryLevel));  
  322.         pw.print("mBatteryStatus=");  
  323.         pw.println(Integer.toString(mBatteryStatus));  
  324.         pw.print("mPlugType=");  
  325.         pw.println(Integer.toString(mPlugType));  
  326.         pw.print("mInvalidCharger=");  
  327.         pw.println(Integer.toString(mInvalidCharger));  
  328.         pw.print("mScreenOffTime=");  
  329.         pw.print(mScreenOffTime);  
  330.         if (mScreenOffTime >= 0) {  
  331.             pw.print(" (");  
  332.             pw.print(SystemClock.elapsedRealtime() - mScreenOffTime);  
  333.             pw.print(" ago)");  
  334.         }  
  335.         pw.println();  
  336.         pw.print("soundTimeout=");  
  337.         pw.println(Settings.Global.getInt(mContext.getContentResolver(),  
  338.                 Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, 0));  
  339.         pw.print("bucket: ");  
  340.         pw.println(Integer.toString(findBatteryLevelBucket(mBatteryLevel)));  
  341.     }  
  342. }  

总结一下:这个类就是接收电池电量改变的广播,然后弹窗提醒。

下一个类就是没电关机界面了,文件目录:\frameworks\base\services\java\com\android\server\ShutdownLowBatteryActivity.java

这个Activity的配置文件是这样的(\frameworks\base\core\res\AndroidManifest.xml):

[html]  view plain  copy
  1.  <activity android:name="com.android.server.ShutdownLowBatteryActivity"  
  2.     android:theme="@android:style/Theme.Translucent.NoTitleBar"  
  3.     android:configChanges="orientation|keyboardHidden|screenSize"  
  4.     android:windowSoftInputMode="stateHidden"  
  5.     android:permission="android.permission.SHUTDOWN"  
  6.     android:excludeFromRecents="true">  
  7.     <intent-filter>  
  8.         <action android:name="android.intent.action.ACTION_REQUEST_SHUTDOWN_LOWBATTERY" />  
  9.         <category android:name="android.intent.category.DEFAULT" />  
  10.     </intent-filter>  
  11. </activity>  

android:theme="@android:style/Theme.Translucent.NoTitleBar"API小于11的透明主题。这样弹出的dialog会是老版本的主题。(这个Activity是透明的)

对于Theme,因为是4.4,我更倾向于:android:theme="@android:style/Theme.Holo.Panel"


ShutdownLowBatteryActivity.java:

[java]  view plain  copy
  1. /* 
  2.  * Copyright (C) 2009 The Android Open Source Project 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  *      http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */  
  16.   
  17. package com.android.server;  
  18.   
  19. import android.app.Activity;  
  20. import android.app.AlertDialog;  
  21. import android.app.KeyguardManager;  
  22. import android.content.BroadcastReceiver;  
  23. import android.content.Context;  
  24. import android.content.DialogInterface;  
  25. import android.content.Intent;  
  26. import android.content.IntentFilter;  
  27. import android.os.Bundle;  
  28. import android.os.Handler;  
  29. import android.util.Slog;  
  30. import android.view.Window;  
  31. import android.view.WindowManager;  
  32. import android.view.View;  
  33.   
  34.   
  35. //import com.android.internal.app.ShutdownThread;  
  36. import com.android.server.power.ShutdownThread;  
  37.   
  38. import android.telephony.TelephonyManager;  
  39. import android.telephony.PhoneStateListener;  
  40. import android.media.MediaPlayer;  
  41. import android.media.MediaPlayer.OnCompletionListener;  
  42. import android.content.ContentResolver;  
  43. import android.provider.Settings;  
  44.   
  45. import java.io.IOException;  
  46.   
  47. public class ShutdownLowBatteryActivity extends Activity {  
  48.   
  49.     private static final String TAG = "ShutdownLowBatteryActivity";  
  50.     private boolean mConfirm;  
  51.     private int mSeconds = 15;//电池电量等于0 15秒内不插充电器就自动关机  
  52.     private AlertDialog mDialog;  
  53.     private Handler myHandler = new Handler();  
  54.     private Runnable myRunnable = new Runnable() { //这里数秒关机  
  55.         @Override  
  56.         public void run() {  
  57.             mSeconds --;  
  58.             if(mSeconds <1)  
  59.                 mSeconds=0;  
  60.             mDialog.setMessage(getString(com.android.internal.R.string.low_battery_shutdown_after_seconds,mSeconds));  
  61.   
  62.             if(mSeconds <= 1){  
  63.                 myHandler.removeCallbacks(myRunnable);  
  64.                 Handler h = new Handler();  
  65.                 h.post(new Runnable() {  
  66.                     public void run() {  
  67.                         ShutdownThread.shutdown(ShutdownLowBatteryActivity.this, mConfirm);  
  68.                     }  
  69.                 });  
  70.             }  
  71.             myHandler.postDelayed(myRunnable,1000);  
  72.         }  
  73.     };  
  74.   
  75.     private BroadcastReceiver mReceiver;  
  76.     private MediaPlayer mplayer;  
  77.   
  78.     @Override  
  79.     protected void onCreate(Bundle savedInstanceState) {  
  80.         super.onCreate(savedInstanceState);  
  81.   
  82.         mConfirm = getIntent().getBooleanExtra(Intent.EXTRA_KEY_CONFIRM, false);  
  83.         Slog.i(TAG, "onCreate(): confirm=" + mConfirm);  
  84.    
  85.         //if(getIntent().getBooleanExtra("can_be_cancel", false)) { //这行注释掉了: 然后当连上充电器后或者电量涨到20的时候,就取消倒计时关机  
  86.                 mReceiver = new BroadcastReceiver() {  
  87.                 @Override  
  88.                 public void onReceive(Context context, Intent intent) {  
  89.                     if(Intent.ACTION_BATTERY_OKAY.equals(intent.getAction())|  
  90.                         Intent.ACTION_POWER_CONNECTED.equals(intent.getAction())){  
  91.                         ShutDownWakeLock.releaseCpuLock();  
  92.                         myHandler.removeCallbacks(myRunnable);  
  93.                         if(mReceiver != null)  
  94.                             unregisterReceiver(mReceiver);  
  95.                         finish();  
  96.                     }  
  97.                 }  
  98.             };  
  99.   
  100.          IntentFilter filter=new IntentFilter(Intent.ACTION_POWER_CONNECTED);  
  101.          filter.addAction(Intent.ACTION_BATTERY_OKAY);  
  102.          registerReceiver(mReceiver, filter);  
  103.          //}  
  104.    
  105.   
  106.         PhoneStateListener mPhoneStateListener = new PhoneStateListener() { //如果正数秒呢,电话呼入了。取消自动关机  
  107.             @Override  
  108.             public void onCallStateChanged(int state, String ignored) {  
  109.                 if (state == TelephonyManager.CALL_STATE_RINGING) {  
  110.                     ShutDownWakeLock.releaseCpuLock();  
  111.                     myHandler.removeCallbacks(myRunnable);  
  112.                     finish();  
  113.                 }  
  114.             }  
  115.         };  
  116.   
  117.         TelephonyManager mTelephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);  
  118.         mTelephonyManager.listen(mPhoneStateListener,  
  119.                 PhoneStateListener.LISTEN_CALL_STATE);  
  120.         requestWindowFeature(android.view.Window.FEATURE_NO_TITLE);  
  121.         Window win = getWindow();  
  122.         win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED  
  123.                 | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);  
  124.         win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON  
  125.                     | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);  
  126.   
  127.         setContentView(new View(this));  
  128.         mDialog=new AlertDialog.Builder(this).create();  
  129.         mDialog.setTitle(com.android.internal.R.string.low_battery_shutdown_title);  
  130.         mDialog.setMessage(getString(com.android.internal.R.string.low_battery_shutdown_after_seconds,mSeconds));  
  131.         if(!getIntent().getBooleanExtra("cant_be_cancel_by_button"false)) {//读取配置文件,是否运行取消,然后根据这个显示取消自动关机按钮  
  132.             mDialog.setButton(DialogInterface.BUTTON_NEUTRAL,getText(com.android.internal.R.string.cancel), new DialogInterface.OnClickListener() {  
  133.                     @Override  
  134.                     public void onClick(DialogInterface dialog, int which) {  
  135.                         myHandler.removeCallbacks(myRunnable);  
  136.                         dialog.cancel();  
  137.                         if(mReceiver != null)  
  138.                             unregisterReceiver(mReceiver);  
  139.                         finish();  
  140.                     }});  
  141.         }  
  142.         mDialog.setCancelable(false);  
  143.         //mDialog.getWindow().setType( WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);  
  144.         mDialog.show();  
  145.         if(mConfirm == false){  
  146.             myHandler.postDelayed(myRunnable, 1000);  
  147.         }  
  148.         myHandler.post(new Runnable(){  
  149.             public void run(){  
  150.                 final ContentResolver cr = getContentResolver();  
  151.                 String path=Settings.System.getString(cr,Settings.System.NOTIFICATION_SOUND);  
  152.   
  153.                mplayer=new MediaPlayer();  
  154.                 try{  
  155.                     mplayer.reset();  
  156.                     mplayer.setDataSource("system/media/audio/ui/LowBattery.ogg");                    
  157.                     mplayer.prepare();  
  158.                     mplayer.start();  
  159.                     mplayer.setOnCompletionListener(new OnCompletionListener() {  
  160.                         @Override  
  161.                         public void onCompletion(MediaPlayer mp) {  
  162.                             if(null != mplayer){  
  163.                                 mplayer.stop();  
  164.                                 mplayer.release();  
  165.                                 mplayer = null;  
  166.                             }  
  167.                         }  
  168.                     });  
  169.                 }  
  170.                 catch(IOException e){  
  171.                       
  172.                 }  
  173.             }  
  174.         });  
  175.     }  
  176. }  
总结如下:这个类就是弹窗计时,提醒不连充电器就要关机了。

最后一个相关的类,文件路径:\frameworks\base\packages\SystemUI\src\com\android\systemui\BatteryMeterView.java

这个类是自定义控件,定义在手机状态栏里:

[java]  view plain  copy
  1. /* 
  2.  * Copyright (C) 2013 The Android Open Source Project 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  *      http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */  
  16.   
  17. package com.android.systemui;  
  18.   
  19. import android.content.BroadcastReceiver;  
  20. import android.content.Context;  
  21. import android.content.Intent;  
  22. import android.content.IntentFilter;  
  23. import android.content.res.Resources;  
  24. import android.content.res.TypedArray;  
  25. import android.graphics.Canvas;  
  26. import android.graphics.Paint;  
  27. import android.graphics.Path;  
  28. import android.graphics.PorterDuff;  
  29. import android.graphics.PorterDuffXfermode;  
  30. import android.graphics.Rect;  
  31. import android.graphics.RectF;  
  32. import android.graphics.Typeface;  
  33. import android.os.BatteryManager;  
  34. import android.os.Bundle;  
  35. import android.provider.Settings;  
  36. import android.util.AttributeSet;  
  37. import android.view.View;  
  38.   
  39. public class BatteryMeterView extends View implements DemoMode {  
  40.     public static final String TAG = BatteryMeterView.class.getSimpleName();  
  41.     public static final String ACTION_LEVEL_TEST = "com.android.systemui.BATTERY_LEVEL_TEST";  
  42.   
  43.     public static final boolean ENABLE_PERCENT = true;  
  44.     public static final boolean SINGLE_DIGIT_PERCENT = false;  
  45.     public static final boolean SHOW_100_PERCENT = false;  
  46.   
  47.     public static final int FULL = 96;  
  48.     public static final int EMPTY = 4;  
  49.   
  50.     public static final float SUBPIXEL = 0.4f;  // inset rects for softer edges  
  51.   
  52.     int[] mColors;  
  53.   
  54.     boolean mShowPercent = true;  
  55.     Paint mFramePaint, mBatteryPaint, mWarningTextPaint, mTextPaint, mBoltPaint;  
  56.     int mButtonHeight;  
  57.     private float mTextHeight, mWarningTextHeight;  
  58.   
  59.     private int mHeight;  
  60.     private int mWidth;  
  61.     private String mWarningString;  
  62.     private final int mChargeColor;  
  63.     private final float[] mBoltPoints;  
  64.     private final Path mBoltPath = new Path();  
  65.   
  66.     private final RectF mFrame = new RectF();  
  67.     private final RectF mButtonFrame = new RectF();  
  68.     private final RectF mClipFrame = new RectF();  
  69.     private final RectF mBoltFrame = new RectF();  
  70.   
  71.     private class BatteryTracker extends BroadcastReceiver {  
  72.         public static final int UNKNOWN_LEVEL = -1;  
  73.   
  74.         // current battery status  
  75.         int level = UNKNOWN_LEVEL;  
  76.         String percentStr;  
  77.         int plugType;  
  78.         boolean plugged;  
  79.         int health;  
  80.         int status;  
  81.         String technology;  
  82.         int voltage;  
  83.         int temperature;  
  84.         boolean testmode = false;  
  85.   
  86.         @Override  
  87.         public void onReceive(Context context, Intent intent) {  
  88.             final String action = intent.getAction();  
  89.             if (action.equals(Intent.ACTION_BATTERY_CHANGED)) { //接收电量改变的广播,取电池状态的属性  
  90.                 if (testmode && ! intent.getBooleanExtra("testmode"false)) return;  
  91.   
  92.                 level = (int)(100f  
  93.                         * intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)  
  94.                         / intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100));  
  95.   
  96.                 plugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);  
  97.                 plugged = plugType != 0;  
  98.                 health = intent.getIntExtra(BatteryManager.EXTRA_HEALTH,  
  99.                         BatteryManager.BATTERY_HEALTH_UNKNOWN);  
  100.                 status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,  
  101.                         BatteryManager.BATTERY_STATUS_UNKNOWN);  
  102.                 technology = intent.getStringExtra(BatteryManager.EXTRA_TECHNOLOGY);  
  103.                 voltage = intent.getIntExtra(BatteryManager.EXTRA_VOLTAGE, 0);  
  104.                 temperature = intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 0);  
  105.   
  106.                 setContentDescription(  
  107.                         context.getString(R.string.accessibility_battery_level, level));  
  108.                 postInvalidate();  
  109.             } else if (action.equals(ACTION_LEVEL_TEST)) {  
  110.                 testmode = true;  
  111.                 post(new Runnable() {  
  112.                     int curLevel = 0;  
  113.                     int incr = 1;  
  114.                     int saveLevel = level;  
  115.                     int savePlugged = plugType;  
  116.                     Intent dummy = new Intent(Intent.ACTION_BATTERY_CHANGED);  
  117.                     @Override  
  118.                     public void run() {  
  119.                         if (curLevel < 0) {  
  120.                             testmode = false;  
  121.                             dummy.putExtra("level", saveLevel);  
  122.                             dummy.putExtra("plugged", savePlugged);  
  123.                             dummy.putExtra("testmode"false);  
  124.                         } else {  
  125.                             dummy.putExtra("level", curLevel);  
  126.                             dummy.putExtra("plugged", incr > 0 ? BatteryManager.BATTERY_PLUGGED_AC : 0);  
  127.                             dummy.putExtra("testmode"true);  
  128.                         }  
  129.                         getContext().sendBroadcast(dummy);  
  130.   
  131.                         if (!testmode) return;  
  132.   
  133.                         curLevel += incr;  
  134.                         if (curLevel == 100) {  
  135.                             incr *= -1;  
  136.                         }  
  137.                         postDelayed(this200);  
  138.                     }  
  139.                 });  
  140.             }  
  141.         }  
  142.     }  
  143.   
  144.     BatteryTracker mTracker = new BatteryTracker();  
  145.   
  146.     @Override  
  147.     public void onAttachedToWindow() {  
  148.         super.onAttachedToWindow();  
  149.   
  150.         IntentFilter filter = new IntentFilter();  
  151.         filter.addAction(Intent.ACTION_BATTERY_CHANGED);  
  152.         filter.addAction(ACTION_LEVEL_TEST);  
  153.         final Intent sticky = getContext().registerReceiver(mTracker, filter);  
  154.         if (sticky != null) {  
  155.             // preload the battery level  
  156.             mTracker.onReceive(getContext(), sticky);  
  157.         }  
  158.     }  
  159.   
  160.     @Override  
  161.     public void onDetachedFromWindow() {  
  162.         super.onDetachedFromWindow();  
  163.   
  164.         getContext().unregisterReceiver(mTracker);  
  165.     }  
  166.   
  167.     public BatteryMeterView(Context context) {  
  168.         this(context, null0);  
  169.     }  
  170.   
  171.     public BatteryMeterView(Context context, AttributeSet attrs) {  
  172.         this(context, attrs, 0);  
  173.     }  
  174.   
  175.     public BatteryMeterView(Context context, AttributeSet attrs, int defStyle) {  
  176.         super(context, attrs, defStyle);  
  177.   
  178.         final Resources res = context.getResources();  
  179.         TypedArray levels = res.obtainTypedArray(R.array.batterymeter_color_levels);  
  180.         TypedArray colors = res.obtainTypedArray(R.array.batterymeter_color_values);  
  181.   
  182.         final int N = levels.length();  
  183.         mColors = new int[2*N];  
  184.         for (int i=0; i<N; i++) {  
  185.             mColors[2*i] = levels.getInt(i, 0);  
  186.             mColors[2*i+1] = colors.getColor(i, 0);  
  187.         }  
  188.         levels.recycle();  
  189.         colors.recycle();  
  190.         mShowPercent = ENABLE_PERCENT && 0 != Settings.System.getInt(  
  191.                 context.getContentResolver(), "status_bar_show_battery_percent"0);  
  192.   
  193.         mWarningString = context.getString(R.string.battery_meter_very_low_overlay_symbol);  
  194.   
  195.         mFramePaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
  196.         mFramePaint.setColor(res.getColor(R.color.batterymeter_frame_color));  
  197.         mFramePaint.setDither(true);  
  198.         mFramePaint.setStrokeWidth(0);  
  199.         mFramePaint.setStyle(Paint.Style.FILL_AND_STROKE);  
  200.         mFramePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));  
  201.   
  202.         mBatteryPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
  203.         mBatteryPaint.setDither(true);  
  204.         mBatteryPaint.setStrokeWidth(0);  
  205.         mBatteryPaint.setStyle(Paint.Style.FILL_AND_STROKE);  
  206.   
  207.         mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
  208.         mTextPaint.setColor(0xFFFFFFFF);  
  209.         Typeface font = Typeface.create("sans-serif-condensed", Typeface.NORMAL);  
  210.         mTextPaint.setTypeface(font);  
  211.         mTextPaint.setTextAlign(Paint.Align.CENTER);  
  212.   
  213.         mWarningTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
  214.         mWarningTextPaint.setColor(mColors[1]);  
  215.         font = Typeface.create("sans-serif", Typeface.BOLD);  
  216.         mWarningTextPaint.setTypeface(font);  
  217.         mWarningTextPaint.setTextAlign(Paint.Align.CENTER);  
  218.   
  219.         mChargeColor = getResources().getColor(R.color.batterymeter_charge_color);  
  220.   
  221.         mBoltPaint = new Paint();  
  222.         mBoltPaint.setAntiAlias(true);  
  223.         mBoltPaint.setColor(res.getColor(R.color.batterymeter_bolt_color));  
  224.         mBoltPoints = loadBoltPoints(res);  
  225.         setLayerType(View.LAYER_TYPE_SOFTWARE, null);  
  226.     }  
  227.   
  228.     private static float[] loadBoltPoints(Resources res) {  
  229.         final int[] pts = res.getIntArray(R.array.batterymeter_bolt_points);  
  230.         int maxX = 0, maxY = 0;  
  231.         for (int i = 0; i < pts.length; i += 2) {  
  232.             maxX = Math.max(maxX, pts[i]);  
  233.             maxY = Math.max(maxY, pts[i + 1]);  
  234.         }  
  235.         final float[] ptsF = new float[pts.length];  
  236.         for (int i = 0; i < pts.length; i += 2) {  
  237.             ptsF[i] = (float)pts[i] / maxX;  
  238.             ptsF[i + 1] = (float)pts[i + 1] / maxY;  
  239.         }  
  240.         return ptsF;  
  241.     }  
  242.   
  243.     @Override  
  244.     protected void onSizeChanged(int w, int h, int oldw, int oldh) {  
  245.         mHeight = h;  
  246.         mWidth = w;  
  247.         mWarningTextPaint.setTextSize(h * 0.75f);  
  248.         mWarningTextHeight = -mWarningTextPaint.getFontMetrics().ascent;  
  249.     }  
  250.   
  251.     private int getColorForLevel(int percent) {  
  252.         int thresh, color = 0;  
  253.         for (int i=0; i<mColors.length; i+=2) {  
  254.             thresh = mColors[i];  
  255.             color = mColors[i+1];  
  256.             if (percent <= thresh) return color;  
  257.         }  
  258.         return color;  
  259.     }  
  260.   
  261.     @Override  
  262.     public void draw(Canvas c) {  //通过电池属性绘制图标  
  263.         BatteryTracker tracker = mDemoMode ? mDemoTracker : mTracker;  
  264.         final int level = tracker.level;  
  265.   
  266.         if (level == BatteryTracker.UNKNOWN_LEVEL) return;  
  267.   
  268.         float drawFrac = (float) level / 100f;  
  269.         final int pt = getPaddingTop();  
  270.         final int pl = getPaddingLeft();  
  271.         final int pr = getPaddingRight();  
  272.         final int pb = getPaddingBottom();  
  273.         int height = mHeight - pt - pb;  
  274.         int width = mWidth - pl - pr;  
  275.   
  276.         mButtonHeight = (int) (height * 0.12f);  
  277.   
  278.         mFrame.set(00, width, height);  
  279.         mFrame.offset(pl, pt);  
  280.   
  281.         mButtonFrame.set(  //这个就是电池图标上边的突起  
  282.                 mFrame.left + width * 0.25f,  
  283.                 mFrame.top,  
  284.                 mFrame.right - width * 0.25f,  
  285.                 mFrame.top + mButtonHeight + 5 /*cover frame border of intersecting area*/);  
  286.   
  287.         mButtonFrame.top += SUBPIXEL;  
  288.         mButtonFrame.left += SUBPIXEL;  
  289.         mButtonFrame.right -= SUBPIXEL;  
  290.   
  291.         mFrame.top += mButtonHeight; //电池图标桶装图  
  292.         mFrame.left += SUBPIXEL;  
  293.         mFrame.top += SUBPIXEL;  
  294.         mFrame.right -= SUBPIXEL;  
  295.         mFrame.bottom -= SUBPIXEL;  
  296.   
  297.         // first, draw the battery shape  
  298.         c.drawRect(mFrame, mFramePaint);  
  299.   
  300.         // fill 'er up  
  301.         final int color = tracker.plugged ? mChargeColor : getColorForLevel(level); //根据等级获取颜色,如果是15以下,就是红色。15以上是白色  
  302.         mBatteryPaint.setColor(color);  
  303.   
  304.         if (level >= FULL) { //96时,电池图标里面填充完整,满电。  
  305.             drawFrac = 1f;  
  306.         } else if (level <= EMPTY) { //这个变量是说,当电量小于4的时候,电池图标里面没有填充色,空电量  
  307.             drawFrac = 0f;  
  308.         }  
  309.   
  310.         c.drawRect(mButtonFrame, drawFrac == 1f ? mBatteryPaint : mFramePaint);  
  311.   
  312.         mClipFrame.set(mFrame);  
  313.         mClipFrame.top += (mFrame.height() * (1f - drawFrac));  
  314.   
  315.         c.save(Canvas.CLIP_SAVE_FLAG);  
  316.         c.clipRect(mClipFrame);//在mFrame区域中保持mClipFrame的区域不变,参考下句注释  
  317.         c.drawRect(mFrame, mBatteryPaint);//在mFrame中,把除去mClipFrame的区域绘制。(电量状态,如果是15以下,就是红色。15以上是白色)  
  318.         c.restore();  
  319.   
  320.         if (tracker.plugged) { //这里就犀利了。这是说插上充电器就画一个闪电  
  321.             // draw the bolt  
  322.             final float bl = mFrame.left + mFrame.width() / 4.5f;  
  323.             final float bt = mFrame.top + mFrame.height() / 6f;  
  324.             final float br = mFrame.right - mFrame.width() / 7f;  
  325.             final float bb = mFrame.bottom - mFrame.height() / 10f;  
  326.             if (mBoltFrame.left != bl || mBoltFrame.top != bt  
  327.                     || mBoltFrame.right != br || mBoltFrame.bottom != bb) {  
  328.                 mBoltFrame.set(bl, bt, br, bb);  
  329.                 mBoltPath.reset();  
  330.                 mBoltPath.moveTo(  
  331.                         mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),  
  332.                         mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());  
  333.                 for (int i = 2; i < mBoltPoints.length; i += 2) {  
  334.                     mBoltPath.lineTo(  
  335.                             mBoltFrame.left + mBoltPoints[i] * mBoltFrame.width(),  
  336.                             mBoltFrame.top + mBoltPoints[i + 1] * mBoltFrame.height());  
  337.                 }  
  338.                 mBoltPath.lineTo(  
  339.                         mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),  
  340.                         mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());  
  341.             }  
  342.             c.drawPath(mBoltPath, mBoltPaint);  
  343.         } else if (level <= EMPTY) { //这个是空电量的时候,画一个红色的感叹号  
  344.             final float x = mWidth * 0.5f;  
  345.             final float y = (mHeight + mWarningTextHeight) * 0.48f;  
  346.             c.drawText(mWarningString, x, y, mWarningTextPaint);  
  347.         } else if (mShowPercent && !(tracker.level == 100 && !SHOW_100_PERCENT)) { //以文字显示电量。不过这个没有用到(除非mShowPercent变量获取的配置属性为true)  
  348.             mTextPaint.setTextSize(height *  
  349.                     (SINGLE_DIGIT_PERCENT ? 0.75f  
  350.                             : (tracker.level == 100 ? 0.38f : 0.5f)));  
  351.             mTextHeight = -mTextPaint.getFontMetrics().ascent;  
  352.   
  353.             final String str = String.valueOf(SINGLE_DIGIT_PERCENT ? (level/10) : level);  
  354.             final float x = mWidth * 0.5f;  
  355.             final float y = (mHeight + mTextHeight) * 0.47f;  
  356.             c.drawText(str,  
  357.                     x,  
  358.                     y,  
  359.                     mTextPaint);  
  360.         }  
  361.     }  
  362.   
  363.     private boolean mDemoMode;  
  364.     private BatteryTracker mDemoTracker = new BatteryTracker();  
  365.   
  366.     @Override  
  367.     public void dispatchDemoCommand(String command, Bundle args) {  
  368.         if (!mDemoMode && command.equals(COMMAND_ENTER)) {  
  369.             mDemoMode = true;  
  370.             mDemoTracker.level = mTracker.level;  
  371.             mDemoTracker.plugged = mTracker.plugged;  
  372.         } else if (mDemoMode && command.equals(COMMAND_EXIT)) {  
  373.             mDemoMode = false;  
  374.             postInvalidate();  
  375.         } else if (mDemoMode && command.equals(COMMAND_BATTERY)) {  
  376.            String level = args.getString("level");  
  377.            String plugged = args.getString("plugged");  
  378.            if (level != null) {  
  379.                mDemoTracker.level = Math.min(Math.max(Integer.parseInt(level), 0), 100);  
  380.            }  
  381.            if (plugged != null) {  
  382.                mDemoTracker.plugged = Boolean.parseBoolean(plugged);  
  383.            }  
  384.            postInvalidate();  
  385.         }  
  386.     }  
  387. }  

总结:这个类就是绘制状态栏里面电量图标的自定义控件。


以上就是大概的逻辑,现总结,用于备忘。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Google Android SDK开发范例大全(完整版)共4个分卷 目录 第1章 了解.深入.动手做. 1.1 红透半边天的Android 1.2 本书目的及涵盖范例范围 1.3 如何阅读本书 1.4 使用本书范例 1.5 参考网站 第2章 Android初体验 2.1 安装AndroidSDK与ADTplug-in 2.2 建立第一个Android项目(HelloAndroid!) 2.3 Android应用程序架构——从此开始 2.4 可视化的界面开发工具 2.5 部署应用程序到Android手机 第3章 用户人机界面 3.1 更改与显示文字标签——TextView标签的使用 3.2 更改手机窗口画面底色——drawable定义颜色常数的方法 3.3 更改TextView文字颜色——引用Drawable颜色常数及背景色 3.4 置换TextView文字——CharSequence数据类型与ResourceID应用 3.5 取得手机屏幕大小——DisplayMetrics取得画面宽高的方法 3.6 样式化的定型对象——Style样式的定义 3.7 简易的按钮事件——Button事件处理 3.8 手机页面的转换——setContentView的应用 3.9 调用另一个Activity——Intent对象的使用 3.10 不同Activity之间的数据传递——Bundle对象的实现 3.11 返回数据到前一个Activity——startActivityForResult方法 3.12 具有交互功能的对话框——AlertDialog窗口 3.13 置换文字颜色的机关——Button与TextView的交互 3.14 控制不同的文字字体——Typeface对象使用 3.15 如iPhone拖动相片特效——Gallery画廊 3.16 自制计算器——多按钮的整合应用 3.17 关于(About)程序信息——Menu功能菜单程序设计 3.18 程序加载中,请稍后——ProgressDialog与线程整合应用 3.19 全屏幕以按钮覆盖——动态产生按钮并最大化 3.20 今晚到哪儿打牙祭?——具选择功能的对话框 3.21 Android变脸——主题(Theme)实现 第4章 史上超豪华的手机控件 4.1 EditText与TextView共舞——setOnKeyListener事件 4.2 设计具有背景图的按钮——ImageButton的焦点及事件处理 4.3 给耶诞老人的信息——Toast对象的使用 4.4 我同意条款——CheckBox的isChecked属性 4.5 消费券采购列表——多选项CheckBox的应用 4.6 向左或向右——RadioGroup组与onCheckedChanged事件 4.7 专业相框设计——ImageView的堆栈应用 4.8 自定义下拉菜单模式——Spinner与setDropDownViewResource 4.9 动态添加/删除的Spinner菜单——ArrayList与Widget的依赖性 4.10 心爱小宝贝相片集——Gallery与衍生BaseAdapter容器 4.11 快速的搜索手机文件引擎——JavaI/O的应用 4.12 按钮也能随点击变换——ImageButton选择特效 4.13 具自动提示功能的菜单——AutoCompleteTextView与数组 4.14 数字及模拟小时钟设计——AnalogClock与DigitalClock的原理 4.15 动态输入日期与时间——DatePicker与TimePicker应用 4.16 猜猜红心A在那儿——ImageView点击事件与透明度处理 4.17 后台程序运行进度提示——ProgressBar与Handler的整合应用 4.18 动态文字排版——GridView与ArrayAdapter设计 4.19 在Activity里显示列表列表——ListView的布局 4.20 以动态列表配置选项——ListActivity与Menu整合技巧 4.21 查找程序根目录下所有文件——JavaI/O与ListActivity的结合.. 4.22 加载手机磁盘里的图文件——使用decodeFile方法 4.23 动态放大缩小ImageView里的图片——运用Matrix对象来缩放图文件 4.24 动态旋转图片——Bitmap与Matrix旋转ImageView 4.25 猜猜我在想什么——RadioButtonID 4.26 离开与关闭程序的弹出窗口——对话窗口上的ICON图标 第5章 交互式通信服务与手机控制 5.1 具有正则表达式的TextView——Linkify规则 5.2 ACTION!CALL!拨打电话——Intent.ACTION.CALL的使用 5.3 自制发送短信程序——SmsManager与PendingIntent对象 5.4 自制发送Email程序——Intent在Email上的使用 5.5 自制日历手机数据库——实现SQLiteOpenHelper 5.6 手机震动的节奏——Vibrator对象及周期运用 5.7 图文可视化提醒——Toast与LinearLayoutView 5.8 状态栏的图标与文字提醒——NotificationManager与Notification对象的应用 5.9 搜索手机通讯录自动完成——使用ContentResolver 5.10 取得联系人资料——Provider.Contact的使用 5.11 制作有图标的文件资源管理器——自定义Adapter对象 5.12 还原手机默认桌面——重写clearWallpaper方法 5.13 置换手机背景图——Gallery与setWallpaper整合实现 5.14 撷取手机现存桌面——getWallpaper与setImageDrawable 5.15 文件资源管理器再进化——JavaI/O修改文件名及删除 5.16 取得目前File与Cache的路径——getCacheDir与getFilesDir 5.17 打开/关闭WiFi服务——WifiManager状态判断 5.18 取得SIM卡内的信息——TelephonyManager的应用 5.19 调用拨号按钮——打电话CALL_BUTTON 5.20 DPAD按键处理——onKeyDown事件与Layout坐标交互 5.21 任务管理器正在运行的程序——RunningTaskInfo 5.22 动态更改屏幕方向——LANDSCAPE与PORTRAIT 5.23 系统设置更改事件——onConfigurationChanged信息处理 5.24 取得电信网络与手机相关信息——TelephonyManager与android.provider.Settings.System的应用 第6章 手机自动服务纪实 6.1 您有一条短信popup提醒——常驻BroadcastReceiver的应用 6.2 手机电池计量还剩多少——使用BroadcastReceiver捕捉Intent.ACTION_BATTERY_CHANGED 6.3 群发拜年短信给联系人——ACTION_PICK与Uri对象 6.4 开始与停止系统服务——Service与Runnable整合并用 6.5 通过短信发送email通知——BroadcastReceiver与Intent整合 6.6 手机拨接状态——PhoneStateListener之onCallStateChanged 6.7 有来电,发送邮件通知——PhoneStateListener与ACTION_SEND 6.8 存储卡剩余多少容量——Environment加StatFs 6.9 访问本机内存与存储卡——File的创建与删除 6.10 实现可定时响起的闹钟——PendingIntent与AlarmManager的运用 6.11 黑名单来电自动静音——PhoneStateListener与AudioManager 6.12 手机翻背面即静音震动——SensorListener及AudioManager整合应用 6.13 指定时间置换桌面背景——多AlarmManager事件处理 6.14 判断发送短信后的状态——BroadcastReceiver聆听PendingIntent 6.15 后台服务送出广播信息——sendBroadcast与BroadcastReceiver 6.16 开机程序设计——receiver与intent-filter协同作业 6.17 双向短信常驻服务——Service与receiver实例 第7章 娱乐多媒体 7.1 访问Drawable资源的宽高——ContextMenu与Bitmap的应用 7.2 绘制几何图形——使用android.graphics类 7.3 手机屏幕保护程序——FadeIn/FadeOut特效与运行线程 7.4 用手指移动画面里的照片——onTouchEvent事件判断 7.5 加载存储卡的Gallery相簿——FileArrayList 7.6 取得手机内置媒体里的图文件——ACTION_GET_CONTENT取回InputStream 7.7 相片导航向导与设置背景桌面——ImageSwitcher与Gallery 7.8 调整音量大小声——AudioManager控制音量 7.9 播放mp3资源文件——raw文件夹与MediaPlayer的使用 7.10 播放存储卡里的mp3音乐——MediaPlayer.setDataSource 7.11 自制录音/播放录音程序——MediaRecorder与AudioEncoder 7.12 通过收到短信开始秘密录音——MediaRecorder与BroadcastReceiver实例 7.13 内置影片播放器载入3gp电影——VideoViewWidget 7.14 自制3gp影片播放器——MediaPlayer与实现SurfaceView 7.15 相机预览及拍照临时文件——Camera及PictureCallback事件 第8章 当Android与Internet接轨 8.1 HTTPGET/POST传递参数——HTTP连接示范 8.2 在程序里浏览网页——WebView.loadUrl 8.3 嵌入HTML标记的程序——WebView.loadData 8.4 设计前往打开网页功能——Intent与Uri.parse 8.5 将网络图像网址放入Gallery中显示——URL.URLConnection.BaseAdapter 8.6 即时访问网络图文件展示——HttpURLConnection 8.7 手机气象局,实时卫星云图——HttpURLConnection与URLConnection和运行线程 8.8 通过网络播放MP3——Runnable存储FileOutputStream技巧 8.9 设置远程下载音乐为手机铃声——RingtoneManager与铃声存放路径 8.10 远程下载桌面背景图案——URLConnection与setWallpaper()搭配 8.11 将手机文件上传至网站服务器——模拟HTTPFORM的POSTACTION 8.12 移动博客发布器——以XML-RPC达成远程过程调用 8.13 移动RSS阅读器——利用SAXParser解析XML 8.14 远程下载安装Android程序——APKInstaller的应用 8.15 手机下载看3gp影片——Runnable混搭SurfaceView 8.16 访问网站LoginAPI——远程服务器验证程序运行权限 8.17 地震速报!——HttpURLConnection与Service侦测服务 第9章 Google服务与Android混搭 9.1 Google帐号验证Token——AuthSub 9.2 Google搜索——AutoCompleteTextView与GoogleSearchAPI 9.3 前端产生QRCode二维条形码——GoogleChartAPI 9.4 以经纬度查找目的地位置——GeoPoint与MapView的搭配运用 9.5 GPSGoogle地图——LocationListener与MapView实时更新 9.6 移动版GoogleMap——Geocoder反查Address对象 9.7 规划导航路径——DirectionsRoute 9.8 移动设备上的Picasa相册——GooglePicasaAPI 9.9 随身翻译机——GoogleTranslateAPI 第10章 创意Android程序设计 10.1 手机手电筒——PowerManager控制WakeLock并改变手机亮度 10.2 GPS轨迹记录器——利用LocationListener在地图上画图并换算距离 10.3 女性贴身看护——AlarmManager.DatePicker.TimePicker 10.4 手机QRCode二维条形码生成器——Canvas与SurfaceHolder绘图 10.5 AndroidQRCode二维条形码扫描仪——BitmapFactory.decodeByteArray 10.6 上班族今天中午要吃什么——热量骰子地图 10.7 掷杯筊——把手机放在空中甩事件处理...

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值