CodePush 熱更新之自定義更新彈框及下載進度

code-push的提示面板可能不符合產品設計的要求,這時就需要我們去自定義一個更新提示框。下面是一個簡單的例子:
參考示例
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,
    }
});

本示例是個人項目中的代碼,請您根據自身情況進行修改.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章