记一次uni-app Android原生插件开发

3,738 阅读4分钟

背景

项目使用到了RFID射频手持设备(PDA),用于读取羊只耳标功能。 原来有写过一个插件 https://ext.dcloud.net.cn/plugin?id=5246,新的设备与原来的不一样,所以需要重新写一遍。

之前没有记录,这次重新记录下过程。

使用的硬件设备Demo 为: uhfg_v2.2.10;

资料

  1. 原生插件开发资料官方: nativesupport.dcloud.net.cn/NativePlugi…

步骤

1.下载App离线SDK及demo导入

nativesupport.dcloud.net.cn/AppDocs/dow… 下载后有对应的工程文件,需要使用Android Studio导入。

我下载的是 3.5.0,目录结构是: 28262329.png 我们将UniPlugin-Hello-AS导入到Android Studio 中。

导入后目录结构是这样的:

28262374.png

我做的项目是使用原生的扫描耳标的能力,不需要界面,所以插件是 扩展 Module 的形式。

2.新建module uhfg模块

28262551.png

3.导入uhf的库及配置build.gradle

28281559.png

uhfg模块 的build.gradle


plugins {
    id 'com.android.library'
}

android {
    signingConfigs {
        release {
        }
    }
    compileSdkVersion 30

    defaultConfig {
        minSdkVersion 21
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        consumerProguardFiles "consumer-rules.pro"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}
repositories {
    flatDir {
        dirs 'libs'
    }
}

dependencies {
// implementation 与 compileOnly在编译时候有区别
    compileOnly fileTree(dir: 'libs', include: ['*.jar'])

    compileOnly fileTree(dir: '../app/libs', include: ['uniapp-v8-release.aar'])

    compileOnly 'androidx.recyclerview:recyclerview:1.1.0'
    compileOnly 'androidx.legacy:legacy-support-v4:1.0.0'
    compileOnly 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.alibaba:fastjson:1.1.46.android'
    implementation 'com.facebook.fresco:fresco:1.13.0'


//    implementation files('libs/reader.jar')
//    implementation files('libs/ModuleAPI_J.jar')
//    implementation files('libs/UHF67_v1.0.3.jar')
//    implementation files('libs/App_Demo_API.jar')
//    implementation files('libs/DeviceAPIver20150204.jar')
//    implementation files('libs/logutil-1.5.1.1.jar')

}

4.编写工具类

UHFGUtil.java

package com.nmbaiyun.yangrong.uhfg;

import android.os.Handler;
import android.util.Log;

import com.handheld.uhfr.UHFRManager;
import com.uhf.api.cls.Reader;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import cn.pda.serialport.Tools;

// 单例模式,确保不多次初始化manager
public class UHFGUtil {
    private static UHFGUtil instance = new UHFGUtil();
    //获取唯一可用的对象
    public static UHFGUtil getInstance(){
        return instance;
    }
    private UHFGUtil(){
        initManager();
    }

    public UHFRManager manager;//uhf

    public void initManager(){
        if(manager == null){
            manager = UHFRManager.getInstance();
        }else{
            Log.e("UHFRUntil","UHFRManager 已经被初始化了!");
        }
    }

    public void close(){
        manager.close();
    }

    //    3次汇总统计
    public void count(UHFGUtilListenner listenner, int delys){
        Log.e("UHFRUntil","开始计数");

        if (delys == 0){
            delys = 1000;
        }
        int times = 0;
        Set<String> allDevices = new HashSet<String>();
        while (times <3){
            times +=1;
            manager.asyncStartReading();
            try {
                int finalTimes = times;
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
//                    tagInventoryByTimer
                        List<Reader.TAGINFO> list = manager.tagInventoryRealTime();
//                    Log.i("scan", list.size()+"__"+list1.size()+"__"+list2.size());

                        manager.asyncStopReading();
                        if(list.size()>0){
                            Log.i("scan", list.get(0).toString());

                            for (Reader.TAGINFO tfs : list) {
                                byte[] epcdata = tfs.EpcId;

                                String data = Tools.Bytes2HexString(epcdata, epcdata.length);
                                allDevices.add(data);
//                        int rssi = tfs.RSSI;
//                        Message msg = new Message();
//                        msg.what = 1;
//                        Bundle b = new Bundle();
//                        b.putString("data", data);
//                        b.putString("rssi", rssi + "");
//                        msg.setData(b);
//
//                        datas += k + ".设备数据是:"+data+"  "+rssi+"\r\n";
//                        k++;

                            }

                        }
                        if (finalTimes >= 3 && listenner != null){
                            listenner.callBackResult(allDevices.size(),allDevices);
                        }

                    }
                },delys);
            }catch (Exception e){
                manager.asyncStopReading();


            }

        }


    }

    //    获取一组耳标
    public void getRFIDs(UHFGUtilListenner listenner){
        manager.asyncStartReading();
        Set<String> allDevices = new HashSet<String>();
        try {
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
//                    tagInventoryByTimer
                    List<Reader.TAGINFO> list = manager.tagInventoryRealTime();
//                    Log.i("scan", list.size()+"__"+list1.size()+"__"+list2.size());

                    manager.asyncStopReading();
                    if(list.size()>0){
                        Log.i("scan", list.get(0).toString());

                        for (Reader.TAGINFO tfs : list) {
                            byte[] epcdata = tfs.EpcId;

                            String data = Tools.Bytes2HexString(epcdata, epcdata.length);
                            allDevices.add(data);
//                        int rssi = tfs.RSSI;
//                        Message msg = new Message();
//                        msg.what = 1;
//                        Bundle b = new Bundle();
//                        b.putString("data", data);
//                        b.putString("rssi", rssi + "");
//                        msg.setData(b);
//
//                        datas += k + ".设备数据是:"+data+"  "+rssi+"\r\n";
//                        k++;

                        }

                    }
                    if ( listenner != null){
                        listenner.callBackResult(allDevices.size(),allDevices);
                    }

                }
            },500);
        }catch (Exception e){
            manager.asyncStopReading();
        }
    }

    /**
     *
     * @return
     * @throws InterruptedException
     */
    public String getSyncRFID() {
        String data = "";
        try{
            manager.asyncStartReading();
            Thread.sleep(300);
            List<Reader.TAGINFO> list = manager.tagInventoryRealTime();
            if(list.size()>0) {
                Log.i("getSyncRFID", list.get(0).toString());

                for (Reader.TAGINFO tfs : list) {
                    byte[] epcdata = tfs.EpcId;

                    data = Tools.Bytes2HexString(epcdata, epcdata.length);
                }
            }
            manager.asyncStopReading();
            return data;
        }catch (Exception e){
            Log.e("getSyncRFID",e.getMessage());
            return data;
        }


    }

//    获取设备读写功率

    /**
     * 获取设备读写功率,功率越大越费电,可监测范围越远
     * @return
     */
    public String getPower(){
        int [] result = manager.getPower();
        if(result != null){
            return "读功率:"+result[0]+" 写功率:"+result[1];
        }
        return "获取失败";
    }

    /**
     * 设置设备读写功率
     * @param readPower 5~30
     * @param writePower 5~30
     * @return
     */
    public Boolean setPower(int readPower, int writePower){
        Reader.READER_ERR err = manager.setPower(readPower,writePower);
        return err == Reader.READER_ERR.MT_OK_ERR;

    }


}



UHFGUtilListenner.java

package com.nmbaiyun.yangrong.uhfg;

import java.util.Set;

public interface UHFGUtilListenner {
    public void callBackResult(int num, Set<String> list);
}

5.编写model类

package com.nmbaiyun.yangrong.uhfg;

import android.util.Log;

import com.alibaba.fastjson.JSONObject;

import java.util.Set;

import io.dcloud.feature.uniapp.annotation.UniJSMethod;
import io.dcloud.feature.uniapp.bridge.UniJSCallback;
import io.dcloud.feature.uniapp.common.UniModule;

public class UHFGUtilModule extends UniModule {
    String TAG = "UHFGUnitModule";
    public static int REQUEST_CODE = 1000;
//    计数中
    boolean isCounting = false;


    @UniJSMethod(uiThread = false)
    public void getSyncRFID(UniJSCallback callback) {

        if(callback != null) {
            String rfid = "";
            JSONObject data = new JSONObject();
            try {
                UHFGUtil until = UHFGUtil.getInstance();

                rfid = until.getSyncRFID();
                data.put("code", "200");
                data.put("data",rfid);
                callback.invoke(data);
            }catch (Exception e){
                data.put("code", "400");
                data.put("msg","异常"+e.getMessage());
                callback.invoke(data);
            }

        }else {
            Log.i(TAG,"未传回调函数");
        }
    }

    @UniJSMethod(uiThread = false)
    public void asyncCount(UniJSCallback callback){
        if(callback != null) {
            String rfid = "";
            JSONObject data = new JSONObject();
            try {
                if(isCounting) {
                    return;
                }
                UHFGUtil until = UHFGUtil.getInstance();
                until.count(new UHFGUtilListenner() {
                    @Override
                    public void callBackResult(int num, Set<String> list) {
                        Log.i(TAG+"toGetAllDevices",num+""+list.toString());
                        isCounting = false;
                        data.put("code", "200");
                        data.put("msg","获取数据成功");
                        data.put("total",num);
                        data.put("data",list);
                        callback.invoke(data);
                    }
                },500);
            }catch (Exception e){
                data.put("code", "400");
                data.put("msg","异常"+e.getMessage());
                callback.invoke(data);
            }

        }else {
            Log.i(TAG,"未传回调函数");
        }
    }



    @UniJSMethod(uiThread = false)
    public JSONObject getPower(){
        UHFGUtil until = UHFGUtil.getInstance();
        String res = until.getPower();
        JSONObject data = new JSONObject();
        data.put("code", "200");
        data.put("data",res);
        return data;
    }

}

6.打包插件

选择Android studio上的gradle工具,使用assembleRelease 的方式进行编译,构建。 最后会在当前库下生成 build文件夹,在outputs/aar 生成 arr文件。

28282298.png

然后将 arr文件+libs+.os文件重新放在一起,构建成uni-app所需的原生插件。 到此,原生插件开发完成。

目录结构:

28282325.png

7.引入项目

本地引入

在项目下创建目录 nativeplugins 将插件包放在该目录下,插件结构参考前问。

28294507.png 在manifest 中配置本地插件;

28294562.png

配置完成后,需要打包为自定义基座才能使用。

云端插件

将插件发布到云市场,然后就可以通过使用云端插件的方式使用。同样,测试时候需要打包自定义基座。

8.web端调用代码

    var modal = uni.requireNativePlugin('Taric-UHFG');
    modal.getSyncRFID(function(res) {
            						
            that.animals = Array.from(new Set(that.animals.concat(res.data)))
            that.amount = that.animals.length;
            that.nodocAnimals = that.animals;
            						
    });        

其他的坑及参考

  • 1.Android studio gradle 无法用Tasks assembleRelease 打包库

解决方案:blog.csdn.net/shulianghan…

  • 2.出现Duplicate class 的报错

28279641.png

多半是有重复的类,或者说是不同jar包定义了相同的类导致。去掉非必要的jar包。

我出现问题的原因是,在引入第三方包的时候采用了 implementation 的方式,该方式会将第三方包编译到 aar文件当中,而uniapp的插件规范是有 libs库的,所以出现两份,报 Duplicate class 的错误.

编译时的区别

28281602.png

  • 3.[JS Framework] 当前运行的基座不包含原生插件[Taric-UHFG],请在manifest中配置该插件,重新制作包括该原生插件的自定义运行基座

参考package.json 标准:nativesupport.dcloud.net.cn/NativePlugi…

我出错的问题是 package.json 中 id与plugins里的name不一致的问题

28294491.png

小结

整体上来将,简单的集成第三方库,不太需要过多的了解android原生的知识,但需要对gradle这套有一定的理解。

本任务耗时1.5天;如果要测试就得打包到基座,我把uni-app都打毛了,说次数太多明天再来。

附件

源码: gitee.com/dream-as-ho…