React Native 如何用 Pushy 進(jìn)行熱更新

一汁掠、準(zhǔn)備工作


此篇文章假設(shè)我們已經(jīng)滿足以上所有需要的準(zhǔn)備工作。

記得【關(guān)注】倔喂,感謝支持恢氯,后期還會有更新。

先上一張 gif 圖 ~~ 看看效果阱州。

hotupdate.gif

二挑秉、安裝

  • 在你項目的根目錄運行以下指令
// 這一句指令在每一臺電腦上只需要運行一次即可
$ npm install -g react-native-update-cli rnpm
  • 如果你訪問速度過慢或者網(wǎng)絡(luò)鏈接失敗,請將鏡像源換成國內(nèi)鏡像源
$ npm config set registry https://registry.npm.taobao.org --global
$ npm config set disturl https://npm.taobao.rog/dist --global
  • 安裝保存指定的版本到你的項目
$ npm install --save react-native-update@具體版本號參看下面表格;
// 例如
$ npm install --save react-native-update@4.x
屏幕快照 2016-12-08 下午6.07.09.png
  • 鏈接
    • 使用指令鏈接
$ react-native link react-native-update;
// 如果你安裝的 React Native 版本低于 0.29 則使用下面這條指令
$ npm link react-native-update

這時候如果 react-native link 成功的話 ( iOS 跟 Android 的工程中能夠看到依賴 )
如果指令鏈接失敗使用下面的手動鏈接

  • 使用手動鏈接
    • iOS
      1. 在 Xcode 中打開你的項目苔货,選擇項目然后右鍵 Show in Finder 進(jìn)入項目的目錄
      2. 找到你的 React Native 安裝的位置犀概,也就是 node_modules 文件夾
      3. 進(jìn)入 node_modules 文件夾找到 react-native-update 里面的 ios 文件夾
      4. 將 RCTHotUpdate.xcodeproj 拖入到你的 Xcode 中
      5. 在項目的 Build Phases 找到 Link Binary With Libraries 點擊下方的 + 加號
      6. 依次添加 RCTHotUpdate.alibz.tbd 還有 libbz2.1.0.tbd
      7. 完成后使用 Command + B 編譯之后不報錯就是 OK 啦
        Snip20161208_38.png
    • Android
      1. 在 android/settings.gradle 中添加如下代碼:
include ':react-native-update' project(':react-native-update').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-update/android')
  2. android/app/build.gradle 的 dependencies 部分增加如下代碼:
compile project(':react-native-update')
  3. 檢查你的RN版本
// 如果是 0.29 及以上打開
android/app/src/main/java/[...]/MainApplication.java
// 否則打開
android/app/src/main/java/[...]/MainActivity.java
  4. 在文件開頭增加
import cn.reactnative.modules.update.UpdatePackage;
在 getPackages() 方法中增加 new UpdatePackage() (注意ES5的話,函數(shù)與函數(shù)之間要加一個逗號)

三夜惭、配置Bundle URL 在源生代碼中使用

  • iOS

一般情況下是在 appDelegate 中 新增如下代碼

#import "RCTHotUpdate.h"
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        NSURL *jsCodeLocation;
#if DEBUG
        jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios&dev=true"];
#else 
        jsCodeLocation=[RCTHotUpdate bundleURL];
#endif 
        // ... 其它代碼
}
  • Android

項目中 React Native 版本 >= 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);
    } 
    // ... 其它代碼 
 }
}

項目中 React Native 版本 <= 0.28 在你的 MainActivity 中增加如下代碼:

// ... 其它代碼
import cn.reactnative.modules.update.UpdateContext;
public class MainActivity extends ReactActivity { 
      @Override protected String getJSBundleFile() {
         return UpdateContext.getBundleUrl(this);
      } 
      // ... 其它代碼
}

四姻灶、登錄與創(chuàng)建應(yīng)用登錄

  • 注冊賬號

http://update.reactnative.cn 注冊賬號

  • 登錄

在你項目的根目錄執(zhí)行登錄指令

$ pushy login
email: <你注冊時的郵箱>
password: <你的登錄密碼>
登錄.png

登錄成功之后會在你項目的根目錄創(chuàng)建一個 .update 文件

Snip20161209_8.png

需要注意的是不要把這個文件上傳到 Git 中
你可以在 .gitignore 的末尾增加一行 .update 來忽略這個文件

Paste_Image.png
  • 創(chuàng)建應(yīng)用
$ pushy createApp --platform ios
App Name: <輸入你應(yīng)用的名字>
$ pushy createApp --platform android
App Name: <輸入你應(yīng)用的名字>

兩次輸入的名字可以相同,不會有任何影響

  • 如果你在網(wǎng)頁端或者其他地方創(chuàng)建過應(yīng)用诈茧,可以直接選擇應(yīng)用
$ pushy selectApp --plateform ios
1) 微信
2) 微博
Total 2 ios apps
Enter appId: <輸入應(yīng)用前面的編號>
Paste_Image.png

選擇完應(yīng)用之后木蹬,會在你的根目錄創(chuàng)建一個 update.json 的文件,其內(nèi)容類似如下形式

{
      "ios": {
          "appId" : "1",
          "appKey" : "一串隨機(jī)字符串",
      },
      "android": {
          "appId" : "2",
          "appKey" : "一串隨機(jī)字符串",
      },
}

你可以把 update.json 文件上傳到 Git 服務(wù)器若皱,與你的團(tuán)隊共享這個文件
當(dāng)然他們在使用任何功能時,都必須使用 pushy loagin 進(jìn)行登錄

五尘颓、使用熱更新功能

  • 獲取 appKey
    檢查更新時必須提供你的 appKey 這個值保存在你項目的 update.json 文件中走触,并且根據(jù)平臺不同而不同
    你可以使用如下代碼進(jìn)行獲取相對應(yīng)的 appKey
import {
    Platform,
} from 'react-native';
//-------------------------   華麗的分割線   -------------------------
import _updateConfig from './update.json';
const {appKey} = _updateConfig[Platform.OS];
  • 檢查更新、下載更新
    • 異步函數(shù) checkUpdate 可以檢查當(dāng)前版本是否需要更新
checkUpdate(appKey)
    .then(info =>{
        // 返回的 info 有三種情況
        // 1. {upToDate: true} 當(dāng)前已經(jīng)更新到最新版本疤苹,無需進(jìn)行更新
        // 2. {expired: true} 該應(yīng)用包(源生應(yīng)用)已經(jīng)過期互广,需要前往應(yīng)用市場下載新版
        // 3. {update: true} 當(dāng)前有新版本可以更新
    })

info 的 name description 字段可 以用于提示用戶
metaInfo 字段則可以根據(jù)你的需求自定義其它屬性(如是否靜默更新、 是否強(qiáng)制更新等等)卧土。
另外還有幾個字段惫皱,包含了完整更新包或補(bǔ)丁包的下載地址 ,
react-native-update 會首先嘗試耗費流量更少的更新方式尤莺。
將 info 對象傳遞給 downloadUpdate 作為參數(shù)即可旅敷。

  • 切換版本

downloadUpdate 的返回值是一個hash字符串,它是當(dāng)前版本的唯一標(biāo)識颤霎。
你可以使用 switchVersion 函數(shù)立即切換版本(此時應(yīng)用會立即重新加載)媳谁,
或者選擇調(diào)用 switchVersionLater 讓應(yīng)用在下一次啟動的時候再加載新的版本。

  • 首次啟動友酱、回滾

在每次更新完畢后的首次啟動時 isFirstTime 常量會為 true 你必須在應(yīng)用退出前的任何時機(jī)晴音,
調(diào)用 markSuccess 否則應(yīng)用下一次啟動的時候?qū)M(jìn)行回滾操作。
這一機(jī)制稱作 “反觸發(fā)”缔杉,這樣當(dāng)你應(yīng)用啟動初期即遭遇問題的時候锤躁,也能在下一次啟動時恢復(fù)運作。
你可以通過 isFirstTime 來獲知這是當(dāng)前版本的首次啟動或详,
也可以通過 isRolledBack 來獲知應(yīng)用剛剛經(jīng)歷了一次回滾操作系羞。 你可以在此時給予用戶合理的提示郭计。

  • 完整的示例

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 ChiefSteward extends Component {
    componentWillMount(){
        if (isFirstTime) {
            Alert.alert('提示', '這是當(dāng)前版本第一次啟動,是否要模擬啟動失敗?失敗將回滾到上一版本', [
                {text: '是', onPress: ()=>{throw new Error('模擬啟動失敗,請重啟應(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: '下次啟動時', 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}>
                        點擊這里檢查更新
                    </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('ChiefSteward', () => ChiefSteward);

現(xiàn)在,你的應(yīng)用已經(jīng)可以通過 update 服務(wù)檢查版本并進(jìn)行更新了觉啊。

六拣宏、發(fā)布與更新

  • 首先需要注意的是

update 上傳發(fā)布版本到發(fā)布版本正式上線期間,不要修改任何腳本和資源杠人,
這會影響 update 獲取本地代碼勋乾,從而導(dǎo)致版本不能更新。
如果在發(fā)布之前修改了腳本或資源嗡善,請在網(wǎng)頁端刪除之前上傳的版本并重新上傳辑莫。

  • 發(fā)布 iOS 應(yīng)用
    • 首先確保你的應(yīng)用 release 模式下 能夠在真機(jī)上使用離線包。如果不能下面的文章也許是你需要的
      React Native 在 release 模式真機(jī)運行

    • 然后點擊菜單罩引,按照正常的發(fā)布流程打一個 ipa 的包
      在 Xcode 中運行設(shè)備選 真機(jī)Generic iOS Device 然后菜單中選擇 Product-Archive
      然后在你項目的根目錄運行如下命令

$ pushy uploadIpa <把你打的 ipa 的包拖拽到這里>

此處上傳 ipa 是為了后續(xù)版本對比之用各吨。

  • 發(fā)布 Android 應(yīng)用

首先參考 文檔-生成已簽名的APK 設(shè)置簽名,然后在 android 文件夾下運行
./gradlew assembleRelease 你就可以在 android/app/build/outputs/apk/app-release.apk 中找到你的應(yīng)用包袁铐。
然后運行如下命令

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

即可上傳 apk 以供后續(xù)版本比對之用揭蜒。
隨后你可以選擇往應(yīng)用市場發(fā)布這個版本,也可以先往設(shè)備上直接安裝這個 apk 文件以進(jìn)行測試剔桨。

  • 發(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ā)布洒缀,此時輸入 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)

此時版本已經(jīng)提交到 update 服務(wù)器萨脑,但用戶暫時看不到此更新。
你需要先將特定的 應(yīng)用包的版本 與此 熱更新版本包 進(jìn)行綁定饺饭。
此時輸入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ù)字>

來一張完成的圖


Paste_Image.png

版本綁定完畢后仅淑,客戶端就應(yīng)當(dāng)可以檢查到更新并進(jìn)行更新了。
恭喜你胸哥,至此為止涯竟,你已經(jīng)完成了植入代碼熱更新的全部工作。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市庐船,隨后出現(xiàn)的幾起案子银酬,更是在濱河造成了極大的恐慌,老刑警劉巖筐钟,帶你破解...
    沈念sama閱讀 218,640評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件揩瞪,死亡現(xiàn)場離奇詭異,居然都是意外死亡篓冲,警方通過查閱死者的電腦和手機(jī)李破,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來壹将,“玉大人嗤攻,你說我怎么就攤上這事》谈” “怎么了妇菱?”我有些...
    開封第一講書人閱讀 165,011評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長暴区。 經(jīng)常有香客問我闯团,道長,這世上最難降的妖魔是什么仙粱? 我笑而不...
    開封第一講書人閱讀 58,755評論 1 294
  • 正文 為了忘掉前任偷俭,我火速辦了婚禮,結(jié)果婚禮上缰盏,老公的妹妹穿的比我還像新娘。我一直安慰自己淹遵,他們只是感情好口猜,可當(dāng)我...
    茶點故事閱讀 67,774評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著透揣,像睡著了一般济炎。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上辐真,一...
    開封第一講書人閱讀 51,610評論 1 305
  • 那天须尚,我揣著相機(jī)與錄音,去河邊找鬼侍咱。 笑死耐床,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的楔脯。 我是一名探鬼主播撩轰,決...
    沈念sama閱讀 40,352評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了堪嫂?” 一聲冷哼從身側(cè)響起偎箫,我...
    開封第一講書人閱讀 39,257評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎皆串,沒想到半個月后淹办,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,717評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡恶复,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,894評論 3 336
  • 正文 我和宋清朗相戀三年怜森,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片寂玲。...
    茶點故事閱讀 40,021評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡塔插,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出拓哟,到底是詐尸還是另有隱情想许,我是刑警寧澤,帶...
    沈念sama閱讀 35,735評論 5 346
  • 正文 年R本政府宣布断序,位于F島的核電站流纹,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏违诗。R本人自食惡果不足惜漱凝,卻給世界環(huán)境...
    茶點故事閱讀 41,354評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望诸迟。 院中可真熱鬧茸炒,春花似錦、人聲如沸阵苇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽绅项。三九已至紊册,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間快耿,已是汗流浹背囊陡。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留掀亥,地道東北人撞反。 一個月前我還...
    沈念sama閱讀 48,224評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像搪花,于是被迫代替她去往敵國和親痢畜。 傳聞我的和親對象是個殘疾皇子垛膝,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,974評論 2 355

推薦閱讀更多精彩內(nèi)容