React-Native开发之原生模块封装(Android)升级版

 本文主题:如何实现原生代码的复用,即如何将原生模块封装。

有时候我们的应用需要进行访问原生平台系统的API接口,但是React Native可能还没有封装相应功能组件。还有可能我们需要

去复用一些原生java代码而不是让JavaScript重新去实现一遍。或者我们可能需要些一些更加高级的功能代码,所线程相关的。例如:

图片处理,数据库以及一些高级功能扩展之类的。

React Native平台的开发其实本身也是可以让你写纯原生代码并且还可以让你访问原生平台的功能。这是一个比较高级的功能不

过官方还是不推荐你在平时开发中使用这样的开发形式。但是如果你具备这样的开发能力,也是还是不错的。特别当在React Native

暂时未提供部分原生功能或者模块,那么你可以使用这样的方法进行封装扩展。今天我们就来看一下原生组件的封装扩展方法。

总体来说,就是当我们需要复用部分原生代码时,比如复用一个原生方法,此时就需要将原生方法进行封装,只暴露出一个接口

来让React-Native调用。本博客以一个Toast消息来作用案例来讲解如何封装原生模块。

步骤:

  • 用Android Studio打开一个已经存在的RN项目,即用AS打开 项目文件夹/android/build.gradle文件。

  • 在Android原生这边创建一个类继承ReactContextBaseJavaModule,这个类里边放我们需要被RN调用的方法,将其封装成一个原生模块。

  • 在Android原生这边创建一个类实现接口ReactPackage包管理器,并把第二步创建的类加到原生模块(NativeModule)列表里。

  • 将第三步创建的包管理器添加到ReactPackage列表里(getPackage方法里)

  • 在RN中去调用原生模块,必须import    NativeModule模块。

首先大家肯定已经安装好了Android Studio,打开build.gradle文件之后,会发现其实连android/app文件夹也一并打开了。其中java文件夹中存放原生代码,也就是将我们要复用的原生代码放进来。大家可以打开  项目文件夹/android/app自行查看各级目录。在默认的包下,创建上边第二步和第三步所需的类。截图如下:

MyNativeModule.java代码如下:

package com.reactnative;

import android.widget.Toast;
import android.content.Context;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;

/**
 * Created by Administrator on 2016/10/18.
 */

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();

  }

}

本类中存放我们要复用的原生方法,继承了ReactContextBaseJavaModule类,并且实现了其getName()方法,构造方法也是必须的。按着Alt+Enter程序会自动提示。接着定义了一个方法,该方法必须使用注解@ReactMethod标明,说明是RN要调用的方法。

MyReactPackage.java代码如下:

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Created by Administrator on 2016/10/18.
 */

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.java代码如下:

package com.reactnative;

import android.app.Application;
import android.util.Log;

import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;

import java.util.Arrays;
import java.util.List;

public class MainApplication extends Application implements ReactApplication {

  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 MyReactPackage()
      );
    }
  };

  @Override
  public ReactNativeHost getReactNativeHost() {
      return mReactNativeHost;
  }
}

MainActivity.java代码如下:

package com.reactnative;

import com.facebook.react.ReactActivity;

public class MainActivity extends ReactActivity {

    /**
     * Returns the name of the main component registered from JavaScript.
     * This is used to schedule rendering of the component.
     */
    @Override
    protected String getMainComponentName() {
        return "reactNative";
    }
}

接着我们需要编写index.android.js文件

代码如下:

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import React, { Component } from ‘react‘;
import {
  AppRegistry,
  StyleSheet,
  Text,
  NativeModules,
  View
} from ‘react-native‘;

class reactNative extends Component {
  render() {
    return (
      <View style={styles.container}>

        <Text style={styles.welcome}
        onPress={this.call_button.bind(this)}
        >
          React Native 调用原生方法!
        </Text>

        <Text style={styles.instructions}>
          To get started, edit index.android.js
        </Text>

        <Text style={styles.instructions}>
          Double tap R on your keyboard to reload,{‘\n‘}
          Shake or press menu button for dev menu
        </Text>

      </View>
    );
  }

   call_button(){

   	 NativeModules.MyNativeModule.rnCallNative(‘调用原生方法的Demo‘);
   }

}

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);

  我们将一个文本框绑定了一个事件,首先import导入NativeModule,当点击文本时将会调用call_button()方法。然后传入一个上下文对象,也就是一句话,通过调用原生方法rnCallNative( )方法来实现。

来分析一下程序运行流程:

(1)在配置文件AndroidManifest.xml中,android:name=".MainApplication",则MainApplication.java会执行。

(2)在MainApplication.java中,有我们创建的包管理器对象。程序加入MyReactPackage.java中。

(3)在MyReactPackage.java中,将我们自己创建的模块加入了原生模块列表中,程序进入MyNativeModule.java中。

(4)在MyNativeModule.java中,有我们需要被复用的原生方法rnCallNative( )。

程序运行结果如下所示,当点击第一行文本时,出现Toast消息。

封装原生方法升级篇:

(1)如何封装复杂方法,实现更多的功能?

在上文中,我们封装了一个简单的方法—弹出Toast 提醒框。但是大家看到可能很郁闷,心里想,我TM 要封装的方法也不会这么简单呀,能不能封装点复杂的方法,用来实现更多的功能?(毕竟是我们想要复用的方法,肯定实现了很多比较牛逼的功能。)

答:能!!!

如何实现呢?以上面所述项目为例讲解。

  • 首先,将原生的java 文件复制到RN项目中存放原生代码的位置,如下图所示。
  • 然后,在MyNativeMoudle.java中写一个可以被RN调用的方法,以注解@ReactMethod表明。
  • 其次,在上一步所述方法内部可以任意调用原生方法,实现更加复杂的功能。
  • 最后,在RN中调用第二步所写的方法。

Demo如下:

ManiActivity.java

package com.firstproject;

import com.facebook.react.ReactActivity;

public class MainActivity extends ReactActivity {

    /**
     * Returns the name of the main component registered from JavaScript.
     * This is used to schedule rendering of the component.
     */
    @Override
    protected String getMainComponentName() {
        return "FirstProject";
    }
}

ManiApplication.java

package com.firstproject;

import android.app.Application;
import android.util.Log;

import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;

import java.util.Arrays;
import java.util.List;

public class MainApplication extends Application implements ReactApplication {

  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 MyPackage()
      );
    }
  };

  @Override
  public ReactNativeHost getReactNativeHost() {
      return mReactNativeHost;
  }
}

MyModule.java

package com.firstproject;

import android.content.Context;

import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;

/**
 * Created by Administrator on 2016/10/30.
 */

public class MyModule extends ReactContextBaseJavaModule {

  //建立上下文对象
  public Context myContext;

  public MyModule(ReactApplicationContext reactContext) {

    super(reactContext);

    myContext=reactContext;
  }

  @Override
  public String getName() {

    return "MyModule";
  }

  @ReactMethod
  public void showTime()
  {
    new Test().getTime(myContext);
  }

}

MyPackage.java

package com.firstproject;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Created by Administrator on 2016/10/30.
 */

public class MyPackage implements ReactPackage {
  @Override
  public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {

    List<NativeModule> modules=new ArrayList<>();
    modules.add(new MyModule(reactContext));

    return modules;
  }

  @Override
  public List<Class<? extends JavaScriptModule>> createJSModules() {
    return Collections.emptyList();
  }

  @Override
  public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
    return Collections.emptyList();
  }
}

Test.java作为我们要复用的原生类,里边有要复用的原生方法。

package com.firstproject;

import android.content.Context;
import android.util.Log;
import android.widget.Toast;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * Created by Administrator on 2016/10/30.
 */

public class Test {

     public void getTime(Context ctx)
     {
       SimpleDateFormat formatDate=new SimpleDateFormat("yyyy年MM月dd日  HH:mm:ss");
       Date date=new Date(System.currentTimeMillis());   //获取当前时间
       String s=formatDate.format(date);

         Log.e("HHH",s);
       Toast.makeText(ctx,s,Toast.LENGTH_SHORT).show();

     }

}

index.android.js如下:

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import React, { Component } from ‘react‘;
import {
  AppRegistry,
  StyleSheet,
  Text,
  NativeModules,
  View
} from ‘react-native‘;

export default class FirstProject extends Component {
    render() {
    return (
      <View style={styles.container}>

        <Text style={styles.welcome}
        //给此处的文字绑定一个事件,其中callNative为要调用的方法名。
        onPress={this.callNative.bind(this)}
        >
          点击此处文字调用原生方法!
        </Text>

        <Text style={styles.instructions}>
          此Demo演示如何调用Android原生中的复杂方法。
        </Text>

      </View>
    );
  }

   callNative()
   {
     NativeModules.MyModule.showTime();
   }
}

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(‘FirstProject‘, () => FirstProject);

程序运行结果如下:

在上边的Demo中,主要是原生类Test.java。我们在要被RN调用的方法中调用原生类中的方法。和最开始的案

不同的是,这里说明了,方法可以调用,我们N多原生类都可以直接粘贴复制过来。这样就可以实现调用复杂方

实现强大功能了。

注意:当Android原生中涉及到权限的使用时,记得在AndroidManifest.xml中添加相应权限,如下图所示。


(2)如何实现数据从Android 原生回调到RN前端界面?

我们都知道,要被RN调用的方法必须是void 类型,即没有返回值,但是项目中很多地方都需要返回数据。那怎

么实现呢?

       如图所示:我们定义一个方法,使用Callback, 在这个方法中,建立并且开启一个线程,

使用callback. invoke( XXXX)实现数据向RN前端的传递。

       其中,MyMainActivity.java文件为我们的一个原生类,按照上面的要求复制到RN项目中存放原生代码的地

方。而ReceiveData为这个类的一个变量。

如何在RN项目中调用?


代码如下:

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import React, { Component } from ‘react‘;
import {
  AppRegistry,
  StyleSheet,
  Text,
  NativeModules,
  View,
  TouchableOpacity
} from ‘react-native‘;

export default class lh extends Component {
	constructor(props) {
    super(props);
    this.state = {
        global: ‘这个是预定的接受信息‘,
    }
}
  render() {

  	var Globle="null";
    return (

      <View style={styles.container}>

        <TouchableOpacity style={styles.button1}
        onPress={this.call_button_show.bind(this)}>
        <Text style={styles.welcome}
        >
        显示信息
        </Text>
        </TouchableOpacity>

      <Text style={styles.welcome} >
        {this.state.global}
      </Text>

      </View>
    );
  }

 call_button_show(){
    Globle="null";

 	NativeModules.MyModule.getResult((result)=>{this.setState({global:result,});});
 }

}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: ‘center‘,
    alignItems: ‘center‘,
    backgroundColor: ‘#F5FCFF‘,
  },
    button1:{
    height: 40,
    width: 100,
    marginTop:1,
    backgroundColor:‘gray‘,
    },
  welcome: {
    fontSize: 20,
    textAlign: ‘center‘,
    margin: 10,
  },
  instructions: {
    textAlign: ‘center‘,
    color: ‘#333333‘,
    marginBottom: 5,
  },
});

AppRegistry.registerComponent(‘lh‘, () => lh);

当我们点击按钮时,将会调用getResult方法,并且将ReceiveData变量的值传递给RN前端的result变量中,利用

setState来实现界面的更新。

  至此,我们实现了RN复用原生代码,即将原生模块封装成一个接口,在RN中调用。并且可以封装更加复杂

的方法,同时实现了数据回调,即将数据从原生模块中传递到RN前端。

程序源代码1下载,请见GitHub:https://github.com/chaohuangtianjie994/ReactNative-call-NativeMethod

Demo2的改动不大,大家可以自行改动哦。

如果对你有帮助,记得点赞哦

时间: 2024-10-25 12:29:15

React-Native开发之原生模块封装(Android)升级版的相关文章

React—Native开发之原生模块向JavaScript发送事件

首先,由RN中文网关于原生模块(Android)的介绍可以看到,RN前端与原生模块之 间通信,主要有三种方法: (1)使用回调函数Callback,它提供了一个函数来把返回值传回给JavaScript. (2)使用Promise来实现. (3)原生模块向JavaScript发送事件. 其中,在我的博客React-Native开发之原生模块封装(Android)升级版 较为详细的阐述了如何使用回调函数Callback 来将数据传向JavaScript 端. 但是有一个比较难以解决的问题是: cal

React Native开发入门

目录: 一.前言 二.什么是React Native 三.开发环境搭建 四.预备知识 五.最简单的React Native小程序 六.总结 七.参考资料 一.前言 虽然只是简单的了解了一下React的皮毛,但是对React Native的学习就轻松了好多.所以在学习React Native之前,最好还是先学习一下React.因为我学习的iOS开发,对iOS更加了解,所以里面主要涉及到的平台也是iOS. 二.什么是React Native React Native是一款用来开发真正原生.可渲染iO

我的 React Native 技能树点亮计划 の React Native 开发 IDE 选型和配置

@author ASCE1885的 Github 简书 微博 CSDN 知乎 本文首发于 InfoQ 移动技术公众号:移动开发前线 由于潜在的商业目的,未经许可不开放全文转载许可,谢谢! React Native 发布一年多了,有不少公司已经在线上产品中或小范围试水,或大范围应用,很多公司或开发者都在为 React Native 的生态系统作出自己的贡献.React Native 的开发基本上是 Javascript + 系统原生开发语言(Java,Objective-C,Swift),原生语言

【React Native开发】React Native开发IDE安装及配置

转载请标明出处: http://blog.csdn.net/developer_jiangqq/article/details/50476350 本文出自:[江清清的博客] (一)前言 上一讲我们已经对于在OS X系统上面对于React Native For Android的环境搭建以及第一个实例做了详细讲解.所谓工欲善其事,必先利其器,做React Native开发也和其他应用开发一样,最好有一个比较好的IDE工具.那么这边比较推荐以下几款工具:sublime,webstorm以及官网推荐的N

【转】【React Native开发】

[React Native开发]React Native控件之ListView组件讲解以及最齐全实例(19) [React Native开发]React Native控件之Touchable*系列组件详解(18) [React Native开发]React Native控件之ViewPagerAndroid讲解以及美团首页顶部效果实例(17) [React Native开发]React Native控件之Switch开关与Picker选择器组件讲解以及使用(16) [React Native开发

React Native开发 - 搭建React Native开发环境

移动开发以前一般都是原生的语言来开发,Android开发是用Java语言,IOS的开发是Object-C或者Swift.那么对于开发一个App,至少需要两套代码.两个团队.对于公司来说,成本还是有的.然而现在有蛮多的公司开发App是基于React Native来开发的,这样可以做到一个App,就是一套代码,一个团队.对于公司来说,无疑节约了成本. <React Native开发>这系列的文章主要是记录本人利用React Native学习开发的笔记,这一篇文章是第一篇<搭建React Na

React Native开发的通讯录应用

React Native开发的通讯录应用(使用JavaScript开发原生iOS应用,vczero) 0.前言: 项目地址:https://github.com/vczero/React-Native-App 欢迎大家提issues讨论任何问题,包括“试衣间”.... 一.项目介绍 基于React-Native & Node通讯录App (1)主要完成的功能有: 基于文件系统的Node.js服务端; 通讯录功能(分类页 + 列表页 + 拨号邮箱邮件) 公告功能(列表页 + 详情页) 通讯录和内容

React Native 开发笔记

ReactNativeDemo 学习ReactNative开发,搭建ReactNative第一个项目 React Native 开发笔记 1.安装Homebrew $ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 2.安装wget.git.curl工具 //每次执行brew命令时,最好先执行brew update 或者 brew upg

【REACT NATIVE 系列教程之七】统一ANDROID与IOS两个平台的程序入口&&区分平台的组件简介

本站文章均为 李华明Himi 原创,转载务必在明显处注明: 转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/react-native/2260.html       本篇介绍两个细节:       1. 关于如何将index.android.js 与index.ios.js统一管理起来.       2.  Platform 组件的简单介绍与使用   一:将index.android.js 与index.ios.js统一管理起来. 由于React本身