资讯专栏INFORMATION COLUMN

React Native 中的 Android 原生模块

habren / 2737人阅读

摘要:当使用开发应用时,你可能需要使用没有被封装的模块。但你可以使用编写原生模块,然后选择性的暴露公共接口到。编写一个的原生模块需要以下步骤创建一个,把很多模块和包含在一起,然后在中的方法引用。最后,在中通过导入你的模块。

当使用 React Native 开发 Android 应用时,你可能需要使用没有被 React Native 封装的模块。但你可以使用 Java 编写原生模块,然后选择性的暴露公共接口到 React Native。一起来试一下!

我们要写一个什么东西

在写这篇文章时,React Native 包含了 ImagePickerIOS 组件,但是在 Android 平台上却没有对应的 ImagePicker 组件。我们接下来就要为 Android 构建一个简单的、和 ImagePickerIOS 大致相仿的 ImagePicker。

编写一个 React Native 的 Android 原生模块需要以下步骤:

创建一个 ReactPackage,把很多模块(Native 和 Javascript)包含在一起,然后在 MainActivity 中的 getPackages 方法引用。

创建一个 Java 类,继承 ReactContextBaseJavaModule 并实现需要的接口,然后注册到我们的 ReactPackage

覆写上述类的 getName 方法,这个方法会作为 Javascript 的调用方法名。

使用 @ReactMethod 注解把需要的公共方法暴露给 Javascript。

最后,在 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.*; // import the package

public class MainActivity extends ReactActivity {
    @Override
    protected List getPackages() {
        return Arrays.asList(
            new MainReactPackage(),
            new ImagePickerPackage() // include it in 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> createJSModules() {
        return Collections.emptyList();
    }

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

现在我们已经创建了一个包并且包含进 MainActivity 中了。

创建一个 ReactContextBaseJavaModule

我们将会以创建 ImagePickerModule 开始,继承 ReactContextBaseJavaModule

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 定义了 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 中导入了 CallbackReadableMap 来对应 JavaScript 中的 functionobject。我们为这个方法加上 @ReactMethod 注解,从而使它作为 ImagePicker 的一部分被 JavaScript 引用。
在方法体中我们获取当前的 activity,如果没有获取到 activity,就调用 cancel 的回调方法。我们现在有了一个可以运行的方法,但是它还不能做任何有趣的事情。让我们用它打开相册。

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 块中来处理可能发生的异常。

当你调用 openSelectDialog 时,你应该可以看到一个相册了。然而,当你选择一张图片时,相册并不做任何事情。为了能够处理图片数据,我们需要在模块中处理 activity 的返回值。

首先,我们需要在 react context 中添加 activity event listener:

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

现在我们可以获取到相册返回的数据了。

@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");
                }
            }
        }
    }
}

在这里我们应该可以通过 success callback 获取到图片 URI。

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

为了和 ImagePickerIOS 的表现大致相仿,我们可以允许用户选择图片、视频或者直接打开相机。这些功能的写法和上面基本一致,我们将会把它留给读者作为练习。


相关阅读

使用 React Native 构建 Facebook Paper 类似的 UI

作者往期佳作

ES6 Generators 工作原理
欢迎关注公众号:MaxLeap_yidongyanfa


作者信息

原文作者: Ryan Linton
原文链接: http://t.cn/Rfadv1R
翻译自MaxLeap团队_前端研发人员:Henry Bai
MaxLeap技术博客首发:https://blog.maxleap.cn/


活动预告

主题:手把手教你玩转微信小程序,领先进入蓝海市场
时间:11月23日
地点:上海徐汇宜山路888号21楼
报名url:http://t.cn/RfcM3Sa

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/13356.html

相关文章

  • React Native 中的 Android 原生模块

    摘要:当使用开发应用时,你可能需要使用没有被封装的模块。但你可以使用编写原生模块,然后选择性的暴露公共接口到。编写一个的原生模块需要以下步骤创建一个,把很多模块和包含在一起,然后在中的方法引用。最后,在中通过导入你的模块。 当使用 React Native 开发 Android 应用时,你可能需要使用没有被 React Native 封装的模块。但你可以使用 Java 编写原生模块,然后选择...

    caiyongji 评论0 收藏0
  • React Native 原理

    摘要:对象和原生代码交互对象可以和原生对象之间相互调用,关系如上图。和本地代码间的通信其中通信的特点是异步的序列化的批量的,对于大批量的通信事件可以将其分成几部分,减少时间延迟参考 React Native简介 React Native是一个建立在JavaScript和React上用于构建本地应用的框架,它具有React和JavaScript相似的代码风格,编写一次可以运行在多个平台之上(>...

    jackzou 评论0 收藏0
  • jpush-react-native 插件的集成与使用 Android

    摘要:接下来我们以插件为例来看看在中如何使用原生的第三方库。是极光推送提供的版本插件,可以让我们快速集成推送功能。作者极光为极光团队账号,欢迎关注原文插件的集成与使用篇知乎专栏极光日报 前言 当前 React Native 虽说版本更新比较快,各种组件也提供的很全面了,但是在某些情况下,混合开发的方式才会快速缩短开发周期,原因无非就是原生平台的底蕴无疑更深,拥有众多且类型丰富的第三方支持库。...

    dcr309duan 评论0 收藏0
  • Android混合开发

    摘要:另外已经开始报名,定于月日在上海举行,早鸟票已售罄,想去的抓紧时间报名。和相互调用当前的开发中,会使用大量的甚至出现了混合开发模式,使用开发,页面开发效率高和移植便利性为主。 前端周刊第 52 期:JS Conf 2017 开始报名、苹果腾讯开战、React Native 周边 哈哈,互联网圈本周的大事件是微信公众号关闭 iOS 平台打赏入口,讨论这件事情的文章很多,前端周刊就没有收录...

    张巨伟 评论0 收藏0
  • React Native 网络层分析

    摘要:内置了三种发送网络请求的方式和。转换二进制为发送到目前为止,不能发送非序列化的数据,所以,要发送二进制数据,采用编码的字符串是个不错的选择。在最新版本的层也已经支持协议来传输二进制文件,但是,相应的原生平台的网络模块暂时还不支持。 端) 本文原创,转载请注明作者及出处 在使用React Native开发中,我们熟练的采用JavaScript的方式发送请求的方式发送一个请求到服务端,但是...

    Elle 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<