Android 开发入门(5)

0x07 数据存储

(1)共享参数 SharedPreferences

a. 用法

  • 用法

    • SharedPreferences 是 Android 的一个轻量级存储工具,采用的存储结构为键值对的方式
    • 共享参数的存储介质是符合 XML 规范的配置文件,路径为/data/data/com.example.test/shared_prefs/xxx.xml
  • 使用场景

    • 简单且孤立的数据
    • 文本形式的数据
    • 需要持久化存储的数据

    共享参数经常存储的数据包括 App 的个性化数据、用户的行为信息、临时需要保存的片段信息

举例:登记个人信息

  • // import ...
    import android.content.Context;
    import android.content.SharedPreferences;
    import android.view.View;
    import android.widget.EditText;
    
    public class Activity1 extends AppCompatActivity implements View.OnClickListener {
    
        private EditText et_name;
        private EditText et_age;
        private SharedPreferences preferences;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_1);
            et_name = findViewById(R.id.et_name);
            et_age = findViewById(R.id.et_age);
            findViewById(R.id.btn).setOnClickListener(this);
            preferences = getSharedPreferences("preference", Context.MODE_PRIVATE);
            reload();
        }
    
        private void reload() { // 加载已经存储的信息
            String name = preferences.getString("name", null);
            if (name != null) {
                et_name.setText(name);
            }
            int age = preferences.getInt("age", 0);
            if (age != 0) {
                et_age.setText(String.valueOf(age));
            }
        }
    
        @Override
        public void onClick(View view) {
            String name = et_name.getText().toString();
            String age = et_age.getText().toString();
            SharedPreferences.Editor editor = preferences.edit();
            // 添加信息
            editor.putString("name", name);
            editor.putInt("age", Integer.parseInt(age));
            // 提交信息
            editor.commit();
        }
    }
    

b. 实现记住密码功能

(2)数据库 SQLite

a. SQLite 基础语法

  1. 数据定义

    1. 创建表格

      CREATE TABLE IF NOT EXISTS user_info (
          _id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
          name VARCHAR NOT NULL,
          age INTEGER NOT NULL
      );
      
      • 仅单引号包括起来的内容区分大小写
      • 通过IF NOT EXISTS语句避免重复建表
      • SQLite 支持整形INTEGER、长整型LONG、字符串VARCHAR、浮点数FLOAT,不支持布尔类型,如果直接保存布尔数据会被强转为 0 / 1,分别代表 false/true
      • 建表必须有唯一标识字段(主键):_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL
    2. 删除表格

      DROP TABLE IF EXISTS user_info;
      
    3. 修改表结构

      SQLite 只支持增加字段,不支持修改/删除字段,且每次智能添加一列字段

      ALTER TABLE user_info ADD COLUMN age INTEGER;
      
  2. 数据操纵语言

    1. 添加

      INSERT INTO user_info (name, age) VALUES ('张三', 20);
      
    2. 删除

      DELETE FROM user_info WHERE name='张三'
      
    3. 修改

      UPDATE user_info SET age=21 WHERE name
      
    4. 查询

      SELECT name FROM table WHERE name='张三'
      
      • 排序输出结果

        SELECT * FROM user_info ORDER BY age ASC;
        
        • ASC:升序
        • DESC:降序

b. 数据库管理器 SQLiteDatabase

  • SQLiteDatabase 是 SQLite 的数据库管理类,提供了若干操作数据表的 API,包含以下三个类

    1. 数据处理类,用于数据表层面的操作

      • execSQL():执行拼接好的 SQL 控制语句
      • insert():插入一条记录
      • update():更新符合条件的记录
      • delete():删除符合条件的记录
      • query():执行查询操作,返回结果集的游标
      • rawQuery():执行拼接好的 SQL 查询语句,返回结果集的游标

      使用详情见UserDBHelper

    2. 事务类,用于事务层面的操作

      • beginTransaction():开始事务
      • setTransactionSuccessful():设置事务的成功标志
      • endTransaction():结束事务

      以数据处理类中的insert()方法实现为例:

      ContentValues values = new ContentValues();
      values.put("name", user.name);
      values.put("age", user.age);
      try {
       mWDB.beginTransaction();
       mWDB.insert(TABLE_NAME, null, values);	// 第一条记录
       mWDB.insert(TABLE_NAME, null, values);	// 第二条记录
       mWDB.setTransactionSuccessful();
      } catch (Exception e) {
       e.printStackTrace();
      } finally {
       mWDB.endTransaction();
      }
      return 1;
      

      当两条记录添加之间出现错误(如:int i = 1 / 0)时,通过添加事务的方法,避免出现仅成功添加第一条记录。事务若未成功,则通过回滚的措施取消本次添加操作

    3. 管理类,用于数据库层面的操作

      • openDatabase():打开指定路径的数据库
      • isOpen():判断数据库是否已打开
      • close():关闭数据库
      • getVersion():获取数据库版本号
      • setVersion():设置数据库版本号

      通过修改UserDBHelper中的onUpgrade()方法

      // ...
      private static final int DB_VERSION = 2;    // 数据库版本
      // ...
      @Override
      public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
       String sql = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN c1 VARCHAR;";
       db.execSQL(sql);
       sql = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN c2 VARCHAR;";
       db.execSQL(sql);
      }
      
  • 创建/打开数据库

    SQLiteDatabase db = openOrCreateDatabase(getFilesDir() + "/test.db", Context.MODE_PRIVATE, null);
    String str = String.format("数据库%s创建%s", db.getPath(), (db != null)?"成功":"失败");
    tv.setText(str);
    
  • 删除数据库

    deleteDatabase(getFilesDir() + "/test.db");
    

c. 数据库帮助器 SQLiteOpenHelper

  • SQLitOpenHelper 是 Android 提供的数据库辅助工具,用于指导合理使用 SQLite,使用方法:
    • 新建一个继承自 SQLiteOpenHelper 的数据库操作类,提示重写onCreate()onUpgrade()
    • 封装保证数据库安全的必要方法
    • 提供对表记录进行增删改查的操作方法
  • 游标
    • 调用 SQLiteDatabase 的query()rawQuery()方法时,返回的都是 Cursor 对象,获取查询结果需要根据游标的指示遍历结果集合,包含以下三个类:
      • 游标控制类,用于指定游标的状态
        • close():关闭游标
        • isClosed():判断游标是否关闭
        • isFirst():判断游标是否在开头
        • isLast():判断游标是否在末尾
      • 游标移动类,用于包游标移动到指定位置
        • move():往后移动若干条记录
        • moveToFirst():移动游标到开头
        • moveToLast():移动游标到末尾
        • moveToNext():移动游标到下一条记录
        • moveToPrevious():移动游标到上一条记录
        • moveToPosition():移动游标到指定位置的记录
      • 获取记录类,用于获取记录的数量、类型、取值
        • getCount():获取记录数量
        • getInt():获取指定字段整型值
        • getLong():获取指定字段长整型值
        • getFloat():获取指定字段浮点数值
        • getString():获取指定字段的字符串值
        • getType():获取指定字段的字段类型

举例:人员信息表的增删改查

  • /database/UserDBHelper.java

    帮助器,包含 SQLite 操作语句与方法

    package com.example.test.database;
    
    import android.content.ContentValues;
    import android.content.Context;
    import android.database.Cursor;
    import android.database.sqlite.SQLiteDatabase;
    import android.database.sqlite.SQLiteOpenHelper;
    
    import com.example.test.util.User;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class UserDBHelper extends SQLiteOpenHelper {
    
        private static final String DB_NAME = "user.db";    // 数据库名
        private static final String TABLE_NAME = "user_info";   // 表名
        private static final int DB_VERSION = 1;    // 数据库版本
        private static UserDBHelper mHelper = null; //帮助器
    
        private SQLiteDatabase mRDB = null; // 读取数据库
        private SQLiteDatabase mWDB = null; // 写入数据库
    
        private UserDBHelper(Context context) {
            super(context, DB_NAME, null, DB_VERSION);
        }
    
        // 打开数据库读取连接
        public SQLiteDatabase openReadLink() {
            if (mRDB == null || !mRDB.isOpen()) {
                mRDB = mHelper.getReadableDatabase();
            }
            return mRDB;
        }
    
        // 打开数据库写入连接
        public SQLiteDatabase openWriteLink() {
            if (mWDB == null || !mWDB.isOpen()) {
                mWDB = mHelper.getWritableDatabase();
            }
            return mWDB;
        }
    
        // 关闭数据库连接
        public void closeLink() {
            if (mRDB != null && mRDB.isOpen()) {
                mRDB.close();
                mRDB = null;
            }
            if (mWDB != null && mWDB.isOpen()) {
                mWDB.close();
                mWDB = null;
            }
        }
    
        // 利用单例模式获取数据库帮助器的唯一实例
        public static UserDBHelper getInstance(Context context) {
            if (mHelper == null) {
                mHelper = new UserDBHelper(context);
            }
            return mHelper;
        }
    
        // 数据库创建
        @Override
        public void onCreate(SQLiteDatabase db) {
            String sql = "CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (" +
                    "_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," +
                    " name VARCHAR NOT NULL," +
                    " age INTEGER NOT NULL);";
            db.execSQL(sql);
        }
    
        // 数据库版本更新
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        }
    
        // 增加
        public long insert(User user) {
            ContentValues values = new ContentValues();
            values.put("name", user.name);
            values.put("age", user.age);
            return mWDB.insert(TABLE_NAME, null, values);
        }
    
        // 删除
        public long delete(String name) {
            return mWDB.delete(TABLE_NAME, "name=?", new String[]{ name });
        }
        // 清空
        public long deleteAll() {
            return mWDB.delete(TABLE_NAME, "1=1", null);
        }
    
        // 修改
        public long update(User user) {
            ContentValues values = new ContentValues();
            values.put("name", user.name);
            values.put("age", user.age);
            return mWDB.update(TABLE_NAME, values, "name=?", new String[]{ user.name });
        }
    
        // 查询
        public List<User> query(String name) {
            List<User> list = new ArrayList<>();
            // 执行记录查询行为,返回为结果集的游标
            Cursor cursor = mRDB.query(TABLE_NAME, null, "name=?", new String[]{ name }, null, null, null);
            // 循环取出游标指向的每条记录
            while (cursor.moveToNext()) {
                User user = new User();
                user.id = cursor.getInt(0);
                user.name = cursor.getString(1);
                user.age = cursor.getInt(2);
                list.add(user);
            }
            return list;
        }
        // 列出全部
        public List<User> queryAll() {
            List<User> list = new ArrayList<>();
            Cursor cursor = mRDB.query(TABLE_NAME, null, null, null, null, null, null);
            while (cursor.moveToNext()) {
                User user = new User();
                user.id = cursor.getInt(0);
                user.name = cursor.getString(1);
                user.age = cursor.getInt(2);
                list.add(user);
            }
            return list;
        }
    }
    
  • /util/User.java

    人员信息类

    package com.example.test.util;
    
    public class User {
    
        public int id;          // 序号
        public String name;     // 姓名
        public int age;         // 年龄
    
        public User() {}
    
        public User(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
    
  • /util/ToastUtil.java

    消息提示

    package com.example.test.util;
    
    import android.content.Context;
    import android.widget.Toast;
    
    public class ToastUtil {
        public static void show(Context ctx, String desc) {
            Toast.makeText(ctx, desc, Toast.LENGTH_SHORT).show();
        }
    }
    
  • MainActivity.java

    主活动

    package com.example.test;
    
    import androidx.appcompat.app.AlertDialog;
    import androidx.appcompat.app.AppCompatActivity;
    
    import android.os.Bundle;
    import android.view.View;
    import android.widget.EditText;
    import android.widget.TextView;
    
    import com.example.test.database.UserDBHelper;
    import com.example.test.util.ToastUtil;
    import com.example.test.util.User;
    
    import java.util.List;
    
    public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    
        private EditText et_name;
        private EditText et_age;
        private TextView tv;
        UserDBHelper mHelper;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            et_name = findViewById(R.id.et_name);
            et_age = findViewById(R.id.et_age);
            tv = findViewById(R.id.tv);
            findViewById(R.id.btn_ins).setOnClickListener(this);
            findViewById(R.id.btn_del).setOnClickListener(this);
            findViewById(R.id.btn_upd).setOnClickListener(this);
            findViewById(R.id.btn_sel).setOnClickListener(this);
            findViewById(R.id.btn_sal).setOnClickListener(this);
            findViewById(R.id.btn_dal).setOnClickListener(this);
        }
    
        @Override
        protected void onStart() {
            super.onStart();
            // 获取数据库帮助器实例
            mHelper = UserDBHelper.getInstance(this);
            mHelper.openReadLink();
            mHelper.openWriteLink();
        }
    
        @Override
        protected void onStop() {
            super.onStop();
            mHelper.closeLink();
        }
    
        @Override
        public void onClick(View view) {
            String name = et_name.getText().toString();
            String age = et_age.getText().toString();
            User user = null;
            switch (view.getId()) {
                case R.id.btn_ins:
                    user = new User(name, Integer.parseInt(age));
                    if (mHelper.insert(user) > 0) {
                        ToastUtil.show(this, "添加成功");
                    }
                    break;
                case R.id.btn_del:
                    if (mHelper.delete(name) > 0) {
                        ToastUtil.show(this, "删除成功");
                    }
                    break;
                case R.id.btn_upd:
                    user = new User(name, Integer.parseInt(age));
                    if (mHelper.update(user) > 0) {
                        ToastUtil.show(this, "更新成功");
                    }
                    break;
                case R.id.btn_sel:
                    String str1 = "";
                    List<User> list1 = mHelper.query(name);
                    for (User u : list1) {
                        str1 = str1 + u.toString() + "\n";
                    }
                    tv.setText(str1);
                    break;
                case R.id.btn_sal:
                    String str2 = "";
                    List<User> list2 = mHelper.queryAll();
                    for (User u : list2) {
                        str2 = str2 + u.toString() + "\n";
                    }
                    tv.setText(str2);
                    break;
                case R.id.btn_dal:
                    AlertDialog.Builder builder = new AlertDialog.Builder(this);
                    builder.setTitle("警告");
                    builder.setMessage("确认清空?");
                    builder.setPositiveButton("确认", (dialogInterface, i) -> {
                        if (mHelper.deleteAll() > 0) {
                            ToastUtil.show(this, "全部清空");
                        }
                    });
                    builder.setNegativeButton("取消", (dialogInterface, i) -> {});
                    AlertDialog alertDialog = builder.create();
                    alertDialog.show();
                    break;
            }
            return;
        }
    }
    

(3)存储卡的文件操作

a. 私有存储空间与公共存储空间

  • 存储空间:Android 把外部存储分成了两部分:
    1. 仅应用本身可以访问的私有空间
    2. 所有应用都可以访问的公共空间
  • 获取外部存储空间(存储卡)的路径:
    • 公共空间:Environment 类的getExternalStoragePublicDirectory()方法
    • 私有空间:Environment 类的getExternalFilesDir()方法

b. 在存储卡上读写文本文件

  • 读取

    • /util/FileUtil.java

      import java.io.BufferedReader;
      import java.io.FileReader;
      import java.io.IOException;
      // ...
      public static String openText(String path) {
          BufferedReader is = null;
          StringBuilder sb = new StringBuilder();
          try {
              is = new BufferedReader(new FileReader(path));
              String line = null;
              while ((line = is.readLine()) != null) {
                  sb.append(line);
              }
          } catch (Exception e) {
              e.printStackTrace();
          } finally {
              if (is != null) {
                  try {
                      is.close();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
          }
          return sb.toString();
      }
      
    • MainActivity.java

      StringBuilder sb = new StringBuilder();
      sb.append("姓名:").append(name);
      sb.append("年龄:").append(age);
      String fileName = System.currentTimeMillis() + ".txt";
      String directory = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString();
      path = directory + File.separatorChar + fileName;	// String 类型全局变量
      FileUtil.saveText(path, sb.toString());
      ToastUtil.show(this, "保存成功");		// 消息提示
      
      1. 外部存储的私有空间

        directory = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString();
        
      2. 外部存储的公共空间

        directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString();
        
        // AndroidManifest.xml
        // 开启读写权限
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
        
      3. 内部存储的私有空间

        directory = getFilesDir().toString(); 
        
  • 写入

    • /util/FileUtil.java

      import java.io.BufferedWriter;
      import java.io.FileWriter;
      import java.io.IOException;
      // ...
      public static void saveText(String path, String text) {	// path:路径, text:文本内容
          BufferedWriter os = null;
          try {
              os = new BufferedWriter(new FileWriter(path));
              os.write(text);
          } catch (Exception e) {
              e.printStackTrace();
          } finally {
              if (os != null) {
                  try {
                      os.close();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
          }
      }
      
    • MainActivity.java

      tv.setText(FileUtil.openText(path));
      

c. 在存储卡上读写图片文件

  • Android 的位图工具是 Bitmap,App 读写 Bitmap 可以使用性能更好的 BufferedOutputStream 和 BufferedInputStream
  • Android 提供了 BitmapFactory 工具来读取各种来源的图片
    • decodeResource():从资源文件中读取图片信息
    • decodeFile():将指定路径的图片读取到 Bitmap 对象
    • decodeStream():从输入流读取位图数据
  • 读取

    • /util/FileUtil.java

      public static Bitmap openImage(String path) {
          Bitmap bitmap = null;
          FileInputStream fis = null;
          try {
              fis = new FileInputStream(path);
              bitmap = BitmapFactory.decodeStream(fis);
          } catch (Exception e) {
              e.printStackTrace();
          } finally {
              if (fis != null) {
                  try {
                      fis.close();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
          }
          return bitmap;
      }
      
    • MainActivity.java

      1. Bitmap bitmap1 = FileUtil.openImage(path);
        iv.setImageBitmap(bitmap1);
        
      2. Bitmap bitmap1 = BitmapFactory.decodeFile(path);
        iv.setImageBitmap(bitmap1);
        
      3. iv.setImageURI(Uri.parse(path));
        
  • 写入

    • /util/FileUtil.java

      import android.graphics.Bitmap;
      // ...
      public static void saveImage(String path, Bitmap bitmap) {
          FileOutputStream fos = null;
          try {
              fos = new FileOutputStream(path);
              // 把位图数据压缩到文件输出流中
              bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
          } catch (Exception e) {
              e.printStackTrace();
          } finally {
              if (fos != null) {
                  try {
                      fos.close();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
          }
      }
      
    • MainActivity.java

      String fileName = System.currentTimeMillis() + ".jpeg";
      // 获取当前 App 的私有下载目录
      path = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString() + fileName;
      // 从指定的资源文件(./res/drawable/test.jpg)获取位图对象
      Bitmap bitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.test);
      // 把位图对象保存为图片文件
      FileUtil.saveImage(path, bitmap1);
      ToastUtil.show(this, "保存成功");
      

(4)应用组件 Application

a. 生命周期

  • Application 是 Android 的一大组件,在 App 运行时有且仅有一个 Application 对象贯穿整个生命周期

  • Application 先于 MainActivity 创建

    public class MyApplication extends Application {
        // App 启动时调用
        @Override
        public void onCreate() {
            super.onCreate();
            Log.d("SRIGT", "MyApplication onCreate");
        }
    
        // App 终止时调用
        @Override
        public void onTerminate() {
            super.onTerminate();
            Log.d("SRIGT", "onTerminate");
        }
    
        // 配置改变时调用
        @Override
        public void onConfigurationChanged(@NonNull Configuration newConfig) {
            super.onConfigurationChanged(newConfig);
            Log.d("SRIGT", "onConfigurationChanged");
        }
    }
    
    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Log.d("SRIGT", "MainActivity onCreate");
        }
    }
    
    <application
        android:name=".util.MyApplication">
        <activity
            android:name=".MainActivity"
            android:exported="true">
        </activity>
    </application>
    

    运行后,Logcat 的输出结果为:

    D/SRIGT: MyApplication onCreate
    D/SRIGT: MainActivity onCreate
    

b. 利用 Application 操作全局变量

  • 全局的意思是其他代码都可以引用该变量,因此,全局变量是共享数据和消息传递的好帮手

  • 适合在 Application 中保存的全局变量主要有下面 3 类数据:

    • 会频繁读取的信息,如用户名、手机号等
    • 不方便由意图传递的数据,如位图对象、非字符串类型的集合对象等
    • 容易因频繁分配内存而导致内存泄露的对象,如 Handle 对象
    // MyApplication.java
    private static MyApplication mApp;
    
    // 声明一个公共的信息映射对象,可当作全局变量使用
    public HashMap<String, String> infoMap = new HashMap<>();
    
    public static MyApplication getInstance() {
        return mApp;
    }
    
    // 实例化 mApp
    @Override
    public void onCreate() {
        super.onCreate();
        mApp = this;
    }
    
    // MainActivity.java
    public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    
        private EditText et;
        private MyApplication app = MyApplication.getInstance();
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            et = findViewById(R.id.et);
            findViewById(R.id.btn).setOnClickListener(this);
            app = MyApplication.getInstance();
            reload();
        }
    
        private void reload() {
            String text = app.infoMap.get("Text");
            et.setText(text);
        }
    
        @Override
        public void onClick(View view) {
            String text = et.getText().toString();
            if (text == null) {
                return;
            }
            app.infoMap.put("Text", text);
        }
    }
    

c. 利用 Room 简化数据库操作

  • Room 框架的导入

    • Room 是 Google 公司推出的基于 SQLite的数据库处理框架,其通过注解技术极大简化了数据库操作,减少了原来相当一部分编码工作量。

    • 使用前要在模块的 build.gradle 文件中的dependencies节点添加两行配置以导入指定版本的 Room 库

      implementation 'androidxx.room:room-runtime:2.2.5'
      annotationProcessor 'androidx.room:room-compiler:2.2.5'
      
  • Room 框架的编码步骤(以录入书籍信息为例,共以下五步)

    1. 编写书籍信息表对应的实体类:@Entity
    2. 编写书籍信息表对应的持久化类:@Dao
    3. 编写书籍信息表对应的数据库类,该类从 RoomDatabase 派生而来:@Database
    4. 在自定义的 Application 类中声明书籍数据库的唯一实例
    5. 在操作书籍信息表的地方获取数据表的持久化对象
  • 实例:

    • 实体类:/enity/BookInfo.java

      package com.example.test.enity;
      
      import androidx.room.Entity;
      import androidx.room.PrimaryKey;
      
      @Entity
      public class BookInfo {
      
          @PrimaryKey(autoGenerate = true)
          private int id;
      
          private String name;
          private String author;
          private String press;
          private double price;
      
          // Generate(生成) - Getter and Setter
          public int getId() {
              return id;
          }
      
          public void setId(int id) {
              this.id = id;
          }
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      
          public String getAuthor() {
              return author;
          }
      
          public void setAuthor(String author) {
              this.author = author;
          }
      
          public String getPress() {
              return press;
          }
      
          public void setPress(String press) {
              this.press = press;
          }
      
          public double getPrice() {
              return price;
          }
      
          public void setPrice(double price) {
              this.price = price;
          }
      
          @Override
          public String toString() {
              return "BookInfo{" +
                      "id=" + id +
                      ", name='" + name + '\'' +
                      ", author='" + author + '\'' +
                      ", press='" + press + '\'' +
                      ", price=" + price +
                      '}';
          }
      }
      
    • 持久化类:/dao/BookDao.java(接口 Interface)

      package com.example.test.dao;
      
      import androidx.room.Dao;
      import androidx.room.Delete;
      import androidx.room.Insert;
      import androidx.room.Query;
      import androidx.room.Update;
      
      import com.example.test.enity.BookInfo;
      
      import java.util.List;
      
      @Dao
      public interface BookDao {
      
          @Insert
          void insert(BookInfo... book);
      
          // 删除全部
          @Query("DELETE FROM BookInfo")
          void deleteAll();
      
          // 指定删除项
          @Delete
          void delete(BookInfo... book);
      
          @Update
          int update(BookInfo... book);I
      
          // 查询所有书籍
          @Query("SELECT * FROM BookInfo")
          List<BookInfo> queryAll();
      
          // 按名称查找书籍
          @Query("SELECT * FROM BookInfo WHERE name = :name ORDER BY id DESC limit 1")
          BookInfo queryByName(String name);
          
      }
      
    • 数据库类:/database/BookDatabase.java

      package com.example.test.database;
      
      import androidx.room.Database;
      import androidx.room.RoomDatabase;
      
      import com.example.test.dao.BookDao;
      import com.example.test.enity.BookInfo;
      
      @Database(entities = {BookInfo.class}, version = 1, exportSchema = true)
      public abstract class BookDatabase extends RoomDatabase {
          // 获取该数据库中某张表的持久化对象
          public abstract BookDao bookDao();
      }
      
      • entities:表示该数据库所包含的表

      • version:表示该数据库的版本

      • exportSchema:表示是否导出数据库信息的 json 串,设置为 true 时须在 build.gradle 中指定 json 文件的保存路径,内容如下:

        android {
        	// ...
            defaultConfig {
            	// ...
                javaCompileOptions {
                    annotationProcessorOptions {
                        arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
                    }
                }
            }
        }
        
    • 实例化:/util/MyApplication.java

      package com.example.test.util;
      
      import android.app.Application;
      import androidx.room.Room;
      import com.example.test.database.BookDatabase;
      
      public class MyApplication extends Application {
      
          private static MyApplication mApp;
          // 声明一个书籍数据库对象
          private BookDatabase bookDatabase;
          public static MyApplication getInstance() {
              return mApp;
          }
          // 实例化书籍数据库
          @Override
          public void onCreate() {
              super.onCreate();
              mApp = this;
              // 构建书籍数据库的实例
              bookDatabase = Room.databaseBuilder(this, BookDatabase.class, "book")
                      // 允许迁移数据库(发生数据库变更时,Room 会默认删除原数据库再创建新数据库)
                      .addMigrations()
                      // 允许在主线程中操作数据库(Room 默认不能在主线程中操作数据库)
                      .allowMainThreadQueries()
                      .build();
          }
          // 获取书籍数据库的实例
          public BookDatabase getBookDatabase() {
              return bookDatabase;
          }
      }
      
    • 在 MainActivity.java 中使用 Room

      // ...
      private BookDao bookDao;
      
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          // ...
          // 从 App 实例中获取唯一的书籍持久化对象
          bookDao = MyApplication.getInstance().getBookDatabase().bookDao();
      }
      
      @Override
      public void onClick(View view) {
          String name = et_name.getText().toString();
          String author = et_author.getText().toString();
          String press = et_press.getText().toString();
          String price = et_price.getText().toString();
          BookInfo b1 = new BookInfo();
          b1.setName(name);
          b1.setAuthor(author);
          b1.setPress(press);
          b1.setPrice(Double.parseDouble(price));
          switch (view.getId()) {
              case R.id.btn_ins:
                  bookDao.insert(b1);
                  ToastUtil.show(this, "保存成功");
                  break;
              case R.id.btn_del:
                  bi.setId(1);
                  bookDao.delete(bi);
                  ToastUtil.show(this, "删除成功");
                  break;
              case R.id.btn_upd:
                  BookInfo bTemp = bookDao.queryByName(name);
                  bi.setId(bTemp.getId());
                  bi.setName(name);
                  bi.setAuthor(author);
                  bi.setPress(press);
                  bi.setPrice(Double.parseDouble(price));
                  bookDao.update(bi);
                  break;
              case R.id.btn_sel:
                  BookInfo bIns = bookDao.queryByName(name);
                  Log.d("SRIGT", bIns.toString());
                  break;
              case R.id.btn_sho:
                  StringBuilder str = new StringBuilder();
                  List<BookInfo> list = bookDao.queryAll();
                  for (BookInfo b : list) {
                      Log.d("SRIGT", b.toString());
                      str.append(b).append("\n");
                  }
                  tv.setText(str.toString());
                  break;
              case R.id.btn_cls:
                  bookDao.deleteAll();
                  break;
          }
      }
      

-End-

posted @ 2023-05-01 17:58  SRIGT  阅读(22)  评论(0编辑  收藏  举报