Android数据存储

本文主要讲述各种存储方式,基本都是常用的。在此进行记录。

一、存储

1.概述

Android提供了多种方式来持久化数据,以及提供对数据的安全保护。主要包括:

1)SharePreference:用于保存键值对;

2)内部存储:在内部存储器中保存私有数据;

3)外部存储:在共享外部存储中存储公共数据;

4)SQLite数据库:在私有数据库中存储结构化数据;

5)网络连接:在网络服务器中存储。

同时,Android提供了ContentProvider来对外(其他应用)公开数据,其可以根据需求设置读写访问权限。

2.SharePreference

SharedPreferences类提供一个通用框架,以键值对的形式保存数据。可以保存任何原始数据:布尔型、浮点型、整形、长整型以及字符串。

获取SharedPreferences,有两种方法:

getSharedPreferences(),用于根据名称获取SharedPreferences文件;

getPreferences(),用于Activity的首选项文件,由于这将是用于Activity的唯一首选项文件,因此无需提供名称。

写入步骤:

1)调用edit()获取SharedPreferences.Editor;

2)使用putString()等方法添加值;

3)使用commit()提交新值。

读取则使用getString()等方法。

public class Calc extends Activity {
    public static final String PREFS_NAME = "MyPrefsFile";

    @Override
    protected void onCreate(Bundle state){
       super.onCreate(state);
       . . .

       SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
       boolean silent = settings.getBoolean("silentMode", false);
       setSilent(silent);
    }

    @Override
    protected void onStop(){
       super.onStop();

      // 通过Editor对象编辑
      SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
      SharedPreferences.Editor editor = settings.edit();
      editor.putBoolean("silentMode", mSilentMode);

      // 提交编辑
      editor.commit();
    }
}

3.使用内部存储

1)特点

a)永远都可获取;

b)文件只有本应用可以获取;

c)应用卸载之后会清空数据;

d)不需要申明访问权限;

应用一般默认安装在内部存储中,但可以在manifest中指定android:installLocation属性指定安装位置(app过大则可以保存到外部)。

2)写入到内部存储

a)getFilesDir()获取app内部存储目录;
b)getCacheDir()获取app内部缓存目录,但是此目录要限制文件大小并且妥善管理,因为系统内存不足就会删除此文件夹的文件;
c)新建文件:File file = new File(context.getFilesDir(), filename);
d)写入:
String filename = "myfile";
String string = "Hello world!";
FileOutputStream outputStream;
try {
    outputStream = openFileOutput(filename, Context.MODE_PRIVATE);
    outputStream.write(string.getBytes());
    outputStream.close();
} catch (Exception e) {
    e.printStackTrace();
}
e)缓存文件:
public File getTempFile(Context context, String url) {
    File file;
    try {
        String fileName = Uri.parse(url).getLastPathSegment();//根据文件路径提取文件名
        file = File.createTempFile(fileName, null, context.getCacheDir());
    } catch (IOException e) {
        // Error while creating file
    }
    return file;
}
注意,别的应用如果想访问内部存储的文件,要有权限,且还要知道应用包名和文件名。

其中,MODE_PRIVATE会创建文件(或替换),并设为应用私有。还有MODE_APPEND、MODE_WORLD_READABLE、MODE_WORLD_WRITEABLE。

API17及之后,MODE_WORLD_READABLE和MODE_WORLD_WRITEABLE已被弃用。Android7.0之后,使用它们会引发SecurityException异常。且7.0之后,无法根据文件名共享私有文件,如果使用file:// URI会引发FileUriExposedException。可以用FileProvider与FLAG_GRANT_URI_READ_URI_PERMISSION配合使用。了解FileProvider使用,请参考我另一篇文章FileProvider使用详解

3)从内部存储读取文件:

a)openFileInput()打开FileInputStream;

b)read()读取文件字节;

c)close()关闭输入流。

注意:如果要读取应用中的静态文件,要在项目res/raw/目录中保存文件。可以使用openRawResource()打开并传入R.raw.filename。返回一个InputStream,用于读取文件。但是不能写入到原始文件。

4.外部存储

1)特点

a)安装了外部存储器才可以获取;
b)保存在外部存储的文件所有人都可以读取;
c)卸载时,只有保存在getExternalFileDir()目录的文件才会被删除
d)需要申明访问权限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> 必须要有,此权限默认包含读权限。
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />目前不需要,但是文档中建议使用,防止将来必须申明此权限。

注意:Android4.4开始,如果仅读写应用的私有文件,则不需要这些权限。

2)检查外存储器可用性

/* 检查外存储器是否可用 */
public boolean isExternalStorageWritable() {
    String state = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED.equals(state)) {
        return true;
    }
    return false;
}

/* 检测外存储器是否可读 */
public boolean isExternalStorageReadable() {
    String state = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED.equals(state) ||
        Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
        return true;
    }
    return false;
}

3)保存可共享文件

使用getExternalStoragePublicDirectory(),并传递需要的目录参数,会打开指定目录,用于保存照片等不需要卸载时删除的文件。

public File getAlbumStorageDir(String albumName) {
    // 获取pictures目录
    File file = new File(Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_PICTURES), albumName);
    if (!file.mkdirs()) {
        Log.e(LOG_TAG, "Directory not created");
    }
    return file;
}

4)保存私有文件

通过getExternalFilesDir(),并传入DIRECTORY_PICTURES等类型。也可以是null,访问私有根目录。代码就不写了,与上面类似。

4.4之后读写私有目录不需要在manifest中申明权限,因此,可以使用maxSdkVersion指定较低版本需要此权限:

<manifest ...>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
                     android:maxSdkVersion="18" />
    ...
</manifest>

注:在卸载时,此目录内容将被删除。因此,如果不想删除文件,需要保存在公共目录中。

注意:上面两个获取文件夹的方法,所需要的参数可以是null(对应/storage/emulated/0/null(或.../Android/data/com.package.app/files))的,但是一般不建议这么用,需要指定其类型,如DIRECTORY_PICTRUES(对应/storage/emulated/0/Pictures(或.../Android/data/com.package.app/files/Pictures)等。而且,该参数还有些其他用途,比如,把铃声文件放在DIRECTORY_RINGTONS,则会默认为铃声而不是音乐文件。

5)保存缓存文件

通过getExternalCahceDir(),访问外部存储的缓存文件。用户卸载应用,文件将被删除。代码与上面类似。

6)总结或注意点

a)需要根据可用空间大小来确定文件是否可以写到存储器中,防止IO异常,getFreeSpace()以及getTotalSpace()获取可用和最大空间。但是文档说不一定能写getFreeSpace()获得的大小,最好是小几MB或者已存储不到90%时可以保存;

b)文件删除

file.delete();内部存储有个专用删除方法:context.deleteFile(file);

c)卸载时,内部存储以及外部存储中getExternalFilesDir()目录下的文件会被删除;

d)及时删除getCacheDir()中不需要的文件;

5.使用数据库

官方推荐继承SqLiteOpenHelper,并重写onCreate()方法创建SQLite数据库。在onCreate()方法中创建数据库表。

public class DictionaryOpenHelper extends SQLiteOpenHelper {

    private static final int DATABASE_VERSION = 2;
    private static final String DICTIONARY_TABLE_NAME = "dictionary";
    private static final String DICTIONARY_TABLE_CREATE =
                "CREATE TABLE " + DICTIONARY_TABLE_NAME + " (" +
                KEY_WORD + " TEXT, " +
                KEY_DEFINITION + " TEXT);";

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

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(DICTIONARY_TABLE_CREATE);
    }
}

之后,可以创建其实例,读写操作时,要传入getWritableDatabase()和getReadableDatabase()。这些都不赘述了。

注意,由于是数据库操作,有可能会消耗很长时间,因此要在工作线程调用getWriteableDatabase()或getReadabledDatabase(),比如AsynTask或IntentService中。

一般,我们开发中,都是使用开源的ORM框架,主流的有ORMLite、greenDao等等。

数据库调试可以使用sqlite3工具,在Android SDK中有。

6.使用网络连接

这个也不多说了





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值