Full data storage solution, detailed persistence technology

Any application is actually constantly dealing with data. What we care about when we talk about WeChat and watch the news is the data inside. Applications without data become an empty shell and have no practical use for users. . So where does this data come from? Nowadays, most data is basically generated by users. For example, WeChat Moments and comments are actually generating data.
Data can be divided into transient data and persistent data. The so-called transient data refers to the data that is stored in the memory and may be lost due to the memory being reclaimed due to program shutdown or other reasons. For example: data account and password on the login page. Persistent data refers to saving the instantaneous data in the memory to the storage device to ensure that these data will not be lost even when the mobile phone or computer is turned off.
It can be simply considered: the data stored in the memory is in a transient state, while the data stored in the storage device is in a persistent state. Persistence technology provides a mechanism that allows data to be converted between a transient state and a persistent state.

The Android system mainly provides 3 ways to realize the data persistence function: file storage, SharedPreferences storage, and database storage.

File storage

File storage is the most basic data storage method in Android. It does not perform any formatting processing on the stored content. All data is saved in the file intact, so it is more suitable for storing some simple text data or Binary data. **If you want to use file storage to save some more complex structured data, you need to define a set of your own format specifications to facilitate re-parse the data from the file later.

Store the data in a file

An openFileOutput method is provided in the Context class, which can be used to store data in a specified file. This method accepts two parameters: the first parameter is the file name, which is used when the file is created. Note that the file name here cannot include the path , because all files are stored in /data/data/<package name'>/ by default Files/ directory; the second parameter is the operation mode of the file. There are mainly two modes to choose from, MODE_PRIVATE and MODE_APPEND. The default is MODE_PRIVATE, which means that when the same file name is specified, the written content will overwrite the original file MODE_APPEND means that if the file exists, add content to the file, and create a new file if it does not exist. In fact, there are two other modes of file operation: MODE_WORLD_READABLE and MODE_WORLD_WRITEABLE. These two modes indicate that other applications are allowed to read and write the files in our program, and have been abandoned in the Android 4.2 version.

package com.example.myapplication

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import java.io.BufferedOutputStream
import java.io.BufferedWriter
import java.io.IOException
import java.io.OutputStreamWriter

class MainActivity : AppCompatActivity() {

    lateinit var timeChangeReceiver: TimeChangeReceiver

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    override fun onDestroy() {
        super.onDestroy()
        save("要保存的内容")//这样就能保证在activity销毁之前一定能保存数据
    }
    
    private fun save(inputText:String){
        
        try {
            val output = openFileOutput("data",Context.MODE_PRIVATE)
            val writer = BufferedWriter(OutputStreamWriter(output))
            writer.use {//kotlin提供的扩展函数,保证在lambda表达式中的代码全部执行完之后自动将外层的流关闭
                it.write(inputText)
            }   
        }catch (e:IOException){
            e.printStackTrace()
        }
    }
}

Read data from file

Similar to storing data in a file, the Context class also provides an openFileInput method for reading data from a file. This method is simpler than the openFileOutput method. It only receives one parameter, the name of the file to be read, and then the system will automatically load the file in the /data/data/<package name'>/files/ directory and return one The FileInputStream object, after getting this object, read the data out through the stream.

    private fun load():String{
        val content = StringBuilder()
        
        try {
            val input = openFileInput("data")
            val reader = BufferedReader(InputStreamReader(input))
            reader.use {
                reader.forEachLine {
                    content.append(it)
                }
            }   
        }catch (e:IOException){
            e.printStackTrace()
        }
        return content.toString()
    }

SharedPreferences storage

Different from the storage method of files, SharedPreferences uses key-value pairs to store data. In other words, when saving a piece of data, you need to provide a corresponding key for this piece of data, so that the corresponding value can be retrieved through this key when reading the data. And SharedPreferences also supports a variety of different data type storage. If the stored data type is integer, then the read data is also integer; if the stored data is a string, then the read data is still characters string.

  • The SharedPreferences file uses the xml format to manage the data

Store data in SharedPreferences

To use SharedPreferences to store data, you must first obtain the SharedPreferences object. Android mainly provides the following two methods to get the SharedPreferences object.

  1. The getSharedPreferences() method in the Context class.
    This method accepts two parameters: the first one is used to specify the name of the SharedPreferences file. If the specified file does not exist, one will be created. The SharedPreferences files are stored in /data/data/<package name'>/shared_prefs/; the second parameter is used to specify the operation mode, currently only MODE_PRIVATE is optional, which is equivalent to directly passing in 0.

  2. The getSharedPreferences() method in
    the Activity class This method only receives one operation mode parameter, because when using this method, the current Activity class name is automatically used as the SharedPreferences file name.

    private fun put(){
        val editor = getSharedPreferences("data",Context.MODE_PRIVATE).edit()
        editor.putString("name","Brett")
        editor.putInt("age",23)
    }

Read data from SharedPreferences

    private fun get(){
        val prefs = getSharedPreferences("data",Context.MODE_PRIVATE)
        val name = prefs.getString("name","")
        val age = prefs.getInt("age",0)
        //......
    }

SQLite database storage

SQLite is a lightweight relational database. It has very fast computing speed and takes up very little resources. Usually only a few hundred kb of memory is enough, so it is especially suitable for use on mobile devices. SQLite not only supports standard SQL syntax, but also follows the ACID transaction of the database, so as long as you have used other relational databases before, you can quickly get started with SQLite. SQLite is much simpler than a normal database, and it can be used without even setting a user name and password.
The file storage and SharedPreferences storage we learned earlier are only suitable for saving some simple data and key-value pairs. When you need to store a large amount of complex relational data, you must use SQLite database storage.

Create database

In order to allow us to manage the database more conveniently, Android provides a SQLiteOpenHelper helper class. With this class, the database can be created and upgraded very simply.
There are two very important instance methods in SQLiteOpenHelper: getReadableDatabase() and getWritableDatabase(). Both of these methods can create or open an existing database (if the database already exists, open it directly, or create a new database), and return an object that can read and write to the database. The difference is that when the database is not writable (such as the disk space is full), the object returned by the getReadableDatabase() method opens the database in a read-only manner, while the getWritableDatabase() method will throw an exception. After constructing an instance of SQLiteOpenHelper, call its getReadableDatabase() or getWritableDatabase() method to create a database. The database files will be stored in the /data/data/<package name'>/databases/ directory. At this time, the overridden onCreate method will also be executed, so some logic of creating a table is usually processed here.

package com.example.myapplication

import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import android.widget.Toast

class MyDatabaseHelper(val context: Context,name:String,version:Int) : SQLiteOpenHelper(context,name,null,version) {//一般继承参数较少的那个构造方法

    private val createBook = "create table Book( " +
            "id integer primary key autoincrement," +
            "author text," + //text表示文本类型
            "price real," + //real表示浮点型
            "pages integer," +
            "name text)"

    private val createCategory = "create table Category( " +
            "id integer primary key autoincrement," +
            "category_name text," + //text表示文本类型
            "category_code integer)"

    override fun onCreate(db: SQLiteDatabase?) {//当Book表没有被创建的时候会回调onCreate方法,当Book表已被创建了,就不会回调该方法
        db?.execSQL(createBook)
        db?.execSQL(createCategory)
        Toast.makeText(context,"Create succeeded",Toast.LENGTH_SHORT).show()
    }

    override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
        if(oldVersion<=1){
            db?.execSQL(createCategory)
        }
    }
}
public class MyFragment extends Fragment {

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        return super.onCreateView(inflater, container, savedInstanceState);
    }

    public void setdata(){

    }

    MyDatabaseHelper helper = new MyDatabaseHelper(getActivity(),"test.db",1);

//第一个参数是表名,第二个参数用于在未指定添加数据的情况下给某些可为空的列自动赋值null,一般我们不用这个功能,直接传入null即可,第三个参数是一个ContentValues对象,他提供了put方法用于向ContentValues中添加数据,只需要将表中的每个列名以及相应的待添加数据传入即可。
    public long add(String name,int number){
        SQLiteDatabase db = helper.getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put("author",name);
        values.put("pages",number);

        long id = db.insert("Book", null, values);
        db.close();
        return id;
    }

//第一个参数是表名,第二个参数是ContentValues对象,要把更新的数据在这里组装进去,第三,第四个参数用于约束更新某一行或某几行中的数据,不指定默认更新所有行
    public long update(int number){
        SQLiteDatabase db = helper.getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put("pages",number);
        long id = db.update("Book", values,"pages=?",new String[]{String.valueOf(number)});
        db.close();
        return id;
    }

//第一个参数是表名,第二,第三个参数用于约束删除某一行或某几行的数据,不指定默认删除所有行
    public long delete(int number){
        SQLiteDatabase db = helper.getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put("pages",number);
        long id = db.delete("Book", "pages=?",new String[]{String.valueOf(number)});
        db.close();
        return id;
    }

    public void query(int number){
        SQLiteDatabase db = helper.getReadableDatabase();
        ContentValues values = new ContentValues();
        values.put("pages",number);

        //参数1:表名;参数2:查询的列名;参数3:查询条件;参数4:查询参数值;参数5:分组条件;参数6:having条件;参数7:排序条件
        Cursor cursor = db.query("book", null, "pages=?", new String[]{String.valueOf(number)}, null, null, null);
        //是否有下一条值
        boolean result = cursor.moveToNext();
        while (result){
            long id = cursor.getLong(cursor.getColumnIndex("id"));
            String author = cursor.getString(1);
            long pages = cursor.getLong(2);
        }
        cursor.close();
        db.close();
    }

    //事务操作
    public void transaction(){
        SQLiteDatabase db = helper.getWritableDatabase();
        //开始数据库的事务
        db.beginTransaction();

        try{
            //数据库的增删改查操作
            db.execSQL();

            db.setTransactionSuccessful();//标记事务执行成功
        }catch (Exception e){
            Log.e("fail transaction",e.toString());
        }finally {
            db.endTransaction();
            db.close();
        }

    }
}

Guess you like

Origin blog.csdn.net/qq_36828822/article/details/113956508