前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Andorid-JetPack-ViewModel组件

Andorid-JetPack-ViewModel组件

原创
作者头像
花落花相惜
修改2021-11-24 13:46:38
1.7K0
修改2021-11-24 13:46:38
举报
文章被收录于专栏:花落的技术专栏
1.什么是ViewModel
  • 具备宿主生命周期感知能力的数据存储组件
  • ViewModel保存的数据,在页面因 配置变更导致页面销毁 重建之后依然是存在的

配置变更:横竖屏切换、分辨率调整、权限变更、系统字体样式变更...

ViewModel是如何做到页面销毁了还能恢复期数据呢?

其实就是ViewModel实例被保存了下来,页面重建之后获取的ViewModel是同一个

2.基本用法

常规用法:存储的数据,仅仅只能当页面因为配置变更导致的销毁再重建时可复用,复用的是ViewModel的实例对象整体

代码语言:txt
复制
public class MyGirlViewModel extends ViewModel {
代码语言:txt
复制
    //定义一个对象,相当于一个用来存放数据的仓库
代码语言:txt
复制
    private static MutableLiveData<List<Girl>> mLiveData;
代码语言:txt
复制
    /**
代码语言:txt
复制
     * 用于获取数据
     */
    public LiveData<List<Girl>> getDataList() {
        if (mLiveData == null) {
            mLiveData = new MutableLiveData<>();
            loadData();
        }
        return mLiveData;
    }
代码语言:txt
复制
    private void loadData() {
代码语言:txt
复制
        List<Girl> data = new ArrayList<>();
代码语言:txt
复制
        data.add(new Girl(R.drawable.f1, "一星", "****"));
代码语言:txt
复制
        data.add(new Girl(R.drawable.f2, "一星", "****"));
代码语言:txt
复制
        data.add(new Girl(R.drawable.f3, "一星", "****"));
代码语言:txt
复制
        data.add(new Girl(R.drawable.f4, "一星", "****"));
代码语言:txt
复制
        data.add(new Girl(R.drawable.f5, "一星", "****"));
代码语言:txt
复制
        data.add(new Girl(R.drawable.f6, "一星", "****"));
代码语言:txt
复制
        data.add(new Girl(R.drawable.f7, "一星", "****"));
代码语言:txt
复制
        data.add(new Girl(R.drawable.f8, "一星", "****"));
代码语言:txt
复制
        data.add(new Girl(R.drawable.f9, "一星", "****"));
代码语言:txt
复制
        data.add(new Girl(R.drawable.f10, "一星", "****"));
代码语言:txt
复制
        //把这些数据存到仓库里
代码语言:txt
复制
        mLiveData.setValue(data);
代码语言:txt
复制
    }
代码语言:txt
复制
    //提供一个方法来改变数据
代码语言:txt
复制
    public void changeValue(int position, String value) {
代码语言:txt
复制
        //把数据取出来,然后更改其中某个条目的值,在把数据塞会仓库里
代码语言:txt
复制
        List<Girl> list = mLiveData.getValue();
代码语言:txt
复制
        Girl girl = list.get(position);
代码语言:txt
复制
        girl.setLike(value);
代码语言:txt
复制
        mLiveData.setValue(list);
代码语言:txt
复制
    }
代码语言:txt
复制
}
代码语言:txt
复制
public class MainActivity extends AppCompatActivity {
代码语言:txt
复制
    private ListView mListView;
代码语言:txt
复制
    @Override
代码语言:txt
复制
    protected void onCreate(Bundle savedInstanceState) {
代码语言:txt
复制
        super.onCreate(savedInstanceState);
代码语言:txt
复制
        setContentView(R.layout.activity_main);
代码语言:txt
复制
        mListView = findViewById(R.id.listview);
代码语言:txt
复制
        initListView();
代码语言:txt
复制
    }
代码语言:txt
复制
    private void initListView() {
代码语言:txt
复制
        MyGirlViewModel myGirlViewModel = new ViewModelProvider(this).get(MyGirlViewModel.class);
代码语言:txt
复制
        LiveData<List<Girl>> liveData = myGirlViewModel.getDataList();
代码语言:txt
复制
        //监听数据的改变,数据改变就会调用onChanged方法
代码语言:txt
复制
        liveData.observe(this, new Observer<List<Girl>>() {
代码语言:txt
复制
            /**
代码语言:txt
复制
             * 当我们的数据发生变化的时候,我们可以在这个onChanged中进行处理
             */
            @Override
            public void onChanged(List<Girl> girls) {
                Log.i("yd","onChanged刷新一次");
                mListView.setAdapter(new GirlAdapter(MainActivity.this, girls));
            }
        });
代码语言:txt
复制
        mListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
代码语言:txt
复制
            @Override
代码语言:txt
复制
            public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
代码语言:txt
复制
                myGirlViewModel.changeValue(position,"哈哈哈哈哈哈哈哈哈");
代码语言:txt
复制
                return false;
代码语言:txt
复制
            }
代码语言:txt
复制
        });
代码语言:txt
复制
        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
代码语言:txt
复制
            @Override
代码语言:txt
复制
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
代码语言:txt
复制
                startActivity(new Intent(MainActivity.this,SecondActivity.class));
代码语言:txt
复制
            }
代码语言:txt
复制
        });
代码语言:txt
复制
    }
代码语言:txt
复制
}

跨页面数据共享

代码语言:txt
复制
public class SecondActivity extends AppCompatActivity {
代码语言:txt
复制
    @Override
代码语言:txt
复制
    protected void onCreate(Bundle savedInstanceState) {
代码语言:txt
复制
        super.onCreate(savedInstanceState);
代码语言:txt
复制
        setContentView(R.layout.activity_second);
代码语言:txt
复制
    }
代码语言:txt
复制
    public void sendData(View view) {
代码语言:txt
复制
        //一定要在主线程中调用
代码语言:txt
复制
        MyGirlViewModel myGirlViewModel = new ViewModelProvider(this).get(MyGirlViewModel.class);
代码语言:txt
复制
        myGirlViewModel.changeValue(0,"我要改变的是第1条数据呀呀呀呀呀呀呀");
代码语言:txt
复制
        //必须是MainActivity 可见才能收到信息,如果不可见哪怕发送了信息也是收不到的
代码语言:txt
复制
        finish();
代码语言:txt
复制
    }
代码语言:txt
复制
}
3.配置变更ViewModel复用实现原理

准确点来说,应该是ViewModel如何做到在宿主销毁了,还能继续存在.以至于页面恢复重建后,还能接着复用

肯定是前后获取到的是同一个ViewModel实例对象

我们先来看下获取ViewModel实例的过程

代码语言:txt
复制
MyGirlViewModel myGirlViewModel = new ViewModelProvider(MainActivity.this).get(MyGirlViewModel.class);

ViewModelProvider本质是从传递进去的ViewModelStore来获取实例,如果没有,则利用factory去创建

3.1 ViewModelProvider的创建

代码语言:txt
复制
public class ViewModelProvider {
代码语言:txt
复制
    private final Factory mFactory;
代码语言:txt
复制
    private final ViewModelStore mViewModelStore;
代码语言:txt
复制
    public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
代码语言:txt
复制
        this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
代码语言:txt
复制
                ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
代码语言:txt
复制
                : NewInstanceFactory.getInstance());
代码语言:txt
复制
    }
代码语言:txt
复制
    //把ViewModelStore 和 Factory 都保持到成员变量中
代码语言:txt
复制
    public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
代码语言:txt
复制
        mFactory = factory;
代码语言:txt
复制
        mViewModelStore = store;
代码语言:txt
复制
    }
代码语言:txt
复制
}

在创建ViewModelProvider的时候需要传参数ViewModelStoreOwner,

我们的MainActivity是继承自顶层ComponentActivity然后实现了ViewModelStoreOwner接口的,所以我们可以直接传MainActivity,可以发现创建ViewModelProvider的时候会把ViewModelStore

Factory 都保存到成员变量中,那我们来看下ViewModelStore

代码语言:txt
复制
public class ViewModelStore {
代码语言:txt
复制
    private final HashMap<String, ViewModel> mMap = new HashMap<>();
代码语言:txt
复制
    final void put(String key, ViewModel viewModel) {
代码语言:txt
复制
        ViewModel oldViewModel = mMap.put(key, viewModel);
代码语言:txt
复制
        if (oldViewModel != null) {
代码语言:txt
复制
            oldViewModel.onCleared();
代码语言:txt
复制
        }
代码语言:txt
复制
    }
代码语言:txt
复制
    final ViewModel get(String key) {
代码语言:txt
复制
        return mMap.get(key);
代码语言:txt
复制
    }
代码语言:txt
复制
    Set<String> keys() {
代码语言:txt
复制
        return new HashSet<>(mMap.keySet());
代码语言:txt
复制
    }
代码语言:txt
复制
    /**
代码语言:txt
复制
     *  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,用来存储ViewModel

然后我们来看下 Factory ,该工厂就是创造ViewModel用的

代码语言:txt
复制
    public interface Factory {
代码语言:txt
复制
        /**
代码语言:txt
复制
         * Creates a new instance of the given {@code Class}.
         * <p>
         *
         * @param modelClass a {@code Class} whose instance is requested
         * @param <T>        The type parameter for the ViewModel.
         * @return a newly created ViewModel
         */
        @NonNull
        <T extends ViewModel> T create(@NonNull Class<T> modelClass);
    }

就是说,当我们来获取ViewModel实例的时候,如果在ViewModelStore中获取不到,就会用Factory去创造一个实例,

ok,到这里ViewModelProvider对象已经创建完毕,接下来看ViewModelProviderget()方法

3.2 ViewModelProvider调用get()方法

代码语言:txt
复制
public class ViewModelProvider {
代码语言:txt
复制
    private static final String DEFAULT_KEY = "androidx.lifecycle.ViewModelProvider.DefaultKey";
代码语言:txt
复制
    @NonNull
代码语言:txt
复制
    @MainThread
代码语言:txt
复制
    public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
代码语言:txt
复制
        String canonicalName = modelClass.getCanonicalName();
代码语言:txt
复制
        if (canonicalName == null) {
代码语言:txt
复制
            throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
代码语言:txt
复制
        }
代码语言:txt
复制
        return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
代码语言:txt
复制
    }
代码语言:txt
复制
    @NonNull
代码语言:txt
复制
    @MainThread
代码语言:txt
复制
    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
代码语言:txt
复制
         //1.从mViewModelStore中根据key去找ViewModel
代码语言:txt
复制
        ViewModel viewModel = mViewModelStore.get(key);
代码语言:txt
复制
         //2.判断viewModel该实例是不是我们传入的modelClass类型的
代码语言:txt
复制
        if (modelClass.isInstance(viewModel)) {
代码语言:txt
复制
            if (mFactory instanceof OnRequeryFactory) {
代码语言:txt
复制
                ((OnRequeryFactory) mFactory).onRequery(viewModel);
代码语言:txt
复制
            }
代码语言:txt
复制
            return (T) viewModel;
代码语言:txt
复制
        } else {
代码语言:txt
复制
            //noinspection StatementWithEmptyBody
代码语言:txt
复制
            if (viewModel != null) {
代码语言:txt
复制
                // TODO: log a warning.
代码语言:txt
复制
            }
代码语言:txt
复制
        }
代码语言:txt
复制
        //3.如果不是就通过工厂来创建一个viewModel实例
代码语言:txt
复制
        if (mFactory instanceof KeyedFactory) {
代码语言:txt
复制
            viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
代码语言:txt
复制
        } else {
代码语言:txt
复制
            viewModel = (mFactory).create(modelClass);
代码语言:txt
复制
        }
代码语言:txt
复制
        //4.创建完成之后存到mViewModelStore 的 map中
代码语言:txt
复制
        mViewModelStore.put(key, viewModel);
代码语言:txt
复制
        return (T) viewModel;
代码语言:txt
复制
    }
代码语言:txt
复制
}

当调用get()方法的时候,我们只是传递了一个ViewModel.class对象,就会把我们ViewModelclassName名字拼接上DEFAULT_KEY作为Key,

这就是在ViewModelStore中存储的Key,Value是我们的ViewModel.class对象

我们知道了ViewModel是从ViewModelStore中获取的,那既然想做到ViewModel实例的复用,那就是说ViewModelStore也要复用,关键点就在这里

3.3ViewModelStore是在哪里获取的?

通过看ViewModelProvider的构造方法,我们可以发现是在owner.getViewModelStore()中获取的

代码语言:txt
复制
    public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
代码语言:txt
复制
        this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
代码语言:txt
复制
                ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
代码语言:txt
复制
                : NewInstanceFactory.getInstance());
代码语言:txt
复制
    }

追进去发现,owner.getViewModelStore()这行代码是在ComponentActivity中实现的,

也就是说ViewModelStore是在ComponentActivity中获取的

代码语言:txt
复制
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
代码语言:txt
复制
        LifecycleOwner,
代码语言:txt
复制
        ViewModelStoreOwner,
代码语言:txt
复制
        SavedStateRegistryOwner,
代码语言:txt
复制
        OnBackPressedDispatcherOwner {
代码语言:txt
复制
    //保存用来配置变更后还想保存的数据
代码语言:txt
复制
    static final class NonConfigurationInstances {
代码语言:txt
复制
        Object custom;
代码语言:txt
复制
        ViewModelStore viewModelStore;
代码语言:txt
复制
    }
代码语言:txt
复制
    @NonNull
代码语言:txt
复制
    @Override
代码语言:txt
复制
    public ViewModelStore getViewModelStore() {
代码语言:txt
复制
        if (getApplication() == null) {
代码语言:txt
复制
            throw new IllegalStateException("Your activity is not yet attached to the "
代码语言:txt
复制
                    + "Application instance. You can't request ViewModel before onCreate call.");
代码语言:txt
复制
        }
代码语言:txt
复制
        if (mViewModelStore == null) {
代码语言:txt
复制
            //1.viewModelStore是根据getLastNonConfigurationInstance()获取的
代码语言:txt
复制
            NonConfigurationInstances nc =(NonConfigurationInstances) getLastNonConfigurationInstance();
代码语言:txt
复制
            if (nc != null) {
代码语言:txt
复制
              //2.从NonConfigurationInstances中把viewModelStore提出来变成成员变量
代码语言:txt
复制
                mViewModelStore = nc.viewModelStore;
代码语言:txt
复制
            }
代码语言:txt
复制
            //3.如果mViewModelStore为空,则就创建一个
代码语言:txt
复制
            if (mViewModelStore == null) {
代码语言:txt
复制
                mViewModelStore = new ViewModelStore();
代码语言:txt
复制
            }
代码语言:txt
复制
        }
代码语言:txt
复制
        return mViewModelStore;
代码语言:txt
复制
    }
代码语言:txt
复制
}

ComponentActivity中的getViewModelStore方法中我们发现

  • 1.viewModelStore是根据getLastNonConfigurationInstance()获取的
  • 2.从NonConfigurationInstances中把viewModelStore提出来变成成员变量
  • 3.如果mViewModelStore为空,则就创建一个

那这个只是ViewModelStore的获取,那我们想知道ViewModelStore是在什么地方存的呢?

3.3ViewModelStore是在哪里存储的?

代码语言:txt
复制
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
代码语言:txt
复制
        LifecycleOwner,
代码语言:txt
复制
        ViewModelStoreOwner,
代码语言:txt
复制
        SavedStateRegistryOwner,
代码语言:txt
复制
        OnBackPressedDispatcherOwner {
代码语言:txt
复制
    //保存用来配置变更后还想保存的数据
代码语言:txt
复制
    static final class NonConfigurationInstances {
代码语言:txt
复制
        Object custom;
代码语言:txt
复制
        ViewModelStore viewModelStore;
代码语言:txt
复制
    }
代码语言:txt
复制
    //什么时候被触发呢?
代码语言:txt
复制
    //是在`Activity`的`retainNonConfigurationInstances()`方法调用的
代码语言:txt
复制
    @Override
代码语言:txt
复制
    @Nullable
代码语言:txt
复制
    public final Object onRetainNonConfigurationInstance() {
代码语言:txt
复制
       //如果我们想在activity保存一下数据,就是说因配置变更页面被销毁了,重建的时候继续复用
代码语言:txt
复制
       //我们就可以重写onRetainCustomNonConfigurationInstance()这个方法,然后获取的时候可以用getLastNonConfigurationInstance()
代码语言:txt
复制
       //不过这种方式已经被废弃掉了,了解即可,现在都推荐使用ViewModel
代码语言:txt
复制
        Object custom = onRetainCustomNonConfigurationInstance();
代码语言:txt
复制
        ViewModelStore viewModelStore = mViewModelStore;
代码语言:txt
复制
        if (viewModelStore == null) {
代码语言:txt
复制
            NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance();
代码语言:txt
复制
            if (nc != null) {
代码语言:txt
复制
                viewModelStore = nc.viewModelStore;
代码语言:txt
复制
            }
代码语言:txt
复制
        }
代码语言:txt
复制
        //如果viewModelStore 为空则就直接 return 空了
代码语言:txt
复制
        if (viewModelStore == null && custom == null) {
代码语言:txt
复制
            return null;
代码语言:txt
复制
        }
代码语言:txt
复制
        //如果viewModelStore 不为空,就创建NonConfigurationInstances对象并把viewModelStore存进去
代码语言:txt
复制
        NonConfigurationInstances nci = new NonConfigurationInstances();
代码语言:txt
复制
        nci.custom = custom;
代码语言:txt
复制
        nci.viewModelStore = viewModelStore;
代码语言:txt
复制
        return nci;
代码语言:txt
复制
    }
代码语言:txt
复制
}

ViewModelStore是在ComponentActivityonRetainNonConfigurationInstance()方法中存储的.

该方法中的onRetainCustomNonConfigurationInstance()这行代码,

如果我们想在activity保存一下数据,就是说因配置变更页面被销毁了,重建的时候继续复用

我们就可以重写onRetainCustomNonConfigurationInstance()这个方法,然后获取的时候可以用getLastNonConfigurationInstance()

不过这种方式已经被废弃掉了,了解即可,现在都推荐使用ViewModel

小总结一下:

**我们已经知道了ViewModelStore是在onRetainNonConfigurationInstance()方法中存储的,

但是我们要知道onRetainNonConfigurationInstance()在什么情况下才会被调用,

只有这个方法调用了,我们的ViewModelStore才能存到NonConfigurationInstances中,这才能实现复用**

3.4onRetainNonConfigurationInstance()方法又是在哪被调用的呢?

是在ComponentActivity的父类ActivityretainNonConfigurationInstances()方法调用的

代码语言:txt
复制
public class Activity extends ContextThemeWrapper
代码语言:txt
复制
        implements LayoutInflater.Factory2,
代码语言:txt
复制
        Window.Callback, KeyEvent.Callback,
代码语言:txt
复制
        OnCreateContextMenuListener, ComponentCallbacks2,
代码语言:txt
复制
        Window.OnWindowDismissedCallback,
代码语言:txt
复制
        AutofillManager.AutofillClient, ContentCaptureManager.ContentCaptureClient {
代码语言:txt
复制
    //该方法又是在哪里调用的呢?是在ActivityTread中调用的
代码语言:txt
复制
    NonConfigurationInstances retainNonConfigurationInstances() {
代码语言:txt
复制
        //调用该方法
代码语言:txt
复制
        Object activity = onRetainNonConfigurationInstance();
代码语言:txt
复制
        ......
代码语言:txt
复制
        NonConfigurationInstances nci = new NonConfigurationInstances();
代码语言:txt
复制
        nci.activity = activity;
代码语言:txt
复制
        nci.children = children;
代码语言:txt
复制
        nci.fragments = fragments;
代码语言:txt
复制
        nci.loaders = loaders;
代码语言:txt
复制
        if (mVoiceInteractor != null) {
代码语言:txt
复制
            mVoiceInteractor.retainInstance();
代码语言:txt
复制
            nci.voiceInteractor = mVoiceInteractor;
代码语言:txt
复制
        }
代码语言:txt
复制
        return nci;
代码语言:txt
复制
    }
代码语言:txt
复制
    public Object onRetainNonConfigurationInstance() {
代码语言:txt
复制
        return null;
代码语言:txt
复制
    }
代码语言:txt
复制
}

retainNonConfigurationInstances()方法又是在哪里调用的呢?答案是在ActivityThread中被调用的,

**3.4

因配置变更重新启动一个activity,会执行ActivityThreadhandleRelaunchActivity()方法,继而调用retainNonConfigurationInstances()**

我们都知道程序启动的时候首先要调用ActivityThreadmain方法,

如果我们正常启动一个activity的时候会调用ActivityThreadhandleLaunchActivity()方法,

如果是因为配置变更重新启动一个activity的时候,是走的handleRelaunchActivity()方法,

这里先告诉大家,retainNonConfigurationInstances()方法是在handleRelaunchActivity()中的handleDestroyActivity()方法中的performDestroyActivity()方法中调用的,具体源码如下

代码语言:txt
复制
public final class ActivityThread extends ClientTransactionHandler {
代码语言:txt
复制
    final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
代码语言:txt
复制
    public static final class ActivityClientRecord {
代码语言:txt
复制
        //这个字段存储的就是因配置变更导致的被销毁的那个activity存留下来的数据  
代码语言:txt
复制
       Activity.NonConfigurationInstances lastNonConfigurationInstances;
代码语言:txt
复制
    }
代码语言:txt
复制
    //正常启动一个activity会走这个方法
代码语言:txt
复制
    @Override
代码语言:txt
复制
    public Activity handleLaunchActivity(ActivityClientRecord r,PendingTransactionActions pendingActions, Intent customIntent) {
代码语言:txt
复制
      ..........
代码语言:txt
复制
    }
代码语言:txt
复制
    //1.因为配置变更重新启动一个`activity`的时候会调用这个方法
代码语言:txt
复制
    @Override
代码语言:txt
复制
    public void handleRelaunchActivity(ActivityClientRecord tmp,PendingTransactionActions pendingActions) {
代码语言:txt
复制
      .....
代码语言:txt
复制
        //mActivities这个集合存储的是当前app已经打开的所有activity
代码语言:txt
复制
        ActivityClientRecord r = mActivities.get(tmp.token);
代码语言:txt
复制
        handleRelaunchActivityInner(r, configChanges, tmp.pendingResults, tmp.pendingIntents,
代码语言:txt
复制
                pendingActions, tmp.startsNotResumed, tmp.overrideConfig, "handleRelaunchActivity");
代码语言:txt
复制
      .....
代码语言:txt
复制
   }
代码语言:txt
复制
    //2.因为配置变更重新启动一个`activity`的时候会调用这个方法
代码语言:txt
复制
    private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges,
代码语言:txt
复制
            List<ResultInfo> pendingResults, List<ReferrerIntent> pendingIntents,
代码语言:txt
复制
            PendingTransactionActions pendingActions, boolean startsNotResumed,
代码语言:txt
复制
            Configuration overrideConfig, String reason) {
代码语言:txt
复制
        final Intent customIntent = r.activity.mIntent;
代码语言:txt
复制
        //重新创建一个activity之前要把发生配置变更的activity销毁
代码语言:txt
复制
        if (!r.paused) {
代码语言:txt
复制
            performPauseActivity(r, false, reason, null /* pendingActions */);
代码语言:txt
复制
        }
代码语言:txt
复制
        if (!r.stopped) {
代码语言:txt
复制
            callActivityOnStop(r, true /* saveState */, reason);
代码语言:txt
复制
        }
代码语言:txt
复制
        //该方法的第4个参数getNonConfigInstance,这里面传的是true
代码语言:txt
复制
        //经过这个方法后,ActivityClientRecord 这个r对象就包含了那个被销毁的activity所存留下来的对象
代码语言:txt
复制
        handleDestroyActivity(r.token, false, configChanges, true, reason);
代码语言:txt
复制
        ......
代码语言:txt
复制
        //重新打开一个新的activity
代码语言:txt
复制
        handleLaunchActivity(r, pendingActions, customIntent);
代码语言:txt
复制
    }
代码语言:txt
复制
   //重要方法,经过这个方法后,ActivityClientRecord 这个r对象就包含了那个被销毁的activity所存留下来的对象
代码语言:txt
复制
    @Override
代码语言:txt
复制
    public void handleDestroyActivity(IBinder token, boolean finishing, int configChanges,boolean getNonConfigInstance, String reason) {
代码语言:txt
复制
        ActivityClientRecord r = performDestroyActivity(token, finishing,configChanges, getNonConfigInstance, reason);
代码语言:txt
复制
    }
代码语言:txt
复制
    //该方法是真正执行Activity的destroy方法的
代码语言:txt
复制
    ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,int configChanges, boolean getNonConfigInstance, String reason) {
代码语言:txt
复制
        ActivityClientRecord r = mActivities.get(token);
代码语言:txt
复制
             if (getNonConfigInstance) {//此处为true
代码语言:txt
复制
               //该行代码就会调用activity的retainNonConfigurationInstances方法,
代码语言:txt
复制
               //从而把Activity受到配置变更而不想丢失的数据给保存起来,那我们的viewModelStore对象也就被保存起来了
代码语言:txt
复制
               r.lastNonConfigurationInstances= r.activity.retainNonConfigurationInstances();
代码语言:txt
复制
            }
代码语言:txt
复制
        return r;
代码语言:txt
复制
    }
代码语言:txt
复制
}
  • 1.当一个activity重建的时候就会执行ActivityThread中的handleRelaunchActivity(),然后会经过一系列调用,undefinedhandleRelaunchActivity()--->handleDestroyActivity()--->performDestroyActivity()--->r.activity.retainNonConfigurationInstances();
  • 2.retainNonConfigurationInstances()方法,作用就是把Activity受到配置变更而不想丢失的数据给保存起来,那我们的viewModelStore对象也就被保存起来了,数据都会存在ActivityClientRecord中,其中有个字段Activity.NonConfigurationInstances lastNonConfigurationInstances,这个字段存储的就是因配置变更导致的被销毁的那个activity存留下来的数据
  • 3.当handleDestroyActivity()这个方法被调用完,我们的ActivityClientRecord 这个r对象就包含了那个被销毁的activity所存留下来的数据,最后会调用handleLaunchActivity()重新打开一个activity,接下来handleLaunchActivity()中就调用performLaunchActivity()方法

3.5handleLaunchActivity方法创建新的Activity并启动

代码语言:txt
复制
public final class ActivityThread extends ClientTransactionHandler {
代码语言:txt
复制
     //正常启动一个activity会走这个方法
代码语言:txt
复制
    @Override
代码语言:txt
复制
    public Activity handleLaunchActivity(ActivityClientRecord r,PendingTransactionActions pendingActions, Intent customIntent) {
代码语言:txt
复制
      final Activity a = performLaunchActivity(r, customIntent);
代码语言:txt
复制
    }
代码语言:txt
复制
     private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
代码语言:txt
复制
        Activity activity = null;
代码语言:txt
复制
        try {
代码语言:txt
复制
            java.lang.ClassLoader cl = appContext.getClassLoader();
代码语言:txt
复制
            //1.新建一个activity
代码语言:txt
复制
            activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
代码语言:txt
复制
            StrictMode.incrementExpectedActivityCount(activity.getClass());
代码语言:txt
复制
            r.intent.setExtrasClassLoader(cl);
代码语言:txt
复制
            r.intent.prepareToEnterProcess();
代码语言:txt
复制
            if (r.state != null) {
代码语言:txt
复制
                r.state.setClassLoader(cl);
代码语言:txt
复制
            }
代码语言:txt
复制
        } catch (Exception e) {
代码语言:txt
复制
        }
代码语言:txt
复制
       //2.执行attach方法把r.lastNonConfigurationInstances传进去了
代码语言:txt
复制
       activity.attach(appContext, this, getInstrumentation(), r.token,r.ident, app, r.intent, r.activityInfo, title, r.parent,
代码语言:txt
复制
                        r.embeddedID, r.lastNonConfigurationInstances, config,r.referrer, r.voiceInteractor, window, r.configCallback,r.assistToken);
代码语言:txt
复制
      }
代码语言:txt
复制
}
  • 1.不难发现,在performLaunchActivity()方法中会通过newActivity()的形式创建activity
  • 2.执行attach方法把r.lastNonConfigurationInstances传进去了

3.6Activity最终把lastNonConfigurationInstances对象保存了起来

代码语言:txt
复制
public class Activity{
代码语言:txt
复制
   @UnsupportedAppUsage
代码语言:txt
复制
   NonConfigurationInstances mLastNonConfigurationInstances;
代码语言:txt
复制
   @UnsupportedAppUsage
代码语言:txt
复制
    final void attach(Context context, ActivityThread aThread,
代码语言:txt
复制
            Instrumentation instr, IBinder token, int ident,
代码语言:txt
复制
            Application application, Intent intent, ActivityInfo info,
代码语言:txt
复制
            CharSequence title, Activity parent, String id,
代码语言:txt
复制
            NonConfigurationInstances lastNonConfigurationInstances,
代码语言:txt
复制
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
代码语言:txt
复制
            Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
代码语言:txt
复制
        //保存了起来
代码语言:txt
复制
        mLastNonConfigurationInstances = lastNonConfigurationInstances;
代码语言:txt
复制
    }
代码语言:txt
复制
    @Nullable
代码语言:txt
复制
    public Object getLastNonConfigurationInstance() {
代码语言:txt
复制
        return mLastNonConfigurationInstances != null ? mLastNonConfigurationInstances.activity : null;
代码语言:txt
复制
    }
代码语言:txt
复制
}

所以在getLastNonConfigurationInstance()方法中能拿到mLastNonConfigurationInstances对象,那我们就能拿到我们的ViewModelStore,从而完成我们实例对象的复用

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
作者已关闭评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.什么是ViewModel
  • 2.基本用法
  • 3.配置变更ViewModel复用实现原理
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档