uni-app Android 原生插件开发
核心知识点
1. Android 开发环境搭建
1.1 开发工具准备
- Android Studio:官方推荐的 Android 开发 IDE,下载地址:https://developer.android.com/studio
- JDK:Java 开发工具包,推荐使用 JDK 8 或以上版本
- Gradle:Android 构建工具,Android Studio 会自动安装
- SDK:Android SDK,包括不同版本的 Android 平台
1.2 环境配置
- 配置 JAVA_HOME:设置 JDK 安装路径
- 配置 ANDROID_HOME:设置 Android SDK 安装路径
- 配置 Gradle 镜像:使用国内镜像加速构建
2. Android 插件项目结构
2.1 基本目录结构
- 标准目录结构:
android/ ├── build.gradle # 项目构建配置 ├── gradle.properties # Gradle 属性配置 ├── gradlew # Gradle 包装脚本 ├── gradlew.bat # Windows 下的 Gradle 包装脚本 ├── local.properties # 本地属性配置 ├── settings.gradle # 项目设置 └── src/ └── main/ ├── AndroidManifest.xml # 应用清单文件 ├── java/ # Java 源代码目录 │ └── com/ │ └── example/ │ └── plugin/ │ ├── Module.java # Module 插件实现 │ └── Component.java # Component 插件实现 └── res/ # 资源目录 ├── drawable/ # 图片资源 ├── layout/ # 布局文件 └── values/ # 字符串、样式等资源
2.2 关键文件说明
- build.gradle:定义插件的依赖、构建配置等
- AndroidManifest.xml:声明插件的权限、组件等
- Module.java:实现 Module 接口,提供 JavaScript 调用的方法
- Component.java:实现 Component 接口,提供自定义 UI 组件
3. Android 插件开发基础
3.1 Module 插件开发
- 继承 UniModule:自定义 Module 类需要继承
io.dcloud.feature.uniapp.common.UniModule - 导出方法:使用
@UniJSMethod注解导出方法,供 JavaScript 调用 - 异步回调:使用
UniJSCallback实现异步回调 - 上下文获取:通过
mWXSDKInstance.getContext()获取 Android 上下文
3.2 Component 插件开发
- 继承 UniComponent:自定义 Component 类需要继承
io.dcloud.feature.uniapp.ui.component.UniComponent - 视图创建:在
createView方法中创建 Android 视图 - 属性设置:在
updateAttrs方法中处理组件属性更新 - 事件处理:使用
fireEvent方法触发自定义事件
3.3 权限管理
- 声明权限:在
AndroidManifest.xml中声明插件需要的权限 - 运行时权限:对于危险权限,需要在运行时动态申请
- 权限检查:在使用需要权限的功能前,检查是否已获得权限
4. 插件打包与集成
4.1 插件打包
- 构建 AAR:在 Android Studio 中构建 AAR 文件
- 配置 package.json:在插件根目录创建 package.json 文件,配置插件信息
- 创建 zip 包:将 AAR 文件和 package.json 等文件打包成 zip 文件
4.2 插件集成
- 本地集成:将插件 zip 包导入到 uni-app 项目中
- 云端集成:将插件发布到 uni-app 插件市场,然后在项目中引用
- 配置 manifest.json:在 uni-app 项目的 manifest.json 文件中配置插件
5. 常见问题与解决方案
5.1 依赖冲突
- 原因:插件依赖的库与 uni-app 或其他插件依赖的库版本冲突
- 解决方案:使用
exclude排除冲突的依赖,或使用force强制使用特定版本
5.2 混淆问题
- 原因:代码混淆导致插件的类或方法被重命名,无法被 uni-app 调用
- 解决方案:在
proguard-rules.pro文件中添加混淆规则,保留插件的类和方法
5.3 版本兼容
- 原因:不同版本的 Android 系统 API 不同,导致插件在某些设备上无法正常工作
- 解决方案:使用
Build.VERSION.SDK_INT检查 Android 版本,根据版本使用不同的 API
实用案例分析
案例一:开发一个 Android 原生 Module 插件
问题描述
需要开发一个 Android 原生插件,提供震动功能。
解决方案
创建一个 Android Module 插件,实现震动功能的方法。
代码示例
- 创建插件目录结构
vibrate-plugin/
├── android/
│ ├── build.gradle
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ └── java/
│ └── com/
│ └── example/
│ └── vibrate/
│ └── VibrateModule.java
└── package.json- 配置 build.gradle
apply plugin: 'com.android.library'
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
minSdkVersion 21
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.3.0'
// 引入 uni-app 插件开发库
implementation 'com.uniplugin:uniplugin-module:1.0.0'
}- 配置 AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.vibrate">
<!-- 声明震动权限 -->
<uses-permission android:name="android.permission.VIBRATE" />
</manifest>- 实现 VibrateModule.java
package com.example.vibrate;
import android.content.Context;
import android.os.Vibrator;
import io.dcloud.feature.uniapp.common.UniModule;
import io.dcloud.feature.uniapp.bridge.UniJSCallback;
public class VibrateModule extends UniModule {
// 震动方法
@UniJSMethod
public void vibrate(int duration, UniJSCallback callback) {
Context context = mWXSDKInstance.getContext();
Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
if (vibrator != null) {
// 检查设备是否支持震动
if (vibrator.hasVibrator()) {
// 震动指定时长
vibrator.vibrate(duration);
if (callback != null) {
callback.invoke("success");
}
} else {
if (callback != null) {
callback.invoke("device not support vibrate");
}
}
} else {
if (callback != null) {
callback.invoke("vibrator service not available");
}
}
}
// 自定义震动模式
@UniJSMethod
public void vibratePattern(int[] pattern, int repeat, UniJSCallback callback) {
Context context = mWXSDKInstance.getContext();
Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
if (vibrator != null) {
// 检查设备是否支持震动
if (vibrator.hasVibrator()) {
// 按照指定模式震动
// pattern: 震动和暂停的时间序列,单位为毫秒
// repeat: 重复次数,-1 表示不重复
vibrator.vibrate(pattern, repeat);
if (callback != null) {
callback.invoke("success");
}
} else {
if (callback != null) {
callback.invoke("device not support vibrate");
}
}
} else {
if (callback != null) {
callback.invoke("vibrator service not available");
}
}
}
// 取消震动
@UniJSMethod
public void cancelVibrate(UniJSCallback callback) {
Context context = mWXSDKInstance.getContext();
Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
if (vibrator != null) {
vibrator.cancel();
if (callback != null) {
callback.invoke("success");
}
} else {
if (callback != null) {
callback.invoke("vibrator service not available");
}
}
}
}- 配置 package.json
{
"name": "vibrate-plugin",
"version": "1.0.0",
"description": "提供设备震动功能的 Android 原生插件",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"uni-app",
"plugin",
"android",
"vibrate"
],
"author": "example",
"license": "MIT",
"uni-app": {
"plugins": {
"Vibrate": {
"version": "1.0.0",
"provider": "com.example.vibrate"
}
}
},
"dcloudPlugins": {
"android": {
"plugins": [
{
"type": "module",
"name": "Vibrate",
"class": "com.example.vibrate.VibrateModule"
}
]
}
}
}- 创建 index.js
const vibrate = uni.requireNativePlugin('Vibrate');
export default {
// 震动指定时长
vibrate(duration, callback) {
vibrate.vibrate(duration, callback);
},
// 自定义震动模式
vibratePattern(pattern, repeat, callback) {
vibrate.vibratePattern(pattern, repeat, callback);
},
// 取消震动
cancelVibrate(callback) {
vibrate.cancelVibrate(callback);
}
};案例二:开发一个 Android 原生 Component 插件
问题描述
需要开发一个 Android 原生插件,提供一个自定义的圆形进度条组件。
解决方案
创建一个 Android Component 插件,实现一个圆形进度条组件。
代码示例
- 创建插件目录结构
circle-progress-plugin/
├── android/
│ ├── build.gradle
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ └── java/
│ └── com/
│ └── example/
│ └── circleprogress/
│ └── CircleProgressComponent.java
└── package.json- 配置 build.gradle
apply plugin: 'com.android.library'
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
minSdkVersion 21
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.3.0'
// 引入 uni-app 插件开发库
implementation 'com.uniplugin:uniplugin-ui:1.0.0'
}- 实现 CircleProgressComponent.java
package com.example.circleprogress;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.view.View;
import io.dcloud.feature.uniapp.ui.component.UniComponent;
import io.dcloud.feature.uniapp.ui.component.UniVContainer;
public class CircleProgressComponent extends UniComponent<View> {
private CircleProgressView mCircleProgressView;
private float mProgress = 0;
private int mProgressColor = Color.BLUE;
private int mBackgroundColor = Color.GRAY;
private float mStrokeWidth = 10;
private float mRadius = 50;
public CircleProgressComponent(UniVContainer uniVContainer, Context context) {
super(uniVContainer, context);
}
@Override
protected View createView(Context context) {
mCircleProgressView = new CircleProgressView(context);
updateProgress();
return mCircleProgressView;
}
@Override
public void updateAttrs(Object attrs) {
super.updateAttrs(attrs);
if (attrs instanceof org.json.JSONObject) {
org.json.JSONObject jsonObject = (org.json.JSONObject) attrs;
try {
// 处理进度属性
if (jsonObject.has("progress")) {
mProgress = (float) jsonObject.getDouble("progress");
updateProgress();
}
// 处理进度颜色属性
if (jsonObject.has("progressColor")) {
String colorStr = jsonObject.getString("progressColor");
mProgressColor = Color.parseColor(colorStr);
updateProgress();
}
// 处理背景颜色属性
if (jsonObject.has("backgroundColor")) {
String colorStr = jsonObject.getString("backgroundColor");
mBackgroundColor = Color.parseColor(colorStr);
updateProgress();
}
// 处理线宽属性
if (jsonObject.has("strokeWidth")) {
mStrokeWidth = (float) jsonObject.getDouble("strokeWidth");
updateProgress();
}
// 处理半径属性
if (jsonObject.has("radius")) {
mRadius = (float) jsonObject.getDouble("radius");
updateProgress();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
private void updateProgress() {
if (mCircleProgressView != null) {
mCircleProgressView.setProgress(mProgress);
mCircleProgressView.setProgressColor(mProgressColor);
mCircleProgressView.setBackgroundColor(mBackgroundColor);
mCircleProgressView.setStrokeWidth(mStrokeWidth);
mCircleProgressView.setRadius(mRadius);
mCircleProgressView.invalidate();
}
}
// 自定义圆形进度条视图
private class CircleProgressView extends View {
private Paint mProgressPaint;
private Paint mBackgroundPaint;
private RectF mRectF;
public CircleProgressView(Context context) {
super(context);
init();
}
private void init() {
// 初始化进度画笔
mProgressPaint = new Paint();
mProgressPaint.setAntiAlias(true);
mProgressPaint.setStyle(Paint.Style.STROKE);
mProgressPaint.setStrokeCap(Paint.Cap.ROUND);
// 初始化背景画笔
mBackgroundPaint = new Paint();
mBackgroundPaint.setAntiAlias(true);
mBackgroundPaint.setStyle(Paint.Style.STROKE);
mBackgroundPaint.setStrokeCap(Paint.Cap.ROUND);
// 初始化矩形
mRectF = new RectF();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getWidth();
int height = getHeight();
int centerX = width / 2;
int centerY = height / 2;
// 计算实际半径
float actualRadius = Math.min(centerX, centerY) - mStrokeWidth / 2;
// 设置矩形范围
mRectF.set(centerX - actualRadius, centerY - actualRadius,
centerX + actualRadius, centerY + actualRadius);
// 绘制背景圆环
mBackgroundPaint.setColor(mBackgroundColor);
mBackgroundPaint.setStrokeWidth(mStrokeWidth);
canvas.drawCircle(centerX, centerY, actualRadius, mBackgroundPaint);
// 绘制进度圆环
mProgressPaint.setColor(mProgressColor);
mProgressPaint.setStrokeWidth(mStrokeWidth);
float sweepAngle = 360 * mProgress / 100;
canvas.drawArc(mRectF, -90, sweepAngle, false, mProgressPaint);
}
public void setProgress(float progress) {
mProgress = progress;
}
public void setProgressColor(int color) {
mProgressColor = color;
}
public void setBackgroundColor(int color) {
mBackgroundColor = color;
}
public void setStrokeWidth(float width) {
mStrokeWidth = width;
}
public void setRadius(float radius) {
mRadius = radius;
}
}
}- 配置 package.json
{
"name": "circle-progress-plugin",
"version": "1.0.0",
"description": "提供圆形进度条组件的 Android 原生插件",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"uni-app",
"plugin",
"android",
"circle-progress"
],
"author": "example",
"license": "MIT",
"uni-app": {
"plugins": {
"CircleProgress": {
"version": "1.0.0",
"provider": "com.example.circleprogress"
}
}
},
"dcloudPlugins": {
"android": {
"plugins": [
{
"type": "component",
"name": "circle-progress",
"class": "com.example.circleprogress.CircleProgressComponent"
}
]
}
}
}- 创建 index.js
export default {
name: 'circle-progress',
props: {
progress: {
type: Number,
default: 0
},
progressColor: {
type: String,
default: '#007AFF'
},
backgroundColor: {
type: String,
default: '#E0E0E0'
},
strokeWidth: {
type: Number,
default: 10
},
radius: {
type: Number,
default: 50
}
}
};案例三:在 uni-app 中使用 Android 原生插件
问题描述
需要在 uni-app 应用中使用刚才开发的震动插件和圆形进度条插件。
解决方案
在 uni-app 项目中引入并使用 Android 原生插件。
代码示例
- 在 manifest.json 中配置插件
{
"name": "uni-app-demo",
"version": "1.0.0",
"description": "uni-app 演示应用",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"uni-app"
],
"author": "example",
"license": "MIT",
"dependencies": {
"@dcloudio/uni-app": "^2.0.0"
},
"uni-app": {
"plugins": {
"Vibrate": {
"version": "1.0.0",
"provider": "com.example.vibrate"
},
"CircleProgress": {
"version": "1.0.0",
"provider": "com.example.circleprogress"
}
}
}
}- 在页面中使用震动插件
<template>
<view class="vibrate-demo">
<h2>震动功能演示</h2>
<button @click="vibrateShort">短震动 (100ms)</button>
<button @click="vibrateLong">长震动 (500ms)</button>
<button @click="vibratePattern">自定义震动模式</button>
<button @click="cancelVibrate">取消震动</button>
</view>
</template>
<script>
import vibratePlugin from '@/plugins/vibrate-plugin/index.js';
export default {
methods: {
vibrateShort() {
vibratePlugin.vibrate(100, (res) => {
console.log('震动结果:', res);
uni.showToast({ title: res, icon: 'none' });
});
},
vibrateLong() {
vibratePlugin.vibrate(500, (res) => {
console.log('震动结果:', res);
uni.showToast({ title: res, icon: 'none' });
});
},
vibratePattern() {
// 震动模式:[震动时长, 暂停时长, 震动时长, 暂停时长, ...]
const pattern = [100, 100, 200, 100, 300];
// 重复次数:-1 表示不重复
const repeat = -1;
vibratePlugin.vibratePattern(pattern, repeat, (res) => {
console.log('震动结果:', res);
uni.showToast({ title: res, icon: 'none' });
});
},
cancelVibrate() {
vibratePlugin.cancelVibrate((res) => {
console.log('取消震动结果:', res);
uni.showToast({ title: res, icon: 'none' });
});
}
}
};
</script>
<style scoped>
.vibrate-demo {
padding: 20rpx;
}
h2 {
margin-bottom: 20rpx;
font-size: 36rpx;
font-weight: bold;
}
button {
width: 100%;
padding: 20rpx;
margin-bottom: 20rpx;
background-color: #1890ff;
color: #fff;
border: none;
border-radius: 8rpx;
}
</style>- 在页面中使用圆形进度条插件
<template>
<view class="circle-progress-demo">
<h2>圆形进度条演示</h2>
<view class="progress-container">
<circle-progress
:progress="progress"
:progressColor="progressColor"
:backgroundColor="backgroundColor"
:strokeWidth="strokeWidth"
:radius="radius"
/>
</view>
<view class="controls">
<text>进度: {{ progress }}%</text>
<slider @change="onProgressChange" :value="progress" :min="0" :max="100" />
<text>线宽: {{ strokeWidth }}</text>
<slider @change="onStrokeWidthChange" :value="strokeWidth" :min="1" :max="30" />
<text>半径: {{ radius }}</text>
<slider @change="onRadiusChange" :value="radius" :min="20" :max="100" />
<view class="color-pickers">
<text>进度颜色:</text>
<view class="color-buttons">
<button
v-for="color in colors"
:key="color"
:style="{ backgroundColor: color }"
@click="progressColor = color"
></button>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
progress: 50,
progressColor: '#007AFF',
backgroundColor: '#E0E0E0',
strokeWidth: 10,
radius: 60,
colors: ['#007AFF', '#FF3B30', '#34C759', '#FF9500', '#AF52DE', '#FF2D55']
};
},
methods: {
onProgressChange(e) {
this.progress = e.detail.value;
},
onStrokeWidthChange(e) {
this.strokeWidth = e.detail.value;
},
onRadiusChange(e) {
this.radius = e.detail.value;
}
}
};
</script>
<style scoped>
.circle-progress-demo {
padding: 20rpx;
}
h2 {
margin-bottom: 20rpx;
font-size: 36rpx;
font-weight: bold;
}
.progress-container {
display: flex;
justify-content: center;
align-items: center;
height: 300rpx;
margin-bottom: 40rpx;
}
.controls {
display: flex;
flex-direction: column;
gap: 20rpx;
}
slider {
width: 100%;
}
.color-pickers {
margin-top: 20rpx;
}
.color-buttons {
display: flex;
gap: 10rpx;
margin-top: 10rpx;
}
.color-buttons button {
width: 60rpx;
height: 60rpx;
border-radius: 50%;
border: 1rpx solid #E0E0E0;
}
</style>学习目标
- 了解 Android 开发环境的搭建和配置
- 掌握 uni-app Android 原生插件的目录结构和配置方法
- 学会开发 Android Module 插件和 Component 插件
- 理解 Android 原生插件的生命周期和工作原理
- 掌握插件的打包、集成和使用方法
- 能够解决 Android 原生插件开发中遇到的常见问题
Android 原生插件开发最佳实践
- 遵循 Android 开发规范:按照 Google 推荐的 Android 开发规范编写代码
- 注重代码质量:使用清晰的命名、适当的注释、合理的代码结构
- 处理异常情况:合理处理各种异常,提供友好的错误信息
- 优化性能:注意内存管理,避免内存泄漏,优化 UI 渲染性能
- 测试兼容性:在不同版本的 Android 设备上测试插件
- 提供详细文档:为插件提供清晰的使用文档和示例代码
总结
Android 原生插件开发是 uni-app 应用扩展功能的重要手段,通过开发 Android 原生插件,可以访问 Android 设备的底层 API,实现更复杂的功能,提升应用性能。
在本教程中,我们学习了 Android 开发环境的搭建、Android 插件项目结构、Module 和 Component 插件的开发方法、插件打包与集成等核心知识点。通过两个实用案例,我们了解了如何开发一个震动功能的 Module 插件和一个圆形进度条的 Component 插件,以及如何在 uni-app 应用中使用这些插件。
要成为一名优秀的 uni-app Android 原生插件开发者,需要掌握以下技能:
- 熟悉 Android 原生开发,包括 Java 语言、Android SDK、Android Studio 等
- 了解 uni-app 插件开发规范和 API
- 具备良好的 API 设计能力
- 注重代码质量和性能优化
- 具备解决问题的能力
通过不断学习和实践,你可以开发出功能强大、性能优异的 Android 原生插件,为 uni-app 生态系统做出贡献。