Android06——Persistence

持久化(Persistence)

由于这个东西和mybatis比较像,而且没有太多的内容,写的比较简单。

简介

Android系统中主要听过了三种方式用于简单地实现数据持久化功能:文件存储、sharedpreference存储以及数据库存储。

这一章默认是在模拟器上学习了,华为手机如果没有root的话,是无法进入根目录(/)文件夹下的,也就没办法去/data/app/com.ssozh.filepersistentcetest下查看相关文件了。而华为的内部存储所在的文件夹是/storage/emulated/0/

文件存储

文件存储是Android中最基本的数据存储方法,它不对存储的内容进行任何格式化处理,所有数据都是原封不动的保存到文件中。

存储

Context类提供了一个openFileOutput()方法,可以用于将数据存储到指定的文件中。

FileOutputStream openFileOutput(fileName, OPFlag)
输入参数:
	fileName是不包括路径的,所有文件都是默认存储到/data/data/<package name>/files/目录下
	OPFlag:文件的操作模式,主要有MODE_PRIVATE(默认)和MODE_ADDEND两种模式,前者是覆盖,后者是追加。另外还有MODE_WORLD_
输出参数:
	FileOutputStream对象,得到这个对象之后就可以使用java流的方式将数据写入文件中了。

假设我们希望关闭app的时候,存储相关数据:

    private EditText edit;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        this.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 = this.edit.getText().toString();
        save(inputText);
    }

    private void save(String inputText) {
        FileOutputStream out = null;
        BufferedWriter writer = null;
        try {
            out = openFileOutput("data", Context.MODE_APPEND);
            writer = new BufferedWriter(new OutputStreamWriter(out));
            writer.write(inputText);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (writer != null){
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

读取

类似于将数据存储到文件中,Context类中还提供了一个openFileInput()方法,用于文件中读取数据。

FileInputStream openFileInput(fileName)
输入参数:
	fileName是不包括路径的,所有文件都是默认存储到/data/data/<package name>/files/目录下.
输出参数:
	FileInputStream对象,得到这个对象之后就可以使用java流的方式将数据读取文件中了。

我们希望再次启动的时候,载入相关数据(注意这个循环会造成文件越存储越大):

    private String load(){
        FileInputStream inputStream = null;
        BufferedReader reader = null;
        StringBuilder content = new StringBuilder();
        try {
            inputStream = openFileInput("data");
            reader  = new BufferedReader(new InputStreamReader(inputStream));
            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();
    }

SharedPreferences存储

不同于文件的存储方式,sharedpreference是使用键值对的方式来来存储数据的。而且sharedpreferences还支持多种不同的数据存储类型。可以是整型、字符串等等。

存储

  • 想要使用sharedpreferences存储数据,首先需要获取对象SharedPreferences。主要有两种方法:
    • Context类中的getSharedPreferences()方法:
      • 这个方法和上面的文件存储方法openFileOutput基本一样,包括文件名和写入模式的选择两个参数。
      • 区别在于默认存放在/data/data/<package name>/shared_prefs目录下。
    • Activity类中的getPreferences()方法:
      • 这个方法只接受操作模式这一个参数。因为是用这个方法时会自动将当前activity的类名作为sharedPreferences的文件名。
  • 调用SharedPreferences对象的edit()方法获取一个SharedPreferences.Editor对象。
  • SharedPreferences.Editor对象中添加数据,比如添加boolran型,就使用putBoolean方法。
  • 调用apply()方法将添加的数据提交、从而完成数据存储操作。

读取

还是获取SharedPreferences对象,然后调用get方法即可。

get方法接受两个参数,第一个是key,第二个参数是默认值,即表示当传入的键找不到对应的值时的返回值。

记住密码

在上一节的登陆部分,添加一个checkBox控件,增加记住密码功能。

public class LoginActivity extends BaseActivity {
    private EditText accountEdit;
    private EditText passwordEdit;
    private Button login;

    private CheckBox rememberPassword;
    private SharedPreferences pref;
    private SharedPreferences.Editor editor;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        login = (Button)findViewById(R.id.login);
        rememberPassword =(CheckBox) findViewById(R.id.remember_password);
        pref = PreferenceManager.getDefaultSharedPreferences(this);
        editor = pref.edit();
        accountEdit = (EditText) findViewById(R.id.account_edit);
        passwordEdit = (EditText) findViewById(R.id.password_edit);
        // 读取SharedPreferences
        boolean isRemember = pref.getBoolean("remember_password",false);
        if(isRemember){
            // 将账户和密码都设置到文本框中
            String account = pref.getString("account","");
            String password = pref.getString("password","");
            accountEdit.setText(account);
            passwordEdit.setText(password);
            rememberPassword.setChecked(true);
        }


        login.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                if("admin".equals(accountEdit.getText().toString()) && "123456".equals(passwordEdit.getText().toString())){
                    if(rememberPassword.isChecked()){
                        // 存储SharedPreferences
                        editor.putBoolean("remember_password",true);
                        editor.putString("account",accountEdit.getText().toString());
                        editor.putString("password",passwordEdit.getText().toString());
                    }else {
                        // 否则清除数据
                        editor.clear();
                    }
                    editor.apply();

                    Intent intent = new Intent(LoginActivity.this, MainActivity.class);
                    startActivity(intent);
                    finish();
                }else {
                    // TODO:华为键盘会挡住这个toast
                    Toast.makeText(LoginActivity.this,"account or password is invalid",Toast.LENGTH_SHORT).show();
                }
            }
        });
    }
}

SQLite数据库存储

基本操作

创建、更新数据库

要想在通过SQLite对数据库进行操作,首先需要创建一个子类继承SQLiteOpenHelper,然后重写他的方法:

  • onCreate:创建数据库
  • onUpgrade:升级数据库
    • 升级数据库的方法:在onUpgrade方法中先把之前的数据库删除,然后重新创建,同时注意在new databaseHelper的时候修改版本号
    • 注意:如果不修改版本号,是不会执行onUpgrade操作的!!!
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 后面的1 -> 2表示数据库升级了,比如添加了一个table 之类的行为都叫update
        dbHelper = new MydatabaseHelper(this,"BookStore.db",null,2);
        Button createDb = (Button) findViewById(R.id.create_database);
        createDb.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dbHelper.getWritableDatabase();
            }
        });
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_BOOK);
        db.execSQL(CREATE_CATEGORY);
        Toast.makeText(mContext, "create succeeded", Toast.LENGTH_SHORT).show();
    }

    @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在F:\Android\SDK\platform-tools目录下,可以先添加path】

可以考虑通过adb shell进行adb调试,但是由于华为不能root,好像无法访问/data/data下面的app:

Android06——Persistence

这里可以查看最下方的Android studio自带的database inspection:

Android06——Persistence

添加数据CRUD

通过调用SQLiteOpenHelper的getReadableDatabasegetWriteableDatabase方法是可以用于创建和升级数据库的,不仅如此,这两个方法还都会返回一个SQLiteDatabase对象,借助这个对象就可以进行CRUD操作了。

操作方式基本一致:

  • 通过dbHelper.getWriteableDatabase(),找到数据库db

  • db.insert() , db.query()等方法进行CRUD

  • 其中CU的对象是ContentValues

  • query和delete不太一样

               @Override
                public void onClick(View v) {
                    SQLiteDatabase db = dbHelper.getWritableDatabase();
                    ContentValues values = new ContentValues();
                    // 开始组装第一条数据
                    values.put("name", "The Dan Vinci Code");
                    values.put("author","Dan Brown");
                    values.put("pages", 154);
                    values.put("price",16.69);
                    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);
                }
    
  • 修改:

  • 删除:

    db.delete("Book", "pages > ?" , new String[] {"500"});
    
  • 查询:

                @Override
                public void onClick(View v) {
                    SQLiteDatabase db = dbHelper.getWritableDatabase();
                    // 查询db表中的所有数据
                    Cursor cursor = db.query("Book", null, null, null, null, null, null);
                    if(cursor!=null) {
                        while (cursor.moveToNext()){
                            String[] columns = new String[]{"name", "author", "pages", "price"};
                            for(String column : columns){
                                printOnLog(cursor,column);
                            }
                        }
                    }
                }
    
                private void printOnLog(Cursor cursor, String column){
    //                String name = cursor.getString(cursor.getColumnIndex("name"));
                    Object tmp = cursor.getString(cursor.getColumnIndex(column));
                    Log.d(TAG,"book " + column + " is " + tmp.toString());
                }
    

直接使用SQL语句操作数据库(有点像mybatis):

db.execSQL("insert into Book(name, author, pages, price) values(?, ?, ?, ?)", new String[]{"","","",""});

SQLite数据库的最佳实践

使用事务

上一篇:c#反射之应用


下一篇:[Hibernate]遇到的问题①——无法引入presistence