第十六章 Android的数据存储方案

作者: 忆念成风 | 来源:发表于2017-09-21 16:21 被阅读158次

    1.引言

      一般在做一些面试题的时候,Android有几种数据存储方案这个问题是经常碰到的。在我们实际应用中,任何一个应用程序说白了都是在不停的和数据接触,比如我们通过QQ,微信聊天都是一个数据传输的过程。又比如我们逛新闻,逛微博,刷淘宝等也都是一个数据展示的过程。说实在点没有数据,所有的应用都是空架子。目前绝大多数数据都是由用户产生的,发评论发朋友圈的等等都是一个产生数据的过程。
      因为我们知道垃圾回收机制,当程序关闭或其他原因导致数据被回收掉的时候,会有些数据被丢失,这些是瞬时数据,例如,在输入框输入一个数字,发送一个字符串等,一旦我们没有再需要的时候就会丢失。但是有些数据需要持久的保存不想被回收掉,例如朋友圈的动态和评论,微博留言等。这就需要保持关键新的数据不丢失。

    2.保持数据持久化的简介

      数据持久化处理是将内存中的数据能够被保存在特定的存储空间中,保证数据在程序关闭和异常情况下不会被丢失。Android 系统提供了5种方式来保存数据:
    1.文件存储
    2.SharePreference存储
    3.数据库存储
    4.ContentProvider存储
    5.网络存储

    3.文件存储

      文件存储是Android中最基本的一种数据存储方法。它通过文件的方式将数据不做任何处理的存储进文件中,因此适合存储一些简单的文本数据或者二进制数据。当然也不是不适合存储复杂的文本数据,只是需要定义一套自己的格式规范,方便之后将数据从文件中重新解析出来。

    3.1 将数据存储到文件夹中

      文件IO流的存取提供了FileOutputStram()和FileInputStream()这两个方法来存储和取出文件中的数据。openFileOutput()这个方法可以用于将数据存储到指定为文件中。openFileOutput(String name , int mode)这个方法第一个参数用于指定文件名,第二个参数指定打开文件的模式。
    具体有:

    1. MODE_PRIVATE:为默认操作模式,代表该文件是私有数据,只能被应用本身访问,在该模式下,写入的内容会覆盖原文件的内容,如果想把新写入的内容追加到原文件中。可以使用Context. MODE_APPEND 。
    2. MODE_APPEND:模式会检查文件是否存在,存在就往文件追加内容,否则就创建新文件。
    3. MODE_WORLD_READABLE:表示当前文件可以被其他应用读取;
    4. MODE_WORLD_WRITEABLE:表示当前文件可以被其他应用写入。(后面两种在Android4.2版本中被废弃了)
      具体实现将数据存储的步骤:
      1.首先新建一个layout文件activity_file_save.xml.这个页面就两个控件,一个输入框EditText,一个保存按钮。
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
       >
    
        <EditText
            android:id="@+id/et_txt_content"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="写点什么呗"/>
    
        <Button
        android:id="@+id/btn_save_data"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:text="保存数据"/>
    </LinearLayout>
    

    2.新建一个Activity,处理保存数据的逻辑。通过调用openFileOutPut()的方法返回一个FileOutputStream对象。得到这个对象之后就可以使用Java保存文件的方法将数据写入到文件中。这里生成一个data文件保存文件中。代码如下:

      private void save(String inputtext) {
             FileOutputStream outputStream = null;
             BufferedWriter bufferedWriter = null;
            try {
                outputStream = openFileOutput("data", Context.MODE_PRIVATE);
                bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
                bufferedWriter.write(inputtext);
                Toast.makeText(FileSaveActivity.this, "保存数据成功"+inputtext, Toast.LENGTH_SHORT).show();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if(bufferedWriter!=null){
                    try {
                        bufferedWriter.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    

    3.运行代码,在模拟器查看保存的数据。点击Android Studio 导航栏的Tools->Android->Android Device Monitor -> File Explorer,在这里找到data/data/com.demo.filesavedemo/files/目录 查看模拟器的数据。

    Android 工具列表

    Android7.0的模拟器有点小bug,选择6.0的好了。

    生成data文件

    点击左边这个按钮,导出文件到电脑上:

    导出导入按钮

    输入qwert 保存,输入结果:

    输出结果

    这就证实了 输入的EditText的内容被成功保存到文件中了。

    3.2 从文件夹中读取数据

    类似于数据存储到文件中,Context类还提供了一个openFileInput()的方法,用于从文件中读取数据这个相比较openFileOutput()简单一些,只要读取文件名这个参数就可以了。系统会自动读取data/data/com.demo.filesavedemo/files/目录这个文件,并返回一个FileInputStream对象。得到这个对象之后就可以使用Java读取文件数据的方法读取文件中的数据

     private  String load(){
             FileInputStream inputStream=null;
             BufferedReader bufferedReader=null;
            StringBuilder content=new StringBuilder();
    
            try {
                inputStream = openFileInput("data");
                bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                String line=" ";
                try {
                    while ((line=bufferedReader.readLine())!=null){
                         content.append(line);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
    
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }finally {
                if(bufferedReader!=null){
                    try {
                        bufferedReader.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            return  content.toString();
        }
    
    

    最后将文件存储输入和输出合并完整代码:
    1.activity_file_save.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
       >
    
        <EditText
            android:id="@+id/et_txt_content"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="写点什么呗"/>
    
        <Button
        android:id="@+id/btn_save_data"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:text="保存数据"/>
    
        <Button
            android:id="@+id/btn_load_data"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:text="读取数据"/>
        <TextView
            android:id="@+id/tv_txt_load_data"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="10dp"
            android:textSize="15sp"
            />
    
    
    </LinearLayout>
    
    

    2.FileSaveActivity.java

    ublic class FileSaveActivity extends AppCompatActivity {
    
        private static final String TAG = "FileSaveActivity";
        private EditText editText;
    
        private Button  btn_load_data,btn_save_data;
        private TextView  tv_txt_load_data;
    
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_file_save);
            editText = (EditText) findViewById(R.id.et_txt_content);
            tv_txt_load_data= (TextView) findViewById(R.id.tv_txt_load_data);
    
             btn_save_data= (Button) findViewById(R.id.btn_save_data);
             btn_load_data= (Button) findViewById(R.id.btn_load_data);
            btn_save_data.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                     String  inputtext=editText.getText().toString();
                    save(inputtext);
    
                }
            });
            btn_load_data.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
    
                    String  inputText=load();
                    Log.e(TAG, "onClick: "+inputText );
                    if(!TextUtils.isEmpty(inputText)){
                        tv_txt_load_data.setText(inputText);
                    }
                    Toast.makeText(FileSaveActivity.this, "读取数据成功="+inputText, Toast.LENGTH_SHORT).show();
                }
    
            });
    
        }
    
        //保存数据到文件中
        private void save(String inputtext) {
             FileOutputStream outputStream = null;
             BufferedWriter bufferedWriter = null;
            try {
                outputStream = openFileOutput("data", Context.MODE_PRIVATE);
                bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
                bufferedWriter.write(inputtext);
                Toast.makeText(FileSaveActivity.this, "保存数据成功"+inputtext, Toast.LENGTH_SHORT).show();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if(bufferedWriter!=null){
                    try {
                        bufferedWriter.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
        //读取文件中的数据
        private  String load(){
             FileInputStream inputStream=null;
             BufferedReader bufferedReader=null;
            StringBuilder content=new StringBuilder();
    
            try {
                inputStream = openFileInput("data");
                bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                String line=" ";
                try {
                    while ((line=bufferedReader.readLine())!=null){
                         content.append(line);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
    
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }finally {
                if(bufferedReader!=null){
                    try {
                        bufferedReader.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            return  content.toString();
        }
    }
    
    

    所用的核心及技术就是openFileInput()和openFileOutput()方法,之后就是Java的各种读写流来进行读写操作。
    但是这种存储方式不适合保存复杂的文本数据。

    4. SharePreferences存储

    与文件存储不同的是,SharePreferences存储是通过键值对来进行存储,在存储数据的时候,需要给数据提供对应的键,然后通过键来寻找相对应的值。而且SharePreferences支持多种不同的数据类型来进行存储,存取的过程中,数据的类型不会发生改变。

    4.1 将数据存储到SharePreferences中

    实现SharedPreferences存储的步骤如下:   
    一、根据Context获取SharedPreferences对象   
    二、利用edit()方法获取Editor对象。   
    三、通过Editor对象存储key-value键值对数据。   
    四、通过commit()方法提交数据。
    示例代码:

     @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_file_share_preference);
            Button button= (Button) findViewById(R.id.btn_share_save);
            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    SharedPreferences.Editor editor=getSharedPreferences("data",MODE_PRIVATE).edit();
                    editor.putString("name","小强");
                    editor.putInt("age",18);
                    editor.putBoolean("married",false);
                    editor.apply();
                    Toast.makeText(FileSharePreferenceActivity.this, "保存数据成功", Toast.LENGTH_SHORT).show();
                }
            });
    
        }
    

    设置点击按钮,保存数据到data/data/com.demo.filesavedemo/shared_prefs/目录。查看的方法和上面的一样。生成了一个xml的文件

    SharedPreferences存储

    这里补充一下Android中主要提供了3种方法去得到SharedPreferences对象
    1.Context类中的getSharedPreferences()方法。这个方法接收两个参数,第一个参数是指定存放SharedPreferences文件的名称。如果指定的文件不存在,则会创建一个,SharedPreferences文件的存放目录为/data/data/<包名>/shared_prefs/目录,第二个参数用于指定操作模式。目前只有MODE_PRIVATE这一种模式可选,它是默认的操作模式,和直接传入数字0是一样的效果,表示只有当前的应用程序才可以对这个SharedPreferences文件进行存储。其他的操作模式均已废弃。MODE_WORLD_READABLE和MODE_WORLD_WRITEABLE,在Android4.2版本被废弃了,MODE_MULTI_PROCESS是在6.0被废弃。
    2.Activity类中的getPreference()方法。这个方法与getSharedPreferences()方法很像,只是它只接受一个操作模式参数,使用这个方法会自动将当前活动的类名作为SharedPreferences的文件名。
    3.PreferenceManager类中的getDefaultSharedPreferences()方法。这是个静态方法,它接收一个Context参数,自动使用当前包名作为前缀来明明SharedPreferences文件。得到SharedPreferences对象之后,就可以开始存储数据了。

    4.2 从SharedPreferences中读取数据

    SharedPreferences的存储数据非常的简单,读取数据其实更加的简单。SharedPreferences提供了一系列的get()方法来对应存储的put()方法。但是与存储不同的是,提取的方法有两个参数,第一个参数是对应的字符类型,第二个是默认值,当传入的键找不到对应的值的时候会以什么样的默认值进行返回。
    1.修改activity_file_share_preference.xml文件

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
       >
        <Button
            android:id="@+id/btn_share_save"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:text="SharePreference存储"/>
        <Button
            android:id="@+id/btn_share_load"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:text="SharePreference读取"/>
    
    </LinearLayout>
    

    2.修改FileSharePreferenceActivity.java文件

    
    public class FileSharePreferenceActivity extends AppCompatActivity {
        private static final String TAG = "FileSharePreferenceActi";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_file_share_preference);
            Button button= (Button) findViewById(R.id.btn_share_save);
            Button button2= (Button) findViewById(R.id.btn_share_load);
            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    saveDate();
                }
            });
            button2.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    loadDate();
                }
            });
    
        }
    
        public void saveDate(){
            SharedPreferences.Editor editor=getSharedPreferences("data",MODE_PRIVATE).edit();
            editor.putString("name","小强");
            editor.putInt("age",18);
            editor.putBoolean("married",false);
            editor.apply();
            Toast.makeText(FileSharePreferenceActivity.this, "保存数据成功", Toast.LENGTH_SHORT).show();
        }
    
        public  void loadDate(){
             SharedPreferences spf=getSharedPreferences("data",MODE_PRIVATE);
            String name= spf.getString("name","");//字符串默认为空
            boolean  married= spf.getBoolean("married",false);
           int age=  spf.getInt("age",0);
            Log.i(TAG, "loadDate: name="+name);
            Log.i(TAG, "loadDate: married="+married);
            Log.i(TAG, "loadDate: age="+age);
        }
    
    }
    

    先将数据存储到shared_prefs目录下的文件,再将文件里的数据读取出来


    SharedPreferences读取数据

    5.数据库存储

    之前在讲ListView的时候讲过了数据库存储。Android 对数据库的支持很好,自身就支持SQLite 这种轻量级的数据库,它的运算速度快,占用资源少,通常几百KB的内存就够了,并且支持标准的SQL语法,遵循数据库的ACID事务(是Atomic(原子性)、Consistency(一致性)、Isolation(隔离性)和Durability(持久性)的英文缩写)。相比较一般的数据库上手简单。
      在存储一些简单的数据和键值对的时候,可能会用上SharedPreferences存储。但是需要存储大量的复杂的关系型数据的时候,SharedPreferences显然不能满足要求,这时候就需要SQLite数据库的存储。数据库存储的主要操作:创建数据库,升级数据库,增,删,查,改操作。

    5.1 创建数据库

    Android 为了能方便管理数据库,专门提供了一个SQLiteOpenHelper类,借助这个类就能简单的对数据库进行创建和升级了。在了解SQLiteOpenHelper之前,查看源码,发现它是一个抽象类,所以在使用的时候需要创建自己的帮助类去继承它,SQLiteOpenHelper有两个抽象方法,分别是onCreat()和onUpgrade(),实现创建和升级数据库的逻辑。这里贴出SQLiteOpenHelper 的源码,下面会对主要的方法进行分析。

    
    package android.database.sqlite;
    
    public abstract class SQLiteOpenHelper {
        public SQLiteOpenHelper(android.content.Context context, java.lang.String name, android.database.sqlite.SQLiteDatabase.CursorFactory factory, int version) { /* compiled code */ }
    
        public SQLiteOpenHelper(android.content.Context context, java.lang.String name, android.database.sqlite.SQLiteDatabase.CursorFactory factory, int version, android.database.DatabaseErrorHandler errorHandler) { /* compiled code */ }
    
        public java.lang.String getDatabaseName() { /* compiled code */ }
    
        public void setWriteAheadLoggingEnabled(boolean enabled) { /* compiled code */ }
    
        public android.database.sqlite.SQLiteDatabase getWritableDatabase() { /* compiled code */ }
    
        public android.database.sqlite.SQLiteDatabase getReadableDatabase() { /* compiled code */ }
    
        public synchronized void close() { /* compiled code */ }
    
        public void onConfigure(android.database.sqlite.SQLiteDatabase db) { /* compiled code */ }
    
        public abstract void onCreate(android.database.sqlite.SQLiteDatabase sqLiteDatabase);
    
        public abstract void onUpgrade(android.database.sqlite.SQLiteDatabase sqLiteDatabase, int i, int i1);
    
        public void onDowngrade(android.database.sqlite.SQLiteDatabase db, int oldVersion, int newVersion) { /* compiled code */ }
    
        public void onOpen(android.database.sqlite.SQLiteDatabase db) { /* compiled code */ }
    }
    

    SQLiteOpenHelper中还有两个非常重要的实例方法:getReadableDatabase()和getWriteableDatabase()。这两个方法都可以创建或打开一个现有的数据库(如果有数据库了,就直接打开,没有就创建),并返回一个可对数据库进行读写操作的对象。不同的是,当数据库不可写入的时候(比如磁盘满了),getReadableDatabase()方法返回的对象将以只读的方式打开数据库,而getWriteableDatabase()的方法则将出现异常。所以要注意下。
      通过源码我们可以发现SQLiteOpenHelper 有两个构造方法可以重写,一般都用参数少的那个构造方法,这个构造方法中接收4个参数,第一个参数是Context,必须要有它才能对数据库进行操作,第二个参数是数据库名,创建数据库时使用的就是这里指定的名称,第三个参数是允许我们在查询数据的时候返回一个Cursor,一般都穿null.第四个参数表示当前数据库的版本号。可用于对数据库进行升级操作。构建出SQLiteOpenHelper的实例之后,再调用getReadableDatabase()或getWriteableDatabase()的方法就能创建数据库了。数据库文件会存在/data/data/<包名>/databases/目录下。此时需要重写onCreate()方法创建表。
    代码如下所示:
    1.activity_sqlsave.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="com.demo.filesavedemo.SQLSave.SQLSaveActivity">
    
        <Button
            android:id="@+id/btn_create_data"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:textSize="18sp"
            android:text="创建数据库"/>
    
    </LinearLayout>
    
    
    1. 创建一个类MySQliteDataHelper继承SQLiteOpenHelper,然后创建一张表CREATE_BOOK, 然后通过sqLiteDatabase.execSQL(CREATE_BOOK)执行创建表的操作。这里设置一个Toast消息,提示创建库成功。
    
    public class MySQliteDataHelper 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 MySQliteDataHelper(Context context, String name, 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) {
    
        }
    }
    
    

    3.在创建SQLSaveActivity ,创建数据库MYBOOKStore.db

    
    public class SQLSaveActivity extends AppCompatActivity {
    
        private MySQliteDataHelper mySQliteDataHelper;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_sqlsave);
            mySQliteDataHelper = new MySQliteDataHelper(this,"MYBOOKStore.db",null,1);
            Button btn_create_data= (Button) findViewById(R.id.btn_create_data);
            btn_create_data.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                     mySQliteDataHelper.getWritableDatabase();
                }
            });
    
        }
    }
    
    
    创建数据库

    在创建数据库之后,我们只是通过一个Toast消息来知道到底是否创建了成功了数据库,但是眼见为实吧。这里有两种方法查看操作的数据库:
    方法一:Android Studio的导航栏->Tools->Android->Android Device Motior->File Explorer->data/data/<项目的包名>/databases/创建的数据库

    创建数据库

    方法二:复杂一点,首先需要在环境变量中配置adb,找到你的Android Studio 的sdk的地址,然后找到platform-tools,创建一个系统变量 android_adb(已经配置了的不用管这样一步)


    创建adb目录

    然后将这个目录加到path的末尾。

    添加目录

    最后打开控制台,输入adb shell 回车,然后输入 cd /data/data/全包名/databases 回车,最后输入 ls 查看里面的目录。创建好的库就在这里了。

    创建数据库

    这里会看到两个数据库文件,一个文件是我们创建的MYBOOKStore.db,另一个MYBOOKStore.db-journal 文件是为了数据库能够支持事务而产生的临时日志文件,通常情况下这个文件是0字节。

    在查看了数据库之后接下来还要结束sqlite命令来打开数据库,输入sqlite3,后面加上数据库的名称就可以了。

    打开数据库

    在打开数据库之后,可以输入 .table 来查看数据库中建的表,

    查看表

    输入 .schema 来查看创建表的SQL语句

    查看创建语句

    输入.quite 或者 .exit命令退出数据库的编辑,在输入exit命令退出控制台。

    5.2 升级数据库

    在之前的MySQliteDataHelper 的类中,还有一个onUpgrade()方法没有用,这个方法之前说过了是升级数据库用的。所以来讲讲怎么升级数据库。因为我们之前已经创建了数据库MYBOOKSore.db,所以我们在onCreate()方法中无法再创建同样的数据库了。但是你又需要添加一张表进这个库怎么办,创建又不能用。修改代码:
    1.修改MySQliteDataHelper ,在onUpgrade()中执行DROP语句,如果发现数据库中已经有了Book或者Category 表就删除掉,然后在重新调用onCreate()方法。这样就可以解决了,重复库的问题。

    public class MySQliteDataHelper extends SQLiteOpenHelper {
    
        public  static final String CREATE_BOOK = "create table Book("
                +"id integer primary key autoincrement,"
                +"author text,"
                +"price real,"
                +"pages integer,"
                +"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 MySQliteDataHelper(Context context, String name, 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 olderVersion, int newVersion) {
            sqLiteDatabase.execSQL("drop table if exists BOOK");
            sqLiteDatabase.execSQL("drop table if exists Category");
            onCreate(sqLiteDatabase);
        }
    }
    
    

    2.解决了重复库的问题,接下来就要解决升级的问题,也很简单,MySQliteDataHelper修改最后一个版本参数,让它大于1就可以了,这样就可以完成数据库升级了。(和我们的App版本升级一样)

    
    public class SQLSaveActivity extends AppCompatActivity {
    
        private MySQliteDataHelper mySQliteDataHelper;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_sqlsave);
            mySQliteDataHelper = new MySQliteDataHelper(this,"MYBOOKStore.db",null,2);
            Button btn_create_data= (Button) findViewById(R.id.btn_create_data);
            btn_create_data.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                     mySQliteDataHelper.getWritableDatabase();
                }
            });
    
        }
    }
    

    5.3 删除数据库

    假如一步我在这里创建了两个数据库了,但是你想删除一个数据库了。这时候怎么办?方法很简单,创建的数据库都是以文件的形式保存在内存中的,既然要删除数据库,首先需要找到这个数据库的地址。然后调用文件的delete()方法删除数据库

    删除前有两个数据库

    1.添加一个删除按钮

    <?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="com.demo.filesavedemo.SQLSave.SQLSaveActivity">
    
         ..............
    
        <Button
            android:id="@+id/btn_delete_data"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:textSize="18sp"
            android:text="删除数据库"
            android:visibility="visible"/>
    </LinearLayout>
    
    
    1. 写一个方法删除文件。注释在代码里面。
    
        public void deleteFile(File file) {
            if (file.exists()) { // 判断文件是否存在
                if (file.isFile()) { // 判断是否是文件
                    // 设置属性:让文件可执行,可读,可写
                    file.setExecutable(true, false);
                    file.setReadable(true, false);
                    file.setWritable(true, false);
                    file.delete(); // delete()方法
                } else if (file.isDirectory()) { // 否则如果它是一个目录
                    File files[] = file.listFiles(); // 声明目录下所有的文件 files[];
                    for (int i = 0; i < files.length; i++) { // 遍历目录下所有的文件
                        this.deleteFile(files[i]); // 把每个文件 用这个方法进行迭代
                    }
                }
                file.setExecutable(true, false);
                file.setReadable(true, false);
                file.setWritable(true, false);
                file.delete();
                Toast.makeText(this, "成功删除"+file.getName(), Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this, file.getName()+"不存在!!!", Toast.LENGTH_SHORT).show();
            }
        }
    

    3.然后点击删除按钮出发删除事件,因为是两个,所以两个文件都要删除。

        Button btn_delete_data = (Button) findViewById(R.id.btn_delete_data);
      btn_delete_data.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    File file1 = new File("/data/data/com.demo.filesavedemo/databases/MYBOOKStore.db");
                    deleteFile(file1);
                    File file2 = new File("/data/data/com.demo.filesavedemo/databases/MYBOOKStore.db-journal");
                    deleteFile(file2);
    
                }
            });
    
    删除后只有一个数据库

    5.4 添加数据

    熟悉数据库操作的话,一般只有增删查改(CRUD)这四种操作,其中C代表添加(Create),R代表查询(Retrieve),U代表更新(Update),D代表删除(Delete)。熟悉SQL语言的话,肯定了解添加数据。Android 的添加数据语言和SQl语言和相似,并且提供了很多辅助方法。前面说了SQLiteOpenHelper中有两个非常重要的实例方法:getReadableDatabase()和getWriteableDatabase()。这两个方法都可以创建或打开一个现有的数据库,并返回一个可对数据库进行读写操作的对象SQLiteDatabase。正是通过这个对象对数据进行CRUD操作。
      添加数据的方法insert() ,这个方法有3个参数。第一个参数是表的名称,第二个参数是在未指定的情况下给某些可为空的列自动赋值,直接传入null就可以了。第三个参数是一个ContentValues对象,提供了一系列的put()方法重载,用于向ContentValues中添加数据。只需要将表中的每个列名和相应的待添加的数据传入即可。
    1.在activity_sqlsave.xml文件中添加一个Button。

     <Button
            android:id="@+id/btn_insert_data"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:textSize="18sp"
            android:text="添加数据"/>
    

    2.在SQLSaveActivity中添加数据

         Button btn_insert_data = (Button) findViewById(R.id.btn_insert_data);
       btn_insert_data.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    SQLiteDatabase sqLiteDatabase=mySQliteDataHelper.getWritableDatabase();
                    ContentValues values=new ContentValues();
                    //开始加入第一条数据
                    values.put("author","Tom");
                    values.put("price",20);
                    values.put("pages",555);
                    values.put("name","your name");
                    sqLiteDatabase.insert("Book",null,values);
                    values.clear();
                    //开始加入第二条数据
                    values.put("author","Json");
                    values.put("price",60);
                    values.put("pages",999);
                    values.put("name","my name");
                    sqLiteDatabase.insert("Book",null,values);
                    Toast.makeText(SQLSaveActivity.this, "插入数据成功", Toast.LENGTH_SHORT).show();
                }
            });
    

    最后出发点击事件就可以了。打开 adb查询数据库里面的数据。输入 select * from Book; 记得要在末尾添加 分号;,最后的结果如图所示:


    插入数据

    5.5 更新数据

    和添加数据相类似,SQLiteDatabase添加了update()的方法,对表中数据进行更新。这个方法的第一个参数是表名,第二个参数是ContentValues对象,第三、四个参数是用于约束更新某一行或几行中的数据,不指定就默认更新全部。
    修改之前的代码
    1.在activity_sqlsave.xml文件中添加一个Button。

      <Button
            android:id="@+id/btn_update_data"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:textSize="18sp"
            android:text="更新数据"/>
    

    2.在SQLSaveActivity中添加 更新数据的事件。这里将数据pages 的值换成1010,

     Button btn_update_data = (Button) findViewById(R.id.btn_update_data);
       btn_update_data.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    SQLiteDatabase sb=mySQliteDataHelper.getWritableDatabase();
                    ContentValues values=new ContentValues();
                    values.put("pages",1010);
                    sb.update("Book",values,"name=?",new String[]{"my name"});
                    Toast.makeText(SQLSaveActivity.this, "更新数据成功", Toast.LENGTH_SHORT).show();
                }
            });
    

    最后再输入一遍查询语句,查看触发更新之后的数据结果如下图所示:

    数据更新

    5.6 删除数据

    同样的SQLiteDatabase提供了delete()方法,专门删除数据。这个方法的第一个参数还是表名,第二,三个用于约束删除某一行或几行的数据,不指定,默认全部删除所有行。
    修改之前的代码
    1.在activity_sqlsave.xml文件中添加一个Button。

      <Button
            android:id="@+id/btn_delete_data_1"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:textSize="18sp"
            android:text="删除数据"/>
    

    2.修改SQLSaveActivity,添加删除事件。如果price的值为20就删除

       Button btn_delete_data_1 = (Button) findViewById(R.id.btn_delete_data_1);
      btn_delete_data_1.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    SQLiteDatabase sb=mySQliteDataHelper.getWritableDatabase();
                    sb.delete("Book","price=?",new String[]{"20"});
                    Toast.makeText(SQLSaveActivity.this, "删除数据成功", Toast.LENGTH_SHORT).show();
                }
            });
    

    最后再输入一遍查询语句,查看触发删除操作之后的数据结果如下图所示:


    删除之后的结果

    5.7 查询数据

    终于到了查询语言了,但是这也是数据操作功能使用最大的一章。增删改都只占了一小部分。SQLiteDatabase提供的查询方法,包含了七个参数。第一个参数是查询的表的名字,第二个是指定查询的列名,第三,四个约束条件指定查询某一行或某几行的数据,不指定会查询所有行的数据。第五个用于指定group by 的列,不指定表示不对group by操作。第六个用于对group by之后的数据进行下一步的过滤,不指定表示不过滤。第七个用于指定查询结果的排序方式。

    query() 方法参数 对应的SQL部分 描述
    table from table_name 指定查询的表名
    columns select column1,column2 指定查询的列名
    selection where column= value 指定where的约束条件 为
    selectionArgs - where中的占位符提供具体的值
    groupBy group by column 指定需要group by 的列
    having having column = value 对group by后的结果进一步约束
    orderBy order by column1,column2 指定查询结果的排列方式

    虽然看起来query()的参数非常多,但是实际开发中,你不一定需要每条语句都指定所有的参数,一般情况下传入几个参数就可以完成查询操作。调用后query()后会返回一个Cursor()对象,查询的所有数据都是从这个对象中取出来。
    具体代码:
    1.在activity_sqlsave.xml文件中添加一个Button。

     <Button
            android:id="@+id/btn_query_data"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:textSize="18sp"
            android:text="查询数据"/>
    

    2.修改SQLSaveActivity,添加查询事件,并将查询的值打印出来。

     Button btn_query_data = (Button) findViewById(R.id.btn_query_data);
    btn_query_data.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    SQLiteDatabase sb=mySQliteDataHelper.getWritableDatabase();
                    //查询表中所有的数据
                    Cursor cursor=sb.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"));
                            int pages=cursor.getInt(cursor.getColumnIndex("pages"));
                            double price=cursor.getDouble(cursor.getColumnIndex("price"));
                            Log.e(TAG, "book name is"+name );
                            Log.e(TAG, "book author is"+author );
                            Log.e(TAG, "book pages is"+pages );
                            Log.e(TAG, "book price is"+price );
                        }while (cursor.moveToNext());
                    }
                    cursor.close();
                }
            });
    
    

    这里通过query()方法查询数据,第一个参数指定查询的表,后面的全部设为空,表示查询所有的数据,虽然这张表只剩一条数据了,但是查询完之后可以得到一个Cursor对象,然后调用它的moveToFirst()方法将指针指向第一行的位置,然后进入循环当中,遍历查询每一个数据。在遍历的过程中,可以通过cursor的getColumnIndex()方法获取某一列在表中对应的位置的索引,然后通过索引获取相应的方法。最后打印出来。


    查询结果

    小总结:

    1.添加数据的方法:

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

    2.更新数据的方法:

    db.execSQL("update Book set price = ? where name = "?",new String[]{"20","your name});
    

    3.删除数据的方法:

    db.execSQL("delete from Book  where name = "?",new String[]{"your name});
    

    4.查询数据的方法:

    db.rawQuery("select * from Book",null);
    

    github地址:https://github.com/wangxin3119/filesavedemo

    相关文章

      网友评论

        本文标题:第十六章 Android的数据存储方案

        本文链接:https://www.haomeiwen.com/subject/beuwsxtx.html