ルームとは何ですか?
Androidのアーキテクチャのコンポーネント、および私たちの部屋まさにこのシナリオの二つのモジュールの1:GoogleのAndroidは、GoogleのI / O 2017で開発者は、設計のAndroidのアーキテクチャを支援するソリューションのセットをリリースし、開発者は建築設計上の問題を解決するのに役立ちます。
- 定義:データベースソリューション
- 作曲:データベース、エンティティ、DAO
なぜ、この記事では、Googleのパラダイムと呼ばれていますか?
学び、理解するために開発を容易にするために、GoogleはGitHubの上のオープンソースコードのAndroidのアーキテクチャのコンポーネントのシリーズをアップロード:googlesamples / Androidのアーキテクチャー・コンポーネントこの記事は、この例の最初の部分によって解決されます。BasicRxJavaSampleルームの使用を分析すること。
この記事のコードとコードの以降の記事については、私は見物人に私のGitHubの歓迎にアップロードした、スター
を参照してください- > FishInWater-1999 / ArchitectureComponentsStudy
始める前に
私たちは部屋を習得しなければならないのはなぜ
私たちは、直接、伝統的な方法に比べて、直接使用する場合はJava
、コードのSQLite
動作を、常に私たちのためにプログラマのためのほとんどは夢、この愚かな手順は、単に拷問の一種である、手で書かれたコードの繰り返しの多くを必要とします。だから、Room
それがされて入ってきました
- それは退屈な退屈なコードをパッケージ化します注釈プロセッサの形態である、我々は唯一の簡単な注釈を追加する必要がある、あなたは、複雑な一連の機能を完了することができます!
最初に、私たちは理解する必要がある
Room
の基本コンポーネントを
私たちは、主にデータベースによって、利用ルームの前に言った、エンティティ、DAO 3つの主要コンポーネントは、これらの3つは一部であり、それは何ですか?
- データベース:必要な操作するルーム管理およびカスタムデータベーステーブルのデータベースを作成します。
要件:
1. 必须是abstract类而且的extends RoomDatabase。
2. 必须在类头的注释中包含与数据库关联的实体列表(Entity对应的类)。
3. 包含一个具有0个参数的抽象方法,并返回用@Dao注解的类。
使用します。
シングルトンモードによって達成、データベース・インスタンスを取得する静的のgetInstance(...)メソッドを渡すことができます。
public static UsersDatabase getInstance(Context context)
エンティティ:データベースのようなエンティティクラスのテーブル:
@Entity(tableName = "users")
public class User {...}
データベースにアクセスするための具体的なインターフェイスのメソッド:DAO
@Dao
public interface UserDao {...}
#BasicRxJavaSampleソース解析
誰もが解決されるための最後の統合の道を解明、上向きに、レイヤーを基本クラスから開始します。ソースコードが解析されているので、私はかかります。だから今、始めましょう。
表が設定さ
部屋は基本的な操作は私達のデータベースで行われ、Androidのデータベース操作のコレクションとして注意します。それでは、データテーブルの「ユーザー」と呼ばれるテーブルを作成してみましょう
/**
* 应用测试的表结构模型
*/
@Entity(tableName = "users")// 表名注解
public class User {
/**
* 主键
* 由于主键不能为空,所以需要 @NonNull 注解
*/
@NonNull
@PrimaryKey
@ColumnInfo(name = "userid")// Room 列注解
private String mId;
/**
* 用户名
* 普通列
*/
@ColumnInfo(name = "username")
private String mUserName;
/**
* 构造方法
* 设置为 @Ignore 将其忽视
* 这样以来,这个注解方法就不会被传入 Room 中,做相应处理
* @param mUserName
*/
@Ignore
public User(String mUserName){
this.mId = UUID.randomUUID().toString();
this.mUserName = mUserName;
}
/**
* 我们发现与上个方法不同,该方法没有标记 @Ignore 标签
*
* 所以编译时该方法会被传入 Room 中相应的注解处理器,做相应处理
* 这里的处理应该是 add 新数据
* @param id
* @param userName
*/
public User(String id, String userName) {
this.mId = id;
this.mUserName = userName;
}
public String getId() {
return mId;
}
public String getUserName() {
return mUserName;
}
}
まず、ヘッダ部分に、我々は前に述べ見@Entity(...)
ラベル、ラベルは、エンティティ・クラスは、テーブル内のデータベースを表し、我々は、そのソース・コードを表示する前に言いました:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface Entity {...}
私たちが知ることができ、そこからノートクラスのノートは、コンパイル時にトリガされ、Googleがこのクラスに私たちをしていることに気づい導入されています。
Marks a class as an entity. This class will have a mapping SQLite table in the database.
クラスのノートが置かれているときに私たちは、この中に持っているような、見ることができUser
、対応する注釈プロセッサをコンパイルするという名前のファイルを作成し、それぞれの内部コードを呼び出すとき、カテゴリusers
(に@Entity(tableName = "users")
渡されたデータ表)
私たちは、その後、見下します:
- @ColumnInfo(名=「USERID」) :注釈データアノテーションのメンバー、テーブル内の対応する名前を生成します:
userid
列 - @PrimaryKey:注釈と名前が示唆する
@ColumnInfo(name = "...")
ことに注意すること、ここで、テーブルの主キーを示すとともに、注釈の使用に@Entity
ソース強調:.各エンティティは、少なくとも有していなければならない {@link主キー}で注釈1つのフィールドすなわち@Entity(...)
マークデータ・テーブル・クラスは、少なくとも1つの一次キーを持っている必要があります - @ignore:注釈データの注釈のメンバー、方法、注釈プロセッサが処理されていない、無視されます
ここでは、コードは2つのコンストラクタが存在していることがわかり、なぜGoogleSampleは一見余分本の存在でしょうか?私たちはもう一度見て、上記のコンストラクタマーカーたいお送りします@Ignore
ラベル、および下記のコンストラクタはありませんでした。以来@Entity
マーククラス、属性および列コンストラクタget()
メソッド自動的プロセッサ識別処理を注釈付きであろう。私たちは、その後、一時的な作成する必要があるので、私たちは簡単に、この設計のために、Googleの理由、と考えることができUser
、オブジェクトを、私たちは望んでいない@Entity
、我々はコンストラクタを呼び出すとき、それはデータベースに保存されます。だから我々はこれが持って@Ignore
自動的にデータベースに格納されていない一時オブジェクトを作成するためのコンストラクタ、我々はデータベースに格納されているオブジェクトは、呼び出しがされるまで待機するUser(String id, String userName)
ことができます。
UserDao
私たちの上@Entity
の設立users
テーブル、のは使用してみましょう@Dao
書き込み変更する注釈をUserDao
インタフェースを。
@Dao
public interface UserDao {
/**
* 为了简便,我们只在表中存入1个用户信息
* 这个查询语句可以获得 所有 User 但我们只需要第一个即可
* @return
*/
@Query("SELECT * FROM Users LIMIT 1")
Flowable<User> getUser();
/**
* 想数据库中插入一条 User 对象
* 若数据库中已存在,则将其替换
* @param user
* @return
*/
@Insert(onConflict = OnConflictStrategy.REPLACE)
Completable insertUser(User user);
/**
* 清空所有数据
*/
@Query("DELETE FROM Users")
void deleteAllUsers();
}
執筆の私たちの通常の習慣に従い、我々は、対応するデータベース操作のコードを書く、クラスになります。しかし、用いた以外は、Room
その後、我々は、インタフェースクラスにそれを回す、とだけ書くことや店舗運営の具体的な実現を気にする必要はありませんでした、適切なラベルを設定する必要があります。
/**
* 为了简便,我们只在表中存入1个用户信息
* 这个查询语句可以获得 所有 User 但我们只需要第一个即可
* @return
*/
@Query("SELECT * FROM Users LIMIT 1")
Flowable<User> getUser();
ここでは、使用してクエリの方法ことがわかり@Query
注釈を、この注釈の具体的な何の機能はありますか?それのGoogleの公式な説明は次のとおりです。1が標識されている@Dao
クラスのラベル、照会する方法。名前は、このメソッドインデックス付きのノートを示唆するように、それがされるRoom
データクエリ方法として、特定のクエリロジックは、私たちのケアを必要としない、プロセッサはノートを認識し、我々は唯一のに必要なSQL 语句
パラメータが渡されるように@Query(...)
することができます。その後、我々は、この方法は、背圧返すことを見出しFlowable<...>
、速度、メモリのオーバーフローの問題を得、受信されたデータよりもはるかに大きい読み取り、テーブル内の過剰なデータを防止するためであるオブジェクトタイプを、特定の詳細RxJava
私はここで、チュートリアル私は詳細には触れません。
/**
* 想数据库中插入一条 User 对象
* 若数据库中已存在,则将其替换
* @param user
* @return
*/
@Insert(onConflict = OnConflictStrategy.REPLACE)
Completable insertUser(User user);
私たちは、上記の方法があることがわかり@Insert
マークされた注釈は、名前から見ることができ、これは挿入方法になります。名前が示すように@Insert
マークされたメソッドを、データベース、私たちはこの混乱し、ブラケットている唯一のものにデータを挿入するために使用されonConflict
、パラメータonConflict
の意味「紛争」、そして私たちの日常生活の中で次のデータベース操作を考える、考えることは難しいことではありません。これは、設定するために使用される場合、元の処理方法を使用してデータベース、データ競合にデータ。ここでは、ことを渡すOnConflictStrategy.REPLACE
などの操作に対応する、より多くのパラメータ、あります「と、データの競合場合は、元のデータに置き換える」という意味、ROLLBACK
ABORT
詳細に記載されていない他のスペースの理由は、我々が所有することができます公式ドキュメントをチェックしてください。それは価値がある格言はこれがあるもCompletable
、戻り値はRxJava
それだけを扱う、プリミティブ型onComplete
onError
、それはRunnableをの受信として見ることができるイベント。
/**
* 清空所有数据
*/
@Query("DELETE FROM Users")
void deleteAllUsers();
この最後の方法は、空にすることでusers
、テーブルの内容全体を、それは非常に簡単です、ここでは説明しません。唯一の注意点は、ここでの使用されるDELETE FROM 表名
形式は、代わりにtruncate table 表名
効率:、違いがあるtruncate
の比率をdelete
より速く、しかし、truncate
同等のは、テーブルの構造を保持この表を再作成し、そのログを削除しないために、データを回復することはできません。
UsersDatabase
Room
我々は2を終了した3つの主要コンポーネント、のは、最後のを見てみましょう@Database
コメント:
@Database(entities = {User.class}, version = 1, exportSchema = false)
public abstract class UsersDatabase extends RoomDatabase {
/**
* 单例模式
* volatile 确保线程安全
* 线程安全意味着改对象会被许多线程使用
* 可以被看作是一种 “程度较轻的 synchronized”
*/
private static volatile UsersDatabase INSTANCE;
/**
* 该方法由于获得 DataBase 对象
* abstract
* @return
*/
public abstract UserDao userDao();
public static UsersDatabase getInstance(Context context) {
// 若为空则进行实例化
// 否则直接返回
if (INSTANCE == null) {
synchronized (UsersDatabase.class) {
if (INSTANCE == null){
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
UsersDatabase.class, "Sample.db")
.build();
}
}
}
return INSTANCE;
}
}
古い方法は、Google
定義はそう書かれている:としてマークされたクラスRoom
のデータベースを。名前が示すように、我々は、ラベルのクラスをマークする必要があり、このようなデータベースの確立、バージョンアップ、およびそれ以上のものとして、特定のデータベース操作を行います。:我々はこれに我々が含むパラメータの数渡すことがわかりentities
、この場合、我々は一つだけ持って、データベース内のテーブルの一連のマーキング、アレイ構造をUser
のでのみ通過する、テーブル、version
データベースのバージョン、exportSchema
履歴リポジトリの輸出
/**
* 单例模式
* volatile 确保线程安全
* 线程安全意味着改对象会被许多线程使用
* 可以被看作是一种 “程度较轻的 synchronized”
*/
private static volatile UsersDatabase INSTANCE;
これは、シングルモードの実施形態では、使用可能なグローバルUsersDatabaseを作成するためのオブジェクトことがわかります。
public static UsersDatabase getInstance(Context context) {
// 若为空则进行实例化
// 否则直接返回
if (INSTANCE == null) {
synchronized (UsersDatabase.class) {
if (INSTANCE == null){
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
UsersDatabase.class, "Sample.db")
.build();
}
}
}
return INSTANCE;
}
これはシングルトンであるINSTANCE取得方法オブジェクト、学生は私がこの見ることができます理解していないシングルトンを-コンテキストはこの1つは十分にある、世界的に利用可能なオブジェクト
UserDataSource
データベース操作方法の大部分はで定義されています。私たちは見ることができるUserDao
方法一般的な注意事項クラスが継承されていないが、しかし、いくつかの方法が継承することができる、特別にマークされているが、それの後に我々は、多くの機能を作成したいですクラスは、我々は呼び出す必要がUserDao
で方法を。そこでここでは定義するUserDataSource
インターフェイスを:
public interface UserDataSource {
/**
* 从数据库中读取信息
* 由于读取速率可能 远大于 观察者处理速率,故使用背压 Flowable 模式
* Flowable:https://www.jianshu.com/p/ff8167c1d191/
*/
Flowable<User> getUser();
/**
* 将数据写入数据库中
* 如果数据已经存在则进行更新
* Completable 可以看作是 RxJava 的 Runnale 接口
* 但他只能调用 onComplete 和 onError 方法,不能进行 map、flatMap 等操作
* Completable:https://www.jianshu.com/p/45309538ad94
*/
Completable insertOrUpdateUser(User user);
/**
* 删除所有表中所有 User 对象
*/
void deleteAllUsers();
}
インターフェイスはツール、方法とされ、非常に簡単ですUserDao
、我々はここで詳細には触れませんが、まったく同じ。
LocalUserDataSource
public class LocalUserDataSource implements UserDataSource {
private final UserDao mUserDao;
public LocalUserDataSource(UserDao userDao) {
this.mUserDao = userDao;
}
@Override
public Flowable<User> getUser() {
return mUserDao.getUser();
}
@Override
public Completable insertOrUpdateUser(User user) {
return mUserDao.insertUser(user);
}
@Override
public void deleteAllUsers() {
mUserDao.deleteAllUsers();
}
}
公式の分析を見てみましょう:「使用Room
データソースとしてデータベースを。」いくつかのクラスが保持しているオブジェクトであることをUserDao
データベース操作を検索変更するオブジェクト、追加および削除。
- これまでのところ、ルームが終了するデータベースの一部を操作についてがあり、その後、私たちが解決構築されたビュー層を実施しています。
UserViewModel
まずは、実現させViewModel
、それが何であるか、クラスをViewModel
、その後のような?文字通り、それは視野と確かにView
、データModel
関連します。それは準備と管理とUIコンポーネントに責任があるとして、実際には、ちょうどその文字通りの意味として、Fragment/Activity
データタイプに関連する、それはViewModel
UI関連のデータを管理するために使用されますが、ViewModel
また、UIコンポーネント間の通信を担当するために使用することができます。だから今、彼の具体的な実現を見てみましょう:
public class UserViewModel extends ViewModel {
/**
* UserDataSource 接口
*/
private final UserDataSource mDataSource;
private User mUser;
public UserViewModel(UserDataSource dataSource){
this.mDataSource = dataSource;
}
/**
* 从数据库中读取所有 user 名称
* @return 背压形式发出所有 User 的名字
*
* 由于数据库中 User 量可能很大,可能会因为背压导致内存溢出
* 故采用 Flowable 模式,取代 Observable
*/
public Flowable<String> getUserName(){
return mDataSource.getUser()
.map(new Function<User, String>() {
@Override
public String apply(User user) throws Exception {
return user.getUserName();
}
});
}
/**
* 更新/添加 数据
*
* 判断是否为空,若为空则创建新 User 进行存储
* 若不为空,说明该 User 存在,这获得其主键 'getId()' 和传入的新 Name 拼接,生成新 User 存储
* 通过 insertOrUpdateUser 接口,返回 Comparable 对象,监听是否存储成功
* @param userName
* @return
*/
public Completable updateUserName(String userName) {
mUser = mUser == null
? new User(userName)
: new User(mUser.getId(), userName);
return mDataSource.insertOrUpdateUser(mUser);
}
}
コードの構造は、非常に単純でmDataSource
、当社のフロント構築することであるUserDataSource
私達のデータベース操作制御クラスとして、インタフェースオブジェクトを:LocalUserDataSource
そのことにより、インタフェースを実装するので、私たちは外であってもLocalUserDataSource
、彼のメソッドがコールバックに対応するので、渡されたオブジェクト、それは、必要なデータベース操作を実現することです。私はノートに与えている各メソッドの機能は、ここではそれらを繰り返しません
ViewModelFactory
そこ我々は上記を参照することができ、我々はデータ処理持つViewModel
クラスを、我々はここでViewModelFactory
、クラス、どのような役割ですか?私たちは、実現の例を見てみましょう:
public class ViewModelFactory implements ViewModelProvider.Factory {
private final UserDataSource mDataSource;
public ViewModelFactory(UserDataSource dataSource) {
mDataSource = dataSource;
}
// 你需要通过 ViewModelProvider.Factory 的 create 方法来创建(自定义的) ViewModel
// 参考文档:https://medium.com/koderlabs/viewmodel-with-viewmodelprovider-factory-the-creator-of-viewmodel-8fabfec1aa4f
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
// 为什么这里用 isAssignableFrom 来判断传入的 modelClass 类的类型, 而不直接用 isInstance 判断?
// 答:二者功能一样,但如果传入值(modelClass 为空)则 isInstance 会报错奔溃,而 isAssignableFrom 不会
if (modelClass.isAssignableFrom(UserViewModel.class)) {
return (T) new UserViewModel(mDataSource);
}
throw new IllegalArgumentException("Unknown ViewModel class");
}
}
ViewModelFactory
継承されます。ViewModelProvider.Factory
あなたが作成を支援する責任である、ViewModel
インスタンスを。しかし、あなたは、私たちが持っていない、求めることができるViewModel
まだコンストラクタを?ViewModelFactory
余計ありませんか?あなたが慣れていない場合ViewModelFactory
:コンテンツについて、あなたはこれを見ることができるのViewModelとViewModelProvider.Factory:ViewModelにクリエーター
注入
Injection
これは、ヘルパークラスであり、それは、論理機能ルームだ問題ではありません。Sample
オブジェクトごとに独立したされる、注射の種類は、私たちはこのクラスの実装を見てみましょう:
public class Injection {
/**
* 通过该方法实例化出能操作数据库的 LocalUserDataSource 对象
* @param context
* @return
*/
public static UserDataSource provideUserDateSource(Context context) {
// 获得 RoomDatabase
UsersDatabase database = UsersDatabase.getInstance(context);
// 将可操作 UserDao 传入
// 实例化出可操作 LocalUserDataSource 对象方便对数据库进行操作
return new LocalUserDataSource(database.userDao());
}
/**
* 获得 ViewModelFactory 对象
* 为 ViewModel 实例化作准备
* @param context
* @return
*/
public static ViewModelFactory provideViewModelFactory(Context context) {
UserDataSource dataSource = provideUserDateSource(context);
return new ViewModelFactory(dataSource);
}
}
クラスには2つの方法が各種のデータとの相互変換を実現するために構成している、我々は道のDirを見てみたいです:
/**
* 通过该方法实例化出能操作数据库的 LocalUserDataSource 对象
* @param context
* @return
*/
public static UserDataSource provideUserDateSource(Context context) {
// 获得 RoomDatabase
UsersDatabase database = UsersDatabase.getInstance(context);
// 将可操作 UserDao 传入
// 实例化出可操作 LocalUserDataSource 对象方便对数据库进行操作
return new LocalUserDataSource(database.userDao());
}
この方法では、まず私たちの受信context
オブジェクト、UsersDatabase.getInstance(context)
聞かせて、メソッドをdatabase
所有context
、リンクをしてデータベースを初期化実現します。一方、背面にLocalUserDataSource
件名、私たちはテーブルの対応する操作内容データを覚ますことができるように。
/**
* 获得 ViewModelFactory 对象
* 为 ViewModel 实例化作准备
* @param context
* @return
*/
public static ViewModelFactory provideViewModelFactory(Context context) {
UserDataSource dataSource = provideUserDateSource(context);
return new ViewModelFactory(dataSource);
}
関数法は、私たちがインスタンス化するためにつまり、非常に明確であるViewModelFactory
私たちは未来を作成するには、オブジェクトをViewModel
準備します。私たちは、上記の呼び出しここで見ることができるprovideUserDateSource
方法を、データベースの操作はの方法により得られたLocalUserDataSource
対象物、ここで私たちが使用ビジョンシングルトンを参照して、このようなデータベースは、接続を作成するために繰り返されることはありません。
- さて、これまでのすべての準備が完了している、のは、ビュー層UserActivityを呼び出してみましょう
- ので
UserActivity
、より多くのコンテンツは、私は完全なコードを投稿していない、我々はステップバイステップの説明します
レディデータメンバ
まず、我々は必要なクラスのデータメンバの準備しました:
private static final String TAG = UserActivity.class.getSimpleName();
private TextView mUserName;
private EditText mUserNameInput;
private Button mUpdateButton;
// 一个 ViewModel 用于获得 Activity & Fragment 实例
private ViewModelFactory mViewModelFactory;
// 用于访问数据库
private UserViewModel mViewModel;
// disposable 是订阅事件,可以用来取消订阅。防止在 activity 或者 fragment 销毁后仍然占用着内存,无法释放。
private final CompositeDisposable mDisposable = new CompositeDisposable();
- まず、インターフェイスはそれぞれの動作を制御します
- そして、これは
mViewModelFactory
、mViewModel
2人のデータメンバーデータソースの操作に責任があります - その後、
CompositeDisposable
活動の終わりを防ぐために、イベントサブスクリプションを管理するためのターゲットは、継続的な例を購読します
onCreate
コントロールは、データソース層を初期化され、データベース、等
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_user);
mUserName = findViewById(R.id.user_name);
mUserNameInput = findViewById(R.id.user_name_input);
mUpdateButton = findViewById(R.id.update_user);
// 实例化 ViewModelFactory 对象,准备实例化 ViewModel
mViewModelFactory = Injection.provideViewModelFactory(this);
mViewModel = new ViewModelProvider(this, mViewModelFactory).get(UserViewModel.class);
mUpdateButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
updateUserName();
}
});
}
- 第一は、各種制御の初期化です
- 続い
ViewModel
初期化、このプロセスでは、彼らは、データベースへのリンクを実現しました - ユーザー情報ボタンリスナー結合、[実行]をクリックし
updateUserName
、次のような方法があります
updateUserName
ユーザデータベース情報を変更します。
private void updateUserName() {
String userName = mUserNameInput.getText().toString();
// 在完成用户名更新之前禁用“更新”按钮
mUpdateButton.setEnabled(false);
// 开启观察者模式
// 更新用户信息,结束后重新开启按钮
mDisposable.add(mViewModel.updateUserName(userName)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action() {
@Override
public void run() throws Exception {
mUpdateButton.setEnabled(true);
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
Log.d(TAG, "accept: Unable to update username");
}
}));
}
- 新しいユーザー名を取得
- ボタンがクリックできません
- で
io
スレッドを変更するためにデータベースにアクセスします - そのようなボタンがクリック可能に復帰することと、処理されるメインスレッドに切り替え
ONSTART
ユーザー情報を初期化し、変更UI
インターフェースの内容を
@Override
protected void onStart() {
super.onStart();
// 观察者模式
// 通过 ViewModel 从数据库中读取 UserName 显示
// 如果读取失败,显示错误信息
mDisposable.add(mViewModel.getUserName()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
mUserName.setText(s);
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
Log.e(TAG, "Unable to update username");
}
}));
}
- では
io
データベース・アクセス・スレッド - メインスレッドに切り替え、変更
UI
情報
onStop
登録解除
@Override
protected void onStop() {
super.onStop();
// 取消订阅。防止在 activity 或者 fragment 销毁后仍然占用着内存,无法释放。
mDisposable.clear();
}
- 当社従来の一例として
CompositeDisposable
、目標、関係を解除
ソース
Demo 地址
概要
使用することを学ぶAndroid Architecture Components
アセンブリは、簡略化され、当社の開発は、アプリケーション・モジュールよりデカップリングとより安定を開発することを可能にし、ビューデータの永続化層を分離し、より良い拡張性と柔軟性を提供します。最後に、コードワードは容易ではない、ああ信者を指すように忘れないでください