RxJava2 实战系列文章
一、前言
如果我们在AndroidManifest.xml
中声明Activity
时,没有对android:configChanges
进行特殊的声明,那么在屏幕旋转时,会导致Activity
的重建,几个关键声明周期的调用情况如下所示:
旋转屏幕前的
Activity
中的变量都会被销毁,但是有时候我们某些任务的执行不和Activity
的生命周期绑定,这时候我们就可以利用Fragment
提供的setRetainInstance
方法,该方法的说明如下:setRetainInstance 方法说明
如果给
Fragment
设置了该标志位,那么在屏幕旋转之后,虽然它依附的Activity
被销毁了,但是该Fragment
的实例会被保留,并且在Activity
的销毁过程中,只会调用该Fragment
的onDetach
方法,而不会调用onDestroy
方法。
而在Activity
重建时,会调用该Fragment
实例的onAttach
、onActivityCreated
方法,但不会调用onCreate
方法。
今天,我们跟着前人脚步,用RxJava
来演示在屏幕旋转导致Activity
重建时,仍然保持后台任务继续执行的例子。
二、示例
2.1 示例
首先,我们声明一个接口,用于Fragment
向Activity
一个ConnectableObservable
,使得Activity
可以监听到Fragment
中后台任务的工作进度。
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
移除了。
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);
}
private void removeWorkerFragment() {
WorkerFragment workerFragment = getWorkerFragment();
if (workerFragment != null) {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.remove(workerFragment);
}
}
private WorkerFragment getWorkerFragment() {
FragmentManager manager = getSupportFragmentManager();
return (WorkerFragment) manager.findFragmentByTag(WorkerFragment.TAG);
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
mCompositeDisposable.clear();
}
}
我们来演示一下屏幕旋转时的效果,在屏幕旋转之后,我们仍然可以继续收到任务进度的更新:
2.2 示例解析
下面,我们来解释一下示例中的几个要点:
2.2.1 Activity 和 Fragment 之间的数据传递
数据传递分为两个方向,它们各自可以通过以下方法来实现:
-
Activity
向Fragment
传递数据(示例中我们传递了任务的名称)
一般用于向WorkerFragment
传递一些任务参数,此时可以通过Fragment
的setArguments
传入相关的字段,Fragment
在onCreate
方法中通过getArguments
获取参数。 -
Fragment
向Activity
传递数据(示例中我们传递了Observable
供Activity
订阅以获取进度)
可以让Activity
实现一个接口,我们在Fragment
的onAttach
方法中获取Activity
实例转换成对应的接口类型,之后通过它来调用Activity
的方法,需要注意的是,在Fragment#onDetach
时,要将该Activity
的引用置空,否则会出现内存泄漏。
2.2 为什么调用 publish 方法,使用 Hot Observable 作为 WorkerFragment 的数据源
- 只有当订阅者订阅时,
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
)取消了订阅,那么就不再执行任务。
整个的架构图如下所示:
2.3 在任务执行之后 removeFragment
在了能让WorkerFragment
能正常进行下一次任务的执行,我们需要在发生错误或者任务完成的时候,通过remove
的方式销毁WorkerFragment
。
更多文章,欢迎访问我的 Android 知识梳理系列:
- Android 知识梳理目录:
- 个人主页:
- 个人知识总结目录: