背景说明:一位老朋友找到我说让我帮忙做个小功能,当时大约是11月初,正赶上公司封闭开发,然后就拖到了11月底,最终我摸了摸我的头发,还是答应了,项目首先要解决的是语音播放问题,但是厂商的系统不支持TTS,最终只能集成了讯飞语音;然后又来了一个新的难点,厂商的原系统在局域网中要与新设备的系统做数据交互,刚开始想用Socket,也确实能实现,后来又被否决了,厂商那边想通过接口调用的方式来实现,最终,在apk中给他起了个服务,最终来到了最后一个最终的难点,厂商的摄像头是前置的,并且只能通过UVC协议来调用,还不提供调用方法,我要你有啥用,卡了几天,最终还是实现了微信、支付宝支付的初始版,以此篇文章作为记录。
1.语音播放的实现
这个只能算是一个小难点吧,本来按照我做应用的经验来说,只需要5分钟就能实现这个小功能。正常来说,可以通过下面方式实现语音播放功能,如下:
//定义
private TextToSpeech mTextToSpeech;
//初始化
mTextToSpeech = new TextToSpeech(this,
new TextToSpeech.OnInitListener() {
@Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) {
// 设置朗读语言
int supported = mTextToSpeech
.setLanguage(Locale.ENGLISH);
if ((supported != TextToSpeech.LANG_AVAILABLE)
&& (supported != TextToSpeech.LANG_COUNTRY_AVAILABLE)) {
Toast.makeText(MainActivity.this, "不支持当前语言", 0)
.show();
}
}
}
});
//调用
mTextToSpeech.speak(“要读的文字”, TextToSpeech.QUEUE_FLUSH, null);
在手机应用就是这么简单的调用原生API即可实现,当然如果某些手机设备或者PDA产品这样也无法将文字转语音,不要着急,下载个讯飞引擎3.0,安装到手机上或者其他设备上,然后在设备的设置-->无障碍-->文字转语音TTS输出-->首选引擎 中设置讯飞语音引擎即可,如下图
而我拿到的这个奇葩系统竟然不支持,好吧,要不是看在我好朋友的面子上看我搭理你不。没办法了,既然不支持只能上讯飞的sdk了,先去官网申请账号,在语音合成中选择一个,拿到APPID的值
然后在去看下官方文档,下载demo,只能说官方给的demo版本太老了,这里报错那里报错的,而且还要加很多文件夹,真费劲弄的,最终使用了两个jar包及一个播放类解决,通过在页面调用 AudioUtils.getInstance().speakText("播放内容")实现。
AudioUtils代码如下:
public class AudioUtils {
private static AudioUtils audioUtils;
private SpeechSynthesizer mySynthesizer;
public AudioUtils() {
}
public static AudioUtils getInstance() {
if (audioUtils == null) {
synchronized (AudioUtils.class) {
if (audioUtils == null) {
audioUtils = new AudioUtils();
}
}
}
return audioUtils;
}
private InitListener myInitListener = new InitListener() {
@Override
public void onInit(int code) {
Log.d("mySynthesiezer:", "InitListener init() code = " + code);
}
};
public void init(Context context) {
//处理语音合成关键类
mySynthesizer = SpeechSynthesizer.createSynthesizer(context, myInitListener);
//设置发音人
mySynthesizer.setParameter(SpeechConstant.VOICE_NAME, "xiaoyan");
//设置音调
mySynthesizer.setParameter(SpeechConstant.PITCH, "50");
//设置音量
mySynthesizer.setParameter(SpeechConstant.VOLUME, "50");
}
public void speakText(String content) {
int code = mySynthesizer.startSpeaking(content, new SynthesizerListener() {
@Override
public void onSpeakBegin() {
}
@Override
public void onBufferProgress(int i, int i1, int i2, String s) {
}
@Override
public void onSpeakPaused() {
}
@Override
public void onSpeakResumed() {
}
@Override
public void onSpeakProgress(int i, int i1, int i2) {
}
@Override
public void onCompleted(SpeechError speechError) {
}
@Override
public void onEvent(int i, int i1, int i2, Bundle bundle) {
}
});
}
}
2.在APK中搭建服务
在此之前,简单写了一个局域网Socket通信demo,稍后会整理下上传供参考,缺点是辅设备需要知道主设备的IP,看有的大神可以通过发送广播来自动获得主设备的IP,由于最终没用Socket通信这种方式,最终也研究下去,主要介绍下服务的搭建。说到服务,最初想的肯定是后端搭建,对应用来说,第一想法是这个臣妾做不到啊,nanohttpd、AndServer均可实现在Android应用中搭建服务,先来写一个获取设备IP地址的方法,并将地址及端口号显示到设备首页
//获取IP地址
public static String getLocalIpStr(Context context){
WifiManager wifiManager=(WifiManager)context.getSystemService(Context.WIFI_SERVICE);
WifiInfo wifiInfo=wifiManager.getConnectionInfo();
return intToIpAddr(wifiInfo.getIpAddress());
}
private static String intToIpAddr(int ip){
return (ip & 0xFF)+"."
+ ((ip>>8)&0xFF) + "."
+ ((ip>>16)&0xFF) + "."
+ ((ip>>24)&0xFF);
}
//页面显示设置
ipShow.setText(getLocalIpStr(this)+":"+portValue);
定义一个HttpServer,然后start,可以设置参数超时时间,不设置参数默认5秒
public void start(final int timeout) throws IOException { start(timeout, true);}
HttpServer实现如下:
/**
* @ProjectName:
* @Package:
* @ClassName: HttpServer
* @Description: java类作用描述
* @Author: yingch
* @CreateDate: 2020/11/27 18:32
* @UpdateUser: 更新者
* @UpdateDate: 2020/11/27 18:32
* @UpdateRemark: 更新说明
* @Version: 1.0
*/
public class HttpServer extends NanoHTTPD {
RespEntity respEntity = new RespEntity();
public String totalCount = "0";
public enum Status implements NanoHTTPD.Response.IStatus {
SWITCH_PROTOCOL(101, "Switching Protocols"),
NOT_USE_POST(700, "not use post");
private final int requestStatus;
private final String description;
Status(int requestStatus, String description) {
this.requestStatus = requestStatus;
this.description = description;
}
@Override
public String getDescription() {
return null;
}
@Override
public int getRequestStatus() {
return 0;
}
}
public HttpServer(int port) {
super(port);
}
public NanoHTTPD.Response serve(NanoHTTPD.IHTTPSession session) {
String body = "";
Map<String, String> mapArr = new HashMap<>();
respEntity.setCode("201");
respEntity.setMessage("失败");
Map<String, String> files = new HashMap<String, String>();
/*获取header信息,NanoHttp的header不仅仅是HTTP的header,还包括其他信息。*/
Map<String, String> header = session.getHeaders();
try {
session.parseBody(files);
body = session.getQueryParameterString();
mapArr = session.getParms();
header.get("http-client-ip");
} catch (IOException e) {
e.printStackTrace();
} catch (NanoHTTPD.ResponseException e) {
e.printStackTrace();
}
String pathUrl = session.getUri();
Map<String, String> temArr = new HashMap<>();
respEntity = new RespEntity();
if (pathUrl.equals("/sell/dev/pay")) {
GoodsEntity goodsEntity = new GoodsEntity();
goodsEntity.setSellDevID(mapArr.get("sellDevID"));
EventBus.getDefault().post(goodsEntity);
respEntity.setPayRecordId(goodsEntity.getPayRecordId());
respEntity.setCode("200");
respEntity.setMessage("成功");
}
return newFixedLengthResponse(NanoHTTPD.Response.Status.OK, "json", JSON.toJSONString(respEntity));
}
}
默认首页如下:
POST请求如下:
这样我们就在应用中成功搭建起了一个服务,多个服务可以通过接口url地址进行判断。
3.通过UVC协议调用摄像头,通过集成zxing开源库实现二维码扫码
这里涉及到的内容比较多,下节在介绍,下面我们上下图看下支付成功的结果,如下:
至此就剩下细节需要完善了总体流程基本没问题了,后续会在加上数据库实现订单数据的本地存储更新。