创建可暂停的CountdownTimer服务,即使应用被终止,其也会更新UI并运行

时间:2019-01-26 19:37:07

标签: java android

我的应用程序有一个“计时器片段”,用户可以在其中设置初始计时器,然后开始倒数计时。开始倒计时时,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();
    }
}

希望这个问题很清楚!

0 个答案:

没有答案