标签:计时 休眠 alarmmanager 计时不准确
首先,完全没必要担心AP休眠会导致收不到消息推送。通讯协议栈运行于BP,一旦收到数据包,BP会将AP唤醒,唤醒的时间足够AP执行代码完成对收到的数据包的处理过程。其它的如Connectivity事件触发时AP同样会被唤醒。那么唯一的问题就是程序如何执行向服务器发送心跳包的逻辑。你显然不能靠AP来做心跳计时。Android提供的Alarm Manager就是来解决这个问题的。Alarm应该是BP计时(或其它某个带石英钟的芯片,不太确定,但绝对不是AP),触发时唤醒AP执行程序代码。那么Wake Lock API有啥用呢?比如心跳包从请求到应答,比如断线重连重新登陆这些关键逻辑的执行过程,就需要Wake Lock来保护。而一旦一个关键逻辑执行成功,就应该立即释放掉Wake Lock了。两次心跳请求间隔5到10分钟,基本不会怎么耗电。除非网络不稳定,频繁断线重连,那种情况办法不多。
网上有说使用AlarmManager,因为AlarmManager 是Android 系统封装的用于管理 RTC 的模块,RTC (Real Time Clock) 是一个独立的硬件时钟,可以在 CPU 休眠时正常运行,在预设的时间到达时,通过中断唤醒 CPU。
后来,本人使用AlarmManager,但是又碰到不准的问题,而且当系统繁忙的时候(比如刚开机前一分钟),更为明显,后来查看官方的API说明。因为我之前使用的方法大致是下面的原理:
targetSdkVersion is
earlier than API 19 will continue to have all of their alarms, including repeating alarms, treated as exact.意思大概是说19或者19以后,为了优化电池,该计时操作不准确了(可能在定义时间之后响应),但是19之前的任然准确。
If your application has strong ordering requirements there are other APIs
that you can use to get the necessary behavior; see setWindow(int,
long, long, PendingIntent) andsetExact(int,
long, PendingIntent).意思是虽然19(包括)之后可能不准确,但是android保留了两个接口,这两个接口是准确的。本人于是使用了这其中一个。
方法如下 TIP:前提是确保你的API是19的或更高,否则做下判断
public static boolean isKitKatOrLater() {
return Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2;
}
然后注册一个广播接收器接收
/**
* 2014-12-05 BenMin FEIXUN_DESKCLOCK_BENMIN_001
* modify PWEUN-4141 to remind the user when the timing is over.
* 2014-12-17 BenMin FEIXUN_DESKCLOCK_BENMIN_002
* modify to ensure the fragment attached to Activity when using getResources() function.
* 2014-12-26 BenMin FEIXUN_DESKCLOCK_BENMIN_003
* modify PLGN-489 to stop the ring when press the back menu.
*/
package com.phicomm.keyer;
import java.util.Timer;
import java.util.TimerTask;
import android.R.integer;
import android.app.AlarmManager;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.MediaPlayer;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.NumberPicker;
import android.widget.NumberPicker.Formatter;
import android.widget.NumberPicker.OnValueChangeListener;
import android.widget.TextView;
import com.phicomm.deskclock.DeskClockFragment;
import com.phicomm.deskclock.FxDeskClock;
import com.phicomm.deskclock.R;
public class FxKeyerFragment extends DeskClockFragment implements
OnClickListener, Formatter, OnValueChangeListener {
private NumberPicker keyerHour;
private NumberPicker keyerMinute;
private NumberPicker keyerSecond;
private Button kbtnStart;
private Button kbtnPause;
private Button kbtnReset;
private LinearLayout time;
private TextView hour;
private TextView minute;
private TextView second;
private int hourtime;
private int minutetime;
private int secondtime;
private Timer timer;
private TimerTask task;
private MediaPlayer alarmMusic;
private AlertDialog dialog = null;
private static final int originState = 0; //original state
private static final int timerState = 1; //timer state
private static final int pauseState = 2; //pause
private static final int resetState = 3; //reset
private static final int overState = 4; //over
private int stateNow = originState;
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
if(msg.what == 1){
if(secondtime > 0) {
secondtime --;
second.setText(format(secondtime));
} else if(minutetime > 0) {
minutetime --;
secondtime = 59;
second.setText(format(secondtime));
minute.setText(format(minutetime));
} else if(hourtime > 0) {
hourtime --;
secondtime = 59;
minutetime = 59;
second.setText(format(secondtime));
minute.setText(format(minutetime));
hour.setText(format(hourtime));
}
if (hourtime <= 0 && minutetime <= 0 && secondtime <= 0) {
startRing();
}
}
}
};
private AlarmManager mAlarmManager;
private PendingIntent sendIntent;
public static String ALARM_KEYER_ACTION = "com.phicomm.keyer.alarm_keyer_action";
private BroadcastReceiver mBroadcastReceiver;
private boolean isRing = false;
@Override
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
mAlarmManager = (AlarmManager) this.getActivity().getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent();
intent.setAction(ALARM_KEYER_ACTION);
sendIntent = PendingIntent.getBroadcast(getActivity(), 0, intent , PendingIntent.FLAG_UPDATE_CURRENT);
mBroadcastReceiver = new BroadcastReceiver(){
@Override
public void onReceive(Context arg0, Intent arg1) {
// TODO Auto-generated method stub
kbtnPause.setEnabled(false);
startRing();
}
};
IntentFilter filter = new IntentFilter(ALARM_KEYER_ACTION);
getActivity().registerReceiver(mBroadcastReceiver, filter );
}
private void startRing(){
if (timer != null) {
timer.cancel();
}
stateNow = overState;
if (isAdded() && isRing == false) {
kbtnPause.setTextColor(getResources().getColor(R.color.text_summery));
alarmMusic = MediaPlayer.create(getActivity(), R.raw.in_call_alarm);
alarmMusic.setLooping(true);
alarmMusic.start();
isRing = true;
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
.setTitle(R.string.tip)
.setMessage(R.string.tip_text)
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface arg0, int arg1) {
// TODO Auto-generated method stub
alarmMusic.stop();
alarmMusic.release();
alarmMusic = null;
isRing = false;
}
});
dialog = builder.setCancelable(false).create();
dialog.show();
}
mAlarmManager.cancel(sendIntent);
startTime = 0;
leftTimeToRun = 0;
ringTime = 0;
adjustTime = 0;
setNumberPicker(0);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
View v = inflater.inflate(R.layout.keyer, container, false);
keyerHour = (NumberPicker) v.findViewById(R.id.keyer_hour);
keyerHour.setMinValue(0);
keyerHour.setMaxValue(23);
keyerHour.setFormatter(this);
keyerHour.setOnValueChangedListener(this);
keyerHour.getChildAt(0).setFocusable(false);
keyerMinute = (NumberPicker) v.findViewById(R.id.keyer_minute);
keyerMinute.setMinValue(0);
keyerMinute.setMaxValue(59);
keyerMinute.setFormatter(this);
keyerMinute.setOnValueChangedListener(this);
keyerMinute.getChildAt(0).setFocusable(false);
keyerSecond = (NumberPicker) v.findViewById(R.id.keyer_second);
keyerSecond.setMinValue(0);
keyerSecond.setMaxValue(59);
keyerSecond.setFormatter(this);
keyerSecond.setOnValueChangedListener(this);
keyerSecond.getChildAt(0).setFocusable(false);
kbtnStart = (Button) v.findViewById(R.id.kbtnStart);
kbtnStart.setOnClickListener(this);
kbtnPause = (Button) v.findViewById(R.id.kbtnPause);
kbtnPause.setOnClickListener(this);
kbtnReset = (Button) v.findViewById(R.id.kbtnReset);
kbtnReset.setOnClickListener(this);
time = (LinearLayout) v.findViewById(R.id.time);
hour = (TextView) v.findViewById(R.id.hour);
minute = (TextView) v.findViewById(R.id.minute);
second = (TextView) v.findViewById(R.id.second);
TimeAllZero();
return v;
}
@Override
public void onResume() {
if (getActivity() instanceof FxDeskClock) {
((FxDeskClock) getActivity()).registerPageChangedListener(this);
}
long now = SystemClock.elapsedRealtime();
if(now <= ringTime && stateNow == timerState){
long leftTime = ringTime - now;
setNumberPicker(leftTime);
}
if(stateNow == overState) {
setNumberPicker(0);
}
super.onResume();
}
private void setNumberPicker(long leftTime){
long secs = leftTime/1000;
int hours = (int) (secs/3600);
int minutes = (int) ((secs%3600)/60);
int seconds = (int) ((secs%3600)%60);
secondtime = seconds;
minutetime = minutes;
hourtime = hours;
second.setText(format(seconds));
minute.setText(format(minutes));
hour.setText(format(hours));
}
@Override
public void onPause() {
if (getActivity() instanceof FxDeskClock) {
((FxDeskClock) getActivity()).unregisterPageChangedListener(this);
}
super.onPause();
}
public String format(int value) {
String tmpStr = String.valueOf(value);
if (value < 10) {
tmpStr = "0" + tmpStr;
}
return tmpStr;
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.kbtnStart:
TimeAllZero();
Start();
break;
case R.id.kbtnPause:
Pause();
break;
case R.id.kbtnReset:
Reset();
kbtnStart.setTextColor(getResources().getColor(R.color.text_summery));
break;
}
}
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
if (picker == keyerHour) {
hourtime = newVal;
}
if (picker == keyerMinute) {
minutetime = newVal;
}
if (picker == keyerSecond) {
secondtime = newVal;
}
TimeAllZero();
}
private long startTime = 0;
private long leftTimeToRun = 0;
private long ringTime = 0;
private long adjustTime = 0;
public void Start() {
if (kbtnStart.isEnabled()) {
kbtnStart.setVisibility(View.GONE);
time.setVisibility(View.VISIBLE);
hour.setText(format(hourtime));
minute.setText(format(minutetime));
second.setText(format(secondtime));
kbtnPause.setVisibility(View.VISIBLE);
kbtnPause.setText(R.string.kpause);
kbtnPause.setEnabled(true);
kbtnPause.setTextColor(getResources().getColor(R.color.text_gray));
kbtnReset.setText(R.string.kreset);
kbtnReset.setVisibility(View.VISIBLE);
keyerHour.setVisibility(View.GONE);
keyerMinute.setVisibility(View.GONE);
keyerSecond.setVisibility(View.GONE);
timer = null;
task = null;
timer = new Timer();
task = new TimerTask() {
@Override
public void run() {
// TODO Auto-generated method stub
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
}
};
timer.schedule(task, 1000, 1000);
startTime = SystemClock.elapsedRealtime();
leftTimeToRun = (60 * 60 * hourtime + 60 * minutetime + secondtime) * 1000;
ringTime = startTime + leftTimeToRun;
mAlarmManager.cancel(sendIntent);
if (isKitKatOrLater()) {
mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, ringTime, sendIntent);
} else {
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, ringTime, sendIntent);
}
stateNow = timerState;
}
}
public static boolean isKitKatOrLater() {
return Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2;
}
public void Pause() {
if(kbtnPause.getText().toString().equals(getResources().getString(R.string.kpause))) {
kbtnPause.setText(R.string.kcontinue);
timer.cancel();
adjustTime = (leftTimeToRun - (SystemClock.elapsedRealtime() - startTime)) % 1000;
mAlarmManager.cancel(sendIntent);
stateNow = pauseState;
} else {
stateNow = timerState;
kbtnPause.setText(R.string.kpause);
secondtime = Integer.parseInt(second.getText().toString());
minutetime = Integer.parseInt(minute.getText().toString());
hourtime = Integer.parseInt(hour.getText().toString());
timer = new Timer();
task = new TimerTask() {
@Override
public void run() {
// TODO Auto-generated method stub
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
}
};
timer.schedule(task, adjustTime, 1000);
startTime = SystemClock.elapsedRealtime();
leftTimeToRun = (60 * 60 * hourtime + 60 * minutetime + secondtime) * 1000 + adjustTime;
ringTime = startTime + leftTimeToRun;
mAlarmManager.cancel(sendIntent);
if (isKitKatOrLater()) {
mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, ringTime, sendIntent);
} else {
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, ringTime, sendIntent);
}
}
}
public void Reset() {
stateNow = resetState;
kbtnStart.setVisibility(View.VISIBLE);
kbtnPause.setVisibility(View.GONE);
kbtnReset.setVisibility(View.GONE);
keyerHour.setVisibility(View.VISIBLE);
keyerHour.setValue(0);
hourtime = 0;
keyerMinute.setVisibility(View.VISIBLE);
keyerMinute.setValue(0);
minutetime = 0;
keyerSecond.setVisibility(View.VISIBLE);
keyerSecond.setValue(0);
secondtime = 0;
time.setVisibility(View.GONE);
timer.cancel();
startTime = 0;
leftTimeToRun = 0;
ringTime = 0;
adjustTime = 0;
mAlarmManager.cancel(sendIntent);
}
public void TimeAllZero() {
if (keyerHour.getValue() == 0 && keyerMinute.getValue() == 0 && keyerSecond.getValue() == 0) {
kbtnStart.setEnabled(false);
kbtnStart.setTextColor(getResources().getColor(R.color.text_summery));
} else {
kbtnStart.setEnabled(true);
kbtnStart.setTextColor(getResources().getColor(R.color.text_gray));
}
}
@Override
public void onDestroyView() {
// TODO Auto-generated method stub
super.onDestroyView();
if (timer != null) {
timer.cancel();
timer = null;
}
if (task != null) {
task = null;
}
if (alarmMusic != null) {
alarmMusic.stop();
alarmMusic.release();
alarmMusic = null;
if(mBroadcastReceiver!= null){
getActivity().unregisterReceiver(mBroadcastReceiver);
}
}
}
标签:计时 休眠 alarmmanager 计时不准确
原文地址:http://blog.csdn.net/liu8497/article/details/44080003