记一次uniappAndroid原生插件开发
背景
项目使用到了RFID射频手持设备(PDA),用于读取羊只耳标功能。 原来有写过一个插件 https://ext.dcloud.net.cn/plugin?id=5246,新的设备与原来的不一样,所以需要重新写一遍。
之前没有记录,这次重新记录下过程。
使用的硬件设备Demo 为: uhfg_v2.2.10;资料原生插件开发资料官方:
https://nativesupport.dcloud.net.cn/NativePlugin/README
步骤1.下载App离线SDK及demo导入
https://nativesupport.dcloud.net.cn/AppDocs/download/android
下载后有对应的工程文件,需要使用Android Studio导入。
我下载的是 3.5.0,目录结构是:
我们将UniPlugin-Hello-AS导入到Android Studio 中。
导入后目录结构是这样的:
我做的项目是使用原生的扫描耳标的能力,不需要界面,所以插件是 扩展 Module 的形式。2.新建module uhfg模块
3.导入uhf的库及配置build.gradle
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.javapackage 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 allDevices = new HashSet(); while (times <3){ times +=1; manager.asyncStartReading(); try { int finalTimes = times; new Handler().postDelayed(new Runnable() { @Override public void run() { // tagInventoryByTimer List 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 "; // 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 allDevices = new HashSet(); try { new Handler().postDelayed(new Runnable() { @Override public void run() { // tagInventoryByTimer List 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 "; // 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 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.javapackage com.nmbaiyun.yangrong.uhfg; import java.util.Set; public interface UHFGUtilListenner { public void callBackResult(int num, Set 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 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文件。
然后将 arr文件+libs+.os文件重新放在一起,构建成uni-app所需的原生插件。 到此,原生插件开发完成。
目录结构:
7.引入项目本地引入
在项目下创建目录 nativeplugins 将插件包放在该目录下,插件结构参考前问。
在manifest 中配置本地插件;
配置完成后,需要打包为自定义基座才能使用。云端插件
将插件发布到云市场,然后就可以通过使用云端插件的方式使用。同样,测试时候需要打包自定义基座。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 打包库
解决方案:https://blog.csdn.net/shulianghan/article/details/1244893982.出现Duplicate class 的报错
多半是有重复的类,或者说是不同jar包定义了相同的类导致。去掉非必要的jar包。
我出现问题的原因是,在引入第三方包的时候采用了 implementation 的方式,该方式会将第三方包编译到 aar文件当中,而uniapp的插件规范是有 libs库的,所以出现两份,报 Duplicate class 的错误.
编译时的区别
3.[JS Framework] 当前运行的基座不包含原生插件[Taric-UHFG],请在manifest中配置该插件,重新制作包括该原生插件的自定义运行基座
参考package.json 标准:https://nativesupport.dcloud.net.cn/NativePlugin/course/package
我出错的问题是 package.json 中 id与plugins里的name不一致的问题
小结
整体上来将,简单的集成第三方库,不太需要过多的了解android原生的知识,但需要对gradle这套有一定的理解。
本任务耗时1.5天;如果要测试就得打包到基座,我把uni-app都打毛了,说次数太多明天再来。附件
源码:https://gitee.com/dream-as-horse/uni-app-rfid-plug-in.git