我一直在安卓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分钟,没有发现任何明显的东西。


当前回答

Kotlin解决方案:对于onSaveInstanceState中的自定义类保存,您可以将类转换为JSON字符串并使用Gson转换进行恢复,对于单字符串、双精度、Int、Long值保存和恢复,如下所示。以下示例适用于片段和活动:

对于活动:

对于将数据放入saveInstanceState:

override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)

        //for custom class-----
        val gson = Gson()
        val json = gson.toJson(your_custom_class)
        outState.putString("CUSTOM_CLASS", json)

        //for single value------
        outState.putString("MyString", stringValue)
        outState.putBoolean("MyBoolean", true)
        outState.putDouble("myDouble", doubleValue)
        outState.putInt("MyInt", intValue)
    }

还原数据:

 override fun onRestoreInstanceState(savedInstanceState: Bundle) {
    super.onRestoreInstanceState(savedInstanceState)

    //for custom class restore
    val json = savedInstanceState?.getString("CUSTOM_CLASS")
    if (!json!!.isEmpty()) {
        val gson = Gson()
        testBundle = gson.fromJson(json, Session::class.java)
    }

  //for single value restore

   val myBoolean: Boolean = savedInstanceState?.getBoolean("MyBoolean")
   val myDouble: Double = savedInstanceState?.getDouble("myDouble")
   val myInt: Int = savedInstanceState?.getInt("MyInt")
   val myString: String = savedInstanceState?.getString("MyString")
 }

您也可以在Activity onCreate上恢复它。

对于碎片:

对于saveInstanceState中的put类:

 override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        val gson = Gson()
        val json = gson.toJson(customClass)
        outState.putString("CUSTOM_CLASS", json)
    }

还原数据:

 override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

        //for custom class restore
        if (savedInstanceState != null) {
            val json = savedInstanceState.getString("CUSTOM_CLASS")
            if (!json!!.isEmpty()) {
                val gson = Gson()
                val customClass: CustomClass = gson.fromJson(json, CustomClass::class.java)
            }
        }

      // for single value restore
      val myBoolean: Boolean = savedInstanceState.getBoolean("MyBoolean")
      val myDouble: Double = savedInstanceState.getDouble("myDouble")
      val myInt: Int = savedInstanceState.getInt("MyInt")
      val myString: String = savedInstanceState.getString("MyString")
    }

其他回答

这是Steve Moseley(由ToolmakerSteve)的回答中的一条评论,它将事情放在了一个角度(在整个onSaveInstanceState与onPause、东成本与西成本传奇中)

@VVK-我部分不同意。退出应用程序的某些方式不会触发onSaveInstanceState(oSIS)。这限制了oSIS的实用性。它的值得支持,操作系统资源最少,但如果应用程序希望将用户返回到他们所处的状态,无论应用程序如何退出时,有必要改用持久存储方法。我使用onCreate检查捆绑包,如果缺少,则检查永久存储。这集中了决策。我可以从崩溃中恢复,或后退按钮退出或自定义菜单项退出,或许多天后,用户重新回到屏幕上ToolmakerSteve九月2015年9月19日10:38

就我而言,拯救国家充其量是一个拙劣的动作。如果需要保存持久数据,只需使用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;

savedInstanceState仅用于保存与“活动”的当前实例相关联的状态,例如当前导航或选择信息,这样,如果Android破坏并重新创建“活动”,它就可以恢复原样。请参阅onCreate和onSaveInstanceState的文档

对于更长寿命的状态,请考虑使用SQLite数据库、文件或首选项。请参阅保存持久状态。

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

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

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

现在,在视图模型中使用两种方法是有意义的。如果要将第一个保存为保存的实例:您可以像这样在视图模型中添加状态参数https://developer.android.com/topic/libraries/architecture/viewmodel-savedstate#java

或者您可以在视图模型中保存变量或对象,在这种情况下,视图模型将保存生命周期,直到活动被破坏。

public class HelloAndroidViewModel extends ViewModel {
   public Booelan firstInit = false;

    public HelloAndroidViewModel() {
        firstInit = false;
    }
    ...
}

public class HelloAndroid extends Activity {

  private TextView mTextView = null;
  HelloAndroidViewModel viewModel = ViewModelProviders.of(this).get(HelloAndroidViewModel.class);
  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    mTextView = new TextView(this);

    //Because even if the state is deleted, the data in the viewmodel will be kept because the activity does not destroy
    if(!viewModel.firstInit){
        viewModel.firstInit = true
        mTextView.setText("Welcome to HelloAndroid!");
    }else{
       mTextView.setText("Welcome back.");
    }

    setContentView(mTextView);
  }
}