大概是2017年底接触的RN,当时项目开发中遇到很多问题,都是自己一步一个坑走过来的。看了下当时的笔记,觉得还是在这里记录下,防丢失。
React-native APP 开始
App Store下载Xcode并安装(about one hour)
下载并安装Android studio:https://developer.android.com/studio/index.html(需翻墙,take too long)
npm install -g react-native-cli
-
react-native init MyApp(最新版可能下载失败导致无法成功run ios,所以用下面的version)
react-native init MyApp --version 0.44.3
iOS模拟器调试
cd 进入MyApp目录下(你的项目目录)
react-native run-ios
-
若报错:
xcrun: error: unable to find utility "instruments", not a developer tool or in PATH
解决:
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer/
-
若模拟器爆红:No bundle URL present
解决1:在模拟器运行期间 项目目录下执行npm install
解决2:检查翻墙的代理,由全局模式改为自动模式即可
iOS真机调试
打开Xcode
绑定Apple ID:Xcode 》preferences 》accounts
选中左侧project根目录,点击右侧General配置
identity 》Bundle identifier 输入唯一标识(保证唯一即可)
Signing 》Team 选中刚刚绑定的Apple ID对应的team
点击左上角run 按钮右侧运行设备选项,选中USB已连接的你的设备
run
过程中出问题按照提示对设备进行授权配置
若build时就出错,尝试重启Xcode,再run
若connect server error:查看手机电脑wiffi是否一致,若不一致,停掉服务重新run
若报错
The application does not have a valid signature.
clean下重跑
android调试
cd进入MyApp目录
react-native run-android
-
若报错:Error watching file for changes: EMFILE
解决办法:watchman出错,卸载并重装
brew uninstall --force watchman
rm -rf/usr/local/var/run/watchman/
brew install watchman
错误信息不明时可打开android studio查看详情
-
若gradle的问题:可能因为android studio 的gradle没加载成功,修改gradle-wrapper.properties文件内url为其他版本,例:
https://services.gradle.org/distributions/gradle-3.3-all.zip
或改成本地地址在setting内加以配置
可通过http://localhost:8081/index.android.bundle?platform=android去验证服务是否成功开启
-
报错could not connect development server:(坑!)
(1). android studio debug运行模式,
(2). 打开命令行,cd到当前目录,react-native start
(3). 真机或模拟器reload,成功与否
(4). 若失败:adb reverse tcp:8081 tcp:8081,reload(5). 若command adb not found:"adb"不是内部命令和sudo: adb: command not found
-
真机adb无响应:(可能因为其他应用占用adb的5037端口)
解决:netstat -aon|findstr “5037” 找到占用端口的进程PID, 打开任务管理器,杀掉对应进程。 重新run
react-native学习指南
引用antd-mobile的坑
对于莫名其妙的问题,确保自己antd-mobile的版本不要太低,坑
对于找不到module的问题,请用yarn安装,cnpm安装可能会漏掉一些依赖
对于antd-mobile not defined 的错误,确保安装了react-dom,antd-mobile,babel-plugin-import,并且修改.babelrc 文件,例:
{
"plugins": [["import", { "libraryName": "antd-mobile" }]], "presets": ["react-native"]
}
在react-native使用Icon
-
使用antd-mobile的Icon
RN 版本由于 Icon 无法做纯 UI,需要 native 支持
下载
https://at.alicdn.com/t/font_r5u29ls31bgldi.ttf
重命名为anticon.ttf
打开 iOS 项目
info.plist
文件,添加Fonts provided by application
,指定一个 item 的值为anticon.ttf
, 将anticon.ttf
拖进项目;Android 项目将
anticon.ttf
放在android/app/src/main/assets/fonts/
目录下;
使用方式:
内置的几个图标: <Icon type="check" size="md" color="red" />
自定义图标:<Icon type={'\ue601'} size={55} /> (具体参看 demo)
自定义图标:<Icon type={'\ue601'} size={55} /> (具体参看 demo)</pre>
-
使用react-native-vector-icons
参考react-native-vector-icons的简单使用
Icon选用参考:http://fontawesome.io/icons/
若出现搜不到的问题,尝试卸载APP重启
native-echarts
-
Android 上图表有时不显示问题
原因:图形渲染支持的不好?
解决:隐藏包含echart的div,数据填充渲染结束后显示div,中间要有一定时间的延迟,比如100ms
android打包
根据RN官网推荐方式打包
-
若类似错:
Could not find com.android.tools.build:gradle:3.0.0.
可能:自己android studio版本不是3.0.0,换成对应版本
或者:android/build.gradle下
buildscript {
repositories {
...
google()
}
}
-
若报错:
Could not get unknown property 'MYAPP_RELEASE_STORE_FILE'
原因:将如下代码加入到android/gradle.properties, **改为密钥
MYAPP_RELEASE_STORE_FILE=my-release-key.keystore
MYAPP_RELEASE_KEY_ALIAS=my-key-alias
MYAPP_RELEASE_STORE_PASSWORD=****
MYAPP_RELEASE_KEY_PASSWORD=****
-
项目中用到react-native-echarts,在android打包后无法显示图表:
原因:release版本只能使用uri加载资源(???)
解决:
把node_modules/native-echarts/src/components/Echarts/tpl.html文件复制一份到android/app/src/main/assets文件里
修改node_modules/native-echarts/src/components/Echarts/index.js:
source={require('./tpl.html')}
改为source={{uri:'file:///android_asset/tpl.html'}}
打包后记得改回来,不然ios无法正常显示图表
-
修改版本信息:
android在android/app/build.gradle文件下修改
其中versionCode一般是整数,上传appstore时,每次都需比上次数字要大,否则不予覆盖上一版本
versionName用于在app上显示版本信息,供自己或用户查看
defaultConfig{
versionCode 1
versionName "1.0"
}
-
IOS打包
在Xcode中选择Product -> Scheme -> Edit Scheme (cmd + <),然后选择Run选项卡,将Build Configuration设置为release
在ios中用http请求需做配置,参照让iOS项目允许使用http协议请求
run,若想调试,将Build Configuration重新设置为debug
设置应用图标和闪屏图片react native ios打包到真机
若出现其他手机无法run release版本,报错:
can't link with a main executable file '/Users/liujinling/Library/Developer/Xcode/DerivedData/gngcApp-glzeunbnyelbpmcrehcygunmtjfq/Build/Products/Release-iphoneos/gngcApp.app/gngcApp' for architecture armv7
clang: error: linker command failed with exit code 1 (use -v to see invocation)
解决:
product ->schema-> edit ->schema-> run
选择release build
中xxxTests
只勾选Test
, build
中xxx 勾选全部
-
账号下手机连接数量达上限,无法使用其他手机调试或安装:
原因:免费的账号连接设备数应该是3个左右,想连接更多的测试机,升级到收费账号,可连100个设备
解决:1. 申请企业收费开发者账号
2. General/signing/Team:添加一个新的account
-
安装release版时报错:
Could not write to the device
,或A valid provisioning profile for this executable was not found.
:原因:我这里因为切换team(开发者账号)导致
解决:Xcode上方导航栏product下clean操作,重新run
Mac 下gradle配置
android studio 下载gradle极慢,所以下载到本地自己配置
下载所需版本:http://services.gradle.org/distributions/ (例:gradle-4.1-all)
命令open .gradle 打开finder,
进入wrapper/dists/gradle-4.1-all/(njsjdscn),删掉目录下所有
将下载的gradle-4.1-all.zip拉进来,重启android studio
记一次重大填坑现场
在android和ios真机+模拟器run成功n次,双机打包均跑成功后,再次android真机调试时,突然的gradle构建失败!!!WTF
诡异1:android打包时按照react-native官网配置了一些文件,今天打开代码,发现一会这里多了个‘g’,一会那里classpath被删掉,还有其他的属性名缺了一半,连gradle.zip后面紧跟了个’android‘……细思极恐
诡异2:react-native-linear-gradient库突然无法正确引入?错误提示:com.facebook.react:react-native:+
诡异3:android/app/build.gradle文件找不到com.facebook.react:react-native:+
解决:
- 在android studio 报错处跟踪进react-native-linear-gradient,添加一块代码,既然gradle找不到
com.facebook.react:react-native:+
,我们就告诉他怎么找
repositories {
jcenter()
}
- 在android/app/build.gradle添加如下代码
allprojects {
repositories {
jcenter()
mavenCentral()
mavenLocal()
maven {
// All of React Native (JS, Android binaries) is installed from npm
url "$rootDir/../node_modules/react-native/android"
}
}
}
若url "$rootDir/../node_modules/react-native/android"
报错,将其改为url "$rootDir/node_modules/react-native/android"
。应该是通过yarn安装的react-native使用前者,通过npm安装的使用后者
莫名其妙的坑,踩了一下午,燥
又一次重重大填坑现场
RN Android 文件预览
-
利用
react-native-doc-viewer
*注意注释部分
const handlePress = (name,id) => {
const nameArr = name.split('.');
const type = nameArr[nameArr.length - 1];
//Interface.downloadUrl + id是文件的下载地址
const downloadUrl = Interface.downloadUrl + id + '.' + type;
if (Platform.OS === 'ios') {
//IOS(成功预览)
OpenFile.openDoc([{
url: downloadUrl,
//此处url后缀必须带文件type(例:.doc)
//原因:ios配置没有fileType,需靠url后缀识别文件类型
fileNameOptional: name
//ios的fileNameOptional取值中文是ok的
}], (error, url) => {
if (error) {
console.error(error);
} else {
console.log(url, '成功')
}
})
} else {
//Android(除图片外,其他无效,打不开文件)
OpenFile.openDoc([{
url: Interface.downloadUrl + id,
//此处url直接为下载地址,无需特意加类型后缀
fileName: name,
//Android的fileName取值中文报错
cache:false,
fileType:type
}], (error, url) => {
if (error) {
console.error(error);
} else {
console.log(url, '成功')
}
})
}
}
- 辅助
react-native-fs
//Android
const SavePath = RNFS.DocumentDirectoryPath;
//const SavePath = RNFS.ExternalDirectoryPath;
//const SavePath = RNFS.ExternalStorageDirectoryPath;
//当配合react-native-doc-viewer,以上三个路径均测试,无法打开文件
//当配合原生模块,使用第二个
//const SavePath = RNFS.MainBundlePath //ios的存储路径
const options = {
fromUrl: downloadUrl,
//加不加类型后缀都ok
toFile: `${SavePath}/${name}`,
};
RNFS.downloadFile(options).promise.then(res => {
console.log(res, 'res')
//配合react-native-doc-viewer(无效)
OpenFile.openDoc([{
url: `file://${SavePath}/${name}`,
//此处url为系统内部路径,前面需加‘file://’
fileName: name,
//Android的fileName取值中文报错
cache:false,
fileType:type
}], (error, url) => {
if (error) {
console.error(error);
} else {
console.log(url, '成功')
}
})
//配合原生模块(成功)
NativeModules.OpenFileModule.show(`${SavePath}/${name}`)
}).catch(err => {
console.log(err, 'err')
})
-
react-native-fs
加原生模块实现
//任意位置新建一个类(java文件)
package com.reactNativeModules;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.webkit.MimeTypeMap;
import android.widget.Toast;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import java.io.File;
import java.util.Locale;
public OpenFileModule(ReactApplicationContext reactContext){
super(reactContext);
}
@Override
public String getName(){
return "OpenFileModule";
}
//getName为必需
@ReactMethod
private void show(final String filePath)
{
String ext = filePath.substring(filePath.lastIndexOf('.')).toLowerCase(Locale.US);
try
{
MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
String temp = ext.substring(1);
String mime = mimeTypeMap.getMimeTypeFromExtension(temp);
Intent intent = new Intent();
intent.setAction(android.content.Intent.ACTION_VIEW);
File file = new File(filePath);
intent.setDataAndType(Uri.fromFile(file), mime);
//这里原本:startActivity(intent);
//但由于react调原生方法,不存在原生activity,报错,使用下面两句代替
Activity currentActivity = getCurrentActivity(); currentActivity.startActivity(intent);
}
catch (Exception e)
{
e.printStackTrace();
Toast.makeText(getReactApplicationContext(), "无法打开后缀名为." + ext + "的文件!",
Toast.LENGTH_LONG).show();
}
}
//任意位置定义一个包(java文件)
package com.reactNativeModules;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Created by liujinling on 2018/1/23.
*/
public class OpenFilePackage implements ReactPackage{
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
@Override
public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
//OpenFileModule为上文新建的类名
modules.add(new OpenFileModule(reactContext));
return modules;
}
//此处为必需
@Override
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
}
//在mainactivity.java内注册模块(必需)
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
//OpenFilePackage为上文新建的包名
new MainReactPackage(),new OpenFilePackage());
}
//js中使用
import {NativeModules} from 'react-native';
//注意这里的path需带文件类型后缀
//且前面不要加‘file://’
//直接取RNFS.ExternalDirectoryPath存储的地址
const path = '/data/file/test.doc'
NativeModules.OpenFileModule.show(path)
android中TextInput顶起TabBar问题
目录:android/app/src/main/AndroidManifest.xml
中的<activity>标签中,添加android:windowSoftInputMode="stateAlwaysHidden|adjustPan|adjustResize"
gngcApp极光推送
苹果开发者平台:苹果开发者平台
证书配置:iOS 证书申请和使用详解
官方描述证书配置:iOS 证书设置指南
-
若报错:
The provisioning profile specified in your build settings ("test") has an AppID of "com.baidu.test" which does not match your bundle identifier "com.apple.test"
原因:info.plist文件中的bundle ID与创建证书时的不一致
解决:
修改info.plist文件中的bundle ID保持一致
把Build Settings中Package分栏下的Product Bundle Identifier改成新的bundle ID
App设置应用名字、图标、启动页
生成应用图标:图标工厂
只需上传一张1024*1024的应用图标,该网站会自动为你生成各种适配版本的icon,包括ios和android
ios
应用名称:Xcode内target/info/Custom iOS Target Propperties下的Bundle display name修改为应用名称
应用图标:项目目录下Images.xcassets/AppIcon内,将需要适配的图标拖进去,注意:若要上传到App Store,1024*1024的图标一定要上传
启动页:关于iOS APP设置启动图片
推送通知图标:极光推送默认选取应用图标
android
应用名称:android/app/src/main/res/values/strings.xml内,修改应用名称
应用图标:android/app/src/main/res/文件夹下图标文件替换为图标工场下载下来的文件们
启动页:
推送通知图标:极光推送默认选取应用图标,但在anfroid上可能行不通,android/app/src/main/res目录下说明:若没有res/drawable-xxxx/jpush_notification_icon这个资源默认使用应用图标作为通知icon,在5.0以上系统将应用图标作为默认推送图标
,官网如是说。没有可以新建一个,把图标命名为相应名称。项目中res下图标用的是mipmap,不是drawable,所以新建相应路径尝试。不行的话,重启手机,此为坑。再不行,换个手机试试,可能当前手机有缓存。