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,
}
});
本示例是個人項目中的代碼,請您根據自身情況進行修改.