jetpack解析ViewModel使用原理解析

概述

  • 在横竖屏切换/系统语言切换的时候都会导致Activity的销毁重建,Activity销毁的时候如果需要保存业务数据需求,一般会在onSaveInstanceState方法保存Bundle类型的数据,然后在onCreate或者onRestoreInstanceState方法中恢复数据。然而这只能解决数据保存的问题而且必须是Bundle支持的数据类型才可以,无法解决销毁重建可能带来的生命周期切换带来的内存泄漏的问题。鉴于此,jetpack推出了ViewModel解决数据保存/恢复和内存泄漏的问题,这篇文章主要分析ViewModelFramwork层的实现原理。

1.ViewModel使用

  • 通常我们会在代码中如下使用ViewModel
[PersonViewModel]
//1.自定义PersonViewModel实现ViewModel
public class PersonViewModel extends ViewModel {

    //2.使用MutableLiveData监听数据的变化,进而做出相应
    public MutableLiveData mutableLiveNameData=new MutableLiveData<>();

    //3.伴随Activity生命周期OnDestroy销毁的时间ViewModel的回调,用来做善后清理工作
    @Override
    protected void onCleared() {
        super.onCleared();
        mutableLiveNameData=null;
    }
}

[MainActivity]
 @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 实例化ViewModel
        personViewModel= ViewModelProviders.of(this).get(PersonViewModel.class);
        personViewModel.mutableLiveNameData.observe(this, new Observer() {
            @Override
            public void onChanged(String s) {
                //监听数据变化时的回调,做出响应操作
            }
        });
  • 使用方式很简单,不做多余的解释,下面直接personViewModel= ViewModelProviders.of(this).get(PersonViewModel.class) 分析原理

2. ViewModelProviders

  • ViewModelProviders是一个辅助类,根据参数activity调用内部static方法完成创建ViewModelProvider
    [ViewModelProviders]
    //1.静态方法,参数是activity实例
    public static ViewModelProvider of(@NonNull FragmentActivity activity) {
        return of(activity, null);
    }

//2.根据activity和factory创建ViewModelProvider
 public static ViewModelProvider of(@NonNull FragmentActivity activity,
            @Nullable Factory factory) {
        //2.1 检查application是否为null,若为null,抛出异常
        Application application = checkApplication(activity);
        if (factory == null) {//2.2默认为null
            factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
        }
        //3.创建ViewModelProvider并返回
        return new ViewModelProvider(activity.getViewModelStore(), factory);
    }

  private static Application checkApplication(Activity activity) {
        Application application = activity.getApplication();
        if (application == null) {
            throw new IllegalStateException("Your activity/fragment is not yet attached to "
                    + "Application. You can't request ViewModel before onCreate call.");
        }
        return application;
    }

3.ViewModelProvider

[ViewModelProvider]
  public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
        mFactory = factory;
        mViewModelStore = store;
    }
  • ViewModelProvider构造函数的参数:ViewModelStore,factory,下面对这两个参数进行分析。

3.1 ViewModelStore

[ViewModelStore]
//1.存储Activity或者Fragment相关的ViewModel集合
public class ViewModelStore {

    private final HashMap mMap = new HashMap<>();

    final void put(String key, ViewModel viewModel) {
        //2.当有值覆盖的时候会调用oldViewModel.onCleared()
        ViewModel oldViewModel = mMap.put(key, viewModel);
        if (oldViewModel != null) {
            oldViewModel.onCleared();
        }
    }

    final ViewModel get(String key) {
        return mMap.get(key);
    }

    Set keys() {
        return new HashSet<>(mMap.keySet());
    }

    /**
     *  Clears internal storage and notifies ViewModels that they are no longer used.
     */
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }
}
  • ViewModelStore使用HashMap存储ActivityFragment实例相关联的所有ViewModel
  • keyViewModel全类名,value是开发者自定义PersonViewModel实例
  • Activity生命周期onDestroy的时候判断是否是由于配置改变造成的destroy,如果不是会调用ViewModelStoreclear方法,遍历map中所有的ViewModel实例,调用ViewModelclear方法,最后调用mMap.clear方法清除集合中的所有元素。

3.2 Factory

  • 创建ViewModel的工厂接口,这里使用了设计模式中的静态工厂设计模式
public interface Factory { 
        @NonNull
         T create(@NonNull Class modelClass);
    }

 public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {

        private static AndroidViewModelFactory sInstance;

        /**
         * Retrieve a singleton instance of AndroidViewModelFactory.
         *
         * @param application an application to pass in {@link AndroidViewModel}
         * @return A valid {@link AndroidViewModelFactory}
         */
        @NonNull
        public static AndroidViewModelFactory getInstance(@NonNull Application application) {
            if (sInstance == null) {
                sInstance = new AndroidViewModelFactory(application);
            }
            return sInstance;
        }

        private Application mApplication;

        /**
         * Creates a {@code AndroidViewModelFactory}
         *
         * @param application an application to pass in {@link AndroidViewModel}
         */
        public AndroidViewModelFactory(@NonNull Application application) {
            mApplication = application;
        }

        @NonNull
        @Override
        public  T create(@NonNull Class modelClass) {
            if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
                //noinspection TryWithIdenticalCatches
                try {
                    return modelClass.getConstructor(Application.class).newInstance(mApplication);
                } catch (NoSuchMethodException e) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                } catch (InstantiationException e) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                } catch (InvocationTargetException e) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                }
            }
            return super.create(modelClass);
        }
    }
}

  public static class NewInstanceFactory implements Factory {

        @SuppressWarnings("ClassNewInstance")
        @NonNull
        @Override
        public  T create(@NonNull Class modelClass) {
            //noinspection TryWithIdenticalCatches
            try {
                return modelClass.newInstance();
            } catch (InstantiationException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            }
        }
    }
  • 根据开发者自定的ViewModel类型选择不同的工厂反射创建ViewModel实例

  • 若开发者自定义ViewModel继承的是ViewModel则使用NewInstanceFactory工程创建

  • 若开发者自定义ViewModel继承的是AndroidViewModel则使用AndroidViewModelFactory工程创建,内部可以获取一个全局的Application应用实例,为了防止调用上下文不当造成内存泄漏.

经过上面的分析知道了创建ViewModel的流程,接下俩看下activity实例怎么关联ViewModelStore的。
[ViewModelPrividers]
public static ViewModelProvider of(@NonNull FragmentActivity activity,
            @Nullable Factory factory) {
        Application application = checkApplication(activity);
        if (factory == null) {
            factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
        }
        //获取activity关联的ViewModelStore
        return new ViewModelProvider(activity.getViewModelStore(), factory);
    }

4. Activity

  • 查看 activity.getViewModelStore方法
  • 由于Activity继承自ComponentActivity所以这里查看的是ComponentActivity
[ComponentActivity]
public ViewModelStore getViewModelStore() {
        if (getApplication() == null) {
            throw new IllegalStateException("Your activity is not yet attached to the "
                    + "Application instance. You can't request ViewModel before onCreate call.");
        }
        if (mViewModelStore == null) {
            //这里调用了getLastNonConfigurationInstance获取旋转配置变化前保存的数据,即恢复之前保存的数据
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
    }

   / * @return the object previously returned by {@link #onRetainNonConfigurationInstance()}
     */
  //这个注释告诉我们,数据保存是在onRetainNonConfigurationInstance()方法中
    @Nullable
    public Object getLastNonConfigurationInstance() {
        return mLastNonConfigurationInstances != null
                ? mLastNonConfigurationInstances.activity : null;
    }

public final Object onRetainNonConfigurationInstance() {
        Object custom = onRetainCustomNonConfigurationInstance();

        ViewModelStore viewModelStore = mViewModelStore;
        if (viewModelStore == null) {
            // No one called getViewModelStore(), so see if there was an existing
            // ViewModelStore from our last NonConfigurationInstance
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                viewModelStore = nc.viewModelStore;
            }
        }

        if (viewModelStore == null && custom == null) {
            return null;
        }

        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.custom = custom;
        nci.viewModelStore = viewModelStore;
        return nci;
    }
  • ativitity实例调用getLastNonConfigurationInstance方法获取config change配置变化前保存的NonConfigurationInstances实例nc,进而通过nc恢复关联的viewModelStore对象。
  • 调用onRetainNonConfigurationInstance方法完成NonConfigurationInstances实例对象的保存。

4.1 NonConfigurationInstance

static final class NonConfigurationInstances {
        Object custom;
        ViewModelStore viewModelStore;
    }
  • NonConfigurationInstances保存了activity关联的ViewModelStore实例。

此时不禁要想下,对NonConfigurationInstances 数据保存的方法onRetainNonConfigurationInstance方法在什么时机调用的呢?恢复数据的NonConfigurationInstances实例数据又是在哪里赋值的呢?

5. ActivityThread & Activity

  • 了解过Framework源码的人对这个类再熟悉不过了,四大组件的创建以及的它们生命周期的回调都是它内部实现的。
public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback,
        OnCreateContextMenuListener, ComponentCallbacks2,
        Window.OnWindowDismissedCallback, WindowControllerCallback,
        AutofillManager.AutofillClient, ContentCaptureManager.ContentCaptureClient {
   
    @UnsupportedAppUsage
    final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
        
        // 省略其他代码...这里对mLastNonConfigurationInstances进行了数据恢复
        mLastNonConfigurationInstances = lastNonConfigurationInstances;
        
    }

public final class ActivityThread extends ClientTransactionHandler {

    // 省略其他代码...
    //这里是执行了Activity的onDestory回调时触发的操作  
    ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
            int configChanges, boolean getNonConfigInstance, String reason) {
            
        // 省略其他代码...
        
        ActivityClientRecord r = mActivities.get(token);
        
        if (getNonConfigInstance) {//1.首先判断是否有LastNonConfigInstance实例
            try {
                r.lastNonConfigurationInstances
                        = r.activity.retainNonConfigurationInstances();
            } catch (Exception e) {
                if (!mInstrumentation.onException(r.activity, e)) {
                    throw new RuntimeException(
                            "Unable to retain activity "
                            + r.intent.getComponent().toShortString()
                            + ": " + e.toString(), e);
                }
            }
        }
        
        return r;
    }
}
  • 经过上面的源码分析,mLastNonConfigurationInstances的恢复是在Activity.attach方法中。
  • retainNonConfigurationInstances()方法的调用是在ActivityThread.performDestroy()中。

6.ViewModelStore中的数据什么时候会回收?

  • 上面说了在Activity#onDestory生命周期的时候会回收activity实例关联的内存中所有ViewModel数据,是不是这样呢?我们可以在源码中找到答案:
[ComponentActivity]
public ComponentActivity() {
        Lifecycle lifecycle = getLifecycle();
        //noinspection ConstantConditions
        if (lifecycle == null) {
            throw new IllegalStateException("getLifecycle() returned null in ComponentActivity's "
                    + "constructor. Please make sure you are lazily constructing your Lifecycle "
                    + "in the first call to getLifecycle() rather than relying on field "
                    + "initialization.");
        }
        if (Build.VERSION.SDK_INT >= 19) {
            getLifecycle().addObserver(new LifecycleEventObserver() {
                @Override
                public void onStateChanged(@NonNull LifecycleOwner source,
                        @NonNull Lifecycle.Event event) {
                    if (event == Lifecycle.Event.ON_STOP) {
                        Window window = getWindow();
                        final View decor = window != null ? window.peekDecorView() : null;
                        if (decor != null) {
                            decor.cancelPendingInputEvents();
                        }
                    }
                }
            });
        }
        getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source,
                    @NonNull Lifecycle.Event event) {
                if (event == Lifecycle.Event.ON_DESTROY) {
                    if (!isChangingConfigurations()) {
                      //如果不是因为配置变化引起的activity销毁就会销毁ViewModelStore中的所有ViewModel数据
                        getViewModelStore().clear();
                    }
                }
            }
        });

        if (19 <= SDK_INT && SDK_INT <= 23) {
            getLifecycle().addObserver(new ImmLeaksCleaner(this));
        }
    }
  • 监听Activity.onDestroy事件,如果不是因为配置变化引起的,比如屏幕旋转/系统字体切换引起的,即Activity是用户触发了返回键操作或者程序中调用了finish就清空activity实例关联的所有ViewModel数据并回调其'clear'方法。

总结

  • 使用ViewModel保存ui数据可以极大的提高我们的开发效率,不再担心因为配置变化引起的数据保存恢复问题,同时也可以避免一定程度发生的内存泄漏问题,而且可以很轻松的实现ActivityFragment之间数据共享,以及FragmentFragment数据交互的问题.

你可能感兴趣的:(jetpack解析ViewModel使用原理解析)