react-native-code-push基础篇

CodePush是什么?

CodePush是一个微软开发的云服务器。通过它,开发者可以直接在用户的设备上部署手机应用更新。CodePush相当于一个中心仓库,开发者可以推送当前的更新(包括JS/HTML/CSS/IMAGE等)到CoduPush,然后应用将会查询是否有更新,本篇主要介绍使用微软云服务器

当然,我们也可以自己搭建code-push-server服务器,详情可以查看本地热更新服务器搭建。

实现环境

1.react-native:0.62.2

2.react-native-code-push:^6.2.0

首先介绍下Code Push相关命令

/**前言——code-pus相关指令**/
  /**注册登录相关指令**/
    code-push login    /*进行身份验证以开始管理您的应用*/
    code-push logout    /*注销当前会话*/
    code-push access-key     /*查看和管理与您的帐户关联的访问密钥*/
    code-push access-key ls    /*列出登陆的token*/
    code-push access-key rm  删除某个 access-key
  /**管理App相关指令**/
    code-push app    /*查看和管理您的CodePush应用*/
    code-push app add 在账号里面添加一个新的app
    code-push app remove 或者 rm 在账号里移除一个app
    code-push app rename 重命名一个存在app
    code-push app list 或则 ls 列出账号下面的所有app
    code-push app transfer 把app的所有权转移到另外一个账号
  /**查看deployment key**/
    code-push deployment    /*查看和管理您的应用程序部署*/
    code-push deployment ls Appname -k    /*查看deployment key*/
  /**其他**/
    code-push collaborator    /*查看和管理应用协作者*/
    code-push debug    /*查看正在运行的应用程序的CodePush调试日志*/
    code-push link    /*将其他身份验证提供程序(例如GitHub)链接到现有的Mobile Center帐户*/
    code-push patch    /*更新现有版本的元数据*/
    code-push promote    /*将最新版本从一种应用程序部署升级到另一种*/
    code-push register    /*注册一个新的Mobile Center帐户*/
    code-push release    /*发布更新到应用程序部署*/
    code-push release-cordova    /*将Cordova更新发布到应用程序部署*/
    code-push release-react    /*将React Native更新发布到应用程序部署*/
    code-push rollback    /*回滚最新版本的应用程序部署*/

接入流程

1.安装 CodePush CLI
2.注册 CodePush账号
3.在CodePush服务器注册App
4.React Native(JavaScript)端集成CodePush
5.原生应用中配置CodePush
6.发布更新的版本

react-native-code-push Demo示例:

首先新建一个React Native项目CodePushDemo

1.CodePush CLI

使用CodePush之前,需要先安装CodePush命令行工具,并注册CodePush账号和应用,安装命令如下:
注意:这个CodePush指令只需要全局安装一次即可,如果第一次安装成功了,那后面就不在需要安装。

npm install -g code-push-cli
安装code-push-cli.png

安装完成后可以通过code-push -v命令进行验证


查看code-push -v.png

2.注册 CodePush账号

注册CodePush账号也很简单,同样是只需简单的执行下面的命令,同样这个注册操作也是全局只需要注册一次即可

code-push register

注意:当执行完上面的命令后,会自动打开一个授权网页,让你选择使用哪种方式进行授权登录,这里我们统一就选择使用GitHub即可

登录页面.png

当注册登录成功后,CodePush会给我们一个key:

获取CodePus Key.png

我们直接复制这个key,然后在终端中将这个key填写进去即可,填写key登录成功显示效果如下


登录成功.png

3.在CodePush服务器注册App

为了使CodePush服务器有我们的App,我们需要CodePush注册App,输入下面命令即可完成注册:
注意:如果我们的应用分为iOS和Android两个平台,这时我们需要分别注册两套key:

 code-push app add 应用平台命名 平台名称 使用的框架/语言
注册Android平台应用
code-push app add CodePushDemo-Android android react-native
注册安卓平台.png
注册iOS平台应用ios
code-push app add CodePushDemo-iOS ios react-native
注册苹果平台.png

应用添加成功后就会返回对应的 ProductionStaging 两个key。
Production 代表生产版的热更新部署,
Staging 代表开发版的热更新部署,
在ios中将staging的部署key复制在info.plist的CodePushDeploymentKey值中,
在android中复制在Application的getPackages的CodePush中

我们可以输入如下命令来查看我们刚刚添加的App
code-push app list
查看App列表.png

4.React Native(JavaScript)端集成CodePush

安装组件
npm install react-native-code-push --save
安装组件.png

添加原生依赖,这里添加依赖我们使用自动添加依赖的方式

react-native link react-native-code-push
添加原生依赖.png

JS逻辑代码实现

主页面(App.js)
/**
 * 热更新主页面
 * Created by 技术渣渣 on 2020/4/26
 **/
import React, {Component} from 'react'
import {View, Text, StyleSheet, TouchableOpacity} from 'react-native'
import CodePush from 'react-native-code-push';
import ProgressBarModal from './ProgressBarModal';
class App extends Component{
    constructor(props) {
        super(props);
        this.state={
            progressModalVisible:false
        }
    }
    componentDidMount() {
        this.syncImmediate(); //开始检查更新
    }
    syncImmediate() {
        CodePush.sync(
            {
                installMode: CodePush.InstallMode.IMMEDIATE,
                updateDialog: {
                    appendReleaseDescription: true, //是否显示更新description,默认为false
                    descriptionPrefix: '更新内容:', //更新说明的前缀。 默认是” Description:
                    mandatoryContinueButtonLabel: '立即更新', //强制更新的按钮文字,默认为continue
                    mandatoryUpdateMessage: '', //- 强制更新时,更新通知. Defaults to “An update is available that must be installed.”.
                    optionalIgnoreButtonLabel: '稍后', //非强制更新时,取消按钮文字,默认是ignore
                    optionalInstallButtonLabel: '后台更新', //非强制更新时,确认文字. Defaults to “Install”
                    optionalUpdateMessage: '有新版本了,是否更新?', //非强制更新时,更新通知. Defaults to “An update is available. Would you like to install it?”.
                    title: '更新提示', //要显示的更新通知的标题. Defaults to “Update available”.
                },
            },
            this.codePushStatusDidChange.bind(this),
            this.codePushDownloadDidProgress.bind(this),
        );
    }

    codePushStatusDidChange(syncStatus) {
        switch (syncStatus) {
            case CodePush.SyncStatus.CHECKING_FOR_UPDATE:
                this.setState({syncMessage: 'Checking for update.'});
                break;
            case CodePush.SyncStatus.DOWNLOADING_PACKAGE:
                this.setState({syncMessage: 'Downloading package.',progressModalVisible:true});
                break;
            case CodePush.SyncStatus.AWAITING_USER_ACTION:
                this.setState({syncMessage: 'Awaiting user action.'});
                break;
            case CodePush.SyncStatus.INSTALLING_UPDATE:
                this.setState({syncMessage: 'Installing update.',progressModalVisible:true});
                break;
            case CodePush.SyncStatus.UP_TO_DATE:
                this.setState({syncMessage: 'App up to date.', progress: false});
                break;
            case CodePush.SyncStatus.UPDATE_IGNORED:
                this.setState({syncMessage: 'Update cancelled by user.', progress: false,});
                break;
            case CodePush.SyncStatus.UPDATE_INSTALLED:
                this.setState({syncMessage: 'Update installed and will be applied on restart.', progress: false,});
                break;
            case CodePush.SyncStatus.UNKNOWN_ERROR:
                this.setState({syncMessage: 'An unknown error occurred.', progress: false,});
                break;
        }
    }

    codePushDownloadDidProgress(progress) {
        this.setState({progress});
    }


    render(){
        let progressView;
        if (this.state.progress) {
            let total = (this.state.progress.totalBytes/(1024*1024)).toFixed(2);
            let received =(this.state.progress.receivedBytes/(1024*1024)).toFixed(2);
            let progress = parseInt((received/total)*100);
                  progressView = (
                      
                  );
        }
        return(
            
                欢迎使用热更新!
                版本1
                
                    Press for dialog-driven sync
                
                { progressView }
            
        )
    }
}

const styles = StyleSheet.create({
    container:{
        flex: 1,
        alignItems: 'center',
        backgroundColor: '#F5FCFF',
        paddingTop: 50,
    },
    welcome:{
        fontSize: 20,
        textAlign: 'center',
        margin: 20,
    },
    syncButton: {
        color: 'green',
        fontSize: 17,
    },
})

let codePushOptions = {checkFrequency: CodePush.CheckFrequency.MANUAL};

App = CodePush(codePushOptions)(App);

export default App;

进度条页面1(ProgressBarModal.js)
import React, { PureComponent } from 'react';
import {
    View,
    Modal,
    Text,
    ImageBackground,
    StyleSheet
} from 'react-native';
import Bar from './Bar';
import scale from './scale';

const propTypes = {
    ...Modal.propTypes,
};

const defaultProps = {
    animationType: 'none',
    transparent: true,
    progressModalVisible: false,
    onRequestClose: () => {},
};

/* 更新进度条Modal */
class ProgressBarModal extends PureComponent {

    constructor(props) {
        super(props);
        this.state = {
            title: '正在下载更新文件' // 更新提示标题
        };
    }

    render() {
        const {
            animationType,
            transparent,
            onRequestClose,
            progress,
            progressModalVisible,
            totalPackageSize,
            receivedPackageSize,
        } = this.props;
        return (
            
                
                    
                        
                            {this.state.title}
                        
                    
                    
                        
                        
                            {`${receivedPackageSize}M/${totalPackageSize}M`}
                        
                        *温馨提示:下载完更新文件,应用会重启
                    
                    
                
            
        );
    }
}

ProgressBarModal.propTypes = propTypes;
ProgressBarModal.defaultProps = defaultProps;

export default ProgressBarModal;

const styles = StyleSheet.create({
    imageBg: {
        width: scale(600),
        height: scale(100),
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor:'#1083E6',
        borderTopLeftRadius:scale(26),
        borderTopRightRadius:scale(26),
    },
    progressBarView: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: 'rgba(0,0,0,0.2)'
    },
    // 默认进度条背景底色
    barBackgroundStyle: {
        backgroundColor: '#e0e0e0'
    },
    subView: {
        width: scale(600),
        height: scale(296),
        backgroundColor: '#fff',
        alignItems: 'center',
        justifyContent: 'center'
    },
    bottomContainer: {
        width: scale(600),
        height: scale(39),
        borderBottomLeftRadius: scale(26),
        borderBottomRightRadius: scale(26),
        backgroundColor: '#FFF'
    },
    textPackageSize: {
        fontSize: scale(40),
        color: '#686868',
        marginTop: scale(36)
    },
    title: {
        color: '#FFF',
        fontSize: scale(30)
    }
})
进度条页面1(Bar.js)
import React, { PureComponent } from 'react';
import {
    View,
    Animated,
} from 'react-native';
import PropTypes from 'prop-types';
import scale from './scale';


const propTypes = {
    progress: PropTypes.number.isRequired,
    backgroundStyle: PropTypes.number.isRequired,
    style: PropTypes.object.isRequired,
};

/* 进度条组件 */
class Bar extends PureComponent {

    constructor(props) {
        super(props);
        this.progress = new Animated.Value(0);
        this.update = this.update.bind(this);
    }

    componentWillReceiveProps(nextProps) {
        if (this.props.progress >= 0 && this.props.progress !== nextProps.progress) {
            this.update(nextProps.progress);
        }
    }

    update(progress) {
        Animated.spring(this.progress, {
            toValue: progress
        }).start();
    }

    render() {
        return (
            
                
            
        );
    }
}

Bar.propTypes = propTypes;

export default Bar;

5.原生应用中配置CodePush

配置Android平台

1.编辑 android/app/build.gradle,新增依赖:

  dependencies {
       implementation project(':react-native-code-push')
  }

2.编辑 android/app/build.gradle,修改buildTypes,输入对应的Key:

  buildTypes {
        debug {
            signingConfig signingConfigs.debug
            resValue "string", "CodePushDeploymentKey", ""
        }
        release {
            // Caution! In production, you need to generate your own keystore file.
            // see https://facebook.github.io/react-native/docs/signed-apk-android.
            signingConfig signingConfigs.release
            minifyEnabled enableProguardInReleaseBuilds
            proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
            //这里的key为Productionde的key
            resValue "string", "CodePushDeploymentKey", "9DSt0TxqNuDlsX1SneATSr8qrrkJ4ksvOXqog"
        }
        releaseStaging {
            //这里的key为Staging的key
            resValue "string", "CodePushDeploymentKey", "HmTsl4j3G7qlMtxmIG93beLpF6ns4ksvOXqog"
            matchingFallbacks = ['release']
        }
    }

3.编辑 android/app/build.gradle,修改defaultConfig,将versionName 修改为三位(1.0.0):

    defaultConfig {
        applicationId "com.codepushdemo"
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
        versionCode 1
        versionName "1.0.0"
    }

6.发布更新的版本

使用前应考虑到应用检查更新的时机,是否要求强制更新,是否要求即时更新等问题。

常见的更新时机有两种

1.打开App就自动检查更新

可以在页面的componentDidMount生命周期方法里通过codePush.sync来触发检查更新,如果有更新包可供下载则会在重启后生效。
注意:这种下载和安装都是静默的,即用户不可见,本文额外增加了更新下载进度条的 modal

codePush.sync({
         installMode: CodePush.InstallMode.IMMEDIATE,
         updateDialog: {
              appendReleaseDescription: true, //是否显示更新description,默认为false
              descriptionPrefix: '更新内容:', //更新说明的前缀。 默认是” Description:
              mandatoryContinueButtonLabel: '立即更新', //强制更新的按钮文字,默认为continue
              mandatoryUpdateMessage: '', //- 强制更新时,更新通知. Defaults to “An update is available that must be installed.”.
              optionalIgnoreButtonLabel: '稍后', //非强制更新时,取消按钮文字,默认是ignore
              optionalInstallButtonLabel: '后台更新', //非强制更新时,确认文字. Defaults to “Install”
              optionalUpdateMessage: '有新版本了,是否更新?', //非强制更新时,更新通知. Defaults to “An update is available. Would you like to install it?”.
              title: '更新提示', //要显示的更新通知的标题. Defaults to “Update available”.
                },
  }),
2.用户点击检查更新并安装

在用户点击按钮触发事件时,在调用上面的codePush.sync方法,在有更新时弹出提示框让用户选择是否更新,如果用户点击立即更新按钮,则会进行安装包的下载,下载完成后会立即重启并生效。

常见的更新时机有两种

如果是强制更新需要在发布的时候指定,发布命令中配置--m true

更新是否要求即时

在更新配置中通过指定installMode来决定安装完成的重启时机,亦即更新生效时机
1.codePush.InstallMode.IMMEDIATE :安装完成立即重启更新
2.codePush.InstallMode.ON_NEXT_RESTART :安装完成后会在下次重启后进行更新
3.codePush.InstallMode.ON_NEXT_RESUME :安装完成后会在应用进入后台后重启更新

如何发布CodePush更新包

本文介绍楼主使用过的发布方式,通过以下指令发布更新包:

code-push release-react   --t <本更新包面向的旧版本号> --dev false --d Production --des <本次更新说明> --m true

注意:CodePush默认是更新Staging 环境的,如果发布生产环境的更新包,需要指定--d参数:--d Production,如果发布的是强制更新包,需要加上 --m true强制更新

你可能感兴趣的:(react-native-code-push基础篇)