一、文件的储存
1、将数据储存到文件中
Context类中提供了openfileOutput()方法,用来获取一个文件流,这个方法接收两个参数,第一个参数是文件名,在文件创建的时候使用的就是这个名称,注意这里指定的文件名不可以包含路径,因为所有的文件都是默认存储到/data/data/iles/目录下的。第二个参数是文件的操作模式,主要有两种模式可选,MODE_PRIVATE和MODE_APPEND。
MODE_PRIVATE:会覆盖源文件数据
MODE_APPEND:在源文件后追加数据
将文本内容保存到文件的代码展示:
public void save(){
    String Data = "Data on save";
    //创建一个文件流
    FileOutputStream out = null;、
        //创建一个缓存流
        BufferedWriter writer  = null;
    try {
        //打开这个文件流
        out = openFileOutput("data",MODE_PRIVATE);
        //将文件流转化转换流,在通过转换流得到缓存流
        writer = new BufferedWriter(new OutputStreamWriter(out));
        //通过缓存流写入数据
        writer.write(Data);
    }catch (IOException e){
        e.printStackTrace();
    }finally {
        if(writer != null){
            try {
                writer.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

2、从文件中读取数据
Context类中还提供了一个openFileInput()方法,用于读取文件。只接收一个参数及要读取的文件名。
读取数据代码如下:
    private String load() {
        FileInputStream fileInputStream = null;
        BufferedReader bufferedReader = null;
        StringBuilder builder = new StringBuilder();
        try {
            fileInputStream = openFileInput("data");
            bufferedReader = new BufferedReader(new InputStreamReader(fileInputStream));
            String leng = "";
            while ((leng = bufferedReader.readLine()) != null){
                builder.append(leng);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(bufferedReader != null){
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        return builder.toString();
    }
bufferedReader.lines()用于处理大型文件,通过返回一个字符串流(Stream<String>)来处理每一行数据。直接返回所有内容,不需要判读是否读取结束。
bufferedReader.readLine()用于读取文件的一行文本数据,并返回一个字符串。读取结束时返回null
bufferedReader.read()用于逐个读取字符的整数值,并需要自己处理字符的连接以获取完整的文本数据。这种方法更适合处理二进制数据或特殊的文本处理场景。
int len = 0; //文件读取长度 byte[] careBuffer = new byte[1024]; //1kb while ((len = buff.read(careBuffer)) != EOF) { buff1.write(careBuffer, 0, len); }
二、SharedPreferences
1、获取SharedPreferences对象的两种方式
1.1 Context类中的getSharedPreferences()
该方法接受两个参数,第一个参数是文件名,第二个参数是MODE_PRIVATE和直接传入0的效果一样,表示只有当前程序才可以对Sharedpreferences进行读写操作。
1.2 Activity类中的getPreferences()方法
这个方法和Context中的getSharedPreferences()方法很相似,不过它只接收一个操作模式参数(MODE_PRIVATE),因为使用这个方法时会自动将当前活动的类名作为SharedPreferences的文件名。
2、将数据储存到SharedPreferences
1.1实现步骤
a、得到SharedPreferences对象以后调用edit()方法获取一个内部类的SharedPreferences.Editor.
b、向Editor对象中添加数据,例如添加字符串就使用putString(),添加int类型数据就使用putInt()。
c、使用apply()方法提交数据完成储存
2.2 实现代码
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    editText = findViewById(R.id.edit);
    SharedPreferences.Editor editor = getSharedPreferences("data", MODE_PRIVATE).edit();
    editor.putString("username", "lukecc0");
    editor.putString("age", "1");
    editor.putBoolean("married", false);
    editor.apply();
}

username就相当于key,在SharedPreferences中,每个数据都有一个关联的键(key),通过这个键可以访问相应的值(value)。通过SharedPreferences.Editor对象,你可以添加、编辑或删除这些键值对。
3、从SharedPrefrences中读取数据
SharedPreferences对象中提供了一系列的get方法,用于对存储的数据进行读取,每种get方法都对应了SharedPreferences.Editor中的一种put方法。
比如读取一个布尔型数据就使用getBoolean()方法,读取一个字符中就使用getString()方法。
这些get方法都接收两个参数,第一个参数是键,传入存储数据时使用的键就可以得到相应的值了,第二个参数是默认值,即表示当传入的键找不到对应的值时会以什么样的默认值进行返回。
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    editText = findViewById(R.id.edit);
    Button button = findViewById(R.id.button);
    button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            //读取数据
            SharedPreferences pref = getSharedPreferences("data",MODE_PRIVATE);
            editText.setText(pref.getString("username","admin")+"\n"
                             +pref.getString("age","0")+"\n"+pref.getBoolean("marride",false));
        }
    });
}
当点击按钮后就会出现数据,如下图:


三、SQLite
1、创建数据库
建立一个MyDatabase类继承SQLite
重写onCreate、onUPgrade方法
获得MyDatabase的实例
选择打开方式打开
SQLiteOpenHelper是一个抽象类,这意味着如果我们想要使用它的话,就需要创建一个自己的帮助类去继承它。SQLiteOpenHelper中有两个抽象方法,分别是onCreate()和onUpgrade()我们必须在自己的帮助类里面重写这两个方法,然后分别在这两个方法中去实现创建、升级数据库的逻辑。
SQLiteOpenHelper中还有两个重要的实例方法,getReadableDatabase和getwirtableDatabase,这两个方法的可以创建或打开一个数据库,不同的是如果磁盘满了的情况下:
-  getReadableDAtabase方法返回的对象将以只读的方式打开数据库
-  getWritableDatabase方法将会出现异常
SQL建立表语句:
create table Book(
    id integer primary key autoincrement,
    author text,
    price real,
    pages integer,
    name text)
integer表示整型,real表示浮点型,txt表示文本类型,blob表示二进制类型,primary key设置主键,autoincrement显示id是自动增长的。
public class MyDatabaseHelper extends SQLiteOpenHelper {
    public static final String CREATE_BOOK = "create table Book("
            + "id integer primary key autoincrement,"
            + "author text,"
            + "price real,"
            + "pages integer,"
            + "name text)";
    public MyDatabaseHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
        mContext = context;
    }
    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        //创建数据库,参数为建库语句
        sqLiteDatabase.execSQL(CREATE_BOOK);
        Toast.makeText(mContext, "Create succeeded", Toast.LENGTH_SHORT).show();
    }
    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
    }
}
**public SQLiteOpenHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version)**构造方法的含义:
- context:Android 应用程序的上下文
- name: 这是要创建或打开的数据库的名称。如果数据库不存在- SQLiteOpenHelper会创建一个新的数据库文件,如果数据库已经存在,它将尝试打开该数据库。这里传入的数据库名称可以是一个字符串,表示数据库的名称。
- factory:这是一个用于创建游标对象的工厂。游标是用于查询数据库并遍历结果集的对象。通常情况下,你可以传入 null。
- version:这是数据库的版本号。当应用程序需要进行数据库结构的更改时,你需要增加数据库版本号。
在MainActivity中使用数据库
public class MainActivity extends AppCompatActivity {
    private MyDatabaseHelper dbHelper;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //创建一个数据库的帮助类
        dbHelper = new MyDatabaseHelper(this,"BookStore.dp",null,2);
        Button button =findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                创建数据库
                dbHelper.getWritableDatabase();
            }
        });
    }
}
那么如何看到这个数据库呢?请参考android官方开发文档
查询数据库
2、更新数据库
使用onUpgrade更新数据库。
例如现在需要再建立一个表,如果已经建好表的情况下,onCreate是不会再继续执行的。因此这样的在onCreate中直接加入建表语句是不行的。
private static final String Category = "create table Category("
    + "id integer primary key autoincrement,"
    + "Category_name text,"
    + "Category_code integer)";
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
    //创建数据库,参数为建库语句
    sqLiteDatabase.execSQL(CREATE_BOOK);
    sqLiteDatabase.execSQL(Category);
    Toast.makeText(mContext, "Create succeeded", Toast.LENGTH_SHORT).show();
}
因此我们需要在onUpgrade中编写代码逻辑。
drop table if exists:是一个 SQL 语句,用于删除表,使用这个语句时要小心,因为一旦执行,表中的所有数据将被永久删除,并且无法恢复。这通常用于在数据库中不再需要某个表时进行清理。
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
    sqLiteDatabase.execSQL("drop table if exists Book");
    sqLiteDatabase.execSQL("drop table if exists Gategory");
    onCreate(sqLiteDatabase);
}
在上述这段代码中,会先删除两个表,然后重新创建两个表。主要原表的数据将会被清空。如何实现不清空原表数据?如果有SQL基础请看下面这段代码,没有请自行忽略这个问题。
    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
        // 备份旧表数据
        sqLiteDatabase.execSQL("CREATE TABLE Book_backup AS SELECT * FROM Book");
        // 删除旧的 Book 表
        sqLiteDatabase.execSQL("DROP TABLE IF EXISTS Book");
        // 创建新的 Book 表
        sqLiteDatabase.execSQL(CREATE_BOOK);
        // 将备份表数据复制到新表中
        sqLiteDatabase.execSQL("INSERT INTO Book (id, author, price, pages, name) SELECT id, author, price, pages, name FROM Book_backup");
        // 删除备份表
        sqLiteDatabase.execSQL("DROP TABLE IF EXISTS Book_backup");
        
        Toast.makeText(mContext, "Database upgraded successfully", Toast.LENGTH_SHORT).show();
    }
接下来我们需要让onUpgrade 可以执行,在使用MyDatabase的构造方法时,修改最后一个传递参数version,他显示的数据库版本,我们之前使用的是1,现在传递一个比1大的数即可。
3、添加数据——insert
前面我们已经知道,调用SQLiteOpenHelper的getReadableDatabase()或getwritable-Datābase()方法是可以用于创建和升级数据库的。
不仅如此,这两个方法还都会返回一个SQLiteDatabase对象,借助这个对象就可以对数据进行CRUD操作了。
**SQLiteDatabase提供了一个insert方法,用于添加数据。**它需要三个参数。
第一个参数是表名,我们需要往哪个表加入数据就写哪个表名。
第二个参数用于在未指定添加数据的情况下给某些可为空的列自动赋值NULL,一般我们用不到这个功能,直接传入null即可。
第三个参数是一个ContentValues对象,它提供了一系列的put()方法重载,用于向ContentValues中添加数据,只需要将表中的每个列名以及相应的待添加数据传入即可。
binding.button1.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        ContentValues values = new ContentValues();
        //将数据保存起来,第一条数据
        values.put("name","This is one");
        values.put("author","dow");
        values.put("pages",23);
        values.put("price",12);
        //添加数据
        db.insert("Book",null,values);
        //将数据保存起来,第二条数据
        values.put("name","This is two");
        values.put("author","dow1");
        values.put("pages",231);
        values.put("price",112);
        db.insert("Book",null,values);
    }
});
点击这个按钮,我们每次向数据库中保存两条数据。

为什么没有给id赋值?因为我们在建表时已经设置为了自动增长
id integer primary key autoincrement
4、更新数据——updata
binding.button2.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put("price",110);
        //更新所有名为This is one的书
        db.update("Book",values,"name = ?",new String[]{"This is one"});
    }
});
5、删除数据——delete
//删
binding.button3.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        db.delete("Book","pages > ?",new String[]{"200"});
    }
});
指定删除pages大于200的书。
6、查询数据——query
query具有7个参数

它会返回一个Cursor对象,这个对象中保存了数据库的信息,查询的所有数据都从这个对象中取出。
//查
binding.button4.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        Cursor cursor = db.query("Book",null,null,null,null,null,null);
        if(cursor.moveToFirst()){
            do {
                String name = cursor.getString(cursor.getColumnIndex("name"));
                int price = cursor.getInt(cursor.getColumnIndex("price"));
                Log.d("TAG1",name);
                Log.d("TAG1",price+"");
            }while (cursor.moveToNext());
        }
    }
});

moveToFirst方法,将数据的指针移到开头第一行的位置。然后进入一个循环读取数据,在while中使用moveToNext方法每次将数据指针向下移动一位。getColumnIndex方法用于获取指定某一列在表中的位置。
如下表,先获取了name、price的位置,然后通过这个位置取得了该位置上的数据。




















