JetPack之Lifecycle使用和原理

什么是Lifecycle

远古时代

在Android开发中,我们需要遵循组件的生命周期,例如在onStart()onResume()的时候更新UI,在onStop()onDestroy()的时候停止后台任务和释放资源。

举个常见的例子:我们需要在Activity的生命周期到底onCreate()的时候调用接口请求数据,然后在onResume()的时候设置到UI界面中,并且在onStop()的时候停止请求,否则有可能会出现内存泄漏的情况。

class MyActivity : AppCompatActivity() {
   private lateinit var request: MainRequset

private var data: List<String> = mutableListOf()

   override fun onCreate(...) {
       request= MainRequset(this) { list ->
           // update UI
data = list
      }
  }

   public override fun onResume() {
       super.onStart()
       textView.setText(data[0])
  }

   public override fun onStop() {
       super.onStop()
       request.stop()
  }

}

随着项目不断迭代,需要在生命周期的不同阶段做的任务也越来越多,导致了这几个生命周期方法里面的业务代码越来越臃肿,最后接手的开发者难以维护。

class MyActivity : AppCompatActivity() {
   private lateinit var request: MainRequest // 首页数据
private lateinit var firebase: FireBase  // 性能监控
private lateinit var pushNotification: PushNotification  // 推送服务
private lateinit var startup: SplashScreen  // 开屏广告

private var data: List<String> = mutableListOf()

   override fun onCreate(...) {
       request = MainRequest(this) { list ->
           // update UI
data = list
      }

firebase = FireBase(this) { isSucceed ->
           // do Something

      }

pushNotification = PushNotification(this) { isSucceed->
           // do Something
      }

startup = SplashScreen(this) { isSucceed->
           // do Something
      }
  }

   public override fun onResume() {
       super.onStart()
       textView.setText(data[0])
//...
  }

   public override fun onStop() {
       super.onStop()
       request.stop()
firebase.stop()
pushNotification.stop()
startup.stop()
  }

}

Lifecycle的出现

这里我们引用Google Developers的图来说明Lifecycle的工作流程:

我们可以通过在代码使用LifecycleObserver和@OnLifecycleEvent注解来实现对生命周期回调方法的监听。举个例子:

class MainActivity : BaseDataBindingActivity() {
   
   override fun onCreate(savedInstanceState: Bundle?) {
       applyExitMaterialTransform()
       super.onCreate(savedInstanceState)
       setContentView(R.layout.activity_main)
       lifecycle.addObserver(testListener())
  }

   inner class testListener(): LifecycleObserver {

       @OnLifecycleEvent(Lifecycle.Event.ON_START)
       fun start() {
           Log.d("bluelzy", "start")
      }

       @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
       fun stop() {
           Log.d("bluelzy","stop")
      }
  }

}

上面这段代码主要做了两件事:

  1. 定义了一个testListener内部类,它继承自LifecycleObserver接口,并且定义了两个方法,分别在onStart()和onStop()的时候会接收到回调
  2. 在Activity的onCreate()中通过addObserver()方法把testListener添加到观察者列表中

然后运行程序,我们可以看到Logcat中会打印出来,当MainActivity的生命周期到onStart()的时候,会调用testListener的start()方法,同样当生命周期到了onStop()的时候,会调用testListener的stop()方法。

相比起之前我们在Activity的各个生命周期方法中写一大段业务代码,是不是简化了许多?

在这里我们会有一些疑问:

  1. LifecycleObserver 是什么,观察者模式是什么运作的?
  2. Lifecycle原理是什么?为什么调用addOberver就可以观察到对应组件的生命周期了?

这些疑问需要我们从源码来解答。

Lifecycle的原理和设计思想

上面我们提出了两个问题,就是关于Lifecycle是如何运作的,它背后的原理到底是什么?我们通过源码来一步步解析。

首先Lifecycle有几个关键的类,分别是:

  • Lifecycle:抽象类,唯一子类就是LifecycleRegistry
  • LifecycleOwner:具有生命周期感知能力的类需要实现的接口
  • LifecycleOberver:Lifecycle需要观察的对象们(被观察者)
  • LifecycleRegistry:负责转发生命周期事件,Lifecycle的真正的实现类

接下来我们一个个来详细讲解。

Abstract Class: Lifecycle

Lifecycle所在路径:package androidx.lifecycle;

整个Lifecycle类再2.2.0版本只有200行代码,主要是定义了生命周期相关状态,以及Observer相关的add和remove方法。

下面是Lifecycle.java源码

public abstract class Lifecycle {

   /**
    * Lifecycle coroutines extensions stashes the CoroutineScope into this field.
    *
    * @hide used by lifecycle-common-ktx
    */
   @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
   @NonNull
   AtomicReference<Object> mInternalScopeRef = new AtomicReference<>();

   /**
    * Adds a LifecycleObserver that will be notified when the LifecycleOwner changes
    * state.
    * <p>
    * The given observer will be brought to the current state of the LifecycleOwner.
    * For example, if the LifecycleOwner is in {@link State#STARTED} state, the given observer
    * will receive {@link Event#ON_CREATE}, {@link Event#ON_START} events.
    *
    * @param observer The observer to notify.
    */
   @MainThread
   public abstract void addObserver(@NonNull LifecycleObserver observer);

   /**
    * Removes the given observer from the observers list.
    * <p>
    * If this method is called while a state change is being dispatched,
    * <ul>
    * <li>If the given observer has not yet received that event, it will not receive it.
    * <li>If the given observer has more than 1 method that observes the currently dispatched
    * event and at least one of them received the event, all of them will receive the event and
    * the removal will happen afterwards.
    * </ul>
    *
    * @param observer The observer to be removed.
    */
   @MainThread
   public abstract void removeObserver(@NonNull LifecycleObserver observer);

   /**
    * Returns the current state of the Lifecycle.
    *
    * @return The current state of the Lifecycle.
    */
   @MainThread
   @NonNull
   public abstract State getCurrentState();

   @SuppressWarnings("WeakerAccess")
   public enum Event {
       /**
        * Constant for onCreate event of the {@link LifecycleOwner}.
        */
       ON_CREATE,
       /**
        * Constant for onStart event of the {@link LifecycleOwner}.
        */
       ON_START,
       /**
        * Constant for onResume event of the {@link LifecycleOwner}.
        */
       ON_RESUME,
       /**
        * Constant for onPause event of the {@link LifecycleOwner}.
        */
       ON_PAUSE,
       /**
        * Constant for onStop event of the {@link LifecycleOwner}.
        */
       ON_STOP,
       /**
        * Constant for onDestroy event of the {@link LifecycleOwner}.
        */
       ON_DESTROY,
       /**
        * An {@link Event Event} constant that can be used to match all events.
        */
       ON_ANY
  }

   /**
    * Lifecycle states. You can consider the states as the nodes in a graph and
    * {@link Event}s as the edges between these nodes.
    */
   @SuppressWarnings("WeakerAccess")
   public enum State {
       /**
        * Destroyed state for a LifecycleOwner. After this event, this Lifecycle will not dispatch
        * any more events. For instance, for an {@link android.app.Activity}, this state is reached
        * <b>right before</b> Activity's {@link android.app.Activity#onDestroy() onDestroy} call.
        */
       DESTROYED,

       /**
        * Initialized state for a LifecycleOwner. For an {@link android.app.Activity}, this is
        * the state when it is constructed but has not received
        * {@link android.app.Activity#onCreate(android.os.Bundle) onCreate} yet.
        */
       INITIALIZED,

       /**
        * Created state for a LifecycleOwner. For an {@link android.app.Activity}, this state
        * is reached in two cases:
        * <ul>
        *     <li>after {@link android.app.Activity#onCreate(android.os.Bundle) onCreate} call;
        *     <li><b>right before</b> {@link android.app.Activity#onStop() onStop} call.
        * </ul>
        */
       CREATED,

       /**
        * Started state for a LifecycleOwner. For an {@link android.app.Activity}, this state
        * is reached in two cases:
        * <ul>
        *     <li>after {@link android.app.Activity#onStart() onStart} call;
        *     <li><b>right before</b> {@link android.app.Activity#onPause() onPause} call.
        * </ul>
        */
       STARTED,

       /**
        * Resumed state for a LifecycleOwner. For an {@link android.app.Activity}, this state
        * is reached after {@link android.app.Activity#onResume() onResume} is called.
        */
       RESUMED;

       /**
        * Compares if this State is greater or equal to the given {@code state}.
        *
        * @param state State to compare with
        * @return true if this State is greater or equal to the given {@code state}
        */
       public boolean isAtLeast(@NonNull State state) {
           return compareTo(state) >= 0;
      }
  }

可以看到,主要定义了两个枚举类型,分别为Event和State,这两者都和生命周期方法回调相对应。可以参考上面的图。

举个例子:

Activity的生命周期走了onStart()方法,这时候会触发ON_START事件(Event),并且对应的状态(State)也会变成STARTED。这个状态在LiveData是否进行回调的时候会用到。

Interface: LifecycleObserver

public interface LifecycleObserver {

}

没有任何方法的一个空接口,所有实现了这个接口的类本质上都具有对生命周期的“被”观察能力。它们在生命周期方法回调的时候,在LifecycleRegistry中会进行分发。

Interface: LifecycleOwner

public interface LifecycleOwner {
   /**
    * Returns the Lifecycle of the provider.
    *
    * @return The lifecycle of the provider.
    */
   @NonNull
   Lifecycle getLifecycle();
}

这个类是一个接口,里面只有一个方法,就是getLifecycle(),返回Lifecycle对象。无论是Activity还是Fragment,都实现了这个接口,而他们返回的Lifecycle对象,就是LifecycleRegistry

Class: LifecycleRegistry

接下来看看LifecycleRegistry,它作为Lifecycle唯一的实现类,承载了生命周期分发的职责。

在Lifecycle里面通过addObserver()方法添加的LifecycleObserver,都会进行生命周期分发。

private FastSafeIterableMap<LifecycleObserver, ObserverWithState> mObserverMap =
           new FastSafeIterableMap<>()

@MainThread
   public void setCurrentState(@NonNull State state) {
       moveToState(state);
  }

   /**
    * Sets the current state and notifies the observers.
    * <p>
    * Note that if the {@code currentState} is the same state as the last call to this method,
    * calling this method has no effect.
    *
    * @param event The event that was received
    */
   public void handleLifecycleEvent(@NonNull Lifecycle.Event event) {
       State next = getStateAfter(event);
       moveToState(next);
  }

   private void moveToState(State next) {
       if (mState == next) {
           return;
      }
       mState = next;
       if (mHandlingEvent || mAddingObserverCounter != 0) {
           mNewEventOccurred = true;
           // we will figure out what to do on upper level.
           return;
      }
       mHandlingEvent = true;
       sync();
       mHandlingEvent = false;
  }

static State getStateAfter(Event event) {
       switch (event) {
           case ON_CREATE:
           case ON_STOP:
               return CREATED;
           case ON_START:
           case ON_PAUSE:
               return STARTED;
           case ON_RESUME:
               return RESUMED;
           case ON_DESTROY:
               return DESTROYED;
           case ON_ANY:
               break;
      }
       throw new IllegalArgumentException("Unexpected event value " + event);
  }

主要的方法就是handleLifecycleEvent(),在通过getStateAfter()把Event转为State之后进行分发

在分发之前,还需要做一步操作,

private void sync() {
       LifecycleOwner lifecycleOwner = mLifecycleOwner.get();
       if (lifecycleOwner == null) {
           throw new IllegalStateException("LifecycleOwner of this LifecycleRegistry is already"
                   + "garbage collected. It is too late to change lifecycle state.");
      }
       while (!isSynced()) {
           mNewEventOccurred = false;
           // no need to check eldest for nullability, because isSynced does it for us.
           if (mState.compareTo(mObserverMap.eldest().getValue().mState) < 0) {
               backwardPass(lifecycleOwner);
          }
           Entry<LifecycleObserver, ObserverWithState> newest = mObserverMap.newest();
           if (!mNewEventOccurred && newest != null
                   && mState.compareTo(newest.getValue().mState) > 0) {
               forwardPass(lifecycleOwner);
          }
      }
       mNewEventOccurred = false;
  }

可以看到,在sync()方法里面,还要判断当前State。

  • 如果当前State小于ObserverMap的状态,说明当前生命周期组件状态需要“降级”
  • 如果当前State大于ObserverMap的状态,说明当前生命周期组件状态需要“升级”

关于“升级”和“降级”的方法可以看下面代码:

@Nullable
public static Event downFrom(@NonNull State state) {
switch (state) {
 case CREATED:
   return ON_DESTROY;
 case STARTED:
   return ON_STOP;
 case RESUMED:
   return ON_PAUSE;
 default:
   return null;
}
}

@Nullable
public static Event upFrom(@NonNull State state) {
 switch (state) {
   case INITIALIZED:
     return ON_CREATE;
   case CREATED:
     return ON_START;
   case STARTED:
     return ON_RESUME;
    default:
      return null;
  }
}

举个例子:当前Activity的生命周期到了onCreate(),然后需要进行生命周期事件分发,这时候生命周期是STARTED,但是mState还是INITIALIZED,因此需要执行forwardPass()。

执行结束之后再进行生命周期事件分发:

private void forwardPass(LifecycleOwner lifecycleOwner) {
       Iterator<Entry<LifecycleObserver, ObserverWithState>> ascendingIterator =
               mObserverMap.iteratorWithAdditions();
       while (ascendingIterator.hasNext() && !mNewEventOccurred) {
           Entry<LifecycleObserver, ObserverWithState> entry = ascendingIterator.next();
           ObserverWithState observer = entry.getValue();
           while ((observer.mState.compareTo(mState) < 0 && !mNewEventOccurred
                   && mObserverMap.contains(entry.getKey()))) {
               pushParentState(observer.mState);
               observer.dispatchEvent(lifecycleOwner, upEvent(observer.mState));
               popParentState();
          }
      }
  }

   private void backwardPass(LifecycleOwner lifecycleOwner) {
       Iterator<Entry<LifecycleObserver, ObserverWithState>> descendingIterator =
               mObserverMap.descendingIterator();
       while (descendingIterator.hasNext() && !mNewEventOccurred) {
           Entry<LifecycleObserver, ObserverWithState> entry = descendingIterator.next();
           ObserverWithState observer = entry.getValue();
           while ((observer.mState.compareTo(mState) > 0 && !mNewEventOccurred
                   && mObserverMap.contains(entry.getKey()))) {
               Event event = downEvent(observer.mState);
               pushParentState(getStateAfter(event));
               observer.dispatchEvent(lifecycleOwner, event);
               popParentState();
          }
      }

分发的方法就是:dispatchEvent()

void dispatchEvent(LifecycleOwner owner, Event event) {
      State newState = event.getTargetState();
      mState = min(mState, newState);
      mLifecycleObserver.onStateChanged(owner, event);
      mState = newState;
}

总结

Lifecycle的最佳实践

  • 使界面控制器(Activity 和 Fragment)尽可能保持精简。它们不应试图获取自己的数据,而应使用 [ViewModel](<https://developer.android.com/reference/androidx/lifecycle/ViewModel>) 执行此操作,并观察 [LiveData](<https://developer.android.com/reference/androidx/lifecycle/LiveData>) 对象以将更改体现到视图中。
  • 设法编写数据驱动型界面,对于此类界面,界面控制器的责任是随着数据更改而更新视图,或者将用户操作通知给 [ViewModel](<https://developer.android.com/reference/androidx/lifecycle/ViewModel>)
  • 将数据逻辑放在 [ViewModel](<https://developer.android.com/reference/androidx/lifecycle/ViewModel>) 类中。[ViewModel](<https://developer.android.com/reference/androidx/lifecycle/ViewModel>) 应充当界面控制器与应用其余部分之间的连接器。不过要注意,[ViewModel](<https://developer.android.com/reference/androidx/lifecycle/ViewModel>) 不负责获取数据(例如,从网络获取)。但是,[ViewModel](<https://developer.android.com/reference/androidx/lifecycle/ViewModel>) 应调用相应的组件来获取数据,然后将结果提供给界面控制器。
  • 使用数据绑定在视图与界面控制器之间维持干净的接口。这样一来,您可以使视图更具声明性,并尽量减少需要在 Activity 和 Fragment 中编写的更新代码。如果您更愿意使用 Java 编程语言执行此操作,请使用诸如 Butter Knife 之类的库,以避免样板代码并实现更好的抽象化。
  • 如果界面很复杂,不妨考虑创建 presenter 类来处理界面的修改。这可能是一项艰巨的任务,但这样做可使界面组件更易于测试。
  • 避免在 [ViewModel](<https://developer.android.com/reference/androidx/lifecycle/ViewModel>) 中引用 [View](<https://developer.android.com/reference/android/view/View>)[Activity](<https://developer.android.com/reference/android/app/Activity>) 上下文。如果 ViewModel 存在的时间比 activity 更长(在配置更改的情况下),activity 将泄露并且不会得到垃圾回收器的妥善处置。
  • 使用 Kotlin 协程管理长时间运行的任务和其他可以异步运行的操作。

Lifecycle的使用场景

Lifecycle可使您在各种情况下更轻松地管理生命周期。下面列举几个例子:

  • 在粗粒度和细粒度位置更新之间切换。使用生命周期感知型组件可在位置应用可见时启用细粒度位置更新,并在应用位于后台时切换到粗粒度更新。借助生命周期感知型组件 [LiveData](<https://developer.android.com/reference/androidx/lifecycle/LiveData>),应用可以在用户使用位置发生变化时自动更新界面。
  • 停止和开始视频缓冲。使用生命周期感知型组件可尽快开始视频缓冲,但会推迟播放,直到应用完全启动。此外,应用销毁后,您还可以使用生命周期感知型组件终止缓冲。
  • 开始和停止网络连接。借助生命周期感知型组件,可在应用位于前台时启用网络数据的实时更新(流式传输),并在应用进入后台时自动暂停。
  • 暂停和恢复动画可绘制资源。借助生命周期感知型组件,可在应用位于后台时暂停动画可绘制资源,并在应用位于前台后恢复可绘制资源。

Realm 数据库基础使用和注意事项(1)

Realm is a mobile database: a replacement for SQLite & ORMs

目前 Realm-Java 最新版本是 10.8.0,安卓项目最开始使用的是从 5 开始,然后是 6 到目前的版本 10,从 5 到 10 版本使用上面的变化差异不大。因此只要熟悉 10 版本即可

一、RealmConfiguration

Realm DB 配置参数对于使用 Realm 很重要

val builder = RealmConfiguration.Builder()
builder.modules(ExampleModule())
    .assetFile("example.realm")
    .directory(File(context.dataDir, "xxx"))
    .allowWritesOnUiThread(true)
    .allowQueriesOnUiThread(true)
    .name("example.realm")
    .schemaVersion(10)
val configuration = builder.build()

1.1Modules

Realm 比较重要基础概念 Modules,Modules 用来指定 Realm DB 中的表,对应 java 或者 kotlin 里面就是 class,这个 class 通过继承 RealmObject 来实现,当然也可以通过接口实现,具体可以参看 Android 官方文档

@RealmModule(classes = [TargetTags::class])
class ExampleModule

以上就一个很简单的 Module 指定,这个 DB 包含一张表,表明叫做 TargetTags

public class TargetTags extends RealmObject {
@PrimaryKey
private String objectId;
@Index
private String targetId;
@Index
private String tag;

public String getObjectId() {
return objectId;
}

public void setObjectId(String objectId) {
this.objectId = objectId;
}

public String getTargetId() {
return targetId;
}

public void setTargetId(String targetId) {
this.targetId = targetId;
}

public String getTag() {
return tag;
}

public void setTag(String tag) {
this.tag = tag;
}
}

这个类实现了 RealmObject,对外提供了基础方法,并且使其一个字段生命为 primaryKey。当生成Realm DB 文件时,里面就会含有 TargetTags 的表,其中字段有 objectId、targetId、tag

1.2 schemaVersion

数据库版本号,类似于 Sqlite 的版本号,主要目的是针对字段变更、表的增加或者删除的。
对于字段的变更安卓不像 ios 那么方便,即使版本号增加也还是需要手动迁移,因此需要特别注意。
Realm DB在使用的时候就会对字段进行相应检查,如果检查不通过就会对外抛出异常,并且提示使用者哪里出问题,一般表现为该有的字段没有做对应迁移

1.3 directory

数据库目录,如果不指定的话默认就会在应用的 data 目录下面,最好指定对应目录,方便数据库文件的管理及迁移操作,尤其 Realm 打开 DB 文件时候会生成多个辅助文件,为了方便管理务必指定文件路径

1.4 name

DB 在数据库目录下面的文件名称,为了方便管理也最好指定

1.5 assetFile

可以让 Realm 直接指定读取 asset 目录下面准备好的 realm 文件,其实现原理也是通过拷贝 asset 目录下面的资源文件到指定目录。如果需要做比较特殊的迁移操作,或者说废弃掉当前目录下面的 realm 文件,需要提前自行删除或者移动

1.6 migration

数据库迁移操作,用户实现 RealmMigration 接口,在这个接口的

public void migrate(@NotNull DynamicRealm realm, long oldVersion, long newVersion) {
}

方法中做相应表的迁移,有增加字段,删除字段,修改字段,为某个字段添加额外索引,另外增加表删除表也需要在这里面声明。其本身实现原理是 Realm 另外一种使用方法,这种使用方式效率较低,但是对扩充字段比较方便,有兴趣的可以去查看文档 DynamicRealm 使用

1.7 其它

.allowWritesOnUiThread(true)
.allowQueriesOnUiThread(true)
.compactOnLaunch()

protected RealmConfiguration(File realmPath,
        @Nullable String assetFilePath,
        @Nullable byte[] key,
        long schemaVersion,
        @Nullable RealmMigration migration,
        boolean deleteRealmIfMigrationNeeded,
        OsRealmConfig.Durability durability,
        RealmProxyMediator schemaMediator,
        @Nullable RxObservableFactory rxObservableFactory,
        @Nullable FlowFactory flowFactory,
        @Nullable Realm.Transaction initialDataTransaction,
        boolean readOnly,
        @Nullable CompactOnLaunchCallback compactOnLaunch,
        boolean isRecoveryConfiguration,
        long maxNumberOfActiveVersions,
        boolean allowWritesOnUiThread,
        boolean allowQueriesOnUiThread) {}

realm 使用配置地方有很多,重要的还有能否在主线程写入和读取等等。可以在有需要的时候再去查看

二 Realm 使用

使用 RealmConfiguration 可以创建空的 Realm DB 文件,也可以通过拷贝方式拷贝现成 Realm 到指定目录,然后通过

Realm.getInstance(configuration)

直接就可以打开 Realm 对其中的表做数据库标准操作:增、删、改、查

不同于一般的关系型数据库,面向对象型数据库操作都是以对象为基本单位,尤其是修改某个属性时操作比较特殊,直接操作对象本身,这些操作可以去查看官方安卓文档
Realm 还有其他不少,例如删除数据库、数据库数据变更监听等,官方文档写得更加详细

三、transaction

transaction 中文为事务,数据库常用的操作,是旨在保证插入安全,尤其是数据插入失败时,保证数据库数据安全,Realm 的所有写入都需要放在 transaction 中,并且 transaction 中不能套 transaction

在特殊情况时,如果不确定当前写入操作是否在 transaction 需要进行额外判断

realm.isInTransaction()


因此需要进行比较复杂操作时,最好使用封装好的工具方法进行插入,防止出现事务操作中套事务的方式出现异常问题

四、Realm Thread

MongoDB Realm enables simple and safe multithreaded code when you follow these three rules:
Avoid writes on the UI thread if you write on a background thread:
You can write to a realm from any thread, but there can be only one writer at a time. Consequently, write transactions block each other. A write on the UI thread may result in your app appearing unresponsive while it waits for a write on a background thread to complete. If you are using Realm Sync, avoid writing on the UI thread as Sync writes on a background thread.
Don’t pass live objects, collections, or realms to other threads:
Live objects, collections, and realm instances are thread-confined: that is, they are only valid on the thread on which they were created. Practically speaking, this means you cannot pass live instances to other threads. However, Realm Database offers several mechanisms for sharing objects across threads.
Don’t lock to read:
Realm Database’s Multiversion Concurrency Control (MVCC) architecture eliminates the need to lock for read operations. The values you read will never be corrupted or in a partially-modified state. You can freely read from realms on any thread without the need for locks or mutexes. Unnecessarily locking would be a performance bottleneck since each thread might need to wait its turn before reading.

4.1.Realm 对象目前是不能跨线程

在某个线程创建,如果某个线程退出就需要把 Realm 对象关闭,通过调用

realm.close();

Realm 本身实现了一套比较比较复杂的缓存机制,因此每次调用 Realm.getInstance(configuration) 都首先从缓存里面去查找,如果没有才会创建新的,当然不同线程缓存不同,在主线程操作 Realm 最好保证 Realm 存活不要轻易关闭,非主 UI 线程如果线程不是永久存活,就需要考虑 Realm 关闭,否则一是会出现 Ream 不能完全被关闭一直在占用内存,另外一种情况就是 Realm 缓存数目一直在增加,可能会超过上限造成崩溃。

4.2.Realm 查询得到 RealmObject 不能跨线程

通过 Realm 查询到的 RealmObject 不能跨线程,当然目前 Realm SDK 提供了几种跨线程操作

  • To modify the data on two threads, query for the object on both threads using a primary key.
  • To send a fast, read-only view of an object to other threads, freeze the object.
  • To keep and share many read-only views of the object in your app, copy the object from the realm.
  • To react to changes made on any thread, use notifications.
  • To see changes from other threads in the realm on the current thread, refresh your realm instance (event loop threads refresh automatically).

比较重要的思路,一个是通过 id 查找,另外通过 realm 拷贝方式,还可以通过比较高级方式 freeze 方式得到的查询结果,realm 拷贝对内存和 cpu 消耗会比较大,不适合那些高频操作,freeze 会对 realm DB 文件增长影响有点大,因此根据具体使用场景决定使用方向

五、Refreshing Realms

刷新 realm 算是比较高阶操作了,尤其是牵涉到后台线程插入 DB 的时候,如果希望主线程立马能够取到刚刚插入或者更新的数据,那需要在主线程刷新 realm

realm.refresh();

当然 Realm 系统提供了一套自动刷新的机制,前提是当前线程有 looper,如果没有 looper 直接调用刷新逻辑会抛异常。

不管在后台大批量插入数据,又或者小批量插入数据,如果发现主 UI 界面有概率性部分数据显示异常,就需要考虑刷新 realm 数据库了,当然刷新会消耗系统性能,不能频繁操作。

六、Encrypt a Realm

加密 DB,这种操作防止数据轻易外泄,只需要在 RealmConfiguration 中指定即可,当然已经加密的 DB 不能通过没有指定秘钥的 RealmConfiguration 打开,切记。

七、其它

Realm 自从被 MongoDB 收购后增加了不少数据同步操作,尤其是对写入和读取在异步线程中操作判断越来越严格。不管是写入还是读取都是比较消耗性能的,写入更加耗性能。
不建议把大批量的写入和读取操作放在主 UI 线程,这会造成界面卡顿,可以考虑把重量级写入放在后台,回到主 UI 线程直接刷新数据库,又或者可以通过 realm 提供的回调方式

RealmObjectChangeListener

单纯的 query 对象基本不怎么消耗手机性能, realm 实现了一套 lazy load 方式,但是如果还想要去读取对象里面字段,尤其是比较大批量读取字段的时候,这个时候是非常耗 CPU 时间,有兴趣的可以去查看 realm 官方文档。

总结:Realm 为我们提供了比较便利的面向对象级别的数据库操作,好处是上手简单,基本只要稍微看过文档,知道基本使用方式就可以开发,坏处是,随着开发量越来越多,realm 到后期使用会越来越复杂,会牵涉到线程,插入读取,性能损耗之类的,是一种易上手但很难精的数据库。使用过程中一定要谨慎。

官方安卓 Realm: https://docs.mongodb.com/realm/sdk/android/
MongoDB Realm:https://www.mongodb.com/zh-cn/realm
旧有 Realm:https://docs.mongodb.com/realm-legacy/docs/java/latest/index.html#getting-started