自从用了 uniapp 之后,腿不酸了,腰不疼了,一口气爬五楼不费劲了。但是,uniapp 虽好,也有解决不了的问题。用了好多好多天的功夫终于调通了 unipush,能够将消息推送到华为手机上了。
然鹅,消息推送下来之后,系统会自动增加桌面图标的角标数字。然后每收到一条消息这个就会+1,再之后,就看到这个数字不断的增加。uni 官方提供了设置角标的api:
plus.runtime.setBadgeNumber(3);
看起来简单粗暴,人畜无害。实际效果呢,就是 iOS 系统上成功了,最起码这个是有效果的,然而再安卓系统上就芭比了。啥用都没有,不过这个倒是也不算出乎意料。
安卓系统的角标设置,简单来说分为两类,一种是标准的,所谓标准就是 google 的原生框架,可以通过原生接口实现,例如 Android Oreo 及以上版本的 Notification Badges。
另外一种就是国情货了,例如各种国产品牌,这些厂商的角标设置本质上是对各个厂商的 launcher 的适配,每个厂商的启动器都不一样。所以就只能通过原生的办法来解决,至于如何解决这个问题,会在后面的文章中写。
上述的这些功能的实现 uni 是解决不了的,就只能通过原生来实现了。此时就需要开发 uni 的原生语言插件,这个插件的本质就是安卓的 module 组件。
创建插件项目最简单的方法就是直接下载官方的 sdk,然后导入示例项目,直接在示例项目上修改或者新建。uni 的官方 sdk 可以通过下面的链接下载:
https://nativesupport.dcloud.net.cn/AppDocs/download/android.html
直接导入 as 工程即可浏览相关的代码和组件。
当然,也可以直接创建一个新的项目,可以参考下面的步骤(前提是已经安装好 android studio,如果还没安装,先去找别的文章看看怎么安装哈):
1.新建安卓项目这个随便选择即可,主要是用于测试一些功能代码:
2.新建 module
3.修改 module 的build.gradle 添加必要的依赖
主要是下面几行:
compileOnly 'com.alibaba:fastjson:1.2.83'
compileOnly fileTree(dir: 'libs', include: ['*.aar', '*.jar'], exclude: [])
api project(':launcherBadge') # 其他项目依赖,如果没有可以不添加
4.复制 sdk 下的uniapp-v8-release.aar 到 module 的 libs 目录下:
5.编写 module 代码,测试代码如下:
package cn.org.obaby.badge;
import android.content.Context;
import android.os.Handler;
import android.util.Log;
import com.alibaba.fastjson.JSONObject;
import io.dcloud.feature.uniapp.annotation.UniJSMethod;
import io.dcloud.feature.uniapp.bridge.UniJSCallback;
import io.dcloud.feature.uniapp.common.UniModule;
public class babyBadgeModule extends UniModule{
private static final String TAG = "badgeModule";
public static final String Name = "hello world";
public void testLoad(){
Log.i("test", "hello");
}
//run ui thread
@UniJSMethod(uiThread = true)
public void testAsyncFunc(JSONObject options, UniJSCallback callback) {
// Log.e(TAG, "testAsyncFunc--"+options);
if(callback != null) {
JSONObject data = new JSONObject();
data.put("code", "success");
callback.invoke(data);
}
}
//run JS thread
@UniJSMethod (uiThread = false)
public JSONObject testSyncFunc(){
JSONObject data = new JSONObject();
data.put("code", "success");
return data;
}
}
注意要 import uni相关的类:
import com.alibaba.fastjson.JSONObject; import io.dcloud.feature.uniapp.annotation.UniJSMethod; import io.dcloud.feature.uniapp.bridge.UniJSCallback; import io.dcloud.feature.uniapp.common.UniModule;
另外 module 要继承自UniModule,如果是Component 扩展类必须继承 UniComponent, 父容器Component(例如ViewGroup组件)则需要继承UniVContainer,具体参考官方教程:https://nativesupport.dcloud.net.cn/NativePlugin/course/android.html
修改module 的consumer-rules.pro,添加规则:
-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontpreverify
-verbose
#-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
-dontoptimize
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class * extends io.dcloud.common.DHInterface.IPlugin
-keep public class * extends io.dcloud.common.DHInterface.IFeature
-keep public class * extends io.dcloud.common.DHInterface.IBoot
-keep public class * extends io.dcloud.common.DHInterface.IReflectAble
-keep class io.dcloud.feature.speech.** {*;}
-keep class io.dcloud.net.** {*;}
-keep class io.dcloud.common.constant.** {*;}
-keep class io.dcloud.common.sonic.** {*;}
-keep class io.dcloud.common.DHInterface.** {*;}
-keep class io.dcloud.common.util.** {*;}
-keep class io.dcloud.common.adapter.** {*;}
-keep class io.dcloud.feature.internal.reflect.** {*;}
-keep class io.dcloud.feature.internal.sdk.** {*;}
-keep class io.dcloud.feature.payment.** {*;}
-keep class io.dcloud.sdk.** {*;}
-keep class com.** {*;}
-keep class io.dcloud.nineoldandroids.** {*;}
-keep class vi.com.gdi.** {*;}
-keep class androidx.** {*;}
-dontwarn pl.droidsonroids.gif.**
-keepclasseswithmembers class * extends io.dcloud.js.geolocation.GeoManagerBase {
<methods>;
}
-keep class io.dcloud.share.AbsWebviewClient
-keepclasseswithmembers class io.dcloud.share.AbsWebviewClient {
<methods>;
}
-keep class io.dcloud.share.ShareAuthorizeView
-keepclasseswithmembers class io.dcloud.share.ShareAuthorizeView {
<methods>;
}
-keep class io.dcloud.share.IFShareApi
-keep public class * extends io.dcloud.share.IFShareApi
-keepclasseswithmembers class io.dcloud.share.IFShareApi {
<methods>;
}
-keepattributes Exceptions,InnerClasses,Signature,Deprecated, SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
-keep class io.dcloud.appstream.StreamAppManager
-keepclasseswithmembers class io.dcloud.appstream.StreamAppManager {
public protected <methods>;
}
-keep class io.dcloud.common.DHInterface.IReflectAble
-keep public class * extends io.dcloud.common.DHInterface.IReflectAble{
public protected <methods>;
public protected *;
}
-keep class **.R
-keep class **.R$* {
public static <fields>;
}
-keep public class * extends io.dcloud.common.DHInterface.IJsInterface{
public protected <methods>;
public protected *;
}
-keepclasseswithmembers class io.dcloud.EntryProxy {
<methods>;
}
-keep class * implements android.os.IInterface {
<methods>;
}
-keepclasseswithmembers class *{
public static java.lang.String getJsContent();
}
-keepclasseswithmembers class io.dcloud.appstream.StreamAppScriptEntry {
<methods>;
}
-keepclasseswithmembers class *{
public static void onReceiver1(android.content.Intent, android.content.Context);
}
-keepclasseswithmembers class *{
public static io.dcloud.share.AbsWebviewClient getWebviewClient(io.dcloud.share.ShareAuthorizeView);
}
-keepclasseswithmembers class *{
public java.lang.String exec(java.lang.String,java.lang.String,java.lang.String[]);
}
-keepattributes Exceptions,InnerClasses,Signature,Deprecated, SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keep public class * extends android.app.Application{
public static <methods>;
public *;
}
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
public static <methods>;
}
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
-keepattributes Signature
-keep class io.dcloud.encryption.K {*;}
-dontwarn com.igexin.**
-keep class org.json.** { *; }
-dontwarn com.amap.**
-dontwarn org.apache.commons.**
-dontwarn com.sina.weibo.sdk.**
-keep class uni.** {*;}
-keep class pl.** {*;}
-keep class io.** {*;}
-keep class org.mozilla.**{*;}
-keep class androidtranscoder.**{*;}
-keep class XI.**{*;}
-keep public class * extends io.dcloud.feature.uniapp.ui.component.UniComponent{*;}
-keep public class * extends io.dcloud.weex.AppHookProxy{*;}
-keep public class * extends io.dcloud.feature.uniapp.UniAppHookProxy{*;}
-keep public class * extends io.dcloud.feature.uniapp.common.UniModule{*;}
6.编译组件:
菜单 build->build module
此时就会在 build 目录下生成对应的 aar 文件了。
7.在 uniapp 目录nativeplugins下新建插件目录babyBadgeModule(如果没有nativeplugins目录,创建即可):
在插件根目录babyBadgeModule 下新建文件夹 android,以及文件 package.json(注意文件名,这个文件名不知道是复制错了,还是写错了,导致 uni 一直识别不到插件,崩溃。)
将生成的 aar 文件放入 android 目录下。
package.json 文件内容:
{
"name": "babyBadgeModule",
"id": "babyBadgeModule",
"version": "1.0.1",
"description": "Android 角标插件",
"_dp_type":"nativeplugin",
"_dp_nativeplugin":{
"android": {
"plugins": [
{
"type": "module",
"name": "babyBadgeModule",
"class": "cn.org.obaby.badge.babyBadgeModule"
}
],
"integrateType": "aar",
"minSdkVersion" : 21
}
}
}
8.选择本地插件
9.uni 代码实现,调用原生插件(下面的代码,调用了两个原生组件):
const babyBadgeModule = uni.requireNativePlugin('babyBadgeModule');
console.log(babyBadgeModule)
babyBadgeModule.setLauncherBadgeCount({COUNT:3},result => {this.showToast('test')});
// require插件名称
const dcRichAlert = uni.requireNativePlugin('DCloud-RichAlert');
// 使用插件
dcRichAlert.show({
position: 'bottom',
title: "提示信息",
titleColor: '#FF0000',
content: "<a href='https://uniapp.dcloud.io/' value='Hello uni-app'>uni-app</a> 是一个使用 Vue.js 开发跨平台应用的前端框架!\n免费的\n免费的\n免费的\n重要的事情说三遍",
contentAlign: 'left',
checkBox: {
title: '不再提示',
isSelected: true
},
buttons: [{
title: '取消'
},
{
title: '否'
},
{
title: '确认',
titleColor: '#3F51B5'
}
]
}, result => {
switch (result.type) {
case 'button':
console.log("callback---button--" + result.index);
break;
case 'checkBox':
console.log("callback---checkBox--" + result.isSelected);
break;
case 'a':
console.log("callback---a--" + JSON.stringify(result));
break;
case 'backCancel':
console.log("callback---backCancel--");
break;
}
});
10.打自定义基座包:
11.打包完成之后就可以真机调试了,不过如果遇到下面的错误可以尝试将插件名称、目录、id 之类的全部同意再次尝试:
应用【闺蜜圈】已启动 09:00:26.335 [JS Framework] 当前运行的基座不包含原生插件[Baby-Badge],请在manifest中配置该插件,重新制作包括该原生插件的自定义运行基座 09:00:26.364 undefined at pages/index.vue:583 09:00:26.365 [Vue warn]: Error in onLoad hook: "TypeError: Cannot read property 'setLauncherBadgeCount' of undefined" (found at pages/index.vue:1) 09:00:26.365 TypeError: Cannot read property 'setLauncherBadgeCount' of undefined
本质上还是插件目录以及文件内容导致的错误,多检查一致性吧,这个确实没什么好办法。
至于调试,再设备上不好调试,可以再原生组件添加 log,通过 logcat 查看相关的日志。至此插件就基本可以用了:














33 comments
灵妹妹发这些看不懂的干嘛
多发点丝不好看
今天的灵妹妹很仙
这不是该干的活还是要干的嘛
看样子挺不错的样子😂
居然出太阳了
风和日丽
android studio 最经还在搞大作业,头都大了……
啥作业啊,还得用 android studio?
移动应用终端开发,结课作业是写一个带数据库的二手交易软件,都快吐了
厉害了,一看角标数字就是很难调通的,看了你的过程还这么复杂
这才是上半段,还有下半段。哈哈哈
安卓的推送真是
哎,一言难尽
手机这么多红点提醒,我想帮你点干净

这,强迫症犯了吗?嘻嘻
你的网站没开评论吗?
太烧脑了,不是一个级别的东西。差距瞬间拉开了一大截
这也是迫于生计啊~~
没开,会经常来访问你的,学习学习一下知识
看不懂 给你点个赞
不至于
看到红色的99+,心里就抓狂,手就不受控制了。
哈哈哈 来让你点
打通五花八门的国内安卓生态,还得女王的实力。
哎,这还没打通呢~~
还差的多呢
虽然看不懂,但还是要给你点赞。
确实有点后悔当年没学编程,挺喜欢的,现在脑子不够用了,学不进去
其实也没什么高级的东西,新时代的农民工
呵呵,码农也是农民工
https://baijiahao.baidu.com/s?id=1708468550895221927&wfr=spider&for=pc

还有这种事?排在我们前面…
哈哈哈,就是这样的
看不懂,但是点赞是必须的。
不用懂,不用懂