最近在做一個Demo,監聽電量變化,持續記錄電量的變化。
一開始我是在Activity中注冊廣播,可以正常監聽電量狀態,但隨着Activity生命周期變化,不能持續監聽電量。就想到用service來持續監聽,嘗試了多次靜態注冊,發現竟然接收不到電量變化的廣播。!!!!????
后來上網搜索,發現有五個不能靜態注冊的廣播,這里記錄一下,免得下次再后知后覺的發現並驚訝於自己的笨拙。
不能靜態注冊的廣播:
android.intent.action.SCREEN_ON
android.intent.action.SCREEN_OFF
android.intent.action.BATTERY_CHANGED
android.intent.action.CONFIGURATION_CHANGED
android.intent.action.TIME_TICK
原因(有以下幾種說法,提供給大家參考):
1.提高系統效率:這兩個事件是android的基本事件,如果大多數程序監聽,會大大的拖慢整個系統,所以android不鼓勵我們在后台監聽這兩個事件。
2.因為有序廣播的優先級問題。以上這些廣播中,靜態注冊時,系統的優先級大於應用,並且系統阻止了廣播的向下傳播。又因在Android 的廣播機制中,動態注冊的優先級是要高於靜態注冊優先級的。故用動態注冊代替靜態注冊。
3.系統安全問題。
解決方式(以android.intent.action.BATTERY_CHANGED為例):
動態注冊不能放到activity中,因為動態注冊必須要在activity消亡的時候調用unregisterReceiver,會隨着activity的解鎖消失而不能再接收廣播。一般的辦法是在activity起來后馬上start一個service,這個service里動態注冊一個broadcastreceiver,service必須常駐在系統內,所以要接收開機消息android.intent.action.BOOT_COMPLETED。
持續監聽電量變化實例:
1,在Broadcast中啟動service代碼,開機自動啟動:
private String boot_action ="android.intent.action.BOOT_COMPLETED"; @Override public void onReceive(Context context, Intent intent) { if(boot_action.equals(action)){ Log.i(TAG, "android.intent.action.BOOT_COMPLETED"); Intent in0 = new Intent(context, BatteryService.class); context.startService(in0); } }
不要忘記在Manifest.xml中注冊開機啟動廣播:
1 <receiver android:name="BootBroadcastReceiver" > 2 <intent-filter> 3 <action android:name="android.intent.action.BOOT_COMPLETED" /> 4 </intent-filter> 5 </receiver>
2,在service中動態注冊廣播,處理電量,service停止后自動啟動
1 import java.text.SimpleDateFormat; 2 3 import android.app.Service; 4 import android.content.BroadcastReceiver; 5 import android.content.Context; 6 import android.content.Intent; 7 import android.content.IntentFilter; 8 import android.os.BatteryManager; 9 import android.os.IBinder; 10 import android.util.Log; 11 12 public class BatteryService extends Service { 13 14 private static final String TAG = "BatteryReceiver"; 15 @Override 16 public IBinder onBind(Intent intent) { 17 return null; 18 } 19 20 @Override 21 public void onCreate() { 22 super.onCreate(); 23 Log.i(TAG, "onCreate--------------"); 24 IntentFilter batteryfilter = new IntentFilter(); 25 batteryfilter.addAction(Intent.ACTION_BATTERY_CHANGED); 26 registerReceiver(batteryReceiver, batteryfilter); 27 } 28 29 @Override 30 public void onStart(Intent intent, int startId) { 31 super.onStart(intent, startId); 32 } 33 34 @Override 35 public int onStartCommand(Intent intent, int flags, int startId) { 36 Log.i(TAG, "onStartCommand--------------"); 37 return Service.START_STICKY; // 38 } 39 40 @Override 41 public void onDestroy() { 42 Log.i(TAG, "onDestroy--------------"); 43 super.onDestroy(); 44 this.unregisterReceiver(batteryReceiver); 45 46 } 47 // 接收電池信息更新的廣播 48 private BroadcastReceiver batteryReceiver = new BroadcastReceiver(){ 49 @Override 50 public void onReceive(Context context, Intent intent) { 51 Log.i(TAG, "BatteryReceiver--------------"); 52 String action = intent.getAction(); 53 Log.i(TAG, " 0 action:"+ action); 54 Log.i(TAG, "ACTION_BATTERY_CHANGED"); 55 int status = intent.getIntExtra("status", 0); 56 int health = intent.getIntExtra("health", 0); 57 boolean present = intent.getBooleanExtra("present", false); 58 int level = intent.getIntExtra("level", 0); 59 int scale = intent.getIntExtra("scale", 0); 60 int icon_small = intent.getIntExtra("icon-small", 0); 61 int plugged = intent.getIntExtra("plugged", 0); 62 int voltage = intent.getIntExtra("voltage", 0); 63 int temperature = intent.getIntExtra("temperature", 0); 64 String technology = intent.getStringExtra("technology"); 65 66 String statusString = ""; 67 switch (status) { 68 case BatteryManager.BATTERY_STATUS_UNKNOWN: 69 statusString = "unknown"; 70 break; 71 case BatteryManager.BATTERY_STATUS_CHARGING: 72 statusString = "charging"; 73 break; 74 case BatteryManager.BATTERY_STATUS_DISCHARGING: 75 statusString = "discharging"; 76 break; 77 case BatteryManager.BATTERY_STATUS_NOT_CHARGING: 78 statusString = "not charging"; 79 break; 80 case BatteryManager.BATTERY_STATUS_FULL: 81 statusString = "full"; 82 break; 83 } 84 String acString = ""; 85 86 switch (plugged) { 87 case BatteryManager.BATTERY_PLUGGED_AC: 88 acString = "plugged ac"; 89 break; 90 case BatteryManager.BATTERY_PLUGGED_USB: 91 acString = "plugged usb"; 92 break; 93 } 94 95 SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss:SSS "); 96 String date = sDateFormat.format(new java.util.Date()); 97 98 Log.i(TAG, "battery: date=" + date + ",status " + statusString 99 + ",level=" + level +",scale=" + scale 100 + ",voltage=" + voltage +",acString=" + acString ); 101 102 MyLog.i("Battery.txt", "Battery",":date=" + date + ",status=" + statusString 103 + ",level=" + level +",scale=" + scale 104 + ",voltage=" + voltage ); 105 106 } 107 }; 108 }
那么怎樣才能保證service不被殺死?
Android開發的過程中,每次調用startService(Intent)的時候,都會調用該Service對象的onStartCommand(Intent,int,int)方法,這里在onStartCommand方法的返回值做文章就可以了,這里用到的是Service.START_STICKY這個返回值:
網絡上找到的解釋:如果service進程被kill掉,保留service的狀態為開始狀態,但不保留遞送的intent對象。隨后系統會嘗試重新創建service,由於服務狀態為開始狀態,所以創建服務后一定會調用onStartCommand(Intent,int,int)方法。如果在此期間沒有任何啟動命令被傳遞到service,那么參數Intent將為null。
3,將電量數據保存到文件中(這里只提取了關鍵的方法)
1 /** 2 * 打開日志文件並寫入日志 3 * 4 * @return 5 * **/ 6 private static void writeLogtoFile(String logTypename, String mylogtype, 7 String tag, String text) {// 新建或打開日志文件 8 Log.i("zjq", "mylog----------"); 9 File path = Environment.getExternalStorageDirectory(); 10 Date nowtime = new Date(); 11 String needWriteFiel = logfile.format(nowtime); 12 String needWriteMessage = text; 13 File file = new File(path, needWriteFiel + logTypename); 14 try { 15 FileWriter filerWriter = new FileWriter(file, true);// 后面這個參數代表是不是要接上文件中原來的數據,不進行覆蓋 16 BufferedWriter bufWriter = new BufferedWriter(filerWriter); 17 bufWriter.write(needWriteMessage); 18 bufWriter.newLine(); 19 bufWriter.close(); 20 filerWriter.close(); 21 } catch (IOException e) { 22 e.printStackTrace(); 23 } 24 }