React Native 熱更新

我們假設(shè)您已經(jīng)能夠配置 React Native 開發(fā)環(huán)境,并且已經(jīng)開發(fā)完畢您的 App 了。
接下來,我們來配置熱更新,讓發(fā)布變得更簡單吧。

一、安裝 Android NDK

1、首先需要下載 NDK 包,

下載地址為:地址1 地址2

2、設(shè)置環(huán)境變量 ANDROID_NDK_HOME

$ vi ~/.bash_profile

ANDROID_NDK_HOME=/你的NDK地址
export PATH=${PATH}:其他變量:${ANDROID_NDK_HOME}

$ source ~/.bash_profile
二、打包簽名App

1、生成簽名文件

keytool -genkey -v -keystore my-release-key.keystore -alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000

此命令使用 keytool 生成一個(gè)簽名文件 my-release-key.keystore,使用此簽名文件 my-release-key.keystore 對已經(jīng)開發(fā)完畢的 App 簽名,生成 release 包進(jìn)行發(fā)布。

2、設(shè)置gradle變量
把my-release-key.keystore文件放到你工程中的android/app文件夾下。
編輯~/.gradle/gradle.properties(沒有這個(gè)文件你就創(chuàng)建一個(gè)),添加如下的代碼(注意把其中的****替換為相應(yīng)密碼)

MYAPP_RELEASE_STORE_FILE=my-release-key.keystore
MYAPP_RELEASE_KEY_ALIAS=my-key-alias
MYAPP_RELEASE_STORE_PASSWORD=*****
MYAPP_RELEASE_KEY_PASSWORD=*****

3、添加簽名到項(xiàng)目的gradle配置文件

編輯你項(xiàng)目目錄下的android/app/build.gradle,添加如下的簽名配置:

...
android {
    ...
    defaultConfig { ... }
    signingConfigs {
        release {
            storeFile file(MYAPP_RELEASE_STORE_FILE)
            storePassword MYAPP_RELEASE_STORE_PASSWORD
            keyAlias MYAPP_RELEASE_KEY_ALIAS
            keyPassword MYAPP_RELEASE_KEY_PASSWORD
        }
    }
    buildTypes {
        release {
            ...
            signingConfig signingConfigs.release
        }
    }
}
...

4、添加 NDK

修改 項(xiàng)目根目錄/android 下的 local.properties 文件(如果沒有,可以新建此文件)

sdk.dir=/你的 Android SDK 路徑
ndk.dir=/你的 Android NDK 路徑

5、生成發(fā)行App包

項(xiàng)目根目錄下運(yùn)行如下命令:

$ cd android && ./gradlew assembleRelease
三、安裝熱更新

1、在你的項(xiàng)目根目錄下運(yùn)行以下命令:

npm install -g react-native-update-cli rnpm
npm install --save react-native-update@具體版本請看下面的表格
react-native link react-native-update

npm install -g react-native-update-cli rnpm 這一句在每一臺電腦上僅需運(yùn)行一次。
如果RN版本低于0.29,請使用rnpm link代替react-native link命令。
因?yàn)镽eact Native不同版本代碼結(jié)構(gòu)不同,因而請按下面表格對號入座:

React Native版本 react-native-update版本
<= 0.26 1.0.x
0.27 - 0.28 2.x
0.29 - 0.33 3.x
0.34 - 0.39 4.x
0.40 暫不支持

2、配置Bundle URL(Android)

0.29及以后版本:在你的MainApplication中增加如下代碼:

// ... 其它代碼

import cn.reactnative.modules.update.UpdateContext;
public class MainApplication extends Application implements ReactApplication {

  private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
    @Override
    protected String getJSBundleFile() {
        return UpdateContext.getBundleUrl(MainApplication.this);
    }
    // ... 其它代碼
  }
}

0.28及以前版本:在你的MainActivity中增加如下代碼:

// ... 其它代碼

import cn.reactnative.modules.update.UpdateContext;

public class MainActivity extends ReactActivity {

    @Override
    protected String getJSBundleFile() {
        return UpdateContext.getBundleUrl(this);
    }
    // ... 其它代碼
}
四、創(chuàng)建pushy帳號

1、注冊帳號

打開 http://update.reactnative.cn/
創(chuàng)建帳號,帳號為 YOUR_ACCOUNT, 密碼為 YOUR_PWD(記住,后期會(huì)用到)
并創(chuàng)建一個(gè)應(yīng)用,如下圖1,填入應(yīng)用名稱,選擇一個(gè)平臺。創(chuàng)建后,如下圖2。

圖1
圖2

2、命令行中,項(xiàng)目根目錄下運(yùn)行以下命令:

$ pushy login
email: <`YOUR_ACCOUNT`>
password: <`YOUR_PWD`>

3、創(chuàng)建或選擇 App

登錄之后可以創(chuàng)建應(yīng)用。注意iOS平臺和安卓平臺需要分別創(chuàng)建:(如果已經(jīng)創(chuàng)建則可以跳過)

$ pushy createApp --platform ios
App Name: <輸入應(yīng)用名字>
$ pushy createApp --platform android
App Name: <輸入應(yīng)用名字>

兩次輸入的名字可以相同,這沒有關(guān)系。
如果你已經(jīng)在網(wǎng)頁端或者其它地方創(chuàng)建過應(yīng)用,也可以直接選擇應(yīng)用:

$ pushy selectApp --platform ios|android
1) AppName1(android)
3) AppName2(ios)

Total 2 ios apps
Enter appId: <輸入應(yīng)用前面的編號> 
五、App添加熱更新功能

修改項(xiàng)目根目錄下的 index.android.js,下面為完整的示例:

import React, {
  Component,
} from 'react';

import {
  AppRegistry,
  StyleSheet,
  Platform,
  Text,
  View,
  Alert,
  TouchableOpacity,
  Linking,
} from 'react-native';

import {
  isFirstTime,
  isRolledBack,
  packageVersion,
  currentVersion,
  checkUpdate,
  downloadUpdate,
  switchVersion,
  switchVersionLater,
  markSuccess,
} from 'react-native-update';

import _updateConfig from './update.json';
const {appKey} = _updateConfig[Platform.OS];

class MyProject extends Component {
  componentWillMount(){
    if (isFirstTime) {
      Alert.alert('提示', '這是當(dāng)前版本第一次啟動(dòng),是否要模擬啟動(dòng)失敗?失敗將回滾到上一版本', [
        {text: '是', onPress: ()=>{throw new Error('模擬啟動(dòng)失敗,請重啟應(yīng)用')}},
        {text: '否', onPress: ()=>{markSuccess()}},
      ]);
    } else if (isRolledBack) {
      Alert.alert('提示', '剛剛更新失敗了,版本被回滾.');
    }
  }
  doUpdate = info => {
    downloadUpdate(info).then(hash => {
      Alert.alert('提示', '下載完畢,是否重啟應(yīng)用?', [
        {text: '是', onPress: ()=>{switchVersion(hash);}},
        {text: '否',},
        {text: '下次啟動(dòng)時(shí)', onPress: ()=>{switchVersionLater(hash);}},
      ]);
    }).catch(err => { 
      Alert.alert('提示', '更新失敗.');
    });
  };
  checkUpdate = () => {
    checkUpdate(appKey).then(info => {
      if (info.expired) {
        Alert.alert('提示', '您的應(yīng)用版本已更新,請前往應(yīng)用商店下載新的版本', [
          {text: '確定', onPress: ()=>{info.downloadUrl && Linking.openURL(info.downloadUrl)}},
        ]);
      } else if (info.upToDate) {
        Alert.alert('提示', '您的應(yīng)用版本已是最新.');
      } else {
        Alert.alert('提示', '檢查到新的版本'+info.name+',是否下載?\n'+ info.description, [
          {text: '是', onPress: ()=>{this.doUpdate(info)}},
          {text: '否',},
        ]);
      }
    }).catch(err => { 
      Alert.alert('提示', '更新失敗.');
    });
  };
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          歡迎使用熱更新服務(wù)
        </Text>
        <Text style={styles.instructions}>
          這是版本一 {'\n'}
          當(dāng)前包版本號: {packageVersion}{'\n'}
          當(dāng)前版本Hash: {currentVersion||'(空)'}{'\n'}
        </Text>
        <TouchableOpacity onPress={this.checkUpdate}>
          <Text style={styles.instructions}>
            點(diǎn)擊這里檢查更新
          </Text>
        </TouchableOpacity>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
});

AppRegistry.registerComponent('MyProject', () => MyProject);
六、發(fā)布帶熱更新功能的App

1、上傳 apk 到 pushy

我們先在android文件夾下運(yùn)行 ./gradlew assembleRelease,你就可以在 android/app/build/outputs/apk/app-release.apk 中找到你的應(yīng)用包。

然后運(yùn)行如下命令,即可上傳apk以供后續(xù)版本比對之用。

$ pushy uploadApk android/app/build/outputs/apk/app-release.apk

2、發(fā)布新版

我們可以嘗試修改一行代碼(譬如將版本一修改為版本二),然后生成新的熱更新版本。

$ pushy bundle --platform <ios|android>
Bundling with React Native version:  0.22.2
<各種進(jìn)度輸出>
Bundled saved to: build/output/android.1459850548545.ppk
Would you like to publish it?(Y/N) 

如果想要立即發(fā)布,此時(shí)輸入Y。當(dāng)然,你也可以在將來使用pushy publish --platform <ios|android> <ppkFile>來發(fā)布版本。

Uploading [========================================================] 100% 0.0s
Enter version name: <輸入版本名字,如1.0.0-rc>
Enter description: <輸入版本描述>
Enter meta info: {"ok":1}
Ok.
Would you like to bind packages to this version?(Y/N)

此時(shí)版本已經(jīng)提交到update服務(wù),但用戶暫時(shí)看不到此更新,你需要先將特定的包版本綁定到此熱更新版本上。

此時(shí)輸入Y立即綁定,你也可以在將來使用 pushy update --platform <ios|android> 來使得對應(yīng)包版本的用戶更新。 除此以外,你還可以在網(wǎng)頁端操作,簡單的將對應(yīng)的包版本拖到此版本下即可。

Offset 0
1) FvXnROJ1 1.0.1 (no package)
2) FiWYm9lB 1.0 [1.0]
Enter versionId or page Up/page Down/Begin(U/D/B) <輸入序號,U/D翻頁,B回到開始,序號就是上面列表中)前面的數(shù)字>

1) 1.0(normal) - 3 FiWYm9lB (未命名)

Total 1 packages.
Enter packageId: <輸入包版本序號,序號就是上面列表中)前面的數(shù)字>

版本綁定完畢后,客戶端就應(yīng)當(dāng)可以檢查到更新并進(jìn)行更新了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容