整体思路概览
①由于要显示电池图标,因此需要一组对应的小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