自定义插件 #
一、创建插件项目 #
1.1 使用CLI创建 #
bash
# 创建插件项目
npm init @capacitor/plugin my-custom-plugin
# 进入项目目录
cd my-custom-plugin
1.2 项目结构 #
text
my-custom-plugin/
├── src/
│ ├── definitions.ts # 接口定义
│ ├── index.ts # 入口文件
│ └── web.ts # Web实现
│
├── ios/
│ ├── Sources/
│ │ └── MyCustomPlugin/
│ │ ├── MyCustomPlugin.swift
│ │ └── MyCustomPlugin.m
│ └── MyCustomPlugin.podspec
│
├── android/
│ └── src/main/
│ ├── java/com/example/mycustomplugin/
│ │ └── MyCustomPlugin.java
│ └── AndroidManifest.xml
│
├── package.json
├── tsconfig.json
├── rollup.config.js
└── README.md
二、定义插件接口 #
2.1 创建接口文件 #
typescript
// src/definitions.ts
import type { PluginListenerHandle } from '@capacitor/core';
export interface MyCustomPlugin {
// 基本方法
echo(options: EchoOptions): Promise<EchoResult>;
// 带回调的方法
doSomething(options: DoSomethingOptions): Promise<DoSomethingResult>;
// 异步操作
startTask(options: StartTaskOptions): Promise<void>;
stopTask(): Promise<void>;
// 事件监听
addListener(
eventName: 'taskProgress',
listener: (data: TaskProgressData) => void
): Promise<PluginListenerHandle>;
addListener(
eventName: 'taskComplete',
listener: (data: TaskCompleteData) => void
): Promise<PluginListenerHandle>;
// 移除监听器
removeAllListeners(): Promise<void>;
}
// 选项接口
export interface EchoOptions {
value: string;
}
export interface EchoResult {
value: string;
}
export interface DoSomethingOptions {
param1: string;
param2?: number;
flag?: boolean;
}
export interface DoSomethingResult {
success: boolean;
message: string;
data?: any;
}
export interface StartTaskOptions {
taskId: string;
duration: number;
}
export interface TaskProgressData {
taskId: string;
progress: number;
status: string;
}
export interface TaskCompleteData {
taskId: string;
success: boolean;
result?: any;
}
2.2 创建入口文件 #
typescript
// src/index.ts
import { registerPlugin } from '@capacitor/core';
import type { MyCustomPlugin } from './definitions';
const MyCustomPlugin = registerPlugin<MyCustomPlugin>('MyCustomPlugin', {
web: () => import('./web').then(m => new m.MyCustomPluginWeb())
});
export * from './definitions';
export { MyCustomPlugin };
三、Web实现 #
3.1 创建Web实现类 #
typescript
// src/web.ts
import { WebPlugin } from '@capacitor/core';
import type {
MyCustomPlugin,
EchoOptions,
EchoResult,
DoSomethingOptions,
DoSomethingResult,
StartTaskOptions,
TaskProgressData,
TaskCompleteData
} from './definitions';
export class MyCustomPluginWeb extends WebPlugin implements MyCustomPlugin {
private tasks: Map<string, any> = new Map();
constructor() {
super();
console.log('MyCustomPluginWeb initialized');
}
async echo(options: EchoOptions): Promise<EchoResult> {
console.log('ECHO', options);
return { value: options.value };
}
async doSomething(options: DoSomethingOptions): Promise<DoSomethingResult> {
console.log('doSomething called with:', options);
// Web实现逻辑
const result: DoSomethingResult = {
success: true,
message: 'Operation completed on web',
data: {
input: options,
platform: 'web'
}
};
return result;
}
async startTask(options: StartTaskOptions): Promise<void> {
const { taskId, duration } = options;
if (this.tasks.has(taskId)) {
throw new Error(`Task ${taskId} already exists`);
}
// 模拟任务执行
const startTime = Date.now();
const interval = setInterval(() => {
const elapsed = Date.now() - startTime;
const progress = Math.min(elapsed / duration, 1);
// 发送进度事件
const progressData: TaskProgressData = {
taskId,
progress,
status: progress < 1 ? 'running' : 'complete'
};
this.notifyListeners('taskProgress', progressData);
if (progress >= 1) {
clearInterval(interval);
this.tasks.delete(taskId);
// 发送完成事件
const completeData: TaskCompleteData = {
taskId,
success: true,
result: { duration: elapsed }
};
this.notifyListeners('taskComplete', completeData);
}
}, 100);
this.tasks.set(taskId, interval);
}
async stopTask(): Promise<void> {
// 停止所有任务
this.tasks.forEach((interval, taskId) => {
clearInterval(interval);
const completeData: TaskCompleteData = {
taskId,
success: false,
result: { reason: 'stopped' }
};
this.notifyListeners('taskComplete', completeData);
});
this.tasks.clear();
}
}
四、iOS实现 #
4.1 创建Swift文件 #
swift
// ios/Sources/MyCustomPlugin/MyCustomPlugin.swift
import Foundation
import Capacitor
@objc(MyCustomPlugin)
public class MyCustomPlugin: CAPPlugin {
private var tasks: [String: Timer] = [:]
@objc func echo(_ call: CAPPluginCall) {
let value = call.getString("value") ?? ""
call.resolve([
"value": value
])
}
@objc func doSomething(_ call: CAPPluginCall) {
guard let param1 = call.getString("param1") else {
call.reject("param1 is required")
return
}
let param2 = call.getInt("param2") ?? 0
let flag = call.getBool("flag") ?? false
// 执行操作
let result: [String: Any] = [
"success": true,
"message": "Operation completed on iOS",
"data": [
"param1": param1,
"param2": param2,
"flag": flag,
"platform": "ios"
]
]
call.resolve(result)
}
@objc func startTask(_ call: CAPPluginCall) {
guard let taskId = call.getString("taskId") else {
call.reject("taskId is required")
return
}
let duration = call.getDouble("duration") ?? 5000
if tasks[taskId] != nil {
call.reject("Task \(taskId) already exists")
return
}
let startTime = Date()
let timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [weak self] timer in
guard let self = self else { return }
let elapsed = Date().timeIntervalSince(startTime) * 1000
let progress = min(elapsed / duration, 1.0)
// 发送进度事件
self.notifyListeners("taskProgress", data: [
"taskId": taskId,
"progress": progress,
"status": progress < 1 ? "running" : "complete"
])
if progress >= 1 {
timer.invalidate()
self.tasks.removeValue(forKey: taskId)
// 发送完成事件
self.notifyListeners("taskComplete", data: [
"taskId": taskId,
"success": true,
"result": ["duration": elapsed]
])
}
}
tasks[taskId] = timer
call.resolve()
}
@objc func stopTask(_ call: CAPPluginCall) {
for (taskId, timer) in tasks {
timer.invalidate()
notifyListeners("taskComplete", data: [
"taskId": taskId,
"success": false,
"result": ["reason": "stopped"]
])
}
tasks.removeAll()
call.resolve()
}
}
4.2 创建Objective-C桥接文件 #
objectivec
// ios/Sources/MyCustomPlugin/MyCustomPlugin.m
#import <Capacitor/Capacitor.h>
CAP_PLUGIN(MyCustomPlugin, "MyCustomPlugin",
CAP_PLUGIN_METHOD(echo, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(doSomething, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(startTask, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(stopTask, CAPPluginReturnPromise);
)
4.3 创建Podspec文件 #
ruby
# ios/MyCustomPlugin.podspec
Pod::Spec.new do |s|
s.name = 'MyCustomPlugin'
s.version = '0.0.1'
s.summary = 'My Custom Capacitor Plugin'
s.license = 'MIT'
s.homepage = 'https://github.com/example/my-custom-plugin'
s.author = 'Your Name'
s.source = { :git => 'https://github.com/example/my-custom-plugin.git', :tag => s.version.to_s }
s.ios.deployment_target = '13.0'
s.swift_version = '5.1'
s.source_files = 'Sources/**/*.{swift,m}'
s.dependency 'Capacitor'
end
五、Android实现 #
5.1 创建Java文件 #
java
// android/src/main/java/com/example/mycustomplugin/MyCustomPlugin.java
package com.example.mycustomplugin;
import android.util.Log;
import com.getcapacitor.JSObject;
import com.getcapacitor.Plugin;
import com.getcapacitor.PluginCall;
import com.getcapacitor.PluginMethod;
import com.getcapacitor.annotation.CapacitorPlugin;
import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
@CapacitorPlugin(name = "MyCustomPlugin")
public class MyCustomPlugin extends Plugin {
private static final String TAG = "MyCustomPlugin";
private Map<String, Timer> tasks = new HashMap<>();
@PluginMethod
public void echo(PluginCall call) {
String value = call.getString("value", "");
JSObject result = new JSObject();
result.put("value", value);
call.resolve(result);
}
@PluginMethod
public void doSomething(PluginCall call) {
String param1 = call.getString("param1");
if (param1 == null) {
call.reject("param1 is required");
return;
}
int param2 = call.getInt("param2", 0);
boolean flag = call.getBoolean("flag", false);
// 执行操作
JSObject result = new JSObject();
result.put("success", true);
result.put("message", "Operation completed on Android");
JSObject data = new JSObject();
data.put("param1", param1);
data.put("param2", param2);
data.put("flag", flag);
data.put("platform", "android");
result.put("data", data);
call.resolve(result);
}
@PluginMethod
public void startTask(PluginCall call) {
String taskId = call.getString("taskId");
if (taskId == null) {
call.reject("taskId is required");
return;
}
if (tasks.containsKey(taskId)) {
call.reject("Task " + taskId + " already exists");
return;
}
long duration = call.getLong("duration", 5000L);
long startTime = System.currentTimeMillis();
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
long elapsed = System.currentTimeMillis() - startTime;
double progress = Math.min((double) elapsed / duration, 1.0);
// 发送进度事件
JSObject progressData = new JSObject();
progressData.put("taskId", taskId);
progressData.put("progress", progress);
progressData.put("status", progress < 1 ? "running" : "complete");
notifyListeners("taskProgress", progressData);
if (progress >= 1) {
cancel();
tasks.remove(taskId);
// 发送完成事件
JSObject completeData = new JSObject();
completeData.put("taskId", taskId);
completeData.put("success", true);
JSObject result = new JSObject();
result.put("duration", elapsed);
completeData.put("result", result);
notifyListeners("taskComplete", completeData);
}
}
}, 0, 100);
tasks.put(taskId, timer);
call.resolve();
}
@PluginMethod
public void stopTask(PluginCall call) {
for (Map.Entry<String, Timer> entry : tasks.entrySet()) {
String taskId = entry.getKey();
Timer timer = entry.getValue();
timer.cancel();
JSObject completeData = new JSObject();
completeData.put("taskId", taskId);
completeData.put("success", false);
JSObject result = new JSObject();
result.put("reason", "stopped");
completeData.put("result", result);
notifyListeners("taskComplete", completeData);
}
tasks.clear();
call.resolve();
}
}
5.2 配置AndroidManifest.xml #
xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.mycustomplugin">
<!-- 添加需要的权限 -->
<!-- <uses-permission android:name="android.permission.CAMERA" /> -->
</manifest>
六、构建和发布 #
6.1 配置package.json #
json
{
"name": "@mycompany/my-custom-plugin",
"version": "1.0.0",
"description": "My Custom Capacitor Plugin",
"main": "dist/plugin.cjs.js",
"module": "dist/esm/index.js",
"types": "dist/esm/index.d.ts",
"files": [
"dist/",
"ios/",
"android/"
],
"keywords": [
"capacitor",
"plugin",
"native"
],
"author": "Your Name",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/example/my-custom-plugin.git"
},
"peerDependencies": {
"@capacitor/core": "^6.0.0"
},
"devDependencies": {
"@capacitor/core": "^6.0.0",
"@capacitor/cli": "^6.0.0",
"typescript": "^5.0.0",
"rollup": "^3.0.0",
"@rollup/plugin-node-resolve": "^15.0.0",
"@rollup/plugin-typescript": "^11.0.0"
},
"scripts": {
"build": "rollup -c",
"prepublishOnly": "npm run build"
},
"capacitor": {
"ios": {
"src": "ios"
},
"android": {
"src": "android"
}
}
}
6.2 构建插件 #
bash
# 安装依赖
npm install
# 构建
npm run build
6.3 本地测试 #
bash
# 在项目中链接本地插件
cd /path/to/your/app
npm link /path/to/my-custom-plugin
# 同步
npx cap sync
七、使用自定义插件 #
7.1 安装插件 #
bash
npm install @mycompany/my-custom-plugin
npx cap sync
7.2 在应用中使用 #
typescript
import { MyCustomPlugin } from '@mycompany/my-custom-plugin';
// 调用方法
async function testPlugin() {
// Echo测试
const echoResult = await MyCustomPlugin.echo({ value: 'Hello' });
console.log('Echo:', echoResult.value);
// 执行操作
const result = await MyCustomPlugin.doSomething({
param1: 'test',
param2: 123,
flag: true
});
console.log('Result:', result);
// 监听事件
await MyCustomPlugin.addListener('taskProgress', (data) => {
console.log('Progress:', data.progress);
});
await MyCustomPlugin.addListener('taskComplete', (data) => {
console.log('Complete:', data);
});
// 启动任务
await MyCustomPlugin.startTask({
taskId: 'task-1',
duration: 3000
});
}
八、插件最佳实践 #
8.1 错误处理 #
typescript
// 统一错误处理
export class MyPluginError extends Error {
constructor(
message: string,
public code: string,
public data?: any
) {
super(message);
this.name = 'MyPluginError';
}
}
// 使用
async doSomething(options: DoSomethingOptions): Promise<DoSomethingResult> {
if (!options.param1) {
throw new MyPluginError('param1 is required', 'INVALID_ARGUMENT');
}
try {
// 执行操作
return { success: true, message: 'OK' };
} catch (error) {
throw new MyPluginError(
error.message,
'OPERATION_FAILED',
{ originalError: error }
);
}
}
8.2 权限处理 #
typescript
// 检查和请求权限
export interface PermissionStatus {
camera: PermissionState;
microphone: PermissionState;
}
async checkPermissions(): Promise<PermissionStatus> {
// 实现权限检查
}
async requestPermissions(): Promise<PermissionStatus> {
// 实现权限请求
}
8.3 平台兼容 #
typescript
// 检查平台支持
async isSupported(): Promise<boolean> {
const { platform } = await Device.getInfo();
return ['ios', 'android', 'web'].includes(platform);
}
// 平台特定实现
async doSomething(options: DoSomethingOptions): Promise<DoSomethingResult> {
const supported = await this.isSupported();
if (!supported) {
throw new MyPluginError('Platform not supported', 'UNSUPPORTED_PLATFORM');
}
// 继续执行
}
九、总结 #
9.1 开发流程 #
text
1. 定义接口 → definitions.ts
2. Web实现 → web.ts
3. iOS实现 → Swift/Objective-C
4. Android实现 → Java
5. 构建发布 → npm
9.2 下一步 #
了解自定义插件开发后,让我们学习 插件发布!
最后更新:2026-03-28