Uniapp 原生插件开发

自从用了 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 查看相关的日志。至此插件就基本可以用了:

☆版权☆

* 网站名称:obaby@mars
* 网址:https://oba.by/
* 个性:https://oba.by/
* 本文标题: 《Uniapp 原生插件开发》
* 本文链接:https://lang.bi/2024/05/17206
* 短链接:https://oba.by/?p=17206
* 转载文章请标明文章来源,原文标题以及原文链接。请遵从 《署名-非商业性使用-相同方式共享 2.5 中国大陆 (CC BY-NC-SA 2.5 CN) 》许可协议。


You may also like

33 comments

  1. Level 5
    Microsoft Edge 125 Microsoft Edge 125 Windows 10 Windows 10 us美国加利福利亚州洛杉矶

    灵妹妹发这些看不懂的干嘛
    多发点丝不好看
    今天的灵妹妹很仙

      1. Level 4
        Google Chrome 125 Google Chrome 125 Windows 11 Windows 11 cn湖北省武汉市 联通

        移动应用终端开发,结课作业是写一个带数据库的二手交易软件,都快吐了 dash2

  2.  Level 5
    Microsoft Edge 125 Microsoft Edge 125 GNU/Linux GNU/Linux cn广东省珠海市 电信

    厉害了,一看角标数字就是很难调通的,看了你的过程还这么复杂

  3. Level 5
    Wordpress App 24 Wordpress App 24 iPad iOS 17.5.1 iPad iOS 17.5.1 cn安徽省 移动

    确实有点后悔当年没学编程,挺喜欢的,现在脑子不够用了,学不进去

    1. 公主 Queen 
      Google Chrome 120 Google Chrome 120 Windows 10 Windows 10 cn山东省临沂市 联通

      其实也没什么高级的东西,新时代的农民工

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注