天天看点

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后面研究好了再补上)

继续阅读