【问题标题】:Android writing custom objects to fileAndroid将自定义对象写入文件
【发布时间】:2014-09-14 16:40:13
【问题描述】:

我有一个自定义对象类Record 实现了Parcelable,我正在通过ArrayAdapter<Record> 创建一个ListView 我希望能够保存该列表,以便在用户下次打开时自动加载应用程序。该列表是动态填充的,每次添加记录时我都会调用我的保存方法。然后我有一个SharedPreference,它的布尔值设置为true,这样我就知道用户已经保存了一些数据,以便在下次打开应用程序时加载。这是我的保存和加载方法:

public void writeRecordsToFile(ArrayAdapter<Record> records) {

    String fileName = Environment.getExternalStorageDirectory().getPath() + "/records.dat";
    try {
        File file = new File(fileName);
        if(!file.exists()){
            file.createNewFile();
        }
        ObjectOutputStream stream = new ObjectOutputStream(new FileOutputStream(file));
        stream.writeObject(records);
        stream.flush();
        stream.close();
    }
    catch (IOException e){
        Log.e("MyApp","IO Exception: " + e);
    }

    writeSavedState();
}

writeSavedState() 是我的SP

public void readRecordsList() {
    String fileName = Environment.getExternalStorageDirectory().getPath() + "/records.dat";

    try {
        ObjectInputStream inputStream = new ObjectInputStream(getApplicationContext().openFileInput(fileName));
        adapter = (ArrayAdapter<Record>)inputStream.readObject();
        inputStream.close();
    }
    catch (Exception e){
        Log.e("MyApp" , "File Not Found: " + e);
    }
}

当我第一次打开应用程序时,我收到一条消息:

E/MyApp﹕ File Not Found: java.lang.IllegalArgumentException: File /storage/emulated/0/records.dat contains a path separator

然后当我将Record 添加到我的列表中时,我会收到以下消息:

E/MyApp﹕ IO Exception: java.io.IOException: open failed: EACCES (Permission denied)

我假设我收到第二条消息是因为第一条消息。这是我第一次在 Android 中使用 I/O,因此我们将不胜感激!

编辑

将权限添加到清单后,我现在只收到一个错误:

E/MyApp﹕ IO Exception: java.io.NotSerializableException: android.widget.ArrayAdapter

正如我所说,我的自定义对象是Parcelable,其余部分在我的MainActivity 中完成。我是否需要创建一个名为 Serializable 的新课程来构建我的 ArrayAdapter

【问题讨论】:

  • 你不需要使用getPath(),Environment.getExternalStorageDirectory()会返回外部存储目录的路径。您是否还在清单文件中设置了读/写权限?
  • 根据数据对象的复杂程度,你为什么不把所有的东西都写到共享首选项中。不仅仅是布尔值,而是实际数据
  • @akshay7692 实际上我还没有这样做,好电话。我会尝试,但我认为它不会修复我的第一条消息
  • @PattaFeuFeu 我想我可以这样做,但我认为这不会解决我的问题
  • @erik 我确实尝试过先这样做,但我的列表的大小可能会有所不同,并且为此使用共享首选项变得太复杂了

标签: android io android-file


【解决方案1】:

我建议以私有模式将记录保存在内部存储中,只能由您的应用程序访问。如果将其存储在外部存储中,则无法保证下次加载应用程序时它会可用。

另外,您应该保存记录对象数组而不是 ArrayAdapter 对象。

Parcel 和 Parcelable 非常快,但它的文档说你不能将它用于通用的序列化到存储,因为实现因 Android 的不同版本而异(即操作系统更新可能会破坏依赖它的应用程序) .所以在这种情况下使用 Serializable 而不是 Parcalable(来自this SO 线程)

您可以使用全局变量将数据从一个活动传递到另一个活动。您还可以在应用程序开始使用扩展 Applicaion 类的全局类时读取/写入记录。

你可以试试下面的代码,

public class GlobalClass extends Application {
public static Object objectToBePassed; // global variable
final static private RECORDS_FILENAME = "myRecords.txt"

@Override
    public void onCreate(){
        super.onCreate();
        readRecordsFromFile();  // read records when app starts
}

    public boolean writeRecordsToFile(ArrayList<Record> records){
                FileOutputStream fos;
                ObjectOutputStream oos=null;
                try{
                    fos = getApplicationContext().openFileOutput(RECORDS_FILENAME, Context.MODE_PRIVATE);
                    oos = new ObjectOutputStream(fos);   
                    oos.writeObject(records);
                    oos.close();
                    return true;
                }catch(Exception e){            
                    Log.e(getClassName(), "Cant save records"+e.getMessage());
                    return false;
                }
                finally{
                    if(oos!=null)
                        try{
                            oos.close();
                        }catch(Exception e){
                            Log.e(getClassName(), "Error while closing stream "+e.getMessage());
                        }
                }
            }

    private boolean readRecordsFromFile(){
            FileInputStream fin;
            ObjectInputStream ois=null;
            try{
                fin = getApplicationContext().openFileInput(RECORDS_FILENAME);
                ois = new ObjectInputStream(fin);   
                ArrayList<Record> records = (ArrayList<Record>) ois.readObject();
                ois.close();
                Log.v(getClassName(), "Records read successfully");
                return true;
                }catch(Exception e){
                    Log.e(getClassName(), "Cant read saved records"+e.getMessage());
                    return false;
                }
            finally{
                if(ois!=null)
                    try{
                        ois.close();
                    }catch(Exception e){
                        Log.e(getClassName(), "Error in closing stream while reading records"+e.getMessage());
                    }
            }
        }
}

所以要将任何对象从活动 A 传递到活动 B,请在活动 A 中使用以下代码,

Intent intent = new Intent(this,B.class);
GlobalClass.objectToBePassed = obj;
startActivity(intent);

在活动 B 中,

MyClass object = (MyClass) GlobalClass.objectToBePassed;

所以要传递一个 Record 类对象,请将 MyClass 替换为 Record。

【讨论】:

  • 这工作得更好一些,我在读/写时仍然收到一个错误,说Record 不可序列化。是否有一个简单的解决方法可以使其与Parcelable 一起使用?我正在使用它,因为我需要在 ActivitiesSerializable 之间传递数据,这不起作用
  • 我建议使用可序列化,因为不建议使用 parcelable(来自 developer.android.com/reference/android/os/Parcel.html)将数据存储在持久存储中。因此,您可以使用全局变量将对象从一个活动传递到另一个活动。我正在用这个解决方案更新我的答案。
猜你喜欢
  • 2013-08-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-26
  • 2015-05-15
相关资源
最近更新 更多