Android Studio集成讯飞语音导出可供Unity使用的jar/aar

Android Studio集成讯飞语音导出可供Unity使用的jar/aar

因工作需要,把讯飞语音听写集成到unity,因为没有直接给unity调用的讯飞语音SDK,就想着通过android studio打包jar给unity调用

也是查了不少资料和demo,否则也无法完成需求,所以要感谢下其它的作者,但是因为过了一段时间才写的,所以参考了那个房个文章也不太记得了。如果有些看到部分相同资料的其它来源,请告诉我,我把出处加进来。

过了一遍就清晰很多,最后还加了些要注意的地方。

原码:https://github.com/yaopin002/ASXunFeiToUnity#as-unity

运行环境

Unity 2017.1.03,

Android Studio 2.3.3

Win 7

一、 创建Android Studio工程

1)第一步,点击File->New->New Project,打开"Create New Project"对话框,选择合适的Application name与Company Domain,保证Package name与Unity项目中的Bundle Idenifier一致

2)第二步,选择Phone and Tablet,并选择合适的Minimum SDK(也可以在创建后的build.gradle中设置)

3)第三步,选择“Empty Activity”

4)第四步,保持默认的Activity Name与Layout Name即可

5)最后,点击"Finish"创建工程

**以上是直接使用新创建的空activity,然后就需要下面第四个步骤--修改gradle把这个Application改成一个library

**另一个办法是直接创建一个Android Library,就不需要修改gralde了,但是空的library是不带空的activity的,所以需要手动创建activity

二、添加Unity的classes.jar引用和讯飞SDK及.so文件

1)把Unity引擎目录下中的”Editor\Data\PlaybackEngines\AndroidPlayer\Variations\mono\Release\Classes\classes.jar“文件拷贝至Android Studio工程中的libs目录

2)把讯飞语音MSC.jar也拷贝至Android Studio工程中的libs目录

3)把以上jar报进行添加依赖动作

4)把讯飞语音armeabi和armeabi-v7a的so文件拷贝到main目录下的jniLibs文件夹下(没有就新建jniLibs)

5)在gradle的android目录下添加一段代码指定库文件(这个动作运行AS时需要,如果只是做Jar/aar包就可以不用),代码如下:

    sourceSets.main{

        jniLibs.srcDir 'src/main/jniLibs'

    }

三、编写Android侧代码(修改MainActivity代码)

public SpeechRecognizer speechRecognizer;

public SpeechSynthesizer speechSynthesizer;

private String ttsSpeakerName = "yefang";

private String ttsSpeakerPitch = "50";

MainActivity

// 写在onCreate这里面的代码,在unity初始化时一样会自动执行

//注意这里的appid为

SpeechUtility.createUtility(getApplicationContext(),"appid=58880d30");

initRecognizer();

}

//初始化

private void initRecognizer(){

//1.创建SpeechRecognizer对象,第二个参数:本地听写时传InitListener

speechRecognizer = SpeechRecognizer.createRecognizer(getApplicationContext(),mInitListener);

speechSynthesizer = SpeechSynthesizer.createSynthesizer(getApplicationContext(),mInitListener);

}

//初始化SpeechRecognizer对象的监听器

public InitListener mInitListener = new InitListener() {

@Override

public void onInit(int i) {

UnityPlayer.UnitySendMessage("Manager", "Result", "init success!");

}

};

public void setTTSSpeaker(String targetName) {

ttsSpeakerName = targetName;

}

public void setTTSPitch(String targetPitch) {

ttsSpeakerPitch = targetPitch;

}

public void doTTS(String ttsStr){

UnityPlayer.UnitySendMessage("MotionManager", "IsSpeaking", "true");

//设置发音人

speechSynthesizer.setParameter(SpeechConstant.VOICE_NAME,ttsSpeakerName);

//合成语调 通过此参数,设置合成返回音频的语调。

speechSynthesizer.setParameter(SpeechConstant.PITCH,ttsSpeakerPitch);

//设置音量

speechSynthesizer.setParameter(SpeechConstant.VOLUME,"50");

int code = speechSynthesizer.startSpeaking(ttsStr, mTTSListener);

}

private SynthesizerListener mTTSListener = new SynthesizerListener() {

@Override

public void onSpeakBegin() {

}

@Override

public void onBufferProgress(int i, int i1, int i2, String s) {

}

@Override

public void onSpeakPaused() {

}

@Override

public void onSpeakResumed() {

}

@Override

public void onSpeakProgress(int i, int i1, int i2) {

}

@Override

public void onCompleted(SpeechError speechError) {

UnityPlayer.UnitySendMessage("MotionManager", "IsSpeaking", "false");

}

@Override

public void onEvent(int i, int i1, int i2, Bundle bundle) {

}

};

//开始听写

public void startSpeechListener(){

UnityPlayer.UnitySendMessage("Manager", "Result", "");

speechRecognizer.setParameter(SpeechConstant.DOMAIN, "iat");

speechRecognizer.setParameter(SpeechConstant.LANGUAGE, "zh_cn");

speechRecognizer.setParameter(SpeechConstant.ACCENT, "mandarin");

speechRecognizer.startListening(mRecognizerListener);

}

public RecognizerListener mRecognizerListener = new RecognizerListener(){

@Override

public void onBeginOfSpeech() {

// TODO Auto-generated method stub

//UnityPlayer.UnitySendMessage("Manager", "Result", "onBeginOfSpeech");

}

@Override

public void onEndOfSpeech() {

// TODO Auto-generated method stub

//UnityPlayer.UnitySendMessage("Manager", "Result", "onEndOfSpeech");

//startSpeechListener();

UnityPlayer.UnitySendMessage("Manager", "SpeechEnd","");

}

@Override

public void onError(SpeechError arg0) {

// TODO Auto-generated method stub

//UnityPlayer.UnitySendMessage("Manager", "Result", "onError");

}

@Override

public void onEvent(int arg0, int arg1, int arg2, Bundle arg3) {

// TODO Auto-generated method stub

//UnityPlayer.UnitySendMessage("Manager", "Result", "onEvent");

}

@Override

public void onResult(RecognizerResult recognizerResult, boolean isLast) {

//UnityPlayer.UnitySendMessage("Manager", "Result", "listener");

printResult(recognizerResult);

//if(isLast)

//startSpeechListener();

}

@Override

public void onVolumeChanged(int arg0, byte[] arg1) {

//UnityPlayer.UnitySendMessage("Manager", "Result", "onVolumeChanged");

// TODO Auto-generated method stub

}

};

//解析

private void printResult(RecognizerResult results) {

String json = results.getResultString();

StringBuffer ret = new StringBuffer();

try {

JSONTokener tokener = new JSONTokener(json);

JSONObject joResult = new JSONObject(tokener);

JSONArray words = joResult.getJSONArray("ws");

for (int i = 0; i < words.length(); i++) {

// 转写结果词,默认使用第一个结果

JSONArray items = words.getJSONObject(i).getJSONArray("cw");

JSONObject obj = items.getJSONObject(0);

ret.append(obj.getString("w"));

}

} catch (Exception e) {

e.printStackTrace();

}

//将解析结果“"result:" + ret.toString()”发送至“Manager”这个GameObject,中的“Result”函数

UnityPlayer.UnitySendMessage("Manager", "Result", ret.toString());

}

public void ShowToast(final String mStr2Show){

UnityPlayer.UnitySendMessage("Manager", "Result", "toast");

runOnUiThread(new Runnable() {

@Override

public void run() {

Toast.makeText(getApplicationContext(),mStr2Show,Toast.LENGTH_LONG).show();

}

});

}

四、修改build.gradle,设置工程导出为aar

1)apply plugin: 'com.android.application'  修改为    apply plugin: 'com.android.library'

2)删除 applicationId "com.zcode.unityandroidplugindemo"

3)修改后的build.gradle为

apply plugin: 'com.android.library'

android {

compileSdkVersion 24

buildToolsVersion "24.0.1"

defaultConfig {

minSdkVersion 18

targetSdkVersion 24

versionCode 1

versionName "1.0"

testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

}

buildTypes {

release {

minifyEnabled false

proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

}

}

}

dependencies {

compile fileTree(include: ['*.jar'], dir: 'libs')

androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {

exclude group: 'com.android.support', module: 'support-annotations'

})

compile 'com.android.support:appcompat-v7:24.1.1'

testCompile 'junit:junit:4.12'

compile files('libs/classes.jar')

}

五、修改AndroidManifest.xml

1)修改样式

我们需要在 AndroidManifest 中的 application 结点修改应用的主样式为系统样式,因为导出的 AAR 文件将不带自定义的样式,在我们的 Unity 项目中生成最终 apk 的时候会出现样式找不到的错误。

android:theme="@android:style/Theme.NoTitleBar"

同时需要删除res\Values目录下的styles.xml文件

备注:如果是直接创建的Android Library就不需要这一步。

2)在主 activity 结点下添加信息,否则在 Unity 导出 APK 时会报找不到manifest 文件的错误信息

备注:如果是直接创建的Android Library就需要添加activity节点,包括里面的intent-filter、action、category(但是上面那行meta-data可以不要)

六、导出供Unity使用的*.aar/jar文件

1)点击右上角的gradle projects找到对应module的Task里的other下的makeJar,双击运行,等一会儿后就完成


1

备注:要在gradle根目录下添加如下两个方法才会在gradle projects出现makeJar(内容需依据需要进行修改):

task makeJar(type: Copy) {

delete 'build/libs/speechrecognizer.jar'

from('build/intermediates/bundles/release/')

into('build/libs/')

include('classes.jar')

rename ('classes.jar', 'speechrecognizer.jar')

}

makeJar.dependsOn(build)

2)也可以通过Terminal执行命令:gradlew makeJar,会使所有Module都执行,如下图:


2

如下目录找到aar(aar文件里最外层就有一个classes.jar)


3

如下目录直接找到jar包


4

3)如果是要调用aar文件,就需使用压缩软件打开aar文件删除libs目录下unity的classes.jar文件(因为会和unity自带的classes.jar冲突),并且要删除其它jar文件和jni目录下的so文件(因为要拷贝到unity指定的目录下,下面步骤会介绍)

如果使用classes.jar就直接拷贝到Unity(拷贝前建议改个名)

Unity导入插件,并调用

一、创建Unity工程

二、导入插件到Unity工程中

1)新建目录:Plugins-Android-bin/libs

2)拷贝aar/jar文件、AndroidManifest.xml文件、so文件至如下目录注意:安卓5.0以上系统需要armeabi-v7a,不然会出现21002的错误


5

三、编写测试代码

1)新建一个cs脚本文件

2)在脚本中编写调用Android侧代码

using UnityEngine;using System.Collections;public class XunFeiTest : MonoBehaviour{ private string showResult = ""; //UnityGUI控制是利用一类被称为OnGUI()的函数,只要在控制脚本激活的状态下 //OnGUI()函数可以在每帧调用,就像Update( )函数一样。 void OnGUI () { //新建一个button,指定文本和高度 if (GUILayout.Button ("startRecognizer", GUILayout.Height (100))) { //这两行代码一般不用改 AndroidJavaClass jc = new AndroidJavaClass ("com.unity3d.player.UnityPlayer"); AndroidJavaObject jo = jc.GetStatic("currentActivity");

//调用方法

jo.Call ("startSpeechListener");

}

//创建一个TextArea,参数一:显示的内容,参数二:指定宽度

GUILayout.TextArea (showResult, GUILayout.Width (200));

}

//这是AS中指定的方法名,AS一旦有发送消息,这里就会传进来结果

public void Result (string recognizerResult)

{

showResult += recognizerResult;  //把结果连接起来

}

}

四、设置并运行APK

1)设置Bundle Identifier(保持与插件的包名+类名一致)

2)设置合适的Minimum API Level(保持与插件)

五、测试

结尾

1.确定所有的aar库中的android:minSdkVerion与android:targetSdkVersion一致

2.android studio包名要和unity包名一致

3.unity的OnGUI()启动方法要大写

4.theme要改为android系统样式android:theme="@android:style/Theme.NoTitleBar",或者不要theme也可以

5.删除res下的style.xml

6.修改build.gradle:一改一删

7.务必真机测试,虚拟机会报错

8.res下activity_main.xml里的这种代码"app:layout_constraintBottom_toBottomOf="parent""要删除,不然会报错

9.AS不需要添加动态权限申请,否则会报activity找不到的错

以上如有错误请高人指正

推荐阅读更多精彩内容