慢牛系列五:用百度語音識別添加自選股
開發慢牛股票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
歡迎安裝試用!
最后,歡迎園友提出好的想法,評論留名!謝謝!
