若是咱们在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
。