Android 數(shù)據(jù)存儲(chǔ)與訪問(wèn)之——SharedPreferences保存用戶偏好參數(shù)

2023-03-31 13:47 更新

本節(jié)引言:

本節(jié)給大家介紹的是第二種存儲(chǔ)用戶數(shù)據(jù)的方式,使用SharedPreferences(保存用戶偏好參數(shù))保存數(shù)據(jù), 當(dāng)我們的應(yīng)用想要保存用戶的一些偏好參數(shù),比如是否自動(dòng)登陸,是否記住賬號(hào)密碼,是否在Wifi下才能 聯(lián)網(wǎng)等相關(guān)信息,如果使用數(shù)據(jù)庫(kù)的話,顯得有點(diǎn)大材小用了!我們把上面這些配置信息稱為用戶的偏好 設(shè)置,就是用戶偏好的設(shè)置,而這些配置信息通常是保存在特定的文件中!比如windows使用ini文件, 而J2SE中使用properties屬性文件與xml文件來(lái)保存軟件的配置信息;而在Android中我們通常使用 一個(gè)輕量級(jí)的存儲(chǔ)類——SharedPreferences來(lái)保存用戶偏好的參數(shù)!SharedPreferences也是使用xml文件, 然后類似于Map集合,使用鍵-值的形式來(lái)存儲(chǔ)數(shù)據(jù);我們只需要調(diào)用SharedPreferences的getXxx(name), 就可以根據(jù)鍵獲得對(duì)應(yīng)的值!使用起來(lái)很方便!


1.SharedPreferences使用示例:

使用流程圖

實(shí)現(xiàn)代碼示例

運(yùn)行效果圖

流程是輸入賬號(hào)密碼后點(diǎn)擊登錄,將信息保存到SharedPreference文件中, 然后重啟app,看到數(shù)據(jù)已經(jīng)顯示在文本框中了

另外保存后,我們可以在File Expoler打開(kāi)data/data/可以看到在shared_prefs目錄下 生成了一個(gè)xml文件(因?yàn)镹5沒(méi)root,這里找了以前的效果圖):

點(diǎn)擊導(dǎo)出到桌面可以看到里面的內(nèi)容:

代碼實(shí)現(xiàn)

布局文件activity_main.xml的編寫(xiě):

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MyActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="用戶登陸" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="請(qǐng)輸入用戶名" />

    <EditText
        android:id="@+id/editname"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="用戶名" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="請(qǐng)輸入密碼" />

    <EditText
        android:id="@+id/editpasswd"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="密碼"
        android:inputType="textPassword" />

    <Button
        android:id="@+id/btnlogin"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="登錄" />
</LinearLayout>

編寫(xiě)簡(jiǎn)單的SP工具類:SharedHelper.java

/**
 * Created by Jay on 2015/9/2 0002.
 */
public class SharedHelper {

    private Context mContext;

    public SharedHelper() {
    }

    public SharedHelper(Context mContext) {
        this.mContext = mContext;
    }

    //定義一個(gè)保存數(shù)據(jù)的方法
    public void save(String username, String passwd) {
        SharedPreferences sp = mContext.getSharedPreferences("mysp", Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        editor.putString("username", username);
        editor.putString("passwd", passwd);
        editor.commit();
        Toast.makeText(mContext, "信息已寫(xiě)入SharedPreference中", Toast.LENGTH_SHORT).show();
    }

    //定義一個(gè)讀取SP文件的方法
    public Map<String, String> read() {
        Map<String, String> data = new HashMap<String, String>();
        SharedPreferences sp = mContext.getSharedPreferences("mysp", Context.MODE_PRIVATE);
        data.put("username", sp.getString("username", ""));
        data.put("passwd", sp.getString("passwd", ""));
        return data;
    }
}

最后是MainActivity.java實(shí)現(xiàn)相關(guān)邏輯:

public class MainActivity extends AppCompatActivity {

    private EditText editname;
    private EditText editpasswd;
    private Button btnlogin;
    private String strname;
    private String strpasswd;
    private SharedHelper sh;
    private Context mContext;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mContext = getApplicationContext();
        sh = new SharedHelper(mContext);
        bindViews();
    }

    private void bindViews() {
        editname = (EditText)findViewById(R.id.editname);
        editpasswd = (EditText)findViewById(R.id.editpasswd);
        btnlogin = (Button)findViewById(R.id.btnlogin);
        btnlogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                strname = editname.getText().toString();
                strpasswd = editpasswd.getText().toString();
                sh.save(strname,strpasswd);
            }
        });
    }

    @Override
    protected void onStart() {
        super.onStart();
        Map<String,String> data = sh.read();
        editname.setText(data.get("username"));
        editpasswd.setText(data.get("passwd"));
    }
}

2.讀取其他應(yīng)用的SharedPreferences

核心: 獲得其他app的Context,而這個(gè)Context代表訪問(wèn)該app的全局信息的接口,而決定應(yīng)用的唯一標(biāo)識(shí) 是應(yīng)用的包名,所以我們可以通過(guò)應(yīng)用包名獲得對(duì)應(yīng)app的Context 另外有一點(diǎn)要注意的是:其他應(yīng)用的SP文件是否能被讀寫(xiě)的前提就是SP文件是否指定了可讀或者 可寫(xiě)的權(quán)限,我們上面創(chuàng)建的是MODE_PRIVATE的就不可以了~所以說(shuō)你像讀別人的SP里的數(shù)據(jù), 很難,另外,一些關(guān)鍵的信息,比如密碼保存到SP里,一般都是會(huì)做加密的,所以只能自己寫(xiě)自己玩~ 等下會(huì)講下常用的MD5加密方法!

實(shí)現(xiàn)流程圖

代碼示例:

運(yùn)行效果圖

代碼實(shí)現(xiàn)

我們讀取SP的操作放在MainActivity.java中完成,點(diǎn)擊按鈕后讀取SP,并通過(guò)Toast顯示出來(lái):

public class MainActivity extends AppCompatActivity {

    private Context othercontext;
    private SharedPreferences sp;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button btnshow = (Button) findViewById(R.id.btnshow);
        btnshow.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //獲得第一個(gè)應(yīng)用的包名,從而獲得對(duì)應(yīng)的Context,需要對(duì)異常進(jìn)行捕獲
                try {
                    othercontext = createPackageContext("com.jay.sharedpreferencedemo", Context.CONTEXT_IGNORE_SECURITY);
                } catch (PackageManager.NameNotFoundException e) {
                    e.printStackTrace();
                }
                //根據(jù)Context取得對(duì)應(yīng)的SharedPreferences
                sp = othercontext.getSharedPreferences("mysp", Context.MODE_WORLD_READABLE);
                String name = sp.getString("username", "");
                String passwd = sp.getString("passwd", "");
                Toast.makeText(getApplicationContext(), "Demo1的SharedPreference存的\n用戶名為:" + name + "\n密碼為:" + passwd, Toast.LENGTH_SHORT).show();
            }
        });
    }
}

3.使用MD5對(duì)SharedPreference的重要數(shù)據(jù)進(jìn)行加密

嘿嘿,上面我們這樣直接把賬號(hào)密碼保存到sp里,如果沒(méi)root的手機(jī),別的應(yīng)用倒無(wú)法訪問(wèn)手機(jī), 如果root了,然后數(shù)據(jù)給其他應(yīng)用獲取到,然后造成了一些后果,這...就不怪我們了,哈哈, 誰(shuí)叫你root了~,這鍋我們不背,的確是這樣!但是作為一名有責(zé)任心的APP開(kāi)發(fā)人員,我們總不能 這樣是吧,我們可以使用一些加密算法對(duì)用戶密碼進(jìn)行加密,另外我們一般加密的都是用戶密碼! 下面我們簡(jiǎn)畫(huà)個(gè)簡(jiǎn)單的圖幫助大家理解下加密的處理的流程:

1.簡(jiǎn)單的加密處理流程

流程圖如下

流程圖解析

  • Step 1.用戶注冊(cè)賬號(hào)密碼,賬號(hào)密碼校驗(yàn)后(賬號(hào)是否重復(fù),密碼位數(shù) > 6位等), 即賬號(hào)密碼有效,注冊(cè)成功后,我們提交給服務(wù)器的賬號(hào),以及本地加密過(guò)的密碼!
  • Step 2.服務(wù)器端將用戶提交的賬號(hào),加密過(guò)的密碼保存到服務(wù)端的數(shù)據(jù)庫(kù)中,也就是服務(wù) 端并不會(huì)保存我們的明文密碼(原始)密碼!
  • Step 3.說(shuō)回客戶端,如果注冊(cè)成功或者登陸成功,你想保存賬號(hào)密碼到SP中,保存的的密碼 也需要走一趟加密流程!即明文密碼——>加密,再保存!如果不保存,每次請(qǐng)求的時(shí)候,明文密碼 也要走一趟家里流程,然后拿著加密后的密碼來(lái)請(qǐng)求服務(wù)器!
  • Step 4.服務(wù)器驗(yàn)證賬號(hào)以及加密密碼,成功,分配客戶端一個(gè)session標(biāo)識(shí),后續(xù)客戶端可以拿著 這個(gè)session來(lái)訪問(wèn)服務(wù)端提供的相關(guān)服務(wù)!

嘿嘿,理解了吧,加密的方法有很多種,小豬也不是這方面的高玩,以前使用過(guò)的加密方法是MD5 加密,本節(jié)也給大家簡(jiǎn)單介紹一下這個(gè)MD5加密,以及演示下用法~

2.MD5簡(jiǎn)單介紹:

1)MD5是什么鬼?:

答:Message Digest Algorithm MD5(中文名為消息摘要算法第五版)為計(jì)算機(jī)安全領(lǐng)域廣泛 使用的一種散列函數(shù),用以提供消息的完整性保護(hù)——摘自《百度百科》 簡(jiǎn)單點(diǎn)說(shuō)就是一種加密算法,可以將一個(gè)字符串,或者文件,壓縮包,執(zhí)行MD5加密后, 就可以生產(chǎn)一個(gè)固定長(zhǎng)度為128bit的串!這個(gè)串基本唯一!另外我們都知道:一個(gè)十六進(jìn)制 需要用4個(gè)bit來(lái)表示,那么對(duì)應(yīng)的MD5的字符串長(zhǎng)度就為:128 / 4 = 32位了!另外可能 你看到一些md5是16位的,只是將32位MD5碼去掉了前八位以及后八位!不信么,我們來(lái)試試 百度一下:md5在線解密,第一個(gè):http://www.cmd5.com/

2)MD5能破解嗎?

答:MD5不可逆,就是說(shuō)沒(méi)有對(duì)應(yīng)的算法,無(wú)法從生成的md5值逆向得到原始數(shù)據(jù)! 當(dāng)然暴力破解除外,簡(jiǎn)單的MD5加密后可以查MD5庫(kù)~

3)MD5值唯一嗎?

答:不唯一,一個(gè)原始數(shù)據(jù)只對(duì)應(yīng)一個(gè)MD5值,但是一個(gè)MD5值可能對(duì)應(yīng)多個(gè)原始數(shù)據(jù)!


3.MD5加密實(shí)現(xiàn)例子:

其實(shí)網(wǎng)上有很多寫(xiě)好的MD5的例子,百度或者谷歌一搜一大堆,這里提供下小豬用的MD5加密工具類!

Md5Util.java

/**
 * Created by Jay on 2015/9/2 0002.
 */
public class MD5 {
    public static String getMD5(String content) {
        try {
            MessageDigest digest = MessageDigest.getInstance("MD5");
            digest.update(content.getBytes());
            return getHashString(digest);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }

    private static String getHashString(MessageDigest digest) {
        StringBuilder builder = new StringBuilder();
        for (byte b : digest.digest()) {
            builder.append(Integer.toHexString((b >> 4) & 0xf));
            builder.append(Integer.toHexString(b & 0xf));
        }
        return builder.toString();
    }
}

MainActivity.java直接調(diào)用getMD5這個(gè)靜態(tài)方法:

Log.e("HeHe", MD5.getMD5("呵呵"));

我們可以看到Logcat上打印出:

這就是加密過(guò)后的呵呵了,我們可以把這串密文拷貝到上面這個(gè)md5的在線解密網(wǎng)站:

嘿嘿,果然,只是這樣加密一次,就直接破解了,有點(diǎn)不安全的樣子,那就加密100次咯, 就是將加密后的字符串再加密,重復(fù)100次,我們?cè)谠鹊幕A(chǔ)上加個(gè)加密一百次的方法:

public static String getMD5x100(String content){
    String s1 = content;
    for(int i = 0;i < 100;i++){
        s1 = getMD5(s1);
    }
    return s1;
}

然后調(diào)用下,發(fā)現(xiàn)打印這個(gè)的Log:

復(fù)制界面網(wǎng)站上:

好的,裝B成功~


4.SharedPreference工具類:

每次都要自行實(shí)例化SP相關(guān)的類,肯定很麻煩,這里貼個(gè)SP的工具類,大家可以貼到 自己的項(xiàng)目中,工具類來(lái)源于鴻洋大神的blog~

SPUtils.java

package com.jay.sharedpreferencedemo3;

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

import java.util.Map;

/**
 * Created by Jay on 2015/9/2 0002.
 */
public class SPUtils {
    /**
     * 保存在手機(jī)里的SP文件名
     */
    public static final String FILE_NAME = "my_sp";

    /**
     * 保存數(shù)據(jù)
     */
    public static void put(Context context, String key, Object obj) {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME, context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        if (obj instanceof Boolean) {
            editor.putBoolean(key, (Boolean) obj);
        } else if (obj instanceof Float) {
            editor.putFloat(key, (Float) obj);
        } else if (obj instanceof Integer) {
            editor.putInt(key, (Integer) obj);
        } else if (obj instanceof Long) {
            editor.putLong(key, (Long) obj);
        } else {
            editor.putString(key, (String) obj);
        }
        editor.commit();
    }

    /**
     * 獲取指定數(shù)據(jù)
     */
    public static Object get(Context context, String key, Object defaultObj) {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME, context.MODE_PRIVATE);
        if (defaultObj instanceof Boolean) {
            return sp.getBoolean(key, (Boolean) defaultObj);
        } else if (defaultObj instanceof Float) {
            return sp.getFloat(key, (Float) defaultObj);
        } else if (defaultObj instanceof Integer) {
            return sp.getInt(key, (Integer) defaultObj);
        } else if (defaultObj instanceof Long) {
            return sp.getLong(key, (Long) defaultObj);
        } else if (defaultObj instanceof String) {
            return sp.getString(key, (String) defaultObj);
        }
        return null;
    }

    /**
     * 刪除指定數(shù)據(jù)
     */
    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);
        editor.commit();
    }

    /**
     * 返回所有鍵值對(duì)
     */
    public static Map<String, ?> getAll(Context context) {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME, context.MODE_PRIVATE);
        Map<String, ?> map = sp.getAll();
        return map;
    }

    /**
     * 刪除所有數(shù)據(jù)
     */
    public static void clear(Context context) {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME, context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        editor.clear();
        editor.commit();
    }

    /**
     * 檢查key對(duì)應(yīng)的數(shù)據(jù)是否存在
     */
    public static boolean contains(Context context, String key) {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME, context.MODE_PRIVATE);
        return sp.contains(key);
    }

}

5.代碼下載:

SharedPreferenceDemo.zip下載 SharedPreferenceDemo.zip 

SharedPreferenceDemo2.zip下載 SharedPreferenceDemo2.zip 

SharedPreferenceDemo3.zip下載 SharedPreferenceDemo3.zip


本節(jié)小結(jié):

好的,關(guān)于Android存儲(chǔ)數(shù)據(jù)的第二種方式:SharedPreference保存用戶偏好參數(shù)的內(nèi)容就這么多, 應(yīng)該可以滿足你日常開(kāi)發(fā)使用SP的需求,如果有什么遺漏,歡迎提出,謝謝~


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)