Android JetPack学习笔记之ViewModel

Android JetPack学习笔记之ViewModel

  • 前言
    • ViewModel
    • LiveData(简要说明)
    • ViewModel源码分析
    • ViewModel的使用

前言

Jetpack 是 Android 软件组件的集合,使您可以更轻松地开发出色的 Android 应用。这些组件可帮助您遵循最佳做法、让您摆脱编写样板代码的工作并简化复杂任务,以便您将精力集中放在所需的代码上。
最近有空边学习边把学习笔记做了。巩固知识,输入输出,强化学习,如有不对望指出,感谢
(以下是对官方代码文档的翻译和自己的部分见解)

如果引用JetPack库

    implementation 'android.arch.lifecycle:extensions:1.1.1'

ViewModel

ViewModel是一个负责用于准备和管理Activity或者Fragment的数据的类,它也负责对Application中其他的Activity和Fragment进行数据通信(比如:调用业务逻辑类,Fragment之间的数据同步,Activity和Fragment的数据同步。后面的列子会讲到)

  1. ViewModel也可以在Activity/Fragment的作用域中创建,生命周期跟随作用域,跟随着Activity的finish销毁而销毁。
  2. 换句话说,这也意味着,如果ViewModel的持有者(Activity/Fragment)因为配置的改变(例如:rotation)而销毁但是ViewModel没有被销毁,那么当持有者(Activity/Fragment)重新生成实例后将重新与已存在的这个ViewModel重新绑定。
  3. ViewModel的目的是用于为一个Activity/Fragment 获取和保存必要的信息,Activity/Fragment 应该能够观察ViewModel内容的变化。ViewModel通常和LiveData或者Android Data Binding来暴露这些信息。您还可以使用您喜欢的框架中的任何Observer类。
  4. ViewModel只负责管理UI的数据,它不应该直接访问你的视图层次和持有你的Activity/Fragment的引用。(例如:不把视图对象或者Activity/Fragment的Context作为对象传入到ViewModel中。避免内存泄漏)

LiveData(简要说明)

LiveData是一个用于保管数据的类。

ViewModel源码分析

启动 Android Studio 3.2 或以上版本,就能使用具有ViewModel的Fragment和Activity。
生成目录结构如下

Android JetPack学习笔记之ViewModel_第1张图片

系统自动生成了MainActivity,MainFragment,MainViewModel。

PS:MainViewModel继承于抽象类ViewModel,ViewModel中有个onCleared的方法,这个方法适用于ViewModel长时间没有被使用,这对于当ViewModel观察一些数据并且你需要清理ViewModel的订阅以防止泄漏很有用。

ViewModel的产生过程如图:

Created with Raphaël 2.2.0 ViewModelProviders.of() 获得ViewModelProvider对象调用get方法 ViewModelProvider.Factory创建ViewModel ViewModelStore是否有ViewModel 返回ViewModel实例 yes no

以下是生成源码分析

//MainFragment中将生成如下代码
@Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        mViewModel = ViewModelProviders.of(getActivity()).get(MainViewModel.class);
        // TODO: Use the ViewModel
        //我在这里创建了个LiveData并
        //添加了一个Observer用于观察User数据的变化,
        //并为TextView赋值
         mViewModel.getUserLiveData().observe(this, new Observer<User>() {
            @Override
            public void onChanged(@Nullable User user) {
                message.setText(user.name);
            }
        });
    }

ViewModel是通过ViewModelProviders.get方法获得的。而get方法是用于返回一个与当前Activity/Fragment绑定的已存在ViewModel或者创建一个新的绑定的ViewModel。那这里是如何判断是否已存在ViewModel或者创建一个新的ViewModel呢?源码如下:

	//1.ViewModelProviders.of()方法获得ViewModelProvider对象,
	//ViewModelProvider方法调用get如下
	@NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
    	//2.返回当前类的更具体的名字
        String canonicalName = modelClass.getCanonicalName();
        if (canonicalName == null) {
            throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
        }
        //3.把这个canonicalName作为key获取
        return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
    }
	@NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
    	//4.通过key获得之前已存在的ViewModel对象
        ViewModel viewModel = mViewModelStore.get(key);
        if (modelClass.isInstance(viewModel)) {
            //noinspection unchecked
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
        //5.如果ViewModel为空则通过ViewModelProvider.Factory创建一个新的ViewModel
        //
        viewModel = mFactory.create(modelClass);
        //6.并把viewModel存入ViewModelStore中.
        mViewModelStore.put(key, viewModel);
        //noinspection unchecked
        return (T) viewModel;
    }
//7.ViewModelProvider.Factory如何创建的ViewModel?
//AndroidViewModelFactory是ViewModelProvider的静态内部类
public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
		//部分代码已省略
		@NonNull
        @Override
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
            if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
                //noinspection TryWithIdenticalCatches
                try {
                	//8.通过反射机制创建实例/如果失败调用默认构造器创建实例
                    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);
        }

}
//9.ViewModel的存储类ViewModelStore
public class ViewModelStore {
	//10.整个Application的ViewModel实例都被存储于Map中。
    private final HashMap<String, ViewModel> mMap = new HashMap<>();

    final void put(String key, ViewModel viewModel) {
        ViewModel oldViewModel = mMap.put(key, viewModel);
        if (oldViewModel != null) {
            oldViewModel.onCleared();//清除ViewModel中长期不用的引用
        }
    }

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

    /**
     *  Clears internal storage and notifies ViewModels that they are no longer used.
     */
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.onCleared();
        }
        mMap.clear();
    }
}

ViewModel的使用

一个简单的例子。Activity中的一个按钮,点击以后,Fragment内部的文字实现变化。
在JetPack出现之前。我们大概会使用如下方法:Fragment暴露个方法给Activity中调用,EventBus,BoradCastRecevie等。但这都有缺陷,当然还有RxBus.现在有了JetPack,谷歌统一框架后,我们就能优雅的在Fragment和Activity之间共享数据了。
示例代码如下:

MainActivity.class

public class MainActivity extends AppCompatActivity {
    private MainViewModel mainViewModel;
    private Button send;
    int count=1;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_activity);
        mainViewModel = ViewModelProviders.of(this).get(MainViewModel.class);
        if (savedInstanceState == null) {
            getSupportFragmentManager().beginTransaction()
                    .replace(R.id.container, MainFragment.newInstance())
                    .commitNow();
        }
        send=findViewById(R.id.send);
        send.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                count++;
                mainViewModel.post("1001","Test"+count);
            }
        });
    }
}

MainFragment

public class MainFragment extends Fragment {
    private MainViewModel mViewModel;
    private TextView message;
    public static MainFragment newInstance() {return new MainFragment();}
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        View view=inflater.inflate(R.layout.main_fragment, container, false);
        message=view.findViewById(R.id.message);
        return view;
    }
    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        mViewModel = ViewModelProviders.of(getActivity()).get(MainViewModel.class);
        // TODO: Use the ViewModel
        mViewModel.getUserLiveData().observe(this, new Observer<User>() {
            @Override
            public void onChanged(@Nullable User user) {
                message.setText(user.name);
            }
        });}}

MainViewModel

public class MainViewModel extends ViewModel {
   private MutableLiveData<User> userLiveData;
    public LiveData<User> getUserLiveData() {
        if(userLiveData==null){
            userLiveData=new MutableLiveData<>();
        }
        return userLiveData;
    }
    public void post(String id, String name){
        userLiveData.setValue(new User(id,name));
    }
}

代码很优雅,逻辑很清晰。
这里没有直接使用LiveData而是MutableLiveData。JetPack中不允许我们直接操作LiveData而是使用MutableLiveData(可变数据)。

public class MutableLiveData<T> extends LiveData<T> {
    @Override
    public void postValue(T value) {
        super.postValue(value);
    }
    @Override
    public void setValue(T value) {
        super.setValue(value);
    }
}

MutableLiveData中postValue和setValue有什么区别呢?
其实源码中的注释描述的很清楚。
postValue是创建一个任务在主线程中用于设置你给的value。
而setValue是直接在主线程执行该方法。

     liveData.postValue("a");
     liveData.setValue("b");
     The value "b" would be set at first and later the main thread would override it with the value "a".

官方给的例子很明显,同时调用后,“a"参数将覆盖"b”。

下次我们仔细来看看LiveData的源码吧。

你可能感兴趣的:(Android)