若是咱們在AndroidManifest.xml
中聲明Activity
時,沒有對android:configChanges
進行特殊的聲明,那麼在屏幕旋轉時,會致使Activity
的重建,幾個關鍵聲明週期的調用狀況以下所示: html
Activity
中的變量都會被銷燬,可是有時候咱們某些任務的執行不和
Activity
的生命週期綁定,這時候咱們就能夠利用
Fragment
提供的
setRetainInstance
方法,該方法的說明以下:
Fragment
設置了該標誌位,那麼在屏幕旋轉以後,雖然它依附的
Activity
被銷燬了,可是該
Fragment
的實例會被保留,而且在
Activity
的銷燬過程當中,只會調用該
Fragment
的
onDetach
方法,而不會調用
onDestroy
方法。
而在Activity
重建時,會調用該Fragment
實例的onAttach
、onActivityCreated
方法,但不會調用onCreate
方法。java
根據Fragment
提供的這一特性,那麼咱們就能夠將一些在屏幕旋轉過程當中,仍然須要運行的任務放在具備該屬性的Fragment
中執行。在 Handling Configuration Changes with Fragments 這篇文章中,做者介紹了經過這個技巧來實現了一個不被中斷的AsyncTask
,你們有須要瞭解詳細說明的能夠查看這篇文章。android
今天,咱們跟着前人腳步,用RxJava
來演示在屏幕旋轉致使Activity
重建時,仍然保持後臺任務繼續執行的例子。架構
首先,咱們聲明一個接口,用於Fragment
向Activity
一個ConnectableObservable
,使得Activity
能夠監聽到Fragment
中後臺任務的工做進度。ide
public interface IHolder {
public void onWorkerPrepared(ConnectableObservable<Long> workerFlow);
}
複製代碼
下面,咱們來實現WorkerFragment
,咱們在onCreate
中建立了數據源,它每隔1s
向下遊發送數據,在onResume
中,經過前面定義的接口向Activity
傳遞一個ConnectableObservable
用於監聽。這裏最關鍵的是須要調用咱們前面說到的setRetainInstance
方法,最後別忘了,在onDetach
中將mHolder
置爲空,不然它就會持有須要被重建的Activity
示例,從而致使內存泄漏。學習
public class WorkerFragment extends Fragment {
public static final String TAG = WorkerFragment.class.getName();
private ConnectableObservable<String> mWorker;
private Disposable mDisposable;
private IHolder mHolder;
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof IHolder) {
mHolder = (IHolder) context;
}
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
if (mWorker != null) {
return;
}
Bundle bundle = getArguments();
final String taskName = (bundle != null ? bundle.getString("task_name") : null);
mWorker = Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> observableEmitter) throws Exception {
for (int i = 0; i < 10; i++) {
String message = "任務名稱=" + taskName + ", 任務進度=" + i * 10 + "%";
try {
Log.d(TAG, message);
Thread.sleep(1000);
//若是已經拋棄,那麼再也不繼續任務。
if (observableEmitter.isDisposed()) {
break;
}
} catch (InterruptedException error) {
if (!observableEmitter.isDisposed()) {
observableEmitter.onError(error);
}
}
observableEmitter.onNext(message);
}
observableEmitter.onComplete();
}
}).subscribeOn(Schedulers.io()).publish();
mDisposable = mWorker.connect();
}
@Override
public void onResume() {
super.onResume();
if (mHolder != null) {
mHolder.onWorkerPrepared(mWorker);
}
}
@Override
public void onDestroy() {
super.onDestroy();
mDisposable.dispose();
Log.d(TAG, "onDestroy");
}
@Override
public void onDetach() {
super.onDetach();
mHolder = null;
}
}
複製代碼
最後來看Activity
,當點擊「開始工做任務」後,咱們嘗試添加WorkerFragment
,這時候就會走到WorkerFragment
的onCreate
方法中啓動任務,以後當WorkerFragment
走到onResume
方法後,就調用onWorkerPrepared
,讓Activity
進行訂閱,Activity
就能夠收到當前任務進度的通知,來更新UI
。而在任務執行完畢以後,咱們就能夠將該Fragment
移除了。spa
public class RotationPersistActivity extends AppCompatActivity implements IHolder {
private static final String TAG = RotationPersistActivity.class.getName();
private Button mBtWorker;
private TextView mTvResult;
private CompositeDisposable mCompositeDisposable;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate");
setContentView(R.layout.activity_rotation_persist);
mBtWorker = (Button) findViewById(R.id.bt_start_worker);
mBtWorker.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startWorker();
}
});
mTvResult = (TextView) findViewById(R.id.tv_worker_result);
mCompositeDisposable = new CompositeDisposable();
}
@Override
public void onWorkerPrepared(Observable<String> worker) {
DisposableObserver<String> disposableObserver = new DisposableObserver<String>() {
@Override
public void onNext(String message) {
mTvResult.setText(message);
}
@Override
public void onError(Throwable throwable) {
onWorkerFinished();
mTvResult.setText("任務錯誤");
}
@Override
public void onComplete() {
onWorkerFinished();
mTvResult.setText("任務完成");
}
};
worker.observeOn(AndroidSchedulers.mainThread()).subscribe(disposableObserver);
mCompositeDisposable.add(disposableObserver);
}
private void startWorker() {
WorkerFragment worker = getWorkerFragment();
if (worker == null) {
addWorkerFragment();
} else {
Log.d(TAG, "WorkerFragment has attach");
}
}
private void onWorkerFinished() {
Log.d(TAG, "onWorkerFinished");
removeWorkerFragment();
}
private void addWorkerFragment() {
WorkerFragment workerFragment = new WorkerFragment();
Bundle bundle = new Bundle();
bundle.putString("task_name", "學習RxJava2");
workerFragment.setArguments(bundle);
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.add(workerFragment, WorkerFragment.TAG);
transaction.commit();
}
private void removeWorkerFragment() {
WorkerFragment workerFragment = getWorkerFragment();
if (workerFragment != null) {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.remove(workerFragment);
transaction.commit();
}
}
private WorkerFragment getWorkerFragment() {
FragmentManager manager = getSupportFragmentManager();
return (WorkerFragment) manager.findFragmentByTag(WorkerFragment.TAG);
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
mCompositeDisposable.clear();
}
}
複製代碼
咱們來演示一下屏幕旋轉時的效果,在屏幕旋轉以後,咱們仍然能夠繼續收到任務進度的更新: 3d
下面,咱們來解釋一下示例中的幾個要點:code
數據傳遞分爲兩個方向,它們各自能夠經過如下方法來實現:cdn
Activity
向Fragment
傳遞數據(示例中咱們傳遞了任務的名稱) 通常用於向WorkerFragment
傳遞一些任務參數,此時能夠經過Fragment
的setArguments
傳入相關的字段,Fragment
在onCreate
方法中經過getArguments
獲取參數。Fragment
向Activity
傳遞數據(示例中咱們傳遞了Observable
供Activity
訂閱以獲取進度) 可讓Activity
實現一個接口,咱們在Fragment
的onAttach
方法中獲取Activity
實例轉換成對應的接口類型,以後經過它來調用Activity
的方法,須要注意的是,在Fragment#onDetach
時,要將該Activity
的引用置空,不然會出現內存泄漏。推薦你們先看一下這篇文章 RxJava 教程第三部分:馴服數據流之 Hot & Cold Observable,這裏面對於Cold & Hot Observable
進行了解釋,它們之間關鍵的區別就是:
Cold Observale
纔開始發送數據,而且每一個訂閱者都獨立執行一遍數據流代碼。Hot Observable
無論有沒有訂閱者,它都會發送數據流。而在咱們的應用場景中,因爲WorkerFragment
是在後臺執行任務:
Activity
的角度來看:每次Activity
重建時,在Activity
中都須要用一個新的Observer
實例去訂閱WorkerFragment
中的數據源,所以咱們只能選擇經過Hot Observable
,而不是Cold Observable
來實現WorkerFragment
中的數據源,不然每次都會從新執行一遍數據流的代碼,而不是繼續接收它發送的事件。WorkerFragment
的角度來看,它只是一個任務的執行者,無論有沒有人在監聽它的進度,它都應該執行任務。經過Observable.create
方法建立的是一個Cold Observable
,該Cold Observable
每隔1s
發送一個事件。咱們調用publish
方法來將它轉換爲Hot Observable
,以後再調用該Hot Observable
的connect
方法讓其對源Cold Observable
進行訂閱,這樣源Cold Observable
就能夠開始執行任務了。而且,經過connect
方法返回的Disposable
對象,咱們就能夠管理轉換後的Hot Observable
和源Cold Observable
之間的訂閱關係。
Disposable
的用途在於:在某些時候,咱們但願可以中止源Observable
任務的執行,例如當該WorkerFragment
真正被銷燬時,也就是執行了它的onDestroy
方法,那麼咱們就能夠經過上面的Disposable
取消Hot Observable
對源Cold Observable
的訂閱,而在Cold Observable
的循環中,咱們判斷若是下游(也就是Hot Observable
)取消了訂閱,那麼就再也不執行任務。
整個的架構圖以下所示:
在了能讓WorkerFragment
能正常進行下一次任務的執行,咱們須要在發生錯誤或者任務完成的時候,經過remove
的方式銷燬WorkerFragment
。