我一直在安卓SDK平台上工作,现在还不清楚如何保存应用程序的状态。因此,考虑到“你好,Android”示例的这个小的重新设计:

package com.android.hello;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class HelloAndroid extends Activity {

  private TextView mTextView = null;

  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    mTextView = new TextView(this);

    if (savedInstanceState == null) {
       mTextView.setText("Welcome to HelloAndroid!");
    } else {
       mTextView.setText("Welcome back.");
    }

    setContentView(mTextView);
  }
}

我认为这对于最简单的情况来说已经足够了,但无论我如何离开应用程序,它总是以第一条消息来响应。

我确信解决方案就像重写onPause之类的东西一样简单,但我已经在文档中翻了大约30分钟,没有发现任何明显的东西。


当前回答

我的问题是,我只在应用程序生命周期内需要持久性(即一次执行,包括在同一应用程序内启动其他子活动和旋转设备等)。我尝试了以上答案的各种组合,但在所有情况下都没有得到我想要的答案。最后,对我有用的是在onCreate期间获取savedInstanceState的引用:

mySavedInstanceState=savedInstanceState;

并在需要时使用它来获取变量的内容,如下所示:

if (mySavedInstanceState !=null) {
   boolean myVariable = mySavedInstanceState.getBoolean("MyVariable");
}

如上所述,我使用了onSaveInstanceState和onRestoreInstanceState,但我想我也可以或替代地使用我的方法在变量更改时保存变量(例如,使用putBoolean)

其他回答

不确定我的解决方案是否受到反对,但我使用绑定服务来保持ViewModel状态。是将其存储在服务的内存中,还是将其持久化并从SQLite数据库中检索,取决于您的需求。这就是任何类型的服务所做的,它们提供诸如维护应用程序状态和抽象公共业务逻辑之类的服务。

由于移动设备固有的内存和处理限制,我以类似于网页的方式处理Android视图。页面不维护状态,它纯粹是一个表示层组件,其唯一目的是显示应用程序状态并接受用户输入。web应用程序架构的最新趋势采用了古老的模型、视图、控制器(MVC)模式,其中页面是视图,域数据是模型,控制器位于web服务后面。同样的模式可以在Android中使用,视图是,嗯。。。View,模型是您的域数据,Controller作为Android绑定服务实现。每当您希望视图与控制器交互时,在开始/恢复时绑定它,在停止/暂停时解除绑定。

这种方法为您提供了实施关注点分离设计原则的额外好处,因为您的所有应用程序业务逻辑都可以移动到您的服务中,这减少了多个视图中的重复逻辑,并允许视图实施另一个重要的设计原则,即单一责任。

2020年,我们有一些变化:

如果您希望“活动”在进程终止并再次启动后恢复其状态,则可能需要使用“保存状态”功能。以前,需要重写Activity中的两个方法:onSaveInstanceState和onRestoreInstanceState。您还可以在onCreate方法中访问恢复的状态。类似地,在Fragment中,您有onSaveInstanceState方法可用(恢复的状态在onCreate、onCreateView和onActivityCreated方法中可用)。

从AndroidX SavedState 1.0.0开始,它是AndroidX Activity和AndroidX Fragment的依赖项,您可以访问SavedStateRegistry。您可以从Activity/Fragment获取SavedStateRegistry,然后注册SavedStateProvider:

class MyActivity : AppCompatActivity() {

  companion object {
    private const val MY_SAVED_STATE_KEY = "MY_SAVED_STATE_KEY "
    private const val SOME_VALUE_KEY = "SOME_VALUE_KEY "
  }
    
  private lateinit var someValue: String
  private val savedStateProvider = SavedStateRegistry.SavedStateProvider {    
    Bundle().apply {
      putString(SOME_VALUE_KEY, someValue)
    }
  }
  
  override fun onCreate(savedInstanceState: Bundle?) {    
    super.onCreate(savedInstanceState)
    savedStateRegistry.registerSavedStateProvider(MY_SAVED_STATE_KEY, savedStateProvider)
    someValue = savedStateRegistry.consumeRestoredStateForKey(MY_SAVED_STATE_KEY)?.getString(SOME_VALUE_KEY) ?: ""
  }
  
}

如您所见,SavedStateRegistry强制您对数据使用键。这可以防止您的数据被附加到同一Activity/Fragment的另一个SavedStateProvider损坏。此外,您还可以将SavedStateProvider提取到另一个类中,以便通过使用所需的抽象来处理数据,从而在应用程序中实现干净的保存状态行为。

当系统需要内存并终止应用程序时,调用onSaveInstanceState。当用户刚刚关闭应用程序时,不会调用它。所以我认为应用程序状态也应该保存在onPause中。

它应该保存到一些持久性存储中,如Preferences或SQLite。

就我而言,拯救国家充其量是一个拙劣的动作。如果需要保存持久数据,只需使用SQLite数据库即可。Android让SOOO变得简单。

类似于:

import java.util.Date;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class dataHelper {

    private static final String DATABASE_NAME = "autoMate.db";
    private static final int DATABASE_VERSION = 1;

    private Context context;
    private SQLiteDatabase db;
    private OpenHelper oh ;

    public dataHelper(Context context) {
        this.context = context;
        this.oh = new OpenHelper(this.context);
        this.db = oh.getWritableDatabase();
    }

    public void close() {
        db.close();
        oh.close();
        db = null;
        oh = null;
        SQLiteDatabase.releaseMemory();
    }


    public void setCode(String codeName, Object codeValue, String codeDataType) {
        Cursor codeRow = db.rawQuery("SELECT * FROM code WHERE codeName = '"+  codeName + "'", null);
        String cv = "" ;

        if (codeDataType.toLowerCase().trim().equals("long") == true){
            cv = String.valueOf(codeValue);
        }
        else if (codeDataType.toLowerCase().trim().equals("int") == true)
        {
            cv = String.valueOf(codeValue);
        }
        else if (codeDataType.toLowerCase().trim().equals("date") == true)
        {
            cv = String.valueOf(((Date)codeValue).getTime());
        }
        else if (codeDataType.toLowerCase().trim().equals("boolean") == true)
        {
            String.valueOf(codeValue);
        }
        else
        {
            cv = String.valueOf(codeValue);
        }

        if(codeRow.getCount() > 0) //exists-- update
        {
            db.execSQL("update code set codeValue = '" + cv +
                "' where codeName = '" + codeName + "'");
        }
        else // does not exist, insert
        {
            db.execSQL("INSERT INTO code (codeName, codeValue, codeDataType) VALUES(" +
                    "'" + codeName + "'," +
                    "'" + cv + "'," +
                    "'" + codeDataType + "')" );
        }
    }

    public Object getCode(String codeName, Object defaultValue){

        //Check to see if it already exists
        String codeValue = "";
        String codeDataType = "";
        boolean found = false;
        Cursor codeRow  = db.rawQuery("SELECT * FROM code WHERE codeName = '"+  codeName + "'", null);
        if (codeRow.moveToFirst())
        {
            codeValue = codeRow.getString(codeRow.getColumnIndex("codeValue"));
            codeDataType = codeRow.getString(codeRow.getColumnIndex("codeDataType"));
            found = true;
        }

        if (found == false)
        {
            return defaultValue;
        }
        else if (codeDataType.toLowerCase().trim().equals("long") == true)
        {
            if (codeValue.equals("") == true)
            {
                return (long)0;
            }
            return Long.parseLong(codeValue);
        }
        else if (codeDataType.toLowerCase().trim().equals("int") == true)
        {
            if (codeValue.equals("") == true)
            {
                return (int)0;
            }
            return Integer.parseInt(codeValue);
        }
        else if (codeDataType.toLowerCase().trim().equals("date") == true)
        {
            if (codeValue.equals("") == true)
            {
                return null;
            }
            return new Date(Long.parseLong(codeValue));
        }
        else if (codeDataType.toLowerCase().trim().equals("boolean") == true)
        {
            if (codeValue.equals("") == true)
            {
                return false;
            }
            return Boolean.parseBoolean(codeValue);
        }
        else
        {
            return (String)codeValue;
        }
    }


    private static class OpenHelper extends SQLiteOpenHelper {

        OpenHelper(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }

        @Override
        public void onCreate(SQLiteDatabase db) {
            db.execSQL("CREATE TABLE IF  NOT EXISTS code" +
            "(id INTEGER PRIMARY KEY, codeName TEXT, codeValue TEXT, codeDataType TEXT)");
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        }
    }
}

之后打个简单的电话

dataHelper dh = new dataHelper(getBaseContext());
String status = (String) dh.getCode("appState", "safetyDisabled");
Date serviceStart = (Date) dh.getCode("serviceStartTime", null);
dh.close();
dh = null;

基本上有两种方法可以实现这一改变。

使用onSaveInstanceState()和onRestoreInstance State()。在manifestandroid:configChanges=“orientation|screenSize”中。

我真的不建议使用第二种方法。因为在我的一次体验中,它导致设备屏幕一半变黑,同时从纵向旋转到横向,反之亦然。

使用上面提到的第一种方法,我们可以在方向改变或任何配置改变时保存数据。我知道一种方法,可以在savedInstance状态对象中存储任何类型的数据。

示例:如果要持久化Json对象,请考虑一个案例。使用getter和setter创建一个模型类。

class MyModel extends Serializable{
JSONObject obj;

setJsonObject(JsonObject obj)
{
this.obj=obj;
}

JSONObject getJsonObject()
return this.obj;
} 
}

现在,在onCreate和onSaveInstanceState方法中的活动中,执行以下操作。它看起来像这样:

@override
onCreate(Bundle savedInstaceState){
MyModel data= (MyModel)savedInstaceState.getSerializable("yourkey")
JSONObject obj=data.getJsonObject();
//Here you have retained JSONObject and can use.
}


@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//Obj is some json object 
MyModel dataToSave= new MyModel();
dataToSave.setJsonObject(obj);
oustate.putSerializable("yourkey",dataToSave); 

}