自从用了 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
还有这种事?排在我们前面…
哈哈哈,就是这样的
看不懂,但是点赞是必须的。
不用懂,不用懂