整体思路概览
①由于要显示电池图标,因此需要一组对应的小icon,程序算出当前电量之后,就在状态栏上显示对应的icon即可。本程序用到的icon已经提供在附件,下载后将里面的icons直接复制到res\drawable-hdpi中即可。
②由于电池更新是一个长期存在的过程,因此需要将我们的程序注册为系统服务,以便长期生存于Android系统中。
③服务不会自行启动,因此我们需要为自己的服务创建一个Activity,在第一次启动本程序的Activity时创建并启动我们的监控服务,之后的事便交由服务处理,Activity暂时未有太大作用(之后还可以在Activity中显示电池当前的各种基本属性等等,本文不作介绍),当然,我们也可以不要Activity,直接将我们的服务设置为开机启动即可,关于开机启动的事项已在【Android开机启动Activity或Service的方法】一文中详细描述,恕不赘述。
④为了方便起见,应该制作本程序的桌面小工具,也就用到了AppWidget方面的知识,稍后一一道来。
分而治之
小麻雀的心脏——电池监控服务MonitorService
ACTION_BATTERY_CHANGED 的完整代码,我会在可能费解的地方批注,希望能帮助大家理解。
001 | package com.billhoo.study; |
003 | import android.app.Notification; //通知,即显示在屏幕左上角的小图标 |
004 | import android.app.NotificationManager; |
005 | import android.app.PendingIntent; |
006 | import android.app.Service; |
007 | import android.content.BroadcastReceiver; |
008 | import android.content.Context; |
009 | import android.content.Intent; |
010 | import android.content.IntentFilter; |
011 | import android.os.BatteryManager; |
012 | import android.os.IBinder; |
014 | public class MonitorService extends Service { |
016 | public IBinder onBind(Intent intent) { |
021 | public void onStart(Intent intent, int startId) { |
022 | // 定义电池电量更新广播的过滤器,只接受带有ACTION_BATTERRY_CHANGED事件的Intent |
023 | IntentFilter batteryChangedReceiverFilter = new IntentFilter(); |
024 | batteryChangedReceiverFilter.addAction(Intent.ACTION_BATTERY_CHANGED); |
026 | // 向系统注册batteryChangedReceiver接收器,本接收器的实现见代码字段处 |
027 | registerReceiver(batteryChangedReceiver, batteryChangedReceiverFilter); |
029 | // 实例化Notification通知的管理器,即字段notification manager |
030 | notifyManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); |
032 | // 由于初始化本服务时系统可能没有发出ACTION_BATTERY_CHANGED广播,那么刚才注册的那个接收器将不会在本服务启动时被激活,这种情况下就无法显示当前电量,因此在这里添加一个匿名广播接收器。 |
033 | new BroadcastReceiver() { |
035 | public void onReceive(Context context, Intent intent) { |
036 | int level = intent.getIntExtra( "level" , 0 ); //电池电量等级 |
037 | int scale = intent.getIntExtra( "scale" , 100 ); //电池满时百分比 |
038 | int status = intent.getIntExtra( "status" , 0 ); //电池状态 |
041 | if (status == BatteryManager.BATTERY_STATUS_CHARGING) |
042 | notification = getNotification(getChargingIcon(level * 100 / scale), |
043 | "电池监控" , System.currentTimeMillis(), "电池电量" , "正在充电" ); |
045 | notification = getNotification(getStateIcon(level * 100 / scale), |
046 | "电池监控" , System.currentTimeMillis(), "电池电量" , "请及时充电" ); |
048 | //向系统发送此通知,为方便起见,通知ID简单设为0 |
049 | notifyManager.notify( 0 , notification); |
055 | public void onDestroy() { |
056 | //当本服务终止时,通过通知ID注销左上角的通知 |
057 | notifyManager.cancel( 0 ); |
060 | // --------------------------------------私有方法------------------------------ |
062 | private Notification getNotification( int notificationIcon, String tickerText, |
063 | long when, String contentTitle, String contentText) { |
064 | // 定义该notification的扩展文本和intent |
065 | Notification notification = new Notification(notificationIcon, tickerText, |
067 | Context context = getApplicationContext(); |
068 | Intent notificationIntent = new Intent(context, BatteryInfo. class ); |
069 | PendingIntent contentIntent = PendingIntent.getActivity( this , 0 , |
070 | notificationIntent, 0 ); |
071 | notification.setLatestEventInfo(context, contentTitle, contentText, |
074 | // 设置本状态出现在“正在进行”而非“通知”栏目,不允许用户清除状态图标 |
075 | notification.flags |= Notification.FLAG_ONGOING_EVENT; |
080 | private int getStateIcon( int batteryHealth) { |
081 | if (batteryHealth >= 0 || batteryHealth <= 100 ) |
082 | return batteryStateIcons[batteryHealth]; //这里是为了方便起见而硬编码的资源数组,直接从R.java文件中提取的十六进制资源ID |
083 | return R.drawable.stat_sys_battery_unknown; |
087 | private int getChargingIcon( int batteryHealth) { |
088 | if (batteryHealth >= 0 && batteryHealth < 5 ) |
089 | return R.drawable.stat_sys_battery_charge_anim0; |
090 | if (batteryHealth >= 5 && batteryHealth < 10 ) |
091 | return R.drawable.stat_sys_battery_charge_anim01; |
092 | if (batteryHealth >= 10 && batteryHealth < 15 ) |
093 | return R.drawable.stat_sys_battery_charge_anim02; |
094 | if (batteryHealth >= 15 && batteryHealth < 20 ) |
095 | return R.drawable.stat_sys_battery_charge_anim03; |
096 | if (batteryHealth >= 20 && batteryHealth < 25 ) |
097 | return R.drawable.stat_sys_battery_charge_anim04; |
098 | if (batteryHealth >= 25 && batteryHealth < 30 ) |
099 | return R.drawable.stat_sys_battery_charge_anim05; |
100 | if (batteryHealth >= 30 && batteryHealth < 35 ) |
101 | return R.drawable.stat_sys_battery_charge_anim06; |
102 | if (batteryHealth >= 35 && batteryHealth < 40 ) |
103 | return R.drawable.stat_sys_battery_charge_anim07; |
104 | if (batteryHealth >= 40 && batteryHealth < 45 ) |
105 | return R.drawable.stat_sys_battery_charge_anim08; |
106 | if (batteryHealth >= 45 && batteryHealth < 50 ) |
107 | return R.drawable.stat_sys_battery_charge_anim09; |
108 | if (batteryHealth >= 50 && batteryHealth < 55 ) |
109 | return R.drawable.stat_sys_battery_charge_anim10; |
110 | if (batteryHealth >= 55 && batteryHealth < 60 ) |
111 | return R.drawable.stat_sys_battery_charge_anim11; |
112 | if (batteryHealth >= 60 && batteryHealth < 65 ) |
113 | return R.drawable.stat_sys_battery_charge_anim12; |
114 | if (batteryHealth >= 65 && batteryHealth < 70 ) |
115 | return R.drawable.stat_sys_battery_charge_anim13; |
116 | if (batteryHealth >= 70 && batteryHealth < 75 ) |
117 | return R.drawable.stat_sys_battery_charge_anim14; |
118 | if (batteryHealth >= 75 && batteryHealth < 80 ) |
119 | return R.drawable.stat_sys_battery_charge_anim15; |
120 | if (batteryHealth >= 80 && batteryHealth < 85 ) |
121 | return R.drawable.stat_sys_battery_charge_anim16; |
122 | if (batteryHealth >= 85 && batteryHealth < 90 ) |
123 | return R.drawable.stat_sys_battery_charge_anim17; |
124 | if (batteryHealth >= 90 && batteryHealth < 95 ) |
125 | return R.drawable.stat_sys_battery_charge_anim18; |
126 | if (batteryHealth >= 95 && batteryHealth < 100 ) |
127 | return R.drawable.stat_sys_battery_charge_anim19; |
128 | if (batteryHealth == 100 ) |
129 | return R.drawable.stat_sys_battery_charge_animfull; |
130 | return R.drawable.stat_sys_battery_unknown; |
133 | // -------------------------------私有字段-------------------------------------- |
134 | private NotificationManager notifyManager = null ; |
135 | private Notification notification = null ; |
137 | //这里是为了方便起见而硬编码的icon资源数组,直接从R.java文件中提取的十六进制资源ID,本数组的0号元素0x7f020002表示资源stat_sys_battery_0.png(见附件),以后累加。不同配置的ID可能不同,请读者自行修改。 |
138 | private int batteryStateIcons[] = { 0x7f020002 , 0x7f020003 , 0x7f020004 , |
139 | 0x7f020005 , 0x7f020006 , 0x7f020007 , 0x7f020008 , 0x7f020009 , 0x7f02000a , |
140 | 0x7f02000b , 0x7f02000c , 0x7f02000d , 0x7f02000e , 0x7f02000f , 0x7f020010 , |
141 | 0x7f020011 , 0x7f020012 , 0x7f020013 , 0x7f020014 , 0x7f020015 , 0x7f020016 , |
142 | 0x7f020017 , 0x7f020018 , 0x7f020019 , 0x7f02001a , 0x7f02001b , 0x7f02001c , |
143 | 0x7f02001d , 0x7f02001e , 0x7f02001f , 0x7f020020 , 0x7f020021 , 0x7f020022 , |
144 | 0x7f020023 , 0x7f020024 , 0x7f020025 , 0x7f020026 , 0x7f020027 , 0x7f020028 , |
145 | 0x7f020029 , 0x7f02002a , 0x7f02002b , 0x7f02002c , 0x7f02002d , 0x7f02002e , |
146 | 0x7f02002f , 0x7f020030 , 0x7f020031 , 0x7f020032 , 0x7f020033 , 0x7f020034 , |
147 | 0x7f020035 , 0x7f020036 , 0x7f020037 , 0x7f020038 , 0x7f020039 , 0x7f02003a , |
148 | 0x7f02003b , 0x7f02003c , 0x7f02003d , 0x7f02003e , 0x7f02003f , 0x7f020040 , |
149 | 0x7f020041 , 0x7f020042 , 0x7f020043 , 0x7f020044 , 0x7f020045 , 0x7f020046 , |
150 | 0x7f020047 , 0x7f020048 , 0x7f020049 , 0x7f02004a , 0x7f02004b , 0x7f02004c , |
151 | 0x7f02004d , 0x7f02004e , 0x7f02004f , 0x7f020050 , 0x7f020051 , 0x7f020052 , |
152 | 0x7f020053 , 0x7f020054 , 0x7f020055 , 0x7f020056 , 0x7f020057 , 0x7f020058 , |
153 | 0x7f020059 , 0x7f02005a , 0x7f02005b , 0x7f02005c , 0x7f02005d , 0x7f02005e , |
154 | 0x7f02005f , 0x7f020060 , 0x7f020061 , 0x7f020062 , 0x7f020063 , 0x7f020064 , |
155 | 0x7f020065 , 0x7f02007b }; |
158 | private BroadcastReceiver batteryChangedReceiver = new BroadcastReceiver() { |
159 | public void onReceive(Context context, Intent intent) { |
160 | int level = intent.getIntExtra( "level" , 0 ); |
161 | int scale = intent.getIntExtra( "scale" , 100 ); |
162 | int status = intent.getIntExtra( "status" , 0 ); |
165 | if (status == BatteryManager.BATTERY_STATUS_CHARGING) |
166 | notification = getNotification(getChargingIcon(level * 100 / scale), |
167 | "Battery Monitor" , System.currentTimeMillis(), "电池电量" , "正在充电" ); |
169 | notification = getNotification(getStateIcon(level * 100 / scale), |
170 | "Battery Monitor" , System.currentTimeMillis(), "电池电量" , "请及时充电" ); |
171 | notifyManager.notify( 0 , notification); |
光有心,不跳怎么行?——BatteryMonitor
服务在系统配置文件中进行注册。
2 | < service android:name = ".MonitorService" android:enabled = "true" /> |
Bill的另一篇文章【Android开机启动Activity或Service的方法】),下面,我们将在一个Activity中启动MonitorService服务。 在AndroidManifest.xml中进行注册
2 | < activity android:name = ".BatteryMonitor" android:label = "@string/battery_monitor_name" > |
4 | < action android:name = "android.intent.action.MAIN" /> |
5 | < category android:name = "android.intent.category.LAUNCHER" /> |
中编码启动我们的MonitorService服务即可
01 | package com.billhoo.study; |
03 | import android.app.Activity; |
04 | import android.content.Intent; |
05 | import android.os.Bundle; |
08 | public class BatteryMonitor extends Activity { |
09 | /** Called when the activity is first created. */ |
11 | public void onCreate(Bundle savedInstanceState) { |
12 | super .onCreate(savedInstanceState); |
13 | setContentView(R.layout.main); |
16 | startService( new Intent( this , MonitorService. class )); |
,看看左上角是不是出现了一个小电池图标,并显示着当前电量? 麻雀虽小,五脏俱全——MonitorWidgetProvider ,为麻雀穿上衣服 用户会将自己喜爱的小程序放在桌面以便快速启动,当然我们就不能放过这个机会! 还做不到这一点,难道我们应该期望用户在他的程序列表中长按我们的Activity,然后使出吃奶的劲儿把它扔在桌面上吗?不,他们通常长按桌面,然后轻点一个自己中意的Widget插件便能满足他们的需求。 ,一来方便用户查看电池状态(仅凭左上角的通知有时候并不能满足用户的需求),二来方便用户随时启动本程序附带的电池详细数据报告(本功能并未在本文中实现)。 本身就是一个广播接收器(BroadcastReceiver),因此我们最初企图直接用这个Widget接受ACTION_BATTERY_CHANGED广播并作出反应,但却发现这个接收器并不会接受电池更新的广播(不论我们在配置文件中配置与否)。所以只能另寻出路,我们想到,可以使用一个单独的为本Widget服务的Service,该Service检测到电池电量更新后就更新桌面的Widget显示。于是便有以下Widget及其配套Service的实现。 创建一个配置文件,用于描述本Widget在桌面显示时是个什么样子。
01 | <!-- 项目路径\res\layout\monitor_widget_layout.xml --> |
02 | <? xml version = "1.0" encoding = "utf-8" ?> |
03 | < LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android" |
04 | android:orientation = "vertical" |
05 | android:layout_width = "72dip" |
06 | android:layout_height = "72dip" > |
07 | < ImageView android:id = "@+id/monitor_widget_view" |
08 | android:layout_width = "fill_parent" |
09 | android:layout_height = "fill_parent" |
11 | android:src = "@drawable/vertical_battery_unknown" /> |
1 | <!-- 项目路径\res\xml\monitor_widget_info.xml (xml文件夹是自行创建的)--> |
2 | <? xml version = "1.0" encoding = "utf-8" ?> |
3 | < appwidget-providerxmlns:androidappwidget-providerxmlns:android = "http://schemas.android.com/apk/res/android" android:initialLayout = "@layout/monitor_widget_layout" |
4 | android:minWidth = "72dip" android:minHeight = "72dip" /> |
配置文件中注册我们的Widget
1 | < receiver android:name = ".MonitorWidgetProvider" > |
3 | < action android:name = "android.appwidget.action.APPWIDGET_UPDATE" /> |
4 | < action android:name = "android.intent.action.BATTERY_CHANGED" /> |
6 | < meta-data android:name = "android.appwidget.provider" |
7 | android:resource = "@xml/monitor_widget_info" /> |
的实现代码
01 | package com.billhoo.study; |
03 | import android.appwidget.AppWidgetManager; |
04 | import android.appwidget.AppWidgetProvider; |
05 | import android.content.Context; |
06 | import android.content.Intent; |
08 | public class MonitorWidgetProvider extends AppWidgetProvider |
10 | //当Widget第一次被启动时,启动配套服务 |
12 | public void onEnabled(Context context) { |
13 | context.startService( new Intent(context, |
15 | WidgetUpdateService. class )); |
20 | public void onUpdate(Context context, AppWidgetManager |
24 | super .onUpdate(context, appWidgetManager, appWidgetIds); |
25 | context.startService( new Intent(context, |
27 | WidgetUpdateService. class )); |
30 | // 当桌面上这个widget的所有实体都被删除后,停止配套服务 |
32 | public void onDisabled(Context context) { |
33 | context.stopService( new Intent(context, WidgetUpdateService. class )); |
编写配套的Service。
中注册这个服务。
1 | < service android:name = ".WidgetUpdateService" android:enabled = "true" /> |
然后编写Java 代码:
001 | package com.billhoo.study; |
003 | import android.app.Service; |
004 | import android.appwidget.AppWidgetManager; |
005 | import android.content.BroadcastReceiver; |
006 | import android.content.ComponentName; |
007 | import android.content.Context; |
008 | import android.content.Intent; |
009 | import android.content.IntentFilter; |
010 | import android.os.BatteryManager; |
011 | import android.os.IBinder; |
012 | import android.widget.RemoteViews; |
014 | //用以根据电池电量更新而刷新桌面Widget的服务 |
015 | public class WidgetUpdateService extends Service { |
017 | public IBinder onBind(Intent intent) { |
022 | public void onStart(Intent intent, int startId) { |
023 | // 向系统注册电池电量更新广播,只接受带有ACTION_BATTERRY_CHANGED事件的Intent |
024 | registerReceiver( this .bCR, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); |
026 | bCR = new BroadcastReceiver() { |
028 | public void onReceive(Context context, Intent intent) { |
029 | int level = intent.getIntExtra( "level" , 0 ); |
030 | int scale = intent.getIntExtra( "scale" , 100 ); |
031 | int status = intent.getIntExtra( "status" , 0 ); |
032 | int batteryHealth = level * 100 / scale; |
035 | AppWidgetManager widgetManager = AppWidgetManager.getInstance(context); |
036 | RemoteViews widgetView = new RemoteViews(context.getPackageName(), |
037 | R.layout.monitor_widget_layout); |
040 | if (status == BatteryManager.BATTERY_STATUS_CHARGING) { |
041 | icon = getChargingIcon(batteryHealth); |
042 | widgetView.setImageViewResource(R.id.monitor_widget_view, icon |
046 | widgetView.setImageViewResource(R.id.monitor_widget_view, |
047 | getStateIcon(batteryHealth)); |
048 | widgetManager.updateAppWidget( new ComponentName(context, |
049 | MonitorWidgetProvider. class ), widgetView); |
054 | private BroadcastReceiver bCR = null ; |
057 | private int getStateIcon( int batteryHealth) { |
058 | if (batteryHealth >= 0 || batteryHealth <= 100 ) |
059 | return batteryStateIcon[batteryHealth / 5 ]; |
060 | return R.drawable.vertical_battery_unknown; |
064 | private int getChargingIcon( int batteryHealth) { |
065 | if (batteryHealth >= 0 && batteryHealth < 5 ) |
066 | return R.drawable.vertical_charging_00; |
067 | if (batteryHealth >= 5 && batteryHealth < 10 ) |
068 | return R.drawable.vertical_charging_01; |
069 | if (batteryHealth >= 10 && batteryHealth < 15 ) |
070 | return R.drawable.vertical_charging_02; |
071 | if (batteryHealth >= 15 && batteryHealth < 20 ) |
072 | return R.drawable.vertical_charging_03; |
073 | if (batteryHealth >= 20 && batteryHealth < 25 ) |
074 | return R.drawable.vertical_charging_04; |
075 | if (batteryHealth >= 25 && batteryHealth < 30 ) |
076 | return R.drawable.vertical_charging_05; |
077 | if (batteryHealth >= 30 && batteryHealth < 35 ) |
078 | return R.drawable.vertical_charging_06; |
079 | if (batteryHealth >= 35 && batteryHealth < 40 ) |
080 | return R.drawable.vertical_charging_07; |
081 | if (batteryHealth >= 40 && batteryHealth < 45 ) |
082 | return R.drawable.vertical_charging_08; |
083 | if (batteryHealth >= 45 && batteryHealth < 50 ) |
084 | return R.drawable.vertical_charging_09; |
085 | if (batteryHealth >= 50 && batteryHealth < 55 ) |
086 | return R.drawable.vertical_charging_10; |
087 | if (batteryHealth >= 55 && batteryHealth < 60 ) |
088 | return R.drawable.vertical_charging_11; |
089 | if (batteryHealth >= 60 && batteryHealth < 65 ) |
090 | return R.drawable.vertical_charging_12; |
091 | if (batteryHealth >= 65 && batteryHealth < 70 ) |
092 | return R.drawable.vertical_charging_13; |
093 | if (batteryHealth >= 70 && batteryHealth < 75 ) |
094 | return R.drawable.vertical_charging_14; |
095 | if (batteryHealth >= 75 && batteryHealth < 80 ) |
096 | return R.drawable.vertical_charging_15; |
097 | if (batteryHealth >= 80 && batteryHealth < 85 ) |
098 | return R.drawable.vertical_charging_16; |
099 | if (batteryHealth >= 85 && batteryHealth < 90 ) |
100 | return R.drawable.vertical_charging_17; |
101 | if (batteryHealth >= 90 && batteryHealth < 95 ) |
102 | return R.drawable.vertical_charging_18; |
103 | if (batteryHealth >= 95 && batteryHealth < 100 ) |
104 | return R.drawable.vertical_charging_19; |
105 | if (batteryHealth == 100 ) |
106 | return R.drawable.vertical_battery_full; |
107 | return R.drawable.stat_sys_battery_unknown; |
110 | private int batteryStateIcon[] = { 0x7f02007d , 0x7f02007e , 0x7f02007f , |
111 | 0x7f020080 , 0x7f020081 , 0x7f020082 , 0x7f020083 , 0x7f020084 , 0x7f020085 , |
112 | 0x7f020086 , 0x7f020087 , 0x7f020088 , 0x7f020089 , 0x7f02008a , 0x7f02008b , |
113 | 0x7f02008c , 0x7f02008d , 0x7f02008e , 0x7f02008f , 0x7f020090 , 0x7f020091 }; |
自带的电池图标了,何乐不为?
原文:http://www.open-open.com/lib//view/open1327829183952.html