天天看点

Android数据存储的方式

1.Android常用的数据存储方式

  1. File存储
  2. SharedPreferences存储
  3. SQLite轻量型数据库
  4. ContentProvider 四大组件之一

2.File存储

最基本的一种数据存储方式,不对存储的内容进行任何的格式化处理,所有数据都是原封不动的保存到文件中,适合存储一些简单的文本数据或二进制数据。存储的数据位于

/data/data/< package name>/files/

目录下

Context类中提供了2种常用方法:

  1. openFileOutput(“fileName”,mode)开启文件输出流,从程序种写入到指定文件

    第一个参数代表文件名称,不能包含路径。第一个为文件的操作模式:

    Context.MODE_PRIVATE 默认操作模式,文件存在时覆盖文件的内容
    Context.MODE_APPEND 文件已存在,内容追加。不存在则自动创建新文件
    Context.MODE_WORLD_READABLE 允许其他程序进行读操作
    Context.MODE_WORLD_WRITEABLE 允许其他程序进行读写操作
               

后两种模式在4.2后已被废弃

  1. openFileInput(“fileName”)开启文件输入流,从文件种读取文件并读取到程序
  2. getDir(String name,int mode)在应用程序的数据文件夹下获取或创建name对应的子目录
  3. getFilesDir() 数据文件夹的绝对路径
  4. fileList() 数据文件夹下的全部文件
  5. deleteFile(String) 删除数据文件夹下指定文件

封装的工具类:

package com.wdl.crazyandroiddemo;

import android.content.Context;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

/**
 * author:   wdl
 * time: 2018/11/13 20:52
 * des:    File方式读写文件,工具类
 */
public class FileWRTool {

    /**
     * 输出流形式,来保存文件
     *
     * @param context  上下文
     * @param fileName 文件名
     * @param data     要保存的字符串
     *                 fileName 是要要生成的文件的文件名(data.txt)
     */
    public static void writeFile(Context context, String fileName, String data) {
        FileOutputStream outputStream;
        BufferedWriter bufferedWriter = null;
        try {
            outputStream = context.openFileOutput(fileName, Context.MODE_PRIVATE);
            bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
            bufferedWriter.write(data);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (bufferedWriter != null)
                try {
                    bufferedWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
    }

    /**
     * 输入流形式,来读取fileName文件
     *
     * @param context  上下文
     * @param fileName 文件名
     * @return 文件内容
     */
    public static String readFile(Context context, String fileName) {
        //字节输入流
        FileInputStream inputStream;
        //缓冲流
        BufferedReader bufferedReader = null;
        StringBuilder stringBuffer = new StringBuilder();
        try {
            inputStream = context.openFileInput(fileName);
            bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                stringBuffer.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return stringBuffer.toString();
    }
}
           

3.SharedPreferences存储

用于存储少量数据,数据格式较为简单,使用键值对进行存储。比如:应用程序的各种配置信息等。具有一定的缓存机制,并发读写可能会导致不可预知的结果。存储于

/data/data/< package name>/shared_prefs/name

格式:

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
    <string name="name">wdl</string>
    <long name="price" value="23" />
    <int name="age" value="23" />
    <boolean name="man" value="true" />
</map>
           

使用方法:

  • 第一步:获取实例

    SharedPreferences是一个接口,无法直接创建SharedPreferences实例。

    A:通过Context.getSharedPreferences(String name,int mode)获取实例Name:文件名 mode:

    1.Context.MODE_PRIVATE 只能被本应用读写

    2.Context.MODE_WORLD_READABLE 可被其他程序读,不可写

    3.Context.MODE_WORLD_WRITEABLE 可被其他程序读写 4.2后舍弃

    B:Activity类中的getPreferences(int mode)方法获取

    与第一种方法类似,以当前活动类名作为文件名称

    C:PreferenceManager类中的getDefaultSharedPreferences(Context context)方法获取使用当前应用程序包名作为前缀来命名文件

  • 第二步:获取SharedPreferences.Editor对象,Edit = sp.edit();
  • 第三步:edit.putXXX(key,value)添加 edit.remove(key) edit.clear()等
  • 第四步:edit.commit() edit.apply()

工具类:

package com.wdl.crazyandroiddemo;

import android.content.Context;
import android.content.SharedPreferences;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;


/**
 * author:   wdl
 * time: 2018/11/14 14:46
 * des:    SharedPreferences工具类
 */
@SuppressWarnings("unused")
public class AppSharedUtil {
    //文件名
    private static final String FILE_NAME = "share_pref";

    /**
     * 根据传入的object按照对应方法写入文件
     *
     * @param context 上下文
     * @param key     key
     * @param object  值
     */
    public static void put(Context context, String key, Object object) {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        if (object instanceof Integer) {
            editor.putInt(key, (Integer) object);
        } else if (object instanceof String) {
            editor.putString(key, (String) object);
        } else if (object instanceof Float) {
            editor.putFloat(key, (Float) object);
        } else if (object instanceof Long) {
            editor.putLong(key, (Long) object);
        } else if (object instanceof Boolean) {
            editor.putBoolean(key, (Boolean) object);
        }
        SharedPreferencesCompat.apply(editor);

    }

    /**
     * 根据key与defaultValue获取对应的值
     *
     * @param context      上下文
     * @param key          key
     * @param defaultValue 默认值
     * @return Object
     */
    public static Object get(Context context, String key, Object defaultValue) {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
        if (defaultValue instanceof String) {
            return sp.getString(key, (String) defaultValue);
        } else if (defaultValue instanceof Integer) {
            return sp.getInt(key, (Integer) defaultValue);
        } else if (defaultValue instanceof Boolean) {
            return sp.getBoolean(key, (Boolean) defaultValue);
        } else if (defaultValue instanceof Float) {
            return sp.getFloat(key, (Float) defaultValue);
        } else if (defaultValue instanceof Long) {
            return sp.getLong(key, (Long) defaultValue);
        }
        return null;
    }

    /**
     * 删除对应key的值
     *
     * @param context 上下文
     * @param key     key
     */
    public static void remove(Context context, String key) {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        editor.remove(key);
        SharedPreferencesCompat.apply(editor);
    }

    /**
     * 是否包含指定key
     *
     * @param context 上下文
     * @param key     key
     * @return boolean
     */
    public static boolean contains(Context context, String key) {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
        return sp.contains(key);
    }

    /**
     * 清空
     *
     * @param context Context
     */
    public static void clear(Context context) {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        editor.clear();
        SharedPreferencesCompat.apply(editor);
    }

    /**
     * 获取所有
     *
     * @param context Context
     * @return map
     */
    public static Map<String, ?> getAll(Context context) {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
        return sp.getAll();
    }


    /**
     *
     */
    private static class SharedPreferencesCompat {

        private static final Method applyMethod = findApplyMethod();

        /**
         * 反射查找apply方法
         *
         * @return Method
         */
        private static Method findApplyMethod() {
            try {
                Class<?> clazz = SharedPreferences.Editor.class;
                return clazz.getMethod("apply");
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
            return null;
        }

        /**
         * apply存在则使用apply,否则使用commit
         *
         * @param editor SharedPreferences.Editor
         */
        private static void apply(SharedPreferences.Editor editor) {
            if (applyMethod != null) {
                try {
                    applyMethod.invoke(editor);
                    return;
                } catch (IllegalAccessException | InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
            editor.commit();
        }
    }
}
           

因为commit方法是同步的,并且我们很多时候的commit操作都是UI线程中,毕竟是IO操作,尽可能异步;

所以我们使用apply进行替代,apply异步的进行写入。

4.SQLite轻量型数据库

Android系统内置了数据库,SQLite是一款轻量级的关系型数据库,运算速度快,占用内存小。SQLite支持标准的SQL语法,遵循了数据库的ACID事务。SQLite数据库只是一个文件。

Android提供SQLiteDatabase代表一个数据库,提供以下静态方法打开对应的数据库:

  1. openDatabase(String path,SQLiteDatabase.CursorFactory factory,int flags) 打开path文件所代表的SQLite数据库
  2. openOrCreateDatabase(File file,CursorFactory factory)打开或(如果不存在)创建file文件所代表的SQLite数据库
  3. openOrCreateDatabase(String path,SQLiteDatabase.CursorFactory factory)打开或(如果不存在)创建path文件所代表的SQLite数据库

获取SQLiteDatabase对象后,调用如下方法来操作数据库:

4.

execSQL(String sql,Object[] bindArgs)

执行带占位符的SQL语句

5.

execSQL(String sql)

执行SQL语句

6.

beginTransaction()

开始事务

7.

endTransaction()

结束事务

8.

rawQuery(String sql,String[] selectionArgs)

执行带占位符的SQL查询

9.

insert(String table,String nullColumnHack,ContentValues values)

向指定表插入数据;nullColumnHack代表强行插入null值的数据列名

10.

update(String table,ContentValues values,String whereClause,String[] whereArgs)

更新表中的特定数据;whereClause限定的条件;whereArgs参数值

11.

delete(String table,String whereClause,String[] whereArgs)

删除指定表中的特定数据

12.

Cursor query(boolean distinct,String table,String[] columns,String whereClause,String[] whereArgs,String groupBy,String having,String orderBy,String limit)

指定数据表进行查询,distinct是否重复;table表名;columns列名;whereClause 条件语句(带占位符);whereArgs参数;groupBy分组;having约束;orderBy排序;limit限制条数

Android数据存储的方式

查询方法返回的都是一个Cursor,相当于JDBC的ResultSet,提供如下方法:

  1. moveToFirst() 将指针移到第一行,成功返回true
  2. moveToLast() 将指针移到最后一行,成功返回true
  3. moveToNext()将指针移到下一行,成功返回true
  4. moveToPosition(int position)将指针移到指定行
  5. moveToPrevious() 将指针移到上一行,成功返回true

Sqlite3常用指令:

  1. .databases 查看当前数据库
  2. .tables 查看所有表
  3. .help 查看支持的命令

    特点:内部只支持NULL,INTEGER,REAL,TEXT,BLOB,实际可接受varchar,char,decimal等数据类型,运算或保存时将它们转换为以上的类型。允许把各种类型的数据保存到任何类型字段中.

事务

  1. beginTransaction() 开始事务
  2. endTransaction() 结束事务
  3. inTransaction() 判断是否处于事务环境中

    注意:

    当程序执行endTransaction() 方法时会结束事务,那到底是提交事务还是回滚事务,取决于SQLiteDatabase是否调用了setTransactionSuccessful()设置事务标志,如果设置则提交,否则则回滚事务。

SQLiteOpenHleper类

项目中通常继承SQLiteOpenHleper类开发子类,通过如下方法:

  1. synchronized SQLiteDatabase getReadableDatabase() 以读写的方式打开对应数据库,磁盘满 只读
  2. synchronized SQLiteDatabase getWriteableDatabase()以写的方式打开对应数据库,磁盘满会出错
  3. onCreate(SQLiteDatabase db) 第一次创建时回调
  4. OnUpgrade(SQLiteDatabase db,int old,int new)版本更新回调

升级时,只能进行列的插入不能进行列的删除,因此升级时先进行数据转储,清空表重新创建,写入数据。

使用:

MySQLiteHelper 类

package com.wdl.crazyandroiddemo

import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper

/**
 * author:   wdl
 * time: 2018/11/13 16:18
 * des:    SQLite helper类
 */
class  MySQLiteHelper constructor(context: Context?, name: String?, factory: SQLiteDatabase.CursorFactory?, version: Int):
        SQLiteOpenHelper(context, name, factory, version) {
    private val CREATE_STUDENT = "create table student(id integer primary key autoincrement,name text,age integer)"
    private val CREATE_BOOK = "create table book(id integer primary key autoincrement,name text,price integer)"
    //第一次创建才会执行onCreate,无法完成升级
    override fun onCreate(db: SQLiteDatabase?) {
        db?.execSQL(CREATE_STUDENT)
        db?.execSQL(CREATE_BOOK)
    }

    override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
        when(oldVersion){
            1 -> db?.execSQL(CREATE_BOOK)
            2 -> db?.execSQL("alter table book add column publishdate integer")
        }
    }

}
           

demo:

package com.wdl.crazyandroiddemo

import android.annotation.SuppressLint
import android.content.ContentValues
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import kotlinx.android.synthetic.main.activity_sqlite.*

class SQLiteActivity : AppCompatActivity() {

    @SuppressLint("Recycle")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_sqlite)
        val helper = MySQLiteHelper(this, "demo.db", null, 3)
        val db = helper.writableDatabase
        mSave.setOnClickListener {
            val name = mName.text.toString()
            val age = mAge.text.toString().toInt()
            val contentValue = ContentValues()
            contentValue.put("name", name)
            contentValue.put("age", age)
            //插入
            val index = db.insert("student", null, contentValue).toInt()
            if (index == -1) Toast.makeText(this, "插入失败", Toast.LENGTH_SHORT).show()
        }
        mQuery.setOnClickListener {
            //查询
            val cursor = db.query("student", null, "name like ?", arrayOf("lijie"), null, null, null)
            while (cursor.moveToNext()) {
                val name = cursor.getString(cursor.getColumnIndexOrThrow("name"))
                val age = cursor.getInt(cursor.getColumnIndexOrThrow("age"))
                Log.e("wdl", "name = $name,age = $age")
            }
            cursor.close()
        }
        mUpdate.setOnClickListener {
            val contentValue = ContentValues()
            val age = mAge1.text.toString().toInt()
            contentValue.put("age", age)
            //更新
            val index = db.update("student", contentValue, "name = ?", arrayOf("lijie"))
            if (index != -1) Toast.makeText(this, "更新成功", Toast.LENGTH_SHORT).show()
        }

        mDel.setOnClickListener {
            val index = db.delete("student","name = ?", arrayOf("wudelin"))
            if (index != -1) Toast.makeText(this, "删除成功", Toast.LENGTH_SHORT).show()
        }
    }
}
           

val cursor = db.query(“student”, null, “name like ?”, arrayOf(“lijie”), null, null, null)

while (cursor.moveToNext()) {

val name = cursor.getString(cursor.getColumnIndexOrThrow(“name”))

val age = cursor.getInt(cursor.getColumnIndexOrThrow(“age”))

Log.e(“wdl”, “name = $name,age = $age”)

}

cursor.close()

5.ContentProvider

**

见(https://blog.csdn.net/qq_34341338/article/details/84191392)

**