需要掌握的基础语法
- 1 使用 fetch
fetch是一个更好的网络API,它由标准委员会提出,并已经在Chrome中实现。它在React Native中也默认可以使用。
- 2 使用promise
我的生涯一片无悔,我想起那个午夜在灯泡下的抠代码,那是我逝去的青春!
fetch是一个更好的网络API,它由标准委员会提出,并已经在Chrome中实现。它在React Native中也默认可以使用。
applicationId
:在Android系统中应用的applicationId
作为应用的唯一标识,就像人的身份证号一样,在一个Android设备中所有的应用程序的applicationId
都是唯一的。
package name
:在用Eclipse构建的Android项目中,使用包名(Package Name)作为应用的唯一标识(applicationId
)。可以说在Eclipse下applicationId
=package name
。所以Eclipse构建的Android项目包名必须唯一,一个包名代表一个应用,不允许两个应用使用同样的包名。包名主要用于系统识别应用,几乎不会被最终用户看到。
在用Android studio构建的Android项目中可以为应用配置applicationId
·,配置的applicationId
可以不和package name
一样。由此可以看来用Android studio构建的Android项目package name
不一定是应用程序的标识。这一点很重要,开发者一定要记住。比如你在使用第三方SDK开发时,如百度地图,需要你来设置Android SDK安全码,在设置安全码时需要用到应用程序的包名,这个包名指的是applicationId
。
注意:识别.R跟Activity文件的路径还是packageName,这个是唯一的
application id
对应 apk 中 manifest 定义的应用包名,同时用于设备以及 Google Play 的应用唯一标识。package
用于在源码中引用 R 类以及解析注册相关的 activity/service,对应 Java 的包名概念。我们在AndroidManifest.xml定义一个包名,这个是唯一的
1 | <manifest xmlns:android="http://schemas.android.com/apk/res/android" |
我们在app.gradle
文件使用Gradle DSL 方法来为不同的 flavor 和 build type 定义不同的 applicationId:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18android{
···
productFlavors {
pro {
applicationId = "com.hammercui.app.pro"
}
free {
applicationId = "com.hammercui.app.free"
}
}
buildTypes {
debug {
applicationIdSuffix ".debug"
}
}
....
}
CodePush ,我在使用的方案,同时支持android跟ios,可以点击查看我的详细教程react-native-android实战:4 CodePush使用
React-Native Android 热更新,这个方案对反射的方案做了补充
名称 | 类型 | 价格 | 个人评价 |
---|---|---|---|
海尔滚筒洗衣机XQG80-BX12636 | 滚筒 | 2899 | 担心小孩,次要考虑 |
海尔洗衣机XQS70-Z1626 | 波轮 | 2599 | 这个不错,防缠绕 |
名称 | 类型 | 价格 | 个人评价 |
---|---|---|---|
海尔冰箱BCD-248WDPM | 双开门 | 2288 | 风冷无霜 |
海尔冰箱BCD-251WDBD | 双开门 | 2699 | 风冷无霜 |
美菱冰箱BCD-220L3BX芙蓉红 | 三开门 | 1699 | 红色,其他无特点 |
名称 | 类型 | 价格 | 个人评价 |
---|---|---|---|
三星(SAMSUNG) UA58J50SWAJXXZ | 58英寸 全高清 LED液晶电视 | 4599 | 首选项,放弃4k |
夏普(SHARP) LCD-55S3A | 55英寸 | 4999 | 4K超高清 智能网络 液晶电视 |
CodePush 是微软开发的,可以实时更新 React Native 和 Cordova 应用。
CodePush 是提供给 React Native 和 Cordova 开发者直接部署移动应用更新给用户设备的云服务。CodePush 作为一个中央仓库,开发者可以推送更新到 (JS, HTML, CSS and images),应用可以从客户端 SDKs 里面查询更新。CodePush 可以让应用有更多的可确定性,也可以让你直接接触用户群。在修复一些小问题和添加新特性的时候,不需要经过二进制打包,可以直接推送代码进行实时更新。
CodePush 可以进行实时的推送代码更新:
CodePush的react-native版本是react-native-code-push
询问code push
服务器是否app有新的版本
codePush.checkForUpdate(deploymentKey: String = null): Promise<RemotePackage>;
该方法返回Promise,有如下两种值:
举例用法:1
2
3
4
5
6
7
8codePush.checkForUpdate()
.then((update) => {
if (!update) {
console.log("The app is up to date!");
} else {
console.log("An update is available! Should we download it?");
}
});
codePush.getCurrentPackage
获得当前已安装更新的元数据(比如描述,安装时间,大小)
举例:1
2
3
4
5
6
7
8
9codePush.getCurrentPackage()
.then((update) => {
// If the current app "session" represents the first time
// this update has run, and it had a description provided
// with it upon release, let's show it to the end user
if (update.isFirstRun && update.description) {
// Display a "what's new?" modal
}
});
codePush.notifyApplicationReady
通知CodePush进程,一个更新安装好了。当你检查并安装更新,(比如没有使用sync方法去handle的时候),这个方法必须被调用。否则CodePush会认为update失败,并rollback当前版本,在app重启时。
当使用sync
方法时,不需要调用本方法,因为sync
会自动调用,
codePush.restartApp
立即重启app.
当以下情况时,这个方式是很有用的。:
sync
or LocalPackage.install
方法时,指定的 install mode 是 ON_NEXT_RESTART
or ON_NEXT_RESUME
时 . 这两种情况都是当app重启或resume时,更新内容才能被看到。因为强制重启,能马上显示更新内容嘛!
codePush.sync
1 | codePush.sync(options: Object, syncStatusChangeCallback: function(syncStatus: Number), downloadProgressCallback: function(progress: DownloadProgress)): Promise<Number>; |
允许检查更新,下载并安装,都在一个方法中。除非你需要自定义UI开表现。我们建议开发者在整合CodePush时,使用这个函数。
checkForUpdate方法只能检查更新,接下来怎么做要自己写。而该方法更新,下载,安装都能处理。该方法提供了两种模式
Silent mode
(默认的模式),它会自动下载可用更新,并在下次重启应用时应用它们(比如操作系统或用户杀死进程,或者重新启动设备) 。这样一来,整个更新是“沉默”给最终用户,因为用户看不到任何更新提示。
Active mode
当有可用的更新,提示用户是否允许下载,然后立即应用此更新。一旦选择了强制通知用户来手动更新,以后每次更新都会强制通知。个人感觉这种方式最合理
sync
方法,有以下设置属性
deploymentKey
(String): 部署key,用来区分不同的app,在Info.plist
(Ios)和MianActivity.java
(Android)修改,当然你也可以在JavaScript里填写,这样我们能动态的修改keyinstallMode
(codePush.InstallMode): 非强制更新,默认codePush.InstallMode.ON_NEXT_RESTART
mandatoryInstallMode
(codePush.InstallMode):强制更新,默认codePush.InstallMode.IMMEDIATE
minimumBackgroundDuration
(Number):updateDialog
(UpdateDialogOptions) :可选的,更新的对话框,默认是null,包含以下属性
appendReleaseDescription
(Boolean) - 是否显示更新description,默认false
descriptionPrefix
(String) - 更新说明的前缀。 默认是” Description: “
mandatoryContinueButtonLabel
(String) - 强制更新的按钮文字. 默认 to “Continue”.
mandatoryUpdateMessage
(String) - 强制更新时,更新通知. Defaults to “An update is available that must be installed.”.
optionalIgnoreButtonLabel
(String) - 非强制更新时,取消按钮文字. Defaults to “Ignore”.
optionalInstallButtonLabel
(String) - 非强制更新时,确认文字. Defaults to “Install”.
optionalUpdateMessage
(String) - 非强制更新时,更新通知. Defaults to “An update is available. Would you like to install it?”.
title
(String) - 要显示的更新通知的标题. Defaults to “Update available”.
举例:
1 | // Prompt the user when an update is available |
npm install -g code-push-cli
可以输入code-push -v
查看版本号
在发布一个更新前,必须有账号。
code-push register
,执行后弹出https://codepush-management.azurewebsites.net/auth/register?hostname=DESKTOP-3PVIHT6,code-push login
code-push loout
code-push access-key ls
code-push access-key rm <accessKey>
常用命令,只有下面几个:
access-key
View and delete active user sessionsapp
View and manage your CodePush-enabled appscollaborator
View and manage collaborators for a given appdeployment
View and manage the deployments for your appslogin
Authenticate with the CodePush server in order to begin managing your appslogout
Log out of the current sessionpromote
Promote the package from one deployment of your app to anotherregister
Register a new account with the CodePush serverrelease
Release a new version of your app to a specific deploymentrelease-react
Release a new version of your React Native app to a specific deploymentrollback
Performs a rollback on the latest package of a specific deployment选项:
比如
code-push app ls
code-push app add Zlot_react
code-push deployment add app名字 部署名
code-push deployment rename app名字 旧部署名 新部署名
code-push deployment rm app名字 部署名字
code-push deployment ls app名字
code-push deployment ls app名 -k
code-push deployment history appName deploymentName
注意:默认的部署名是staging,所以staging的key值就是我们的deployment key
需要有git环境,并在app工程目录控制台输入
npm install react-native-code-push --save
安装成功在package.json会看到
"react-native-code-push": "^1.8.0-beta",
1 | include ':app', ':react-native-code-push' |
1 | compile project(':react-native-code-push') |
android react-native v0.18.0+
此种配置方法仅仅适用react-native 0.18版本及其以上。而且是android studio环境
MainActivity.java
文件,添加CodePush包1 | // 1. Import the plugin class |
android/app/build.gradle
文件有android.defaultConfig.versionName
属性1 | android { |
import codePush from "react-native-code-push";
componentDidMount
阶段调用sync
,来开启一个后台的更新。codePush.sync();
isMandatory === true
强制更新,采用弹出model框提醒的方式isMandatory === false
非强制更新,采用静默安装的方式如:1
2
3
4
5
6
7componentDidMount() {
codePush.sync({
updateDialog: false,
installMode: codePush.InstallMode.ON_NEXT_RESUME,
deploymentKey: CODE_PUSH_PRODUCTION_KEY,
});
},
注意:从react-native v0.19.0开始支持android更新assets资源,并且需要CodePush v1.7.0。所以你需要升级你的react-native跟CodePush。
确保在工程目录,新建release文件夹,如下
1 | react-native bundle |
Android版本
1 | react-native bundle --platform android --entry-file index.android.js --bundle-output ./release/main.jsbundle --assets-dest ./release --dev false |
ios版本
1 | react-native bundle --platform ios --entry-file index.ios.js --bundle-output ./release/main.jsbundle --assets-dest ./release --dev false |
1 | code-push release <appName> <updateContents> <targetBinaryVersion> |
mandatory :是否强制更新。
注意:targetBinaryVersion指的是当前的app版本,比如客户端是1.0.0,如果我们要更新客户端,targetBinaryVersion也要是1.0.0,表示是1.0.0版本的更新,跟native的更新不太一样。这是我们第一次经常犯的错误大家不要搞混了。
1 | code-push release Zlot ./release 1.0.0 --description "这是一个1.0.0版本的更新" --mandatory true |
but it is being ignored due to having been previously rolled back.
问题原因:客户端版本1.0.0,然后release的版本是1.0.1,所以检测的时候发现不了1.0.0版本的更新。
因此我们也建议,js的更新采用静默更新,native的更新,做出提示。
注意:targetBinaryVersion指的是当前的app版本,比如客户端是1.0.0,如果我们要更新客户端,targetBinaryVersion也要是1.0.0,表示是1.0.0版本的更新,跟native的更新不太一样。这是我们第一次经常犯的错误大家不要搞混了。
[CodePush] An update is available, but it is being ignored due to having been previously rolled back.
解决方案:一般这种情况是在,我们自定义model,展示更新过程,并监听了安装完成,比如这个demo,我这这里忘了在componentDidMount()
方法里添加CodePush.notifyApplicationReady();
,就一直报这个错误。添加上就ok了
封装好java模块供JavaScript调用,比如我们用js实现不了的控件或模块,可以通过java实现并封装,供JavaScript使用。
java模块新建LogModule类,继承自ReactContextBaseJavaModule,如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21public class LogModule extends ReactContextBaseJavaModule {
private static final String MODULE_NAME= "Log";
//构造函数
public LogModule(ReactApplicationContext reactContext) {
super(reactContext);
}
//字符串是JavaScript调用模块的名字
@Override
public String getName() {
return MODULE_NAME;
}
//暴露给JavaScript端的方法
@ReactMethod
public void d(String tag,String msg)
{
Log.d(tag,msg);
}
}
新建module注册类,实现自ReactPackage,把module注册如package1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24public class MyAppReactPackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
//add Log模块
modules.add(new LogModule(reactContext));
//add DBlocal模块
modules.add(new DBlocalModule(reactContext));
return modules;
}
@Override
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
注册包,在MainActivity中1
2
3
4
5
6
7
8@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage()
+ , new VectorIconsPackage()
+ ,new MyAppReactPackage()
);
}
我们把需要自定义的java模块,统一管理,放在NativeBrigde.android.js文件1
2
3
4
5
6
7
8
9//log模块
var RCTLog = NativeModules.Log;
var Log = {
d: function (tag: string, msg: string): void {
console.log(tag,msg);
RCTLog.d(tag, msg);
},
};
module .exports ={Log,DBlocal};
需要调用的时候1
2var {Log,DBlocal} = require('../AnComponent/NativeBridge');`
Log.d('hehe',"我是打印输出");
下面的参数类型在@ReactMethod注明的方法中,会被直接映射到它们对应的JavaScript类型。
1 | Boolean -> Bool |
java层需要把结果传递给js,答案就是回调,最典型的一个场景就是javascript层调用java层的网络请求方法,java层拿到网络数据后需要将结果返回给javascript层
为了测试我们在已建好的模块新增方法,invoke回调的参数是无类型,无个数限制的1
2
3
4@ReactMethod
public void getValue(String key,final Callback callback){
callback.invoke("我是结果");
}
JavaScript中的使用1
2
3
4//从本地数据库取值
DBlocal.getValue('test',(result)=>{
Log.d('hehe',result);
});
在android studio控制台可以看到结果
还可以用事件调用JavaScript层,假设调用了java层方法后,我们希望发送一个事件给javascript,在javascript层再次进行打印输出。
java端
1 | @ReactMethod |
而对应的log.js并不需要做什么修改,我们只需要在想获得该事件的地方注册事件即可,比如我们想在主界面接收该事件,则在index.android.js中进行注册。
JavaScript端1
2
3
4
5
6
7
8
9componentDidMount:function(){
Log.d("tag","tag");//调用java层log方法,之后就会回调对应的事件(前提是注册了事件)
//直接使用DeviceEventEmitter进行事件注册
DeviceEventEmitter.addListener('logInConsole',(e)=>{
console.log(e);
}); //记得在UnDidMount里卸载事件
},
});
通关官方文档我们了解到,尽量建立统一的image读取路径,便于管理,我们都知道ios的图片资源通过@2x,@3x自动适配,react-native也使用了这一方法。
官方的PixelRatio告诉我们如何获得dpi
static get()
具体用法1
var bat = PixelRatio.get();
返回设备的像素密度,例如:
学习官方文档Image可知。
我们在src路径存放nav_back@2x.png
nav_back@2x.png
,加载时调用
require('../src/nav_back.png')
参考github
我们知道打开local-cli/bundle/assetPathUtils.js文件可以看到
1 | function getAndroidAssetSuffix(scale) { |
而我们创建的资源有@3.5x,应该去掉,去掉之后打包就成功了。
APP 运行的时候不应再从 Debug Server 获取资源。分发应用时 JS 资源应该被打包到 APP 中。
另外对外发布的安装包,资源中的业务代码的混淆也是必须的。
1 创建keystore,使用eclipse,android studio都行
2 拷贝keystore到路径android\app\下
3 修改android\app\build.gradle
文件
添加如下内容
1 | android { |
4 在工程目录,运行命令行开始打包
cd android && ./gradlew assembleRelease
解决方法常见我的blogreact-native之旅:Image资源适配,
以下是StyleSheet所有可用的属性
如果不知道具体的属性怎么写,可以随便写一个,然后运行,报错,react-native会提示你正确的参数,比如我们选择borderStyle,不知随便写,然后报如下错误。
这样我们就知道了borderStyle的参数可以使1
'solid', 'dotted','dashed'
具体什么意思可以翻译。
实线 点 虚线