Android原生SpeechRecognizer(语音识别)

2023-05-16

开篇先吐槽下,在Android 平台开发原生的SpeechRecognizer真是难受的,不像ios,无比轻松,平台统一。 由于Android 平台的碎片化问题比较严重,各个厂商都有自己的实现,尤其是语音助手出来以后,每家的语音服务肯定是不一样的。

目前Android原生的SpeechRecognizer做法应该有两种

  1. 默认调用原生SpeechRecognizer,并稍作修改
  2. 调用第三方,科大讯飞,百度等

这两种做法中

  • 1. 在Google原生系统是可以的,但是在国内的环境是需要修改,修改后能保证各个机型基本可以用,至于识别效果就要看各个机型自己实现的怎么样了
  • 2. 最简单省心省力,如果你的项目可以这么做,那么兄弟恭喜你,你是最幸福的

这里我们不讲第三方的,大家可以自己去集成第三方sdk,主要讨论原生的开发

​ 首先权限不要忘记(记得6.0以后动态请求权限)

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
复制代码

​ 在SpeechRecognizer.class有这样SpeechRecognizer .isRecognitionAvailable一个方法

    /**
     * Checks whether a speech recognition service is available on the system. If this method
     * returns {@code false}, {@link SpeechRecognizer#createSpeechRecognizer(Context)} will
     * fail.
     * 
     * @param context with which {@code SpeechRecognizer} will be created
     * @return {@code true} if recognition is available, {@code false} otherwise
     */
    public static boolean isRecognitionAvailable(final Context context) {
        final List<ResolveInfo> list = context.getPackageManager().queryIntentServices(
                new Intent(RecognitionService.SERVICE_INTERFACE), 0);
        return list != null && list.size() != 0;
    }
复制代码

​ 该方法在使用语音识别前建议要调用下,该方法是检查当前系统有没有语音识别服务,我相信绝大多数厂商都有这个服务,但是都有自己特别的实现,但是它至少有,有就可以用。但是,你像oppo的7.0以后机器,这个方法调用后就是false,这时候就是毫无办法了。oppo 7.0以后就是这样调用完后返回false,对于 oppo 这种情况,可以在手机上装一个**讯飞语音+**的app,语音识别就可以了,但是这种方法我估计没人会用,用户体验太差。

​ 如果该方法返回false在我们调用*SpeechRecognizer.startListening();*方法的时候会日志中发现这行log

no selected voice recognition service
复制代码

​ 该日志在SpeechRecognizer.startListening(final Intent recognizerIntent)方法中,大家可以进源码查看这里就不贴了。

​ 检查完如果语音识别可用,接下来有两种做法我们一个个来


  1. 直接创建实例启动服务
mSpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(context);
mSpeechRecognizer.setRecognitionListener(this);
复制代码

​ 创建识别实例,并添加监听,这里没有什么问题,在监听中我们可以拿到我们的想要的回调

/**
 * Used for receiving notifications from the SpeechRecognizer when the
 * recognition related events occur. All the callbacks are executed on the
 * Application main thread.
 * 值的注意的是,所有的回调都在主线程
 */
public interface RecognitionListener {
    
  	// 实例准备就绪 
    void onReadyForSpeech(Bundle params);

    // 开始语音识别
    void onBeginningOfSpeech();

   	// 聆听分贝值 可能会有负数哦
    void onRmsChanged(float rmsdB);

    void onBufferReceived(byte[] buffer);

    // 识别结束
    void onEndOfSpeech();

    // 错误码
    void onError(int error);

    // 识别的结果,在某些国产机上,这个结果会是空
    void onResults(Bundle results);

    // 识别的部分结果 有些过程机上 [onResults] 方法为空,可以在这里拿到结果
    void onPartialResults(Bundle partialResults);

    void onEvent(int eventType, Bundle params);
}

复制代码

​ 指的注意的是,如果 SpeechRecognizer .isRecognitionAvailable 方法返回false的话,即使注册了监听*mSpeechRecognizer.setRecognitionListener(this);*回调方法不会走的,因为没有该服务的

​ 下面就是样板代码了,都一样的

// 启动服务需要一个 Intent
mRecognitionIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
            mRecognitionIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
mRecognitionIntent.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS, true);
mRecognitionIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 3);
// mLocale 是一个语音种类,可以根据自己的需求去设置
mRecognitionIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, mLocale);

// 开始语音识别 结果在 mSpeechRecognizer.setRecognitionListener(this);回调中
mSpeechRecognizer.startListening(mRecognitionIntent);

// 停止监听
mSpeechRecognizer.stopListening();

// 取消服务
mSpeechRecognizer.cancel();
复制代码

​ 在识别过程中,如果出错,错误码很有用

    // 错误码
    void onError(int error);
复制代码

​ 这里是错误码的原因,可以做参考去排查,错误码在SpeechRecognizer.class中,可以自行查阅

    /** Network operation timed out. */
    public static final int ERROR_NETWORK_TIMEOUT = 1;

    /** Other network related errors. */
    public static final int ERROR_NETWORK = 2;

    /** Audio recording error. */
    public static final int ERROR_AUDIO = 3;

    /** Server sends error status. */
    public static final int ERROR_SERVER = 4;

    /** Other client side errors. */
    public static final int ERROR_CLIENT = 5;

    /** No speech input */
    public static final int ERROR_SPEECH_TIMEOUT = 6;

    /** No recognition result matched. */
    public static final int ERROR_NO_MATCH = 7;

    /** RecognitionService busy. */
    public static final int ERROR_RECOGNIZER_BUSY = 8;

    /** Insufficient permissions */
    public static final int ERROR_INSUFFICIENT_PERMISSIONS = 9;
复制代码

​ 这里特别说明下,如果你所有操作都正常,可是在监听回调中一直出现 ERROR_INSUFFICIENT_PERMISSIONS 即错误码返回一直是 9,这时候可以尝试常看各个厂商的语音助手有没有在这里做处理,方法就是在说话的时候,这时打开语音助手,会有语音助手提示赋予应用权限,这种情况我在小米手机上遇到过,小爱同学需要打开权限,打开就好了。

​ 以上就是一般的做法,但是不一定有用,厂商会做什么事,我们是不知道滴,具体问题需要具体对待,下面我们来讨论另外一种实现


  1. 照旧在启动服务前需要检查服务是否存在 *SpeechRecognizer.isRecognitionAvailable(context);*如果返回false,要么歇菜(绝大多数不会出现),要么自己实现,我自己实现不了。

    如果返回true,说明有语音识别服务可以用,这时候我们需要记录下当前系统内置的是哪个服务

String serviceComponent = Settings.Secure.getString(context.getContentResolver(),
                                                            "voice_recognition_service");
复制代码

serviceComponent就是我们的服务名称,eg:华为手机返回"com.huawei.vassistant/com.huawei.ziri.service.FakeRecognitionService"从名字看就是 FakeRecognitionService伪造的语音识别服务,就是说这个是不用的。这里多说下,华为使用的讯飞的语音识别服务。

​ 组装成组件

// 当前系统内置语音识别服务
ComponentName component = ComponentName.unflattenFromString(serviceComponent);
复制代码

​ 组装成一个Component组件,后面我们需要用到


// 内置语音识别服务是否可用
boolean isRecognizerServiceValid = false;
ComponentName currentRecognitionCmp = null;
// 查找得到的 "可用的" 语音识别服务
List<ResolveInfo> list = context.getPackageManager().queryIntentServices(new Intent(RecognitionService.SERVICE_INTERFACE), MATCH_ALL);

if (list != null && list.size() != 0) {
    for (ResolveInfo info : list) {
        debugLog(TAG, "\t" + info.loadLabel(context.getPackageManager()) + ": "
                + info.serviceInfo.packageName + "/" + info.serviceInfo.name);
				
      	// 这里拿系统使用的语音识别服务和内置的语音识别比较,如果相同,OK我们直接直接使用
      	// 如果相同就可以直接使用mSpeechRecognizer = 			   SpeechRecognizer.createSpeechRecognizer(context);来创建实例,因为内置的可以使用
        if (info.serviceInfo.packageName.equals(component.getPackageName())) {
            isRecognizerServiceValid = true;
            break;
        } else {
         // 如果服务不同,说明 内置服务 和 系统使用 不是同一个,那么我们需要使用系统使用的
         // 因为内置的系统不用,我们用了也没有用
            currentRecognitionCmp = new ComponentName(info.serviceInfo.packageName, info.serviceInfo.name);
        }

    }
} else {
  	// 这里既是查不到可用的语音识别服务,可以歇菜了
    debugLog(TAG, "No recognition services installed");
    return false;
}
复制代码

​ 根据判断结果创建实例

        // 当前系统内置语音识别服务可用
        if (isRecognizerServiceValid) {
            mSpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(context);
        } else {
          // 内置不可用,需要我们使用查找到的可用的
            mSpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(context, currentRecognitionCmp);
        }

        mSpeechRecognizer.setRecognitionListener(this);
复制代码

​ 关于*SpeechRecognizer createSpeechRecognizer(final Context context,final ComponentName serviceComponent)*方法源码如下

    /**
     * Factory method to create a new {@code SpeechRecognizer}. Please note that
     * {@link #setRecognitionListener(RecognitionListener)} should be called before dispatching any
     * command to the created {@code SpeechRecognizer}, otherwise no notifications will be
     * received.
     *
     * Use this version of the method to specify a specific service to direct this
     * {@link SpeechRecognizer} to. Normally you would not use this; use
     * {@link #createSpeechRecognizer(Context)} instead to use the system default recognition
     * service.
     * 
     * @param context in which to create {@code SpeechRecognizer}
     * @param serviceComponent the {@link ComponentName} of a specific service to direct this
     *        {@code SpeechRecognizer} to
     * @return a new {@code SpeechRecognizer}
     */
    public static SpeechRecognizer createSpeechRecognizer(final Context context,
            final ComponentName serviceComponent) {
        if (context == null) {
            throw new IllegalArgumentException("Context cannot be null)");
        }
        checkIsCalledFromMainThread();
        return new SpeechRecognizer(context, serviceComponent);
    }
复制代码

​ 注释写的很明白,不建议我们使用,但是没办法,我们也不想折腾,各大厂商自己有实现,只能这样了。

​ 使用该方法来做基本能满足大多数手机的功能实现,但是这种方法的前提有一个*SpeechRecognizer.isRecognitionAvailable(final Context context)*该方法要返回true,系统没有服务可用,那是没有办法的。

​ 以下是自己的实现,可以根据自己使用修改

        // 查找当前系统的内置使用的语音识别服务
        // com.huawei.vassistant/com.huawei.ziri.service.FakeRecognitionService
        String serviceComponent = Settings.Secure.getString(context.getContentResolver(),
                                                            "voice_recognition_service");

        debugLog(TAG, "voice_recognition_service : " + serviceComponent);

        if (TextUtils.isEmpty(serviceComponent)) {
            return false;
        }

        ComponentName component = ComponentName.unflattenFromString(serviceComponent);

        if (component == null) {
            debugLog(TAG, "voice_recognition_service component == null");
            return false;
        }

        debugLog(TAG, "serviceComponent : " + component.toShortString());

        boolean isRecognizerServiceValid = false;
        ComponentName currentRecognitionCmp = null;

        // 查找得到的 "可用的" 语音识别服务
        List<ResolveInfo> list = context.getPackageManager().queryIntentServices(new Intent(RecognitionService.SERVICE_INTERFACE), MATCH_ALL);
        if (list != null && list.size() != 0) {
            for (ResolveInfo info : list) {
                debugLog(TAG, "\t" + info.loadLabel(context.getPackageManager()) + ": "
                        + info.serviceInfo.packageName + "/" + info.serviceInfo.name);

                if (info.serviceInfo.packageName.equals(component.getPackageName())) {
                    isRecognizerServiceValid = true;
                    break;
                } else {
                    currentRecognitionCmp = new ComponentName(info.serviceInfo.packageName, info.serviceInfo.name);
                }

            }
        } else {
            debugLog(TAG, "No recognition services installed");
            return false;
        }

        if (mSpeechRecognizer != null) {
            return true;
        }

        debugLog(TAG, "isRecognitionAvailable: " + SpeechRecognizer.isRecognitionAvailable(context));

        if (isRecognizerServiceValid) {
            mSpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(context);
        } else {
            mSpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(context, currentRecognitionCmp);
        }

        mSpeechRecognizer.setRecognitionListener(this);

        if (mRecognitionIntent == null) {
            mRecognitionIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
            mRecognitionIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
            mRecognitionIntent.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS, true);
            mRecognitionIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 3);
        }
        return true;
复制代码

到这里之后就是监听回调,回到了第一种方法的实现。就不贴代码了。


如有错误,不吝赐教

转载于:https://juejin.im/post/5cfa1a866fb9a07eab687234

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android原生SpeechRecognizer(语音识别) 的相关文章

  • 如何从该 JAVA 文件中提取 Delphi 类以与 Android 一起使用?

    我的Delphi XE7项目需要与FTDI FT311 Android 配件芯片 http www ftdichip com Products ICs FT311D html 他们帮助提供了一个 Android 演示 其中包括他们的 JAV
  • Gradle 构建错误:内存不足

    当我使用 gradle 构建时 它失败并显示以下信息 OpenJDK 64 Bit Server VM warning INFO os commit memory 0x0000000788800000 89128960 0 failed e
  • 我在哪里可以获得可靠的熵来源(真正的随机性字节[])?

    目前 我正在寻找一种方法来增加随机性的质量 in my Android应用程序 纸牌游戏 之前 估计对于我的情况 52 排列 至少需要 226 位熵 226 个随机位 我打算用这个byte 作为种子SecureRandom SecureRa
  • Android上如何模拟后台Activity因内存不足而被系统杀死的过程?

    我正在处理 内存不足 不再有后台进程 问题 当这种情况发生时 我的活动处于后台并被杀死 我正在尝试保存并加载实例状态来解决它 但因为它并不是每次都会发生 在这种情况下我应该如何测试我的活动 Thanks 您可以通过 adb 强制进程终止 g
  • 使用一个 apk 安装两个应用程序

    我有 2 个应用程序 1 内容提供者 2 使用此 ContentProvider 的应用程序 我需要使用单个 apk 文件安装这 2 个应用程序 我想在 Eclipse 中同时推送这两个应用程序 如果我将另一个项目添加到一个应用程序的构建路
  • 从历史堆栈中删除活动

    我的应用程序在用户第一次运行应用程序时显示注册活动 如下所示 活动启动画面 欢迎来到游戏 注册帐户 ActivitySplashScreenSignUp 很好 填写此信息 ActivityGameMain 游戏主屏幕 因此 当用户单击每个屏
  • 如何在 android 中启动“添加联系人”活动

    您能告诉我如何在 Android 中启动 添加联系人 活动吗 谢谢 API 级别 5 及以上解决方案 Add listener so your activity gets called back upon completion of act
  • 如何访问android库项目中的资源

    我正在构建一个 android 库项目 它内部需要一些静态资源 图像 xml 等 然后我想知道我可以把这些资源放在哪里以及如何访问它们 既然我把资源放到了assets文件夹 我使用 AssetManager 来访问资源 public cla
  • 更改卡片高度即更改 Jetpack 中与 Material 3 组合的卡片颜色

    我正在使用 Card 可组合项 我希望它的颜色为白色 但是当我向它添加一些高度时 它的颜色会更改为更像主要容器颜色 我看过文档 其中有一种称为高程覆盖的东西 但找不到说明如何使用它的示例 这是我的代码 Card modifier Modif
  • Google 地图删除标记路线上下文菜单

    我使用 Android Studio 的 Google 地图模板启动了一个新项目 并在地图上添加了一个标记 LatLng location new LatLng lat lng Marker marker mMap addMarker ne
  • Android 版 jTwitter 授权错误

    我在我的 Android 应用程序中使用 jTwitter 库 直到前天一切都运转良好 但今天遇到异常 服务提供商响应错误 301 请帮助我 这是堆栈跟踪 02 21 21 07 27 258 E AndroidRuntime 4013 F
  • 使用 Android Firebase 堆栈推送通知

    我开发了使用 Firebase 接收推送通知的 Android 应用程序 我的代码基于 Firebase Google 官方文档 https firebase google com docs cloud messaging android
  • 来自相机的 MediaCodec 视频流方向和颜色错误

    我正在尝试流式传输视频捕获直接从相机适用于 Android 设备 到目前为止 我已经能够从 Android 相机捕获每一帧预览帧 byte data Camera camera 函数 对数据进行编码 然后成功解码数据并显示到表面 我用的是安
  • 适用于 Android 的 Google 云端硬盘\文档 API

    我在几个小时内将 Dropbox 与我的应用程序集成 因为 SDK 描述清晰并且有很好的使用示例 Google Drive 似乎只有一个 一刀切 的 Gdata SDK 它非常重 有很多依赖项 它使我的应用程序的大小增加了三倍 而且不是很直
  • Android 纹理仅显示纯色

    我正在尝试在四边形上显示单个纹理 我有一个可用的 VertexObject 它可以很好地绘制一个正方形 或任何几何对象 现在我尝试扩展它来处理纹理 但纹理不起作用 我只看到一种纯色的四边形 坐标数据位于 arrayList 中 the ve
  • android中listview显示数据库中的数据

    我是安卓新手 我想知道如何在列表视图中显示数据库中的数据 它不会向数据库添加数据 我只是显示我们存储在数据库中的任何内容 请帮助我实现这一目标 提前致谢 使用这些课程可能会对您有所帮助 用于数据库创建 package com example
  • TextInputLayout 对于在 EditText 中以编程方式给出提示没有效果

    我有一个 EditText 它的父级是 TextInputLayout 我试图以编程方式为 EditText 提供提示 不在布局中 在这种情况下 文本输入提示动画不起作用 它像简单的 EditText 一样工作 有人可以建议如何处理它吗 下
  • 如何构建自定义摄像机应用程序?

    我正在尝试开发一个自定义摄像机录像机 当我的设备在 Activity 的 beginRecording 中执行 start MediaRecorder 方法时 应用程序崩溃 我不知道出了什么问题 因为我遵循谷歌API指南 http deve
  • 如何正确编写AttributeSet的XML?

    我想创建一个面板适用于 Android 平台的其他小部件 http code google com p android misc widgets 在运行时 XmlPullParser parser getResources getXml R
  • 我应该如何在 Android 上使用 Retrofit 处理“无互联网连接”

    我想处理没有互联网连接的情况 通常我会运行 ConnectivityManager cm ConnectivityManager context getSystemService Context CONNECTIVITY SERVICE N

随机推荐

  • matlab练习程序(双边滤波)

    双边滤波模板主要有两个模板生成 xff0c 第一个是高斯模板 xff0c 第二个是以灰度级的差值作为函数系数生成的模板 然后这两个模板点乘就得到了最终的双边滤波模板 第一个模板是全局模板 xff0c 所以只需要生成一次 第二个模板需要对每个
  • hive:导出数据记录中null被替换为\n的解决方案

    在hive中 xff0c 一般情况下通过 1 use my hive db 2 set hive merge mapfiles 61 true 3 set hive merge mapredfiles 61 true 4 set hive
  • STP试验的综合应用

    实验环境 xff1a Catalyst 2950 24 S1 SwA S2 SwB S3 SwC S4 SwD 实验目的 xff1a 1 利用VTP协议实现VLAN配置的一致性 2 通过PVST的配置实现交换网络的负载分担 其次实现冗余备份
  • Ubuntu12.04LTS安装好后是空白桌面的解决步骤

    安装完毕启动后 xff0c 明显慢的要死 xff0c 登陆后竟然是一个空白的桌面环境 xff0c Ctrl 43 Alt 43 T 根本没有任何反应 唯一的反应就是右键能够创建文件和文档 同时打开的窗口没有最大化 xff0c 最小化及关闭按
  • could not execute menu item系统找不到指定的文件

    Wamp3 0 6 64bit xff0c 系统任务栏图标 xff0c 左键 xff0c Apache菜单 xff0c httpd conf xff0c 报错 could not execute menu item 系统找不到指定的文件 根
  • 这么好用的U盘数据恢复软件,推荐!

    U盘受到了用户不同程度的青睐 xff0c 可以将临时要用的数据输入到U盘中 但是U盘在使用过程中 xff0c 也会出现一些突发情况 xff0c 让用户措手不及 xff0c 其中最常见的就属数据丢失 数据丢失的原因包括多种 xff0c 误删除
  • node+微信小程序实现商城案例

    说明 xff1a 1 本人也是初次完整使用小程序 xff0c 如有BUG或者不足的地方请在Issues或者本文下方留言 xff0c 作者会尽快修改 xff0c 谢谢 xff01 2 本项目适合初学者或者准备自学小程序的伙伴 小程序功能 xf
  • SpringBoot使用fastjson的JsonField注解序列化Bigdecimal

    代码 lt xml version 61 34 1 0 34 encoding 61 34 UTF 8 34 gt lt project xmlns 61 34 http maven apache org POM 4 0 0 34 xmln
  • 网盘告急 亚信安全Safesync让企业级数据存储吃下“定心丸”

    近日 xff0c 两条重磅消息登上新闻头条 xff1a 一则是360云盘由于无法解决盗版侵权等问题 xff0c 将于2017年2月1日起关闭云盘 xff1b 另一则是百度遭到渔具店老板 撞库 xff0c 50万账号被盗后又遭转卖 xff0c
  • android内存泄漏分析的一种方式

    最近在处理项目的拷机问题 xff0c 发现在测试24小时内 xff0c 都是正常的 xff0c 但是超过24小时后 xff0c 重启一大片 xff0c 让人抓狂 分析了logcat打印 xff0c 发现重启是因为系统服务已经被watchdo
  • 29个运维经典面试题

    前言 这篇博文参考阿铭linux 28个运维经典面试题 xff0c 并对其中的一些题目进行扩展和解析 如有侵权 xff0c 请联系我删除 xff5e 再次感谢阿铭老师的分享 大家有空可以看看阿铭老师的教程 xff5e 第一题 xff1a L
  • win10下丢失msvcr110.dll解决办法

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 笔者尝试过的方法 xff1a 方法一 xff1a 360安全卫士 功能大全 搜索区查找 xff1a msvcr110 dll xff0c 回车键结束 选择第一个 xff0c
  • moment.js格式化日期,获取前一个月的时间

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 下载moment js 格式化当前日期 xff1a 显示结果为 xff1a 34 2017 09 20 15 35 52 34 moment new Date format
  • 在springboot中配置web.xml

    在springBoot中配置web xml中配置的servlet http www cnblogs com wangxiaomei p 8885470 html 转载于 https www cnblogs com lxcmyf p 1006
  • SQL字符串处理函数大全

    select语句中只能使用sql函数对字段进行操作 xff08 链接sql server xff09 xff0c select 字段1 from 表1 where 字段1 IndexOf 34 云 34 61 1 这条语句不对的原因是ind
  • 设计模式之(十五)职责链Chain of Responsibility

    Chain of Responsibility定义 Chain of Responsibility CoR 是用一系列类 classes 试图处理一个请求request 这些类之间是一个松散的耦合 唯一共同点是在他们之间传递request
  • 如何判断一个C++对象是否在堆上

    摘要 在帖子 34 如何判断一个C 43 43 对象是否在堆栈上 中 xff0c 又有人提出如何判断一个C 43 43 对象是否在堆上 阅读全文 Richard Wei 2012 05 12 14 30 发表评论 转载于 https www
  • 前端框架Vue、angular、React的优点和缺点,以及应用场景

    学习web前端开发中 xff0c 会有很多的框架 xff0c 那么目前流行的框架有哪些 xff0c 以及他们的优缺点和应用场景有哪些呢 xff1f 一 Vue js xff1a 其实Vue js不是一个框架 xff0c 因为它只聚焦视图层
  • AFNetworking(二)AFNetworking对form-data请求体的处理

    AFNetworking 发送 GET POST 等请求时可以直接将参数按照字典结构传入 xff0c 最终编码到 url 中或者是 body 实体中 xff0c 同时也支持按照 multipart form data 格式 xff0c 将多
  • Android原生SpeechRecognizer(语音识别)

    开篇先吐槽下 xff0c 在Android 平台开发原生的SpeechRecognizer真是难受的 xff0c 不像ios xff0c 无比轻松 xff0c 平台统一 由于Android 平台的碎片化问题比较严重 xff0c 各个厂商都有