你不可不知的 React Native 混合用法(Android 篇)

前言

当前 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 – 极光

原文:你不可不知的 React Native 混合用法(Android 篇)

知乎专栏:极光日报

    原文作者:移动开发
    原文地址: https://my.oschina.net/jpushtech/blog/895091
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞