0%

uni-app Android 开发杂记

最近在使用 uni-app 开发 Android 应用,过程中涉及自定义基座、原生插件接入、微信登录、流式传输等内容。这里把踩过的坑和关键配置整理一下,方便后续回顾。

自定义基座

下载源码

先到 DCloud 官方文档下载自定义基座的基础代码:Android 离线 SDK 文档

下载后主要使用其中的 HBuilder-Integrate-AS 文件包。

增加自定义配置

1. 配置 Gradle 依赖

~\HBuilder-Integrate-AS\simpleDemo\build.gradle 中增加或确认以下依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
dependencies {
implementation fileTree(dir: 'libs', include: ['*.aar', '*.jar'], exclude: [])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
implementation 'androidx.core:core:1.1.0'
implementation "androidx.fragment:fragment:1.1.0"
implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'com.facebook.fresco:fresco:2.5.0'
implementation "com.facebook.fresco:animated-gif:2.5.0"
implementation 'com.github.bumptech.glide:glide:4.9.0'
implementation 'androidx.webkit:webkit:1.5.0'
implementation 'com.tencent.mm.opensdk:wechat-sdk-android:+'

// 自定义基座配置
implementation "com.alibaba:fastjson:1.2.83"
implementation "com.squareup.okhttp3:okhttp:3.12.12"
implementation("net.lingala.zip4j:zip4j:2.11.5")
implementation 'com.squareup.okio:okio:1.15.0'

debugImplementation fileTree(includes: ['.jar', '.aar'], dir: 'debug/libs')
}

2. 配置 DCloud AppKey

AndroidManifest.xmlapplication 节点中增加 DCloud 的 appkey

1
2
3
<meta-data
android:name="dcloud_appkey"
android:value="填写你的appkey" />

这里的 appkey 需要到 DCloud 后台申请,并且要和 Android 证书信息匹配。

3. 可选:接入微信能力

如果项目需要使用微信登录、分享等能力,需要在同一个位置继续增加微信相关配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<meta-data android:value="填写你的APPID" android:name="WX_APPID" />

<activity
android:name="这里填写WXEntryActivity在项目中的路径"
android:label="@string/app_name"
android:exported="true"
android:launchMode="singleTop"
android:theme="@android:style/Theme.Translucent.NoTitleBar">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="填写你的APPID" />
</intent-filter>
</activity>

我是在 src/main/java/cn.com.wxapi 目录下创建的 WXEntryActivity 文件,内容如下:

1
2
3
4
5
6
7
8
9
10
11
package cn.com.wxapi; // 确保包名和文件路径一致

import io.dcloud.feature.oauth.weixin.AbsWXCallbackActivity;

public class WXEntryActivity extends AbsWXCallbackActivity {

// 这里不需要写额外代码。
// 微信回调逻辑由父类 AbsWXCallbackActivity 处理,
// 最终会把结果传递给 uni-app 前端。

}

4. 导入 data 文件

将 SDK 中的 SDK/assets/data/ 目录导入到项目的 main/assets/data/ 中。

导入后需要修改 dcloud_control.xml 中的 appid,确保它和当前 uni-app 项目保持一致。

运行 Android 项目

运行 Android 项目时,需要先用 HBuilderX 生成 App 资源,然后把生成后的代码放到原生项目的 main/assets/apps/ 目录中。

开发中遇到的问题

流式传输

uni-app 在 Android 端处理流式响应时,直接使用普通请求并不方便。我的处理方式是通过 renderjs 配合 fetch-event-source 实现 SSE 流式传输。

示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
h5PostStream(url, data) {
let controller = null;
let onChunkCallback = null;
let onErrorCallback = null;
let onCompleteCallback = null;

const streamTask = {
// 设置数据块接收回调
onChunkReceived(callback) {
onChunkCallback = callback;
},

// 设置错误回调
onError(callback) {
onErrorCallback = callback;
},

// 设置完成回调
onComplete(callback) {
onCompleteCallback = callback;
},

// 中止请求
abort() {
if (controller) {
controller.abort();
}
},
};

// 启动 SSE 连接
setTimeout(() => {
controller = new AbortController();

fetchEventSource(baseurl + url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: getToken(),
Accept: 'text/event-stream',
},
body: JSON.stringify(data),
signal: controller.signal,

// 当连接打开时
onopen(response) {
if (response.ok) {
console.log('SSE 连接已建立');
return;
}

// 处理错误状态码
if (response.status === 401 || response.status === 403) {
throw new Error('认证失败');
}

throw new Error('连接失败: ' + response.status);
},

// 当收到消息时
onmessage(event) {
if (onChunkCallback && event.data) {
// 将字符串转换为 ArrayBuffer,模拟 uni.request 的返回格式
const encoder = new TextEncoder();
const arrayBuffer = encoder.encode(event.data).buffer;

onChunkCallback({
data: arrayBuffer,
});
}
},

// 当发生错误时
onerror(err) {
console.error('SSE 错误:', err);

// 检查是否是认证错误
if (err.message && err.message.includes('认证失败')) {
useDialog('登录过期,请前往登录', { title: '提示', showCancelButton: true })
.then(() => {
uni.navigateTo({
url: '/pages/login/phone/index',
});
})
.catch(err => {});
}

if (onErrorCallback) {
onErrorCallback(err);
}

// 抛出错误以停止重连
throw err;
},

// 当连接关闭时
onclose() {
console.log('SSE 连接已关闭');
if (onCompleteCallback) {
onCompleteCallback();
}
},

// 不自动重连
openWhenHidden: false,
});
}, 0);

return streamTask;
},

ECharts 的写法差异

Android 端使用 ECharts 时,不需要像小程序一样额外注册 canvas

我的做法是直接全量导入 echarts,然后按正常方式初始化和注册图表即可。

本地安装原生插件

这里以阿里云一键登录插件为例。

1. 放置插件文件

先到 阿里云号码认证插件 下载插件。

打开 Android 本地框架后,将插件的 aar 文件放到项目的 libs 目录下。

然后在 assets 目录中创建 dcloud_uniplugins.json,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"nativePlugins": [
{
"plugins": [
{
"type": "module",
"name": "AliCloud-NirvanaPns",
"class": "com.nirvana.prd.auth.ALiLoginManager"
}
]
}
]
}

dcloud_uniplugins.json 中的这些信息,可以在离线安装包内的 package.json 中找到。

2. 增加依赖配置

build.gradle 中还需要加入插件依赖,具体可参考上面的「配置 Gradle 依赖」章节。

3. 注意回调函数写法

阿里云一键登录的回调函数不要使用 async

如果回调函数使用了 async,授权页可能会被阻塞,表现为没有报错,但授权页也不会弹出。

接入微信登录

微信登录可以直接使用 uni-app 的 uni.login API,并将登录方式设置为微信登录。

需要注意的是,微信登录不能只依赖自定义基座调试验证,最好打正式包后再测试。

另外,打包时使用的证书和签名必须与微信开放平台配置保持一致,否则微信登录无法正常拉起。