目录
一、首次接触快应用
由于项目需要,第一次接触快应用的开发。但是在项目开发完成,进入到测试阶段才发现。华为厂商自己创造了一套文档,有些接口在其他机型(如:vivo、小米)上直接报错,不兼容,咨询了华为快应用技术支持,回复的答案是:建议分开两套代码维护,即:华为机型一套代码,其他的,另一套代码,这是多么悲伤的故事。所以在项目中,遇到兼容性的问题,总结了下来,方便后续查找。
二、开发工具
快应用联盟,使用 IDE 工具进行编码、调试、测试、上传等功能。
华为,使用 华为快应用IDE 工具进行编码、调试、测试、上传等功能。
三、技术难点
1、文件(语音、图片等)转base64
文档上没有接口,能直接把文件转为base64编码,这里提供一个可行的解决方案。base64-arraybuffer结合 '@system.file'。
a、 在需要把文件转bse64的页面中,引入这两个文件,其中,base64-arraybuffer,为 npm i
base64-arraybuffer导入依赖。
import file from '@system.file'
const base64Arraybuffer = require('base64-arraybuffer')
b、获取文件路径后,执行如下代码:
getBase64(type) {
file.readArrayBuffer({
uri: this.currUri, // this.currUri 是文件路径
success: (data) => {
console.log('buffer.length: ' + data.buffer.length)
const base64 = base64Arraybuffer.encode(data.buffer)
console.log(base64)
},
fail: function (data, code) {
console.log(`handling fail, code = ${code}`)
}
})
},
2、 base64转音频文件
语音播放功能,接口返回base64编码,需要在快应用播放。使用`@system.audio`。audio接收一个播放的音频媒体 uri,因此要把base64转换成uri。有效方案如下:
a、导入依赖文件
import audio from '@system.audio'
import file from '@system.file'
import base642uint8 from '../../../helper/base642uint8'
base642uint8.js文件,是我本地得,通过base64转为uintArray。在此附上代码:
const barf = (s, i) => {
throw new Error(`Not a valid base64 string. Found "${s[i]}" at index ${i}.`);
}
/**
* base64 转为 decode
* @param {*} base64
*/
const decode = (base64) => {
const charCode2Index = [
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1,
62, // +
-1, -1, -1,
63, // /
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // 0-9
-1, -1, -1,
0, // =
-1, -1, -1,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, // A-J
10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // K-T
20, 21, 22, 23, 24, 25, // U-Z
-1, -1, -1, -1, -1, -1,
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, // a-j
36, 37, 38, 39, 40, 41, 42, 43, 44, 45, // k-t
46, 47, 48, 49, 50, 51 // u-z
]
const base64_length = base64.length;
const output_length = base64_length / 4 * 3;
if (output_length !== (output_length | 0)) {
throw new Error("base64 is not a valid base64 encoding.");
}
const output = new Uint8Array(output_length);
let c1, c2, c3, c4,
i = 3, j = 0;
for (; i < base64.length; i += 4) {
c1 = charCode2Index[base64.charCodeAt(i - 3)]; // 00010011 = 19 = T
c2 = charCode2Index[base64.charCodeAt(i - 2)]; // 00010110 = 22 = W
c3 = charCode2Index[base64.charCodeAt(i - 1)]; // 00000101 = 5 = F
c4 = charCode2Index[base64.charCodeAt(i)]; // 00101110 = 46 = u
if (c1 === -1 || c1 === undefined) {
barf(base64, i - 3)
}
if (c2 === -1 || c2 === undefined) {
barf(base64, i - 2)
}
if (c3 === -1 || c3 === undefined) {
barf(base64, i - 1)
}
if (c4 === -1 || c4 === undefined) {
barf(base64, i)
}
output[j++] = c1 << 2 | c2 >> 4; // 010011xx | xxxx0001 = 01001101 = 77 = M
output[j++] = c2 << 4 | c3 >> 2; // 0110xxxx | xx000001 = 01100001 = 97 = a
output[j++] = c3 << 6 | c4; // 01xxxxxx | 00101110 = 01101110 = 110 = n
}
if (base64[base64_length - 1] === "=") {
const padding = base64[base64_length - 2] === "=" ? 2 : 1;
return output.subarray(0, output_length - padding);
}
return output;
}
export default {
decode,
}
b、封装方法
// base64 => 音频文件
getAudioFile(base64) {
const arrayBuffer = base642uint8.decode(base64)
const randomNumber = new Date().getTime()
file.writeArrayBuffer({
uri: `${this.voiceUriPrefix}${randomNumber}.m4a`,
buffer: arrayBuffer,
success: () => {
this.voiceUriArr.push(`${this.voiceUriPrefix}${randomNumber}.m4a`)
if (this.voiceIndex + 1 === this.voiceArr.length) {
this.loading = false
}
},
fail: (data, code) => {
console.log(`handling fail, code = ${code}`)
}
})
},
四、兼容性问题集合
1、返回首页
非华为机型:
router.back({
path:'/' , // '/',默认为 `manifest.json`文件配置的文件入口,`entry`的值
})
华为机型:
router.back({
path: '/pages/Home' // entry的值
})
2、语音播放接口
非华为机型:
引入`import texttoaudio from '@service.texttoaudio'`,具体使用方法,点击跳转
华为机型:
引入`import mltts from '@service.ml.tts'`,具体使用方法,点击跳转
3、获取事件对象,比如阻止事件冒泡,如何获取当前的事件对象
非华为机型:
<text class="iconfont icon-del"
@click="clickDeleteImage(evt, value2, index2, $idx)"> 删除
<text>
// 点击删除图片
clickDeleteImage(evt, delItem, delInd, itemIndex) {
// 需要阻止冒泡
evt.stopPropagation()
},
华为机型:
传入的时候不用加event对象,接受的时候要加evt,传入的时候默认会在最后补上一个
<text class="iconfont icon-del"
@click="clickDeleteImage(value2, index2, $idx)"> 删除
<text>
// 点击删除图片
clickDeleteImage(delItem, delInd, itemIndex, evt) {
// 需要阻止冒泡
evt.stopPropagation()
},
4、自定义文件引入
比如,iconfont.css 需要引入 iconfont.ttf 文件,两个文件所在目录如下图:
如何引入?
非华为机型:
@font-face {
font-family: iconfont; /* project id 1009460 */
src: url('/assets/styles/iconfont.ttf'); // 可以
// src: url('../../../assets/styles/iconfont.ttf'); // 也可以,但是在华为机上不行
}
华为机型:
@font-face {
font-family: iconfont; /* project id 1009460 */
src: url('/assets/styles/iconfont.ttf'); // 在非华为机上也可以引入成功
}