CodePush 热更新之自定义更新弹框及下载进度

code-push的提示面板可能不符合产品设计的要求,这时就需要我们去自定义一个更新提示框。下面是一个简单的例子:
CodePush 热更新之自定义更新弹框及下载进度_第1张图片
1. 检查是否需要更新
首先判断是否需要更新,如果有新版本,显示更新面板,根据用户行为执行后续操作。

import CodePush from "react-native-code-push";
const CodePushOptions = { checkFrequency: CodePush.CheckFrequency.MANUAL };
// 安卓下的热更新 key,可以根据platform判断是哪个平台再给key赋值
const key = 'jju*********************mob';

// component
...
UNSAFE_componentWillMount(){
   // 更新重启之后,防止回滚
    CodePush.notifyAppReady();
    // 检查更新
    this.check();
}

// 检查更新
check = () => {
    CodePush.checkForUpdate(key).then((update) => {
    	// update 不存在 或 当failedInstall为true时,不做操作
        if (!update || update.failedInstall) {
          
        } else {
            this.setState({
                modalVisible: true, // modal是否显示
                updateInfo: update, // 更新信息
                isMandatory: update.isMandatory   // 是否强制更新
            })
        }
    })
}
...

2. 自定义更新面板
自定义更新面板在下面完整代码处可看,此处不再过多讨论,根据UI设计去做即可。
3. 点击更新
用户点击自定义面板的更新后,需要执行下载更新,并提示下载进度的操作,下载完之后,自动重启进入应用。

// 用户点击更新,更新版本
update = () => {
    this.setState({isUpdate: true})
    // updateDialog: false,code-push不再对客户进行确认操作,直接更新,避免弹出原生的modal
    CodePush.sync(
        {deploymentKey: key, updateDialog: false, installMode: CodePush.InstallMode.IMMEDIATE},
        this.codePushStatusDidChange,
        this.codePushDownloadDidProgress
    )
}

// 取消,不显示modal面板
 onCancel = () => {
     this.setState({
         modalVisible: false
     })
 }

// 下载状态变化,监听状态变化,当更新出错时,提示更新失败信息并关闭面板
codePushStatusDidChange = (syncStatus) => {
    if (this.state.isUpdate) {
        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'
            // 当更新出错时,提示信息并关闭面板
            this.toast.show('更新出错,请重启应用!');
            this.setState({modalVisible: false})
            break;
        }
      }
}

// 计算下载进度
codePushDownloadDidProgress = (Progress) => {
    if (this.state.isUpdate) {
        let currProgress = Math.round((Progress.receivedBytes / Progress.totalBytes) * 100)/100;
        if(currProgress >= 1) {
            this.setState({modalVisible: false})
        } else {
            this.setState({
                progress: currProgress
            })
        }
    }
}

4. 完整代码

import React, {Component} from 'react';
import { connect } from 'react-redux';
import {View, Text, StyleSheet, Modal, TouchableNativeFeedback, Dimensions, ProgressBarAndroid} from 'react-native';
import Toast from './toast';
import CodePush from "react-native-code-push";
const CodePushOptions = { checkFrequency: CodePush.CheckFrequency.MANUAL };
// 安卓下的热更新 key
const key = 'jju*************************-mob';
// 屏幕
const win = Dimensions.get('window');
class HotpushModal extends React.Component{
    constructor(props){
        super(props);
        this.syncMessage = '';
        this.state = {
            modalVisible:false,
            isMandatory: false,
            isUpdate: false,
            updateInfo: {},
            progress: 0,
        }
    }

    UNSAFE_componentWillMount(){
        // 热更新
        CodePush.notifyAppReady();
        // 检查更新
        this.check();
    }

    // 检查更新
    check = () => {
        CodePush.checkForUpdate(key).then((update) => {
            console.log(update);
            if (!update || update.failedInstall) {
              // 已是最新版
            } else {
                this.setState({
                    modalVisible: true, 
                    updateInfo: update, 
                    isMandatory: update.isMandatory
                })
            }
        })
    }

    // 更新版本
    update = () => {
        this.setState({isUpdate: true})
        CodePush.sync(
            {deploymentKey: key, updateDialog: false, installMode: CodePush.InstallMode.IMMEDIATE},
            this.codePushStatusDidChange,
            this.codePushDownloadDidProgress
        )
    }

    // 取消
    onCancel = () => {
        this.setState({
            modalVisible: false
        })
    }

    // 下载状态变化
    codePushStatusDidChange = (syncStatus) => {
        if (this.state.isUpdate) {
            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'
                this.toast.show('更新出错,请重启应用!');
                this.setState({modalVisible: false})
                break;
            }
          }
    }

    // 计算下载进度
    codePushDownloadDidProgress = (Progress) => {
        if (this.state.isUpdate) {
            let currProgress = Math.round((Progress.receivedBytes / Progress.totalBytes) * 100)/100;
            if(currProgress >= 1) {
                this.setState({modalVisible: false})
            } else {
                this.setState({
                    progress: currProgress
                })
            }
        }
    }

    render() {
        return (
            <Modal
                animationType={"none"}
                transparent={true}
                visible={this.state.modalVisible} >
                <View style={styles.content}>
                    {!this.state.isUpdate ? <View style={styles.contentArea}>
                        <Text style={[styles.header,{color:this.props.theme.themeColor}]}>发现新版本</Text>
                        <View style={styles.updateDes}>
                            <Text style={styles.title}>更新内容</Text>
                            <Text style={[styles.description,{color:this.props.theme.subColor}]}>{this.state.updateInfo.description || '暂无版本介绍'}</Text>
                        </View>
                        {/* 按钮,判断是否强制更新 */}
                        {this.state.isMandatory ? <View style={styles.buttonArea}><TouchableNativeFeedback onPress={this.update}>
                            <View style={[styles.button,{backgroundColor:this.props.theme.themeColor}]}>
                                <Text style={styles.buttonText}>立即更新</Text>
                            </View></TouchableNativeFeedback></View> : <View style={styles.buttonsArea}>
                            <TouchableNativeFeedback onPress={this.onCancel}>
                                <View style={[styles.buttons,{backgroundColor:'#DB4C40'}]}>
                                    <Text style={[styles.buttonText]}>残忍拒绝</Text>
                                </View>
                            </TouchableNativeFeedback>
                            <TouchableNativeFeedback onPress={this.update}>
                                <View style={[styles.buttons,{backgroundColor:this.props.theme.themeColor}]}>
                                    <Text style={styles.buttonText}>立即更新</Text>
                                </View>
                            </TouchableNativeFeedback>
                        </View>}
                    </View> : <View style={styles.contentArea}>
                        <Text style={[styles.header,{color:this.props.theme.themeColor}]}>正在更新下载,请稍等</Text>
                        <ProgressBarAndroid
                            color={this.props.theme.themeColor}
                            indeterminate={false}
                            progress={this.state.progress}
                            styleAttr='Horizontal'
                            style={styles.progress}
                        ></ProgressBarAndroid>
                        <Text style={[styles.header,{color:this.props.theme.subColor,fontSize:18}]}>{this.state.progress * 100 + '%'}</Text>
                    </View>}
                    <Toast ref={(toast) => this.toast = toast}></Toast>
                </View>
            </Modal>
        )
    }
}
const mapStateToProps = state => {
    return {
        theme: state.themeReducer.theme,
        score: state.baseReducer.score,
    }
  }
export default connect(mapStateToProps)(HotpushModal)
const styles = StyleSheet.create({
    content:{
        flex:1,
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: 'rgba(0,0,0,0.6)'
    },
    contentArea:{
        // height:400,
        width:300,
        backgroundColor:'#fff',
        borderRadius:20,
        display:'flex',
        justifyContent:'flex-start',
        alignItems:'flex-start',
    },
    header:{
        width:300,
        textAlign:'center',
        fontSize:20,
        marginTop:20,
        marginBottom:20,
    },
    updateDes:{
        width:300,
        paddingLeft:30,
        paddingRight:30,
    },
    title:{
        fontSize:18,
        marginBottom:10,
    },
    description:{
        fontSize:16
    },
    buttonArea:{
        width:300,
        display:'flex',
        justifyContent:'center',
        alignItems:'center'
    },
    button:{
        width:150,
        height:40,
        borderRadius:20,
        marginTop:20,
        marginBottom:10,
    },
    buttonText:{
        lineHeight:40,
        color:'#fff',
        fontSize:18,
        textAlign:'center',
    },
    buttonsArea:{
        width:300,
        display:'flex',
        flexDirection:'row',
        justifyContent:'space-around',
        alignItems:'center',
        paddingLeft:10,
        paddingRight:10,
    },
    buttons:{
        width:105,
        height:40,
        borderRadius:20,
        marginTop:20,
        marginBottom:10,
    },
    progress:{
        height:20,
        width:200,
        marginLeft:50,
        marginRight:50,
        borderRadius:10,
    }
});

本示例是个人项目中的代码,请您根据自身情况进行修改.

你可能感兴趣的:(react-native)