慢牛系列五:用百度语音识别添加自选股
开发慢牛股票app已经有很长一段时间,最近在考虑用什么方式添加自选股最方便?传统的做法是为用户专门开发一个键盘,字母或者数字的,帮助用户录入股票的简称或者编码,大多数app都是这么做的,下面是海通证券app为用户开发的键盘,多数券商是这么做的,这么做已经很不错了。
当然,我的想法没有什么太突出的,无外乎三种办法:
- 第一种:从其他APP导入,比如从腾讯自选股导入,要获得腾讯自选股数据,需要用QQ号登陆QQ的Web端,一般不会有人愿意在我这里输入QQ和密码的。
- 第二种:语音识别,上网搜了下,主要是百度和科大讯飞提供语音识别SDK,而且免费的,下载了demo,感觉不错,可以做。
- 第三种:拍照,用户只需拍下来QQ自选股,上传图片,后台分析出个股,添加到用户的自选股里,上网搜了下图片识别的SDK,百度有,但是只有企业版,收费的,图片识别这块有个开源项目:Tessercat,出自惠普实验室,目前是谷歌在维护,试用了下,对简体中文的识别项目不太理想,如果想识别高的话,需要进行中文的识别训练,当然也尝试了下,是个体力活,所以这方面还是等百度开放他们的图片识别API在做吧,或者园里的大牛们有什么好的方案推荐下,多谢啦!
下面说说我使用百度语音识别的效果:
实现过程如下:
1.注册百度语音
注册网址:百度语音注册
开发者需要有个百度开发平台账号,还需要在应用中创建一个应用,并为应用开通语音识别服务,开通后获得api_key和secret_key。
下载SDK,我使用的是在线SDK。
我的应用是需要再有网络情况下使用的,所以选择在线的SDK。
2.引用文件
将SDK中的libs和res有两个文件夹拷贝到android项目对应的目录下,可以拷贝到android的主项目下,也可以专门建立子项目,放置这些文件。我单独建立了一个android项目,然后主项目引用子项目。
官方的手册里只是说明了如何在Eclipse里如何引用,可以参考这篇文章链接:
Eclipse中使用百度语音
我项目里用的是Android Studio,build工具是gradle,主要是gradle的设置。
apply plugin: 'com.android.library'
android {
……
sourceSets{
main {
jniLibs.srcDir(['libs']) // <-- Set your folder here!
}
}
……
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
……
}
说明:
SDK中包含jar和so文件,jar可以通过dependencies里设置引用:
compile fileTree(dir: 'libs', include: ['*.jar'])
so文需要再上面s件ourceSets中设置,如上面的main的设置:
jniLibs.srcDir(['libs'])
注意:还需要在libs下复制一个文件夹armeabi,重命名:armeabi-v7a,如果不加个这个文件夹,会报一个找不到BDVoiceRecognitionClient_MFE_V1类型的异常。
3.接入
语音主要有两种模式,对话框模式和API模式,我这里是说下对话框模式,API模式参考后续的代码。
关于如何调用语音对话框,百度语音官方的文档里有说明,因为我要在Reac Native里使用百度语音,所以需要写桥接代码,下面是在android端的代码,继承ViewGroupManager,在React Native里做了一个容器。
public class VoiseRecognitionManager extends ViewGroupManager<ReactViewGroup> {
public static final int COMMAND_SHOW = 0;
public static final int COMMAND_DISMISS = 1;
private static final String CLASS_NAME = "VoiseRecognition";
private ReactViewGroup view=null;
private BaiduASRDigitalDialog mDialog=null;
@Override
public String getName() {
return CLASS_NAME;
}
@Override
protected ReactViewGroup createViewInstance(ThemedReactContext reactContext) {
this.view= new ReactViewGroup(reactContext);
return this.view;
}
@Override
public @Nullable Map<String, Integer> getCommandsMap() {
return MapBuilder.of(
"show",
COMMAND_SHOW,
"dismiss",
COMMAND_DISMISS);
}
@Override
public void receiveCommand(
ReactViewGroup view,
int commandId,
@Nullable ReadableArray config) {
if(commandId==COMMAND_SHOW){
this.showDialog(view.getContext(),config.getMap(0));
}else if(commandId==COMMAND_DISMISS){
this.dismiss();
}
}
@ReactMethod
public void showDialog(Context context,ReadableMap config) {
if (mDialog != null) {
mDialog.dismiss();
}
Bundle params=new Bundle();
String api_key=config.getString("api_key");
String secret_key=config.getString("secret_key");
//设置开放平台 API Key
params.putString(BaiduASRDigitalDialog.PARAM_API_KEY,api_key);
//设置开放平台 Secret Key
params.putString(BaiduASRDigitalDialog.PARAM_SECRET_KEY,secret_key);
//设置识别领域:搜索、输入、地图、音乐……,可选。默认为输入。
Object PARAM_PROP_VALUE= config.getInt("prop");
params.putInt( BaiduASRDigitalDialog.PARAM_PROP, PARAM_PROP_VALUE==null?VoiceRecognitionConfig.PROP_INPUT:(Integer) PARAM_PROP_VALUE);
//设置语种类型:中文普通话,中文粤语,英文,可选。默认为中文普通话
Object LANGUAGE=config.getString("language");
params.putString( BaiduASRDigitalDialog.PARAM_LANGUAGE,LANGUAGE==null?VoiceRecognitionConfig.LANGUAGE_CHINESE:LANGUAGE.toString());
//如果需要语义解析,设置下方参数。领域为输入不支持
Object PARAM_NLU_ENABLE=config.getBoolean("nlu_enable");
params.putBoolean(BaiduASRDigitalDialog.PARAM_NLU_ENABLE,PARAM_NLU_ENABLE==null?false:(boolean)PARAM_NLU_ENABLE);
// 设置对话框主题,可选。BaiduASRDigitalDialog 提供了蓝、暗、红、绿、橙四中颜色,每种颜
// 色又分亮、暗两种色调。共 8 种主题,开发者可以按需选择,取值参考 BaiduASRDigitalDialog 中
// 前缀为 THEME_的常量。默认为亮蓝色
Object PARAM_DIALOG_THEME=config.getInt("dialog_theme");
params.putInt(BaiduASRDigitalDialog.PARAM_DIALOG_THEME,PARAM_DIALOG_THEME==null?BaiduASRDigitalDialog.THEME_RED_DEEPBG:(Integer) PARAM_DIALOG_THEME);
params.putBoolean( BaiduASRDigitalDialog.PARAM_START_TONE_ENABLE,true);
params.putBoolean( BaiduASRDigitalDialog.PARAM_END_TONE_ENABLE,true);
params.putBoolean( BaiduASRDigitalDialog.PARAM_TIPS_TONE_ENABLE,true);
mDialog=new BaiduASRDigitalDialog(context,params);
final ThemedReactContext reactContext=(ThemedReactContext)context;
final ReactViewGroup tempView=this.view;
DialogRecognitionListener mRecognitionListener=new DialogRecognitionListener(){
@Override
public void onResults(Bundle results){
//此处处理识别结果,识别结果可能有多个,按置信度从高到低排列,第一个元素是置信度最高的结果。
ArrayList<String> rs=results !=null?results
.getStringArrayList(RESULTS_RECOGNITION):null;
if(rs!=null){
WritableArray params=Arguments.createArray();
for (String r : rs) {
params.pushString(r);
}
WritableMap data=Arguments.createMap();
data.putArray("result",params);
//向JS端发送事件,附带识别结果
reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(
tempView.getId(),
"topChange",
data);
}
}
};
mDialog.setDialogRecognitionListener(mRecognitionListener);
mDialog.show();
}
@ReactMethod
public void dismiss(){
if(this.mDialog!=null)
this.mDialog.dismiss();
}
}
JS端代码
var requireNativeComponent = require('requireNativeComponent');
var React = require('React');
var View = require('View');
var PropTypes = require('ReactPropTypes');
var UIManager = require('UIManager');
var TouchableOpacity=require('TouchableOpacity');
var VOISE_REF = 'baiduVoise';
var params=require('./Params')
var BaiduVoise = React.createClass({
getDefaultProps: function() {
return {
prop:params.PROP_FINANCE,
language:params.LANGUAGE_CHINESE,
dialog_theme:params.THEME_RED_DEEPBG,
nlu_enable:false,
};
},
propTypes: {
...View.propTypes,
api_key: PropTypes.string,
secret_key: PropTypes.string,
prop: PropTypes.number,
language: PropTypes.string,
dialog_theme: PropTypes.number,
},
show: function(callback) {
var config = {
api_key: this.props.api_key,
secret_key: this.props.secret_key,
prop: this.props.prop,
language: this.props.language,
dialog_theme: this.props.dialog_theme,
nlu_enable: this.props.nlu_enable
};
//向原生端发送事件,启动对话框
UIManager.dispatchViewManagerCommand(
React.findNodeHandle(this.refs[VOISE_REF]),
UIManager.VoiseRecognition.Commands.show,
[config]);
},
hide: function() {
//向原生端发送事件,隐藏对话框
UIManager.dispatchViewManagerCommand(
React.findNodeHandle(this.refs[VOISE_REF]),
UIManager.VoiseRecognition.Commands.dismiss,
[]);
},
onReceive: function(e) {
var result=e.nativeEvent.result;
if(this.props.nlu_enable){
var str=e.nativeEvent.result;
var obj=JSON.parse(str);
result=obj.item;
}
this.props.onReceive&&this.props.onReceive(result);
},
onPress:function (argument) {
var me=this
me.show();
},
render: function() {
return (
<TouchableOpacity
activeOpacity ={0.5}
underlayColor="#B5B5B5"
onPress={this.onPress}>
<VoiseRecognition
ref={VOISE_REF}
onChange={this.onReceive}
style= {this.props.style}>
{this.props.children}
</VoiseRecognition>
</TouchableOpacity>
);
},
});
BaiduVoise.Params=params;
var VoiseRecognition = requireNativeComponent('VoiseRecognition', BaiduVoise, {
nativeOnly: {
onChange: true
}
});
module.exports = BaiduVoise;
4.使用方法
/* @flow */
'use strict';
var React = require('react-native');
var {
BaiduVoise,
SpeechRecognizer
}=require('react-native-voise');
var {
StyleSheet,
View,
Text
} = React;
var Component = React.createClass({
getInitialState() {
return { result:'' }
},
onReceive:function (results) {
//results is a list ,the first one is the best result.
this.setState((state)=>{
state.result=results[0];
});
},
render: function() {
return (
<View style={styles.container}>
<Text>this.state.result</Text>
<BaiduVoise
ref={'BaiduVoise'}
style={styles.button}
api_key={'q0UcNM0glvjekMtBQNWzM92y'} //设置api_key和secret_key
secret_key={'8hRsMQCQGNdwqnyF8GkWBgr6WObZFT5l'}
onReceive={this.onReceive}>
<Text>点击,说话</Text>
</BaiduVoise>
</View>
);
}
});
var styles = StyleSheet.create({
container:{
flex:1
},
button:{
height:50,
}
});
module.exports = Component;
在React Native里,桥接原生组件是一件比较简单的事情,只要学一些原生的东西就能做了,主要是React Native给我们提供了平台和工具集,平台负责管理组件的布局,样式,行为,又建立了原生和JS端通信机制,让我们做Web的同学也可以做原生了,当然,特殊情况下还是需要完全原生的,大多数小而美的app,React Native是非常合适的。
听说微信要做应用号,将来微信估计也要做这样的一个平台,微信提供标准的UI组件,众多app开发者可以在微信的平台上通过js写出性能体验更好的app了,相比现在通过浏览器实现的企业号,体验肯定会更好。
如果有想体验百度语音的用户,可以下载慢牛APP的APK体验,点击右上角的放大镜,进入自选股添加页面:
关注慢牛的公众号:发送react,返回apk下载链接,apk大小8M,最好连接WiFi下载。
这个项目的代码已经发布到github上:https://github.com/hongyin163/react-native-voise
欢迎安装试用!
最后,欢迎园友提出好的想法,评论留名!谢谢!