野生大熊猫

安卓(Android)数据存储方案
导语:本文是我在看《Android 第一行代码》的时候的记录,觉得比较有用,也特别重要,其中最后的LitePal操...
扫描右侧二维码阅读全文
12
2018/07

安卓(Android)数据存储方案

导语:本文是我在看《Android 第一行代码》的时候的记录,觉得比较有用,也特别重要,其中最后的LitePal操作数据库特别的重要。

一、文件存储

1. 存储函数(openFileOutput)

这里先通过openFileOutPut()方法能够得到一个FileOutputStream 对象,然后借助它构建一个OutputStreamWriter对象,接着再使用OutputStreamWriter构建出一个BufferedWriter对象,这样就通过BufferedWriter将文本内容写入到文件中。

    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();
            }
        }
    }

2. 读取函数(openFileInput)

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

项目文件地址:https://github.com/imlhb/StudyAndroid/tree/master/FilePersistenceTest

二、SharedPreferences储存

1.存入数据

                SharedPreferences.Editor editor = getSharedPreferences("data",MODE_PRIVATE).edit();
                editor.putString("name","Tom");
                editor.putInt("age",28);
                editor.putBoolean("married",false);
                editor.apply();

2.获取数据

                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(TAG, "onClick: name is " + name);
                Log.d(TAG, "onClick: age is " + age);
                Log.d(TAG, "onClick: married is " + married);

项目文件地址:https://github.com/imlhb/StudyAndroid/tree/master/SharedPreferencesTest

三、SQLite数据库储存

Android系统内置了SQLite数据库。
首先需要创建一个类去继承SQLiteOpenHelper类,SQLiteOpenHelper中有两个方法,分别是onCreate()onUpgrade(),我们必须重写这两个方法,然后分别在这两个方法中实现创建,升级数据库的逻辑。

1. 创建数据库

public class MyDatabaseHelper extends SQLiteOpenHelper {
//编写Sql语句,创建Book表
    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(Context context , String name, 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,"创建数据库成功",Toast.LENGTH_SHORT).show();
    }

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

2. 升级数据库

这里新增加drop table if exists,如果发现数据库中已经存在Book表Category表,就先删除表,再调用onCreate()方法重新创建

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)";
//新增加Category表
    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(Context context , String name, SQLiteDatabase.CursorFactory factory , int version){
        super(context ,name ,factory ,version);
        mContext = context;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_BOOK);
//创建Category表
        db.execSQL(CREATE_CATEGORY);
        Toast.makeText(mContext,"创建数据库成功",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);
    }
}

3. 添加数据

这边书中是使用了按钮,按下后创建。
首先需要private MyDatabaseHelper dbHelper;
然后new一个MyDatabaseHelper给dbHelper ,像这样:

dbHelper = new MyDatabaseHelper(this,"BookStore.db",null,2);

将它实例化后,然后再使用SQLiteDatabase db = dbHelper.getWritableDatabase();方法会返回一个SQLiteDatabase对象,这个对象可以对数据进行CRUD操作。
这边我们先使用insert()方法,这个方法需要3个参数:

insert(表名,Null,ContentValues对象);

剩下的就put数据即可values.put("字段名","数据")

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);

这里解释一下CRUD操作:create添加数据read读取数据 update 修改数据delete删除数据

4. 更新数据

SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("price",10.99);
db.update("Book",values,"name = ?",new String[]{"The Da Vinci Code"});

这里update()方法的第一个参数为表名称,第二个为实例化的ContentValues,第三个参数对应sql语句中的where。?是一个占位符,它等于第四个参数的值。

5. 删除数据

SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
db.delete("Book","pages > ?",new String[]{"500"} );

删除pages大于500的数据。

6. 查询数据库

这里先调出moveToFirst()方法将数据指针移动到第一位,然后使用do...while来循环导出数据,最后使用close()方法关闭cursor

SQLiteDatabase db = dbHelper.getWritableDatabase();
Cursor cursor = db.query("Book",null,null,null,null,null,null);
if (cursor.moveToFirst()){
    do{
        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.d(TAG, "书的名字为" + name);
        Log.d(TAG, "书的作者为" + author);
        Log.d(TAG, "书的页数为" + pages);
        Log.d(TAG, "书的价格为" + price);
    }while (cursor.moveToNext());
}
cursor.close();

7. 使用SQL操作数据库

a. 添加数据

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

b. 更新数据

db.execSQL("update Book set price = ? where name = ?", new String[]{"10.99","The Da Vinci Code"});

c. 删除数据

db.execSQL("delete from Book where pages > ?" ,new String[]{"500"});

d. 查询数据

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

项目地址:https://github.com/imlhb/StudyAndroid/tree/master/DatabaseTest

四、使用LitePal操作数据

传统的建表方式就像上面那样复杂,并且再升级数据库的情况下,会先把之前的表drop掉,然后再重新创建。这是特别严重的,导致数据丢失,每升级一次数据库就会清空之前的数据。
然而LitePal就不会。

1. 配置LitePal

第一步

打开app/build.gradle文件,在dependencies闭包中添加:implementation 'org.litepal.android:core:1.4.1',目前最新版本是2.0,我们参考《Android 第一行代码》第二版进行编写。

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:28.0.0-alpha3'
    implementation 'com.android.support.constraint:constraint-layout:1.1.2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    implementation 'org.litepal.android:core:1.4.1'
}

第二步

然后在app/src/main目录下新建个assets目录,并新建个文件litepal.xml,编辑该文件:
TIM截图20180715132508.png

<?xml version="1.0" encoding="utf-8" ?>
<litepal>
    <dbname value="BookStore"></dbname>
    <version value="1"></version>
    <list>

    </list>
</litepal>

<dbname>标签:数据库名
<version>标签:数据库版本号
<list>标签:指定所有映射模型

第三步

最后配置LitePalApplication,修改AndroidManifest.xml中代码,增加android:name="org.litepal.LitePalApplication"

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.litepaltest">

    <application
//增加
        android:name="org.litepal.LitePalApplication"
//增加
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

这里我们将项目的application配置为org.litepal.LitePalApplication,这样才能让LitePal所有的功能正常使用。

2. 创建和升级数据库

a.创建数据库

先定义一个book类

package com.example.litepaltest;
import org.litepal.crud.DataSupport;
public class Book extends DataSupport{
    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;
    }
}

然后修改litepal.xml中<list>里面的代码

    <list>
        <mapping class="com.example.litepaltest.Book"></mapping>
    </list>

这里使用<mapping>标签来申明我们要配置的映射模型类。
最后,在MainActivity中增加代码LitePal.getDatabase();,这里同样是通过点击生成。

        createDatabase.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                LitePal.getDatabase();
            }
        });

使用sqlite3查看
TIM截图20180715133813.png

b.升级数据库

这里升级数据库我们尝试先给Book表新增加一个字段,press,只需要添加:

private String press;

public String getPress() {
    return press;
}

public void setPress(String press) {
    this.press = press;
}

然后我们和之前一样,创建一个Category类:

package com.example.litepaltest;

public class Category {
    private int id;

    private String categoryName;

    private int categoryCode;

    public int getId() {
        return id;
    }

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

    public String getCategoryName() {
        return categoryName;
    }

    public void setCategoryName(String categoryName) {
        this.categoryName = categoryName;
    }

    public int getCategoryCode() {
        return categoryCode;
    }

    public void setCategoryCode(int categoryCode) {
        this.categoryCode = categoryCode;
    }
}

添加category到映射模型中:

<?xml version="1.0" encoding="utf-8" ?>
<litepal>
    <dbname value="BookStore"></dbname>
//修改版本号
    <version value="2"></version>
    <list>
        <mapping class="com.example.litepaltest.Book"></mapping>
//新增
        <mapping class="com.example.litepaltest.Category"></mapping>
    </list>
</litepal>

运行,点击查看。

TIM截图20180715134337.png

3. 添加数据

这边第一步先需要Book类继承DataSupport类

public class Book extends DataSupport{...}

然后修改MainActivity中代码

        Button addData = (Button) findViewById(R.id.add_data);
        addData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Book book = new Book();
                book.setName("第一行代码");
                book.setAuthor("郭霖");
                book.setPages(570);
                book.setPrice(79);
                book.setPress("人民邮电出版社");
                book.save();
            }
        });

代码比较简单我就不多解释了。

4.更新数据

        Button updateData = (Button) findViewById(R.id.update_data);
        updateData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Book book = new Book();
                book.setPrice(14.95);
                book.setPress("Anchor");
                book.updateAll("name = ? and author = ?","第一行代码","郭霖");
            }
        });

5. 删除数据

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

6. 查询数据

        Button queryButton = (Button) findViewById(R.id.query_data);
        queryButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                List<Book> books = DataSupport.findAll(Book.class);
                for (Book book:books){
                    Log.d(TAG, "onClick: name is " + book.getName());
                    Log.d(TAG, "onClick: author is " + book.getAuthor());
                    Log.d(TAG, "onClick: pages is " + book.getPages());
                    Log.d(TAG, "onClick: price is " + book.getPrice());
                    Log.d(TAG, "onClick: press is " + book.getPress());
                }
            }
        });

7.查询的其他用法

查询第一条数据

Book firstBook = DataSupport.findFirst(Book.class);

查询最后一条数据

Book firstBook = DataSupport.findLast(Book.class);

查询哪几列的数据

List<Book> books = DataSupport.select("name","author").find(Book.class);

where()约束条件

List<Book> books = DataSupport.where("pages > ?","400").find(Book.class);

order()用法

List<Book> books = DataSupport.order("price desc").find(Book.class);

limit()用法

指定结果数量,比如结果的前3个。

List<Book> books = DataSupport.limit(3).find(Book.class);

offset()用法

查询结果偏移量,比如第2条,第3条。

List<Book> books = DataSupport.limit(3).offset(1).find(Book.class);

对5个方法组合

代表Book表中,第11~20条满足页数大于400的这个条件的name,author,pages这三个列的数据。

List<Book> books = DataSupport.select("name","author").where("pages > ?","400").order("price desc").limit(10).offset(10).find(Book.class);
Last modification:July 20th, 2018 at 12:22 am
If you think my article is useful to you, please feel free to appreciate

2 comments

  1. 武胜

    文章很好值得一看

    1. 野生大熊猫
      @武胜

      谢谢!

Leave a Comment