跳到主要内容
版本:v4

Capacitor Android 插件指南

为 Android 构建 Capacitor 插件需要编写 Java 或 Kotlin 来与 Android SDK 交互。

开始​

首先,按照插件指南的开始部分所述生成一个插件。

接下来,在 Android Studio 中打开 echo/android/。然后导航到插件的 .java 文件,该文件的位置取决于您在创建插件时使用的插件 ID 和插件类名。

例如,对于 ID 为 com.domain.echo、插件类名为 Echo 的插件,您可以在 android/src/main/java/com/domain/echo/EchoPlugin.java 找到 .java 文件。

使用 Kotlin​

Capacitor 默认使用 Java,但您也可以选择使用 Kotlin。

生成插件后,在 Android Studio 中右键单击 Java 插件类,从菜单中选择"Convert Java file to Kotlin file"选项。Android Studio 将引导您完成项目的 Kotlin 支持配置。完成后,再次右键单击 Java 类并重新选择转换选项,将其转换为 Kotlin 类。

插件基础​

Capacitor 的 Android 插件是一个简单的 Java 类,继承自 com.getcapacitor.Plugin,并带有 @CapacitorPlugin() 注解。它包含一些带有 @PluginMethod() 注解的方法,供 JavaScript 调用。

生成插件后,您可以打开以生成时选择的插件类名命名的文件,开始编辑。

简单示例​

在生成的示例中,有一个简单的 echo 插件,包含一个 echo 函数,该函数简单地返回传入的值。

这个示例展示了 Capacitor 插件的几个核心组件:从插件调用中接收数据,以及将数据返回给调用者。

EchoPlugin.java

package android.plugin.test;

import com.getcapacitor.JSObject;
import com.getcapacitor.Plugin;
import com.getcapacitor.PluginCall;
import com.getcapacitor.PluginMethod;
import com.getcapacitor.annotation.CapacitorPlugin;

@CapacitorPlugin(name = "Echo")
public class EchoPlugin extends Plugin {

@PluginMethod()
public void echo(PluginCall call) {
String value = call.getString("value");

JSObject ret = new JSObject();
ret.put("value", value);
call.resolve(ret);
}
}

访问调用数据​

每个插件方法都会收到一个 com.getcapacitor.PluginCall 实例,其中包含来自客户端的插件方法调用的所有信息。

客户端可以发送任何可以 JSON 序列化的数据,例如数字、文本、布尔值、对象和数组。这些数据可以通过调用实例的 getData 字段访问,或者使用 getString 或 getObject 等便捷方法访问。

例如,以下是获取传递给方法的数据的方式:

@PluginMethod()
public void storeContact(PluginCall call) {
String name = call.getString("yourName", "default name");
JSObject address = call.getObject("address", new JSObject());
boolean isAwesome = call.getBoolean("isAwesome", false);

if (!call.getData().has("id")) {
call.reject("Must provide an id");
return;
}
// ...

call.resolve();
}

注意在 PluginCall 实例上访问数据的各种方式,包括如何使用 getData 的 has 方法检查键是否存在。

返回数据​

插件调用可以成功或失败。插件调用借用了 JavaScript Promise 的方法命名:调用 resolve() 表示成功(可选择返回数据),使用 reject() 表示失败并附带错误消息。

PluginCall 的 resolve() 方法接受一个 JSObject,支持 JSON 可序列化的数据类型。以下是将数据返回给客户端的示例:

JSObject ret = new JSObject();
ret.put("added", true);
JSObject info = new JSObject();
info.put("id", "unique-id-1234");
ret.put("info", info);
call.resolve(ret);

要失败或拒绝调用,请使用 call.reject,传入错误字符串,并可选择传入错误码和 Exception 实例:

call.reject(exception.getLocalizedMessage(), null, exception);

持久化插件调用​

在大多数情况下,插件方法被调用来执行任务并可以立即完成。但有些情况下,您需要保留插件调用以便稍后访问。您可能希望这样做以定期返回数据,例如流式传输实时地理定位数据,或执行异步任务。

有关如何持久化插件调用的更多详细信息,请参阅关于保存插件调用的指南。

在插件加载时运行代码​

有时,插件可能需要在首次加载时运行一些代码。

为此,请提供 load() 方法的实现:

@Override
public void load() {
}

权限​

如果您的插件在 Android 上的功能需要最终用户的权限,那么您需要实现权限模式。

在遵循本节之前,请确保您已设置好权限别名和状态接口。如果尚未设置,请参阅 Web 指南中的权限部分。

注解变更​

仍在使用 @NativePlugin?请参阅升级指南切换到 @CapacitorPlugin。

 @CapacitorPlugin(
name = "FooBar",
+ permissions = {
+ @Permission(
+ alias = "camera",
+ strings = { Manifest.permission.CAMERA }
+ ),
+ @Permission(
+ alias = "storage",
+ strings = {
+ Manifest.permission.READ_EXTERNAL_STORAGE,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE
+ }
+ )
+ }
)
public class FooBarPlugin extends Plugin {
...

在 @CapacitorPlugin 注解中添加 permissions 属性,它是一个包含一个或多个 @Permission 注解的数组。每个 @Permission 注解包含零个或多个 Android 权限 strings 和一个描述用途的简短 alias。

将每个 @Permission 中的权限字符串按插件功能的独立模块进行分组。如果您的插件在其他平台上需要权限但在 Android 上不需要,则使用相同的别名定义一个 strings 为空数组的权限。这将导致该权限别名的权限请求结果自动返回为"已授权"。

@Permission(
alias = "notifications",
strings = {}
)

实现权限请求​

通过在 @CapacitorPlugin 注解中定义权限,checkPermissions() 和 requestPermissions() 方法应该可以完全正常工作。应用开发者可以根据需要手动请求权限。但是,最佳实践是同时将插件功能包装在自动权限请求中。

权限回调​

创建一个带有单个 PluginCall 参数的 void 方法,并使用 @PermissionCallback 注解,然后将该方法的名称作为字符串传递给权限请求调用。回调将在权限请求完成后运行。

@PluginMethod()
public void takePhoto(PluginCall call) {
if (getPermissionState("camera") != PermissionState.GRANTED) {
requestPermissionForAlias("camera", call, "cameraPermsCallback");
} else {
loadCamera(call);
}
}

@PermissionCallback
private void cameraPermsCallback(PluginCall call) {
if (getPermissionState("camera") == PermissionState.GRANTED) {
loadCamera(call);
} else {
call.reject("Permission is required to take a picture");
}
}

发起权限请求​

通过调用请求辅助方法之一来发起权限请求。

对于单个别名,可以使用 requestPermissionForAlias。可以向 requestPermissionForAliases 提供多个别名。使用 requestAllPermissions 请求插件注解中定义的所有权限。

 @PluginMethod()
public void takePhoto(PluginCall call) {
if (!hasRequiredPermissions()) {
+ requestAllPermissions(call, "cameraPermsCallback");
} else {
loadCamera(call);
}
}

@PermissionCallback
private void cameraPermsCallback(PluginCall call) {
...
}

Manifest​

将任何所需的安装时权限放在插件的 AndroidManifest.xml 中。不要添加运行时权限(会提示用户接受的权限)。这些应该由应用开发者添加到 Capacitor 应用的 manifest 中。请确保您的插件文档记录了应在应用中添加的任何必需运行时权限。

  <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mycompany.plugins.network">
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
</manifest>

错误处理​

不可用​

此错误可抛出以指示该功能目前无法使用,通常是因为它需要更新的 Android API 版本。

@PluginMethod
public void methodThatUsesNewAndroidAPI(PluginCall call) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// TODO implementation
} else {
call.unavailable("Not available on Android API 25 or earlier.");
}
}

建议尽可能优雅地降级旧 API 的体验。请谨慎使用 unavailable。

未实现​

使用此错误指示某个方法无法在 Android 上实现。

@PluginMethod
public void methodThatRequiresIOS(PluginCall call) {
call.unimplemented("Not implemented on Android.");
}

展示原生屏幕​

要在 Capacitor 屏幕之上展示原生屏幕,我们将使用 Android 的 Intents。Intents 允许您从应用或另一个应用启动 Activity。查看常见 Intents

无返回结果的 Intents​

大多数情况下,您只希望展示原生 Activity,这时您可以直接触发相关操作。

Intent intent = new Intent(Intent.ACTION_VIEW);
getActivity().startActivity(intent);

有返回结果的 Intents​

有时当您启动一个 Intent 时,您期望返回一些结果。这种情况下,您需要使用 startActivityForResult。

创建一个带有 PluginCall 和 ActivityResult 参数的回调方法来处理启动 Activity 的结果,并使用 @ActivityCallback 注解。将此方法的名称传递给 startActivityForResult,它将在启动的 Activity 完成时运行。

@CapacitorPlugin()
class ImagePicker extends Plugin {

@PluginMethod()
public void pickImage(PluginCall call) {
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType("image/*");

// 使用回调方法的名称启动 Activity 并获取结果
startActivityForResult(call, intent, "pickImageResult");
}

@ActivityCallback
private void pickImageResult(PluginCall call, ActivityResult result) {
if (call == null) {
return;
}

// 对结果数据进行处理
}
}

插件事件​

插件可以发出自己的事件,您可以通过像这样给插件对象添加监听器来监听:

import { MyPlugin } from 'my-plugin';

MyPlugin.addListener('myPluginEvent', (info: any) => {
console.log('myPluginEvent was fired');
});

从 Java 插件类发出事件:

JSObject ret = new JSObject();
ret.put("value", "some value");
notifyListeners("myPluginEvent", ret);

从插件对象移除监听器:

import { MyPlugin } from 'my-plugin';

const myPluginEventListener = await MyPlugin.addListener(
'myPluginEvent',
(info: any) => {
console.log('myPluginEvent was fired');
},
);

myPluginEventListener.remove();

也可以在 window 上触发全局事件。请参阅 triggerJSEvent 的文档。

覆盖导航​

Capacitor 插件可以覆盖 WebView 的导航。为此,插件可以重写 public Boolean shouldOverrideLoad(Uri url) 方法。 返回 true 会使 WebView 中止加载 URL。 返回 false 会使 WebView 继续加载 URL。 返回 null 将遵循 Capacitor 的默认策略。