Android 中的垃圾收集时如何在共享首选项中保留静态成员

How to persist static member in Shared preferences when it is about to be Garbage collected in Android

我在 Utility class 中有一个静态成员,我的大部分活动都会访问它来存储历史信息。我想在 Utility class 即将被 GC 时保留这个静态成员。我尝试了以下选项。

  1. finalize() method implementation:

我已经覆盖了实用程序 class 的 finalize()(我知道并不总是保证 finalize() 会 运行)以在 Shared Preferences 中保留静态成员。但是finalize() 根本没有调用!

  1. Implementing onDestroy() in each activity to persist static member

我开始在所有可以访问这个静态成员的活动中实现onDestroy(),当每个activity即将被销毁时,静态成员将被持久化在SharedPreference中。这是有效的,但写入共享首选项的情况非常频繁,导致不必要的重复持久化,我想避免这种情况。

有没有更好的方法来做到这一点?

This is working but writing to Shared preferences is happening very frequently causing unnecessary repetition of persists which I want to avoid.

只需在静态成员旁边保留一个 boolean 标志,指示当前数据是否已被持久化。

private static Object myStaticMember;
private static boolean saved = true;

public static changeStaticMember(...) {
    ...
    saved = false;
}

public static persistStaticMember {
    if(!saved){
        ...
        saved = true;
    }
}

附加注意事项:如果您想避免活动中的重复代码,您可以在超类中实现 onDestroy 并让所有活动都从它扩展。


您还应该考虑使用另一种生命周期方法。 onDestroy 不保证会被调用。参见

不要开始实施不良习惯做法。考虑到 documentation of onPause(),这是您应该保留数据的地方。

This callback is mostly used for saving any persistent state the activity is editing, to present a "edit in place" model to the user and making sure nothing is lost if there are not enough resources to start the new activity without first killing this one. This is also a good place to do things like stop animations and other things that consume a noticeable amount of CPU in order to make the switch to the next activity as fast as possible, or to close resources that are exclusive access such as the camera.

In situations where the system needs more memory it may kill paused processes to reclaim resources. Because of this, you should be sure that all of your state is saved by the time you return from this function. In general onSaveInstanceState(Bundle) is used to save per-instance state in the activity and this method is used to store global persistent data (in content providers, files, etc.)

您无法保证,对于任何方法,您都会达到存储数据的程度。

考虑到您的 Utility class 只有静态方法和成员,并且您永远不会创建它的实例,因此永远不会调用 finalize()。如果您正在使用实用程序 class 的实例,只需将存储保留在您希望它最后可访问的位置。

考虑使用 onDestroy() 也不是一个好主意。 The documentation 明确说明这一点。

Note: do not count on this method being called as a place for saving data! For example, if an activity is editing data in a content provider, those edits should be committed in either onPause() or onSaveInstanceState(Bundle), not here. This method is usually implemented to free resources like threads that are associated with an activity, so that a destroyed activity does not leave such things around while the rest of its application is still running. There are situations where the system will simply kill the activity's hosting process without calling this method (or any others) in it, so it should not be used to do things that are intended to remain around after the process goes away.

如果您想在任何 Activity 处于暂停状态时执行某个函数,您可以创建一个 class 来扩展应用程序,然后使用您的逻辑实现 onPause() 方法。 如果您需要更多帮助或需要有关 SharedPreferences class 如何工作的更多信息,请告诉我!!!

假设目标仅在用户停止使用该应用程序时将该静态成员保存到 SharedPreferences,如果任何活动仍在显示,只需将该静态成员保留在 RAM 上以便快速访问并且不要做太多的 IO 工作。

您可以尝试覆盖方法 onTrimMemory in Application class, and check if the parameter level is larger than ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN 然后将该成员存储到 SharedPreferences。

它是:当您的所有活动不再向用户显示(转到后台或完全销毁)时,将触发此回调,这是释放一些内存缓存并存储一些历史数据的好时机进入持久存储。

在我的项目中,我做了这样的事情:

@Override
public void onTrimMemory(int level) {
    super.onTrimMemory(level);

    if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW) {
        ImageLoader.getInstance().clearMemoryCache();

        if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
            // When our app is not visible anymore, start freeing up some cache and resources
            // that only need when UI is visible
            CurrentSession.getInstance(this).endSession();

            if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
                cacheManager.freeUpMemCache();
                cacheManager.flush();
            }
        }
    }
}

首先问题是:那个对象是什么(看起来它不是一个复杂的对象,因为你将它存储在你的共享首选项中)?!?!

如果我们正在谈论一个对象,这就是你应该做的:

只需制作一个 class 并从应用程序扩展它。

public class MasterApplication extends Application {
   @Override
   public void onCreate() {
       super.onCreate();
   }
}

然后在清单中定义它:

<application
    ...
    android:name=".MasterApplication"
    ... >

现在在您的应用程序中声明您的变量 class :

public class MasterApplication extends Application {
   //HERE DECLARE YOUR VARIABLE
   public static int MY_INT=1;

   @Override
   public void onCreate() {
       super.onCreate();
   }
}

现在您可以从应用程序的任何位置访问该变量。

IMPORTANT NOTE : There is no need to be afraid of GC and memory leak !!!

因为应用程序 class 是您应用程序的会话,并且当您的应用程序 运行 或在后台时它始终处于活动状态(直到 android 终止您的应用程序)

假设我们正在谈论一个需要上下文的方法或class或对象,并且您担心内存泄漏作为内存中的持久上下文。不要再害怕了!你已经完成任务了!

就在您的应用程序的 onCreate 中 class 调用该对象或函数并传递它而不是上下文!

这个,参考你的应用程序上下文,正如我之前提到的,它总是在后台!

Saving in and reading from SharedPreference Scenario:

最简单的方法是在您的应用程序中重新创建一个函数 class。这样做:

public class MasterApplication 扩展应用程序 { //这里声明你的变量 public 静态整数 MY_INT=1;

   @Override
   public void onCreate() {
       super.onCreate();
       SharedPreference sf= ....;
       MY_INT=sf.getInt("MYDATA",0);
   }

   public static void setNewVariableToMyObject(int newdata){
      MY_INT=newdata;
      SharedPreference sf= ....
      sf.edit().putInt("MYDATA",MY_INT).commit();
   }
}

无论什么时候你想改变你的变量,都可以使用这个函数。您还可以确定,无论何时创建应用程序,您的变量都将由最后保存的 Var 分配。

你可以这样做:

  1. 创建一个与您的对象类型关联的 ReferenceQueue
  2. 为您的对象创建一个 WeakReference 并将其与 ReferenceQueue.
  3. 相关联
  4. 你的 WeakReferenece 对象在没有强引用(弱可达)时将被添加到 ReferenceQueue 中,因此它有资格完成.
  5. 您可以创建一个线程或服务轮询引用对象的RefereneQueue,如果可用,您可以通过WeakReference get( ) 方法并将其保存到 SharedPreferences.