美文网首页android基础知识
无界面Fragment绑定数据

无界面Fragment绑定数据

作者: apkcore | 来源:发表于2018-01-07 17:10 被阅读132次

无界面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<ActivityResultInfo> startForResult(Intent intent, int requestCode) {
        return mAvoidOnResultFragment.startForResult(intent, requestCode);
    }

    public Observable<ActivityResultInfo> 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<Integer, PublishSubject<ActivityResultInfo>> mSubjects = new HashMap<>();
    private Map<Integer, AvoidOnResult.Callback> mCallbacks = new HashMap<>();

    public AvoidOnResultFragment() {
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);
    }

    public Observable<ActivityResultInfo> startForResult(final Intent intent, final int requestCode) {
        PublishSubject<ActivityResultInfo> subject = PublishSubject.create();
        mSubjects.put(requestCode, subject);
        return subject.doOnSubscribe(new Consumer<Disposable>() {
            @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<ActivityResultInfo> 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<ActivityResultInfo>() {
                            @Override
                            public boolean test(ActivityResultInfo activityResultInfo) throws Exception {
                                return activityResultInfo.getResultCode() == 9527;
                            }
                        })
                        .subscribe(new Consumer<ActivityResultInfo>() {
                            @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:
 * <ul>
 * <li> {@link #onDestroy()} will not be called (but {@link #onDetach()} still
 * will be, because the fragment is being detached from its current activity).
 * <li> {@link #onCreate(Bundle)} will not be called since the fragment
 * is not being re-created.
 * <li> {@link #onAttach(Activity)} and {@link #onActivityCreated(Bundle)} <b>will</b>
 * still be called.
 * </ul>
 */
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<Void, Integer, Void> {

        @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

参考

End

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

Apkcore.jpg

相关文章

  • 无界面Fragment绑定数据

    无界面Fragment 最近在跟着郭神的glide源码分析,发现有这么一段 不管你在Glide.with()方法中...

  • vue\angular\react区别

    一、关于数据绑定 Angular使用双向绑定即:界面的操作能实时反映到数据,数据的变更能实时展现到界面。 原理:$...

  • 微信小程序

    界面数据的绑定 刷新 1. 在.js文件中 申明数据 2. 在.js文件中 绑定新数据刷新界面 计时器 每隔1秒执...

  • vue学习扩展

    Vue数据绑定的过程 先将未绑定数据的界面展示给用户 view 根据模型中的数据和控制的区域生成绑定数据后的htm...

  • angular.js的双向绑定

    angular.js的双向绑定就是可以将界面操作可以反映到数据,数据改变也可以体现在界面上。 界面到数据的传递,主...

  • fragment 懒加载

    fragment 的懒加载 懒加载 什么是懒加载:只有在 fragment 显示在界面的时候,才进行数据的加载 懒...

  • Fragment恢复(重建)后onActivityResult中

    前言 先说场景:Activity A界面有Viewpager+Fragment,Fragment中是列表,在A界面...

  • JetPack_ViewModel + LiveData +Da

    使用 ViewModel 将控制器中 ( Activity , Fragment ) 的数据单独抽离出来,并且界面...

  • Jetpack系列(二、架构)

    一、数据绑定库 数据绑定库是一种支持库,可以使用声明格式将布局中的界面组件绑定在应用的数据源。布局通常使用该框架方...

  • AngularJS双向绑定之脏检查机制

    一、Angular双向绑定(MVVM) 视图的改变能反映到数据模型上,数据的更改也能在界面呈现 二、触发双向绑定 ...

网友评论

    本文标题:无界面Fragment绑定数据

    本文链接:https://www.haomeiwen.com/subject/iscmnxtx.html