React Native和android原生端通信
React Native和android原生端通信
更为详细的请参考React Native官方网站,此文章只介绍从React Native 到Android端通信的基本示例。
在新建好的React Native项目中都会存在一个名为android
的文件夹,使用 Android Studio来打开这文件夹
1. 在android/app/src/main/java/com/your-app-name/
文件夹中新建Kontlin文件:CalendarModule.kt
写入如下内容:
package com.your-apps-package-name; // replace your-apps-package-name with your app’s package name
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
class CalendarModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {...}
CalendarModule
类继承了ReactContextBaseJavaModule
,对于安卓系统,要使得这个类能够被React Native端调用,继承这个类是必不可少的。
从技术上讲,Java/Kotlin 类只需要扩展BaseJavaModule类或实现NativeModule接口,才能被 React Native 视为原生模块,然而官方更推荐使用上面所示的ReactContextBaseJavaModule。ReactContextBaseJavaModule提供了ReactApplicationContext(RAC)的访问权限,这对于需要挂钩到活动生命周期方法的原生模块非常有用。使用ReactContextBaseJavaModule也将使您更容易在将来实现原生模块的类型安全性。为了实现原生模块类型安全性(将在未来版本中推出),React Native 会查看每个原生模块的 JavaScript 规范,并生成一个抽象基类,该基类扩展自ReactContextBaseJavaModule
2.确定模块名称
所有原生Android平台上的原生模块都需要实现getName()
方法,该方法返回一个字符串,代表了原生模块的名称,我们在JavaScript中调用时,就是通过getName()
中返回的字符串来确定调用的是哪个类
// add to CalendarModule.kt
override fun getName() = "CalendarModule"
对应的在JavaScript中调用的方法如下:
const {CalendarModule} = ReactNative.NativeModules;
3. 添加可调用方法
接下来,我们在原生模块中添加一个方法,该方法可以创建日历时间,并可以在JavaScript中调用,所有打算从JavaScript中调用的原生模块方法都必须使用@ReactMethod
进行注解
import android.util.Log
@ReactMethod
fun createCalendarEvent(name: String, location: String) {
Log.d("CalendarModule", "Create event called with name: $name and location: $location")
}
该方法也可以标记为同步执行,@ReactMethod(isBlockingSynchronousMethod = true)
但是官方并不建议,因为可能会带来严重的性能损耗,而且无法使用调试模式
4. 在Android上注册模块
一旦编写完成原生模块(即上文的CalendarModule.kt),就需要将其注册到React Native中。
为此,你需要将你的原生模块添加到一个ReactPackage
中,并将该ReactPackage
注册到React Native。在初始化中,React Native会遍历所有包,并对于每个ReactPackage
注册其中的每个原生模块
React Native会调用ReactPackage
的createNativeModules()
方法,以获取要注册的原生模块列表,对于Android而言,如果一个模块未在createNativeModules
中实例化并返回,那么在JavaScript中就不可用
要将你的原生模块添加到 ReactPackage
中,首先在 android/app/src/main/java/com/your-app-name/ 文件夹中创建一个新的 Kotlin 类MyAppPackage.kt
,并实现 ReactPackage
接口
package com.your-app-name // replace your-app-name with your app’s name
import android.view.View
import com.facebook.react.ReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ReactShadowNode
import com.facebook.react.uimanager.ViewManager
class MyAppPackage : ReactPackage {
override fun createViewManagers(
reactContext: ReactApplicationContext
): MutableList<ViewManager<View, ReactShadowNode<*>>> = mutableListOf()
override fun createNativeModules(
reactContext: ReactApplicationContext
): MutableList<NativeModule> = listOf(CalendarModule(reactContext)).toMutableList()
}
这个文件导入和创建的原生模块CalendarModule
,然后在createNativeModules
函数中实例化了CalendarModule
并将其作为NativeModules
列表返回以便注册,如果将来添加更多原生模块,也可以在这里实例化他们并添加到返回的列表中
要注册CalendarModule包,你必须将MyAppPackage添加到 ReactNativeHost 的getPackages()方法返回的包列表中。打开ainApplication.kt文件,位于如下路径:android/app/src/main/java/com/your-app-name/
找到 ReactNativeHost 的getPackages()方法,并将你的包添加到getPackages()返回的包列表中。
override fun getPackages(): List<ReactPackage> =
PackageList(this).packages.apply {
// Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage());
add(MyAppPackage())
}
5. 测试运行
在React Native中测试一下:
import React from 'react';
import {NativeModules, Button} from 'react-native';
const {CalendarModule} = NativeModules;
const NewModuleButton = () => {
const onPress = () => {
CalendarModule.createCalendarEvent('testName', 'testLocation');
console.log('We will invoke the native module here!');
};
return (
<Button
title="Click to invoke your native module!"
color="#841584"
onPress={onPress}
/>
);
};
export default NewModuleButton;
重新构建一下应用程序,此时应该可以正确调用android中的方法:
npm run android
注意如果你打印NativeModules
,你会发现始终是{}
,这是由于异步导致的,React Native 在GitHub的Open Issues中承认此Bug,但是修复时间尚不确定
附加:
在上面代码的基础上,添加React Native按钮点击调用Android中的代码来显示一个简单的通知栏消息:
新建文件:NotificationHelper.kt
package com.haha_react_native
// 在你的项目中创建一个新的 Kotlin 文件,例如 NotificationHelper.kt
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.graphics.BitmapFactory
import android.os.Build
import androidx.core.app.NotificationCompat
class NotificationHelper(private val context: Context) {
private val channelId = "my_channel_id" // 设置通知通道 ID
fun showNotification(notificationId: Int, title: String, content: String) {
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
// 创建通知通道(仅适用于 Android 8 及更高版本)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(channelId, "My Channel", NotificationManager.IMPORTANCE_DEFAULT)
notificationManager.createNotificationChannel(channel)
}
// 创建通知
val builder = NotificationCompat.Builder(context, channelId)
.setSmallIcon(R.drawable.ic_notification) // 设置通知栏小图标
.setContentTitle(title) // 设置通知标题
.setContentText(content) // 设置通知内容
.setLargeIcon(BitmapFactory.decodeResource(context.resources, R.mipmap.ic_launcher)) // 设置通知栏大图标
.setAutoCancel(true) // 点击通知后自动取消
// 发送通知
notificationManager.notify(notificationId, builder.build())
}
}
在上边的createCalendarEvent
方法中新增调用:
val notificationHelper = NotificationHelper(this.reactApplicationContext)
val notificationId = 1;
val title = "新消息"
val content = "你有一条新消息"
notificationHelper.showNotification(notificationId,title,content);
重新运行React Native程序,单击按钮时会在通知栏显示一条通知。
React Native中很多开发包已经很久没有更新,在新版本的React Native中会有各式各样的奇怪问题,如果开始一个新的项目,并不推荐使用。