无界面Fragment
最近在跟着郭神的glide源码分析,发现有这么一段
不管你在Glide.with()方法中传入的是Activity、FragmentActivity、v4包下的Fragment、还是app包下的Fragment,最终的流程都是一样的,那就是会向当前的Activity当中添加一个隐藏的Fragment。那么这里为什么要添加一个隐藏的Fragment呢?因为Glide需要知道加载的生命周期。很简单的一个道理,如果你在某个Activity上正在加载着一张图片,结果图片还没加载出来,Activity就被用户关掉了,那么图片还应该继续加载吗?当然不应该。可是Glide并没有办法知道Activity的生命周期,于是Glide就使用了添加隐藏Fragment的这种小技巧,因为Fragment的生命周期和Activity是同步的,如果Activity被销毁了,Fragment是可以监听到的,这样Glide就可以捕获这个事件并停止图片加载了。
有一种拍大腿的感觉(原谅我书读得少)
而后发现RxPermissions也是这样处理的,它内部持有一个Fragment,这个Fragment没有视图,只负责请求权限和返回结果,相当于一个桥梁的作用,我们通过rxPermissions发起request的时候,其实并不是activity去request,而是通过这个Fragment去请求,然后在Fragment的onRequestPermissionsResult中把结果发送出来,如此来避开activity的onRequestPermissionsResult方法。
没有布局的Fragment的使用
这样就给了我们很大的想象空间,在掘金上看到anotherJack有关于Lifecycle add Fragment 方案1统一优雅地封装和使用 onActivityResult,使用AvoidOnResult来start统一这里我就直接copy一下作者的代码
public class AvoidOnResult {
private static final String TAG = "AvoidOnResult";
private AvoidOnResultFragment mAvoidOnResultFragment;
public AvoidOnResult(Activity activity) {
mAvoidOnResultFragment = getAvoidOnResultFragment(activity);
}
public AvoidOnResult(Fragment fragment){
this(fragment.getActivity());
}
private AvoidOnResultFragment getAvoidOnResultFragment(Activity activity) {
AvoidOnResultFragment avoidOnResultFragment = findAvoidOnResultFragment(activity);
if (avoidOnResultFragment == null) {
avoidOnResultFragment = new AvoidOnResultFragment();
FragmentManager fragmentManager = activity.getFragmentManager();
fragmentManager
.beginTransaction()
.add(avoidOnResultFragment, TAG)
.commitAllowingStateLoss();
fragmentManager.executePendingTransactions();
}
return avoidOnResultFragment;
}
private AvoidOnResultFragment findAvoidOnResultFragment(Activity activity) {
return (AvoidOnResultFragment) activity.getFragmentManager().findFragmentByTag(TAG);
}
public Observable startForResult(Intent intent, int requestCode) {
return mAvoidOnResultFragment.startForResult(intent, requestCode);
}
public Observable startForResult(Class> clazz, int requestCode) {
Intent intent = new Intent(mAvoidOnResultFragment.getActivity(), clazz);
return startForResult(intent, requestCode);
}
public void startForResult(Intent intent, int requestCode, Callback callback) {
mAvoidOnResultFragment.startForResult(intent, requestCode, callback);
}
public void startForResult(Class> clazz, int requestCode, Callback callback) {
Intent intent = new Intent(mAvoidOnResultFragment.getActivity(), clazz);
startForResult(intent, requestCode, callback);
}
public interface Callback {
void onActivityResult(int requestCode, int resultCode, Intent data);
}
}
监听的 Fragment 如下:
public class AvoidOnResultFragment extends Fragment {
private Map> mSubjects = new HashMap<>();
private Map mCallbacks = new HashMap<>();
public AvoidOnResultFragment() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
public Observable startForResult(final Intent intent, final int requestCode) {
PublishSubject subject = PublishSubject.create();
mSubjects.put(requestCode, subject);
return subject.doOnSubscribe(new Consumer() {
@Override
public void accept(Disposable disposable) throws Exception {
startActivityForResult(intent, requestCode);
}
});
}
public void startForResult(Intent intent, int requestCode, AvoidOnResult.Callback callback) {
mCallbacks.put(requestCode, callback);
startActivityForResult(intent, requestCode);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
//rxjava方式的处理
PublishSubject subject = mSubjects.remove(requestCode);
if (subject != null) {
subject.onNext(new ActivityResultInfo(requestCode, resultCode, data));
subject.onComplete();
}
//callback方式的处理
AvoidOnResult.Callback callback = mCallbacks.remove(requestCode);
if (callback != null) {
callback.onActivityResult(requestCode, resultCode, data);
}
}
}
扩展rxjava调用
//callback方式
callback.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new AvoidOnResult(MainActivity.this).startForResult(ScrollingActivity.class, 9527, new AvoidOnResult.Callbck() {
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.d("bsb3", requestCode + "-" + resultCode + data.getStringExtra("a"));
}
});
}
});
//rxjava方式
rxjava.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new AvoidOnResult(MainActivity.this)
.startForResult(ScrollingActivity.class, 9527)
.filter(new Predicate() {
@Override
public boolean test(ActivityResultInfo activityResultInfo) throws Exception {
return activityResultInfo.getResultCode() == 9527;
}
})
.subscribe(new Consumer() {
@Override
public void accept(ActivityResultInfo activityResultInfo) throws Exception {
Log.d("bsb3", activityResultInfo.toString());
}
});
}
});
发散思维
重要的是这一种思想,而不是代码的实现。
实现非侵入式的RxLifecycle,我们大部分使用RxLifecycle来自动管理rxjava的生命周期时,侵入性太强,activity必须extends RxActivity
,所以借鉴的是Glide的做法:Glide.with(),我们知道当Fragment通过FragmentManager加入到Activity中后,具有和activity基本相同的生命周期(忽略其他),那么我们通过这个Fragment发送activity的使命周期事件,同样能达到预期的效果。当然还有诸如此类可以解决各种第三方登录的问题,也可以同理解决。
setRetainInstance介绍
在上面我们发现在Fragment的onCreate方法中,setRetainInstance(true);
官方对此的解释为
/**
* Control whether a fragment instance is retained across Activity
* re-creation (such as from a configuration change). This can only
* be used with fragments not in the back stack. If set, the fragment
* lifecycle will be slightly different when an activity is recreated:
*
* - {@link #onDestroy()} will not be called (but {@link #onDetach()} still
* will be, because the fragment is being detached from its current activity).
*
- {@link #onCreate(Bundle)} will not be called since the fragment
* is not being re-created.
*
- {@link #onAttach(Activity)} and {@link #onActivityCreated(Bundle)} will
* still be called.
*
*/
public void setRetainInstance(boolean retain) {
mRetainInstance = retain;
}
当设备配置发生变化时,FragmentManager首先销毁队列中fragment的视图(因为可能有更合适的匹配资源);
紧接着,FragmentManager将检查每个fragment的retainInstance属性值。
如果retainInstance属性值为false,FragmentManager会立即销毁该fragment实例。
随后,为适应新的设备配置,新的Activity的新的FragmentManager会创建一个新的fragment及其视图。
如果retainInstance属性值为true,则该fragment的视图立即被销毁,但fragment本身不会被销毁。
为适应新的设备配置,当新的Activity创建后,新的FragmentManager会找到被保留的fragment,并重新创建其视图。
虽然保留的fragment没有被销毁,但它已脱离消亡中的activity并处于保留状态。
尽管此时的fragment仍然存在,但已经没有任何activity托管它,其生命周期如下
[图片上传失败...(image-91b8eb-1515316184589)]
需要说明的是:
只有调用了fragment的setRetainInstance(true)方法,
并且因设备配置改变,托管Activity正在被销毁的条件下,
fragment才会短暂的处于保留状态。
如果activity是因操作系统需要回收内存而被销毁,则所有的fragment也会随之销毁。
相对而言,onSaveInstanceState可以更长久的保持数据。
当Activity所在进程被系统杀死(非用户主动关闭),系统重新创建activity时,
将恢复onSaveInstanceState中保留的数据。
从上文我们知道,当Fragment设置了setRetainInstance(true)后,
在设备旋转等情况下,该Fragment可以暂时与Activity分离。
如果此时Fragment持有的后台线程,例如AsyncTask中的后台操作等,
需要使用Fragment的Context等信息,就可能出现错误。
为此,Fragment中定义了isAdded接口,
用于判断Fragment是否已经绑定到某个Activity。
setRetainInstance使用
下面代码演示如何使用fragment在配置发生变化的时候保存AsyncTask的状态。这段代码保证了最新的进度和结果能够被传回更当前正在显示的Activity实例,并确保我们不会在配置发生变化的时候丢失AsyncTask的状态。
MainActivity.java
public class MainActivity extends AppCompatActivity implements TaskFragment.TaskCallbacks {
private static final String TAG = "TaskFragment";
private TaskFragment mTaskFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentManager fm = getFragmentManager();
mTaskFragment = (TaskFragment) fm.findFragmentByTag(TAG);
if (mTaskFragment == null) {
mTaskFragment = new TaskFragment();
fm.beginTransaction().add(mTaskFragment, TAG).commitAllowingStateLoss();
}
}
@Override
public void onPreExecute() {
Log.d(TAG, "onPreExecute: ");
}
@Override
public void onProgressUpdtae(int percent) {
Log.d(TAG, "onProgressUpdtae: " + percent);
}
@Override
public void onCancelled() {
Log.d(TAG, "onCancelled: ");
}
@Override
public void onPostExecute() {
Log.d(TAG, "onPostExecute: ");
}
TaskFragment
public class TaskFragment extends Fragment {
private static final String TAG = "TaskFragment";
public interface TaskCallbacks {
void onPreExecute();
void onProgressUpdtae(int percent);
void onCancelled();
void onPostExecute();
}
private TaskCallbacks mTaskCallbacks;
private DummyTask mDummyTask;
@Override
public void onAttach(Activity context) {
super.onAttach(context);
Log.d(TAG, "onAttach: ");
mTaskCallbacks = (TaskCallbacks) context;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate: ");
setRetainInstance(true);
mDummyTask = new DummyTask();
mDummyTask.execute();
}
@Override
public void onDetach() {
Log.d(TAG, "onDetach: ");
super.onDetach();
mTaskCallbacks = null;
}
private class DummyTask extends AsyncTask {
@Override
protected Void doInBackground(Void... voids) {
for (int i = 0; !isCancelled() && i < 100; i++) {
SystemClock.sleep(100);
publishProgress(i);
}
return null;
}
@Override
protected void onPreExecute() {
if (mTaskCallbacks != null) {
mTaskCallbacks.onPreExecute();
}
}
@Override
protected void onProgressUpdate(Integer... values) {
if (mTaskCallbacks != null) {
mTaskCallbacks.onProgressUpdtae(values[0]);
}
}
@Override
protected void onPostExecute(Void aVoid) {
if (mTaskCallbacks != null) {
mTaskCallbacks.onPostExecute();
}
}
@Override
protected void onCancelled() {
if (mTaskCallbacks != null) {
mTaskCallbacks.onCancelled();
}
}
}
}
观看生命周期回调
01-07 07:53:29.690 5456-5456/com.apkcore.taskfragment D/TaskFragment: onDetach:
01-07 07:53:29.698 5456-5456/com.apkcore.taskfragment D/TaskFragment: onAttach:
验证了在旋转屏幕的时候,fragment只回调了onDetach与onAttach方法。
Github
参考
- Android图片加载框架最全解析(二),从源码的角度理解Glide的执行流程
- 动手实现一个轻量级无侵入性的RxJava自动注销库RxLifecycle
- 避免使用onActivityResult,以提高代码可读性
- Fragment调用setRetainInstance的原理
End
下面的是我的公众号,欢迎大家关注我。