머리말
저장하고 활동 및 조각의 데이터를 관리하는 데 사용할 수있는 사용자 인터페이스, 관련 데이터를 관리하기위한 라이프 사이클의 뷰 모델에 대한 인식. 또한 조각 조각 등 사이의 통신을 처리하는 데 사용할 수 있습니다.
활동이나 조각이 관련된 뷰 모델, 너무 오래 같은 활동을 만들거나 조각이 활성화되면, 다음 뷰 모델은 활동 화면 회전의 경우에도 재건을 파괴하지 않습니다. 이 데이터를 어떻게 사용할 수 있도록 일시적으로 저장된다.
뷰 모델 주로 구 또는 개발자가 활성 뷰 모델 관측을 변경할 수 있고, 데이터 액티비티 / 단편은 필요한 유지하는데 사용된다 /의 단편 (이 LiveData 먹게한다).
UI의 ViewModel은 데이터를 관리하는 데 사용, 그것은보기, 활동 또는 조각 참조 (주의 메모리 누수 수) 개최하지 않습니다.
본 논문에서는 방법에 대한 진보적 인 접근 방식은 뷰 모델을 학습.
뷰 모델을 사용하여
도입의 ViewModel
//引入AndroidX吧,替换掉support包
implementation 'androidx.appcompat:appcompat:1.0.2'
def lifecycle_version = "2.0.0"
// ViewModel and LiveData
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
사용이 간편
사용자는 데이터 클래스를 정의합니다.
class User implements Serializable {
public int age;
public String name;
public User(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "User{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
그리고 오늘의 주인공 뷰 모델에 연결됩니다.
public class UserModel extends ViewModel {
public final MutableLiveData<User> mUserLiveData = new MutableLiveData<>();
public UserModel() {
//模拟从网络加载用户信息
mUserLiveData.postValue(new User(1, "name1"));
}
//模拟 进行一些数据骚操作
public void doSomething() {
User user = mUserLiveData.getValue();
if (user != null) {
user.age = 15;
user.name = "name15";
mUserLiveData.setValue(user);
}
}
}
이번에는 당신은 활동의 뷰 모델을 사용할 수 있습니다. 사실, 코드의 간단한 예, 다음은 뷰 모델을 사용할 수 있습니다.
//这些东西我是引入的androidx下面的
import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProviders;
public class MainActivity extends FragmentActivity {
private TextView mContentTv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContentTv = findViewById(R.id.tv_content);
//构建ViewModel实例
final UserModel userModel = ViewModelProviders.of(this).get(UserModel.class);
//让TextView观察ViewModel中数据的变化,并实时展示
userModel.mUserLiveData.observe(this, new Observer<User>() {
@Override
public void onChanged(User user) {
mContentTv.setText(user.toString());
}
});
findViewById(R.id.btn_test).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//点击按钮 更新User数据 观察TextView变化
userModel.doSomething();
}
});
}
}
사실이 작업이 다시 만들어, 그리고 때이 시간, 우리는, 우리가 휴대 전화 화면을 회전 할 수 있습니다 (사용자가 15 세의이됩니다) 버튼을 클릭 (즉에서 onCreate () 메서드를 다시 호출 있지만, 사실 뷰 모델이 다시 생성되지 않습니다 또는 뷰 모델 전),하지만 우리가 회전 할 때, 우리는 나이가 실제로 뷰 모델 마법 거짓말 인 텍스트 뷰 (15)에 표시되는 것을 발견했다. 뷰 모델은 수명을 언급 할 것, 그리고 그것은 단지 활동의 파괴 후,이 자폭 (그래서 뷰 모델이 활동 인용 아, 윌 메모리 누수를 보유하지 않는) 것입니다. 뷰 모델의 수명주기의 구글 공식 사진에 대한 다음의 인용문은 가장을 보여줍니다.
뷰 모델 마법 1 : 활동의 조각과 "소통"
조각이의 ViewModel을 인스턴스화 활동 들어오는 ViewModelProviders의 활동에 의존하기 때문에 뷰 모델로, 활동 및 조각이하는 뷰 모델을 공유 할 수 있습니다, 그것은 당신에게 줄 것이다 good've는 뷰 모델의 활동을 만들어,이 프래그먼트 수 있습니다 뷰 모델의 데이터에 편리하게 액세스 할 수 있습니다. 활동 userModel 수정 된 데이터는 조각의 수를 업데이트받을 수 있습니다.
뷰 모델 마법 2 : 조각 조각이와 "소통"
다음의 예제 (구글 공식 예)를 살펴 보자
public class SharedViewModel extends ViewModel {
private final MutableLiveData<Item> selected = new MutableLiveData<Item>();
public void select(Item item) {
selected.setValue(item);
}
public LiveData<Item> getSelected() {
return selected;
}
}
public class MasterFragment extends Fragment {
private SharedViewModel model;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(item -> {
model.select(item);
});
}
}
public class DetailFragment extends Fragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
model.getSelected().observe(this, { item ->
// Update the UI.
});
}
}
- 첫째, 뷰 모델을 정의 거기에 일부 데이터를 넣어.
(2) 다음 MasterFragment DetailFragment는 뷰 모델을 얻을 수 있고, 뷰 모델을 통해 간접적으로 통신에 대응하는 데이터를 얻을 수있는 뷰 모델 내부 얻는다. 너무 쉽게 ...
소스 해결의 ViewModel
우리는 아래의 코드에서 시작합니다.
final UserModel userModel = ViewModelProviders.of(this).get(UserModel.class);
우리는 ViewModelProviders.of (이) 따라 새로운 세계의 문을 엽니 다.
ViewModelProviders.of (이) 方法
/**
* 用于构建一个ViewModelProvider,当Activity是alive时它会保留所有的该Activity对应的ViewModels.
*/
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
return of(activity, null);
}
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity,
@Nullable Factory factory) {
//检查application是否为空,不为空则接收
Application application = checkApplication(activity);
if (factory == null) {
//构建一个ViewModelProvider.AndroidViewModelFactory
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
return new ViewModelProvider(activity.getViewModelStore(), factory);
}
() 함수의 내부 ViewModelProviders 우리가 ViewModelProvider를 구축 사실을 촉진하는 것입니다. 그리고 ViewModelProvider는 모습하고, 뷰 모델을 제공의 이름을 알고있다.
공장 ViewModelProvider 내부 인터페이스이며, 그 구현은 뷰 모델 클래스의 인스턴스를 생성하기 위해 사용된다. 그것은 단 하나의 방법은 뷰 모델을 만드는 것입니다있다.
/**
* Implementations of {@code Factory} interface are responsible to instantiate ViewModels.
*/
public interface Factory {
/**
* 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);
}
하나 NewInstanceFactory, 하나 AndroidViewModelFactory입니다 : 공장 구현 클래스는 두 가지가 있습니다.
NewInstanceFactory 소스
public static class NewInstanceFactory implements Factory {
@SuppressWarnings("ClassNewInstance")
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> 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);
}
}
}
NewInstanceFactory 인자가없는 생성자 클래스가 없을 것이라는 점을 인스턴스화하도록 설계 뷰 모델 컨텍스트없이 어떤 한 다음이 인스턴스를 위해서, newInstance ()하는 것입니다.
AndroidViewModelFactory 소스
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 extends ViewModel> T create(@NonNull Class<T> 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);
}
}
AndroidViewModelFactory 내부 클래스 생성자 매개 변수의 인스턴스를 설계하고, 컨텍스트와 뷰 모델이있을 수 있습니다.
newInstance와 (응용 프로그램) 인스턴스화를 통해 때문이다. 경우이 응용 프로그램 인스턴스와 인수가 있습니다.
매개 변수없이 응용 프로그램이 있다면, 여전히 인스턴스를 구축하기 위해서, newInstance () 메소드를 이동합니다.
의 ViewModel 응용 프로그램에 생성자가 제기 AndroidViewModelFactory, 당신은 APP 응용 프로그램은 글로벌이기 때문에, 다음 메모리 누수 문제가 없으며, 내부의 상황에 맞는 뷰 모델에서 얻을 수있는 완벽한 솔루션 컨텍스트 참조는 내부의 뷰 모델을 필요로하지만, 메모리 누수에 대한 걱정 문제.
여기에 우리가 ViewModelProviders.of (이) 방법은 그것을 분석하는 새로운 ViewModelProvider (activity.getViewModelStore (), 공장)의 마지막 문장에주의를 기울여야 계속 계속, 첫 번째 인수는 getViewModelStore의 활동 () 메소드를 (이 방법은 ViewModelStore을 반환 호출이 클래스 뷰 모델을 저장하는 데 사용됩니다, 우리는 활동은 여기 getViewModelStore () 메소드를 보면, androidx.fragment.app.FragmentActivity입니다) 아래에 설명합니다.
/**
* 获取这个Activity相关联的ViewModelStore
*/
@NonNull
@Override
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) {
//获取最近一次横竖屏切换时保存下来的数据
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
//没想到吧,Activity在横竖屏切换时悄悄保存了viewModelStore
//注意,这是FragmentActivity中的NonConfigurationInstances(其实Activity中还定义了一个NonConfigurationInstances,内容要比这个多一些,但是由于没有关系到它,这里就不提及了)
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
FragmentManagerNonConfig fragments;
}
() 복원 onRestoreInstanceState 호출 onSaveInstanceState ()를 전환 할 때 안드로이드 수평 및 수직 화면이 실행됩니다,하지만 두 가지 방법 onRetainNonConfigurationInstance ()와 getLastNonConfigurationInstance ()이 두 가지 방법이라고 안드로이드의 활동 클래스가 있습니다.
결코 만난 적이 두 가지의 구체적인 방법을보십시오.
/**
保留所有fragment的状态。你不能自己覆写它!如果要保留自己的状态,请使用onRetainCustomNonConfigurationInstance()
这个方法在FragmentActivity里面
*/
@Override
public final Object onRetainNonConfigurationInstance() {
Object custom = onRetainCustomNonConfigurationInstance();
FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();
if (fragments == null && mViewModelStore == null && custom == null) {
return null;
}
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = mViewModelStore;
nci.fragments = fragments;
return nci;
}
//这个方法在Activity里面,而mLastNonConfigurationInstances.activity实际就是就是上面方法中年的nci
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
이제 getLastNonConfigurationInstance ()를 호출 할 수있는 기회를 살펴 보자, 그리고
protected void onCreate(@Nullable Bundle savedInstanceState) {
......
super.onCreate(savedInstanceState);
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null && nc.viewModelStore != null && mViewModelStore == null) {
mViewModelStore = nc.viewModelStore;
}
......
}
나는 당시 활동으로 전환했다 그것을 기대하지 않았다 스크린 조용히 따라서 수평 및 수직 화면을 피하고, NonConfigurationInstances 예, 다시 전환 할 때, 해당하는 뷰 모델 인스턴스가 아 아직 재개 저장 수평 및 수직 화면 안에 배치 viewModelStore를 저장할 때 스위치 데이터 손실.
viewModelProvider.get (UserModel.class)
여기에 우리가 그것을 실현 있는지 확인하기 위해 GET () 메소드 ViewModelProvider 인의 절반을 구축하기 위해 뷰 모델 코드 구문에 와서, 실제로는 매우 간단합니다.
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);
}
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.
}
}
//无缓存 则重新通过mFactory构建
viewModel = mFactory.create(modelClass);
//缓存起来
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}
일반적인 아이디어가 복원되지 않으며, 캐시는 캐시가, 뷰 모델 캐시 키를 사용하는 것입니다. 공장 () 메서드의 상단을 구성 할 때 공장이 사용됩니다.
ViewModelStore
하나 개 이상의 장소는 위 실제로 뷰 모델 클래스 저장 보통이다, ViewModelStore를 사용합니다.
public class ViewModelStore {
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();
}
}
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();
}
}
ViewModelStore 일반 줄을 저장하는 전용의 HashMap이있다.
전화했을 때의 명확 보자 ().
ViewModel.onCleared () 자원화
뷰 모델은 인식의 라이프 사이클이기 때문에, 다음 뷰 모델은 청소해야 할 때?
우리는 () 메소드들의 OnDestroy의 FragmentActivity 와서는 청소 여기 것으로 나타났습니다.
/**
* Destroy all fragments.
*/
@Override
protected void onDestroy() {
super.onDestroy();
if (mViewModelStore != null && !isChangingConfigurations()) {
mViewModelStore.clear();
}
mFragments.dispatchDestroy();
}
뷰 모델을 봐
내 친구의 많은 요청해야 할 수도 있습니다, 뷰 모델은 결국 무엇인가?
public abstract class ViewModel {
/**
* 这个方法会在ViewModel即将被销毁时调用,可以在这里清理垃圾
*/
@SuppressWarnings("WeakerAccess")
protected void onCleared() {
}
}
사실, 아주 간단한, 추상 빈 방법에 ??? 내가 문질러 클래스, 그래서 지금, 원래의 ViewModel하지 영웅 ...
AndroidViewModel
뷰 모델이 AndroidViewModel있는 서브 클래스가있다. 그것은 내부의 상황에 맞는 뷰 모델을 용이하게하기 위해, 아무 응용 프로그램 속성을 포함하지 않는다.
public class AndroidViewModel extends ViewModel {
@SuppressLint("StaticFieldLeak")
private Application mApplication;
public AndroidViewModel(@NonNull Application application) {
mApplication = application;
}
/**
* Return the application.
*/
@SuppressWarnings("TypeParameterUnusedInFormals")
@NonNull
public <T extends Application> T getApplication() {
//noinspection unchecked
return (T) mApplication;
}
}
개요
뷰 모델의 소스는,별로 이해하기 쉽게, 주요 공식 FragmentActivity 기술, onRetainNonConfigurationInstance () 상태를 저장하고, getLastNonConfigurationInstance () 복구 기능을 제공합니다.
난 그냥 onSaveInstanceState ()와 onRestoreInstanceState (), 상승 자세를 알기도 전에 너무 원래 활동이 물건이 있습니다.