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 基础语法
数据定义
创建表格
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
删除表格
DROP TABLE IF EXISTS user_info;
修改表结构
SQLite 只支持增加字段,不支持修改/删除字段,且每次智能添加一列字段
ALTER TABLE user_info ADD COLUMN age INTEGER;
数据操纵语言
添加
INSERT INTO user_info (name, age) VALUES ('张三', 20);
删除
DELETE FROM user_info WHERE name='张三'
修改
UPDATE user_info SET age=21 WHERE name
查询
SELECT name FROM table WHERE name='张三'
排序输出结果
SELECT * FROM user_info ORDER BY age ASC;
- ASC:升序
- DESC:降序
b. 数据库管理器 SQLiteDatabase
SQLiteDatabase 是 SQLite 的数据库管理类,提供了若干操作数据表的 API,包含以下三个类
数据处理类,用于数据表层面的操作
execSQL()
:执行拼接好的 SQL 控制语句insert()
:插入一条记录update()
:更新符合条件的记录delete()
:删除符合条件的记录query()
:执行查询操作,返回结果集的游标rawQuery()
:执行拼接好的 SQL 查询语句,返回结果集的游标
使用详情见UserDBHelper
事务类,用于事务层面的操作
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
)时,通过添加事务的方法,避免出现仅成功添加第一条记录。事务若未成功,则通过回滚的措施取消本次添加操作管理类,用于数据库层面的操作
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()
- 封装保证数据库安全的必要方法
- 提供对表记录进行增删改查的操作方法
- 新建一个继承自 SQLiteOpenHelper 的数据库操作类,提示重写
- 游标
- 调用 SQLiteDatabase 的
query()
和rawQuery()
方法时,返回的都是 Cursor 对象,获取查询结果需要根据游标的指示遍历结果集合,包含以下三个类:- 游标控制类,用于指定游标的状态
close()
:关闭游标isClosed()
:判断游标是否关闭isFirst()
:判断游标是否在开头isLast()
:判断游标是否在末尾
- 游标移动类,用于包游标移动到指定位置
move()
:往后移动若干条记录moveToFirst()
:移动游标到开头moveToLast()
:移动游标到末尾moveToNext()
:移动游标到下一条记录moveToPrevious()
:移动游标到上一条记录moveToPosition()
:移动游标到指定位置的记录
- 获取记录类,用于获取记录的数量、类型、取值
getCount()
:获取记录数量getInt()
:获取指定字段整型值getLong()
:获取指定字段长整型值getFloat()
:获取指定字段浮点数值getString()
:获取指定字段的字符串值getType()
:获取指定字段的字段类型
- 游标控制类,用于指定游标的状态
- 调用 SQLiteDatabase 的
举例:人员信息表的增删改查
/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 把外部存储分成了两部分:
- 仅应用本身可以访问的私有空间
- 所有应用都可以访问的公共空间
- 获取外部存储空间(存储卡)的路径:
- 公共空间:Environment 类的
getExternalStoragePublicDirectory()
方法 - 私有空间:Environment 类的
getExternalFilesDir()
方法
- 公共空间:Environment 类的
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, "保存成功"); // 消息提示
外部存储的私有空间
directory = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString();
外部存储的公共空间
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" />
内部存储的私有空间
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
Bitmap bitmap1 = FileUtil.openImage(path);
iv.setImageBitmap(bitmap1);
Bitmap bitmap1 = BitmapFactory.decodeFile(path);
iv.setImageBitmap(bitmap1);
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 框架的编码步骤(以录入书籍信息为例,共以下五步)
- 编写书籍信息表对应的实体类:
@Entity
- 编写书籍信息表对应的持久化类:
@Dao
- 编写书籍信息表对应的数据库类,该类从 RoomDatabase 派生而来:
@Database
- 在自定义的 Application 类中声明书籍数据库的唯一实例
- 在操作书籍信息表的地方获取数据表的持久化对象
- 编写书籍信息表对应的实体类:
实例:
实体类:/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-
Android 开发入门(5)的更多相关文章
- [译]:Xamarin.Android开发入门——Hello,Android Multiscreen深入理解
原文链接:Hello, Android Multiscreen_DeepDive. 译文链接:Xamarin.Android开发入门--Hello,Android Multiscreen深入理解. 本 ...
- [译]:Xamarin.Android开发入门——Hello,Android深入理解
返回索引目录 原文链接:Hello, Android_DeepDive. 译文链接:Xamarin.Android开发入门--Hello,Android深入理解 本部分介绍利用Xamarin开发And ...
- [译]:Xamarin.Android开发入门——Hello,Android快速上手
返回索引目录 原文链接:Hello, Android_Quickstart. 译文链接:Xamarin.Android开发入门--Hello,Android快速上手 本部分介绍利用Xamarin开发A ...
- 教我徒弟Android开发入门(一)
前言: 这个系列的教程是为我徒弟准备的,也适合还不懂java但是想学android开发的小白们~ 本系列是在Android Studio的环境下运行,默认大家的开发环境都是配置好了的 没有配置好的同学 ...
- Android开发入门经典【申明:来源于网络】
Android开发入门经典[申明:来源于网络] 地址:http://wenku.baidu.com/view/6e7634050740be1e650e9a7b.html?re=view
- Android开发入门要点记录:四大组件
cocos2dx跨平台开发中需要了解android开发,昨天快速的浏览了一本Android开发入门教程,因为之前也似懂非懂的写过Activity,Intent,XML文件,还有里面许多控件甚至编程思想 ...
- Android开发入门
教我徒弟Android开发入门(一) 教我徒弟Android开发入门(二) 教我徒弟Android开发入门(三) 出处:http://www.cnblogs.com/kexing/tag/Androi ...
- android开发入门经验 ADT Bundle环境搭建
现在有许多做开发的转做移动端开发,做J2EE的转做Android开发,我也把自己的一些入门经验与大家分享一下,希望能给你带来帮助. 工具/原料 JDK,ADT,JAVA 方法/步骤 开发工具的准备 ...
- [Android]Android开发入门之HelloWorld
引言:在做Unity开发的时候,发现这么个问题,虽然Unity是跨平台的,能够进行Android,IOS,Web,PC等开发,但如果要实现一些稍微系统层的东西,还是需要通过通信,调用原系统的接口(自定 ...
- 教我徒弟Android开发入门(二)
前言: 上一期实现了简单的QQ登录效果,这一期继续对上一期进行扩展 本期的知识点: Toast弹窗,三种方法实现按钮的点击事件监听 正文: Toast弹窗其实很简单,在Android Studio ...
随机推荐
- 接口自动化有多少case?覆盖率是多少?执行完需要多久?
case根据接口数量而定,比如两百个接口,大概有5000个用例,一个接口大概有25到30个用例,一个接口大概200ms左右响应时间 覆盖率能达到95%以上,有时候可以达到百分之百,所有接口自动化用例执 ...
- TLS原理与实践(四)国密TLS
主页 个人微信公众号:密码应用技术实战 个人博客园首页:https://www.cnblogs.com/informatics/ 引言 TLS作为保证网络通信安全的关键技术和基石被广泛应用,但目前主流 ...
- A left join B B表有多条记录,max(create_time)取最新一条
例如:A表合同表t_contract B表合同审核表t_contract_audit.两个表根据contract_id关联.且一条合同有多条审核记录.求:A.合同状态.B.最新审核记录结果. 简单: ...
- RC4算法:流密码算法的经典之作
一.RC4算法的起源与演变 RC4算法是由著名密码学家Ron Rivest在1987年设计的一种流密码算法,其名字来源于Rivest Cipher 4.RC4算法简单高效,被广泛应用于数据加密和网络安 ...
- 摆脱鼠标系列 - vscode 软件 最大化快捷键 - win + ↑
摆脱鼠标系列 - vscode 软件 最大化快捷键 - win + ↑ vscode默认打开不是最大化,所以按 win + 上箭头 使其最大化 不想按 F11 那个不太方便,左上角就没有项目名称了 优 ...
- 32位数字电位器AD5228使用及调试总结
一 概念 什么是数字电位计? 数字电位器(Digital Potentiometer)亦称数控可编程电阻器,是一种代替传统机械电位器(模拟电位器)的新型CMOS数字.模拟混合信号处理的集成电路.数字电 ...
- cpprestsdk移植到mingw,项目上传至github
如题 https://github.com/bbqz007/cpprestsdk4mingw 移植过程解决的问题,下面列出其中一些问题: 1. mingw对#pragma once支持不好. 须要在所 ...
- [置顶]
tomcat处理请求导致页面出现ERR_CONNECTION_RESET错误解决方案
现象: 浏览器发送请求到servlet,servlet处理时间太久,所以导致chrome浏览器出现ERR_CONNECTION_RESET错误 解决方案: 在相应servlet执行最后添加一句代码: ...
- LinuxDNS分析从入门到放弃(记一次有趣的dns问题排查记录,ping 源码分析,getaddrinfo源码分析)
PS:要转载请注明出处,本人版权所有. PS: 这个只是基于<我自己>的理解, 如果和你的原则及想法相冲突,请谅解,勿喷. 环境说明 ubuntu 18.04 前言 我们这里有一块 ...
- Moe RE - 【bugku】
发现好像没人写wp,虽然很简单但是写一个.... 题目 分析 下载文件打开,习惯首先丢到Exeinfo PE里看看有没有壳 没有壳的样子 那放心丢到IDA(64-bit)里面 一进去就看到很有嫌疑的字 ...