Jetpackの、ビューモデルの分解能を用いて原理およびコア・コンポーネント

序文

活性フラグメント内のデータを管理するために使用することができるユーザ・インターフェースに関連するデータを保存および管理することを意図しライフサイクルのビューモデル意識。また、フラグメント、フラグメントなどとの間の通信を処理するために使用することができます。

アクティビティまたはフラグメントは、関連ビューモデルを作成すると、あまりにも長い間活性または断片がアクティブであるように、その後、ビューモデルは、アクティビティ画面回転の場合であっても再構築、破壊されないであろう。それは、データを行うために使用できるように一時的に格納されます。

ビューモデルは、主に必要なデータアクティビティ/断片を取得または保持するために使用され、開発者は、(それがLiveDataで食べなければならない)で活性/断片中のビューモデル観測を変更することができます。

UIのViewModelはデータのみを管理するために使用される、それは(慎重にメモリリークすること)表示、活動またはフラグメント参照を保持することはできません。

本論文では、ViewModelには学習方法への漸進的なアプローチ。

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 + '\'' +
                '}';
    }
}

そして、今日のヒーローのViewModelにつながります。

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);
        }
    }

}

今度は、活動中のViewModelを使用することができます。実際には、コードの簡単な例、そしてあなたは、ビューモデルを使用することができます。

//这些东西我是引入的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()メソッドが再び呼び出されたが、実際にViewModelには、再作成されていませんまたはViewModelに前)が、我々が回転するとき、私たちは、年齢が実際にViewModelに魔法嘘であるTextViewの15、上に表示されることがわかりました。ViewModelには、ライフサイクルを言及しなければならない、そしてそれが唯一の活動を破壊した後、それは自己破壊(そうViewModelには、活動の引用ああ、意志メモリリークを保持させない)だろうです。ViewModelのライフサイクルのGoogleの公式写真について、以下の引用は、ほとんどを示しています。
Jetpackの、ビューモデルの分解能を用いて原理およびコア・コンポーネント

ビューモデル魔法1:活性断片を用いて、「通信します」

フラグメントはViewModelにをインスタンス化活動の着信ViewModelProvidersの活動に依存しているため、ViewModelにして、活動およびフラグメントは、ViewModelに共有することができ、それはあなたを与えるだろうgood'veはViewModelにの活動を作成し、このことは、フラグメントViewModel内のデータへのアクセスも便利。活動userModel変更されたデータは、フラグメントの数が更新されますすることができます。

ViewModelにマジカル2:フラグメントフラグメントは、と「通信します」

のは、例えば、(Googleの公式例)を見てみましょう

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.
        });
    }
}
  1. 最初のViewModelを定義し、それにいくつかのデータを置きます。

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、やっての名前を知っている表情は、ViewModelにを提供しています。

工場ViewModelProviderは内部インタフェースであり、その実装クラスは、ビューモデルのインスタンスを構築するために使用されます。それは唯一の方法は、ViewModelにを作成することですがあります。

/**
 * 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);
}

ファクトリ実装クラスには2を持っている:1はNewInstanceFactory、1 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は、引数なしのコンストラクタクラスがないことをインスタンス化するために設計された、とのViewModelコンテキストなしである、そしてそれがインスタンス化するの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()メソッドを行きます。

AndroidViewModelFactoryは、APPのアプリケーションがグローバルであるので、あなたは、内部のコンテキストのViewModelに取得することができ、その後、メモリリークの問題がない、完璧なソリューションのコンテキストの参照が内部にいくつかのViewModelを必要とViewModelにアプリケーションにコンストラクタによってもたらされたが、彼らはメモリリークの心配します問題。

ここでは、新しいViewModelProvider(activity.getViewModelStore()、工場)の最後の文に注意を払う、それを分析するために継続するViewModelProviders.of(本)メソッド続け、最初の引数はgetViewModelStore()メソッド(このメソッドはViewModelStoreを返し、このクラスの活動を呼び出しますが)私たちは以下について説明します、ViewModelにを格納するために使用され、ここでの活動は、この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を()、切り替え時のAndroid水平方向および垂直方向の画面が起動されているが、二つの方法onRetainNonConfigurationInstance()とgetLastNonConfigurationInstance()これらの2つの方法と呼ばれるAndroidのActivityクラスがあります。

会ったことのない2の具体的な方法を見てください。

/**
 保留所有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あるの半分を構築するViewModelにコードフレーズに来て、実際には非常に簡単です。

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;
}

一般的な考え方は、それが再構築されていない、キャッシュがキャッシュを持っている、ViewModelにキャッシュにキーを使用することです。工場()メソッドのトップを構築する際に工場が使用されています。

ViewModelStore

複数の場所は、上記のViewModelStoreを使用して、それは実際のViewModelクラスのセーブ普通です。

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()資源回収

ViewModelには、知覚のライフサイクルであるので、そのときViewModelには、それをきれいにする必要がありますか?

私たちは、onDestroyのFragmentActivity()メソッドは、それがクリーンアップするためにここにあることがわかった、来ました。

/**
 * Destroy all fragments.
 */
@Override
protected void onDestroy() {
    super.onDestroy();

    if (mViewModelStore != null && !isChangingConfigurations()) {
        mViewModelStore.clear();
    }

    mFragments.dispatchDestroy();
}

見てのViewModel

私の友人の多くは依頼する必要があり、ViewModelには、最終的には何ですか?

public abstract class ViewModel {
    /**
     * 这个方法会在ViewModel即将被销毁时调用,可以在这里清理垃圾
     */
    @SuppressWarnings("WeakerAccess")
    protected void onCleared() {
    }
}

実際には、空の方法で???私は、だから今、オリジナルのViewModelにないヒーローをこすり、非常に単純な、抽象クラス、...

AndroidViewModel

ViewModelにはAndroidViewModelあるサブクラスがあります。これは、内部のコンテキストのViewModelを容易にするためには、何もアプリケーションの属性が含まれていません。

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;
    }
}

概要

ViewModelにソースは、あまりない、理解しやすく、主な公式FragmentActivityは、技術、onRetainNonConfigurationInstance()状態を保存し、getLastNonConfigurationInstance()の回復を提供します。

私はちょうどonSaveInstanceState()とonRestoreInstanceState()、上昇姿勢を知る前に、その元の活性2のものがあります。

おすすめ

転載: blog.51cto.com/14332859/2401440