android 在后台运行的倒数计时器

Countdown timer which runs in the background in android

这是我的全功能代码,当我按下按钮时,按钮会被禁用,倒数计时器会启动,每当它结束时,按钮就会被启用。我的问题是,如果我离开 activity 进程会重置。

我的问题是如何在后台完成此操作,这样即使我关闭了应用程序,计时器也会在后台运行?

package com.mycompany.myapp;

import android.app.*;
import android.os.*;
import android.widget.*;
import android.view.View.*;
import android.view.*;

public class MainActivity extends Activity {
    Button btnCountdown;
    TextView tvCountdown;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        btnCountdown = findViewById(R.id.btnCountdown);
        tvCountdown = findViewById(R.id.tvCountdown);
        btnCountdown.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Timer();
                btnCountdown.setEnabled(false);
            }
            
        });
    }
    private void Timer() {
        new CountDownTimer(30*1000,1000) {
            @Override
            public void onTick(long millisUntilFinished) {
                long second = (millisUntilFinished / 1000) % 60;
                long minutes = (millisUntilFinished / (1000*60)) % 60;
                tvCountdown.setText(minutes + ":" + second);
            }
            @Override
            public void onFinish() {
                tvCountdown.setText("Fin");
                btnCountdown.setEnabled(true);
            }
        }.start();
    }
}

添加到您的AndroidManifest.xml

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

BroadcastReceiver.java

public class BroadcastReceiver extends AppCompatActivity {

TextView tvTimer, tvTimerRunningState, tvTimerFinishedState;
private static final String TAG = "CountdownTimer";

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_broadcast_receiver);
}
public void handleStartTimer(View view) {
    Intent intent = new Intent(this, BroadcastService.class);
    intent.putExtra("inputExtra", "");
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        ContextCompat.startForegroundService(this, intent);
    } else {
        this.startService(intent);
    }
    Log.i(TAG, "timerStarted");
}
public void handleCancelTimer (View view) {
    Intent intent = new Intent(this, BroadcastService.class);
    stopService(intent);
}
/* CountDown */
final private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        updateGUI(intent);
    }
};

@Override
public void onResume() {
    super.onResume();
    registerReceiver(broadcastReceiver, new IntentFilter(BroadcastService.COUNTDOWN_BR));
    Log.i(TAG, "Registered broadcast receiver");
}

@Override
public void onPause() {
    super.onPause();
    unregisterReceiver(broadcastReceiver);
    Log.i(TAG, "Unregistered broadcast receiver");
}

@Override
public void onStop() {
    try {
        unregisterReceiver(broadcastReceiver);
    } catch (Exception e) {
        // Receiver was probably already stopped in onPause()
    }
    super.onStop();
}

private void updateGUI(Intent intent) {
    if (intent.getExtras() != null) {
        long millisUntilFinished = intent.getLongExtra("countdown", 0);
        long seconds = (millisUntilFinished / 1000) % 60;
        long minutes = (millisUntilFinished / (1000*60)) % 60;
        long hours = (millisUntilFinished / (1000*60*60)) % 60;
        String time = (hours + " : " + minutes + " : " + seconds);
        tvTimer = findViewById(R.id.tvTimer);
        tvTimer.setText(time);

        boolean countdownTimerRunning = intent.getBooleanExtra("countdownTimerRunning", false);
        tvTimerRunningState = findViewById(R.id.tvTimerRunningState);
        if (countdownTimerRunning) {
            tvTimerRunningState.setText("CountdownTimerRunning");
        } else {
            tvTimer.setText("0 : 0 : 0");
            tvTimerRunningState.setText("CountdownTimerNotRunning");
        }

        boolean countdownTimerFinished = intent.getBooleanExtra("countdownTimerFinished", false);
        tvTimerFinishedState = findViewById(R.id.tvTimerFinishedState);
        if (countdownTimerFinished) {
            tvTimerFinishedState.setText("Finished");
        } else {
            tvTimerFinishedState.setText("Unfinished");
        }
    }
}

activity_broadcast_receiver.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<Button
    android:id="@+id/btnStartJob"
    android:onClick="handleStartTimer"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="Start Timer" />
<Button
    android:id="@+id/btnStopJob"
    android:onClick="handleCancelTimer"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="Cancel Timer" />
<TextView
    android:id="@+id/tvTimer"
    android:text="0 : 0 : 0"
    android:gravity="center"
    android:textSize="30sp"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />
<TextView
    android:id="@+id/tvTimerFinishedState"
    android:gravity="center"
    android:textSize="20sp"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />
<TextView
    android:id="@+id/tvTimerRunningState"
    android:gravity="center"
    android:textSize="18sp"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

BroadcastService.java

public class BroadcastService extends Service {

public static final String CHANNEL_ID = "ForegroundServiceChannel";
private final static String TAG = "BroadcastService";
public static final String COUNTDOWN_BR = "your.package.name";
Intent bi = new Intent(COUNTDOWN_BR);

CountDownTimer cdt = null;

@Override
public void onCreate() {
    super.onCreate();
    Log.i(TAG, "Starting timer...");
    cdt = new CountDownTimer(30000, 1000) {
        @Override
        public void onTick(long millisUntilFinished) {
            Log.i(TAG, "Countdown seconds remaining: " + millisUntilFinished / 1000);
            bi.putExtra("countdown", millisUntilFinished);
            bi.putExtra("countdownTimerRunning", true);
            bi.putExtra("countdownTimerFinished", false);
            sendBroadcast(bi);
        }
        @Override
        public void onFinish() {
            Log.i(TAG, "Timer finished");
            bi.putExtra("countdownTimerFinished", true);
            sendBroadcast(bi);
            stopForeground(true);
            stopSelf();
        }
    }; cdt.start();

}

@Override
public void onDestroy() {
    cdt.cancel();
    Log.i(TAG, "Timer cancelled");
    bi.putExtra("countdownTimerRunning", false);
    sendBroadcast(bi);
    super.onDestroy();
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    /* Notification */
    String input = intent.getStringExtra("inputExtra");
    createNotificationChannel();
    Intent notificationIntent = new Intent(this, BroadcastReceiver.class);
    PendingIntent pendingIntent = PendingIntent.getActivity(this,
            0, notificationIntent, 0);
    /* NotificationBuilder */
    Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
            .setContentTitle("Foreground Service")
            .setContentText(input)
            .setSmallIcon(R.drawable.ic_launcher_background)
            .setContentIntent(pendingIntent)
            .build();
    startForeground(1, notification);
    return START_NOT_STICKY;
}

@Nullable
@Override
public IBinder onBind(Intent arg0) {
    return null;
}

private void createNotificationChannel() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        NotificationChannel serviceChannel = new NotificationChannel(
                CHANNEL_ID,
                "Foreground Service Channel",
                NotificationManager.IMPORTANCE_DEFAULT
        );
        NotificationManager manager = getSystemService(NotificationManager.class);
        manager.createNotificationChannel(serviceChannel);
    }
}