技术标签: RN学习笔记
前言:无奈研究了一下CodePush,遇到了很多坑~~ 但是原理呢不是很难理解,就是配置有点多,原理可以简单的参考一下我之前的一篇博客React-Native 热更新尝试(Android),下面说一下期间遇到的坑~
大家可以看一下官网:https://github.com/Microsoft/react-native-code-push,
如果觉得自己英文不太好的话可以看一下这哥们的博客:
React Native热更新部署/热更新-CodePush最新集成总结(新)
下面带大家一步一步实现一下传说中的rn热更新:
首先我们创建一个rn项目叫UpdateDemo,然后运行android:
好啦! 很干净的一个app(不要在问我怎么创建和运行rn了)~~
开始之前小伙伴可以自己去看看CodePush做一个简单的了解,然后你需要的是一台mac电脑~
一、安装 CodePush
进入命令栏执行:
npm install -g code-push-cli
然后短暂等待一会:
二、创建一个CodePush账号, 并登入
执行在命令栏里执行:
code-push register
然后会弹出一个注册页面,我们直接github登入,登入成功后会显示你的access-key,我们直接copy一下:
然后复制到命令栏中:
可以看到,我们已经成功的登入了~~
三、在CodePush注册一个我们的app
我们在终端输入:
code-push app add <appname> android react-native
我们这里是以android为例子的~~
然后我们把Production和Staging对应的可以copy一下,后面需要用到~~
四、集成Android开发环境
1、进入到项目个根目录然后执行:
npm install --save react-native-code-push
然后短暂停留几秒~~~
2、进到android目录,然后执行:
npm i -g rnpm
3、回到项目根目录,直接命令集成android环境:
rnpm link react-native-code-push
一路回车~~~~
然后用 AndroidStudio 打开android项目,找到/xxxx/UpdateDemo/android/app/build.gradle,你会发现多了几行代码:
compile project(':react-native-code-push')
apply from: "../../node_modules/react-native-code-push/android/codepush.gradle"
这就是脚本文件为我们自动生成的,然后/xxx/UpdateDemo/android/settings.gradle这个文件也多了几行代码:
include ':react-native-code-push'
project(':react-native-code-push').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-code-push/android/app')
最后我们点击重新编译app:
点击”Sync Now”~~
然后我们试着运行app,你会发现报了一个不明的错误,我们继续找到/xxx/UpdateDemo/android/app/src/main/java/com/updatedemo/MainApplication.java文件:
package com.updatedemo;
import android.app.Application;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.SoLoader;
import com.microsoft.codepush.react.CodePush;
import java.util.Arrays;
import java.util.List;
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
protected String getJSBundleFile() {
return CodePush.getJSBundleFile();
}
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new CodePush("deployment-key-here", MainApplication.this, BuildConfig.DEBUG)
);
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
}
}
如果为了偷懒一下的话,直接把“deployment-key-here”用我们之前获取的Production的key替换就可以了
当然,我们是需要切换Production跟Staging的,所以我们得动态的配置我们的key,我们需要变成这样:
你会看到CODEPUSH_KEY变红色了,那么这个变量我们怎么配置呢?
我们找到xxxx/UpdateDemo/android/app/build.gradle文件,
改成:
buildTypes {
release {
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
buildConfigField "String", "CODEPUSH_KEY", '"Ce2Lap6BN2ZqSrf6GyQ4U2AZlvpc55514efb-34f8-4da9-8ce5-b8c65a00e283"'
}
debug {
buildConfigField "String", "CODEPUSH_KEY", '"3RCKlOnnPmFdxolA0_BKzwH85IkL55514efb-34f8-4da9-8ce5-b8c65a00e283"'
}
releaseStaging {
minifyEnabled enableProguardInReleaseBuilds
buildConfigField "String", "CODEPUSH_KEY", '"3RCKlOnnPmFdxolA0_BKzwH85IkL55514efb-34f8-4da9-8ce5-b8c65a00e283"'
}
}
里面的CODEPUSH_KEY即为我们之前获取的Deployment Key ,
release对应的Production
releaseStaging跟debug对应的Staging
然后重新编译一下as,会发现之前的地方不报红色了:
new CodePush(BuildConfig.CODEPUSH_KEY, MainApplication.this, BuildConfig.DEBUG)
package com.updatedemo;
import android.app.Application;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.SoLoader;
import com.microsoft.codepush.react.CodePush;
import java.util.Arrays;
import java.util.List;
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
protected String getJSBundleFile() {
return CodePush.getJSBundleFile();
}
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new CodePush(BuildConfig.CODEPUSH_KEY, MainApplication.this, BuildConfig.DEBUG)
);
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
}
}
然后我们顺便把我们的keystore打包key配置一下:
不懂的小伙伴可以去看我之前的一篇博客:
然后配置好keystore之后,然后我们的/xxxx/UpdateDemo/android/app/build.gradle文件就变成了这样:
apply plugin: "com.android.application"
import com.android.build.OutputFile
/**
* The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
* and bundleReleaseJsAndAssets).
* These basically call `react-native bundle` with the correct arguments during the Android build
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
* bundle directly from the development server. Below you can see all the possible configurations
* and their defaults. If you decide to add a configuration block, make sure to add it before the
* `apply from: "../../node_modules/react-native/react.gradle"` line.
*
* project.ext.react = [
* // the name of the generated asset file containing your JS bundle
* bundleAssetName: "index.android.bundle",
*
* // the entry file for bundle generation
* entryFile: "index.android.js",
*
* // whether to bundle JS and assets in debug mode
* bundleInDebug: false,
*
* // whether to bundle JS and assets in release mode
* bundleInRelease: true,
*
* // whether to bundle JS and assets in another build variant (if configured).
* // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
* // The configuration property can be in the following formats
* // 'bundleIn${productFlavor}${buildType}'
* // 'bundleIn${buildType}'
* // bundleInFreeDebug: true,
* // bundleInPaidRelease: true,
* // bundleInBeta: true,
*
* // whether to disable dev mode in custom build variants (by default only disabled in release)
* // for example: to disable dev mode in the staging build type (if configured)
* devDisabledInStaging: true,
* // The configuration property can be in the following formats
* // 'devDisabledIn${productFlavor}${buildType}'
* // 'devDisabledIn${buildType}'
*
* // the root of your project, i.e. where "package.json" lives
* root: "../../",
*
* // where to put the JS bundle asset in debug mode
* jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
*
* // where to put the JS bundle asset in release mode
* jsBundleDirRelease: "$buildDir/intermediates/assets/release",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in debug mode
* resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in release mode
* resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
*
* // by default the gradle tasks are skipped if none of the JS files or assets change; this means
* // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
* // date; if you have any other folders that you want to ignore for performance reasons (gradle
* // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
* // for example, you might want to remove it from here.
* inputExcludes: ["android/**", "ios/**"],
*
* // override which node gets called and with what additional arguments
* nodeExecutableAndArgs: ["node"],
*
* // supply additional arguments to the packager
* extraPackagerArgs: []
* ]
*/
apply from: "../../node_modules/react-native/react.gradle"
apply from: "../../node_modules/react-native-code-push/android/codepush.gradle"
/**
* Set this to true to create two separate APKs instead of one:
* - An APK that only works on ARM devices
* - An APK that only works on x86 devices
* The advantage is the size of the APK is reduced by about 4MB.
* Upload all the APKs to the Play Store and people will download
* the correct one based on the CPU architecture of their device.
*/
def enableSeparateBuildPerCPUArchitecture = false
/**
* Run Proguard to shrink the Java bytecode in release builds.
*/
def enableProguardInReleaseBuilds = false
android {
signingConfigs {
release {
keyAlias 'update'
keyPassword '123456'
storeFile file('/Users/yasin/SelfRnWorkSpace/UpdateDemo/android/update.keystore')
storePassword '123456'
}
}
compileSdkVersion 23
buildToolsVersion "23.0.1"
defaultConfig {
applicationId "com.updatedemo"
minSdkVersion 16
targetSdkVersion 22
versionCode 1
versionName "1.0"
ndk {
abiFilters "armeabi-v7a", "x86"
}
}
splits {
abi {
reset()
enable enableSeparateBuildPerCPUArchitecture
universalApk false // If true, also generate a universal APK
include "armeabi-v7a", "x86"
}
}
buildTypes {
release {
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
buildConfigField "String", "CODEPUSH_KEY", '"Ce2Lap6BN2ZqSrf6GyQ4U2AZlvpc55514efb-34f8-4da9-8ce5-b8c65a00e283"'
signingConfig signingConfigs.release
}
debug {
buildConfigField "String", "CODEPUSH_KEY", '"3RCKlOnnPmFdxolA0_BKzwH85IkL55514efb-34f8-4da9-8ce5-b8c65a00e283"'
}
releaseStaging {
minifyEnabled enableProguardInReleaseBuilds
buildConfigField "String", "CODEPUSH_KEY", '"3RCKlOnnPmFdxolA0_BKzwH85IkL55514efb-34f8-4da9-8ce5-b8c65a00e283"'
signingConfig signingConfigs.release
}
}
// applicationVariants are e.g. debug, release
applicationVariants.all { variant ->
variant.outputs.each { output ->
// For each separate APK per architecture, set a unique version code as described here:
// http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits
def versionCodes = ["armeabi-v7a": 1, "x86": 2]
def abi = output.getFilter(OutputFile.ABI)
if (abi != null) { // null for the universal-debug, universal-release variants
output.versionCodeOverride =
versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
}
}
}
}
dependencies {
compile project(':react-native-code-push')
compile fileTree(include: ['*.jar'], dir: 'libs')
compile 'com.android.support:appcompat-v7:23.0.1'
compile 'com.facebook.react:react-native:+'
// From node_modules
}
// Run this once to be able to run the application with BUCK
// puts all compile dependencies into folder libs for BUCK to use
task copyDownloadableDepsToLibs(type: Copy) {
from configurations.compile
into 'libs'
}
好啦~~~ 有点偏题了哈~ 配置完key后,我们的android配置到这就结束了
五、配置React Native环境
我们什么时候更新我们的app呢? 我们为了简单一点就直接在rn的第一个页面中作更新了,我们直接在我们的index.android.js文件的componentDidMount方法:
componentDidMount() {
AppState.addEventListener("change", (newState) => {
newState === "active" && CodePush.sync();
});
}
全部内容:
/**
* Sample React Native App
* https://github.com/facebook/react-native
* @flow
*/
import React, {Component} from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
AppState,
} from 'react-native';
import CodePush from 'react-native-code-push';
const VERSION = '1.0.0';
export default class UpdateDemo extends Component {
render() {
return (
<View style={styles.container}>
<Text>{
'当前版本:' + VERSION}</Text>
<Text style={styles.welcome}>
Welcome to React Native!
</Text>
<Text style={styles.instructions}>
To get started, edit index.android.js
</Text>
<Text style={styles.instructions}>
Double tap R on your keyboard to reload,{
'\n'}
Shake or press menu button for dev menu
</Text>
</View>
);
}
componentDidMount() {
AppState.addEventListener("change", (newState) => {
newState === "active" && CodePush.sync();
});
}
}
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('UpdateDemo', () => UpdateDemo);
然后我们打个生产包运行一下,我们直接在根目录执行:
React-native bundle --entry-file index.android.js --bundle-output ./android/app/src/main/assets/index.android.bundle --platform android --assets-dest ./android/app/src/main/res/ --dev false
然后到我们android studio中把 buildtype改为release:
然后直接运行我们的app:
可以看到,我们的app出现了,我们加了一个版本控制为:1.0.0,
然后我们怎么发布我们的jsbundle让它热更新呢?
六、发布jsbundle到codepush
比如我们现在要升级了,我们模拟一下,把rn页面的当前版本1.0.0的提示改为1.0.1:
const VERSION = '1.0.1';
export default class UpdateDemo extends Component {
render() {
return (
<View style={styles.container}>
<Text>{
'当前版本:' + VERSION}</Text>
然后我们在项目根目录创建一个bundles文件夹:
然后打一个jsbundle包到bundles文件夹中:
react-native bundle --platform android --entry-file index.android.js --bundle-output ./bundles/index.android.bundle --dev false
index.android.bundle即为我们需要上传到codepush的文件~~
最后到codepush
code-push release UpdateDemo ./bundles/index.android.bundle 1.0.0 --deploymentName Production --description "更改版本为1.0.1" --mandatory true
然后查看一下我们的发布情况:
code-push deployment ls UpdateDemo
好啦~~~ 我们改变一下我们android中的版本为1.0.0,因为要跟我们codepush上的版本对应起来,所以我们找到andoid的/xxx/UpdateDemo/android/app/build.gradle文件,然后把版本号改为:1.0.0:
defaultConfig {
applicationId "com.updatedemo"
minSdkVersion 16
targetSdkVersion 22
versionCode 1
versionName "1.0.0"
ndk {
abiFilters "armeabi-v7a", "x86"
}
}
然后重新编译运行一下:
可以看到,我们运行后当前版本先是1.0.0,然后过了一会变成了1.0.1,也就是我们的热更新已经集成好了~~
注意:android中的versionName一定要跟codepush中的version一样,我就是这里卡了很久~~~
那如果我们要针对1.0.0再做一次升级呢?
我们继续操作一下~~
const VERSION = '1.0.1';
export default class UpdateDemo extends Component {
render() {
return (
<View style={styles.container}>
<Text>{
'我添加了热更新:' + VERSION}</Text>
可以看到,我改了几个文字,然后我们重新打包:
然后重新上传codepush:
我们重新打开我们的app,顺便看一下as的log:
好啦~到这里我们的热更新就全部完毕了
小伙伴正在项目的情况可能是这样的:进入app请求后台接口–>根据后台接口判断是否需要更新—>弹出dialog提示用户—>点击更新—>执行CodePush.sync();
具体我就不掩饰了~~~
不懂的童鞋可以进群联系我,欢迎交流~~
qq交流群:
参考:
http://www.jianshu.com/p/9e3b4a133bcc
https://github.com/Microsoft/react-native-code-push,
昨天接到市场的一个bug在生成xml文件的时候整个Eclipse RCP卡死了 提到xml就不得不想到著名的“GFW导致DTD访问无能而Dom4j做DTD校验如果发现有网络能通会一直等到超时所以超级慢”的bug。当然,最后排查发现和这个bug无关,这个bug解决方案:1、保持断网状态2、保持FQ状态3、本地化DTD4、使用国内镜像DTDView Co...
操作 Docker 容器容器是 Docker 又一核心概念。简单的说,容器是独立运行的一个或一组应用,以及它们的运行态环境。对应的,虚拟机可以理解为模拟运行的一整套操作系统(提供了运行态环境和其他系统环境)和跑在上面的应用。本章将具体介绍如何来管理一个容器,包括创建、启动和停止等。启动容器启动容器有两种方式,一种是基于镜像新建一个容器并启动,另外一个是将在终止状态(stop...
基于物品的协同过滤推荐算法可以概括为一下两个步骤:计算物品之间的相似度对用户的历史行为进行分析,计算物品相似度进而为用户产生推荐列表,我们可以利用如下的凡是对物品与物品之间的相似度进行计算:其中N(i)表示喜欢物品i的用户数,N(j)表示喜欢物品j的用户数。 3. 从上面的公式中,我们可以看出,当两个物品同时被很多用户喜欢的时候,这两个物品也就有了相似度,换句话说就是用户的历史兴趣和爱好可以为喜欢的物品贡献相似度。所以再次就
浅谈输入法编程作者:启程软件源代码下载摘要:本文拟结合作者的亲身体验简要介绍一下在Windows环境下如何编写输入法程序。 关键字:输入法编程 一直想写一点关于输入法编程的东西,今天终于有点时间,希望对后来者有点帮助。在此要特别感谢“自由拼音”的作者李振春,我刚开始的几个问题都是在他的帮助下才解决。 首先我们需要明白输入法是什么东西。目前常用的输入法基本上有两种类型:外挂
最近学习c语言,因为我用mac本来一直用vs code写一些小的程序还能将就写,后来引用自己定义的头文件报错怎么也解决不了,我知道我的引用头文件格式都没问题,一直以为自己最开始vs code的配置没有搞好,所以下载了Clion来写,无奈仍然报symbol(s) not found for architecture arm64的错误。在我折腾一下午也没解决问题之后,偶然发现是我定义函数的game.c源文件中的函数名与game.h头文件的函数名没有对应起来。函数名对应起来之后,函数名所在行开
官方文档章节 24. Externalized Configuration 24.7 Type-safe Configuration Properties什么是Type-safe Configuration Properties这是spring boot加载自己的配置文件的一种方式, 通过翻译来看, spring boot称之为类型安全的配置文件原理是通过一个限定配置前缀...
继续学习团队开发模式的思想,深入理解git
技术:Java、JSP等摘要:二手书籍的处理一直以来都是令人非常头疼的问题,大学生作为需要广泛阅读的群体,更是大量的教学课本和课外读物常年积压于身。为了解决这个问题,有的同学选择低价贱卖书籍,有的同学选择销毁书籍,显然这些都不很好的处理方式,所以很多二手书籍的交易网站应运而生。这些网站的出现不仅避免了严重的资源浪费现象,也大大降低了书籍拥有者的财产损失。但是众多的二手书交易网站没有统一的规范,加之复杂的界面操作,使得很多的学生不愿使用这类交易网站来完成二手书的循环利用。为了应对这样的问题,我特意设计实现了
论文地址:Mask R-CNN论文源码:R-CNNCaffe版本:rbgirshick/rcnnFast R-CNNCaffe版本: rbgirshick/fast-rcnn Faster R-CNNCaffe版本: https://github.com/rbgirshick/py-faster-rcnn PyTorch版本: https://github.com/longcw/faster_rc...
sudo /usr/libexec/locate.updatedblocate liblaunch_sim转载于:https://www.cnblogs.com/lisjie/p/5135069.html
相关错误:...Scripts\activate.ps1,因为在此系统上禁止运行脚本。解决方案:1.打开设置--->>>更新和安全--->>>开发者选项2.勾选 PowerShell下的应用以下设置以执行PowerShell脚本,应用即可。...
1.List的结构特点是:List中的内容有序可重复2.Set的结构特点是:Set中的内容是无序不可重复的3.Map的结构特点是:Map中的内容是以键值对的形式存放 的,其中key不能重复,如果重复value将被覆盖