【稀饭】react native 实战系列教程之数据存储
来源:互联网 发布:手机淘宝怎么修改店招 编辑:程序博客网 时间:2024/04/29 10:45
概述
在开发一款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;},
创建收藏表
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();}
启动程序就可以看到成功执行了
以面向对象的思想来说,我们需要为收藏表的字段创建一个实体类对象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
- 【稀饭】react native 实战系列教程之数据存储
- 【稀饭】react native 实战系列教程之项目介绍
- 【稀饭】react native 实战系列教程之项目初始化
- 【稀饭】react native 实战系列教程之完成首页
- 【稀饭】react native 实战系列教程之自定义原生模块
- 【稀饭】react native 实战系列教程之影片数据获取并解析
- 【稀饭】react native 实战系列教程之首页列表UI实现
- 【稀饭】react native 实战系列教程之Navigator实现页面跳转
- 【稀饭】react native 实战系列教程之自定义原生UI组件
- 【稀饭】react native 实战系列教程之热更新原理分析与实现
- [置顶] 【稀饭】react native 实战系列教程之热更新原理分析与实现
- 【稀饭】react native 系列教程之已有项目接入React Native
- 【稀饭】react native 系列教程之已有项目接入React Native
- React-Native实战系列
- react native 实战系列教程之热更新原理分析与实现
- react native 实战系列教程之热更新原理分析与实现
- React Native 实战系列一
- React Native 实战系列二
- 深入Java关键字null
- 理解CGI、FCGI、php-cgi、php-fpm的概念
- 97条架构建议之持续集成-进度调整-取舍
- 利用Docker搭建大数据处理集群
- VS20XX C程序连接数据库时的配置
- 【稀饭】react native 实战系列教程之数据存储
- Fiddler抓不到forefox火狐浏览器包
- 什么是md5盐值
- poj 3304
- iOS 图片下拉变大
- 1-初识java
- 最小m段和问题
- linux常用命令集合
- struts项目,拦截器注入action参数失败的原因