發現今年React Native發展的挺好了,已經有很多公司開始使用RN來開發,我看慕課網也出了好多RN的視訊以及實戰課程。之前我也自學了RN,感覺上手入門還是挺快的,也打算繼續學習下去。下面開始切入正題:
假如你的Android原生項目已經寫了很多,接下來想使用RN實作一些頁面功能,那麼該怎麼接入呢,其實React Native中文網也有參考,但是我當時按照那篇文章來操作還是有些問題的,感覺不适合初學者來使用,接下來我打算記錄一些我自己的接入過程。希望能給一些正在搞這個的朋友作出一些參考
接入的前提條件:
- 已有Android原生的工程
- RN環境已經搭建好
本篇文章的所記錄的知識點有:
- Android工程加載RN頁面
- RN來調用原生的方法,如果需要傳回結果,則可以回調
先大緻浏覽一下,接入RN之後Android工程的樣子:
接下來開始實作接入
- 步驟一: 用android studio打開已經建立好的Android工程,然後在Terminal中執行一下指令:
- npm init
- npm install –save react
- npm install –save react-native
- curl -o .flowconfig https://raw.githubusercontent.com/facebook/react-native/master/.flowconfig
說明,在建立 npm init的時候,需要輸入name、version等值,這裡可以随意先輸入一下,貌似在輸入name的值隻能用英文的小寫字母表示,其實這個無所謂的,可以将其他的React Native工程中的package.json檔案Copy過來,将裡面的name等值改一下就ok了,還省事。 其他3個指令,依次敲回車執行即可。然後再從React Native工程中Copy一份index.android.js,注意的是,需要将
第二個參數與package.json的name值保持一緻(我這裡改的是RNComponent,這個名字可以起别的也行)AppRegistry.registerComponent('RNComponent', () => RNComponent);
- 步驟二:這樣React Native的工作差不多完成了,然後Android端需要建立一個Activity來裝載所有的RN頁面,這裡我建立了MyReactActivity。在建立之前,需要在Android中添加一些依賴,否則用到一些類找不到,下面先開始配置Android工程
step1. 在整個工程的build.gradle中添加 :
allprojects { repositories { mavenLocal() jcenter() maven { // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm url "$rootDir/node_modules/react-native/android" } } } task clean(type: Delete) { delete rootProject.buildDir }
step2. 在moudle的build.gradle中添加
具體參考我的builde.gradle配置,如下所示compile "com.facebook.react:react-native:+"
android { compileSdkVersion buildToolsVersion "25.0.2" defaultConfig { applicationId "com.reactnative" minSdkVersion targetSdkVersion versionCode versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } splits { abi { reset() enable true universalApk true // If true, also generate a universal APK include "armeabi-v7a", "x86" } } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } configurations.all { resolutionStrategy.force 'com.google.code.findbugs:jsr305:3.0.0' } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) compile 'com.android.support:appcompat-v7:25.0.1' testCompile 'junit:junit:4.12' compile "com.facebook.react:react-native:+" // From node_modules }
- 緊接着上面的步驟二,開始建立MyReactActivity,代碼如下;
import com.facebook.react.ReactActivity;
public class MyReactActivity extends ReactActivity{
@Override
protected String getMainComponentName() {
return "RNComponent";
}
}
還有一種建立方式,就是下面這樣的,其中這兩種建立的MyReactActivity都是可以的,因為ReactActivity也是實作DefaultHardwareBackBtnHandler接口。
public class MyReactActivity extends Activity implements DefaultHardwareBackBtnHandler {
private ReactRootView mReactRootView;
private ReactInstanceManager mReactInstanceManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mReactRootView = new ReactRootView(this);
mReactInstanceManager = ReactInstanceManager.builder()
.setApplication(getApplication())
.setBundleAssetName("index.android.bundle")
.setJSMainModuleName("index.android")
.addPackage(new MainReactPackage())
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
mReactRootView.startReactApplication(mReactInstanceManager, "RNComponent", null);
setContentView(mReactRootView);
}
@Override
public void invokeDefaultOnBackPressed() {
super.onBackPressed();
}
}
- 接下來開始建立 MainApplication,代碼如下
public class MainApplication extends Application implements ReactApplication {
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()
//将我們建立的包管理器給添加進來
);
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
}
}
- ok,最後在AndroidManifest檔案中,添加一些權限,以及聲明MainApplication跟MyReactActivity
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.reactnative">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW"/>
<application
android:name=".MainApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name=".MyReactActivity"
android:configChanges="orientation|screenSize"
android:theme="@style/NoActionBar"
/>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity"/>
</application>
</manifest>
這裡需要說明一下,在聲明MyReactActivity的時候,需要給它指定一個沒有ActionBar的樣式,在styles.xml中作修改:
<style name="NoActionBar" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
到此為止,不出意外,一般都能正常加載RN頁面,效果如下:
到這裡 簡單的接入已經完成了,但是正常項目中,接入RN之後,RN肯定還要調用原生的一些方法,并且希望能拿到結果。比方說,RN想要得到系統相冊某些圖檔的路徑,那麼Android端就能提供查找的方法,并且将這個結果回調給RN
接下來開始實作 互動的過程
- 第一步,需要寫個類來繼承ReactContextBaseJavaModule
package com.reactnative;
import android.content.Context;
import android.text.TextUtils;
import android.widget.Toast;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.uimanager.IllegalViewOperationException;
public class MyNativeModule extends ReactContextBaseJavaModule {
private Context mContext;
public MyNativeModule(ReactApplicationContext reactContext) {
super(reactContext);
mContext = reactContext;
}
@Override
public String getName() {
//傳回的這個名字是必須的,在rn代碼中需要這個名字來調用該類的方法。
return "MyNativeModule";
}
//函數不能有傳回值,因為被調用的原生代碼是異步的,原生代碼執行結束之後隻能通過回調函數或者發送資訊給rn那邊。
@ReactMethod
public void rnCallNative(String msg) {
Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show();
}
/**
* 建立給js調用的方法 将網絡請求的結果以回調的方式傳遞給js
*
* @param url
* @param callback
*/
@ReactMethod
public void getResult(String url, final Callback callback) {
new Thread(new Runnable() {
@Override
public void run() {
try {
// 模拟網絡請求資料的操作
String result = "我是請求結果";
callback.invoke(true, result);
} catch (Exception e) {
}
}
}).start();
}
@ReactMethod
public void tryCallBack(String name, String psw, Callback errorCallback, Callback successCallback) {
try {
if (TextUtils.isEmpty(name) && TextUtils.isEmpty(psw)) {
// 失敗時回調
errorCallback.invoke("user or psw is empty");
}
// 成功時回調
successCallback.invoke("add user success");
} catch (IllegalViewOperationException e) {
// 失敗時回調
errorCallback.invoke(e.getMessage());
}
}
/**
* 回調給android端的資料
*
* @param callback
*/
@ReactMethod
public void renderAndroidData(Callback callback) {
callback.invoke("android data");
}
}
說明,這裡第一個需要注意的地方是getName()方法,傳回的這個别名,将來在RN的js中需要用到這個别名來調原生的方法。第二個地方,凡是需要RN調用的方法,都需要加注解。第三個地方,原生提供的方法的傳回值類型都是void類型,比較簡單的如
@ReactMethod
, RN那邊調用,就是吐司一下。如果需要傳回結果就需要回調,如
rnCallNative(String msg)
renderAndroidData(Callback callback)
ok,原生方法算是寫好了,接下來需要建立一個類來實作ReactPackage,将我們建立的類添加進原生子產品清單中。如下所示:
public class MyReactPackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
//将我們建立的類添加進原生子產品清單中
modules.add(new MyNativeModule(reactContext));
return modules;
}
@Override
public List<Class<? extends JavaScriptModule>> createJSModules() {
//傳回值需要修改
return Collections.emptyList();
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
//傳回值需要修改
return Collections.emptyList();
}
}
還需要在MainApplication中,将我們建立的包管理器給添加進來 ,如下所示:
public class MainApplication extends Application implements ReactApplication {
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()
);
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
}
}
這樣的話,原生子產品算是寫好了,接下來RN來調用Native方法,将index.android.js中可以這樣調用:
- 首先 import { NativeModules} from ‘react-native’;
- 調用原生方法可以這麼去寫
NativeModules.MyNativeModule.rnCallNative('調用原生方法的Demo');
展示 index.android.js 全部代碼;
export default class RNComponent extends Component {
// 構造
constructor(props) {
super(props);
// 初始狀态
this.state = {
title: '',
};
}
render() {
tryCall = () => {
var rnToastAndroid = NativeModules.MyNativeModule;
rnToastAndroid.tryCallBack("luo", "131", (errorCallback) => {
alert(errorCallback)
},
(successCallback) => {
alert(successCallback);
});
};
androidback = () => {
var ANdroidNative = NativeModules.MyNativeModule;
ANdroidNative.renderAndroidData((Callback) => {
alert(Callback);
});
};
return (
<View style={styles.container}>
<Text style={styles.welcome}
onPress={this.call_button.bind(this)}
>
React Native 調用原生方法!
</Text>
<Text style={styles.instructions}
onPress={()=>androidback()}
>
獲得android回調的資料
</Text>
<Text style={styles.instructions}>
{NativeModules.MyNativeModule.rnCallNative(this.state.title)}
</Text>
<Text style={styles.instructions} onPress={()=>tryCall()}>
trycallAndroid
</Text>
</View>
);
}
call_button() {
NativeModules.MyNativeModule.rnCallNative('調用原生方法的Demo');
}
}
const styles = StyleSheet.create({
container: {
flex: ,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: ,
textAlign: 'center',
margin: ,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: ,
},
});
AppRegistry.registerComponent('RNComponent', () => RNComponent);