Android 百度语音合成 (含离线、在线、API合成方式,详细步骤+源码)

android:textSize=“12dp” />

<ScrollView

android:layout_width=“match_parent”

android:layout_height=“match_parent”

android:layout_above=“@+id/btn”

<TextView

android:id=“@+id/showText”

android:layout_width=“match_parent”

android:layout_height=“match_parent”

android:layout_margin=“10dp”

android:background=“@android:color/darker_gray”

android:minLines=“3”

android:scrollbars=“vertical” />

下面再来看OnlineActivity的代码

2. 编辑代码

package com.llw.speechsynthesis;

import android.Manifest;

import android.content.pm.PackageManager;

import android.os.Bundle;

import android.os.Handler;

import android.os.Message;

import android.util.Log;

import android.view.View;

import android.widget.Button;

import android.widget.TextView;

import androidx.annotation.NonNull;

import androidx.appcompat.app.AppCompatActivity;

import androidx.core.app.ActivityCompat;

import androidx.core.content.ContextCompat;

import com.baidu.tts.chainofresponsibility.logger.LoggerProxy;

import com.baidu.tts.client.SpeechSynthesizer;

import com.baidu.tts.client.SpeechSynthesizerListener;

import com.baidu.tts.client.TtsMode;

import com.llw.speechsynthesis.control.InitConfig;

import com.llw.speechsynthesis.listener.UiMessageListener;

import com.llw.speechsynthesis.util.Auth;

import com.llw.speechsynthesis.util.AutoCheck;

import com.llw.speechsynthesis.util.FileUtil;

import com.llw.speechsynthesis.util.IOfflineResourceConst;

import java.io.File;

import java.util.ArrayList;

import java.util.HashMap;

import java.util.Map;

/**

  • 除了SDK,该类没有任何依赖,可以直接复制进你的项目

  • 默认TEMP_DIR = “/sdcard/baiduTTS”; // 重要!请手动将assets目录下的3个dat 文件复制到该目录

  • 确保 TEXT_FILENAME 和 MODEL_FILENAME 存在

  • Created by fujiayi on 2017/9/14.

*/

public class OnlineActivity extends AppCompatActivity implements IOfflineResourceConst {

/**

  • 要合成的文本,可以自行改动。

*/

private static final String TEXT = “欢迎使用百度语音合成,请在代码中修改合成文本”;

protected String appId;

protected String appKey;

protected String secretKey;

protected String sn; // 纯离线合成SDK授权码;离在线合成SDK没有此参数

//TtsMode.ONLINE 纯在线

private TtsMode ttsMode = TtsMode.ONLINE;

private boolean isOnlineSDK = TtsMode.ONLINE.equals(DEFAULT_SDK_TTS_MODE);

// ================ 纯离线sdk或者选择TtsMode.ONLINE 以下参数无用;

private static final String TEMP_DIR = “/sdcard/baiduTTS”; // 重要!请手动将assets目录下的3个dat 文件复制到该目录

// 请确保该PATH下有这个文件

private static final String TEXT_FILENAME = TEMP_DIR + “/” + TEXT_MODEL;

// 请确保该PATH下有这个文件 ,m15是离线男声

private static final String MODEL_FILENAME = TEMP_DIR + “/” + VOICE_MALE_MODEL;

// ===============初始化参数设置完毕,更多合成参数请至getParams()方法中设置 =================

protected SpeechSynthesizer mSpeechSynthesizer;

// =========== 以下为UI部分 ==================================================

private TextView mShowText;

protected Handler mainHandler;

private String desc; // 说明文件

private static final String TAG = “MiniActivity”;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

appId = Auth.getInstance(this).getAppId();

appKey = Auth.getInstance(this).getAppKey();

secretKey = Auth.getInstance(this).getSecretKey();

sn = Auth.getInstance(this).getSn(); // 纯离线合成必须有此参数;离在线合成SDK没有此参数

desc = FileUtil.getResourceText(this, R.raw.mini_activity_description);

setContentView(R.layout.activity_online);

initView();

initPermission();

initTTs();

}

/**

  • 注意此处为了说明流程,故意在UI线程中调用。

  • 实际集成中,该方法一定在新线程中调用,并且该线程不能结束。具体可以参考NonBlockSyntherizer的写法

*/

private void initTTs() {

LoggerProxy.printable(true); // 日志打印在logcat中

boolean isSuccess;

if (!isOnlineSDK) {

// 检查2个离线资源是否可读

isSuccess = checkOfflineResources();

if (!isSuccess) {

return;

} else {

print(“离线资源存在并且可读, 目录:” + TEMP_DIR);

}

}

// 日志更新在UI中,可以换成MessageListener,在logcat中查看日志

SpeechSynthesizerListener listener = new UiMessageListener(mainHandler);

// 1. 获取实例

mSpeechSynthesizer = SpeechSynthesizer.getInstance();

mSpeechSynthesizer.setContext(this);

// 2. 设置listener

mSpeechSynthesizer.setSpeechSynthesizerListener(listener);

// 3. 设置appId,appKey.secretKey

int result = mSpeechSynthesizer.setAppId(appId);

checkResult(result, “setAppId”);

result = mSpeechSynthesizer.setApiKey(appKey, secretKey);

checkResult(result, “setApiKey”);

// 4. 如果是纯离线SDK需要离线功能的话

if (!isOnlineSDK) {

// 文本模型文件路径 (离线引擎使用), 注意TEXT_FILENAME必须存在并且可读

mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_TTS_TEXT_MODEL_FILE, TEXT_FILENAME);

// 声学模型文件路径 (离线引擎使用), 注意TEXT_FILENAME必须存在并且可读

mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_TTS_SPEECH_MODEL_FILE, MODEL_FILENAME);

mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_MIX_MODE, SpeechSynthesizer.MIX_MODE_DEFAULT);

// 该参数设置为TtsMode.MIX生效。

// MIX_MODE_DEFAULT 默认 ,wifi状态下使用在线,非wifi离线。在线状态下,请求超时6s自动转离线

// MIX_MODE_HIGH_SPEED_SYNTHESIZE_WIFI wifi状态下使用在线,非wifi离线。在线状态下, 请求超时1.2s自动转离线

// MIX_MODE_HIGH_SPEED_NETWORK , 3G 4G wifi状态下使用在线,其它状态离线。在线状态下,请求超时1.2s自动转离线

// MIX_MODE_HIGH_SPEED_SYNTHESIZE, 2G 3G 4G wifi状态下使用在线,其它状态离线。在线状态下,请求超时1.2s自动转离线

}

// 5. 以下setParam 参数选填。不填写则默认值生效

// 设置在线发声音人: 0 普通女声(默认) 1 普通男声 3 情感男声<度逍遥> 4 情感儿童声<度丫丫>

mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEAKER, “0”);

// 设置合成的音量,0-15 ,默认 5

mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_VOLUME, “9”);

// 设置合成的语速,0-15 ,默认 5

mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEED, “5”);

// 设置合成的语调,0-15 ,默认 5

mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_PITCH, “5”);

// mSpeechSynthesizer.setAudioStreamType(AudioManager.MODE_IN_CALL); // 调整音频输出

if (sn != null) {

// 纯离线sdk这个参数必填;离在线sdk没有此参数

mSpeechSynthesizer.setParam(PARAM_SN_NAME, sn);

}

// x. 额外 : 自动so文件是否复制正确及上面设置的参数

Map<String, String> params = new HashMap<>();

// 复制下上面的 mSpeechSynthesizer.setParam参数

// 上线时请删除AutoCheck的调用

if (!isOnlineSDK) {

params.put(SpeechSynthesizer.PARAM_TTS_TEXT_MODEL_FILE, TEXT_FILENAME);

params.put(SpeechSynthesizer.PARAM_TTS_SPEECH_MODEL_FILE, MODEL_FILENAME);

}

// 检测参数,通过一次后可以去除,出问题再打开debug

InitConfig initConfig = new InitConfig(appId, appKey, secretKey, ttsMode, params, listener);

AutoCheck.getInstance(getApplicationContext()).check(initConfig, new Handler() {

@Override

/**

  • 开新线程检查,成功后回调

*/

public void handleMessage(Message msg) {

if (msg.what == 100) {

AutoCheck autoCheck = (AutoCheck) msg.obj;

synchronized (autoCheck) {

String message = autoCheck.obtainDebugMessage();

print(message); // 可以用下面一行替代,在logcat中查看代码

// Log.w(“AutoCheckMessage”, message);

}

}

}

});

// 6. 初始化

result = mSpeechSynthesizer.initTts(ttsMode);

checkResult(result, “initTts”);

}

/**

  • 在线SDK不需要调用,纯离线SDK会检查资源文件

  • 检查 TEXT_FILENAME, MODEL_FILENAME 这2个文件是否存在,不存在请自行从assets目录里手动复制

  • @return 检测是否成功

*/

private boolean checkOfflineResources() {

String[] filenames = {TEXT_FILENAME, MODEL_FILENAME};

for (String path : filenames) {

File f = new File(path);

if (!f.canRead()) {

print(“[ERROR] 文件不存在或者不可读取,请从demo的assets目录复制同名文件到:”

  • f.getAbsolutePath());

print(“[ERROR] 初始化失败!!!”);

return false;

}

}

return true;

}

private void speak() {

/* 以下参数每次合成时都可以修改

  • mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEAKER, “0”);

  • 设置在线发声音人: 0 普通女声(默认) 1 普通男声 3 情感男声<度逍遥> 4 情感儿童声<度丫丫>

  • mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_VOLUME, “5”); 设置合成的音量,0-15 ,默认 5

  • mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEED, “5”); 设置合成的语速,0-15 ,默认 5

  • mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_PITCH, “5”); 设置合成的语调,0-15 ,默认 5

*/

if (mSpeechSynthesizer == null) {

print(“[ERROR], 初始化失败”);

return;

}

int result = mSpeechSynthesizer.speak(TEXT);

mShowText.setText(“”);

print(“合成并播放 按钮已经点击”);

checkResult(result, “speak”);

}

private void stop() {

print(“停止合成引擎 按钮已经点击”);

int result = mSpeechSynthesizer.stop();

checkResult(result, “stop”);

}

// 下面是UI部分

private void initView() {

Button mSpeak = this.findViewById(R.id.speak);

Button mStop = this.findViewById(R.id.stop);

mShowText = this.findViewById(R.id.showText);

mShowText.setText(desc);

View.OnClickListener listener = new View.OnClickListener() {

@Override

public void onClick(View v) {

int id = v.getId();

switch (id) {

case R.id.speak:

speak();

break;

case R.id.stop:

stop();

break;

default:

break;

}

}

};

mSpeak.setOnClickListener(listener);

mStop.setOnClickListener(listener);

mainHandler = new Handler() {

/*

  • @param msg

*/

@Override

public void handleMessage(Message msg) {

super.handleMessage(msg);

if (msg.obj != null) {

print(msg.obj.toString());

}

}

};

}

private void print(String message) {

Log.i(TAG, message);

mShowText.append(message + “\n”);

}

@Override

protected void onDestroy() {

if (mSpeechSynthesizer != null) {

mSpeechSynthesizer.stop();

mSpeechSynthesizer.release();

mSpeechSynthesizer = null;

print(“释放资源成功”);

}

super.onDestroy();

}

private void checkResult(int result, String method) {

if (result != 0) {

print(“error code :” + result + " method:" + method);

}

}

// 下面是android 6.0以上的动态授权

/**

  • android 6.0 以上需要动态申请权限

*/

private void initPermission() {

String[] permissions = {

Manifest.permission.INTERNET,

Manifest.permission.ACCESS_NETWORK_STATE,

Manifest.permission.MODIFY_AUDIO_SETTINGS,

Manifest.permission.WRITE_SETTINGS,

Manifest.permission.ACCESS_WIFI_STATE,

Manifest.permission.CHANGE_WIFI_STATE,

Manifest.permission.WRITE_EXTERNAL_STORAGE

};

ArrayList toApplyList = new ArrayList<>();

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, @NonNull String[] permissions, @NonNull int[] grantResults) {

// 此处为android 6.0以上动态授权的回调,用户自行实现。

}

}

这里的代码其实都是这个SDK中的,直接就可以使用了。我只改动了一点点。

3. 配置

然后修改AndroidManifest.xml

在这里插入图片描述

然后在activity_main.xml中增加一个按钮。

<Button

android:text=“在线SDK合成”

android:onClick=“onlineSDK”

android:layout_width=“match_parent”

android:layout_height=“wrap_content”/>

在MainActivity中增加方法。

/**

  • 在线SDK合成

  • @param view

*/

public void onlineSDK(View view) {

startActivity(new Intent(this, OnlineActivity.class));

}

4. 运行

下面运行:

在这里插入图片描述

可以看到在线SDK合成,没有网络时是合成不了的,有网络才行,这里的声音是女声。

四、在线语音合成 - API方式


使用API方式就稍稍有一些麻烦,因为这个设计到网络的请求,而且不是一次请求,首先进行鉴权,拿到token,然后通过Token去请求合成,下载MP3文件,首先要构建网络模块,当然我也只是简单的写一下而已。

1. 鉴权返回实体

在com.llw.imagediscerndemo下新建一个model包,包下新建一个GetTokenResponse类,代码如下:

package com.llw.speechsynthesis.model;

/**

  • 获取鉴权认证Token响应实体

  • @author llw

  • @date 2021/5/7 16:16

*/

public class GetTokenResponse {

/**

  • refresh_token : 25.0141c302b0f460cd0500827fa31f22ce.315360000.1935736936.282335-24113250

  • expires_in : 2592000

  • session_key : 9mzdCS6a/7/wIFWLR8zFoYs2koSri++RGhSecVXM/vY93At4kxYRajL/xMV17MoxcTAJfadRVaSBxokIqFeQoxsZ8e3NPQ==

  • access_token : 24.2830c05696b214cf07bfbdf764599b39.2592000.1622968936.282335-24113250

  • scope : audio_voice_assistant_get brain_enhanced_asr audio_tts_post brain_speech_realtime public brain_all_scope picchain_test_picchain_api_scope brain_asr_async wise_adapt lebo_resource_base lightservice_public hetu_basic lightcms_map_poi kaidian_kaidian ApsMisTest_Test权限 vis-classify_flower lpq_开放 cop_helloScope ApsMis_fangdi_permission smartapp_snsapi_base smartapp_mapp_dev_manage iop_autocar oauth_tp_app smartapp_smart_game_openapi oauth_sessionkey smartapp_swanid_verify smartapp_opensource_openapi smartapp_opensource_recapi fake_face_detect_开放Scope vis-ocr_虚拟人物助理 idl-video_虚拟人物助理 smartapp_component smartapp_search_plugin avatar_video_test

  • session_secret : 2cdde5fd8f3fd4394c1b090e2ffa2d1c

*/

private String refresh_token;

private int expires_in;

private String session_key;

private String access_token;

private String scope;

private String session_secret;

public String getRefresh_token() {

return refresh_token;

}

public void setRefresh_token(String refresh_token) {

this.refresh_token = refresh_token;

}

public int getExpires_in() {

return expires_in;

}

public void setExpires_in(int expires_in) {

this.expires_in = expires_in;

}

public String getSession_key() {

return session_key;

}

public void setSession_key(String session_key) {

this.session_key = session_key;

}

public String getAccess_token() {

return access_token;

}

public void setAccess_token(String access_token) {

this.access_token = access_token;

}

public String getScope() {

return scope;

}

public void setScope(String scope) {

this.scope = scope;

}

public String getSession_secret() {

return session_secret;

}

public void setSession_secret(String session_secret) {

this.session_secret = session_secret;

}

}

下面简单的写一个网络请求框架。

2. 添加框架依赖

打开你的app的build.gradle,在dependencise{}闭包下添加如下依赖:

//retrofit2

implementation ‘com.squareup.retrofit2:retrofit:2.4.0’

implementation ‘com.squareup.retrofit2:converter-gson:2.4.0’

implementation ‘com.squareup.okhttp3:logging-interceptor:3.4.1’

//权限请求框架

implementation ‘com.tbruyelle.rxpermissions2:rxpermissions:0.9.4@aar’

implementation ‘io.reactivex.rxjava2:rxandroid:2.0.2’

implementation “io.reactivex.rxjava2:rxjava:2.0.0”

然后在android{}闭包下添加JDK1.8的支持

compileOptions {

sourceCompatibility = 1.8

targetCompatibility = 1.8

}

在这里插入图片描述

记得要Sync Now,这里的依赖一个是网络,一个是权限请求,后面都会用到的。

3. 搭建网络请求框架

在com.llw.speechsynthesis下新建一个network包,在这个包下新建一个NetCallBack抽象类。里面的代码如下:

package com.llw.speechsynthesis.network;

import android.util.Log;

import retrofit2.Call;

import retrofit2.Callback;

import retrofit2.Response;

/**

  • 网络请求回调

  • @param

*/

public abstract class NetCallBack implements Callback {//这里实现了retrofit2.Callback

//访问成功回调

@Override

public void onResponse(Call call, Response response) {//数据返回

if (response != null && response.body() != null && response.isSuccessful()) {

onSuccess(call, response);

} else {

onFailed(response.raw().toString());

}

}

//访问失败回调

@Override

public void onFailure(Call call, Throwable t) {

Log.d(“data str”, t.toString());

onFailed(t.toString());

}

//数据返回

public abstract void onSuccess(Call call, Response response);

//失败异常

public abstract void onFailed(String errorStr);

}

然后在network包下新增一个ServiceGenerator类,里面的代码如下:

package com.llw.speechsynthesis.network;

import java.util.concurrent.TimeUnit;

import okhttp3.OkHttpClient;

import okhttp3.logging.HttpLoggingInterceptor;

import retrofit2.Retrofit;

import retrofit2.converter.gson.GsonConverterFactory;

/**

  • 接口地址管理

  • @author llw

*/

public class ServiceGenerator {

public static String BASE_URL = null;

public static String getBaseUrl(int type) {

switch (type) {

case 0://鉴权地址

BASE_URL = “https://openapi.baidu.com”;

break;

case 1://合成地址

BASE_URL = “https://tsn.baidu.com”;

break;

default:

break;

}

return BASE_URL;

}

/**

  • 创建服务 参数就是API服务

  • @param serviceClass 服务接口

  • @param 泛型规范

  • @return api接口服务

*/

public static T createService(Class serviceClass, int type) {

//创建OkHttpClient构建器对象

OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();

//设置请求超时的时间,这里是10秒

okHttpClientBuilder.connectTimeout(20000, TimeUnit.MILLISECONDS);

//消息拦截器 因为有时候接口不同在排错的时候 需要先从接口的响应中做分析。利用了消息拦截器可以清楚的看到接口返回的所有内容

HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();

//setlevel用来设置日志打印的级别,共包括了四个级别:NONE,BASIC,HEADER,BODY

//BASEIC:请求/响应行

//HEADER:请求/响应行 + 头

//BODY:请求/响应航 + 头 + 体

httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

//为OkHttp添加消息拦截器

okHttpClientBuilder.addInterceptor(httpLoggingInterceptor);

//在Retrofit中设置httpclient

//设置地址 就是上面的固定地址,如果你是本地访问的话,可以拼接上端口号 例如 +“:8080”

Retrofit retrofit = new Retrofit.Builder().baseUrl(getBaseUrl(type))

//用Gson把服务端返回的json数据解析成实体

.addConverterFactory(GsonConverterFactory.create())

//放入OKHttp,之前说过retrofit是对OkHttp的进一步封装

.client(okHttpClientBuilder.build())

.build();

//返回这个创建好的API服务

return retrofit.create(serviceClass);

}

}

下面写接口,在network包下新增ApiService接口,代码如下:

package com.llw.speechsynthesis.network;

import com.llw.speechsynthesis.model.GetTokenResponse;

import okhttp3.ResponseBody;

import retrofit2.Call;

import retrofit2.http.Field;

import retrofit2.http.FormUrlEncoded;

import retrofit2.http.POST;

import retrofit2.http.Streaming;

/**

  • API服务

  • @author llw

  • @date 2021/5/8 10:48

*/

public interface ApiService {

/**

  • 获取鉴权认证Token

  • @param grant_type 类型

  • @param client_id API Key

  • @param client_secret Secret Key

  • @return GetTokenResponse

*/

@FormUrlEncoded

@POST(“/oauth/2.0/token”)

Call getToken(@Field(“grant_type”) String grant_type,

@Field(“client_id”) String client_id,

@Field(“client_secret”) String client_secret);

/**

  • 在线API音频合成

  • @param tok 鉴权token

  • @param ctp 客户端类型选择,web端填写固定值1

  • @param cuid 用户唯一标识,用来计算UV值。建议填写能区分用户的机器 MAC 地址或 IMEI 码,长度为60字符以内

  • @param lan 固定值zh。语言选择,目前只有中英文混合模式,填写固定值zh

  • @param tex 合成的文本,使用UTF-8编码。小于2048个中文字或者英文数字,文本在百度服务器内转换为GBK后,长度必须小于4096字节(5003、5118发音人需小于512个中文字或者英文数字)

  • @return 正常合成之后返回一个音频文件

*/

@Streaming

@FormUrlEncoded

@POST(“/text2audio”)

Call synthesis(@Field(“tok”) String tok,

@Field(“ctp”) String ctp,

@Field(“cuid”) String cuid,

@Field(“lan”) String lan,

@Field(“tex”) String tex);

}

里面有两个接口,一个是用来获取鉴权Token的,另一个是用来将文字合成音频文件的。这里会比较的麻烦一些。到此为止这个简单的网络框架就写好了。

4. 编辑布局和页面

在com.llw.speechsynthesis下新建一个OnlineAPIActivity,对应的布局是activity_online_api.xml,里面的代码如下:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”

xmlns:app=“http://schemas.android.com/apk/res-auto”

xmlns:tools=“http://schemas.android.com/tools”

android:layout_width=“match_parent”

android:layout_height=“match_parent”

android:orientation=“vertical”

tools:context=“.OnlineAPIActivity”>

<EditText

android:layout_margin=“12dp”

android:background=“#FFF”

android:padding=“12dp”

android:gravity=“top”

android:textColor=“#000”

android:id=“@+id/et_text”

android:hint=“请输入要合成的文本”

android:layout_width=“match_parent”

android:layout_height=“100dp”/>

<Button

android:id=“@+id/btn_synth_api”

android:text=“在线API合成”

android:layout_width=“match_parent”

android:layout_height=“wrap_content”/>

<Button

android:id=“@+id/btn_play”

android:text=“播放合成的音频”

android:visibility=“gone”

android:layout_width=“match_parent”

android:layout_height=“wrap_content”/>

下面先到AndroidManifest.xml中去配置Title。

在这里插入图片描述

下面回到OnlineAPIActivity看原始的代码是什么样子。

package com.llw.speechsynthesis;

import android.os.Bundle;

import androidx.appcompat.app.AppCompatActivity;

/**

  • 在线API合成

  • @author llw

*/

public class OnlineAPIActivity extends AppCompatActivity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_online_api);

}

}

先来完成页面的初始化。现在布局的控件有三个

声明变量

private static final String TAG = “OnlineAPIActivity”;

/**

  • 输入框

*/

private EditText etText;

/**

  • 页面按钮

*/

private Button btnSynthApi, btnPlay;

写一个初始化页面的方法

/**

  • 初始化

*/

private void initView() {

etText = findViewById(R.id.et_text);

btnSynthApi = findViewById(R.id.btn_synth_api);

btnPlay = findViewById(R.id.btn_play);

btnSynthApi.setOnClickListener(this);

btnPlay.setOnClickListener(this);

}

这里我给两个按钮添加了点击的监听,那么你需要给Activity实现控件的点击监听。

在这里插入图片描述

然后重写onClick方法

@Override

public void onClick(View v) {

switch (v.getId()) {

case R.id.btn_synth_api://在线API合成

break;

case R.id.btn_play://播放音频

break;

default:

break;

}

}

然后要在onCreate方法中调用initView()方法。

在这里插入图片描述

5. 获取鉴权Token

声明变量

/**

  • Api服务

*/

private ApiService service;

/**

  • 鉴权Toeken

*/

private String accessToken;

然后新增一个requestApiGetToken方法,代码如下:

/**

  • 访问API获取接口

*/

private void requestApiGetToken() {

String grantType = “client_credentials”;

String apiKey = “sKWlGNoBrNyaKaAycoiKFzdT”;

String apiSecret = “OwEPWPiSnMNxCF5GFZlORKzP01KwgC1Z”;

service = ServiceGenerator.createService(ApiService.class, 0);

service.getToken(grantType, apiKey, apiSecret)

.enqueue(new NetCallBack() {

@Override

public void onSuccess(Call call, Response response) {

if (response.body() != null) {

//鉴权Token

accessToken = response.body().getAccess_token();

Log.d(TAG, accessToken);

}

}

@Override

public void onFailed(String errorStr) {

Log.e(TAG, “获取Token失败,失败原因:” + errorStr);

accessToken = null;

}

});

}

这里的apiKey、apiSecret 的值改成自己平台创建应用时产生,你要是用我的,除了问题又问我为什么,我就只能。。。了。当然也要在onCreate中调用,这样我们已经入页面就会请求接口拿到鉴权Token。

在这里插入图片描述

下面我们运行一下,不过要先在MainActivity中写一个入口才行,在activity_main.xml中增加一个按钮。

<Button

android:text=“在线API合成”

android:onClick=“onlineAPI”

android:layout_width=“match_parent”

android:layout_height=“wrap_content”/>

然后在MainActivity中增加方法

/**

  • 在线API合成

  • @param view

*/

public void onlineAPI(View view) {

startActivity(new Intent(this,OnlineAPIActivity.class));

}

那么现在你就可以运行了。

在这里插入图片描述

看起来好像什么都没有做是吧。你过你看看控制台的打印。

在这里插入图片描述

这里的鉴权Token就拿到了,这种方式用户就是无感知的。其实这个鉴权Token还有优化的空间,至于怎么做,我在其他的文章写过了,你也可以自己实践。

6. 动态权限请求

因为接口请求之后会下载一个文件到手机本地,因此你需要文件读写权限、

声明变量

/**

  • 权限请求框架

*/

private RxPermissions rxPermissions;

然后在initView中实例化。

在这里插入图片描述

然后新怎一个方法

/**

  • android 6.0 以上需要动态申请权限

*/

@SuppressLint(“CheckResult”)

private void requestPermission() {

rxPermissions.request(Manifest.permission.WRITE_EXTERNAL_STORAGE,

Manifest.permission.READ_EXTERNAL_STORAGE)

.subscribe(grant -> {

if (grant) {

//获得权限

} else {

Toast.makeText(OnlineAPIActivity.this,“未获取到权限”,Toast.LENGTH_SHORT).show();

}

});

}

这里也是很简单的代码,当点击在线合成API按钮时,先调用requestPermission方法进行权限的检查。

在这里插入图片描述

7. Api语音合成

这里合成是读取页面中的文本,如果输入框的内容为空则使用默认文字进行语音合成,因此需要一个默认的文本。

声明变量

/**

  • 默认文本,当输入框未输入使用,

*/

private String defaultText = “你好!百度。”;

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

总结

最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上相关的我搜集整理的Android开发中高级必知必会核心笔记,共计2968页PDF、58w字,囊括Android开发648个知识点,我把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包知识脉络 + 诸多细节。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

2021年虽然路途坎坷,都在说Android要没落,但是,不要慌,做自己的计划,学自己的习,竞争无处不在,每个行业都是如此。相信自己,没有做不到的,只有想不到的。

虽然面试失败了,但我也不会放弃入职字节跳动的决心的!建议大家面试之前都要有充分的准备,顺顺利利的拿到自己心仪的offer。

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!

ission.WRITE_EXTERNAL_STORAGE,

Manifest.permission.READ_EXTERNAL_STORAGE)

.subscribe(grant -> {

if (grant) {

//获得权限

} else {

Toast.makeText(OnlineAPIActivity.this,“未获取到权限”,Toast.LENGTH_SHORT).show();

}

});

}

这里也是很简单的代码,当点击在线合成API按钮时,先调用requestPermission方法进行权限的检查。

在这里插入图片描述

7. Api语音合成

这里合成是读取页面中的文本,如果输入框的内容为空则使用默认文字进行语音合成,因此需要一个默认的文本。

声明变量

/**

  • 默认文本,当输入框未输入使用,

*/

private String defaultText = “你好!百度。”;

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-Q1geXShR-1712162221815)]

[外链图片转存中…(img-q8SX8KFR-1712162221816)]

[外链图片转存中…(img-WafFDCoM-1712162221816)]

[外链图片转存中…(img-x8vLKbqV-1712162221816)]

[外链图片转存中…(img-SG05puX6-1712162221817)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

总结

最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上相关的我搜集整理的Android开发中高级必知必会核心笔记,共计2968页PDF、58w字,囊括Android开发648个知识点,我把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包知识脉络 + 诸多细节。

[外链图片转存中…(img-Vde49Lws-1712162221817)]

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

2021年虽然路途坎坷,都在说Android要没落,但是,不要慌,做自己的计划,学自己的习,竞争无处不在,每个行业都是如此。相信自己,没有做不到的,只有想不到的。

虽然面试失败了,但我也不会放弃入职字节跳动的决心的!建议大家面试之前都要有充分的准备,顺顺利利的拿到自己心仪的offer。

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
  • 18
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值