Android Architecture Componets - ViewModel

ViewModel

/**
ViewModel是一个负责准备和管理Activity或Fragment数据的类。
它还处理Activity / Fragment与其他应用程序的通信 (例如调用业务逻辑类)
ViewModel会在一个关联的范围内(activity 或者 fragment)创建,只要activity或fragment是活着的,它将被保留.
换句话说,这意味着如果它的所有者由于配置改变被销毁了(例如旋转), ViewModel不会被销毁,所有者的新实例将重新连接到现有的ViewModel。
ViewModel的目的是获取并保存Activity或Fragment所需的信息. Activity或Fragment应该能够观察ViewModel中的变化。ViewModel通常通过LiveData或Android DataBinding公开这些信息。 你也可以使用你喜欢的框架的任何可观察性构造。
ViewModel唯一的职责就是管理用户界面的数据, 它不应该访问您的视图层次结构或持有引用回到Activity或Fragment.

典型的用法:
 * public class UserActivity extends Activity {
 *
 *     {@literal @}Override
 *     protected void onCreate(Bundle savedInstanceState) {
 *         super.onCreate(savedInstanceState);
 *         setContentView(R.layout.user_activity_layout);
 *         final UserModel viewModel = ViewModelProviders.of(this).get(UserModel.class);
 *         viewModel.userLiveData.observer(this, new Observer<User>() {
 *            {@literal @}Override
 *             public void onChanged(@Nullable User data) {
 *                 // update ui.
 *             }
 *         });
 *         findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
 *             {@literal @}Override
 *             public void onClick(View v) {
 *                  viewModel.doAction();
 *             }
 *         });
 *     }
 * }
 
 ViewModel:
  * public class UserModel extends ViewModel {
 *     public final LiveData&lt;User&gt; userLiveData = new LiveData<>();
 *
 *     public UserModel() {
 *         // trigger user load.
 *     }
 *
 *     void doAction() {
 *         // depending on the action, do necessary business logic calls and update the
 *         // userLiveData.
 *     }
 * }
ViewModels也可以用作Activity中的不同Fragment之间的通信层。 每个Fragment可以通过他们的Activity使用相同的密钥来获取ViewModel。这允许Fragment之间以分离的方式进行通信,使得它们不需要直接与另一个Fragment进行通信。
 * public class MyFragment extends Fragment {
 *     public void onStart() {
 *         UserModel userModel = ViewModelProviders.of(getActivity()).get(UserModel.class);
 *     }
 * }
*/
public abstract class ViewModel {
    /**
     * 这个ViewModel被销毁时调用
     * <p>
     * It is useful when ViewModel observes some data and you need to clear this subscription to
     * prevent a leak of this ViewModel.
     */
    @SuppressWarnings("WeakerAccess")
    protected void onCleared() {
    }
}
ViewModelProviders.of(this).get(MyViewModel::class.java)
// ViewModelProviders
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
    // 确保单例对象 sDefaultFactory 实例存在
    initializeFactoryIfNeeded(checkApplication(activity));
    return new ViewModelProvider(ViewModelStores.of(activity), sDefaultFactory);
}
// ViewModelStores
@MainThread
public static ViewModelStore of(@NonNull FragmentActivity activity) {
    // 创建HolderFragment 并且添加到Activity当中, 返回ViewModelStore
    return HolderFragment.holderFragmentFor(activity).getViewModelStore();
}
// HolderFragment

// getViewModelStore() 返回该实例
private ViewModelStore mViewModelStore = new ViewModelStore();

private static final HolderFragmentManager sHolderFragmentManager = new HolderFragmentManager();

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static HolderFragment holderFragmentFor(FragmentActivity activity) {
    return sHolderFragmentManager.holderFragmentFor(activity);
}

// HolderFragmentManager
HolderFragment holderFragmentFor(FragmentActivity activity) {
    FragmentManager fm = activity.getSupportFragmentManager();
    // 根据 HOLDER_TAG 寻找 HolderFragment
    HolderFragment holder = findHolderFragment(fm);
    if (holder != null) {
        return holder;
    }
    // mNotCommittedActivityHolders 保存已经执行了add操作, 但是没有添加进去的 Fragment
    holder = mNotCommittedActivityHolders.get(activity);
    if (holder != null) {
        return holder;
    }

    if (!mActivityCallbacksIsAdded) {
        mActivityCallbacksIsAdded = true;
        // 在activity的 onDestroy()方法中 mNotCommittedActivityHolders释放引用
        activity.getApplication().registerActivityLifecycleCallbacks(mActivityCallbacks);
    }
    // 创建 HolderFragment 添加到当前 Activity 当中
    holder = createHolderFragment(fm);
    // 上面方法执行了 fragment 的 add以及commit 操作, 到HolderFragment 真正添加到activity之前记录
    mNotCommittedActivityHolders.put(activity, holder);
    return holder;
}
// HolderFragment
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    sHolderFragmentManager.holderFragmentCreated(this);
}
// HolderFragmentManager
void holderFragmentCreated(Fragment holderFragment) {
    // 如果是在Fragment中嵌套Fragment, 该返回不为空, 这里只讨论Activity的情况
    Fragment parentFragment = holderFragment.getParentFragment();
    if (parentFragment != null) {
        mNotCommittedFragmentHolders.remove(parentFragment);
        parentFragment.getFragmentManager().unregisterFragmentLifecycleCallbacks(
                mParentDestroyedCallback);
    } else {
        // 执行 onCreate() 说明HolderFragment以及添加到Activity, 可以释放引用了
        mNotCommittedActivityHolders.remove(holderFragment.getActivity());
    }
}
@NonNull
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
    String canonicalName = modelClass.getCanonicalName();
    if (canonicalName == null) {
        throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
    }
    return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
    ViewModel viewModel = mViewModelStore.get(key);

    if (modelClass.isInstance(viewModel)) {
        //noinspection unchecked
        return (T) viewModel;
    } else {
        //noinspection StatementWithEmptyBody
        if (viewModel != null) {
            // TODO: log a warning.
        }
    }
    // 根据反射创建实例
    viewModel = mFactory.create(modelClass);
    // 存入 viewModel实例
    mViewModelStore.put(key, viewModel);
    //noinspection unchecked
    return (T) viewModel;
}

到这里会发现只是将一个毫不起眼的HolderFragment添加到Activity,并且利用HolderFragment中的ViewModelStore中的Map保存了 自定义ViewModel实例.

HolderFragment是如何在在Activity配置变更被销毁时继续持有viewModel的?

// HolderFragment
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // 这行代码上面已经分析过了
    sHolderFragmentManager.holderFragmentCreated(this);
}
@Override
public void onDestroy() {
    super.onDestroy();
    // 调用ViewModel的
    mViewModelStore.clear();
}

// ViewModelStore
public final void clear() {
    for (ViewModel vm : mMap.values()) {
        // 空方法, 继承给开发者用, 做释放等操作
        vm.onCleared();
    }
    // 清空map, 也就是 释放ViewModel数据
    mMap.clear();
}

上面也并没有什么特殊的操作, 并且在onDestroy() 中释放了 viewModel, 那么在 Activity例如旋转的时候会调用onDestroy(), 跟着 Fragment的 onDestroyView(), onDestroy(), onDetach() 会依次触发. HolderFragment为什么不会调用 onDestroy()?

关键来了!!

// 关键就在 HolderFragment的构造方法中
public HolderFragment() {
    setRetainInstance(true);
}
// Fragment
/**
 控制是否在重新创建Activity(例如配置更改)中保留Fragment实例。这只能用于不在back stack中的Fragment。 如果设置,则在 recreate Activity时,Fragment生命周期将略有不同:
 onDestroy() 将不会被调用 (但onDetach() 仍然会,因为片段正在从它当前的活动分离). onCreate(Bundle)将不会被调用,因为片段不被重新创建。onAttach(Activity) 和 onActivityCreated(Bundle)仍然被调用。
 */
public void setRetainInstance(boolean retain) {
    mRetainInstance = retain;
}

所以在ViewModel类的注释中有一段

ViewModel唯一的职责就是管理用户界面的数据, 它不应该访问您的视图层次结构或持有引用回到Activity或Fragment.

一旦ViewModel持有了Activity, 并且在旋转时, 新的Activity需要创建, 而旧的Activity需要被释放. 但是被ViewModel持有而释放不了就有可能造成内存泄漏.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容