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找不到的错

以上如有错误请高人指正

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 143,736评论 1 303
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 61,631评论 1 258
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 95,145评论 0 213
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 41,213评论 0 181
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 49,025评论 1 259
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 38,893评论 1 178
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 30,497评论 2 273
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,240评论 0 167
  • 想象着我的养父在大火中拼命挣扎,窒息,最后皮肤化为焦炭。我心中就已经是抑制不住地欢快,这就叫做以其人之道,还治其人...
    爱写小说的胖达阅读 29,114评论 6 235
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 32,596评论 0 213
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,363评论 2 215
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 30,717评论 1 232
  • 白月光回国,霸总把我这个替身辞退。还一脸阴沉的警告我。[不要出现在思思面前, 不然我有一百种方法让你生不如死。]我...
    爱写小说的胖达阅读 24,279评论 0 32
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,183评论 2 214
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 31,629评论 3 209
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,647评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,047评论 0 167
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 33,625评论 2 232
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 33,734评论 2 236

推荐阅读更多精彩内容