首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

RxJava 實戰:頁面切換/旋轉屏幕時保持後台任務持續更新 UI 狀態

     編輯:Java 綜合教學

RxJava 實戰:頁面切換/旋轉屏幕時保持後台任務持續更新 UI 狀態

Android 應用中,後台任務(下載、播放進度)需持續更新前台 UI,且不因 Activity 重建(旋轉屏幕、配置變更)而重置或洩漏。本文展示如何結合 Fragment.setRetainInstance(true) 與 RxJava 實現狀態保持與無縫恢復。

一、核心架構:Retained Fragment 存儲觀察者狀態

// Activity 中獲取/創建 Retained Fragment
FragmentManager fm = getFragmentManager();
mRetainedFragment = (RetainedFragment) fm.findFragmentByTag(RETAINED_FRAGMENT);
if (mRetainedFragment == null) {
    mRetainedFragment = new RetainedFragment();
    fm.beginTransaction().add(mRetainedFragment, RETAINED_FRAGMENT).commit();
}

// RetainedFragment.java
public class RetainedFragment extends Fragment {
    private CustomAsyncTask mCustomAsyncTask;
    private Observable<Long> mObservable;
    private PublishSubject<Long> mSubject;
    private int mMode;
    private boolean mBusy;
    // getter/setter 省略

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true); // 關鍵:配置變更時不銷毀
    }
}

二、onResume 中恢復訂閱並同步 UI

@Override
protected void onResume() {
    super.onResume();
    // 從 RetainedFragment 取回狀態
    mMode = mRetainedFragment.getMode();
    mCustomAsyncTask = mRetainedFragment.getCustomAsyncTask();
    mObservable = mRetainedFragment.getObservable();
    mSubject = mRetainedFragment.getSubject();
    mSubscriber = createSubscriber(); // 新建訂閱者綁定當前 Activity UI

    switch (mMode) {
        case ASYNC_TASK:
            if (mCustomAsyncTask != null && !mCustomAsyncTask.isCompleted()) {
                mCustomAsyncTask.setActivity(this); // 綁定新 Activity
            }
            break;
        case TIME_INTERVAL:
            if (mObservable != null) {
                mObservable.subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .take(MAX_PROGRESS)
                    .map(x -> x + 1)
                    .subscribe(mSubscriber);
            }
            break;
        case DELAY_EMIT:
            if (mObservable != null) {
                mObservable.subscribeOn(Schedulers.io())
                    .delay(1, TimeUnit.SECONDS)
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(mSubscriber);
            }
            break;
        case CUSTOM_ITERATOR:
            if (mSubject != null) {
                mSubject.subscribe(mSubscriber);
            }
            break;
    }
    setBusy(mRetainedFragment.isBusy());
}

關鍵點:在 onResume 而非 onCreate 綁定新 Activity,確保 onRestoreInstanceState 已恢復 UI 狀態(如 ProgressBar 進度)。

三、三種 RxJava 後台任務模式對比

模式實現旋轉屏幕行為適用場景
時間間隔Observable.interval(1, TimeUnit.SECONDS)重新發射,進度歸零重來輪詢、心跳
延遲發射Observable.create() + delay()完成當前項後重新開始定時單次任務
定制迭代器Observable.from(Iterable) + PublishSubject無縫銜接,進度不中斷長時間下載、播放進度

四、定制迭代器完整實現(推薦)

// 1. 定義可迭代對象,封裝發射邏輯
public class CustomIterator implements Iterable<Long> {
    private final List<Long> numbers = new ArrayList<>();
    public CustomIterator() {
        for (long i = 1; i <= MAX_PROGRESS; i++) numbers.add(i);
    }
    @Override
    public Iterator<Long> iterator() {
        return new Iterator<Long>() {
            int index = 0;
            @Override public boolean hasNext() { return index < numbers.size(); }
            @Override public Long next() {
                SystemClock.sleep(EMIT_DELAY_MS); // 模擬耗時
                return numbers.get(index++);
            }
            @Override public void remove() { throw new UnsupportedOperationException(); }
        };
    }
}

// 2. Activity 啟動時
private void handleCustomIteratorClick() {
    mObservable = Observable.from(new CustomIterator());
    mSubject = PublishSubject.create();
    // 關鍵:Observable 訂閱 Subject,Subject 再訂閱 UI Subscriber
    mObservable.subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(mSubject);
    mSubject.subscribe(createSubscriber());
    // 保存 Subject 與 Observable 到 RetainedFragment
    mRetainedFragment.setObservable(mObservable);
    mRetainedFragment.setSubject(mSubject);
    mRetainedFragment.setMode(CUSTOM_ITERATOR);
}

原理:PublishSubject 既是 Observer 又是 Observable,作為熱觀察者緩衝上游發射,下游(新 Activity 的 Subscriber)訂閱時可接收後續事件,實現斷點續傳效果。

五、AsyncTask 與 IntentService 對比

  • AsyncTask:輕量但易洩漏(需 WeakReference<Activity>),擴展性差,適合簡單更新。
  • IntentService:獨立進程、生命周期獨立,通過 LocalBroadcastManager 回傳進度,重量級,適合複雜業務邏輯。
  • RxJava:靈活組合、精確控制訂閱生命周期、支援背壓、切換線程簡單,**最推薦用於 UI 狀態持續更新**。

應用界面截圖

旋轉屏幕進度持續演示

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved