前言 當前 React Native 雖說版本更新比較快,各種組件也提供的很全面了,但是在某些情況下,混合開發的方式才會快速縮短開發周期,原因無非就是原生平臺的“底蘊”無疑更深,擁有眾多且類型豐富的第三方支持庫。很多情況下,運用這些庫可以避免苦逼的重覆勞動。接下來我們以 "jpush react n ...
前言
當前 React Native 雖說版本更新比較快,各種組件也提供的很全面了,但是在某些情況下,混合開發的方式才會快速縮短開發周期,原因無非就是原生平臺的“底蘊”無疑更深,擁有眾多且類型豐富的第三方支持庫。很多情況下,運用這些庫可以避免苦逼的重覆勞動。接下來我們以 jpush-react-native 插件為例來看看在 React Native 中如何使用原生的第三方庫。
開始
在開始之前,你必須安裝以下軟體:npm 命令行工具,react-native 命令行工具, Android Studio。jpush-react-native 是極光推送提供的 React Native 版本插件,可以讓我們快速集成推送功能。實際上這個插件就是一個混合應用,使用他們自己的原生 SDK,並且封裝了一些介面,讓開發者可以在 JS 和原生平臺之間互相調用。接下來我們只需要三個步驟就可以完成絕大多數原生庫在 React Native 中的應用。
先明確兩個概念,在 Android Studio 中一個項目往往會包含很多模塊(Module),項目中的 build.gradle 配置一般對所有 module 生效。而 Module 中的 build.gradle 則配置了該 Module 所需的依賴或者任務等等。
第一步——安裝
在命令行中進入 React Native 項目,然後使用如下兩個命令即可完成 jpush-react-native 插件的安裝:
npm install jpush-react-native --save
rnpm link jpush-react-native
jpush-react-native 發佈到 npm 了,所以使用命令行可以輕鬆安裝。
然而有很多第三方庫可能需要原生的安裝方式。比如提供 jar 包或者 aar 包的方式在 Android 中很常見,也有可能以 maven 依賴的方式安裝。如果是以上方式安裝需要做一些調整:
- jar 包或者 aar 包的方式:
i. 將依賴包複製到 module 的 libs 文件夾下(如果沒有則需要手動創建)
ii. 在 build.gradle 中添加:
android {
...
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
...
}
...
dependencies {
compile fileTree(dir: "libs", include: ["*.jar"])
}
- 以 maven 依賴的方式:
i. 在項目的 build.gradle 中增加:
allprojects {
repositories {
...
mavenCentral()
maven {
url "https://oss.sonatype.org/content/repositories/snapshots/"
}
}
}
ii. 在 module 的 build.gradle 中添加:
dependencies {
...
compile 'xxx-SNAPSHOT'
}
其中的 xxx 指代 groupId、artifactId 以及版本號(以 : 分隔),一般都會由提供方給出。比如 ActiveAndroid:
compile 'com.michaelpardo:activeandroid:3.1.0-SNAPSHOT'
第二步——適配(配置)
這一步因第三方庫而異,其實大多都是大同小異,有的庫甚至不需要配置直接可以“搭建橋梁”使用。jpush-react-native 還是要配置一下的
首先在命令行中運行腳本:
// 將 AppKey 和 Module Name 替換成自己的
npm run configureJPush [AppKey] [Module Name]
- 配置 AndroidManifest
<meta-data android:name="JPUSH_CHANNEL" android:value="${APP_CHANNEL}"/>
<meta-data android:name="JPUSH_APPKEY" android:value="${JPUSH_APPKEY}"/>
將以上代碼複製到你 Module 的 AndroidManifest 下即可。
- 配置 build.gradle
打開與 AndroidManifest 同級的 build.gradle 文件,做以下改動:
android {
...
defaultConfig {
// 換成自己的包名
applicationId "com.xxx"
...
manifestPlaceholders = [
JPUSH_APPKEY: "xxx", //在此替換你的AppKey,極光官網註冊應用獲得
APP_CHANNEL: "developer-default" //應用渠道號
]
}
}
到此配置完成。
第三步 搭建橋梁
這一步是最後也是最核心的一步。“搭建橋梁”主要是在 Native 側提供一些 @ReactMethod 標簽的方法,或者在 Native 中處理後回調到 JS,說白了就是要使得 JS 和 Native 可以相互調用。這也是混合開發的優勢所在,原生平臺提供的庫,我們只需要搭建一下橋梁,就可以拿來使用。只要稍微寫一點原生的代碼,可以省去我們絕大部分工作。許多剛接觸 React Native 的人不知道如何在 Native 中打開 JS 的界面(眾所周知,JS 的界面由一個個 Component 組成,有自己的路由系統)後面我會寫一個簡單例子,用 Native 的方式聲明註冊界面(在 Android 中即為 Activity),然後用 JS 渲染界面,這個問題就迎刃而解了。接下來還是先看看 jpush-react-native 的例子。
首先在你的 Module 下創建一個 ManiActivity 以及 MainApplication 類。RN 0.29 後,需要在 MainApplication 中添加原生模塊。
MainActivity.java
public class MainActivity extends ReactActivity implements DefaultHardwareBackBtnHandler {
@Override
protected String getMainComponentName() {
// 這裡返回的名字要與 JS 側註冊的 Component 名字一致
return "PushDemoApp";
}
@Override
protected void onPause() {
super.onPause();
JPushInterface.onPause(this);
}
@Override
protected void onResume() {
super.onResume();
JPushInterface.onResume(this);
}
}
接下來需要在 MainApplication 中增加原生模塊
MainApplication.java
public class MainApplication extends Application implements ReactApplication {
private boolean SHUTDOWN_TOAST = false;
private boolean SHUTDOWN_LOG = false;
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
protected boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new JPushPackage(SHUTDOWN_TOAST, SHUTDOWN_LOG)
);
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
}
這樣就完成了。在 Android Studio 中 sync 一下,可以看到 jpush-react-native 作為 Library Module 導進來了。打開 JPushModule 類,看到其中的 onReceive 方法,通知的處理都在這一塊。在極光推送後臺發送通知(也可以使用 服務端 sdk)後,客戶端 sdk 收到通知後會回調到 onReceive 方法,在 onReceive 中可以做一些定製化的操作。比如收到通知後,點擊通知打開特定的界面:
public static class JPushReceiver extends BroadcastReceiver {
public JPushReceiver() {
HeadlessJsTaskService.acquireWakeLockNow(mRAC);
}
@Override
public void onReceive(Context context, Intent data) {
...
} else if (JPushInterface.ACTION_NOTIFICATION_OPENED.equals(data.getAction())) {
Logger.d(TAG, "用戶點擊打開了通知");
Intent intent = new Intent();
intent.setClassName(context.getPackageName(), context.getPackageName() + ".MainActivity");
intent.putExtras(bundle);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
// 如果需要跳轉到指定的界面,那麼需要同時啟動 MainActivity 及指定界面(SecondActivity):
// If you need to open appointed Activity, you need to start MainActivity and
// appointed Activity at the same time.
Intent detailIntent = new Intent();
detailIntent.setClassName(context.getPackageName(), context.getPackageName() + ".SecondActivity");
detailIntent.putExtras(bundle);
Intent[] intents = {intent, detailIntent};
// 同時啟動 MainActivity 以及 SecondActivity
context.startActivities(intents);
// 或者回調 JS 的某個方法
}
}
...
@ReactMethod
public void finishActivity() {
Activity activity = getCurrentActivity();
if (activity != null) {
activity.finish();
}
}
上面的例子中,我們在收到點擊通知的事件時,打開一個特定的界面。這個界面在 Native 中創建(這樣做的好處在於還可以實現一些特定的需求,比如收到通知後,如果存在 SecondActivity,則讓位於 SecondActivity 之上的界面全部彈出,如果不存在,則創建等等之類的需求),但是還是用 JS 的代碼來渲染界面,這對於熟悉 JS 的同學來說,再好不過。要做到這一點,首先我們創建一個 SecondActivity (與 MainActivity 同級)類:
SecondActivity.java
public class SecondActivity extends ReactActivity {
@Override
protected String getMainComponentName() {
// 註意這個名字與 JS 對應的 Component 中
// AppRegistry.registerComponent 方法的第一個參數相同
return "SecondActivity";
}
}
然後在 AndroidManifest 註冊 SecondActivity:
AndroidManifest
<activity android:name=".SecondActivity" />
在 React Native 項目下新建一個文件夾 react-native-android,專門用來存放 js 相關的文件。新建 second.js 文件:
second.js
'use strict';
import React from 'react';
import ReactNative from 'react-native';
const {
AppRegistry,
View,
Text,
TouchableHighlight,
StyleSheet,
NativeModules,
} = ReactNative;
var JPushModule = NativeModules.JPushModule;
export default class second extends React.Component {
constructor(props) {
super(props);
}
onBackPress = () => {
let navigator = this.props.navigator;
if (navigator != undefined) {
this.props.navigator.pop();
} else {
console.log("finishing second activity");
JPushModule.finishActivity();
}
}
onButtonPress = () => {
console.log("will jump to setting page");
let navigator = this.props.navigator;
if (navigator != undefined) {
this.props.navigator.push({
name: "setActivity"
});
} else {
}
}
render() {
return (
<View>
<TouchableHighlight
style={styles.backBtn}
underlayColor = '#e4083f'
activeOpacity = {0.5}
onPress = {this.onBackPress}>
<Text>
Back
</Text>
</TouchableHighlight>
<Text
style={styles.welcome}>
Welcome !
</Text>
<TouchableHighlight underlayColor = '#e4083f'
activeOpacity = {0.5}
style = {styles.btnStyle}
onPress = {this.onButtonPress}>
<Text style={styles.btnTextStyle}>
Jump To Setting page!
</Text>
</TouchableHighlight>
</View>
);
}
}
var styles = StyleSheet.create({
backBtn: {
padding: 10,
marginTop: 10,
marginLeft: 10,
borderWidth: 1,
borderColor: '#3e83d7',
backgroundColor: '#3e83d7',
borderRadius: 8,
alignSelf: 'flex-start'
},
welcome: {
textAlign: 'center',
margin: 10,
},
btnStyle: {
marginTop: 10,
borderWidth: 1,
borderColor: '#3e83d7',
borderRadius: 8,
backgroundColor: '#3e83d7',
alignSelf: 'center',
justifyContent: 'center'
},
btnTextStyle: {
textAlign: 'center',
fontSize: 25,
color: '#ffffff'
},
});
AppRegistry.registerComponent('SecondActivity', () => second);
就這樣,大功告成!接下來可以在 index.android.js 中註冊路由,使得在 JS 中也可以跳轉到這個界面。源碼請戳這裡
總結
以上就是在 React Native 中以混合的方式開發應用的大概過程。用這種方式可以馬上使用豐富的原生平臺第三方庫,只是在 Native 部分需要寫些代碼,但是花費的代價遠遠小於自己用 JS 的方式再實現一遍。
作者:KenChoi - 極光
原文:jpush-react-native 插件的集成與使用 Android 篇
知乎專欄:極光日報