数据存储如今主要有
文件存储、SharedPreferences 存储以及数据库存储
1.文件
文件存储就是新建一个文件以字符串的形式存储数据
不实际上书中并没有提到是否只能以字符串形式存储
书中在这部分的原文是:
它不对存储的内容进行任何的格式化处 理,全部数据都是原封不动地保存到文件当中的,因而它比力得当用于存储一些简朴的文本数据 或二进制数据。假如你想使用文件存储的方式来保存一些较为复杂的文本数据,就必要界说一套 自己的格式规范,如许可以方便之后将数据从文件中重新解析出来。
因此是我先入为主的以为只能存储字符串形式,我并不知道这是否是正确的,如今只是假定云云。
1.存储
文件存储就是新建一个文件以字符串的形式存储数据
不实际上书中并没有提到是否只能以字符串形式存储
书中在这部分的原文是:
它不对存储的内容进行任何的格式化处 理,全部数据都是原封不动地保存到文件当中的,因而它比力得当用于存储一些简朴的文本数据 或二进制数据。假如你想使用文件存储的方式来保存一些较为复杂的文本数据,就必要界说一套 自己的格式规范,如许可以方便之后将数据从文件中重新解析出来。
因此是我先入为主的以为只能存储字符串形式,我并不知道这是否是正确的,如今只是假定云云。
如今看看如何写入文件吧:
- public void save(String inputText){
- FileOutputStream out = null;
- BufferedWriter writer = null;
- try {
- out = openFileOutput("data1", Context.MODE_PRIVATE);
- writer = new BufferedWriter(new OutputStreamWriter(out));
- writer.write(inputText);
- }catch (IOException e){
- e.printStackTrace();
- }finally {
- try {
- if (writer != null){
- writer.close();
- }
- }catch (IOException e){
- e.printStackTrace();
- }
- }
- }
复制代码 这里是通过 openFileOutput()得到一个 FileOutputStream 对象,然后再使用它来构建出一个 OutputStreamWriter 对象,继承使用 OutputStreamWriter 构建出一个 BufferedWriter 对象,然后通过 BufferedWriter 将文本内容写入到文件中。
如今写一个实例来实现这个功能
我并没有按照书上说的来写,我尝试使用了约束布局来进行书写布局
我加入了一个EditText来进行用户输入,并且创建了一个Button来进行保存,而不是像书中那样使用生命周期中的 onDestroy()方法(烧毁),如许会方便很多
- <?xml version="1.0" encoding="utf-8"?>
- <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/main"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- tools:context=".MainActivity">
- <EditText
- android:id="@+id/edit"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:hint="写点东西在这里"
- tools:ignore="MissingConstraints" />
- <Button
- android:id="@+id/button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="保存"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/edit"
- tools:ignore="InvalidId,MissingConstraints" />
-
- </androidx.constraintlayout.widget.ConstraintLayout>
复制代码 然后我们再对MainActivity进行编辑
- private EditText edit;//这个我并不知道是什么,是Android studio要求加上的,颠末搜刮,是用来无视膨胀警告的,为什么会发生这个警告我并不清晰@SuppressLint("MissingInflatedId")@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); EdgeToEdge.enable(this); setContentView(R.layout.activity_main); ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); return insets; }); //书中使用了强转,但实际不必要强转,可能是由于版本不一样? edit = findViewById(R.id.edit); Button button = findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { String inputText = edit.getText().toString(); save(inputText); Toast.makeText(MainActivity.this,"已保存至data1文件",Toast.LENGTH_SHORT).show(); } });}public void save(String inputText){
- FileOutputStream out = null;
- BufferedWriter writer = null;
- try {
- out = openFileOutput("data1", Context.MODE_PRIVATE);
- writer = new BufferedWriter(new OutputStreamWriter(out));
- writer.write(inputText);
- }catch (IOException e){
- e.printStackTrace();
- }finally {
- try {
- if (writer != null){
- writer.close();
- }
- }catch (IOException e){
- e.printStackTrace();
- }
- }
- }
复制代码 这里我使用了按钮来进行save方法的调用,同时使用Toast来表现文字表示成功执行
运行后我查察书中说就会存在到/data/data/com.example.filepersistencetest/files/路径下
由于书中使用的是模仿器,而我并没能成功创建模仿器,因此我使用的是iqoo pro实机,另外,它的Android版本是11
因此我没有看书中查找文件的方式,直接实机操作打开文件管理冲进Android/data/文件,无论使用系统自带文件管理还是mt文件管理器都无法找到com.example.filepersistencetest文件夹。我第一反应是文件没有成功生成,从而开始查抄代码。这浪费了我很多时间,因此在这里提一嘴。
但实际上是生成了的,请放心
后来颠末了一番无用的努力,我尝试使用书中的方法,但我并没有搜刮到一个叫做Android Device Monitor的工具(Ctrl+Shift+A打开搜刮输入Device File Explorer,注:这是第三版才有的内容,我正在学习的第二版并没有,我是查阅的第三版才得到的方法)
但是很遗憾,我还是找到了这个工具
我是在Device Manager功能下选择对应设备最后的三个点发现了一个叫做Open in Device Explorer,哦是的,这就是我在找的文件路径
打开它,与书中所描述的文件路径千篇同等/data/data/com.example.filepersistencetest/files/
在这个目次下我发现了我创建的data1文件,他并没有后缀名。
2.读取
文件读取的代码与文件存储的代码大差不差:
- public String load(){
- FileInputStream in = null;
- BufferedReader reader = null;
- StringBuilder content = new StringBuilder();
- try {
- in = openFileInput("data1");
- reader = new BufferedReader(new InputStreamReader(in));
- String line = "";
- while ((line = reader.readLine()) != null){
- content.append(line);
- }
- }catch (IOException e){
- e.printStackTrace();
- }finally {
- if (reader != null){
- try {
- reader.close();
- }catch (IOException e){
- e.printStackTrace();
- }
- }
- }
- return content.toString();
- }
复制代码 这次是通过 openFileInput()方法获取到 FileInputStream 对象,然后使用它构建出了 InputStreamReader 对象,接着再构建出一个 BufferedReader 对象,如许就可以通过 BufferedReader 一行行地读取,然后把文件中全部的文本内容全部读取出来,并存放在 StringBuilder 对象中,最后将读取到的内容返回。
也只是将方法更换了而已,书写的结构基本没有变
那么如今,读取方法会返回一个字符串,因此我们必要设定一个字符串来接收它
书中是将字符串返回到EditText,我则是新建了一个TextView来接收,并且创建了第二个按钮用来读取代码如下
activity_main.xml
- <TextView
- android:id="@+id/tv"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="0000000"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/button"
- tools:ignore="MissingConstraints" />
- <Button
- android:id="@+id/button2"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="读取"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/tv" />
复制代码 MainActivity.java
- TextView tv = findViewById(R.id.tv);
- Button button2 = findViewById(R.id.button2);
- button2.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- tv.setText(load());
- }
- });
复制代码 如许运行后点击保存按钮在点击读取按钮就可以将当前输入框内的文字表如今TextView中
2.SharedPreferences
Android有三种方法来获取SharedPreferences对象
- Context 类中的 getSharedPreferences()方法
此方法接收两个参数,第一个参数用于指定 SharedPreferences 文件的名称,假如指定的文件 不存在则会创建一个,SharedPreferences 文件都是存放在/data/data//shared_prefs/ 目次下的。第二个参数用于指定操作模式,如今只有 MODE_PRIVATE 这一种模式可选,它是默 认的操作模式,和直接传入 0 结果是相同的,表示只有当前的应用程序才可以对这个 SharedPreferences 文件进行读写。
- Activity 类中的 getPreferences()方法
这个方法和 Context 中的 getSharedPreferences()方法很相似,不外它只接收一个操作模 式参数,由于使用这个方法时会主动将当前活动的类名作为 SharedPreferences 的文件名。
- PreferenceManager 类中的 getDefaultSharedPreferences()方法
这是一个静态方法,它接收一个 Context 参数,并主动使用当前应用程序的包名作为前缀 来定名 SharedPreferences 文件。得到了 SharedPreferences 对象之后,就可以开始向 SharedPreferences 文件中存储数据了,主要可以分为 3 步实现。
(1) 调用 SharedPreferences 对象的 edit()方法来获取一个SharedPreferences.Editor 对象。
(2) 向 SharedPreferences.Editor 对象中添加数据,比如添加一个布尔型数据就使用 putBoolean()方法,添加一个字符串则使用 putString()方法,以此类推。
(3) 调用 apply()方法将添加的数据提交,从而完成数据存储操作。
1.SharedPreferences 存储
如今开始存储数据的操作
同样的使用约束布局创建一个Button
- <Button
- android:id="@+id/button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="存储"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent" />
复制代码 然后再MainActivity.java文件中编辑
- Button button = findViewById(R.id.button);
- button.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- SharedPreferences.Editor editor = getSharedPreferences("data",
- MODE_PRIVATE).edit();
- editor.putString("name","Tom");
- editor.putInt("age",20);
- editor.putBoolean("married",false);
- editor.apply();
- }
- });
复制代码 是的这个按钮执行的便是存储操作
editor.putXXX便是存储方法
运行后点击按钮
如今使用相同的方法前去/data/data/com.example. sharedpreferencestest/shared_prefs/目次下
我们会发现一个data.xml文件内容是
- <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
- <map>
- <string name="name">Tom</string>
- <boolean name="married" value="false" />
- <int name="age" value="20" />
- </map>
复制代码 是的它是使用xml进行存储数据的
2.SharedPreferences 读取
我们在存储操作时使用的是putxxx,而读取更加简朴,只必要将put改为get
get方法有两个参数,第一个是键就是存储时的"name"等等,第二个是默认值,是没有找到对应数据时返回的值
如今我们新建一个按钮用来触发读取,在创建一个TextView用来表现数据
- <Button
- android:id="@+id/button2"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="读取"
- app:layout_constraintBottom_toTopOf="@+id/button"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent" />
- <TextView
- android:id="@+id/textView"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="TextView"
- app:layout_constraintBottom_toTopOf="@+id/button2"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent" />
复制代码 然后再MainActivity中写表现逻辑
- Button button1 =findViewById(R.id.button2);
- button1.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- SharedPreferences pref = getSharedPreferences("data",MODE_PRIVATE);
- String name = pref.getString("name","");
- int age = pref.getInt("age",0);
- boolean married = pref.getBoolean("married",false);
- TextView tv = findViewById(R.id.textView);
- tv.setText("姓名:"+ name + "年龄:" + age + "婚配:" + married);
- }
- });
复制代码 如许我们先点击存储按钮再点击读取按钮就会发现textview的表现发生了改变
3.SQLite 数据库
SQLite 是一款轻量级的关系型数据库
Android 正是把这个功能极为强大的数据库嵌入到了系统当中
实用于存储大量复杂的关系型数据
SQLiteOpenHelper 是一个抽象类有两个抽象方法,分别是 onCreate() 和 onUpgrade()
SQLiteOpenHelper 中还有两个非常紧张的实例方法:getReadableDatabase()和 getWritableDatabase()。这两个方法都可以创建或打开一个现有的数据库(假如数据库已存在则直接打开,否则创建一个新的数据库),并返回一个可对数据库进行读写操作的对象。不同的是,当数据库不可写入的时间(如磁盘空间已满),getReadableDatabase()方法返回的对象将以只读的方式去打开数据库,而 getWritableDatabase()方法则将出现异常。
1.创建SQLite 数据库
起首呢由于SQLiteOpenHelper 是一个抽象类所以我们必要新建一个类来继承这个抽象类并重写抽象方法
新建 MyDatabaseHelper
- package com.example.databasetest;
- import android.content.Context;
- import android.database.sqlite.SQLiteDatabase;
- import android.database.sqlite.SQLiteOpenHelper;
- import android.widget.Toast;
- import androidx.annotation.Nullable;
- public class MyDatabaseHelper extends SQLiteOpenHelper {
- public static final String CREATE_BOOK = "create table Book ("
- +"id integer primary key autoincrement, "
- +"author text, "
- +"price real, "
- +"name text) ";
- private Context mContext;
- 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, "创建成功", Toast.LENGTH_SHORT).show();
- }
- @Override
- public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
- }
- }
复制代码 我们可以看到,创建数据库的SQL指令是以字符串的形式进行存储的
而在表面的onCreate方法也就是创建方法中,我们使用sqLiteDatabase.execSQL(CREATE_BOOK);方法来执行字符串形式的SQL指令,并发出Toast来提示已经创建成功.
我们来到activity_main.xml文件,添加一个button用来执行创建指令
- <Button
- android:id="@+id/button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="创建数据库"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent" />
复制代码 如今来修改MainActivity.java
- MyDatabaseHelper dbHelper = new MyDatabaseHelper(this,"BookStore.db",null,1);
- Button button1 = findViewById(R.id.button);
- button1.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- dbHelper.getWritableDatabase();
- }
- });
复制代码 我们可以看到,创建了一个我们刚刚创建的类MyDatabaseHelper对象,并传入了this,"BookStore.db",null,1这四个参数
此中this是Context
"BookStore.db"是数据库名
第三个参数允许我们在查询数据的时间返回一个自界说的 Cursor,一般都是传入 null
第四个参数 1 表示当前数据库的版本号
按下按钮则是调用了getWritableDatabase()方法
如今我们运行程序
点击一次按钮会弹出Toast提示
再次点击就没有了
这是由于我们的Toast写在数据库的onCreate(创建)方法
getWritableDatabase()找不到指定的数据库所以进行了创建,调用了onCreate方法
之后都能找到,就不会调用onCreate方法了
之后是查询数据库是否创建成功,这部分我进行了跳过
2.升级数据库
这部分非常简朴在我们的MyDatabaseHelper类中有一个onUpgrade()的方法
当我们在MainActivity中使用的new MyDatabaseHelper()时将最后一个参数改为比之前的参数更大的值就会触发onUpgrade()方法
书中使用的例子是将1改为2,然后再onUpgrade()方法中删除原本的表并触发onCreate()方法来重新创建表代码如下
MyDatabaseHelper
- package com.example.databasetest;
- import android.content.Context;
- import android.database.sqlite.SQLiteDatabase;
- import android.database.sqlite.SQLiteOpenHelper;
- import android.widget.Toast;
- import androidx.annotation.Nullable;
- public class MyDatabaseHelper extends SQLiteOpenHelper {
- public static final String CREATE_BOOK = "create table Book ("
- +"id integer primary key autoincrement, "
- +"author text, "
- +"price real, "
- +"name text) ";
- public static final String CREATE_CATEGORY = "create table Category ("
- +"id integer primary key autoincrement, "
- +"category_name text, "
- +"category_code integer) ";
- private Context mContext;
- 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);
- sqLiteDatabase.execSQL(CREATE_CATEGORY);
- Toast.makeText(mContext, "创建成功", Toast.LENGTH_SHORT).show();
- }
- @Override
- public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
- sqLiteDatabase.execSQL("drop table if exists Book");
- sqLiteDatabase.execSQL("drop table if exists Category");
- onCreate(sqLiteDatabase);
- }
- }
复制代码 MainActivity只必要把1改为2 就行,这里就不放代码了
3.对数据库进行操作
我们可以对数据库进行添加C(Create),R 代 表查询(Retrieve),U 代表更新(Update),D 代表删除(Delete)这四个操作简称为CRUD
在 Android 中可以不编写 SQL 语句,也能完成以上CRUD
前面我们已经知道,调用 SQLiteOpenHelper 的 getReadableDatabase()或 getWritableDatabase()方法是可以用于创建和升级数据库的,而且这两个方法还都会返回一个 SQLiteDatabase 对象
我们可以使用他们返回的对象来进行CRUD
1.添加
SQLiteDatabase 中提供了一 个 insert()方法,这个方法就是专门用于添加数据的
第一个参数是表名
第二个是用于在未指定添加数据的环境下给某些可为空的列主动赋值 NULL,一般用不到,直接传入 null
第三个参数是一个 ContentValues 对象,它提供了一系列的 put()方法重载,用于向 ContentValues 中添加数据,必要传入将表中的每个列名以及相应的待添加数据
添加一个按钮用来触发添加数据
- <Button
- android:id="@+id/button2"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="添加数据"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/button" />
复制代码 写按钮被按下的逻辑
- Button button2 = findViewById(R.id.button2);
- button2.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- SQLiteDatabase db = dbHelper.getWritableDatabase();
- ContentValues values = new ContentValues();
- //数据
- values.put("name", "The Da Vinci Code");
- values.put("author", "Dan Brown");
- values.put("price", 16.96);
- //插入数据
- db.insert("Book", null, values);
- }
- });
复制代码 先获取 SQLiteDatabase 对象,然后使用 ContentValues 来对数据进行组装。这里只对 Book 表里此中四列的数据进行了组装,id 那一列没给它赋值。由于在前面创建表的时间,我们将 id 列设置为自增长,它的值会在入库的时间主动生成。接下来调用 insert()方法将数据添加到表当中。
由于我前面创建表的时间没有创建页数,所以这里也比原文少了页数一条(懒得加就直接删了)
2.修改
SQLiteDatabase 中也提供了一个非常好用的 update()方法,用于对数据进行更新
第一个参数还是表名,在这里指定去更新哪张表里的数据。
第二个参数也还是 ContentValues 对象,要把更新数据在这里组装进去。
第三、第四个参数则是用于约束,更新某一行或某几行中的数据,不指定的话默认就是更新全部行
起首还是创建一个按钮
- Button
- android:id="@+id/button3"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:text="修改数据"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/button2" />
复制代码 然后就是写逻辑
- Button button3 = findViewById(R.id.button3);
- button3.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- SQLiteDatabase db = dbHelper.getWritableDatabase();
- ContentValues values = new ContentValues();
- values.put("price", 10.99);
- db.update("Book", values, "name = ?", new String[] { "The Da Vinci Code"});
- }
- });
复制代码 前面的就不说了
从第三行开始,我们只给 ContentValues了一个一组数据,只改动价格变成10.99
第四行是更新此中"name = ?"中的?是一个占位符,而紧随其后的new String[] { "The Da Vinci Code"}这个数组则是用来对第三个参数中的每一个?提供相应的内容
这里的意思是给Book表中的全部名字是The Da Vinci Code的价格改为10.99
3.删除
SQLiteDatabase 中提供了一个 delete()方法,专门用于删除数据
这个方法接收 3 个参数,
第一个参数仍旧是表名,
第二、第三个参数还是用于约束删除某一行或某几行的数据,不指定的话默认就是删除全部行。
同样的创建按钮
- <Button
- android:id="@+id/button4"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="删除数据"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/button3" />
复制代码 按钮的逻辑
- Button button4 = findViewById(R.id.button4);
- button4.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- SQLiteDatabase db = dbHelper.getWritableDatabase();
- db.delete("Book", "price < ?", new String[] { "11" });
- }
- });
复制代码 由于上面添加的时间只添加了一个,所以这里我将删除的条件改为了price小于11
我们只有一本书并且在上次修改事后他的价格是10.99刚好小于11
所以删除后会将这本书删除
4.查询数据
终于到查询了,可以直观的验证我们以上的全部逻辑了
这里只会先容 Android 上的查询功能,假如你对 SQL 语言非常感兴趣,可以找一本专门先容 SQL 的书进行学习
SQLiteDatabase 中还提供了一个 query()方法用于对数据进行查询。
这个方法的参数非常复杂,最短的一个方法重载也必要传入 7 个参数
第一个参数不用说,当然还是表名。
第二个参数用于指定去查询哪几列,假如不指定则默认查询全部列。
第三、第四个参数用于约束查询某一行或某几行的数据,不指定则默认查询全部行的数据。
第五个参数用于指定必要去group by 的列,不指定则表示不对查询结果进行 group by 操作。
第六个参数用于对 group by 之后的数据进行进一步的过滤,不指定则表示不进行过滤。
第七个参数用于指定查询结果的排序方式,不指定则表示使用默认的排序方式
似乎很多但是没事,我们继承
我们还是新建一个按钮
- <Button
- android:id="@+id/button5"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="12dp"
- android:text="查询"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/button4" />
复制代码 然后是我们的逻辑
- Button button5 = findViewById(R.id.button5);
- button5.setOnClickListener(new View.OnClickListener() {
- @SuppressLint("Range")
- @Override
- public void onClick(View v) {
- SQLiteDatabase db = dbHelper.getWritableDatabase();
- // 查询 Book 表中所有的数据
- Cursor cursor = db.query("Book", null, null, null, null, null, null);
- if (cursor.moveToFirst()) {
- do {
- // 遍历 Cursor 对象,取出数据并打印
- String name = cursor.getString(cursor.getColumnIndex("name"));
- String author = cursor.getString(cursor.getColumnIndex
- ("author"));
- double price = cursor.getDouble(cursor.getColumnIndex
- ("price"));
- Log.d("MainActivity", "book name is " + name);
- Log.d("MainActivity", "book author is " + author);
- Log.d("MainActivity", "book price is " + price);
- } while (cursor.moveToNext());
- }
- cursor.close();
- }
- });
复制代码 这里第一个参数指明去查询 Book 表,背面的参数全部为 null。这就表示盼望查询这张表中的全部数据。
查询完之后就得到了一个 Cursor 对象,接着我们调用它的 moveToFirst()方法将数据的指针移动到第一行的位置(假如没有数据这个方法返回false),然后进入循环去遍历查询到的每一行数据。
在这个循环中可以通过 Cursor 的 getColumnIndex()方法获取到某一列在表中对应的位置索引,然后将这个索引传入到相应的取值方法中,就从数据库中读取到数据了
最后在使用log来打印
假如你盼望在这里对之前的按钮进行测试,你可以选择将应用程序卸载,然后重新运行
也可以选择将前面说的版本号写一个更大的数,如许你就可以通过log测试之前的按钮是否成功。
这里是最开始设置的值与修改事后的值,删除后就没有数据了,所以不放图
5.使用SQL语句操作
添加数据的方法如下:
- db.execSQL("insert into Book (name, author, pages, price) values(?, ?, ?, ?)", new String[] { "The Da Vinci Code", "Dan Brown", "454", "16.96" });
- db.execSQL("insert into Book (name, author, pages, price) values(?, ?, ?, ?)", new String[] { "The Lost Symbol", "Dan Brown", "510", "19.95" });
复制代码 更新数据的方法如下:
- db.execSQL("update Book set price = ? where name = ?", new String[] { "10.99", "The Da Vinci Code" });
复制代码 删除数据的方法如下:
- db.execSQL("delete from Book where pages > ?", new String[] { "500" });
复制代码 查询数据的方法如下:
- db.rawQuery("select * from Book", null);
复制代码 除了查询数据的时间调用的是 SQLiteDatabase 的 rawQuery()方法,其他的操作 都是调用的 execSQL()方法
4.事件
此节来自第三版,第二版的LitePal似乎已被弃用,如今搜刮github页面可以发现上次更新已是3年前
事件的使用场景:
比如你正在进行 一次转账操作,银行会先将转账的金额从你的账户中扣除,然后再向收款方的账户中添加等量 的金额。看上去似乎没什么题目吧?可是,假如当你账户中的金额刚刚被扣除,这时由于一些 异常原因导致对方收款失败,这一部分钱就凭空消散了!当然银行肯定已经充分考虑到了这种环境,它会包管扣款和收款的操作要么一起成功,要么都不会成功
直接开始尝试吧
起首还是添加一个按钮
- <Button
- android:id="@+id/button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="替换"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/button5" />
复制代码 按钮逻辑,我将书中的kotlin语言改为了java版本
- Button button6 = findViewById(R.id.button);
- button6.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- SQLiteDatabase db = dbHelper.getWritableDatabase();
- db.beginTransaction();//开启事务
- try {
- db.delete("Book", null, null);
- // if (true) {
- // // 手动抛出一个异常,让事务失败
- // throw new NullPointerException();
- // }
- ContentValues values = new ContentValues();
- values.put("name","游戏");
- values.put("author","游戏1");
- values.put("price","20.85");
- db.insert("Book",null,values);
- db.setTransactionSuccessful();//事务执行成功
- }catch (Exception e){
- e.printStackTrace();
- }finally {
- db.endTransaction();//结束事务
- }
- }
- });
复制代码 这里是调用SQLiteDatabase的 beginTransaction()方法开启一个事件
然后在捕获异常代码块的最后调用setTransactionSuccessful()来表示事件成功运行了
最后在finally代码块中调用endTransaction()结束事件
而在删除数据的代码之后我们写了一个if代码来抛出一个异常,使事件执行失败
当if没被注释时点击更换后再查询还是老数据(删除数据代码未执行)
但if注释后,点击更换后变成了新数据
5.升级数据库的最佳写法
之前的升级数据库是删除后重新添加
这天然是不可以的,这会使原本的数据丢失
升级可以依靠数据库的版本号来进行升级代码示例如下
如今只必要创建一个表就可以了代码需求很简朴
- package com.example.databasetest;
- import android.content.Context;
- import android.database.sqlite.SQLiteDatabase;
- import android.database.sqlite.SQLiteOpenHelper;
- import androidx.annotation.Nullable;
- public class MyDatabaseHelper1 extends SQLiteOpenHelper {
- private String createBook = "create table Book (" +
- " id integer primary key autoincrement," +
- "author text," +
- "price real," +
- "pages integer," +
- "name text)";
- private Context mContext;
- public MyDatabaseHelper1(@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(createBook);
- }
- @Override
- public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
- }
- }
复制代码 这次必要向数据库中再添加一张Category表。于是,修改 MyDatabaseHelper中的代码
- package com.example.databasetest;
- import android.content.Context;
- import android.database.sqlite.SQLiteDatabase;
- import android.database.sqlite.SQLiteOpenHelper;
- import androidx.annotation.Nullable;
- public class MyDatabaseHelper1 extends SQLiteOpenHelper {
- private String createBook = "create table Book (" +
- " id integer primary key autoincrement," +
- "author text," +
- "price real," +
- "pages integer," +
- "name text)";
- private String createCategory = "create table Category (" +
- "id integer primary key autoincrement," +
- "category_name text," +
- "category_code integer)";
- private Context mContext;
- public MyDatabaseHelper1(@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(createBook);
- }
- @Override
- public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
- if (i1 <= 1){
- sqLiteDatabase.execSQL(createCategory);
- }
- }
- }
复制代码 在onCreate()方法里我们新增了一条建表语句,然后又在onUpgrade()方法中添 加了一个if判断,假如用户数据库的旧版本号小于等于1,就只会创建一张Category表
如许当用户直接安装第2版的程序时,就会进入onCreate()方法,将两张表一起创建。而当用 户使用第2版的程序覆盖安装第1版的程序时,就会进入升级数据库的操作中,此时由于Book表已经存在了,因此只必要创建一张Category表即可
这次要给Book表和Category表之间建立关联,必要在Book 表中添加一个category_id字段
- package com.example.databasetest;
- import android.content.Context;
- import android.database.sqlite.SQLiteDatabase;
- import android.database.sqlite.SQLiteOpenHelper;
- import androidx.annotation.Nullable;
- public class MyDatabaseHelper1 extends SQLiteOpenHelper {
- private String createBook = "create table Book (" +
- " id integer primary key autoincrement," +
- "author text," +
- "price real," +
- "pages integer," +
- "name text," +
- "category_id integer)" ;
- private String createCategory = "create table Category (" +
- "id integer primary key autoincrement," +
- "category_name text," +
- "category_code integer)";
- private Context mContext;
- public MyDatabaseHelper1(@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(createBook);
- }
- @Override
- public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
- if (i1 <= 1){
- sqLiteDatabase.execSQL(createCategory);
- }
- if (i1 <= 2) {
- sqLiteDatabase.execSQL("alter table Book add column category_id integer");
- }
- }
- }
复制代码 我们在Book表的建表语句中添加了一个category_id列,如许当用户直接安装第3版的程序时,这个新增的列就会直接新建。但是假如有用户必要覆盖安装,就会进入升级数据库的操作。在onUpgrade()方法里,我们添加了一个条件,假如当前数据库的版本号是2,就会执行alter命令,为Book表新增一个category_id列。
每当升级一个数据库版本的时间,onUpgrade()方法里都一定要写一个相应的if判断语句。可以包管App在跨版本升级的时间, 每一次的数据库修改都能被全部执行。比如用户当前是从第2版升级到第3版,那么只有第二条判断语句会执行,而假如用户是直接从第1版升级到第3版,那么两条判断语句都会执行。使用这种方式来维护数据库的升级,不管版本怎样更新,都可以包管数据库的表结构是最新的,而且表中的数据完全不会丢失。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |