【稀飯】react native 實戰系列教程之數據存儲

概述

在開發一款APP,對於數據的存儲是在正常不過了,在此之前,【稀飯】這個應用還沒有用到存儲數據的地方,爲了學習研究React Native的數據存儲,打算給應用增加【我的收藏】和【觀看歷史】這兩個功能。接下來,我們來看看如何實現。

我的收藏

關於React Native數據存儲的解決方案

關於RN如何存儲數據,有兩種方案。
- AsyncStorage
- SQLite

第一種是官網提供的一種數據存儲方案,它是一個簡單的、異步的、持久化的Key-Value文件存儲系統,它對於App來說是全局性的。如果你是個android的開發者,那麼這就是類似於SharedPreferences,它適用於存儲些系統設置、全局變量等簡單的key-value數據,不適用於value過於龐大的數據,也不適用於一些包含數據結構等複雜數據;那麼針對這種不足,我們需要藉助SQLite,輕量的數據庫,但RN並沒有提供,如果你看完了之前的自定義模塊 ,那麼你也可以利用原生的SQLiteDatabase開發自己的一個數據庫。顯然,這種需求很普遍,網上肯定有很多現有的輪子,我們就可以直接拿來用了。這裏推薦使用 react-native-sqlite-storage

實現我的收藏功能

從上面的效果圖來看,我們需要在詳情頁面增加一個收藏按鈕,點擊之後,空心圖變實心圖,然後在收藏列表裏增加一條數據。要實現這個功能,需要在點擊收藏之後,將需要的信息保存到數據庫中,然後在列表頁讀取出來顯示。這裏利用react-native-sqlite-storage這個第三方庫來實現數據的存儲。

引入 react-native-sqlite-storage

安裝

根據github上的文檔說明(Android部分),首先我們在項目根目錄下執行cmd命令:

npm install --save react-native-sqlite-storage

如果命令執行很久沒有反應,建議換個npm鏡像(淘寶鏡像

配置

修改android項目的settings.gradle

// file: android/settings.gradle
...

include ':react-native-sqlite-storage'
project(':react-native-sqlite-storage').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-sqlite-storage/src/android')

修改app\build.gradle

// file: android/app/build.gradle
...

dependencies {
    ...
    compile project(':react-native-sqlite-storage')
}

修改MainApplication.java,添加SQLitePluginPackage

public class MainApplication extends Application implements ReactApplication {

    private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
        @Override
        protected boolean getUseDeveloperSupport() {
            return BuildConfig.DEBUG;
        }

        @Override
        protected List<ReactPackage> getPackages() {
            return Arrays.<ReactPackage>asList(
                    new MainReactPackage(),
                    new OrientationPackage(),
                    new VideoViewPackage(),
                    new SQLitePluginPackage()
            );
        }
    };

    @Override
    public ReactNativeHost getReactNativeHost() {
        return mReactNativeHost;
    }
}

到這裏算是做好了前期的配置工作,下面結合實際需求講述如何使用它。

接口封裝

首先,我們需要封裝一個SQLite模塊,方便應用調用。

在項目根目錄js下新建db文件夾,然後在js/db/下新建SQLite.js

import React from 'react';
import SQLiteStorage from 'react-native-sqlite-storage';

SQLiteStorage.DEBUG(true);
const SQLite = React.createClass({
    render (){
        return null;
    },
});
module.exports = SQLite;

該模塊類似於工具類,不需要渲染任何界面,所以render return null。

定義打開數據open和關閉數據庫close的方法

open(){
    db = SQLiteStorage.openDatabase(
        database_name,
        database_version,
        database_displayname,
        database_size,
        ()=>{
            this._successCB('open');
        },
        (err)=>{
            this._errorCB('open',err);
        });
},
close(){
    if(db){
        this._successCB('close');
        db.close();
    }else {
        console.log("SQLiteStorage not open");
    }
    db = null;
},

創建收藏表

字段 類型 說明
id INTEGER 主鍵
name VARCHAR 電影名稱
actor VARCHAR 主演
time VARCHAR 收藏時間
pic VARCHAR 封面
url VARCHAR 詳情地址
title VARCHAR 標題
createTable(){
    if (!db) {
        open();
    }
    //創建收藏表
    db.transaction((tx)=> {
        tx.executeSql('CREATE TABLE IF NOT EXISTS ' + Collection_TABLE_NAME + '(' +
            'id INTEGER PRIMARY KEY NOT NULL,' +
            'name VARCHAR,' +
            'actor VARCHAR,' +
            'time VARCHAR,' +
            'pic VARCHAR,' +
            'url VARCHAR,' +
            'title VARCHAR'
            + ');'
            , [], ()=> {
                this._successCB('executeSql');
            }, (err)=> {
                this._errorCB('executeSql', err);
            });
    }, (err)=> {
        this._errorCB('transaction', err);
    }, ()=> {
        this._successCB('transaction');
    })
}

以上完整的代碼:

import React from 'react';
import SQLiteStorage from 'react-native-sqlite-storage';

SQLiteStorage.DEBUG(true);
var database_name = "xifan.db";
var database_version = "1.0";
var database_displayname = "MySQLite";
var database_size = -1;
var db;
const Collection_TABLE_NAME = "Collection";//收藏表

const SQLite = React.createClass({

    render(){
        return null;
    },
    componentWillUnmount(){
        if(db){
            this._successCB('close');
            db.close();
        }else {
            console.log("SQLiteStorage not open");
        }
    },
    open(){
        db = SQLiteStorage.openDatabase(
            database_name,
            database_version,
            database_displayname,
            database_size,
            ()=>{
                this._successCB('open');
            },
            (err)=>{
                this._errorCB('open',err);
            });
    },
    createTable(){
        if (!db) {
            open();
        }
        //創建收藏表
        db.transaction((tx)=> {
            tx.executeSql('CREATE TABLE IF NOT EXISTS ' + Collection_TABLE_NAME + '(' +
                'id INTEGER PRIMARY KEY NOT NULL,' +
                'name VARCHAR,' +
                'actor VARCHAR,' +
                'time VARCHAR,' +
                'pic VARCHAR,' +
                'url VARCHAR,' +
                'title VARCHAR'
                + ');'
                , [], ()=> {
                    this._successCB('executeSql');
                }, (err)=> {
                    this._errorCB('executeSql', err);
                });
        }, (err)=> {
            this._errorCB('transaction', err);
        }, ()=> {
            this._successCB('transaction');
        })
    },
    close(){
        if(db){
            this._successCB('close');
            db.close();
        }else {
            console.log("SQLiteStorage not open");
        }
        db = null;
    },
    _successCB(name){
        console.log("SQLiteStorage "+name+" success");
    },
    _errorCB(name, err){
        console.log("SQLiteStorage "+name+" error:"+err);
    }
});

module.exports = SQLite;

然後在程序啓動進入到首頁時去創建表

MainScene.js

import SQLite from './db/SQLite';
var sqLite = new SQLite();
//省略其它代碼
componentDidMount(){
    sqLite.createTable();
}

componentWillUnmount(){
    sqLite.close();
}

啓動程序就可以看到成功執行了

create table

以面向對象的思想來說,我們需要爲收藏表的字段創建一個實體類對象Movie
在js/db下創建Movie.js

import React from 'react';
var id;
var name = "";
var actor = "";
var time = "";
var pic = "";
var url = "";
var title = "";

const Movie = React.createClass({
    render(){
        return null;
    }
    ,
    setId(id){
        this.id = id;
    },
    getId(){
        return this.id;
    },
    setName(name){
        this.name = name;
    },
    getName(){
        return this.name;
    },
    setActor(actor){
        this.actor = actor;
    },
    getActor(){
        return this.actor;
    },
    setTime(time){
        this.time = time;
    },
    getTime(){
        return this.time;
    },
    setPic(pic){
        this.pic = pic;
    },
    getPic(){
        return this.pic;
    },
    setUrl(url){
        this.url = url;
    },
    getUrl(){
        return this.url;
    },
    setTitle(title){
        this.title = title;
    },
    getTitle(){
        return this.title;
    }
});
module.exports = Movie;

接着,我們爲收藏表增加增刪查方法

saveCollection(movie){//保存收藏記錄
    return new Promise((resolve, reject)=>{
        if(db){
            db.executeSql(
                'INSERT INTO '+Collection_TABLE_NAME+' (name,actor,time,pic,url,title) VALUES(?,?,?,?,?,?)',
                [movie.getName(),movie.getActor(),movie.getTime(),movie.getPic(),movie.getUrl(),movie.getTitle()],
                ()=>{
                    this._successCB('saveCollection');
                    resolve();
                },
                (err)=>{
                    this._errorCB('saveCollection',err);
                    reject();
                })
        }else {
            reject('db not open');
        }
    });

}
findCollectionByName(name){//通過影片名稱獲取對應收藏記錄
    return new Promise((resolve, reject)=>{
        if(db){
            db.executeSql('SELECT * FROM '+Collection_TABLE_NAME +' WHERE name=? LIMIT 1',[name],
                (results)=>{
                    console.log(results);
                    if(results.rows.length > 0){
                        resolve(results.rows.item(0));
                    }else {
                        reject('not find item');
                    }

                    this._successCB('findCollectionByName')
                },(err)=>{
                    reject(err);
                    this._errorCB('findCollectionByName',err)
                });
        }else {
            reject('db not open');
        }
    });

}
deleteCollectionByName(name){//通過影片名稱刪除對應收藏記錄
    return new Promise((resolve, reject)=>{
        if(db){
            db.executeSql('DELETE FROM '+Collection_TABLE_NAME +' WHERE name=?',[name],
                ()=>{
                    resolve();
                    this._successCB('deleteCollectionByName');
                },(err)=>{
                    reject(err);
                    this._errorCB('deleteCollectionByName',err);
                });
        }else {
            reject('db not open');
        }
    });

}
listCollection(pageSize,index){//獲取收藏記錄列表
    return new Promise((resolve, reject)=>{
        if(db){
            db.executeSql('SELECT * FROM '+Collection_TABLE_NAME +' LIMIT '+pageSize+' OFFSET '+((index-1)*pageSize),[],
                (results)=>{
                    var len = results.rows.length;
                    var datas = [];
                    for(let i=0;i<len;i++){
                        datas.push(results.rows.item(i));
                    }
                    resolve(datas);
                    this._successCB('listCollection');
                },(err)=>{
                    reject(err);
                    this._errorCB('listCollection',err);
                });
        }else {
            reject('db not open');
        }
    });
}

這幾個方法都使用到了Promise,這使得對象調用時可以使用鏈式的方法,更加方便。

接口調用

定義完接口,我們就可以按需求來實現了。在詳情頁DramaDetailScene.js添加一個收藏按鈕,如效果圖,這裏不在闡述UI的實現,直接來看如何保存數據。

//收藏
_onCollectionPress(movie){
    console.log(movie);
    /*{ name: '信義',
        title: '全集中字',
        actor: '金喜善,李敏鎬,劉德煥,樸世英,李必立,沈恩京,成勳,李民浩',
        pic: 'http://img.y3600.com/d/file/p/2016/10/26/40d39df617fc663a21f1e433e67742de.jpg',
        url: '/hanju/2016/958.html' }*/
    var isCollection = !this.state.isCollection;
    if(isCollection){//保存
        var coll = new Movie();
        coll.setName(movie.name);
        coll.setActor(movie.actor);
        coll.setPic(movie.pic);
        coll.setUrl(movie.url);
        coll.setTitle(movie.title);
        var date = new Date();
        var time=date.getFullYear()+'-'+(date.getMonth()+1)+'-'+date.getDate()+' ';
        var hours = date.getHours();
        if(hours < 9){
            time = time+'0'+hours+':';
        }else {
            time = time+hours+':';
        }
        var minutes = date.getMinutes();
        if(minutes < 9){
            time = time+'0'+minutes+':';
        }else {
            time = time+minutes+':';
        }
        var sec = date.getSeconds();
        if(sec < 9){
            time = time+'0'+sec;
        }else {
            time = time+sec;
        }
        coll.setTime(time);
        sqlite.saveCollection(coll).then(()=>{
            this.setState({
                isCollection:isCollection,
            });
        }).catch((e)=>{}).done();
    }else {//刪除
        sqlite.deleteCollectionByName(this.props.data.name).then(()=>{
            this.setState({
                isCollection:isCollection,
            })
        }).catch((e)=>{}).done();
    }
}

我們通過this.state.isCollection來保存收藏狀態。當未收藏時執行保存,已收藏時執行刪除。

再者,當我們一進入詳情頁時,需要知道是否已經收藏。

componentDidMount(){
    sqlite.findCollectionByName(this.props.data.name).then((result)=>{
        if(result){
            this.setState({
                isCollection:true,
            });
        }
    }).catch((e)=>{}).done();
    this._fetchData(this.props.data.url);
}

最後就是在我的收藏列表檢索出所有的收藏記錄並展示

_queryData(){
    sqlite.listCollection(10,index).then((results)=>{
        datas = datas.concat(results);
        this.setState({
            movies:this.state.movies.cloneWithRows(datas),
            isRefreshing:false
        });
    }).catch((err)=>{

    }).done();
}

【觀看歷史】也是差不多這個流程,具體的實現不在這裏貼代碼了,更多請查看我的github

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章