React-Native實(shí)例

前言

移動(dòng)開(kāi)發(fā)也走過(guò)了將近10年的歷程,開(kāi)發(fā)的技術(shù)也隨著移動(dòng)設(shè)備各個(gè)版本的迭代發(fā)生了巨大的變化,不管是iphone X和android 8.X和最初的iphone 1以及android1.X,無(wú)疑都發(fā)生了巨大的變化,哪怕一兩年的節(jié)點(diǎn)之間,移動(dòng)設(shè)備的軟硬件都會(huì)產(chǎn)生巨大的變化。于此同時(shí),開(kāi)發(fā)技術(shù)和模式也有了長(zhǎng)足的發(fā)展,從早前的純native開(kāi)發(fā),到后來(lái)的webview+html的hybrid native開(kāi)發(fā)模式,以及現(xiàn)在以React-Native、Weex等融合前端技術(shù)的DyNative模式,以及Google推出instant run之后各大公司開(kāi)發(fā)的熱修復(fù)框架、相應(yīng)開(kāi)發(fā)場(chǎng)景的開(kāi)源框架等等,目標(biāo)都是一致的,在合適的開(kāi)發(fā)場(chǎng)景運(yùn)用相應(yīng)最合適的技術(shù),為用戶帶來(lái)最好的使用體驗(yàn),這篇文章就已我當(dāng)前的項(xiàng)目的一小部分改造來(lái)聊聊React-Native的一些特性。

React-Native的優(yōu)缺點(diǎn)

下圖是幾種開(kāi)發(fā)模式的描述圖:

三種模式.png

從上圖描述中能得到幾個(gè)關(guān)鍵的特性,1、性能比WebView+Html的傳統(tǒng)APP要好。2、開(kāi)發(fā)體系不完善。對(duì)于第一點(diǎn),傳統(tǒng)的webview實(shí)際上是基于webkit的微型瀏覽器,通過(guò)webkit的內(nèi)核對(duì)頁(yè)面進(jìn)行渲染,由于webkit本身就是一個(gè)重量級(jí)內(nèi)核,執(zhí)行效率一般,React-Native較高的效率的原因是由于RN基于虛擬DOM技術(shù),虛擬DOM技術(shù)指的是使用javascript Object模擬DOM樹(shù),計(jì)算變更,減少重繪,提升效率的一種技術(shù)實(shí)現(xiàn)。對(duì)于第二點(diǎn),由于React-Native是基于前端的跨平臺(tái)的框架,因此很難做到平衡各平臺(tái)之間的特性,往往會(huì)出現(xiàn)各種各樣的問(wèn)題,例如某些特性IOS上有,Android平臺(tái)不支持,或者各平臺(tái)表現(xiàn)不一致,這都是開(kāi)發(fā)者需要面對(duì)的問(wèn)題。

總的來(lái)講,按我個(gè)人的理解,React-Native最大的優(yōu)點(diǎn)在于兩點(diǎn),第一,能夠統(tǒng)一各大平臺(tái)的UI顯示(這點(diǎn)有點(diǎn)類似于BootStrap);第二,執(zhí)行效率較傳統(tǒng)webapp要快,能一定程度提升用戶體驗(yàn);第三,較于原生開(kāi)發(fā),能夠支持熱更新。缺點(diǎn)在于,React-Native并不能簡(jiǎn)化開(kāi)發(fā)流程,減少開(kāi)發(fā)量,React-Native提出的“Learn Once ,Write Anywhere”口號(hào)描繪的開(kāi)發(fā)者基于React-Native的移動(dòng)app能運(yùn)行于android、ios平臺(tái)能打破傳統(tǒng)一個(gè)平臺(tái)至少一個(gè)開(kāi)發(fā)者的愿景是不太可能實(shí)現(xiàn)的,原因無(wú)他,因?yàn)榧幢闶莂ndroid或者ios自家平臺(tái)的不同系統(tǒng)版本都無(wú)法做到完全適配,目前使用Dynative的互聯(lián)網(wǎng)公司的配置一般是前端組、android組、ios組,前端組專門負(fù)責(zé)這些頁(yè)面的繪制、邏輯和于各平臺(tái)的native開(kāi)發(fā)人員進(jìn)行交互。再一個(gè)開(kāi)發(fā)體系不完善,不同平臺(tái)之間不能做到完全無(wú)差異化,以及對(duì)native特性的完全支持。

本篇文章要達(dá)到的目標(biāo)

目前我從事的項(xiàng)目執(zhí)行效果如下:

手機(jī)端
PC端

本篇文章的目標(biāo)就是將手機(jī)端的主頁(yè)面改造成React-Native,模擬Native端發(fā)送連接信息,改變主頁(yè)連接信息的顯示以及React-Native的主頁(yè)點(diǎn)擊掃描按鈕調(diào)用Native端的掃碼頁(yè)面。

準(zhǔn)備工作:

React-Native的配置:

環(huán)境的搭建可以參考React-Native 入門(環(huán)境搭建及運(yùn)行)

但是大多數(shù)時(shí)候第一運(yùn)行結(jié)果都會(huì)報(bào)錯(cuò)

運(yùn)行失敗.png

總結(jié)報(bào)錯(cuò)的原因,主要原因有以下幾點(diǎn):

本地的端口和真機(jī)或模擬器的ip未設(shè)置好。

解決辦法:

adb reverse tcp:8081 tcp:8081,然后在項(xiàng)目運(yùn)行后搖動(dòng)手機(jī)進(jìn)入設(shè)置頁(yè)面,設(shè)置項(xiàng)目的ip端口,如進(jìn)入不了設(shè)置頁(yè)面。

需要將設(shè)置頁(yè)面的聲明加入到manifest

Could not get BatchedBridge, make sure your bundle is packaged correctly:

解決辦法:

在app/src/main目錄下新建assets文件夾,然后啟動(dòng)cmd,回到項(xiàng)目根目錄執(zhí)行

react-native bundle --platform android --dev false --entry-file index.android.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/main/res

react-native服務(wù)沒(méi)有啟動(dòng)

解決方法:

在terminal里面執(zhí)行npm start啟動(dòng)react-native服務(wù)

React-Native集成方案

一種方式是在原有布局中添加com.facebook.react.ReactRootView,可以參考現(xiàn)有Android項(xiàng)目引入ReactNative--九步大法

但是這種方式的話,不管是通過(guò)adb的命令還是搖晃手機(jī)都無(wú)法調(diào)出DevSettingActivity的設(shè)置界面,無(wú)法修改真機(jī)調(diào)試的ip,涉及到修改配置腳本,較為麻煩,推薦使用Activity繼承ReactActivity的方式加載React-Native頁(yè)面。


public class XXXActivity extends ReactActivity{

@Override

protected String getMainComponentName() {

return "XXX";

}

在項(xiàng)目中引入React-Native中常見(jiàn)錯(cuò)誤:
Cannot resolve ReactApplication或者ReactNativeHost。
解決辦法:

allprojects {
    repositories {
        mavenLocal()
        jcenter()
        maven {
            url 'http://maven.aliyun.com/nexus/content/groups/public/'//用國(guó)內(nèi)aliyun、不用google
        }

        maven {
            // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
            url "$rootDir/node_modules/react-native/android"
        }
        google()
    }
}

rootDir/node_modules/react-native/android路徑配置錯(cuò)誤 ,很多教程把這個(gè)路徑配置成url "rootDir/../node_modules/react-native/android",這個(gè)路徑要配置成根目錄和node_modules之間的正確路徑,當(dāng)前項(xiàng)目的真實(shí)路徑是怎樣的就該怎樣配置。否則會(huì)出現(xiàn)加載錯(cuò)誤的react-native的本地庫(kù),導(dǎo)致ReactApplication解析錯(cuò)誤。

代碼改造

完整的js代碼如下。

import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View,Image,Dimensions,PixelRatio,NativeModules,DeviceEventEmitter,Alert,TouchableWithoutFeedback,
} from 'react-native';
const deviceWidth = Dimensions.get('window').width;
const deviceHeight = Dimensions.get('window').height;

const px2dp = px=>PixelRatio.roundToNearestPixel(px);
const instructions = Platform.select({
  ios: 'Press Cmd+R to reload,\n' + 'Cmd+D or shake for dev menu',
  android:
    'Double tap R on your keyboard to reload,\n' +
    'Shake or press menu button for dev menu',
});

type Props = {};
export default class LmsaHomePage extends Component<Props> {
constructor(props) {
    super();
    this.state = {
        connect_img: require('.././image/connect_disconnected.png'),
        connect_text:'Device Disconnected',
        connect_color:'#6D6D6D',
        model_name:'Welcome',
    };

}


componentWillMount(){
    DeviceEventEmitter.addListener('LmsaConnect',this.handleConnectMessage);
    DeviceEventEmitter.addListener('LmsaModelName',this.handleModelMessage);
  }

  componentWillunMount(){
     DeviceEventEmitter.remove('LmsaConnect',this.handleConnectMessage);
     DeviceEventEmitter.remove('LmsaModelName',this.handleModelMessage);
  }
  handleConnectMessage=(msg)=>{
       //RN端獲得native端傳遞的數(shù)據(jù)
       if(msg=='USB_CONNECTING'||msg=='USB_CONNECED')
       {
       /*this.refs.ref_connect_img.setNativeProps({
                                            *//*source: {require('.././image/new_version_usb.png')}*//*style:{width:200,}
                                        });*/
           this.setState({
                           connect_img: require('.././image/new_version_usb.png'),
                           connect_text:msg,
                           connect_color:'#04E861',
                       });
       }
  }
  handleModelMessage=(msg)=>{
       //RN端獲得native端傳遞的數(shù)據(jù)
      this.setState({
                                 model_name: msg,
                                 connect_text: 'USB_CONNECTING',
                                 connect_color:'#04E861',
                             });
  }
  goToScanActivity() {
         //alert('gotoscan');
         NativeModules.homePageReactNativeModule.scanToConnect(phone);
  }
  render() {
    return (
      <View style={styles.container}>
        <View style={styles.topframe}>
        <Image source={require('.././image/home_page_top_bg_new.png')}
         style={styles.topframeBg}
         />
         <Image source={require('.././image/home_page_lmsa_log.png')}
         style={styles.topframeLogo}
         />
         <Text style={{position:'absolute',fontSize:deviceWidth*0.04,color:'#FFFFFF',marginTop:deviceHeight*0.17}}>{this.state.model_name}</Text>
         
        </View>
        <Image source={this.state.connect_img/*require('.././image/connect_disconnected.png')*/}
         style={styles.topframeConnect} ref={'ref_connect_img'}/*{(c) => this.ref_connect_img = c}*/
         />
         <Text style={{position:'absolute',fontSize:deviceWidth*0.042,color:this.state.connect_color,marginTop:deviceHeight*0.2428,marginLeft:deviceWidth*0.346}}>{this.state.connect_text}</Text>
         <Image source={require('.././image/home_page_connect_bg.png')}
         style={styles.topframeConnectBg}
         />
         <TouchableWithoutFeedback
             onPress={()=> {
                  NativeModules.homePageReactNativeModule.scanToConnect();
             }}
         >
         <Image source={require('.././image/home_page_scan_30.png')}
         style={styles.topframeConnectScan}
         />
         </TouchableWithoutFeedback>
         <Text style={{position:'absolute',fontSize:deviceWidth*0.035,color:'#3E8DDC',marginTop:deviceHeight*0.316,marginLeft:deviceWidth*0.42}}>Scan to Connect</Text>
        <View style={styles.mainframe}>
            <View style={styles.mainframe_colomn}>
                <View style={styles.mainframe_colomn_item}><Image source={require('.././image/home_page_new_version_device_info.png')} style={styles.mainframe_colomn_item_image}/>
                <Text style={{fontSize:deviceWidth*0.04,color:'#3E8DDC',marginTop:deviceHeight*0.046}}>Device Info.</Text></View>
                <View style={styles.mainframe_colomn_item}><Image source={require('.././image/home_page_new_version_cpu_info.png')} style={styles.mainframe_colomn_item_image}/>
                <Text style={{fontSize:deviceWidth*0.04,color:'#3E8DDC',marginTop:deviceHeight*0.046}}>CPU Info.</Text></View>
            </View>
            <View style={styles.mainframe_colomn}>
                <View style={styles.mainframe_colomn_item}><Image source={require('.././image/home_page_new_version_hw_detection.png')} style={styles.mainframe_colomn_item_image}/>
                <Text style={{fontSize:deviceWidth*0.04,color:'#3E8DDC',marginTop:deviceHeight*0.046}}>HW Detection</Text></View>
                <View style={styles.mainframe_colomn_item}><Image source={require('.././image/home_page_new_version_rom_clean.png')} style={styles.mainframe_colomn_item_image}/>
                <Text style={{fontSize:deviceWidth*0.04,color:'#3E8DDC',marginTop:deviceHeight*0.046}}>ROM CleanwIII</Text></View>
            </View>
        </View>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
  topframe: {
    flex:4,
    
    alignItems: 'center',
  },
  mainframe: {
    flex:6,
    justifyContent: 'center',
    alignItems: 'center',   
    backgroundColor:'rgba(0,0,0,0)',
    flexDirection: 'column',
  },
  topframeBg: {
      flex:1,
        alignItems:'center',
        justifyContent:'center',
       resizeMode :'contain',
        
        
        backgroundColor:'rgba(0,0,0,0)',
  },
  topframeLogo: {
    alignItems:'center',
    justifyContent:'center',
    position:'absolute',
     marginTop:deviceHeight*0.053,
      width:deviceWidth*0.158,
      height:deviceWidth*0.158,
  },
  topframeConnect: {
     
    position:'absolute',
     marginTop:deviceHeight*0.24,
     marginLeft:deviceWidth*0.26,
      width:deviceWidth*0.07,
      height:deviceWidth*0.07,
  },
  topframeConnectBg: {
      position:'absolute',
     marginTop:deviceHeight*0.298,
     alignItems:'center',
     marginLeft:deviceWidth*0.3,
     justifyContent:'center',
      width:deviceWidth*0.405,
      height:deviceWidth*0.104,
  },
  topframeConnectScan: {
       position:'absolute',
     marginTop:deviceHeight*0.312,
     alignItems:'center',
     marginLeft:deviceWidth*0.335,
     justifyContent:'center',
      width:deviceWidth*0.063,
      height:deviceWidth*0.063,
  },
  mainframe_colomn: {
    flex:1,  
    flexDirection: 'row',
  },
  mainframe_colomn_item: {
      flex:1,
      alignItems:'center',
      
      flexDirection: 'column',
      backgroundColor:'rgba(0,0,0,0)',
      borderWidth: 0.3,
      borderTopWidth: 0,
      borderLeftWidth: 0,
      borderColor:'#7C7C7C', 
  },
  mainframe_colomn_item_image: {
      
      alignItems:'center',
     
      flexDirection: 'column',
      backgroundColor:'rgba(0,0,0,0)',
       
      marginTop:deviceHeight*0.095,
      width:deviceWidth*0.127,
      height:deviceWidth*0.127,
  },
});

代碼主要做了三件事,1完成首頁(yè)的js布局;2接受native發(fā)送過(guò)來(lái)連接信息改變text文字和圖片;3點(diǎn)擊掃描圖標(biāo),調(diào)用native方法跳轉(zhuǎn)本地掃碼界面。
js的布局主要根據(jù)當(dāng)前的屏幕寬度和高度對(duì)每個(gè)元素的寬高屬性進(jìn)行設(shè)置,保證機(jī)型適配;native頁(yè)面于react-native之間的數(shù)據(jù)交互原理可以參考React-Native數(shù)據(jù)交互
最終效果圖如下

效果圖.gif

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,037評(píng)論 25 709
  • 用兩張圖告訴你,為什么你的 App 會(huì)卡頓? - Android - 掘金 Cover 有什么料? 從這篇文章中你...
    hw1212閱讀 13,992評(píng)論 2 59
  • 評(píng)論——回復(fù)評(píng)論:走同一個(gè)編輯框 recycleAdapter.setOnItemClickListener(ne...
    自然之秋閱讀 484評(píng)論 0 0
  • 源碼 參考鏈接 http://www.cnblogs.com/52cik/p/js-regexp-s.html h...
    lousoandso閱讀 296評(píng)論 0 0
  • 姓名: 劉威 公司:瑞亨電子 365期感謝二組學(xué)員 【日精進(jìn)打卡第25天】 【知~學(xué)習(xí)】 ...
    劉威356期學(xué)員閱讀 132評(píng)論 0 0

友情鏈接更多精彩內(nèi)容