第一行代码笔记⑤


持久化技术

5.1 文件存储

存储数据:利用java流实现

public class MainActivity extends AppCompatActivity {

    private EditText edit;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        edit = (EditText)findViewById(R.id.edit);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        String inputText = edit.getText().toString();
        save(inputText);
    }

    public void save(String inputText){
        FileOutputStream out = null;
        BufferedWriter writer = null;
        try {
            out = openFileOutput("data", 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();
            }
        }
    }

}

存储的默认路径是/data/data//files/

从文件中读取:

public class MainActivity extends AppCompatActivity {

    private EditText edit;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        edit = (EditText) findViewById(R.id.edit);
        String inputText = load();
        if (!TextUtils.isEmpty(inputText)) {
            edit.setText(inputText);
            edit.setSelection(inputText.length()); // 将光标移动到文本的末尾
            Toast.makeText(this, "Restoring succeeded",
                    Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        String inputText = edit.getText().toString();
        save(inputText);
    }

    public void save(String inputText) {
        FileOutputStream out = null;
        BufferedWriter writer = null;
        try {
            out = openFileOutput("data", 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();
            }
        }
    }

    public String load() {
        FileInputStream in = null;
        BufferedReader reader = null;
        StringBuilder context = new StringBuilder();
        try {
            in = openFileInput("data");
            reader = new BufferedReader(new InputStreamReader(in));
            String line = "";
            while ((line = reader.readLine()) != null) {
                context.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return context.toString();
    }

}onCreate()方法中调用load方法去读取文件中存储的文本内容,如果读到的内容不为null,就调用EditTextsetText()方法将内容填充到EditText里,并调用setSelection()方法将输入光标移动到文本的末尾位置以便于继续输入,然后弹出一句还原成功的提示

5.2 SharedPreferences存储

文件存储不适合保存一些较为复杂的数据。不同于文件的存储方式,SharedPreferences是使用键值对的方式来存储数据。
存储的默认路径是/data/data//shared_prefs/

要想使用SharedPreferences来存储数据,首先要得到SharedPreferences的对象,android中提供了三种方法

1Context类中的getSharedPreferences()方法  第一个参数指定文件名称,第二个参数指定操作模式
2Acitivity类中的getPreferences()方法  只有一个参数,指定操作模式,会自动将当前或者的类名作为SharedPreferences文件名
3PreferenceManager类的getDefaultSharedPreferences()方法 ,该方法是一个静态方法,接受一个Context参数

得到SharedPreferences对象以后,就可以向SharedPreferences文件中存储对象了,分为三个步骤实现

1、调用SharedPreferences对象的edit()方法,得到一个SharedPreferences.Editor对象
2、向SharedPreferences.Editor对象中添加数据,putBoolean()putString()3、调用apply()方法将已添加的数据提交,从而完成数据存储操作
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button saveData = (Button) findViewById(R.id.save_data);
        saveData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SharedPreferences.Editor editor = getSharedPreferences("data", MODE_PRIVATE).edit();
                editor.putString("name", "Tom");
                editor.putInt("age", 28);
                editor.putBoolean("married", false);
                editor.apply();
            }
        });
        Button restoreData = (Button) findViewById(R.id.restore_data);
        restoreData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SharedPreferences pref = getSharedPreferences("data", MODE_PRIVATE);
                String name = pref.getString("name", "");
                int age = pref.getInt("age", 0);
                boolean married = pref.getBoolean("married", false);
                Log.d("MainActivity", "name is " + name);
                Log.d("MainActivity", "age is " + age);
                Log.d("MainActivity", "married is " + married);

            }
        });
    }
}

5.3 SQLite数据库存储

前面提到的两种存储仍然只是适用于保存一些简单的数据。Android其实内置了数据库。同时Android为了让我们能够方便地管理数据库,专门提供了一个SQLiteOpenHelper帮助类,借助这个类就可以非常简单地对数据库进行创建和升级

存储的默认路径是/data/data//databases/

1 创建数据库

新建类
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)";

    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 db) {
        db.execSQL(CREATE_BOOK);
        Toast.makeText(mContext,"Create succeeded",Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}

修改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.db",null,1);
        Button createDatabase = (Button) findViewById(R.id.create_database);
        createDatabase.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dbHelper.getWritableDatabase();
            }
        });
    }
}
修改xml文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/create_database"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Create database" />

</LinearLayout>

运行以后,点击Create database按钮就会创建数据库,使用adb shell去查看,需要注意的是,这里需要将SDK的platform-tools添加到系统变量里面。

2 升级数据库

上面的MyDatabaseHelper中有一个空方法onUpgrade()
如果此时我们再建一张表,onCreate()方法不会再执行,因为此时已经有BookStore.db数据库了。解决办法也很简单,可以直接将程序卸载掉,再重新运行,再创建就可以了,但是每次都通过卸载程序去新增一张表无疑是很极端的做法。使用SQLiteOpenHelper的升级功能就可以轻松的解决这个问题。

修改构造函数,只需要将数据库的版本号,之前是1,改成比1大的数,就会让onUpgrade()方法执行

        dbHelper = new MyDatabaseHelper(this,"BookStore.db",null,2);
@Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("drop table if exists Book");
        db.execSQL("drop table if exists Category");
        onCreate(db);
    }

adb查看结果如下图所示
在这里插入图片描述
在这里插入图片描述

3 操作数据

操作无非四种,即CRUD。
Create 添加
Retrieve 查询
Update 更新
Delete 删除

添加数据

Button addData = (Button) findViewById(R.id.add_data);
        addData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SQLiteDatabase db = dbHelper.getWritableDatabase();
                ContentValues values = new ContentValues();
                // 开始组装第一条数据
                values.put("name","The Da Vinci Code");
                values.put("author","Dan Brown");
                values.put("pages",454);
                values.put("price",16.96);
                // 插入第一条数据
                db.insert("Book",null,values);

                values.clear();
                // 开始组装第二条数据
                values.put("name","The Lost Symbol");
                values.put("author","Dan Brown");
                values.put("pages",510);
                values.put("price",19.95);
                // 插入第二条数据
                db.insert("Book",null,values);

            }
        });

id那一列我们没有进行封装,因为它是自增键,同时需要注意的是,每个虚拟机启动以后,数据库好像不会丢失,除非你删掉重建一个新的虚拟机。

更新数据

        Button updateData = (Button) findViewById(R.id.update_data);
        updateData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SQLiteDatabase db = dbHelper.getWritableDatabase();
                ContentValues values = new ContentValues();
                values.put("price",10.99);
                // 第三四个参数,是约束更新的行数,即将名字是The Da Vinci Code的价格改为10.99
                db.update("Book",values,"name = ?",new String[]{"The Da Vinci Code"});
            }
        });

删除数据

Button deleteButton = (Button) findViewById(R.id.delete_data);
        deleteButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SQLiteDatabase db = dbHelper.getWritableDatabase();
                // 通过第二、三个参数,去指定删除页数超过500页的书
                db.delete("Book","pages>?",new String[]{"500"});
            }
        });

查询数据

Button queryButton = (Button) findViewById(R.id.query_data);
        queryButton.setOnClickListener(new View.OnClickListener() {
            @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对象,取出数据并打印
                        // 下面这个方法会报红,是因为编译器检查,传入值要大于等于0
                        // 而getColumnIndex方法返回值是从-1开始的,存在不符合要求的可能性,就报错
                        // 可以使用注解忽视警告
                        @SuppressLint("Range") String name = cursor.getString(cursor.getColumnIndex("name"));
                        @SuppressLint("Range") String author = cursor.getString(cursor.getColumnIndex("author"));
                        @SuppressLint("Range") int pages = cursor.getInt(cursor.getColumnIndex("pages"));
                        @SuppressLint("Range") 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 pages is " + pages);
                        Log.d("MainActivity", "book price is " + price);
                    } while (cursor.moveToNext());
                }
                cursor.close();
            }
        });

使用SQL操作数据库

        SQLiteDatabase db = dbHelper.getWritableDatabase();
        // 添加数据
        db.execSQL("insert into Book(name,author,pages,price) values(?,?,?,?)", new String[]{"The Da Vinci Code", "Dan Brown", "454", "14.96"});
        // 更新数据
        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);

5.4 使用LitePal操作数据库

LitePal是一款开源的Android数据库框架,它采用了对象关系映射的模式,将我们平时开发最常用到的一些数据库功能进行了封装,使得不用编写一行sql语句就能完成各种建表和增删改查操作。项目地址:https://github.com/LitePalFramework/Litepal
配置起来稍微麻烦一点

1、创建和升级数据库

首先需要先下载本地jar包

https://gitcode.net/mirrors/litepalframework/litepal/-/tree/master/downloads

接着在AndroidManifest.xml中配置

android:name="org.litepal.LitePalApplication"

最后main目录下新建assets目录和litepal.xml

<?xml version="1.0" encoding="utf-8"?>
<litepal>
    <dbname value="BookStore"></dbname>
    <version value="1"></version>
    <list>
        <mapping class="com.example.litepaltest.Book" />
    </list>
</litepal>

定义Book类

package com.example.litepaltest;

public class Book {
    private int id;
    private String author;
    private double price;
    private int pages;
    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public int getPages() {
        return pages;
    }

    public void setPages(int pages) {
        this.pages = pages;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

修改MaActivity

package com.example.litepaltest;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;

import org.litepal.LitePal;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button createDatabase = (Button) findViewById(R.id.create_database);
        createDatabase.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                LitePal.getDatabase();
            }
        });
    }
}

在这里插入图片描述
是不是很神奇!
上一节我们使用SQLiteOpenHelper来升级数据库的时候,虽然功能是实现了,但是每次在升级数据库的时候,都需要把之前的表drop掉,然后再重新创建才行。这是一个非常严重的问题,这会造成数据丢失,每当升级一次数据库,之前表中的数据就没了。
而有了LitePal,去升级数据库就非常简单,只需要将版本号加1就行。

比如Book类加一个press字段,与此同时我们再添加一张Category表
修改litepal.xml文件

<?xml version="1.0" encoding="utf-8"?>
<litepal>
    <dbname value="BookStore"></dbname>
    <version value="2"></version>
    <list>
        <mapping class="com.example.litepaltest.Book" />
        <mapping class="com.example.litepaltest.Category"/>
    </list>
</litepal>

结果如下所示
在这里插入图片描述

2、使用LitePal添加数据

Litepal进行表管理操作时不需要模型类有任何的继承结果,但是进行CRUD操作时,必须继承自DataSupport类

public class Book extends DataSupport {}
        Button addData = (Button) findViewById(R.id.add_data);
        addData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Book book = new Book();
                book.setName("The Da Vinci Code");
                book.setAuthor("Dan Brown");
                book.setPages(454);
                book.setPrice(16.96);
                book.setPress("Unknow");
                book.save();
            }
        });

在这里插入图片描述

3、使用LitePal更新数据

最简单的就是对已存储的对象重新设值,然后重新调用save()方法
对于LitePal来说,对象是否存储就是根据调用model.isSaved()方法的结果来判断的,返回true就是表示已存储。该方法只有在两种情况下会返回true,一种情况是已经调用model.save()方法去添加数据了,此时model会被认为是已存储的对象,另一种情况是model对象是通过LitePal提供的查询API查出来的,由于是从数据库中查到的对象,因此也被认为是已存储的对象。下面暂时介绍第一种。

Button updateData = (Button) findViewById(R.id.update_data);
        updateData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Book book = new Book();
                book.setName("The Lost Symbol");
                book.setAuthor("Dan Brown");
                book.setPages(540);
                book.setPrice(16.95);
                book.setPress("Unknow");
                book.save();
                book.setPrice(10.99);
                book.save();
            }
        });

该方法只能对已存储的对象进行操作,限制性较大。一种更加灵巧的方式如下

Button updateData = (Button) findViewById(R.id.update_data);
        updateData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Book book = new Book();

                book.setPrice(14.95);
                book.setPress("Anchor");
                book.updateAll("name = ? and author = ?" ,"The Lost Symbol","Dan Brown");

            }
        });

updateAll()方法,不能使用set默认值,字段都会有默认数据类型,set默认值以后LitePal不会对这个字段进行更新。其提供了一个统一方法,setToDefault()方法。

Book book = new Book();
bood.setToDefault("pages");
book.updateAll();

这段代码的意思是,将所有书的页数都更新为0,因为updateAll()方法中没有指定
约束条件,因此更新操作对所有的数据都生效了。

4、使用LitePal删除数据

直接使用delete()

deleteAll()

Button deleteButton = (Button) findViewById(R.id.delete_data);
        deleteButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                DataSupport.deleteAll(Book.class, "price<?", "15");
            }
        });

使用LitePal查询数据

Button queryButton = (Button) findViewById(R.id.query_data);
        List<Book> books = DataSupport.findAll(Book.class);
        for (Book book : books){
            Log.d("MainAcitivity","book name is "+ book.getName());
        }

LitePal的查询API有很多,可以直接参考官方文档,同时LitePal仍然支持原生的SQL语句。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值