Quản lý Activity đang hoạt động hay chạy nền trong Android
Bài đăng này đã không được cập nhật trong 6 năm
Vòng đời của Activity
được thể hiện rất rõ qua các sự kiện onPause, onStop, hay onDestroy
nhưng đấy là khi bạn đang thao tác trực tiếp với Activity
đó. Còn khi bạn viết code dưới Service's
hay BroadcastReceiver's
thì Andrroid không cung cấp trực tiếp các hàm đề bạn có thể biết được Activity
bạn cần thao tác hiện đang ở trạng thái đang hoạt động hay đang chạy nền.
Nhưng từ API 14 (Android 4, ICS) chúng ta có thể biết được thông tin này thông qua Application.registerActivityLifecycleCallbacks.
trong Application
của ứng dụng.
Đầu tiên là bạn cần tùy chỉnh Application
mặc định của ứng dụng bằng một MyApplication
.
class MyApplication extends Application {
public void onCreate(){
LifecycleHandler.init(this);
}
}
Đăng ký Application
trong Manifest.xml
<application
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:name=".MyApplication">
Tiếp đến chúng ta khỏi tạo class LifecycleHandler
class LifecycleHandler
implements Application.ActivityLifecycleCallbacks {
private static LifecycleHandler instance;
public static void init(Application app){
if (instance == null){
instance = new LifecycleHandler();
app.registerActivityLifecycleCallbacks(instance);
}
}
public static LifecycleHandler get(){
return instance;
}
private LifecycleHandler(){}
// TODO: implement the lifecycle callback methods!
}
Trong này mình có sử dụng Singleton Pattern
, cách khai báo như này được khuyến khích từ đội ngũ kỹ thuật của Google.
private boolean foreground;
public boolean isForeground(){
return foreground;
}
public boolean isBackground(){
return !foreground;
}
public void onActivityPaused(Activity activity){
foreground = false;
}
public void onActivityResumed(Activity activity){
foreground = true;
}
// other ActivityLifecycleCallbacks methods omitted for brevity
// we don't need them, so they are empty anyway ;)
đến đây bạn có thể gọi LifecycleHandler.get().isForeground()
để biết ứng dụng của mình có đang hoạt động hay không (hoặc đang nền). Nhưng làm như này sẽ nảy sinh các vấn đề sau:
- Activity có thể được chuyển xuống chạy nền bất cứ lúc nào. Nên ở đây chúng ta nên sử dụng cơ chế
notify
để thông báo có cácService's
hayBroadcastReceiver's
cần sử dụng. - Khi úng dụng của bạn đang chuyển đổi giữa các
Activity
thì khiActivity
thứ nhất tạm dừng (onPaused()
được gọi) vàActivity
thứ 2 đang trong quá trình khởi tạo thì hàmisForeground()
sẽ trả vềfalse
mặc dù ứng dụng của bạn đang chạy.
Để giải quyết 2 vấn đề trên mình có thêm 1 số đoạn code để tối ưu hơn cho việc Service's
hay BroadcastReceiver's
theo dõi sự hoạt động của ứng dụng.
Đầu tiên là sử dụng Callback
public interface Listener {
public void onBecameForeground();
public void onBecameBackground();
}
private List listeners =
new CopyOnWriteArrayList();
public void addListener(Listener listener){
listeners.add(listener);
}
public void removeListener(Listener listener){
listeners.remove(listener);
}
sử dụng notify trong 2 hàm onActivityPaused
và onActivityResumed
public void onActivityPaused(){
foreground = false;
for (Listener l : listeners){
try {
l.onBecameBackground();
} catch (Exception exc) {
Log.e("Foreground", "Unhappy listener", exc);
}
}
}
public void onActivityResumed(){
foreground = true;
for (Listener l : listeners){
try {
l.onBecameForeground();
} catch (Exception exc) {
Log.e("Foreground", "Unhappy listener", exc);
}
}
}
ở đây các Service
chỉ cần đăng ký lắng nghe là có thể chủ động biết ứng dụng của bạn có đang hoạt động hay không. Như vậy là vấn đề 1 đã được giải quyết, còn vấn đề 2 là khoảng thời gian chuyển đổi giữa 2 Activity
.
Vấn đề này chúng ta sẽ đưa ra 1 khoảng thời gian nhất định là khoảng thời gian chuyển đổi giữa 2 Acvitiy
. Tất hiên khoảng thời gian này chỉ là tương đối đối với từng ứng dụng của bạn
public static final long CHECK_DELAY = 500;
Và sử dụng Handler
và Runnable
để quản lý việc Notify
.
private boolean foreground = false, paused = true;
private Handler handler = new Handler();
private Runnable check;
Giờ chúng ta sẽ cập nhật lại 2 hàm onActivityPaused
và onActivityResumed
.
@Override
public void onActivityResumed(Activity activity) {
paused = false;
boolean wasBackground = !foreground;
foreground = true;
if (check != null)
handler.removeCallbacks(check);
if (wasBackground){
Log.i(TAG, "went foreground");
for (Listener l : listeners) {
try {
l.onBecameForeground();
} catch (Exception exc) {
Log.e(TAG, "Listener threw exception!", exc);
}
}
} else {
Log.i(TAG, "still foreground");
}
}
@Override
public void onActivityPaused(Activity activity) {
paused = true;
if (check != null)
handler.removeCallbacks(check);
handler.postDelayed(check = new Runnable(){
@Override
public void run() {
if (foreground && paused) {
foreground = false;
Log.i(TAG, "went background");
for (Listener l : listeners) {
try {
l.onBecameBackground();
} catch (Exception exc) {
Log.e(TAG, "Listener threw exception!", exc);
}
}
} else {
Log.i(TAG, "still foreground");
}
}
}, CHECK_DELAY);
}
Tới đây thì gần như đã hoàn thành việc quản lý ứng dụng đang hoạt động hay chạy nền. Các Service
... có thế đăng ký lắng nghe Listener
thông qua 2 hàm onBecameForeground
và onBecameBackground
để có những xử lý tương tác chính xác với Activity
.
All rights reserved