CodePush自定义更新弹框及下载进度条
codepush 热更新之自定义更新弹框及下载进度
先来几张弹框效果图
-
非强制更新场景
-
强制更新场景
-
更新包下载进度效果
核心代码
这里的热更新modal框,是封装成一个功能独立的组件来使用的,需不需要更新以及是否为强制更新等逻辑均在组件内实现
updatecomp 热更新组件核心代码如下:
/** * created by guangqiang on 2018/3/29. */ import react, {component} from 'react' import {view, text, stylesheet, modal, touchableopacity, image} from 'react-native' import progress from './index' import {globalstyles} from '../../../constants/globalstyles' import {deviceinfo} from "../../../constants/deviceinfo" import {icon} from '../../../utils/iconfont' import codepush from "react-native-code-push" import {toast} from "../../../utils/toast" const code_push_key = 'je39cjdnkzqfpxgrylpxddnkezjm3ac740b8-b071-474f-afbf-369c6e4642ab' let codepushoptions = { checkfrequency : codepush.checkfrequency.on_app_start } class progressbar extends component { constructor(props) { super(props) this.currprogress = 0.0 this.syncmessage = '' this.state = { modalvisible: false, ismandatory: false, immediateupdate: false, updateinfo: {} } } codepushstatusdidchange(syncstatus) { if (this.state.immediateupdate) { switch(syncstatus) { case codepush.syncstatus.checking_for_update: this.syncmessage = 'checking for update' break; case codepush.syncstatus.downloading_package: this.syncmessage = 'downloading package' break; case codepush.syncstatus.awaiting_user_action: this.syncmessage = 'awaiting user action' break; case codepush.syncstatus.installing_update: this.syncmessage = 'installing update' break; case codepush.syncstatus.up_to_date: this.syncmessage = 'app up to date.' break; case codepush.syncstatus.update_ignored: this.syncmessage = 'update cancelled by user' break; case codepush.syncstatus.update_installed: this.syncmessage = 'update installed and will be applied on restart.' break; case codepush.syncstatus.unknown_error: this.syncmessage = 'an unknown error occurred' toast.showerror('更新出错,请重启应用!') this.setstate({modalvisible: false}) break; } } } codepushdownloaddidprogress(progress) { if (this.state.immediateupdate) { this.currprogress = parsefloat(progress.receivedbytes / progress.totalbytes).tofixed(2) if(this.currprogress >= 1) { this.setstate({modalvisible: false}) } else { this.refs.progressbar.progress = this.currprogress } } } syncimmediate() { codepush.checkforupdate(code_push_key).then((update) => { console.log('-------' + update) if (!update) { toast.showlongsuccess('已是最新版本!') } else { this.setstate({modalvisible: true, updateinfo: update, ismandatory: update.ismandatory}) } }) } componentwillmount() { codepush.disallowrestart() this.syncimmediate() } componentdidmount() { codepush.allowrestart() } _immediateupdate() { this.setstate({immediateupdate: true}) codepush.sync( {deploymentkey: code_push_key, updatedialog: {}, installmode: codepush.installmode.immediate}, this.codepushstatusdidchange.bind(this), this.codepushdownloaddidprogress.bind(this) ) } rendermodal() { return ( <modal animationtype={"none"} transparent={true} visible={this.state.modalvisible} onrequestclose={() => alert("modal has been closed.")}> <view style={styles.modal}> <view style={styles.modalcontainer}> { !this.state.immediateupdate ? <view> <image style={{width: deviceinfo.devicewidth - 60}} source={require('../../../assets/images/me/updatebg.png')} resizemode={'stretch'}/> <view style={{backgroundcolor: globalstyles.white}}> <view style={{marginhorizontal: 15}}> <text style={{marginvertical: 20, fontsize: 17, color: globalstyles.textblockcolor, fontweight: 'bold'}}>更新内容</text> <text style={{lineheight: 20}}>{this.state.updateinfo.description}</text> </view> <view style={{alignitems: globalstyles.center, margintop: 20}}> <text style={{fontsize: 14, color: globalstyles.textgraycolor}}>wifi情况下更新不到30秒</text> </view> { !this.state.ismandatory ? <view style={{flexdirection: globalstyles.row, height: 50, alignitems: globalstyles.center, margintop: 20, bordertopcolor: globalstyles.linecolor, bordertopwidth: 1 }}> <touchableopacity onpress={() => this.setstate({modalvisible: false})}> <view style={{flexdirection: globalstyles.row, alignitems: globalstyles.center, width: (deviceinfo.devicewidth - 60) / 2, height: 50, borderrightcolor: globalstyles.linecolor, borderrightwidth: 1, alignitems: globalstyles.center, justifycontent: globalstyles.center}}> <icon name={'oneicon|reject_o'} size={20} color={'#b6b6b6'}/> <text style={{fontsize: 17, fontweight: 'bold', color: globalstyles.textgraycolor, marginleft: 10}}>残忍拒绝</text> </view> </touchableopacity> <touchableopacity style={{flexdirection: globalstyles.row, alignitems: globalstyles.center, width: (deviceinfo.devicewidth - 60) / 2, height: 50, alignitems: globalstyles.center, justifycontent: globalstyles.center}} onpress={() => this._immediateupdate()} > <view style={{backgroundcolor: '#3496fa', flex: 1, height: 40, alignitems: globalstyles.center, justifycontent: globalstyles.center, margin: 10, borderradius: 20}}> <text style={{fontsize: 17, color: globalstyles.white, fontweight: 'bold'}}>极速下载</text> </view> </touchableopacity> </view> : <view style={{flexdirection: globalstyles.row, height: 60, alignitems: globalstyles.center, margintop: 20, bordertopcolor: globalstyles.linecolor, bordertopwidth: 1, width: deviceinfo.devicewidth - 60}}> <touchableopacity style={{flexdirection: globalstyles.row, alignitems: globalstyles.center, width: (deviceinfo.devicewidth - 60), height: 50, alignitems: globalstyles.center, justifycontent: globalstyles.center}} onpress={() => this._immediateupdate()} > <view style={{backgroundcolor: '#3496fa', flex: 1, height: 40, alignitems: globalstyles.center, justifycontent: globalstyles.center, borderradius: 20, marginhorizontal: 40}}> <text style={{fontsize: 17, color: globalstyles.white, fontweight: 'bold'}}>立即更新</text> </view> </touchableopacity> </view> } </view> </view> : <view> <image style={{width: deviceinfo.devicewidth - 60}} source={require('../../../assets/images/me/updatebg.png')} resizemode={'stretch'}/> <view style={{backgroundcolor: globalstyles.white, paddingvertical: 20, backgroundcolor: globalstyles.white, alignitems: globalstyles.center}}> <progress ref="progressbar" progresscolor={'#89c0ff'} style={{ margintop: 20, height: 10, width: deviceinfo.devicewidth - 100, backgroundcolor: globalstyles.bgcolor, borderradius: 10, }} /> <view style={{alignitems: globalstyles.center, marginvertical: 20}}> <text style={{fontsize: 14, color: globalstyles.textgraycolor}}>版本正在努力更新中,请等待</text> </view> </view> </view> } </view> </view> </modal> ) } render(){ return( <view style={styles.container}> {this.rendermodal()} </view> ) } } const styles = stylesheet.create({ container: { flex: 1, justifycontent: 'center', alignitems: 'center', backgroundcolor: globalstyles.bgcolor }, modal: { height: deviceinfo.deviceheight, width: deviceinfo.devicewidth, alignitems: 'center', justifycontent: 'center', backgroundcolor: 'rgba(0,0,0,0.3)' }, modalcontainer: { marginhorizontal: 60, borderbottomleftradius: 10, borderbottomrightradius: 10, } }) export default codepush(codepushoptions)(progressbar)
下载进度条组件progress
这里也是封装成一个组件,核心代码如下:
/** * created by guangqiang on 2018/3/29. */ import react, {component}from 'react' import {view, stylesheet, animated, easing}from 'react-native' import proptypes from 'prop-types' export default class cusprogressbar extends component { static proptypes = { ...view.proptypes, // 当前进度 progress: proptypes.number, // second progress进度 buffer: proptypes.number, // 进度条颜色 progresscolor: proptypes.string, // buffer进度条颜色 buffercolor: proptypes.string, // 进度动画时长 progressaniduration: proptypes.number, // buffer动画时长 bufferaniduration: proptypes.number } static defaultprops = { // 进度条颜色 progresscolor: 'white', // buffer进度条颜色 buffercolor: 'rgba(255,0,0,0.7)', // 进度条动画时长 progressaniduration: 100, // buffer进度条动画时长 bufferaniduration: 100 } constructor(props) { super(props) this._progressani = new animated.value(0) this._bufferani = new animated.value(0) } componentwillreceiveprops(nextprops) { this._progress = nextprops.progress this._buffer = nextprops.buffer } componentwillmount() { this._progress = this.props.progress this._buffer = this.props.buffer } render() { return ( <view style={[styles.container,this.props.style]} onlayout={this._onlayout.bind(this)}> <animated.view ref="progress" style={{ position:'absolute', width: this._progressani, backgroundcolor:this.props.progresscolor, borderradius: 10 }}/> <animated.view ref="buffer" style={{ position:'absolute', width: this._bufferani, backgroundcolor:this.props.buffercolor, borderradius: 10, }}/> </view> ) } _onlayout({nativeevent: {layout:{width, height}}}) { // 防止多次调用,当第一次获取后,后面就不再去获取了 if (width > 0 && this.totalwidth !== width) { // 获取progress控件引用 let progress = this._getprogress() // 获取buffer控件引用 let buffer = this._getbuffer() // 获取父布局宽度 this.totalwidth = width //给progress控件设置高度 progress.setnativeprops({ style: { height: height } }) // 给buffer控件设置高度 buffer.setnativeprops({ style: { height: height } }) // 开始执行进度条动画 this._startaniprogress(this.progress) // 开始执行buffer动画 this._startanibuffer(this.buffer) } } _startaniprogress(progress) { if (this._progress >= 0 && this.totalwidth !== 0) { animated.timing(this._progressani, { tovalue: progress * this.totalwidth, duration: this.props.progressaniduration, easing: easing.linear }).start() } } _startanibuffer(buffer) { if (this._buffer >= 0 && this.totalwidth !== 0) { animated.timing(this._bufferani, { tovalue: buffer * this.totalwidth, duration: this.props.bufferaniduration, }).start() } } _getprogress() { if (typeof this.refs.progress.refs.node !== 'undefined') { return this.refs.progress.refs.node } return this.refs.progress._component } _getbuffer() { if (typeof this.refs.buffer.refs.node !== 'undefined') { return this.refs.buffer.refs.node; } return this.refs.buffer._component; } } object.defineproperty(cusprogressbar.prototype, 'progress', { set(value){ if (value >= 0 && this._progress !== value) { this._progress = value; this._startaniprogress(value); } }, get() { return this._progress; }, enumerable: true, }) object.defineproperty(cusprogressbar.prototype, 'buffer', { set(value){ if (value >= 0 && this._buffer !== value) { this._buffer = value; this._startanibuffer(value); } }, get() { return this._buffer; }, enumerable: true, }) const styles = stylesheet.create({ container: { height: 4, backgroundcolor: 'blue' } })
对updatecomp
组件中的热更新核心代码讲解
这我们在updatecomp 组件中,在 componentwillmount
的生命周期函数中,我们调用codepush提供的这两个函数:并在syncimmediate
函数中,我们调用codepush的checkforupdate
函数来检查是否已有新版本,以及新版本的信息等,具体代码实现如下:
注意:
codepush有两个代理函数我们需要调用:
-
codepushstatusdidchange: codepush状态的变化的钩子函数
-
codepushdownloaddidprogress: codepush下载更新包的进度钩子函数
当我们处理完上面的内容,codepush的基本功能我们就处理完毕了,剩下的工作就是处理一些逻辑了,包括该不该弹更新框,以及更新弹框和更新进度的处理
总结:
本篇教程主要是讲解codepush中如何处理安装包的下载进度,以及如何自定义更新弹框和下载进度条,上面的弹框功能和下载进度条功能基本都已处理完毕,可以直接复制两个组件代码到自己项目中,稍作修改即可使用。如果还有小伙伴对codepush详细的接入流程不熟悉的,请点击查看作者的codepush热更新详细接入教程一文,如果还有其他的问题,也可以简书留言或者进群提问
rn实战总结
- 作者react native开源项目onem地址(按照企业开发标准搭建框架完成开发的):https://github.com/guangqiang-liu/onem:欢迎小伙伴们 star
- 作者简书主页:包含60多篇rn开发相关的技术文章欢迎小伙伴们:多多关注,多多点赞
- 作者react native qq技术交流群:620792950 欢迎小伙伴进群交流学习
- 友情提示:在开发中有遇到rn相关的技术问题,欢迎小伙伴加入交流群(620792950),在群里提问、互相交流学习。交流群也定期更新最新的rn学习资料给大家,谢谢大家支持!
上一篇: PHP 编码规范(19)_PHP教程
下一篇: Django实现登录随机验证码的示例代码