React Native Android 的 native 模块

简介: 本文讲的是React Native Android 的 native 模块,当我们使用 React Native 开发一个安卓应用时,可能需要访问一个还没有对应的 React Native 模块的 API。
本文讲的是React Native Android 的 native 模块,

当我们使用 React Native 开发一个安卓应用时,可能需要访问一个还没有对应的 React Native 模块的 API。我们可以通过用 Java 编写自己的 native 模块并向 React Native 选择性地开放接口来解决。让我们一起来试一试。

我们将要做的事

在写这篇文章的时候,React Native 包含 ImagePickerIOS 组件却没有对应的安卓 ImagePicker 组件。我们打算创建一个功能行为大致跟 ImagePickerIOS 一样的简单的 ImagePicker 组件。

根据下列步骤写一个安卓的 native 模块

  1. 创建一个 ReactPackage 对象,这个对象可以把许多模块组合到一起(包括 native 和 JavaScript)。在 MainActivity 中把它写进 getPackages 方法中。
  2. 创建一个继承 ReactContextBaseJavaModule 的 Java 类来实现目标功能,并将这个类和我们的 ReactPackage 绑定。
  3. 在上面创建的类里重写 getName 方法。它返回的名字会成为 JavaScript 中的 native 模块的名字。
  4. 通过添加注解 @ReactMethod 的方式向 JavaScript 暴露想要的公有方法。
  5. 最后,在你的 JavaScript 代码中导入 NativeModules 里的模块并调用这些方法。

让我们来看看实际中时什么样子。

创建一个 ReactPackage

启动 AndroidStudio 并逐层找到 MyApp/android/app/src/main/java/com/myapp/MainActivity.java 文件。它看起来差不多应该是下面这个样子:

package com.myapp;

import com.facebook.react.ReactActivity;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;

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

public class MainActivity extends ReactActivity {
    @Override
    protected String getMainComponentName() {
    	return "MyApp";
    }

    @Override
    protected boolean getUseDeveloperSupport() {
    	return BuildConfig.DEBUG;
    }
    
    @Override
    protected List getPackages() {
    	return Arrays.asList(new MainReactPackage());
    }
}

我们准备乐观地把我们还未定义的包引进来。

import com.myapp.imagepicker.*; // 导入包

public class MainActivity extends ReactActivity { 

	@Override protected List getPackages() { 
		return Arrays.asList(new MainReactPackage(), new ImagePickerPackage()); // 把它包括进 getPackages 里 
	}
	
}

现在,我们才来真正定义这个包。我们会为它创建一个名为 imagepicker 的新目录并把下面的代码添加进ImagePickerPackage :

package com.myapp.imagepicker;

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;

public class ImagePickerPackage implements ReactPackage {
    @Override
    public List createNativeModules(ReactApplicationContext reactContext) {
    	List modules = new ArrayList<>();

    	modules.add(new ImagePickerModule(reactContext));

    	return modules;
	}

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

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

既然我们已经创建了一个包并且也把它放进了 MainActivity 。我们现在可以开始定义自己的模块了。

创建一个 ReactContextBaseJavaModule 模块

我们将开始创建一个继承 ReactContextBaseJavaModule 的类 ImagePickerModule.

package com.myapp.imagepicker;

import com.facebook.react.bridge.ReactContextBaseJavaModule;

public class ImagePickerModule extends ReactContextBaseJavaModule {
    public ImagePickerModule(ReactApplicationContext reactContext) {
    	super(reactContext);
	}
}

这是一个好的开端,但为了让 React Native 在 NativeModules 中找到我们的模块,我们需要重写 getName 方法。

@Override 
public String getName() { 
	return "ImagePicker"; 
}

现在,我们有可以导入到 JavaScript 代码的功能完备的 native 模块了。让我们再让它做点有趣的事情。

暴露方法

ImagePickerIOS 中定义了一个以 config 对象以及成功和取消两个回调对象为参数的 openSelectDialog 方法。让我们在ImagePickerModule 中也定义一个类似的方法。

import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReadableMap;

public class ImagePickerModule extends ReactContextBaseJavaModule {
    @ReactMethod
    public void openSelectDialog(ReadableMap config, Callback successCallback, Callback cancelCallback) {
    	Activity currentActivity = getCurrentActivity();

    	if (currentActivity == null) {
    		cancelCallback.invoke("Activity doesn't exist");
    		return;
		}
	}
}

这里我们从 React Native 的 bridge 包导入分别对应 JavaScript object 和 function 的 Callback 和 ReadableMap 类。我们给这个方法添加注解 **@ReactMethod,**作为 ImagePicker 模块的一部分暴露给 JavaScript. 在这个方法体里, 我们获取当前的 activity ,如果它不存在的话也可以调用取消回调。现在我们就有一个能工作的方法了,但它还没有做任何有趣的事情。让我们给它添加打开画册的功能吧。

public class ImagePickerModule extends ReactContextBaseJavaModule {
private static final int PICK_IMAGE = 1;

private Callback pickerSuccessCallback;
private Callback pickerCancelCallback;

@ReactMethod
public void openSelectDialog(ReadableMap config, Callback successCallback, Callback cancelCallback) {
    Activity currentActivity = getCurrentActivity();

    if (currentActivity == null) {
        cancelCallback.invoke("Activity doesn't exist");
        return;
    }

    pickerSuccessCallback = successCallback;
    pickerCancelCallback = cancelCallback;

    try {
        final Intent galleryIntent = new Intent();

        galleryIntent.setType("image/*");
        galleryIntent.setAction(Intent.ACTION_GET_CONTENT);

        final Intent chooserIntent = Intent.createChooser(galleryIntent, "Pick an image");

        currentActivity.startActivityForResult(chooserIntent, PICK_IMAGE);
    } catch (Exception e) {
        cancelCallback.invoke(e);
    }
}

首先,我们设置回调作为实例变量,原因之后会阐明。接着创建和配置我们的 Intent 并传入 startActivityForResult。最后,我们用 try/catch 语句块把整段代码囊括起来,处理期间可能产生的异常。

现在当你在 ImagePicker 调用 openSelectDialog 时应该看到一个图片画册。但是当选择一个图片时,画册会不做任何操作并消失。为了能返回图片数据,我们需要在模块中处理 activity 的结果。

首先我们需要在我们的 react 代码里添加一个 activity 的事件监听函数:

public class ImagePickerModule extends ReactContextBaseJavaModule implements ActivityEventListener { 
	public ImagePickerModule(ReactApplicationContext reactContext) { 
		super(reactContext);
		reactContext.addActivityEventListener(this); 
	} 
}

既然我们可以监听 activity 事件,我们就可以通过处理 onActivityResult 返回我们想要的图片数据。

@Override
public void onActivityResult(final int requestCode, final int resultCode, final Intent intent) {
	if (pickerSuccessCallback != null) {
		if (resultCode == Activity.RESULT_CANCELED) {
			pickerCancelCallback.invoke("ImagePicker was cancelled");
		} else if (resultCode == Activity.RESULT_OK) {
			Uri uri = intent.getData();

			if (uri == null) {
				pickerCancelCallback.invoke("No image data found");
			} else {
			    try {
			    	pickerSuccessCallback.invoke(uri);
			    } catch (Exception e) {
			    	pickerCancelCallback.invoke("No image data found");
		    	}
	    	}
    	}
	}
}

有了这段代码,当我们调用 openSelectDialog 时,应该能持续从成功回调中接收到图片的 URI。

NativeModules.ImagePicker.openSelectDialog(
{}, // no config yet 
(uri) => { console.log(uri) },
(error) => { console.log(error) }
)

为了进一步模仿 ImagePickerIOS 的行为,我们可以建立设置选项,允许用户选择图片,视频或者同时支持直接开启摄像头。因为这些功能都是基于相同的概念,前面已经演示过了,所以就作为练习留给读者吧。


特别鸣谢

多亏 Infinite Red 的技术主管 Gant Laborde 的帮助和支持,我才能写出这篇文章。他的丰富知识帮了我大忙。

关于 Ryan Linton

Ryan Linton 是 Infinite Red 的资深软件工程师。他喜欢在把他们的项目带到生活中的同时与客户密切合作。在不折腾前端样式和后台数据库的时候,他会到世界各地去旅行,或者试图从他那飞速增长的书单上划去一两本(已经读过的书)。






原文发布时间为:2016年11月22日

本文来自云栖社区合作伙伴掘金,了解相关信息可以关注掘金网站。
目录
相关文章
|
3月前
|
开发框架 前端开发 Android开发
Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
328 4
|
9天前
|
前端开发 Java 编译器
当flutter react native 等混开框架-并且用vscode-idea等编译器无法打包apk,打包安卓不成功怎么办-直接用android studio如何打包安卓apk -重要-优雅草卓伊凡
当flutter react native 等混开框架-并且用vscode-idea等编译器无法打包apk,打包安卓不成功怎么办-直接用android studio如何打包安卓apk -重要-优雅草卓伊凡
69 36
当flutter react native 等混开框架-并且用vscode-idea等编译器无法打包apk,打包安卓不成功怎么办-直接用android studio如何打包安卓apk -重要-优雅草卓伊凡
|
5月前
|
开发框架 Dart 前端开发
Android 跨平台方案对比之Flutter 和 React Native
本文对比了 Flutter 和 React Native 这两个跨平台移动应用开发框架。Flutter 使用 Dart 语言,提供接近原生的性能和丰富的组件库;React Native 则基于 JavaScript,具备庞大的社区支持和灵活性。两者各有优势,选择时需考虑团队技能和项目需求。
501 8
|
6月前
|
编解码 网络协议 前端开发
如何实现Android平台GB28181设备接入模块按需打开摄像头并回传数据
后台采集摄像头,如果想再进一步扩展,可以把android平台gb28181的camera2 demo,都移植过来,实现功能更强大的国标设备侧,这里主要是展示,收到国标平台侧的回传请求后,才打开摄像头,才开始编码打包,最大限度的减少资源的占用
|
6月前
|
监控 Java 开发工具
如何快速对接Android平台GB28181接入模块(SmartGBD)
大牛直播SDK推出的Android平台GB28181接入SDK(SmartGBD),可实现不具备国标音视频能力的 Android终端,通过平台注册接入到现有的GB/T28181—2016服务,可用于如执法记录仪、智能安全帽、智能监控、智慧零售、智慧教育、远程办公、明厨亮灶、智慧交通、智慧工地、雪亮工程、平安乡村、生产运输、车载终端等场景,可能是业内为数不多功能齐全性能优异的商业级水准GB28181接入SDK。
|
6月前
|
编解码 开发工具 Android开发
Android平台RTMP直播推送模块技术接入说明
大牛直播SDK跨平台RTMP直播推送模块,始于2015年,支持Windows、Linux(x64_64架构|aarch64)、Android、iOS平台,支持采集推送摄像头、屏幕、麦克风、扬声器、编码前、编码后数据对接,功能强大,性能优异,配合大牛直播SDK的SmartPlayer播放器,轻松实现毫秒级的延迟体验,满足大多数行业的使用场景。RTMP直播推送模块数据源,支持编码前、编码后数据对接
|
6月前
|
监控 开发工具 Android开发
结合GB/T28181规范探讨Android平台设备接入模块心跳实现
本文介绍了GB28181标准中的状态信息报送机制,即心跳机制,用于监控设备与服务器间的连接状态。根据国标GB/T28181-2016,设备在异常时需立即发送状态信息,在正常状态下则按固定间隔(默认60秒)定期发送。若连续三次(默认值)未收到心跳,则视为离线。文章展示了在Android平台的GB28181设备接入模块(SmartGBD)中,如何调整心跳间隔为20秒及超时次数为3次,并给出了心跳消息的示例和异常处理代码片段。对于希望深入了解或遇到问题的开发者,作者提供了进一步交流的机会。
|
6月前
|
编解码 API 开发工具
Android平台轻量级RTSP服务模块二次封装版调用说明
本文介绍了Android平台上轻量级RTSP服务模块的二次封装实践,旨在简化开发流程,让开发者能更专注于业务逻辑。通过`LibPublisherWrapper`类提供的API,可在应用中轻松初始化RTSP服务、配置视频参数(如分辨率、编码类型)、启动与停止RTSP服务及流发布,并获取RTSP会话数量。此外,还展示了如何处理音频和视频数据的采集与推送。最后,文章提供了从启动服务到销毁资源的完整示例,帮助开发者快速集成实时流媒体功能。
|
6月前
|
编解码 开发工具 Android开发
Android平台轻量级RTSP服务模块技术接入说明
为满足内网无纸化/电子教室等内网超低延迟需求,避免让用户配置单独的服务器,大牛直播SDK在推送端发布了轻量级RTSP服务SDK。 轻量级RTSP服务解决的核心痛点是避免用户或者开发者单独部署RTSP或者RTMP服务,实现本地的音视频数据(如摄像头、麦克风),编码后,汇聚到内置RTSP服务,对外提供可供拉流的RTSP URL,轻量级RTSP服务,适用于内网环境下,对并发要求不高的场景,支持H.264/H.265,支持RTSP鉴权、单播、组播模式,考虑到单个服务承载能力,我们支持同时创建多个RTSP服务,并支持获取当前RTSP服务会话连接数。
|
3月前
|
前端开发 JavaScript 开发者
颠覆传统:React框架如何引领前端开发的革命性变革
【10月更文挑战第32天】本文以问答形式探讨了React框架的特性和应用。React是一款由Facebook推出的JavaScript库,以其虚拟DOM机制和组件化设计,成为构建高性能单页面应用的理想选择。文章介绍了如何开始一个React项目、组件化思想的体现、性能优化方法、表单处理及路由实现等内容,帮助开发者更好地理解和使用React。
116 9

热门文章

最新文章

  • 1
    APP-国内主流安卓商店-应用市场-鸿蒙商店上架之必备前提·全国公安安全信息评估报告如何申请-需要安全评估报告的资料是哪些-优雅草卓伊凡全程操作
    50
  • 2
    【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
    29
  • 3
    当flutter react native 等混开框架-并且用vscode-idea等编译器无法打包apk,打包安卓不成功怎么办-直接用android studio如何打包安卓apk -重要-优雅草卓伊凡
    69
  • 4
    【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
    90
  • 5
    Android经典面试题之Kotlin中Lambda表达式和匿名函数的区别
    29
  • 6
    如何修复 Android 和 Windows 不支持视频编解码器的问题?
    242
  • 7
    【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
    62
  • 8
    【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
    35
  • 9
    【03】优雅草央千澈详解关于APP签名以及分发-上架完整流程-第三篇安卓APP上架华为商店后面的步骤-华为应用商店相对比较麻烦一些-华为商店安卓上架
    52
  • 10
    app开发之安卓Android+苹果ios打包所有权限对应解释列表【长期更新】-以及默认打包自动添加权限列表和简化后的基本打包权限列表以uniapp为例-优雅草央千澈
    83