天天看點

Android輪訓機制以及API19之後定時不準的一種解決方案

在項目的進展中,使用到了定時輪訓機制,參考網上的一個例子,稍加修改後可以使用,但是發現在5.x的系統上有定時不準的問題,      
網上說從API19開始,alarm的機制都是非準确傳遞的,是以如果還是使用了setRepeating()方法,将會出現定時不準,但是如果強行      
想用的話也還是有解決辦法的,下面我給出我在項目中用到的例子,希望能給大家一些想法,本人也是菜鳥一枚      
希望大神勿噴。      
輪訓機制的工具類封裝參考《android輪詢最佳實踐service+AlarmManager+Thread》      
不想移步的可以看下面的,我将其命名為AlarmUtil      
public class AlarmUtil {
	// 開啟輪詢服務
	public static void startPollingService(Context context, int seconds,
			Class<?> cls, String action) {
		// 擷取AlarmManager系統服務
		AlarmManager manager = (AlarmManager) context
				.getSystemService(Context.ALARM_SERVICE);

		// 包裝需要執行Service的Intent
		Intent intent = new Intent(context, cls);
		intent.setAction(action);
		PendingIntent pendingIntent = PendingIntent.getService(context, 0,
				intent, PendingIntent.FLAG_UPDATE_CURRENT);

		// 觸發服務的起始時間
		long triggerAtTime = SystemClock.elapsedRealtime();

		// 這裡要注意,如果API>=19,就不能再使用setRepeating,應該改為setWindow
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
			manager.setWindow(AlarmManager.RTC_WAKEUP, triggerAtTime,
					seconds * 1000, pendingIntent);
		} else {
			// 使用AlarmManger的setRepeating方法設定定期執行的時間間隔(seconds秒)和需要執行的Service
			manager.setRepeating(AlarmManager.RTC_WAKEUP, triggerAtTime,
					seconds * 1000, pendingIntent);
		}

	}

	// 停止輪詢服務
	public static void stopPollingService(Context context, Class<?> cls,
			String action) {
		AlarmManager manager = (AlarmManager) context
				.getSystemService(Context.ALARM_SERVICE);
		Intent intent = new Intent(context, cls);
		intent.setAction(action);
		PendingIntent pendingIntent = PendingIntent.getService(context, 0,
				intent, PendingIntent.FLAG_UPDATE_CURRENT);
		// 取消正在執行的服務
		manager.cancel(pendingIntent);
	}
}
           
在原作者的基礎上做了些改動,可以看《Android API 19 及以上版本AlarmManager setRepeating 不準或隻執行一次的解決方案》      
// 這裡要注意,如果API>=19,就不能再使用setRepeating,應該改為setWindow
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
			manager.setWindow(AlarmManager.RTC_WAKEUP, triggerAtTime,
					seconds * 1000, pendingIntent);
		} else {
			// 使用AlarmManger的setRepeating方法設定定期執行的時間間隔(seconds秒)和需要執行的Service
			manager.setRepeating(AlarmManager.RTC_WAKEUP, triggerAtTime,
					seconds * 1000, pendingIntent);
		}
           
我這裡對應的MainActivity比較簡單,兩個按鈕,一個啟動輪訓,一個停止輪訓,文本框用來顯示狀态      
public class MainActivity extends Activity implements OnClickListener {

	// 注冊Service時候對應的Action
	private final static String SERVIE_ACTION = "myAlarmService";
	private Button btn_start;
	private Button btn_stop;
	private TextView tv_msg ;

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

		tv_msg = (TextView) findViewById(R.id.tv_msg);
		btn_start = (Button) findViewById(R.id.btn_start);
		btn_stop = (Button) findViewById(R.id.btn_stop);
		btn_start.setOnClickListener(this);
		btn_stop.setOnClickListener(this);
	}

	@Override
	public void onClick(View view) {
		switch (view.getId()) {
		case R.id.btn_start: // 啟動定時輪訓
			tv_msg.setText("啟動輪訓……");
			AlarmUtil.startPollingService(this, 1, MyService.class,
					SERVIE_ACTION);
			break;
		case R.id.btn_stop: // 關閉定時輪訓
			tv_msg.setText("停止輪訓!");
			AlarmUtil.stopPollingService(this, MyService.class, SERVIE_ACTION);
			break;
		default:
			break;
		}
	}

	@Override
	protected void onPause() {
		// 除非有必要一直在背景運作,否則最好在Activity停止或銷毀的時候停止輪訓服務
		AlarmUtil.stopPollingService(this, MyService.class, SERVIE_ACTION);
		super.onPause();
	}

}
           
下面重點看運作時候log列印的日志:      
Android輪訓機制以及API19之後定時不準的一種解決方案
Android輪訓機制以及API19之後定時不準的一種解決方案
每隔5秒鐘執行一次,基本達到了效果(這裡運作時候有一個BUG暫時還沒有解決,程式中我設定的時間間隔并是不是5秒,而是2秒,後來不管我怎麼改這個間隔, 模拟器運作出來的結果都是5秒,這個問題很頭痛,當時在硬體上調試的時候是沒問題的,關于這個BUG後面研究好了再補上)

繼續閱讀