Android專案整合RN系列:修改埠號 / 離線包 / 訊息通訊
摘要:
【簡述RN整合到Android原生專案】
手動修改React Native埠號
將RN專案打包成離線包
...
不清楚Android專案如何整合RN,見【簡述RN整合到Android原生專案】
1.手動修改React Native埠號
-
修改ReactNative Server埠號
執行一個React Native專案的時候,React Native會啟動一個預設埠號為8081的本地服務,該8081的服務就是React Native專案的一個本地伺服器,用於提供JSBundle包和一些靜態資源。-
臨時修改Server埠號
通過cmd執行yarn start --port=8082
或者npm run start --port=8082
. -
永久修改Server埠號
在你的專案名稱/node_modules/react-native/local-cli/server/server.js
找到server.js
檔案,開啟後找到module.exports -> options -> command: '--port [number]'
修改對應的default的值。
修改前:{ command: '--port [number]', parse: (val: string) => Number(val), default: (config: ConfigT) => config.server.port, }
修改後:{ command: '--port [number]', parse: (val: string) => Number(val), default: 8082, }
-
臨時修改Server埠號
-
修改對應整合RN的Android 專案的埠號
-
方案一:程式碼中在application或者主activity中做如下設定:
@Override public void onCreate() { super.onCreate(); SharedPreferences mPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); mPreferences.edit().putString("debug_http_host","localhost:8082").commit(); }
-
方案二:React Native 會給 Android手機或者是虛擬機器安裝一個軟體,就是你的軟體,首先我們先開啟這個軟體,報錯頁面搖晃手機顯示選單,找到
Dev Settings
=>然後找到Debug server host & port for device
=> 然後輸入電腦的ip地址和埠 , 點選返回 => 頁面是空白,再次點選搖一搖,選擇Reload JS
程式就顯示出來了。
-
方案一:程式碼中在application或者主activity中做如下設定:
2. 打離線包,手動生成index.android.bundle 檔案儲存到assets 資料夾下或者本地儲存資料夾中。
react-native bundle --entry-file index.android.js --platform android --dev false --bundle-output ./app/src/main/assets/index.android.bundle --assets-dest ./app/src/main/res/
- --platform:平臺
- --dev:開發模式
- --entry-file:條目檔案
- --bundle-output:bundle檔案生成的目錄
-
--assets-dest:資原始檔生成的目錄
備註:
react-native bundle --entry-file index.js --platform android --dev false --bundle-output ./app/src/main/assets/index.android.bundle --assets-dest ./app/src/main/res/
不同業務對應的--entry-file檔案不一樣(有的是index.js,有的是index.android.js),打包時填寫正確的入口檔名,並且--bundle-output輸出的檔名也需要根據業務區分。
檢視打包命令
react-native bundle -h
3.RN與Android原生的訊息通訊
-
建立
MyNativeModule.java
package com.liujc.rnappdemo.hybride; import android.app.Activity; import android.content.Intent; import android.support.annotation.Nullable; import android.text.TextUtils; import android.util.Log; import android.widget.Toast; import com.facebook.react.bridge.Callback; import com.facebook.react.bridge.JSApplicationIllegalArgumentException; import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; import com.facebook.react.modules.core.DeviceEventManagerModule; import java.util.HashMap; import java.util.Map; /** * 類名稱:MyNativeModule * 建立者:Create by liujc * 描述:原生預留給RN的呼叫方法 */ public class MyNativeModule extends ReactContextBaseJavaModule { public static final String REACT_NATIVE_CLASSNAME = "MyNativeModule"; private ReactApplicationContext mContext; public static final String EVENT_NAME = "nativeCallRn"; public static final String TAG = "TAG"; public MyNativeModule(ReactApplicationContext reactContext) { super(reactContext); mContext = reactContext; } @Override public String getName() { //返回的這個名字是必須的,在rn程式碼中需要這個名字來呼叫該類的方法。 return REACT_NATIVE_CLASSNAME; } //函式不能有返回值,因為被呼叫的原生程式碼是非同步的,原生程式碼執行結束之後只能通過回撥函式或者傳送資訊給rn那邊。 @ReactMethod public void rnCallNative(String msg){ Toast.makeText(mContext,msg,Toast.LENGTH_SHORT).show(); } @ReactMethod public void startActivityRN(String name, String params) { try { Activity activity = getCurrentActivity(); if (activity != null) { Class toClass = Class.forName(name); Intent intent = new Intent(activity, toClass); intent.putExtra("params", params); activity.startActivity(intent); } } catch (Exception ex) { throw new JSApplicationIllegalArgumentException("不能開啟Activity " + ex.getMessage()); } } //後面該方法可以用Linking代替 @ReactMethod public void getDataFromIntent(Callback success, Callback error) { try { Activity currentActivity = getCurrentActivity(); String result = currentActivity.getIntent().getStringExtra("result");//會有對應資料放入 if (!TextUtils.isEmpty(result)) { success.invoke(result); } } catch (Exception ex) { error.invoke(ex.getMessage()); } } /** * Native呼叫RN * @param msg */ public void nativeCallRn(String msg) { mContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(EVENT_NAME,msg); } /** * Callback 方式 * rn呼叫Native,並獲取返回值 * @param msg * @param callback */ @ReactMethod public void rnCallNativeFromCallback(String msg, Callback callback) { String result = "hello RN!Native正在處理你的callback請求"; // .回撥RN,即將處理結果返回給RN callback.invoke(result); } /** * Promise * @param msg * @param promise */ @ReactMethod public void rnCallNativeFromPromise(String msg, Promise promise) { Log.e(TAG,"rnCallNativeFromPromise"); String result = "hello RN!Native正在處理你的promise請求" ; promise.resolve(result); } /** * 向RN傳遞常量 */ @Nullable @Override public Map<String, Object> getConstants() { Map<String,Object> params = new HashMap<>(); params.put("Constant","我是Native常量,傳遞給RN"); return params; } }
-
建立
MyReactPackage.java
/** * 類名稱:MyReactPackage * 建立者:Create by liujc * 描述:RN包管理器 */ public class MyReactPackage implements ReactPackage { public MyNativeModule myNativeModule; @Override public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) { myNativeModule = new MyNativeModule(reactContext); List<NativeModule> modules = new ArrayList<>(); //將我們建立NativeModule新增進原生模組列表中 modules.add(myNativeModule); return modules; } @Override public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) { //該處後期RN呼叫原生控制元件或自定義元件時可用到 return Collections.emptyList(); } }
-
將我們建立的
MyReactPackage
新增到我們在AppApplication
中建立的ReactNativeHost
中。private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { @Override public boolean getUseDeveloperSupport() { return BuildConfig.DEBUG; } @Override protected List<ReactPackage> getPackages() { return Arrays.<ReactPackage>asList( new MainReactPackage(), //將我們建立的包管理器給新增進來 new MyReactPackage() ); } };
-
接下來我們在js檔案中如何呼叫?
'use strict'; import React, { Component } from 'react'; import { AppRegistry, StyleSheet, Text, NativeModules, View, ToastAndroid, DeviceEventEmitter } from 'react-native'; let title = 'React Native介面'; class reactNative extends Component { /**載入完成*/ componentWillMount() { let result = NativeModules.MyNativeModule.Constant; console.log('原生端返回的常量值為:' + result); } /** * 原生呼叫RN */ componentDidMount() { DeviceEventEmitter.addListener('nativeCallRn',(msg)=>{ title = "React Native介面,收到資料:" + msg; ToastAndroid.show("傳送成功", ToastAndroid.SHORT); }) } /** * RN呼叫Native且通過Callback回撥 通訊方式 */ callbackComm(msg) { NativeModules.MyNativeModule.rnCallNativeFromCallback(msg,(result) => { ToastAndroid.show("CallBack收到訊息:" + result, ToastAndroid.SHORT); }) } /** * RN呼叫Native且通過Promise回撥 通訊方式 */ promiseComm(msg) { NativeModules.MyNativeModule.rnCallNativeFromPromise(msg).then( (result) =>{ ToastAndroid.show("Promise收到訊息:" + result, ToastAndroid.SHORT) } ).catch((error) =>{console.log(error)}); } /** * 呼叫原生程式碼 */ call_button(){ NativeModules.MyNativeModule.rnCallNative('呼叫原生方法操作'); } callNative(){ NativeModules.MyNativeModule.startActivityRN('com.liujc.rnappdemo.MyRNActivity','test'); } render() { return ( <View style={styles.container}> <Text style={styles.welcome} > {title} </Text> <Text style={styles.welcome} onPress={this.call_button.bind(this)} > React Native 呼叫原生方法操作! </Text> <Text style={styles.welcome} //給此處的文字繫結一個事件,其中callNative為要呼叫的方法名。 onPress={this.callNative.bind(this)}> 跳轉MyRNActivity! </Text> <Text style={styles.welcome} onPress={this.callbackComm.bind(this,'callback傳送啦')}> Callback通訊方式 </Text> <Text style={styles.welcome} onPress={this.promiseComm.bind(this,'promise傳送啦')}> Promise通訊方式 </Text> </View> ); } } 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('reactNative', () => reactNative);