Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 數據存儲詳解(SharedPreferences, 文件, Sqlite, ContentProvider)

Android 數據存儲詳解(SharedPreferences, 文件, Sqlite, ContentProvider)

編輯:關於Android編程

文章大部分內容來自 < < Android開發全程實錄 > >, 希望大家能看看原書

SharedPreferences

sharepreferences是Android中最輕量級的數據存儲. 原理相信很多人也很清楚, 這裡簡單再介紹以下. 系統提供了SharedPreferences這個類, 所有用這個類存儲的內容都會放在 data/data/< package name >/shares_prefs/… 目錄下以xml文件的形式存儲. xml文件中存儲的都是鍵值對形式的內容. 一般可以存儲java的簡單類型(boolean, int, float, long, string, set< string > 等). 通常只使用SharedPreferences存儲一些簡單配置信息.

SharedPreferences存儲基本流程

獲取SharedPreferences 對象 獲取Editor 寫入數據 editor.commit()
Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    SharedPreferences sp = getSharedPreferences("user_info", MODE_PRIVATE);
    SharedPreferences.Editor editor = sp.edit();
    editor.putString("username", "jake");
    editor.putInt("age", 20);
    editor.putBoolean("islogin", true);
    editor.commit();
}

查看結果如圖:
這裡寫圖片描述
導出 user_info.xml, 裡面的內容是:


    jake
    
    

獲取SharedPreferences對象有兩種方法:
1. context.getSharedPreferences("user_info", MODE_PRIVATE);
2. context.SharedPreferences sp = getPreferences(MODE_PRIVATE);

使用 getPreferences(mode)會直接使用 類名.xml 作為存儲文件名, 注意只有Activity的context能使用 getPreferences(mode), 所以可以輕松的區分出每個activity保存的配置文件.
這裡寫圖片描述

從SharedPreferences讀取值

讀取流程:
1. 獲取到SharedPreferences對象
2. 使用get方法讀取指定值, 這裡注意, 如果SharedPreferences沒有存儲指定鍵的值, 可以使用默認值代替返回值.

SharedPreferences sp_read = getSharedPreferences("user_info", MODE_PRIVATE);
String username = sp_read.getString("username", "default_string");
int age = sp_read.getInt("age", 0);
boolean islogin = sp_read.getBoolean("islogin", false);
String not_exist = sp_read.getString("not_exist", null);
Log.e(TAG, "username:" + username + " age:" + age + " islogin:" + islogin + " not_exits:" + not_exist);

打印結果:

 E/MainActivity: username:jake age:20 islogin:true not_exits:null

SharedPreferences操作模式

這裡寫圖片描述<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxoMyBpZD0="封裝sharedpreferences工具類">封裝SharedPreferences工具類

該工具類來自 hyman大神 的工具類, 很好用. 大家可自行嘗試引入到自己項目中
SPUtils.java

package com.zhy.utils;

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

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

public class SPUtils
{
    public SPUtils()
    {
        /* cannot be instantiated */
        throw new UnsupportedOperationException("cannot be instantiated");
    }

    /**
     * 保存在手機裡面的文件名
     */
    public static final String FILE_NAME = "share_data";

    /**
     * 保存數據的方法,我們需要拿到保存數據的具體類型,然後根據類型調用不同的保存方法
     * 
     * @param context
     * @param 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 String)
        {
            editor.putString(key, (String) object);
        } else if (object instanceof Integer)
        {
            editor.putInt(key, (Integer) object);
        } else if (object instanceof Boolean)
        {
            editor.putBoolean(key, (Boolean) object);
        } else if (object instanceof Float)
        {
            editor.putFloat(key, (Float) object);
        } else if (object instanceof Long)
        {
            editor.putLong(key, (Long) object);
        } else
        {
            editor.putString(key, object.toString());
        }

        SharedPreferencesCompat.apply(editor);
    }

    /**
     * 得到保存數據的方法,我們根據默認值得到保存的數據的具體類型,然後調用相對於的方法獲取值
     * 
     * @param context
     * @param key
     * @param defaultObject
     * @return
     */
    public static Object get(Context context, String key, Object defaultObject)
    {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
                Context.MODE_PRIVATE);

        if (defaultObject instanceof String)
        {
            return sp.getString(key, (String) defaultObject);
        } else if (defaultObject instanceof Integer)
        {
            return sp.getInt(key, (Integer) defaultObject);
        } else if (defaultObject instanceof Boolean)
        {
            return sp.getBoolean(key, (Boolean) defaultObject);
        } else if (defaultObject instanceof Float)
        {
            return sp.getFloat(key, (Float) defaultObject);
        } else if (defaultObject instanceof Long)
        {
            return sp.getLong(key, (Long) defaultObject);
        }

        return null;
    }

    /**
     * 移除某個key值已經對應的值
     * 
     * @param context
     * @param 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);
    }

    /**
     * 清除所有數據
     * 
     * @param 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);
    }

    /**
     * 查詢某個key是否已經存在
     * 
     * @param context
     * @param key
     * @return
     */
    public static boolean contains(Context context, String key)
    {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
                Context.MODE_PRIVATE);
        return sp.contains(key);
    }

    /**
     * 返回所有的鍵值對
     * 
     * @param context
     * @return
     */
    public static Map getAll(Context context)
    {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
                Context.MODE_PRIVATE);
        return sp.getAll();
    }

    /**
     * 創建一個解決SharedPreferencesCompat.apply方法的一個兼容類
     * 
     * @author zhy
     * 
     */
    private static class SharedPreferencesCompat
    {
        private static final Method sApplyMethod = findApplyMethod();

        /**
         * 反射查找apply的方法
         * 
         * @return
         */
        @SuppressWarnings({ "unchecked", "rawtypes" })
        private static Method findApplyMethod()
        {
            try
            {
                Class clz = SharedPreferences.Editor.class;
                return clz.getMethod("apply");
            } catch (NoSuchMethodException e)
            {
            }

            return null;
        }

        /**
         * 如果找到則使用apply執行,否則使用commit
         * 
         * @param editor
         */
        public static void apply(SharedPreferences.Editor editor)
        {
            try
            {
                if (sApplyMethod != null)
                {
                    sApplyMethod.invoke(editor);
                    return;
                }
            } catch (IllegalArgumentException e)
            {
            } catch (IllegalAccessException e)
            {
            } catch (InvocationTargetException e)
            {
            }
            editor.commit();
        }
    }

}

文件

Android文件操作就是使用Java的API, 這裡需要注意的是 Android中的路徑以及權限. Android中文件操作一般都用來存一些二進制文件和簡單的文本, 例如圖片, 視頻等, 一般不用來存配置信息.

幾個典型的路徑

使用 context.this.openFileOutput("1.txt", MODE_PRIVATE);會將文件存儲在 /data/data/< package name >/ files
這裡寫圖片描述 context.getFilesDir(); 保存在 /data/user/0/< package name >/filesEnvironment.getExternalStorageDirectory(); 保存在 /storage/emulated/0

注意: 這3個是比較典型的, 其中通過 context 獲取的路徑下的內容在 APP被卸載的時候都會被刪除, 存在外接存儲設備上的則不會被刪除.

大家可以寫代碼測試:

        try {
            FileOutputStream fos = this.openFileOutput("1.txt", MODE_PRIVATE);
            OutputStreamWriter osw = new OutputStreamWriter(fos);
            String test = "test";
            osw.write(test, 0, test.length());
            fos.close();

            File file2 = this.getFilesDir();
            File file3 = this.getCacheDir();
            File file4 = Environment.getExternalStorageDirectory();
            File file5 = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);

            Log.e(TAG, "file2:" + file2);
            Log.e(TAG, "file3:" + file3);
            Log.e(TAG, "file4:" + file4);
            Log.e(TAG, "file5:" + file5);

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

打印結果

file1:/data/data/< package name >/files // 這個看上面截圖
file2:/data/user/0/org.yxm.filesimple/files
file3:/data/user/0/org.yxm.filesimple/cache
file4:/storage/emulated/0
file5:/storage/emulated/0/Pictures

文件讀寫權限:

如果要將文件寫入外部存儲設備, 需要一定的權限.


Sqlite

簡介

這裡寫圖片描述

管理工具

SQLite Database Browser Sqlite Expert Professional (推薦) SqliteAdmin …

使用方法

使用Sqlite數據庫和使用普通數據庫一樣, 都是以下幾個步驟:
1. 創建, 刪除數據庫
2. 增加數據
3. 刪除數據
4. 修改數據
5. 查詢數據

Android數據庫核心類是: SQLiteDatabase, 為了讓Android中的數據庫更加好使用, Android還提供了: SQLiteOpenHelper來幫助管理數據庫. 這裡我只簡單介紹數據庫的基本操作, 後面再寫一篇專門介紹 SQLiteOpenHelper的使用, 和對數據庫的封裝.

創建數據庫

使用 context.openOrCreateDatabase(...), 數據庫文件會默認保存到 /data/data/< package name >/ databases/…
SQLiteDatabase db = this.openOrCreateDatabase("mySqliteDb.db", MODE_PRIVATE, null);

這裡寫圖片描述
2. 使用SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(getFilesDir() + "/mySqliteDb.db", null); 可以將數據庫文件放在自定義目錄中, 但是經過實驗, 不能放在外部存儲中, 會包下面的錯誤:

Caused by: android.database.sqlite.SQLiteCantOpenDatabaseException: unknown error (code 14): Could not open database

所以建議還是建議使用方法一, 而且數據庫本來就不應該被其他應用訪問, 所以應該放在最隱私的位置. 如果想為其他應用提供數據, 可以使用後面介紹的 ContentProvider

創建table

使用 db.execSQL(createTableStr);來創建表, 例如:

String createTableStr = "CREATE TABLE IF NOT EXISTS account (" +
        "_id INTEGER PRIMARY KEY," +
        "username VARCHAR(60)," +
        "password VARCHAR(60)" +
        ");";
db.execSQL(createTableStr);

插入數據

當然可以直接使用原生sql語句, 使用 db.execSQL(insertStr); 執行插入, 但是Android為我們封裝了鍵值對的map, 只需要構造鍵值對 ContentValues , 然後調用 insert 即可插入.

ContentValues values = new ContentValues();
values.put("username","yxm");
values.put("password", "123");
db.insert("account", null, values);

刪除數據

db.delete("account", "_id>?", new String[]{"5"});

更新數據

ContentValues values = new ContentValues();
values.put("username", "mxy");
values.put("password", "321");
db.update("account", values, "_id



查詢數據

Cursor cur = db.query("account", null, null, null, null, null, null);
if (cur.moveToFirst()) {
    while (!cur.isLast()) {
        int id = cur.getInt(0);
        String username = cur.getString(1);
        String password = cur.getString(2);
        Log.e(TAG, "id:" + id + " username:" + username + " password:" + password);
        cur.moveToNext();
    }
}

關閉數據庫

db.close();

當然Adnroid還封裝了很多其他關於 SQLiteDatabase 的方法, 大家自行探索, 都很easy.

ContentProvider

我們可以將ContentProvider 看做Android中一個應用向其他應用公開數據的接口, 做數據共享. Android系統本身就有一些 ContentProvider, 例如: Contacts, Browser, CallLog, Settrings 等. 當然我們也可以自己創建ContentProvider來向其他程序提供數據.

創建自己的ContentProvider

繼承ContentProvider 聲明 CONTENT_URI, 實現 UriMatcher 在 AndroidManifest.xml 中注冊

繼承ContentProvider

這裡寫圖片描述
這裡寫圖片描述
在抽象方法中我們發現一個重要的類: Uri, ContentProvider通過 Uri定位資源然後共享出來, Uri需要符合一定的格式, 來看看系統聯系人的格式:

這裡寫圖片描述

這裡寫圖片描述

定義 CONTENT_URI, 實現UriMatcher

public static final Uri CONTENT_URI = Uri.parse("content://org.yxm.sqlitesimple");

在 AndroidManifest.xml 中注冊

感覺繼續寫下去太長了, 下次寫一篇新的放在最後

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved