我的应用程序有一个“计时器片段”,用户可以在其中设置初始计时器,然后开始倒数计时。开始倒计时时,Fragment会创建一个包含所有信息的Intent,并启动CountdownTimer IntentService。
我现在要做的是使其行为与Samsung本地时钟的Timer类似:
1-当我开始计时器倒计时时,将创建服务并开始倒计时,更新片段计时器的textView值;
2-如果应用程序最小化或被终止,则该服务将继续运行(或使用从较早停止的位置开始的计时器重新创建)并显示通知。 为此,我阅读了其他链接,以在Fragment的onDestroy()上使用BroadcastReceiver并从Service onDestroy()发送广播消息。接收者应重新创建服务。但这是行不通的:在使用服务时,我无法收到发送的任何广播。
3-在通知中,我需要一个按钮来暂停或恢复计时器。因此,此按钮将向我的服务发送intentAction,并且服务需要执行此操作。 我不知道我在“服务”的哪一部分收到“意图”操作
4-如果再次打开应用,通知会消失。
5-如果计时器已停止(重置),服务将终止。
我想这样做,即使关闭了应用程序,我也需要使服务运行。我在其他链接中读到, IntentService 无法做到这一点,因为应用程序死后它会关闭,因此我需要使用Service。 但是Service在与UI相同的线程中运行,并且如果我在执行操作时通过onTick发送广播,它不会更新View并导致应用崩溃。
我使用IntentService编写了此代码。就像我说的那样,它起作用了,但并没有达到我想要的方式,因为当应用被杀死并且服务无法在应用关闭的情况下重新创建时,服务就会停止。
计时器片段代码:
public class TimerFragment extends Fragment {
MyTimer myTimer;
TimerServiceReceiver receiver;
Intent serviceIntent;
...
public TimerFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_timer, container, false);
myTimer = new MyTimer();
/*=== BROADCAST MANAGER ===*/
receiver = new TimerServiceReceiver();
IntentFilter statusIntentFilter = new IntentFilter(Constants.TIMER_BROADCAST);
// Registers the DownloadStateReceiver and its intent filters
LocalBroadcastManager.getInstance(requireNonNull(getActivity())).registerReceiver(
receiver, statusIntentFilter);
...
return v;
}
...
//Start MyTimer Actions and TimerService
private void setBtnStart() {
btnStart.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
tvTimer.setEnabled(false);
setButtonVisibility(View.GONE, View.VISIBLE, View.VISIBLE);
myTimer.setRunning(true);
startService();
}
});
}
...
private void startService(){
serviceIntent = new Intent(getActivity(), TimerIntentService.class);
serviceIntent.putExtra("id_execution", myTimer.getIdExecution());
serviceIntent.putExtra("id_exercise", myTimer.getIdExercise());
serviceIntent.putExtra("position", myTimer.getPosition());
serviceIntent.putExtra("timer_running", myTimer.isRunning());
serviceIntent.putExtra("pause", myTimer.isPause());
serviceIntent.putExtra("init_time", myTimer.getInitTime());
serviceIntent.putExtra("millis_left", myTimer.getTimeWhenStopped());
serviceIntent.putExtra("end_time", myTimer.getEndTime());
requireNonNull(getActivity()).startService(serviceIntent);
}
public class TimerServiceReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Log.v("Received", "rec");
if (intent.getExtras() != null) {
String s = intent.getStringExtra("elapsed_time");
tvTimer.setText(s);
}
}
}
服务代码:
public class TimerIntentService extends IntentService {
private MyTimer myTimer;
private CountDownTimer countdownTimer;
private NotificationManager mNotificationManager;
private NotificationCompat.Builder mBuilder;
int notifyID;
public TimerIntentService() {
super("TimerIntentService");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
@Override
protected void onHandleIntent(Intent intent) {
Bundle extras = intent.getExtras();
if (extras != null) {
myTimer = new MyTimer();
myTimer.setIdExecution(extras.getLong("id_execution"));
myTimer.setIdExercise(extras.getLong("id_exercise"));
myTimer.setPosition(extras.getInt("position"));
myTimer.setRunning(extras.getBoolean("timer_running"));
myTimer.setPause(extras.getBoolean("pause"));
myTimer.setInitTime(extras.getLong("init_time"));
myTimer.setTimeWhenStopped(extras.getLong("millis_left"));
myTimer.setEndTime(extras.getLong("end_time"));
if (myTimer.isRunning()) {
if (!myTimer.isPause()) {
createTimer(myTimer.getInitTime());
myTimer.startTimer();
createNotification();
} else {
createTimer(myTimer.getElapsedTime());
}
}
Looper.loop();
}
}
private void createTimer(long t){
CountDownTimer timer = new CountDownTimer(t, 1000) {
@Override
public void onTick(long l) {
myTimer.setElapsedTime(l/1000);
Log.v("Timer: ", "Timer = " + myTimer.getElapsedTime());
long hour = myTimer.getElapsedTime()/3600;
long minute = myTimer.getElapsedTime()/60;
long second = myTimer.getElapsedTime()%60;
String s = String.format("%02d", hour) + ":"
+ String.format("%02d", minute) +":"
+ String.format("%02d", second);
Intent localIntent = new Intent(Constants.TIMER_BROADCAST)
.putExtra("elapsed_time", s);
LocalBroadcastManager.getInstance(TimerIntentService.this).sendBroadcast(localIntent);
if(mBuilder != null){
mBuilder.setContentText(s);
mNotificationManager.notify(notifyID, mBuilder.build());
}
}
@Override
public void onFinish() {
cancel();
myTimer.setRunning(false);
showNotification();
}
};
myTimer.setTimer(timer);
}
private void createNotification(){
mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// Sets an ID for the notification, so it can be updated
notifyID = 1;
Intent intent = new Intent(this, ExecuteExerciseActivity.class);
intent.putExtra("id_execution", myTimer.getIdExecution());
intent.putExtra("id_exercise", myTimer.getIdExercise());
intent.putExtra("position", myTimer.getPosition());
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addNextIntentWithParentStack(intent);
PendingIntent pendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
//Action Buttons & Intents
Intent pauseIntent = new Intent(this, TimerIntentService.class);
pauseIntent.setAction(Constants.TIMER_PAUSE);
PendingIntent pPauseIntent = PendingIntent.getService(this, 0, pauseIntent, 0);
mBuilder = new NotificationCompat.Builder(requireNonNull(this), Constants.CHANNEL_ID)
.setSmallIcon(R.drawable.d77)
.setContentTitle(getResources().getString(R.string.timer))
.setContentText("00:00:00")
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.addAction(R.drawable.ntf_pause, getString(R.string.pause_timer), pPauseIntent);
mBuilder.build();
}
}
希望这个问题很清楚!