环境准备
1.如下拷贝(由adndroid的那个demo)

2.bdasr_V3_20190327_58c9395.jar拷贝
拷贝到如下目录:工程目录/app/libs
3.AndroidManifest.xml
3.1权限
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
3.2 服务APP_ID/API_KEY/SECRET_KEY
代码
1 语音识别开始
private Button mstart;
mstart = findViewById(R.id.id_button);
mstart.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
start();
}
});
语音识别开始
private void start() {
txtLog.setText("");
Map<String, Object> params = new LinkedHashMap<String, Object>();
String event = null;
event = SpeechConstant.ASR_START; // 替换成测试的event
if (enableOffline) {
params.put(SpeechConstant.DECODER, 2);
}
// 基于SDK集成2.1 设置识别参数
params.put(SpeechConstant.ACCEPT_AUDIO_VOLUME, false);
// params.put(SpeechConstant.NLU, "enable");
// params.put(SpeechConstant.VAD_ENDPOINT_TIMEOUT, 0); // 长语音
// params.put(SpeechConstant.IN_FILE, "res:///com/baidu/android/voicedemo/16k_test.pcm");
// params.put(SpeechConstant.VAD, SpeechConstant.VAD_DNN);
// params.put(SpeechConstant.PID, 1537); // 中文输入法模型,有逗号
/* 语音自训练平台特有参数 */
// params.put(SpeechConstant.PID, 8002);
// 语音自训练平台特殊pid,8002:搜索模型类似开放平台 1537 具体是8001还是8002,看自训练平台页面上的显示
// params.put(SpeechConstant.LMID,1068); // 语音自训练平台已上线的模型ID,https://ai.baidu.com/smartasr/model
// 注意模型ID必须在你的appId所在的百度账号下
/* 语音自训练平台特有参数 */
// 请先使用如‘在线识别’界面测试和生成识别参数。 params同ActivityRecog类中myRecognizer.start(params);
// 复制此段可以自动检测错误
(new AutoCheck(getApplicationContext(), new Handler() {
public void handleMessage(Message msg) {
if (msg.what == 100) {
AutoCheck autoCheck = (AutoCheck) msg.obj;
synchronized (autoCheck) {
String message = autoCheck.obtainErrorMessage(); // autoCheck.obtainAllMessage();
txtLog.append(message + "\n");
; // 可以用下面一行替代,在logcat中查看代码
// Log.w("AutoCheckMessage", message);
}
}
}
},enableOffline)).checkAsr(params);
String json = null; // 可以替换成自己的json
json = new JSONObject(params).toString(); // 这里可以替换成你需要测试的json
asr.send(event, json, null, 0, 0);
//printLog("输入参数:" + json);
}
响应结果
// 基于sdk集成1.2 自定义输出事件类 EventListener 回调方法
// 基于SDK集成3.1 开始回调事件
@Override
public void onEvent(String name, String params, byte[] data, int offset, int length) {
String logTxt = "name: " + name;
if (params != null && !params.isEmpty()) {
logTxt += " ;params :" + params;
}
if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_PARTIAL)) {
if (params != null && params.contains("\"nlu_result\"")) {
if (length > 0 && data.length > 0) {
logTxt += ", 语义解析结果:" + new String(data, offset, length);
}
}
} else if (data != null) {
logTxt += " ;data length=" + data.length;
}
printLog(logTxt);
}
private void printLog(String text) {
if (logTime) {
text += " ;time=" + System.currentTimeMillis();
}
text += "\n";
Log.i(getClass().getName(), text);
txtLog.append(text + "\n");
}
2.识别后的表示
protected TextView txtLog;
<TextView
android:id="@+id/id_log"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
txtLog = findViewById(R.id.id_log);
public void onEvent(String name, String params, byte[] data, int offset, int length) {
。。。。。
printLog(logTxt);
}
private void printLog(String text) {
if (logTime) {
text += " ;time=" + System.currentTimeMillis();
}
text += "\n";
Log.i(getClass().getName(), text);
txtLog.append(text + "\n");
}
3.权限申请
/**
* android 6.0 以上需要动态申请权限
*/
private void initPermission() {
String permissions[] = {Manifest.permission.RECORD_AUDIO,
Manifest.permission.ACCESS_NETWORK_STATE,
Manifest.permission.INTERNET,
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
ArrayList<String> toApplyList = new ArrayList<String>();
for (String perm : permissions) {
if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(this, perm)) {
toApplyList.add(perm);
// 进入到这里代表没有权限.
}
}
String tmpList[] = new String[toApplyList.size()];
if (!toApplyList.isEmpty()) {
ActivityCompat.requestPermissions(this, toApplyList.toArray(tmpList), 123);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
// 此处为android 6.0以上动态授权的回调,用户自行实现。
}
3.取消语音识别
<Button
android:id="@+id/id_clear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="结束" />
Button mclaer = findViewById(R.id.id_clear);
mclaer.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onStop();
}
});
protected void onStop() {
super.onStop();
this.getDelegate().onStop();
}
4.工具类(直接由百度的deomo拷贝过来即可)
package com.qiming.yuyinshibie;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import com.baidu.speech.asr.SpeechConstant;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeSet;
public class AutoCheck {
public static final boolean isOnlineLited = false; // 是否只需要是纯在线识别功能
private LinkedHashMap<String, Check> checks;
private Context context;
private Handler handler;
private boolean hasError;
private boolean enableOffline;
private boolean isFinished = false;
private String name;
private static final String TAG = "AutoCheck";
public AutoCheck(Context context, final Handler handler, boolean enableOffline) {
this.context = context;
checks = new LinkedHashMap<>();
this.handler = handler;
this.enableOffline = enableOffline;
}
public void checkAsr(final Map<String, Object> params) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
AutoCheck obj = checkAsrInternal(params);
name = "识别";
synchronized (obj) { // 偶发,同步线程信息
isFinished = true;
Message msg = handler.obtainMessage(100, obj);
handler.sendMessage(msg);
}
}
});
t.start();
}
public String obtainErrorMessage() {
PrintConfig config = new PrintConfig();
return formatString(config);
}
public String obtainDebugMessage() {
PrintConfig config = new PrintConfig();
config.withInfo = true;
return formatString(config);
}
public String obtainAllMessage() {
PrintConfig config = new PrintConfig();
config.withLog = true;
config.withInfo = true;
config.withLogOnSuccess = true;
return formatString(config);
}
private String formatString(PrintConfig config) {
StringBuilder sb = new StringBuilder();
hasError = false;
for (HashMap.Entry<String, Check> entry : checks.entrySet()) {
Check check = entry.getValue();
String testName = entry.getKey();
if (check.hasError()) {
if (!hasError) {
hasError = true;
}
sb.append("【错误】【").append(testName).append(" 】 ").append(check.getErrorMessage()).append("\n");
Log.e("AutoCheck", sb.toString());
if (check.hasFix()) {
sb.append("【修复方法】【").append(testName).append(" 】 ").append(check.getFixMessage()).append("\n");
}
} else if (config.withEachCheckInfo) {
sb.append("【无报错】【").append(testName).append(" 】 ").append("\n");
}
if (config.withInfo && check.hasInfo()) {
sb.append("【请手动检查】【").append(testName).append("】 ").append(check.getInfoMessage()).append("\n");
}
if (config.withLog && (config.withLogOnSuccess || hasError) && check.hasLog()) {
sb.append("【log】:" + check.getLogMessage()).append("\n");
}
}
if (!hasError) {
sb.append("【" + name + "】集成自动排查工具: 恭喜没有检测到任何问题\n");
}
return sb.toString();
}
private AutoCheck checkAsrInternal(Map<String, Object> params) {
commonSetting(params);
checks.put("外部音频文件存在校验", new FileCheck(context, params, SpeechConstant.IN_FILE));
checks.put("离线命令词及本地语义bsg文件存在校验",
new FileCheck(context, params, SpeechConstant.ASR_OFFLINE_ENGINE_GRAMMER_FILE_PATH));
for (Map.Entry<String, Check> e : checks.entrySet()) {
Check check = e.getValue();
check.check();
if (check.hasError()) {
break;
}
}
return this;
}
private void commonSetting(Map<String, Object> params) {
checks.put("检查申请的Android权限", new PermissionCheck(context));
checks.put("检查so文件是否存在", new JniCheck(context));
AppInfoCheck infoCheck = null;
try {
infoCheck = new AppInfoCheck(context, params);
checks.put("检查AppId AppKey SecretKey", infoCheck);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
Log.e(TAG, "检查AppId AppKey SecretKey 错误", e);
return;
}
if (enableOffline) {
checks.put("检查包名", new ApplicationIdCheck(context, infoCheck.appId));
}
}
private static class PrintConfig {
public boolean withEachCheckInfo = false;
public boolean withInfo = false;
public boolean withLog = false;
public boolean withLogOnSuccess = false;
}
private static class PermissionCheck extends Check {
private Context context;
public PermissionCheck(Context context) {
this.context = context;
}
@Override
public void check() {
String[] permissions = {
Manifest.permission.RECORD_AUDIO,
Manifest.permission.ACCESS_NETWORK_STATE,
Manifest.permission.INTERNET,
// Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_PHONE_STATE
};
ArrayList<String> toApplyList = new ArrayList<String>();
for (String perm : permissions) {
if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(context, perm)) {
toApplyList.add(perm);
// 进入到这里代表没有权限.
}
}
if (!toApplyList.isEmpty()) {
errorMessage = "缺少权限:" + toApplyList;
fixMessage = "请从AndroidManifest.xml复制相关权限";
}
}
}
private static class JniCheck extends Check {
private Context context;
private String[] soNames;
public JniCheck(Context context) {
this.context = context;
if (isOnlineLited) {
soNames = new String[]{"libBaiduSpeechSDK.so", "libvad.dnn.so"};
} else {
soNames = new String[]{"libBaiduSpeechSDK.so", "libvad.dnn.so",
"libbd_easr_s1_merge_normal_20151216.dat.so", "libbdEASRAndroid.so",
"libbdSpilWakeup.so"};
}
}
@Override
public void check() {
String path = context.getApplicationInfo().nativeLibraryDir;
appendLogMessage("Jni so文件目录 " + path);
File[] files = new File(path).listFiles();
TreeSet<String> set = new TreeSet<>();
if (files != null) {
for (File file : files) {
set.add(file.getName());
}
}
// String debugMessage = "Jni目录内文件: " + set.toString();
// boolean isSuccess = true;
for (String name : soNames) {
if (!set.contains(name)) {
errorMessage = "Jni目录" + path + " 缺少so文件:" + name + ", 该目录文件列表: " + set.toString();
fixMessage = "如果您的app内没有其它so文件,请复制demo里的src/main/jniLibs至同名目录。"
+ " 如果app内有so文件,请合并目录放一起(注意目录取交集,多余的目录删除)。";
break;
}
}
}
}
private static class AppInfoCheck extends Check {
private String appId;
private String appKey;
private String secretKey;
public AppInfoCheck(Context context, Map<String, Object> params) throws PackageManager.NameNotFoundException {
Bundle metaData = context.getPackageManager().getApplicationInfo(context.getPackageName(),
PackageManager.GET_META_DATA).metaData;
if (params.get(SpeechConstant.APP_ID) != null) {
appId = params.get(SpeechConstant.APP_ID).toString();
} else {
int id = metaData.getInt("com.baidu.speech.APP_ID", 0);
if (id > 0) {
appId = "" + id;
}
}
if (params.get(SpeechConstant.APP_KEY) != null) {
appKey = params.get(SpeechConstant.APP_KEY).toString();
} else {
appKey = metaData.getString("com.baidu.speech.API_KEY", "");
}
if (params.get(SpeechConstant.SECRET) != null) {
secretKey = params.get(SpeechConstant.SECRET).toString();
} else {
secretKey = metaData.getString("com.baidu.speech.SECRET_KEY", "");
}
}
public void check() {
do {
appendLogMessage("try to check appId " + appId + " ,appKey=" + appKey + " ,secretKey" + secretKey);
if (appId == null || appId.isEmpty()) {
errorMessage = "appId 为空";
fixMessage = "填写appID";
break;
}
if (appKey == null || appKey.isEmpty()) {
errorMessage = "appKey 为空";
fixMessage = "填写appID";
break;
}
if (secretKey == null || secretKey.isEmpty()) {
errorMessage = "secretKey 为空";
fixMessage = "secretKey";
break;
}
try {
checkOnline();
} catch (UnknownHostException e) {
infoMessage = "无网络或者网络不连通,忽略检测 : " + e.getMessage();
} catch (Exception e) {
errorMessage = e.getClass().getCanonicalName() + ":" + e.getMessage();
fixMessage = " 重新检测appId, appKey, appSecret是否正确";
}
} while (false);
}
public void checkOnline() throws Exception {
String urlpath = "http://openapi.baidu.com/oauth/2.0/token?client_id="
+ appKey + "&client_secret=" + secretKey + "&grant_type=client_credentials";
Log.i("AutoCheck", "Url is " + urlpath);
URL url = new URL(urlpath);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(1000);
InputStream is = conn.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder result = new StringBuilder();
String line = "";
do {
line = reader.readLine();
if (line != null) {
result.append(line);
}
} while (line != null);
String res = result.toString();
if (!res.contains("audio_voice_assistant_get")) {
errorMessage = "appid:" + appId + ",没有audio_voice_assistant_get 权限,请在网页上开通\"语音识别\"能力";
fixMessage = "secretKey";
return;
}
appendLogMessage("openapi return " + res);
JSONObject jsonObject = new JSONObject(res);
String error = jsonObject.optString("error");
if (error != null && !error.isEmpty()) {
errorMessage = "appkey secretKey 错误" + ", error:" + error + ", json is" + result;
fixMessage = " 重新检测appId对应的 appKey, appSecret是否正确";
return;
}
String token = jsonObject.getString("access_token");
if (token == null || !token.endsWith("-" + appId)) {
errorMessage = "appId 与 appkey及 appSecret 不一致。appId = " + appId + " ,token = " + token;
fixMessage = " 重新检测appId对应的 appKey, appSecret是否正确";
}
}
}
private static class ApplicationIdCheck extends Check {
private String appId;
private Context context;
public ApplicationIdCheck(Context context, String appId) {
this.appId = appId;
this.context = context;
}
@Override
public void check() {
infoMessage = "如果您集成过程中遇见离线命令词或者唤醒初始化问题,请检查网页上appId:" + appId
+ " 应用填写了Android包名:"
+ getApplicationId();
}
private String getApplicationId() {
return context.getPackageName();
}
}
private static class FileCheck extends Check {
private Map<String, Object> params;
private String key;
private Context context;
private boolean allowRes = false;
private boolean allowAssets = true;
public FileCheck(Context context, Map<String, Object> params, String key) {
this.context = context;
this.params = params;
this.key = key;
if (key.equals(SpeechConstant.IN_FILE)) {
allowRes = true;
allowAssets = false;
}
}
@Override
public void check() {
if (!params.containsKey(key)) {
return;
}
String value = params.get(key).toString();
if (allowAssets) {
int len = "assets".length();
int totalLen = len + ":///".length();
if (value.startsWith("assets")) {
String filename = value.substring(totalLen);
if (!":///".equals(value.substring(len, totalLen)) || filename.isEmpty()) {
errorMessage = "参数:" + key + "格式错误:" + value;
fixMessage = "修改成" + "assets:///sdcard/xxxx.yyy";
}
try {
context.getAssets().open(filename);
} catch (IOException e) {
errorMessage = "assets 目录下,文件不存在:" + filename;
fixMessage = "demo的assets目录是:src/main/assets";
e.printStackTrace();
}
appendLogMessage("assets 检验完毕:" + filename);
}
}
if (allowRes) {
int len = "res".length();
int totalLen = len + ":///".length();
if (value.startsWith("res")) {
String filename = value.substring(totalLen);
if (!":///".equals(value.substring(len, totalLen)) || filename.isEmpty()) {
errorMessage = "参数:" + key + "格式错误:" + value;
fixMessage = "修改成" + "res:///com/baidu/android/voicedemo/16k_test.pcm";
}
InputStream is = getClass().getClassLoader().getResourceAsStream(filename);
if (is == null) {
errorMessage = "res,文件不存在:" + filename;
fixMessage = "demo的res目录是:app/src/main/resources";
} else {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
appendLogMessage("res 检验完毕:" + filename);
}
}
if (value.startsWith("/")) {
if (!new File(value).canRead()) {
errorMessage = "文件不存在:" + value;
fixMessage = "请查看文件是否存在";
}
appendLogMessage("文件路径 检验完毕:" + value);
}
}
}
private abstract static class Check {
protected String errorMessage = null;
protected String fixMessage = null;
protected String infoMessage = null;
protected StringBuilder logMessage;
public Check() {
logMessage = new StringBuilder();
}
public abstract void check();
public boolean hasError() {
return errorMessage != null;
}
public boolean hasFix() {
return fixMessage != null;
}
public boolean hasInfo() {
return infoMessage != null;
}
public boolean hasLog() {
return !logMessage.toString().isEmpty();
}
public void appendLogMessage(String message) {
logMessage.append(message + "\n");
}
public String getErrorMessage() {
return errorMessage;
}
public String getFixMessage() {
return fixMessage;
}
public String getInfoMessage() {
return infoMessage;
}
public String getLogMessage() {
return logMessage.toString();
}
}
}
本文详细介绍了在Android应用中集成百度语音识别SDK的过程,包括环境准备、权限申请、代码实现及工具类使用。涵盖关键步骤如SDK配置、事件监听、识别结果处理及常见问题排查。
2963

被折叠的 条评论
为什么被折叠?



