最近在弄react native的code push热更新问题。开始是用的后台默默更新配置。由于微软服务器速度问题,经常遇到用户一直在下载中问题。而用户也不知道代码需要更新才能使用新功能,影响了正常业务流程。而目前公司也无力搭建自己的服务器和dns设置。所以比较快速的方案就是,前端自定义热更新弹框,在需要更新代码的情况下禁止用户向下操作。
- /**
- * Created by susie on 2018/9/20.
- */
- import React, { Component } from 'react';
- import {View, Text, StyleSheet, Modal, TouchableOpacity, Image , Dimensions , Alert} from 'react-native'
- import CodePush from "react-native-code-push"
- import Progress from './CusProgressBar';
- import color from '../../styles/theme';
- import {showLoadingImg,hideLoadingImg,px2pt} from '../../utils/util';
- import Global from "../../constants/global";
-
-
- let SCREEN_WIDTH = Dimensions.get('window').width;//宽
- let SCREEN_HEIGHT = Dimensions.get('window').height;//高
-
- let codePushOptions = {
- checkFrequency : CodePush.CheckFrequency.ON_APP_START,
- installMode: CodePush.InstallMode.IMMEDIATE
- }
-
- class CodePushModal 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})
- CodePush.allowRestart();
- break;
- }
- }
- }
-
- codePushDownloadDidProgress(progress) {
- var self = this;
- if(self.state.immediateUpdate){
- self.currProgress = parseFloat(progress.receivedBytes / progress.totalBytes).toFixed(2);
- if(self.currProgress >= 1) {
- self.setState({modalVisible: false})
- } else if(self.refs.progressBar) {
- self.refs.progressBar.progress = self.currProgress;
- self.refs.progressBar.buffer = self.currProgress;
- }
- }
- }
-
- syncImmediate() {
- CodePush.checkForUpdate().then((update) => {
- Global.isCheckCodePush = false;
- hideLoadingImg();
- if (!update) {
- CodePush.allowRestart();
- } else {
- this.setState({modalVisible: true, updateInfo: update, isMandatory: update.isMandatory})
- }
- }).catch(function () {
- Global.isCheckCodePush = false;
- CodePush.allowRestart();
- })
- }
-
- componentWillMount() {
- Global.isCheckCodePush = true;
- showLoadingImg();
- CodePush.disallowRestart()
- this.syncImmediate()
- }
-
- componentDidMount() {
- //CodePush.allowRestart()
- }
-
- _immediateUpdateNew() {
- this.setState({immediateUpdate: true});
- let self = this;
- var timer = setTimeout(function () {
- CodePush.sync(
- {
- updateDialog: {},
- installMode: CodePush.InstallMode.IMMEDIATE},
- self.codePushStatusDidChange.bind(self),
- self.codePushDownloadDidProgress.bind(self)
- )
- clearTimeout(timer);
- CodePush.allowRestart();
- },10);
- }
-
- render() {
- return (
- <View style={styles.container}>
- <Modal
- animationType={"none"}
- transparent={true}
- onRequestClose={() => {}}
- visible={this.state.modalVisible}
- >
- <View style={styles.modal}>
- <View style={styles.modalContainer}>
- {
- !this.state.immediateUpdate ?
- <View>
- <View style={styles.modalContent}>
- <View>
- <Text style={styles.modalTitle}>页面升级</Text>
- </View>
- <View style={styles.updateDes}>
- <Text style={styles.updateDesText}>升级内容:</Text>
- <Text style={styles.updateDesText}>{this.state.updateInfo.description}</Text>
- </View>
- <View style={styles.updateTip}>
- <Text style={styles.updateTipText}>本升级非APP更新,wifi环境下30s内即可完成</Text>
- </View>
- {
- !this.state.isMandatory ?
- <View style={styles.updateBtns}>
- <TouchableOpacity
- onPress={() => this.setState({modalVisible: false})}>
- <View style={[styles.btnRight,styles.btnLeft]}>
- <Text style={{fontSize: 16, color: '#989898'}}>残忍拒绝</Text>
- </View>
- </TouchableOpacity>
- <TouchableOpacity
- style={styles.btnRight}
- onPress={() => this._immediateUpdateNew()}
- >
- <View style={styles.btnRightText}>
- <Text style={{fontSize: 16, color: color.theme}}>立即升级</Text>
- </View>
- </TouchableOpacity>
- </View> :
- <View style={styles.updateBtns}>
- <TouchableOpacity
- style={[styles.btnRight,styles.onlyBtn]}
- onPress={() => this._immediateUpdateNew()}
- >
- <View style={[styles.btnRightText,{marginHorizontal: 40}]}>
- <Text style={{fontSize: 16, color: color.theme, letterSpacing:1}}>立即升级</Text>
- </View>
- </TouchableOpacity>
- </View>
- }
- </View>
- </View> : <View style={styles.modalContent}>
- <View>
- <Text style={styles.modalTitle}>页面升级中</Text>
- </View>
- <View style={{ paddingVertical: 20, alignItems: 'center'}}>
- <Progress
- ref="progressBar"
- progressColor={'#89C0FF'}
- style={{
- marginTop: 20,
- width: SCREEN_WIDTH - 80,
- marginLeft:10,
- marginRight:10
- }}
- />
- <View style={styles.updateTip}>
- <Text style={styles.updateTipText}>本升级非APP更新,wifi环境下30s内即可完成</Text>
- </View>
- </View>
- </View>
-
- }
- </View>
- </View>
- </Modal>
- </View>
- );
- }
- }
- let modalW = SCREEN_WIDTH - 40;
- const styles = StyleSheet.create({
- container: {
- flex: 1,
- justifyContent: 'center',
- alignItems: 'center'
- },
- modal: {
- height: SCREEN_HEIGHT,
- width: SCREEN_WIDTH,
- alignItems: 'center',
- justifyContent: 'center',
- backgroundColor: 'rgba(0,0,0,0.3)'
- },
- modalContainer: {
- marginHorizontal: px2pt(120),
- borderBottomLeftRadius: px2pt(20),
- borderBottomRightRadius: px2pt(20),
- },
- modalContent:{
- backgroundColor: '#FFF',
- borderRadius:5,
- width: modalW
- },
- modalTitle:{
- marginTop:px2pt(70),
- fontSize:px2pt(36),
- color:color.gray3,
- textAlign:'center',
- width:'100%'
- },
- modalTips:{
- fontSize:px2pt(28),
- color:color.darkOrange,
- textAlign:'center',
- marginBottom:px2pt(10)
- },
- updateDes:{
- marginLeft:px2pt(40),
- marginRight:px2pt(40),
- marginTop:px2pt(30)
- },
- updateDesText:{
- fontSize:px2pt(32),
- color: color.gray6,
- lineHeight:px2pt(44)
- },
- updateTip:{
- alignItems: 'center',
- marginTop: px2pt(40)
- },
- updateTipText:{
- fontSize: px2pt(28),
- color: color.gray6
- },
- updateBtns:{
- flexDirection: 'row',
- height: px2pt(100),
- alignItems: 'center',
- marginTop: px2pt(40),
- borderTopColor: '#E6E6E6',
- borderTopWidth: 1
- },
- btnLeft:{
- borderRightColor: '#E6E6E6',
- borderRightWidth: 1
- },
- btnRight:{
- flexDirection: 'row',
- alignItems: 'center',
- width: modalW / 2,
- height:px2pt(100),
- justifyContent: 'center'
- },
- btnRightText:{
- flex: 1,
- height:px2pt(80),
- alignItems: 'center',
- justifyContent: 'center'
- },
- onlyBtn:{
- width: modalW
- }
- })
-
- export default CodePush(codePushOptions)(CodePushModal)
- /**
- * Created by susie on 2018/9/20.
- */
- import React, {Component}from 'react'
- import {View, StyleSheet, Animated, Easing,Text}from 'react-native'
- import LinearGradient from 'react-native-linear-gradient'
-
- 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,
- // 进度动画时长
- progressAniDuration: PropTypes.number,
- // buffer动画时长
- bufferAniDuration: PropTypes.number
- }
-
- static defaultProps = {
- // 进度条颜色
- progressColor: 'white',
- // 进度条动画时长
- 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)}>
- <LinearGradient colors={['#daddff', '#d3eeff']} start={{ x: 0, y: 0 }} end={{ x: 1, y: 0 }} style={{position:'absolute',borderRadius:10,width:'100%',height:'100%'}}></LinearGradient>
- <Animated.View
- ref="progress"
- style={{
- position:'absolute',
- width: this._progressAni,
- borderRadius:10
- }}>
- <LinearGradient colors={['#4669ff', '#3eefff']} start={{ x: 0, y: 0 }} end={{ x: 1, y: 0 }} style={{borderRadius:10,width:'100%',height:'100%'}}></LinearGradient>
- </Animated.View>
- <Animated.Image
- ref="buffer"
- style={{
- position:'absolute',
- left: this._bufferAni,
- marginLeft:0,
- top : -3,
- width:35
- }} source={require('../../styles/images/huojian.png')} />
- </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+6
- }
- })
- }
- }
-
- _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: 12
- }
- })