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日

本文来自云栖社区合作伙伴掘金,了解相关信息可以关注掘金网站。
目录
相关文章
|
4月前
|
开发框架 前端开发 Android开发
Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
450 4
|
1月前
|
前端开发 Java 编译器
当flutter react native 等混开框架-并且用vscode-idea等编译器无法打包apk,打包安卓不成功怎么办-直接用android studio如何打包安卓apk -重要-优雅草卓伊凡
当flutter react native 等混开框架-并且用vscode-idea等编译器无法打包apk,打包安卓不成功怎么办-直接用android studio如何打包安卓apk -重要-优雅草卓伊凡
82 36
当flutter react native 等混开框架-并且用vscode-idea等编译器无法打包apk,打包安卓不成功怎么办-直接用android studio如何打包安卓apk -重要-优雅草卓伊凡
|
6月前
|
开发框架 Dart 前端开发
Android 跨平台方案对比之Flutter 和 React Native
本文对比了 Flutter 和 React Native 这两个跨平台移动应用开发框架。Flutter 使用 Dart 语言,提供接近原生的性能和丰富的组件库;React Native 则基于 JavaScript,具备庞大的社区支持和灵活性。两者各有优势,选择时需考虑团队技能和项目需求。
530 8
|
7月前
|
编解码 网络协议 前端开发
如何实现Android平台GB28181设备接入模块按需打开摄像头并回传数据
后台采集摄像头,如果想再进一步扩展,可以把android平台gb28181的camera2 demo,都移植过来,实现功能更强大的国标设备侧,这里主要是展示,收到国标平台侧的回传请求后,才打开摄像头,才开始编码打包,最大限度的减少资源的占用
|
7月前
|
编解码 网络协议 Android开发
Android平台GB28181设备接入模块实现后台service按需回传摄像头数据到国标平台侧
我们在做Android平台GB28181设备对接模块的时候,遇到这样的技术需求,开发者希望能以后台服务的形式运行程序,国标平台侧没有视频回传请求的时候,仅保持信令链接,有发起视频回传请求或语音广播时,打开摄像头,并实时回传音视频数据或接收处理国标平台侧发过来的语音广播数据。
|
7月前
|
Java Android开发
添加Flutter模块到已有项目(Android)
添加Flutter模块到已有项目(Android)
62 3
|
7月前
|
监控 Java 开发工具
如何快速对接Android平台GB28181接入模块(SmartGBD)
大牛直播SDK推出的Android平台GB28181接入SDK(SmartGBD),可实现不具备国标音视频能力的 Android终端,通过平台注册接入到现有的GB/T28181—2016服务,可用于如执法记录仪、智能安全帽、智能监控、智慧零售、智慧教育、远程办公、明厨亮灶、智慧交通、智慧工地、雪亮工程、平安乡村、生产运输、车载终端等场景,可能是业内为数不多功能齐全性能优异的商业级水准GB28181接入SDK。
|
8月前
|
前端开发 JavaScript Android开发
React Native跨平台开发实战
【7月更文挑战第21天】React Native为跨平台移动应用开发提供了一种高效且强大的解决方案。通过本文的学习,你应该能够掌握React Native的基本概念和实战步骤,并开始在你的项目中使用React Native进行开发。随着你对React Native的深入理解,你将能够利用其强大的功能来构建更加复杂和高效的移动应用。
|
7月前
|
编解码 开发工具 Android开发
Android平台RTMP直播推送模块技术接入说明
大牛直播SDK跨平台RTMP直播推送模块,始于2015年,支持Windows、Linux(x64_64架构|aarch64)、Android、iOS平台,支持采集推送摄像头、屏幕、麦克风、扬声器、编码前、编码后数据对接,功能强大,性能优异,配合大牛直播SDK的SmartPlayer播放器,轻松实现毫秒级的延迟体验,满足大多数行业的使用场景。RTMP直播推送模块数据源,支持编码前、编码后数据对接
|
7月前
|
监控 开发工具 Android开发
结合GB/T28181规范探讨Android平台设备接入模块心跳实现
本文介绍了GB28181标准中的状态信息报送机制,即心跳机制,用于监控设备与服务器间的连接状态。根据国标GB/T28181-2016,设备在异常时需立即发送状态信息,在正常状态下则按固定间隔(默认60秒)定期发送。若连续三次(默认值)未收到心跳,则视为离线。文章展示了在Android平台的GB28181设备接入模块(SmartGBD)中,如何调整心跳间隔为20秒及超时次数为3次,并给出了心跳消息的示例和异常处理代码片段。对于希望深入了解或遇到问题的开发者,作者提供了进一步交流的机会。
104 0

热门文章

最新文章

  • 1
    【03】微信支付商户申请下户到配置完整流程-微信开放平台创建APP应用-填写上传基础资料-生成安卓证书-获取Apk签名-申请+配置完整流程-优雅草卓伊凡
    56
  • 2
    android FragmentManager 删除所有Fragment 重建
    25
  • 3
    Android实战经验之Kotlin中快速实现MVI架构
    39
  • 4
    即时通讯安全篇(一):正确地理解和使用Android端加密算法
    40
  • 5
    escrcpy:【技术党必看】Android开发,Escrcpy 让你无线投屏新体验!图形界面掌控 Android,30-120fps 超流畅!🔥
    44
  • 6
    【01】噩梦终结flutter配安卓android鸿蒙harmonyOS 以及next调试环境配鸿蒙和ios真机调试环境-flutter项目安卓环境配置-gradle-agp-ndkVersion模拟器运行真机测试环境-本地环境搭建-如何快速搭建android本地运行环境-优雅草卓伊凡-很多人在这步就被难倒了
    155
  • 7
    Cellebrite UFED 4PC 7.71 (Windows) - Android 和 iOS 移动设备取证软件
    52
  • 8
    【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    67
  • 9
    Android历史版本与APK文件结构
    170
  • 10
    【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    54