自定义插件 #

一、创建插件项目 #

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