ReactNative 在丁香医生项目中引入的踩坑日记

this没绑定到函数导致空指针

参考

React-Native 踩坑第二弹-undefined is not a function(evaluating 'this.setState(...))
为什么在es6在这种情况下不需要bind this
React Native绑定this(bind(this) ES5语法React.createClass会自动绑定this,ES6的写法,不再自动绑定this。
React:ES6:ES7中的6种this绑定方法

说明

1、ES5语法React.createClass会自动绑定this,ES6的写法,不再自动绑定this
2、可以在constructor中进行this绑定
3、如果将自定义的函数写成箭头函数形式则会自动绑定this 

异常

undefined is not a function(evaluating 'this.setState(...))

错误代码

export default class App extends Component<Props> {
//省略N多代码
    //点击事件
    _pressRow(rowToastMsg){
        ToastAndroid.show(rowToastMsg,ToastAndroid.SHORT);
    }
    //this._pressRow(rowData)} 处报this空指针
    _renderRow(rowData) {
        return (
            <TouchableOpacity onPress={() => this._pressRow(rowData)}>
                <Text style={{fontSize:40}}>
                    {rowData}
                </Text>
            </TouchableOpacity>
        );
    }

解决方案

    constructor(props) {
        super(props);
        const ds = new ListView.DataSource({rowHasChanged:(r1,r2) => r1 !== r2});
        this.state = {
            dataSource: ds.cloneWithRows([
                '全部科室',
            ])
        };
        //在构造器中添加下面这句代码
        this._renderRow = this._renderRow.bind(this);
    }

npm install异常——npm ERR! Unexpected end of JSON input while parsing near

异常

npm install
npm WARN deprecated connect@2.30.2: connect 2.x series is deprecated
npm ERR! Unexpected end of JSON input while parsing near '...5716bd740fd2","size":'

参考网址

npm install异常:npm ERR! Unexpected end of JSON input while parsing near

解决方案

npm cache clean --force
npm install

查看的配置npm代理

参考网址

Npm的配置管理及设置代理

查看npm配置文件路径

npm config get userconfig

设置npm代理

vim /Users/ybf326/.npmrc
添加如下内容
proxy=http://127.0.0.1:8118/
https-proxy=http://127.0.0.1:8118
注释可以使用#号,代理设置完成记得重新打开一下终端

连接数过多异常—— npm ERR! 503 Too many open connections

参考网址

python mechanize( HTTP Error 503: Too many open connections)

异常

当执行 npm install 时,出现 npm ERR! 503 Too many open connections 异常

解决方案

可能是代理对连接数做了限制,关闭代理,或者更换代理

android react-native run-android无法安装异常

异常

Building and installing the app on the device (cd android && ./gradlew installDebug)...
Could not install the app on the device, read the error above for details.
Make sure you have an Android emulator running or a device connected and have
set up your Android development environment:
https://facebook.github.io/react-native/docs/android-setup.html

可能原因

gradlew没有运行权限

解决方案

chmod +x gradlew

alert(this.props.navigation) 的 navigation 始终为 undefined

主要原因

没有正确配置如下代码 

AppRegistry.registerComponent('AspirinPageProject', () => myStackNavigator);

上述代码解析

AspirinPageProject是你项目执行init的时候的名字
myStackNavigator是类型为StackNavigator的常量

关于myStackNavigator的创建方法

示例 

const myStackNavigator = StackNavigator({
    MyHome: {screen: HomeScreen},
    SectionGroupActivity: {screen: SectionGroupActivityScene},
});

MyHome 名字可以随便取,StackNavigator 会默认加载第一个screen作为根视图
SectionGroupActivity 名字也可以随便取,不过,下面调用 navigate方法时,需要使用此名字 

const {navigate} = this.props.navigation;
navigate("SectionGroupActivity");

screen为固定类型,HomeScreen 和 SectionGroupActivityScene 为类名
例如: 

export default class SectionGroupActivityScene extends Component {
    static navigationOptions = {
        title: 'SectionGroupActivityScene',
        //旧版-隐藏NavigationBar
        //visible: false
        //新版-隐藏NavigationBar
        header: null,
    };
    render() {
        return (
            <View >
                <Text>ChatScreen界面</Text>
            </View>
        );
    }
}

使用教程 react-navigation

1、安装react-navigation

yarn add react-navigation
or
npm install --save react-navigation

2、删除index.js所有内容,添加为如下内容

import './App'

3、修改App.js的最顶部,导入必要的类

import {
  Platform,
  StyleSheet,
  Text,
  View,
  ListView,
  TouchableOpacity,
  ToastAndroid,
  AppRegistry
} from 'react-native';
import SectionGroupActivityScene from "./SectionGroupActivityScene";
import {StackNavigator} from 'react-navigation';

4、修改App.js的默认类名为HomeScreen(也可以保留类名为App)

5、修改HomeScreen类的首行,设置如下NavigationBar的选项

    static navigationOptions = {
        //NavigationBar标题
        title: 'SectionGroupActivityScene',
        //旧版-隐藏NavigationBar
        //visible: false
        //新版-隐藏NavigationBar
        //header: null,
    };

6、在App.js最底部,创建myStackNavigator常量对象,类型为StackNavigator。并注册为项目的根对象。

const myStackNavigator = StackNavigator({
    MyHome: {screen: HomeScreen},
    SectionGroupActivity: {screen: SectionGroupActivityScene},
});
//默认打开 SectionGroupActivityScene 界面
//AppRegistry.registerComponent('AspirinPageProject', () => SectionGroupActivityScene);
//默认打开导航器(这才是我们需要的)
AppRegistry.registerComponent('AspirinPageProject', () => myStackNavigator);

7、在TouchableOpacity的onPress方法(如 _pressRow 方法)中使用navigate进入跳转导航

//点击事件
_pressRow = (rowToastMsg) => {
    const {navigate} = this.props.navigation;
    navigate("SectionGroupActivity");
    //alert(this.props.navigation);
}

fetch函数出现异常

异常

react native TypeError: Network request failed

参考网址

react native 使用fetch进行网络请求(https),解决SSLHandshake问题,以及怎样进行二次封装

可能的解决方案

如果你正在使用Charles.app设置代理抓包,请立即停止
如果你的服务端证书是非法的,请立即更换
你可以封装并调用原生方法或者使用其它方法来获取网络数据,而非使用fetch函数 

fetch无网络时异常 —— React Native no internet fetch crashes

异常

react native fetch attempt to invoke virtual method on a null object reference

解决方案

调用fetch之前,一定记得检查网络状态
先添加Android网络状态访问权限 

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

检查并监听网络状态示例 

    //检查网络状态,调用数据请求函数
    //不检查网络状态,直接fetch,应用可能crash
    checkConnectionState = () => {
        NetInfo.isConnected.fetch().then(isConnected => {
            if(isConnected){
                this.getSectionList();
            }else{
                NetInfo.isConnected.addEventListener(
                    'connectionChange',
                    handleFirstConnectivityChange.bind(this)
                );
                alert("当前网络未连接,请连接网络");
            }
        });
        function handleFirstConnectivityChange(isConnected) {
            if(isConnected){
                //移除监听器本身
                NetInfo.isConnected.removeEventListener(
                    'connectionChange',
                    handleFirstConnectivityChange
                );
                ToastAndroid.show("网络已连接,正在为您加载最新数据",ToastAndroid.SHORT);
                this.getSectionList();
            }
        }
    }
    //获取所有科室数据
    getSectionList = () => {
    }

关于使用了getInitialState函数后,this.state为null的问题

原因

React在ES6的实现中去掉了getInitialState这个hook函数,规定state在constructor中实现 

在已有项目中引入React Native的坑

gradle.properties中添加参数

android.useDeprecatedNdk=true

app模块的build.gradle修改

android-defaultConfig节点 

ndk {
    abiFilters "armeabi-v7a"
}

android节点 

// 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(com.android.build.OutputFile.ABI)
        if (abi != null) {  // null for the universal-debug, universal-release variants
            output.versionCodeOverride =
                    versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
        }
    }
}

准备使用react native的模块(如果只有一个app模块,则可以在同一模块中修改)添加依赖信息

+号表示依赖最新版,也可以指定明确的版本号,+号有问题可以改成具体版本号 

compile "com.facebook.react:react-native:0.55.3"

准备使用react native的模块添加依赖的下载位置(远方的maven库可能更新不及时,所以需要使用本地仓库),修改本模块的build.gradle

apply plugin: 'com.android.library'
apply from: "../../aspirin-2016/node_modules/react-native/react.gradle"

添加maven库路径,修改根项目(app模块外面一层)下的build.gradle

allprojects-repositories节点 

maven {
    url "$rootDir/node_modules/react-native/android"
}

准备使用react native的模块修改build.gradle

android-defaultConfig节点 

ndk {
    abiFilters "armeabi-v7a"
}

android节点 

splits {
    abi {
        reset()
        enable enableSeparateBuildPerCPUArchitecture
        universalApk false  // If true, also generate a universal APK
        include "armeabi-v7a"
    }
}

离线bundle打包脚本

react-native bundle --platform android --dev false --entry-file ReactSectionGroupActivityForReactRootView.js --bundle-output android/allbundle/index.android.bundle --assets-dest android/app/build/intermediates/res/merged/debug

核心类的创建

private static ReactInstanceManager createReactInstanceManager(Application application){
    List<ReactPackage> mReactPackages = new ArrayList<>();
    mReactPackages.add(new MainReactPackage());
    mReactPackages.add(new AspirinAppPackage());
    ReactInstanceManager reactInstanceManager = ReactInstanceManager.builder()
            .setApplication(application)
            .setBundleAssetName("index.android.bundle")
            .setJSMainModulePath("ReactSectionGroupActivityForReactRootView")
            .addPackages(mReactPackages)
            .setUseDeveloperSupport(isDebug())
            .setInitialLifecycleState(LifecycleState.RESUMED)
            .build();
    return reactInstanceManager;
}

index.android.bundle为打包时生成的离线bundle的名字
ReactSectionGroupActivityForReactRootView为入口js,也就是打开时去加载的js文件名
isDebug()为false才会加载asset中的离线bundle,为true时,会去访问本地服务端 

网上文章:

Android项目集成React Native实践总结
so报空-react-native植入原生应用-踩坑记

关于so加载不到,libgnustl_shared.so加载错误的处理

异常如下

05-14 13:40:12.578 1506-26433/? E/ActivityTrigger: activityStartTrigger: not whiteListedcn.dxy.aspirin.reactnative.demo/cn.dxy.aspirin.reactnative.ReactSectionGroupActivityForReactRootView/1
05-14 13:40:12.579 1506-26433/? E/ActivityTrigger: activityResumeTrigger: not whiteListedcn.dxy.aspirin.reactnative.demo/cn.dxy.aspirin.reactnative.ReactSectionGroupActivityForReactRootView/1
05-14 13:40:12.585 1506-26433/? E/ActivityTrigger: activityResumeTrigger: not whiteListedcn.dxy.aspirin.reactnative.demo/cn.dxy.aspirin.reactnative.ReactSectionGroupActivityForReactRootView/1
05-14 13:40:12.832 12756-12810/cn.dxy.aspirin.reactnative.demo E/AndroidRuntime: FATAL EXCEPTION: Thread-2
                                                                                 Process: cn.dxy.aspirin.reactnative.demo, PID: 12756
                                                                                 java.lang.UnsatisfiedLinkError: dlopen failed: "/data/data/cn.dxy.aspirin.reactnative.demo/lib-main/libgnustl_shared.so" is 32-bit instead of 64-bit
                                                                                     at java.lang.Runtime.load0(Runtime.java:928)
                                                                                     at java.lang.System.load(System.java:1621)
                                                                                     at com.facebook.soloader.DirectorySoSource.loadLibraryFrom(DirectorySoSource.java:71)
                                                                                     at com.facebook.soloader.DirectorySoSource.loadLibrary(DirectorySoSource.java:42)
                                                                                     at com.facebook.soloader.SoLoader.loadLibraryBySoName(SoLoader.java:299)
                                                                                     at com.facebook.soloader.DirectorySoSource.loadLibraryFrom(DirectorySoSource.java:65)
                                                                                     at com.facebook.soloader.DirectorySoSource.loadLibrary(DirectorySoSource.java:42)
                                                                                     at com.facebook.soloader.SoLoader.loadLibraryBySoName(SoLoader.java:299)
                                                                                     at com.facebook.soloader.DirectorySoSource.loadLibraryFrom(DirectorySoSource.java:65)
                                                                                     at com.facebook.soloader.DirectorySoSource.loadLibrary(DirectorySoSource.java:42)
                                                                                     at com.facebook.soloader.SoLoader.loadLibraryBySoName(SoLoader.java:299)
                                                                                     at com.facebook.soloader.DirectorySoSource.loadLibraryFrom(DirectorySoSource.java:65)
                                                                                     at com.facebook.soloader.DirectorySoSource.loadLibrary(DirectorySoSource.java:42)
                                                                                     at com.facebook.soloader.SoLoader.loadLibraryBySoName(SoLoader.java:299)
                                                                                     at com.facebook.soloader.SoLoader.loadLibrary(SoLoader.java:247)
                                                                                     at com.facebook.react.bridge.ReactBridge.staticInit(ReactBridge.java:18)
                                                                                     at com.facebook.react.bridge.NativeMap.<clinit>(NativeMap.java:19)
                                                                                     at com.facebook.react.bridge.JSCJavaScriptExecutorFactory.create(JSCJavaScriptExecutorFactory.java:21)
                                                                                     at com.facebook.react.ReactInstanceManager$5.run(ReactInstanceManager.java:912)
                                                                                     at java.lang.Thread.run(Thread.java:764)
05-14 13:40:12.852 1506-1592/? E/ANDR-PERF-JNI: com_qualcomm_qtiperformance_native_perf_io_prefetch_start
05-14 13:40:12.852 747-747/? E/ANDR-IOP: IOP HAL: Received pkg_name = cn.dxy.aspirin.reactnative.demo pid = 12756
05-14 13:40:12.853 747-810/? E/ANDR-IOP: io prefetch Capture is deactivated
05-14 13:40:12.860 1914-1914/? E/Icon: Unable to load resource 0x00000000 from pkg=com.android.systemui
                                       android.content.res.Resources$NotFoundException: Resource ID #0x0
                                           at android.content.res.ResourcesImpl.getValueForDensity(ResourcesImpl.java:220)
                                           at android.content.res.Resources.getDrawableForDensity(Resources.java:889)
10-03 14:44