无界面Fragment绑定数据

无界面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就可以捕获这个事件并停止图片加载了。

有一种拍大腿的感觉(原谅我书读得少)

无界面Fragment绑定数据_第1张图片
读书

而后发现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());
                            }
                        });
            }
        });

发散思维

重要的是这一种思想,而不是代码的实现。

无界面Fragment绑定数据_第2张图片
举个粟子

实现非侵入式的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

下面的是我的公众号,欢迎大家关注我。

无界面Fragment绑定数据_第3张图片
Apkcore.jpg

你可能感兴趣的:(无界面Fragment绑定数据)