更新记录
1.1.2(2022-07-11)
补全JobHandlerService中非空判断中空值处理,尝试解决部分机型崩溃问题(无问题机型,未测效果)
1.1.1(2022-04-12)
优化:fun_notif_title,fun_notif_text不设置分别默认为app名称、"正在后台运行..."。
新增:跳转系统设置。
1.1.0(2022-03-21)
修复:startForegroundService后因非空判断造成的startForeground未调用错误。
变更:startKeep接口通知标题内容设置改为meta-data中配置fun_notif_title,fun_notif_text;建议使用配置值方式,接口中不再配置,后台服务保活中读数据为空时,会去读取meta-data的值。
查看更多平台兼容性
Android | Android CPU类型 | iOS |
---|---|---|
适用版本区间:4.4 - 11.0 | armeabi-v7a:未测试,arm64-v8a:未测试,x86:未测试 | × |
原生插件通用使用流程:
- 购买插件,选择该插件绑定的项目。
- 在HBuilderX里找到项目,在manifest的app原生插件配置中勾选模块,如需要填写参数则参考插件作者的文档添加。
- 根据插件作者的提供的文档开发代码,在代码中引用插件,调用插件功能。
- 打包自定义基座,选择插件,得到自定义基座,然后运行时选择自定义基座,进行log输出测试。
- 开发完毕后正式云打包
付费原生插件目前不支持离线打包。
Android 离线打包原生插件另见文档 https://nativesupport.dcloud.net.cn/NativePlugin/offline_package/android
iOS 离线打包原生插件另见文档 https://nativesupport.dcloud.net.cn/NativePlugin/offline_package/ios
注意事项:使用HBuilderX2.7.14以下版本,如果同一插件且同一appid下购买并绑定了多个包名,提交云打包界面提示包名绑定不一致时,需要在HBuilderX项目中manifest.json->“App原生插件配置”->”云端插件“列表中删除该插件重新选择
2022-03-18发布的1.1.0版本使用必读
重大变更:startKeep接口通知标题内容设置改为meta-data中配置fun_notif_title,fun_notif_text。建议使用配置值方式,接口中不再配置,后台服务保活中读数据为空时,会去读取meta-data的值。
即manifest.json->app原生插件配置->fun-keeplive下2个配置项(如果不存在,删除重新添加一下)
前言
因项目特殊需要制作此插件,因个人时间有限,当前插件未做全机型测试,需要的请自行测试。
<!--测试时使用样例代码见最后面-->
目前在小米6(安卓9,夜间12:10开启早上7:31手动杀死,中间每隔一分钟上传数据到服务器,早上查看服务器共收440条数据记录在redis中)都能有很好的保活效果(只要你不主动杀死程序)。小米11Ultra[安卓11]和华为[鸿蒙]测试40分钟页无问题。
【测试过程:手机开启保活(+屏幕唤醒(10分钟),使用setInterval每分钟项服务器上传数据,数据内容为手机时间,服务端记录到redis集合中。)】
如果你的应用希望能够一直在后台运行(比如推送服务)而不被系统自动杀死的话,可以尝试一下。需要注意的是,程序保活并不代表能做到程序杀不死,除非你把你的应用做成系统应用或者加入到系统的白名单内,否则也只是提高了程序的优先级权重,减少程序被系统回收杀死的概率而已。
插件扩展
1.定位扩展插件【需使用本插件前提下,再加入扩展插件使用】https://ext.dcloud.net.cn/plugin?id=7013
已知问题
1.申请忽略电池优化:在小米新机子上打开的不是优化申请窗或优化页,而是【应用智能省电】(点击是省电策略),旧机子(如MI6)没问题。但是去【设置】搜索【电池优化】当前前应用已加入。
解决方式:可以先调用省电策略,在判断是否忽略电池优化,没忽略再申请。(实际小米高版本【智能省电无限制】,好像就默认忽略电池优化了)
更新内容
1.1.1
优化:fun_notif_title,fun_notif_text不设置分别默认为app名称、"正在后台运行..."。
新增:跳转系统设置。
1.1.0
修复:startForegroundService后因非空判断造成的startForeground未调用错误。
变更:startKeep接口通知标题内容设置改为meta-data中配置fun_notif_title,fun_notif_text;建议使用配置值方式,接口中不再配置,后台服务保活中读数据为空时,会去读取meta-data的值。
1.0.7
修复:个别机型保活过程中常驻通知发出声音问题。
1.0.6
更新:将淘宝weex类全部更为Uniapp类。
1.0.5
修复app关闭[杀死]后点击通知栏无效(即不打开app)
1.0.4
1.重构,精简功能
1.0.3
1.新增通知类接口
2.新增悬浮窗权限检测和授权申请
3.新增获取自启动开启状态(当前仅小米可用)
4.新增打开应用设置接口
5.新增打开电源管理页接口(在不同手机展示页面不同)
使用说明
需要先添加原生插件到uniapp项目,并配置manifest.json,再代码引用。使用教程见https://ask.dcloud.net.cn/article/35412
简易使用流程:先初始化→开启保活(+屏幕唤醒)→配置白名单类设置→不需要保活时关闭保活。
所有方法result返回值带code的最好判断为0再取值
初始化
const keepLive = uni.requireNativePlugin('fun-keeplive');
1.开启保活
//v1.1.0开始不建议在此处配置title&text,建议在原生插件处配置fun_notif_title,fun_notif_text,以规避异常风险。
let cof = {
mode: 1, //0省电模式 1流氓模式
//title: "推送服务", //通知栏标题 //v1.1.0开始不建议在此处配置
//text: "正在运行,请勿关闭",//通知栏内容//v1.1.0开始不建议在此处配置
//isShow: true //通知栏显示 true显示,false隐藏(建议true)现在绝大部分手机无效,可忽略
// jobTime: 60.默认值,测试时使用的默认值
}
keepLive.startKeep(cof, function(result) {
console.log(result)
});
//定时回调,时间为上面jobTime,需要明确的是,实际效果这个时间间隔不是固定的,
const globalEvent = uni.requireNativePlugin('globalEvent');//官方的,不需额外配置,引用即可
globalEvent.addEventListener('doKeepJobEvent', function() {
console.log("----doKeepJobEvent-----");//http上传
});
2.停止保活机制
keepLive.stopKeep(function(result) {
console.log(result)
});
3.打开屏幕唤醒【开启保活后,开启唤醒增加保活效果】
//time:间隔时间,单位:分,建议5-10。
//1台华为[鸿蒙],2台小米(米6[安卓9]、米11Ultra[安卓11])使用的10分测试的。
keepLive.openWakeLock({time:5},function(result) {
console.log(result)
});
4.关闭屏幕唤醒
keepLive.colseWakeLock(function(result) {
console.log(result)
});
5.打开白名单配置页(电池优化、省电策略、自启动)
keepLive.gotoWhiteList(function(result) {
console.log(result)
});
6.检测是否忽略电池优化(简单解释:开启电池优化,会干掉应用)
keepLive.isIgnoringBatteryOptimizations(function(result) {//SDK>=23,否则返回不支持
console.log(result);
})
//以下为结果示例(期望结果是忽略优化即"flag": true)
{
"sdk_int": 28,
"flag": true,//电池是否忽略优化,true:忽略;false:优化
"code": 0,
"msg": "ok"
}
7. 申请忽略电池优化(如果已加入电池优化的白名单 则进入系统电池优化页面,若未加入则弹窗申请。先部分手机省电策略为无限制则自动忽略电池优化)
//如果已加入电池优化的白名单 则进入系统电池优化页面,
//若未加入则弹窗申请,
//可使用isIgnoringBatteryOptimizations检测
keepLive.requestIgnoreBatteryOptimizations(function(result) {
console.log(result)
});
//以下为结果示例(调用那一刻的情况,比如初次默认是优化,那返回结果就是"flag": false)
{
"sdk_int": 28,
"flag": true,//电池是否忽略优化,true:忽略;false:优化
"code": 0,
"msg": "ok"
}
8. 设置省电策略
keepLive.setPowerKeeper(function(result) {
console.log(result)
});
9.设置自启动
keepLive.startToAutoStartSetting(function(result) {
console.log(result)
});
10.是否允许自启动[1.0.3新增](检测第9个接口。当前仅支持小米)
keepLive.isAutoStart(function(result) {
});
11.打开应用设置页[1.0.3新增]
keepLive.openSetting(function(result) {
});
12.打开耗电统计[1.0.3新增]
keepLive.openPowerUsageSummary(function(result) {
});
13.获取Android版本、应用版本号等
keepLive.getVersion(function(result) {
console.log(result)
});
14.是否有悬浮窗权限[1.0.3新增]
keepLive.isAlertWindow(function(result) {
});
15.申请悬浮窗[1.0.3新增]
keepLive.requestAlertWindow(function(result) {
});
16.是否开启通知[1.0.3新增]
keepLive.isNotificationEnabled(function(result) {
});
17.进入应用通知设置页[1.0.3新增]
keepLive.openNotifySetting(function(result) {
});
18.检查是否授权读取通知[1.0.3新增]
keepLive.enabledNotificationListeners(function(result) {
});
19.申请授权读取通知[1.0.3新增]
keepLive.openNotifyListenerSetting(function(result) {
});
20.开启通知监听[1.0.3新增]
// packageNameList: 监听的包名,数组形式。空则全部监听
keepLive.startNotifyListener({
// packageNameList: ["com.tencent.mobileqq"]
}, function(result) {
});
// onFunNotification 通知回调Event
const globalEvent = uni.requireNativePlugin('globalEvent');//内置原生插件,引用即可
globalEvent.addEventListener('onFunNotification', function() {
console.log("----onFunNotification-----");//
//type : onNotificationRemoved 移除消息 onNotificationPosted 发布消息
});
21.关闭通知监听[1.0.3新增]
keepLive.stopNotifyListener(function(result) {
});
22.清空通知信息[1.0.3新增]
keepLive.clearNotify(function(result) {
});
23.打开系统设置页[1.1.1新增]
keepLive.openSystemSetting(function(result) {
});
内置原生插件简要说明(示例中用到的)
内置原生插件,代码引用即可,不需要额外配置;
globalEvent
用于监听持久性事件,例如定位信息,陀螺仪等的变化。全局事件是需要额外 APIs 处理的次要 API。
modal
模块提供了以下展示消息框的 API。
toast(options)
- @options
- message, string, 展示的内容.
- duration, number, 持续时间(以秒为单位)
例如:使用原生toast方便调试(在后台时也能弹,不用都接电脑方便调试)
const modal = uni.requireNativePlugin('modal');
modal.toast({
message: "====:进主页",
duration: 2
});
简单调用示例
页面按钮只是展示可以不点击,onLoad()方法已调用,打开页面,按要求授权即可测试。
如果app有推送或者手机上后台运行着厂商白名单app(如微信/QQ/邮箱等)保活效果最好。(时间长了有时假死,屏幕量一下,立马工作)
本人测试时手机除了当前插件app均无自启动权限,并且清理了内存,只剩插件app,因时间有限,测试了一小时多没问题,截止1.0.2提交时没在测试,后台看到没上传数据,只要你点亮一下屏幕(无需解锁),会继续上传。【测试手机:小米11Ultra/安卓11】
<template>
<view class="content">
<view style="margin-bottom: 100rpx;"></view>
<button @tap="startKeep ">启动服务</button>
<button @tap="gotoWhiteList">配置后台运行,白名单,自启动</button>
<button @tap="isIgnoringBattery">判断应用是否添加在白名单之中</button>
<button @tap="requestIgnoreBattery">申请电池优化</button>
<button @tap="setPowerKeeper">设置省电策略</button>
<button @tap="startToAutoStartSetting">设置自启动</button>
<button @tap="stopKeep">关闭服务</button>
<!-- <button @tap="hasLocationPermissions">获取是否授权定位</button>
<button @tap="requestLocationPermissions">申请定位</button> -->
<!-- <button @tap="topost">保活传数据</button> -->
<u-button class="m-button" type="primary" shape="circle" text="保活传数据" @click="topost"></u-button>
<view style="margin-bottom: 30rpx;"></view>
<view class="msg">{{ msg2 }}</view>
<view class="msg">
<text>{{ msg }}</text>
</view>
<!-- <view class="msg" v-html="msg"> </view> -->
</view>
</template>
<script>
// const fUN_IM = uni.requireNativePlugin('FUN-IM');
const keepLive = uni.requireNativePlugin('fun-keeplive');
const globalEvent = uni.requireNativePlugin('globalEvent');
// const fUN_AmapLocation = uni.requireNativePlugin('FUN-AmapLocation');
var _this;
export default {
data() {
return {
title: '',
msg: '',
msg2: '',
intTime: 0,
maxTime: 0,
minTime: 99999999999,
totalTime: 0,
initTime: new Date().getTime(),
lastTime: 0,
nowtime: 0,
locTime: 0,
reTime: 0,
locationType: null,
time_start: 0,
time_end: 0
};
},
onLoad() {
console.log('====:进主页');
_this = this;
const deviceId = '';
try {
deviceId = uni.getStorageSync('storage_deviceId');
if (deviceId) {
console.log(deviceId);
} else {
try {
const res = uni.getSystemInfoSync();
_this.msg2 = res.deviceId;
} catch (e) {
_this.msg2 = _this.timeFormat(null, 'mm_dd_hh_MM_ss_SSS');
}
try {
uni.setStorageSync('storage_deviceId', _this.msg2);
} catch (e) {
}
}
} catch (e) {
// error
}
if (!deviceId) {
_this.msg2 = _this.timeFormat(null, 'mm_dd_hh_MM_ss_SSS');
}
////////////////////////////////////////////直接调用这一段可以测试
_this.startKeep();
globalEvent.addEventListener('doKeepJobEvent', function() {
console.log("----doKeepJobEvent-----");
uni.request({
url: 'https://XX.XXX.com/api/open/localtion',
data: {
id: 'moyu-' + _this.msg2 + '-KJ',
loc: _this.timeFormat()
},
method: 'POST',
success: (res) => {
console.log(res)
}
})
});
_this.topost();
//////////////////////////////////////////////直接调用这一段可以测试
},
methods: {
start: function() {},
stop: function() {},
once: function() {},
timeFormat(dateTime = null, fmt = 'hh:MM:ss:SSS') {
if (!dateTime) dateTime = Number(new Date());
if (dateTime.toString().length == 10) dateTime *= 1000;
let date = new Date(Number(dateTime));
let ret;
let opt = {
'y+': date.getFullYear().toString(), // 年
'm+': (date.getMonth() + 1).toString(), // 月
'd+': date.getDate().toString(), // 日
'h+': date.getHours().toString(), // 时
'M+': date.getMinutes().toString(), // 分
's+': date.getSeconds().toString(), // 秒
'S+': date.getMilliseconds().toString() // 毫秒
};
for (let k in opt) {
ret = new RegExp('(' + k + ')').exec(fmt);
if (ret) {
fmt = fmt.replace(ret[1], ret[1].length == 1 ? opt[k] : opt[k].padStart(ret[1].length, '0'));
}
}
return fmt;
},
topost() {
uni.request({
url: 'https://XX.XXX.com/api/open/localtion',
data: {
id: 'moyu-' + _this.msg2 + 'I',
loc: _this.timeFormat()
},
method: 'POST',
success: (res) => {
console.log(res)
}
});
let dst = setInterval(function() {
uni.request({
url: 'https://XX.XXX.com/api/open/localtion',
data: {
id: 'moyu-' + _this.msg2 + 'I',
loc: _this.timeFormat()
},
method: 'POST',
success: (res) => {
console.log(res)
}
});
}, 1000 * 60)
},
startKeep() {
let cof = {
mode: 1, //0省电模式 1流氓模式
title: "推送服务", //通知栏标题
text: "正在运行,请勿关闭", //通知栏内容
isShow: true //通知栏显示 true显示,false隐藏(建议true)
}
keepLive.startKeep(cof, function(result) {
console.log(result)
});
keepLive.isIgnoringBatteryOptimizations(function(res) {
console.log(res);
let flag = res.flag;
if (flag == false) {
keepLive.gotoWhiteList()
}
})
keepLive.openWakeLock({
time: 1
}, function(result) {
console.log(result)
});
},
//判断应用是否添加在白名单之中
isIgnoringBattery() {
keepLive.isIgnoringBatteryOptimizations(function(result) {
console.log(result)
})
},
//申请加入白名单
requestIgnoreBattery() {
keepLive.requestIgnoreBatteryOptimizations(function(result) {
console.log(result)
});
},
setPowerKeeper() {
keepLive.setPowerKeeper(function(result) {
console.log(result)
});
},
//设置app自启动
startToAutoStartSetting() {
keepLive.startToAutoStartSetting();
},
stopKeep() {
keepLive.stopKeep(function(result) {
console.log(result)
});
},
hasLocationPermissions() {
keepLive.hasLocationPermissions(function(result) {
console.log(result)
});
},
requestLocationPermissions() {
keepLive.requestLocationPermissions();
},
gotoWhiteList() {
keepLive.gotoWhiteList()
},
}
};
</script>
<style>
.content {
margin: 15rpx;
}
.m-button {
margin: 15rpx 0;
}
.msg {
width: 750rpx;
}
</style>