Github的地址—VoiceTranslation項(xiàng)目
功能
- 英文翻譯成中文
- 英文語音翻譯
- 翻譯時(shí)沒有網(wǎng)絡(luò)的提示
實(shí)現(xiàn)
思路
整體思路
- 使用 AsyncTask 發(fā)起網(wǎng)絡(luò)請(qǐng)求
- .doInBackground 做真正的后臺(tái)網(wǎng)絡(luò)請(qǐng)求
- onPostExecute 對(duì)返回的結(jié)果作處理
- 利用接口將請(qǐng)求的數(shù)據(jù)(翻譯的結(jié)果)返回給 UI
翻譯模塊
翻譯使用有道翻譯 的 API训堆,參考 http://fanyi.youdao.com/openapi?path=data-mode
- 通過網(wǎng)址拼接的形式來獲取翻譯的數(shù)據(jù)
- http://fanyi.youdao.com/openapi.do?keyfrom=<keyfrom>&key=<key>&type=data&doctype=<doctype>&version=1.1&q=要翻譯的文本
- 需要修改的地方
- key需要替換為自己申請(qǐng)的key 挡爵;
- doctype - 返回結(jié)果的數(shù)據(jù)格式西采,xml或json或jsonp,此處是json
- 返回碼errorCode是0 的時(shí)候表示 正常
訊飛語音接口
- 下載相應(yīng)的SDK,集成到項(xiàng)目中
- 根據(jù)文檔 http://www.xfyun.cn/doccenter/awd 調(diào)用接口
關(guān)鍵點(diǎn)
網(wǎng)絡(luò)請(qǐng)求
AsyncTask
AsyncTask的三個(gè)參數(shù)肛捍,
- Params 啟動(dòng)任務(wù)執(zhí)行的輸入?yún)?shù),比如HTTP請(qǐng)求的URL————決定了doInBackground方法、execute方法的參數(shù)類型
- Progress 后臺(tái)任務(wù)執(zhí)行的百分比————決定了publishProgress方法耳高、onProgressUpdate方法的參數(shù)類型
- Result 后臺(tái)執(zhí)行任務(wù)最終返回的結(jié)果,比如String,Integer等————決定了doInBackground方法的返回值類型所踊、onPostExecute方法的參數(shù)類型
不需要參數(shù)可以傳 void泌枪,此處 doInBackground方法 執(zhí)行后臺(tái)翻譯的網(wǎng)絡(luò)請(qǐng)求,所以參數(shù)類型是 String 秕岛,返回的結(jié)果也是 String 類型碌燕,在onPostExecute中處理返回的結(jié)果误证,第二個(gè)參數(shù)百分比目前不需要,設(shè)置為void
HttpURLConnection
//執(zhí)行網(wǎng)絡(luò)請(qǐng)求
URL url = null;
HttpURLConnection httpURLConnection = null;
BufferedReader reader = null;
StringBuilder response = new StringBuilder();
try {
url = new URL(params[0]);
Log.e(TAG,"網(wǎng)絡(luò)請(qǐng)求鏈接-----"+url.toString());
httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setRequestMethod("GET");
httpURLConnection.setConnectTimeout(5000);
httpURLConnection.setReadTimeout(5000);
InputStream is = httpURLConnection.getInputStream();
reader = new BufferedReader(new InputStreamReader(is));
Log.e(TAG,"網(wǎng)絡(luò)請(qǐng)求-----"+httpURLConnection.getResponseCode());
String line ;
while ((line = reader.readLine()) != null){
response.append(line);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (httpURLConnection != null) {
httpURLConnection.disconnect();
}
請(qǐng)求結(jié)果與UI交互
onPostExecute中來處理返回的結(jié)果
與UI交互修壕,利用接口
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
if (s != null) {
onResponseListener.onSuccess(s);
}
}
回調(diào)接口
public interface onResponseListener{
void onSuccess(String result);
void onFail(String error);
}
網(wǎng)絡(luò)請(qǐng)求實(shí)例寫成單例
public static HttpUtil getInstance() {
if (mHttpUtil == null) {
synchronized (HttpUtil.class){
if (mHttpUtil == null) {
mHttpUtil = new HttpUtil();
}
}
}
return mHttpUtil;
}
語音識(shí)別
初始化
- 在OnCreate方法中初始化
SpeechUtility.createUtility(context, SpeechConstant.APPID +"=APPID");
- 創(chuàng)建SpeechRecognizer對(duì)象
SpeechRecognizer mIat = SpeechRecognizer.createRecognizer(MainActivity.this,mInitListener);
/**
* 初始化監(jiān)聽器愈捅。
*/
private InitListener mInitListener = new InitListener() {
@Override
public void onInit(int code) {
if (code != ErrorCode.SUCCESS) {
Toast.makeText(MainActivity.this, "初始化失敗,錯(cuò)誤碼:" + code, Toast.LENGTH_SHORT).show();
}
}
};
- 設(shè)置參數(shù)
public void setParam() {
// 清空參數(shù)
mIat.setParameter(SpeechConstant.PARAMS, null);
// 設(shè)置聽寫引擎
mIat.setParameter(SpeechConstant.ENGINE_TYPE, mEngineType);
// 設(shè)置返回結(jié)果格式
mIat.setParameter(SpeechConstant.RESULT_TYPE, "json");
// 設(shè)置語言
mIat.setParameter(SpeechConstant.LANGUAGE, "en_us");
// 設(shè)置音頻保存路徑慈鸠,保存音頻格式支持pcm蓝谨、wav,設(shè)置路徑為sd卡請(qǐng)注意WRITE_EXTERNAL_STORAGE權(quán)限
// 注:AUDIO_FORMAT參數(shù)語記需要更新版本才能生效
mIat.setParameter(SpeechConstant.AUDIO_FORMAT,"wav");
mIat.setParameter(SpeechConstant.ASR_AUDIO_PATH, Environment.getExternalStorageDirectory()+"/msc/iat.wav");
}
4.開始聽寫
mIat.startListening(mRecoListener);
聽寫回調(diào)
- 聽寫結(jié)果回調(diào)接口(返回Json格式結(jié)果
- 一般情況下會(huì)通過onResults接口多次返回結(jié)果青团,完整的識(shí)別內(nèi)容是多次結(jié)果的累加譬巫;
- 關(guān)于解析Json的代碼可參見MscDemo中JsonParser類;
- isLast等于true時(shí)會(huì)話結(jié)束
private RecognizerListener mRecoListener = new RecognizerListener() {
//音量值0~30
@Override
public void onVolumeChanged(int i, byte[] bytes) {
// Toast.makeText(MainActivity.this, "音量:" + i, Toast.LENGTH_SHORT).show();
}
//開始錄音
@Override
public void onBeginOfSpeech() {
// Toast.makeText(MainActivity.this, "開始錄音" , Toast.LENGTH_SHORT).show();
}
//結(jié)束錄音
@Override
public void onEndOfSpeech() {
// Toast.makeText(MainActivity.this, "結(jié)束錄音" , Toast.LENGTH_SHORT).show();
mIatDialog.dismiss();
}
//一般情況下會(huì)通過onResults接口多次返回結(jié)果壶冒,完整的識(shí)別內(nèi)容是多次結(jié)果的累加
@Override
public void onResult(RecognizerResult recognizerResult, boolean isLast) {
printResult(recognizerResult);
Log.d("Result:",recognizerResult.getResultString ());
}
//會(huì)話發(fā)生錯(cuò)誤回調(diào)接口
@Override
public void onError(SpeechError speechError) {
}
//擴(kuò)展用接口
@Override
public void onEvent(int i, int i1, int i2, Bundle bundle) {
}
};
語音返回?cái)?shù)據(jù)處理
數(shù)據(jù)格式-json
![enter description here](http://wx4.sinaimg.cn/large/534fc2d6ly1fe3ikmjx8mj208q09q748.jpg)
![enter description here](http://wx1.sinaimg.cn/large/534fc2d6ly1fe3ilu2ucdj208u071q30.jpg)
- 分析數(shù)據(jù)結(jié)構(gòu)
需要的數(shù)據(jù)在 ws 這個(gè)array下缕题,先去這個(gè)array
JSONArray words = joResult.getJSONArray("ws");
![enter description here](http://wx4.sinaimg.cn/large/534fc2d6ly1fe3in7jwhaj206003l3yb.jpg)
- ws的目錄結(jié)構(gòu)
取ws這個(gè)array 里面的第一個(gè)值
![enter description here](http://wx2.sinaimg.cn/large/534fc2d6ly1fe3iqnj6i2j205601jjr5.jpg)
words.getJSONObject(i)
3.ws下第一個(gè)jsonobject的結(jié)構(gòu)
![enter description here](http://wx4.sinaimg.cn/large/534fc2d6ly1fe3isg4swpj206702k3yb.jpg)
取 cw這個(gè)數(shù)組
JSONArray items = words.getJSONObject(i).getJSONArray("cw");
-
cw 這個(gè)數(shù)組的結(jié)構(gòu)
enter description here
取第一個(gè)值
JSONObject obj = items.getJSONObject(0);
![enter description here](http://wx4.sinaimg.cn/large/534fc2d6ly1fe3ivlfoi5j20im05zaa5.jpg)
取 key為 w 的值
obj.getString("w")
坑。胖腾。烟零。
導(dǎo)包 jar 和 so文件
導(dǎo)包 jar
![enter description here](http://wx2.sinaimg.cn/large/534fc2d6ly1fe3ibhvy6hj208w0a5wet.jpg)
- 按照上圖創(chuàng)建libs目錄
- 拷貝jar包到libs下
- 選中jar包,右鍵
![enter description here](http://wx3.sinaimg.cn/large/534fc2d6ly1fe3igq3u85j209z01iglf.jpg)
- enter description here
添加so文件
![enter description here](http://wx4.sinaimg.cn/large/534fc2d6ly1fe3j09f12sj20a20bsaac.jpg)
- 建立目錄jniLibs
- 拷貝so文件進(jìn)去
-
在該目錄enter description here
中的android添加
sourceSets { main { jni.srcDirs = ['src/main/jni', 'src/main/jniLibs'] }
android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
.....
sourceSets { main { jni.srcDirs = ['src/main/jni', 'src/main/jniLibs'] } }
}
網(wǎng)絡(luò)權(quán)限
開發(fā)過程中咸作,打包完成之后訊飛在錄音時(shí)報(bào)錯(cuò)锨阿,進(jìn)行排查
- 檢查需要的錄音權(quán)限是否申請(qǐng)——已申請(qǐng)
- 檢查是否有其他程序占用麥克風(fēng)——無
- 重啟手機(jī)——重啟之后還是不行
最后發(fā)現(xiàn)應(yīng)用安裝在手機(jī)上之后手機(jī)沒有給應(yīng)用權(quán)限
小技巧
快捷鍵
Ctrl+F3 跳轉(zhuǎn)到下一個(gè)相同變量——向下搜索
Shift+F3 跳轉(zhuǎn)到上一個(gè)相同變量——向上搜索
Ctrl+F12 查看類中所有的參數(shù)和方法
每次設(shè)置參數(shù)或者給Edittext內(nèi)容之前都應(yīng)該先清空內(nèi)容
第三方引入的庫中對(duì)象的初始化在使用前應(yīng)該首先判斷是否為空,并彈框提示记罚,方便定位問題墅诡。
if( null == mIat )
{ // 創(chuàng)建單例失敗
Toast.makeText(this, "創(chuàng)建對(duì)象失敗,請(qǐng)確認(rèn) libmsc.so 放置正確桐智,且有調(diào)用 createUtility 進(jìn)行初始化" , Toast.LENGTH_SHORT).show();
return;
}