33.Android資料儲存

不爱美女爱辣条發表於2024-04-20

資料儲存方式:
對比sql資料庫唄


1.檔案儲存
方式同java中I/O輸入輸出流一樣

舉個例子:

public class 位元組流_寫文字檔案 {
    public static void main(String[] args) throws IOException {
        /**位元組流建立文字檔案。在D盤SS目錄中建立students.txt檔案,並新增以下資訊:
         * 姓名:張三,性別:男,年齡:19,成績:90,入學日期:2022-9-10
         * 姓名:李四,性別:女,年齡:19,成績:95,入學日期:2022-9-9
         */
        String str="姓名:張三,性別:男,年齡:19,成績:90,入學日期:2022-9-10 " +
                "姓名:李四,性別:女,年齡:19,成績:95,入學日期:2022-9-9";
            OutputStream fileOutputStream = new FileOutputStream("D:\\SS\\students.txt");
            //OutputStream是所有輸出流的父類,為一個抽象類
        byte[] bytes = str.getBytes();    // getBytes()將字串轉換為位元組陣列
        for (int i = 0; i < bytes.length; i++) {
            fileOutputStream.write(bytes[i]);
        }
            fileOutputStream.close();   //關閉輸出流並釋放與其關聯的所有系統資源
    }
}

public class 位元組流_讀文字檔案 {
    public static void main(String[] args) throws IOException {
        InputStream fileInputStream = new FileInputStream("D:\\SS\\students.txt");
        byte[] bytes =new byte[170];
        int b;
        while ((b=fileInputStream.read(bytes))!=1){
            System.out.println(new String(bytes,0,b));
            //將指定byte陣列中從起始索引off開始的len位元組寫入輸出流
            //      b={'1' ,'2', '3', '4', '5', '6', '7', '8'};
            //      String item=new String(b,2,2)
            //      結果 item=34
        }
        fileInputStream.close();

    }
}

來看看Android中的:
首先分為內部儲存和外部儲存
:外部儲存就是SD卡之類的 屬於永久性的儲存方式 但不安全

三項:
獲取SD卡的狀態
判斷是否可用
獲取SD卡路徑
        String state = Environment.getExternalStorageState();
        state.equals(Environment.MEDIA_MOUNTED);
        Environment.getExternalStorageDirectory();
然後再透過輸入輸出流寫入讀取就行
注意注意:申請SD卡寫讀檔案的許可權 要在清單檔案中靜態申請許可權
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

主要來看看內部儲存:
透過一個案例模擬qq登入:
image
image
layout佈局就不寫了

我沒寫trycatch這樣是不對的 沒有處理異常
但主要是為了驗證
package com.example.store;

import android.content.Context;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

//工具類 實現QQ的賬號密碼儲存和讀取
public class Tool_qqAcitivity {
    public static boolean saveUser(Context context, String user, String pwd) throws IOException {
        FileOutputStream fos = context.openFileOutput("haha.txt", Context.MODE_PRIVATE);
        //將資料轉換為位元組碼的形式寫入haha.txt中
        fos.write((user + ":" + pwd).getBytes());
        fos.close();
        return true;

    }

    public static Map<String, String> getUser(Context context) throws IOException {
        String str = "";
        FileInputStream fis = context.openFileInput("haha.txt");
        byte[] buffer = new byte[fis.available()];
        fis.read(buffer);
        str = new String(buffer);
        HashMap<String, String> map = new HashMap<String, String>();
        String[] split = str.split(":");
        if (split.length >= 2) {
            map.put("user", split[0]);
            map.put("pwd", split[1]);
        } else {
            // 處理錯誤情況,例如檔案內容格式不正確或為空
            // 可以選擇丟擲異常、記錄日誌或返回null等
            // 這裡只是簡單地返回一個空的map
            return new HashMap<>();
        }
        fis.close();
        return map;

    }

}

package com.example.store;

import android.os.Bundle;
import android.os.Environment;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import java.io.IOException;
import java.util.Map;

public class QQ_Activity extends AppCompatActivity {
    //初始化
    private EditText et_account;
    private EditText et_password;
    private Button btn_login;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_qq);
        et_account = findViewById(R.id.et_account);
        et_password = findViewById(R.id.et_password);
        btn_login = findViewById(R.id.btn_login);
        try {
            Map<String, String> user = Tool_qqAcitivity.getUser(this);
            if(user!=null){
                et_account.setText(user.get("user"));
                et_password.setText(user.get("pwd"));
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }


        btn_login.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String user = et_account.getText().toString().trim();
                String pwd = et_password.getText().toString();
                //檢查是否為空
                if(TextUtils.isEmpty(user)){
                    Toast.makeText(QQ_Activity.this, "請輸入賬號", Toast.LENGTH_SHORT).show();
                    return;
                }
                if(TextUtils.isEmpty(pwd)){
                    Toast.makeText(QQ_Activity.this, "請輸入密碼", Toast.LENGTH_SHORT).show();
                    return;
                }
                Toast.makeText(QQ_Activity.this, "登入成功", Toast.LENGTH_SHORT).show();

                //儲存使用者資訊 呼叫工具
                try {
                    boolean b = Tool_qqAcitivity.saveUser(QQ_Activity.this, user, pwd);
                    if(b){
                        Toast.makeText(QQ_Activity.this, "儲存成功", Toast.LENGTH_SHORT).show();
                    }else {
                        Toast.makeText(QQ_Activity.this, "儲存失敗", Toast.LENGTH_SHORT).show();
                    }
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }

            }
        });
    }

}

需要注意的就是:
內部儲存 當建立的應用程式被解除安裝時 內部儲存檔案也隨之刪除
許可權引數:
MODE_PRIVATE 只能被當前程式讀寫
MODE_APPEND 內容可以追加
MODE_WORLD_READABLE 內容可被其他程式讀
MODE_WORLD_WRITEABLE 內容可被其他程式寫


2.SharedPreferences儲存
一個輕量級儲存類

       獲取例項物件
        SharedPreferences 名字 = getSharedPreferences("名字", MODE_PRIVATE);
		獲取編輯器
		透過鍵值對形式儲存(put方法)在data/data/包名/shared_prefs中的XML中
        SharedPreferences.Editor edit = 名字.edit();
        edit.putString("key","value");
        edit.commit();
        讀取資料
        名字.getString("key","不存在返回這個");
		移除資料
        edit.remove("key");
        edit.clear();

3.SQLite資料庫儲存
這個就有意思了 是Android自帶的一個小型資料庫
儲存的檔案在這裡:
參考:https://blog.csdn.net/hjjshua/article/details/124150812
image
直接透過例子來看:

還是跳過layout佈局檔案

package com.example.store;

import android.annotation.SuppressLint;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import org.w3c.dom.Text;


//在Activity中編寫 與前面傳遞過來的屬性 進行操作
public class SimpleDataActivity extends AppCompatActivity implements View.OnClickListener {
    //資料初始化
    MyHelper helper;
    private EditText et_id;
    private EditText et_name;
    private EditText et_age;
    private Button btn_add;
    private Button btn_query;
    private Button btn_update;
    private Button btn_delete;
    //用來展示結果
    private TextView tv_show;

    @SuppressLint("MissingInflatedId")
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_simple);
        //呼叫自己繼承SQLiteOpenHelper的MyHelper
        helper = new MyHelper(this);
        et_id = findViewById(R.id.et_id);
        et_name = findViewById(R.id.et_name);
        et_age = findViewById(R.id.et_age);
        btn_add = findViewById(R.id.btn_add);
        btn_query = findViewById(R.id.btn_query);
        btn_update = findViewById(R.id.btn_update);
        btn_delete = findViewById(R.id.btn_delete);
        tv_show = findViewById(R.id.tv_show);
        //第三種方法別忘記寫這個
        btn_add.setOnClickListener(this);
        btn_query.setOnClickListener(this);
        btn_update.setOnClickListener(this);
        btn_delete.setOnClickListener(this);
    }

    //使用第三種方法繼承介面實現方法 按鈕點選後的改變 增刪改查也在這裡實現
    /*
        ContentValues類 它負責儲存一些名值對
       它儲存的名值對當中的名是一個String型別 而值都是基本型別
    */
    @Override
    public void onClick(View v) {
        String id, name, age;
        SQLiteDatabase db;
        ContentValues values;
        //Google寫好的增刪改查api
        if (v.getId() == R.id.btn_add) {
            //增
            //獲取輸入框的值
            id = et_id.getText().toString();
            name = et_name.getText().toString();
            age = et_age.getText().toString();
            //獲取可讀寫的SQLiteDatabase物件
            db = helper.getWritableDatabase();
            //建立ContentValues物件
            values = new ContentValues();
            values.put("id", id);
            values.put("name", name);
            values.put("age", age);
            //三個引數  1.資料表名字 2.如果發現將要插入的行為空行 列名設為null 3.ContentValues物件
            db.insert("gao", null, values);
            Toast.makeText(this, "資訊也新增", Toast.LENGTH_SHORT).show();
            //當使用完SQLiteDatabase物件後呼叫close方法關閉資料庫連線 否則資料庫一直存在 消耗記憶體
            db.close();
        }

        //查
        if (v.getId() == R.id.btn_query) {
            //獲取可讀的SQLiteDatabase物件
            db = helper.getReadableDatabase();
            //Cursor是一個遊標介面 提供了便利查詢結果的方法
            Cursor cursor = db.query("gao", null, null, null, null, null, null);
            //判斷下是否有資料
            //兩種Cursor物件的遍歷方法

            //第一種先輸出第一個值 再移動遊標
            if (cursor.getCount() == 0) {
                Toast.makeText(this, "無資料", Toast.LENGTH_SHORT).show();
            } else {
                cursor.moveToNext();
                tv_show.append("id:" + cursor.getString(0) + " name:" + cursor.getString(1) + " age" + cursor.getString(2));
            }
            while (cursor.moveToNext()) {
                tv_show.append("\n" + "id:" + cursor.getString(0) + " name:" + cursor.getString(1) + " age" + cursor.getString(2));
            }
            //第二種先移動遊標 再輸出第一個值
//            if(cursor.getCount()>0){
//                while (cursor.moveToNext()) {
//                tv_show.append("id:" + cursor.getString(0) + " name:" + cursor.getString(1) + " age" + cursor.getString(2));
//            }
//            }else {
//                Toast.makeText(this, "無資料", Toast.LENGTH_SHORT).show();
//            }


            //用完Cursor 要關閉 否則造成記憶體洩漏
            cursor.close();
            db.close();
        }

        //修
        if (v.getId() == R.id.btn_update) {
            db = helper.getWritableDatabase();
            values = new ContentValues();
            values.put("name",name=et_name.getText().toString());
            values.put("age",name=et_age.getText().toString());
            // 資料表名字 最新資料 要修改資料的查詢條件 查詢條件的引數
            db.update("gao",values,"id=?",new String[]{
                    et_id.getText().toString()
            });
            Toast.makeText(this,"資訊已修改",Toast.LENGTH_SHORT).show();
            db.close();
        }

        //刪
        if (v.getId() == R.id.btn_delete) {
            db = helper.getWritableDatabase();
            db.delete("gao",null,null);
            Toast.makeText(this,"資訊已刪除",Toast.LENGTH_SHORT).show();
            db.close();
        }


    }

    //資料庫和表的建立
    class MyHelper extends SQLiteOpenHelper {
        public MyHelper(Context context) {
            //四個引數  上下文 資料庫名字 遊標工廠 資料庫版本
            super(context, "gao.db", null, 2);
        }

        // 建立表
        @Override
        public void onCreate(SQLiteDatabase db) {
            db.execSQL("create table gao(id int primary key,name varchar,age int)");
        }

        //資料庫版本號增加時呼叫
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        }
    }


}

總結一下這個:
大致裡面寫的註釋也夠清楚了
其中兩種Cursor物件的遍歷方法
參考 https://blog.csdn.net/qq_64628470/article/details/129974041

當然還有其他資料儲存方式
也對應著Android四大元件之一的內容提供者和觀察者

相關文章