android TextToSpeech 源码分析

本文详细剖析了Android TextToSpeech组件的初始化过程,包括构造方法、引擎选择及连接等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

TTS 相关的源码位置:
android / platform / frameworks / base / core / java / android / speech


1.先从TextToSpeech.java分析
构造方法:
public TextToSpeech(Context context, OnInitListener listener) {
        this(context, listener, null);
    }


  public TextToSpeech(Context context, OnInitListener listener, String engine) {
        this(context, listener, engine, null, true);
    }
最终会调用这个构造方法:
 public TextToSpeech(Context context, OnInitListener listener, String engine,
            String packageName, boolean useFallback) {
        mContext = context;
        mInitListener = listener;
        mRequestedEngine = engine;
        mUseFallback = useFallback;
        mEarcons = new HashMap<String, Uri>();
        mUtterances = new HashMap<CharSequence, Uri>();
        mUtteranceProgressListener = null;
        mEnginesHelper = new TtsEngines(mContext);//前面都是赋值操作,主要看这里,new 一个TtsEngines();
        initTts();
    }


我们看一下TtsEngines的构造方法:
public TtsEngines(Context ctx) {
        mContext = ctx;//赋值Context
    }




接下来看initTts()
 private int initTts() {
        // Step 1: Try connecting to the engine that was requested.
        if (mRequestedEngine != null) {//如果我们使用new TextToSpeech(mContext, this)这种方式来构造TTS,则这里的mRequestedEngine肯定等于null
            if (mEnginesHelper.isEngineInstalled(mRequestedEngine)) {
                if (connectToEngine(mRequestedEngine)) {
                    mCurrentEngine = mRequestedEngine;
                    return SUCCESS;
                } else if (!mUseFallback) {
                    mCurrentEngine = null;
                    dispatchOnInit(ERROR);
                    return ERROR;
                }
            } else if (!mUseFallback) {
                Log.i(TAG, "Requested engine not installed: " + mRequestedEngine);
                mCurrentEngine = null;
                dispatchOnInit(ERROR);
                return ERROR;
            }
        }
        // Step 2: Try connecting to the user's default engine.
        final String defaultEngine = getDefaultEngine();
        if (defaultEngine != null && !defaultEngine.equals(mRequestedEngine)) {
            if (connectToEngine(defaultEngine)) {
                mCurrentEngine = defaultEngine;
                return SUCCESS;
            }
        }
        // Step 3: Try connecting to the highest ranked engine in the
        // system.
        final String highestRanked = mEnginesHelper.getHighestRankedEngineName();
        if (highestRanked != null && !highestRanked.equals(mRequestedEngine) &&
                !highestRanked.equals(defaultEngine)) {
            if (connectToEngine(highestRanked)) {
                mCurrentEngine = highestRanked;
                return SUCCESS;
            }
        }
        // NOTE: The API currently does not allow the caller to query whether
        // they are actually connected to any engine. This might fail for various
        // reasons like if the user disables all her TTS engines.
        mCurrentEngine = null;
        dispatchOnInit(ERROR);
        return ERROR;
    }








直接看step 2: 
getDefaultEngine();
public String getDefaultEngine() {
        return mEnginesHelper.getDefaultEngine();
    }




调用mEnginesHelper的方法,mEnginesHelper就是之前new 出来的TtsEngines。


TtsEngines的getDefaultEngine()方法:
 /**
     * @return the default TTS engine. If the user has set a default, and the engine
     *         is available on the device, the default is returned. Otherwise,
     *         the highest ranked engine is returned as per {@link EngineInfoComparator}.
     */
    public String getDefaultEngine() {
        String engine = getString(mContext.getContentResolver(),
                Settings.Secure.TTS_DEFAULT_SYNTH);
        return isEngineInstalled(engine) ? engine : getHighestRankedEngineName();
    }
|
|
|
|
|
|


/**
     * @return the package name of the highest ranked system engine, {@code null}
     *         if no TTS engines were present in the system image.
     */
    public String getHighestRankedEngineName() {
        final List<EngineInfo> engines = getEngines();
        if (engines.size() > 0 && engines.get(0).system) {
            return engines.get(0).name;
        }
        return null;
    }




返回系统中最高级别的engine










 /**
     * Information about an installed text-to-speech engine.
     *
     * @see TextToSpeech#getEngines
     */
    public static class EngineInfo {
        /**
         * Engine package name..
         */
        public String name;
        /**
         * Localized label for the engine.
         */
        public String label;
        /**
         * Icon for the engine.
         */
        public int icon;
        /**
         * Whether this engine is a part of the system
         * image.
         *
         * @hide
         */
        public boolean system;
        /**
         * The priority the engine declares for the the intent filter
         * {@code android.intent.action.TTS_SERVICE}
         *
         * @hide
         */
        public int priority;
        @Override
        public String toString() {
            return "EngineInfo{name=" + name + "}";
        }
    }








setp2中会 把获取到的引擎name赋值给mCurrentEngine, mCurrentEngine = defaultEngine;
然后调用
private boolean connectToEngine(String engine) {
        Connection connection = new Connection();
        Intent intent = new Intent(Engine.INTENT_ACTION_TTS_SERVICE);
        intent.setPackage(engine);
        boolean bound = mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE);
        if (!bound) {
            Log.e(TAG, "Failed to bind to " + engine);
            return false;
        } else {
            Log.i(TAG, "Sucessfully bound to " + engine);
            mConnectingServiceConnection = connection;
            return true;
        }
    }




可以看到该方法中去bindService了。
但是这个地方的Connection是个什么东西呢?
就是




initTts方法的最后会调用:




private void dispatchOnInit(int result) {
        synchronized (mStartLock) {
            if (mInitListener != null) {
                mInitListener.onInit(result);
                mInitListener = null;
            }
        }
    }


mInitListener是构造方法中传的参数TextToSpeech(Contex t context, OnInitListener listener) ,可以看到我们传进去的listener在接收到回调之后就没有用了。
       
<think>好的,用户现在问的是`torch.cuda.empty_cache()`是否只能清空一个GPU的缓存。我需要先回忆一下PyTorchCUDA内存管理的机制。 首先,用户之前遇到了关于Scikit-learn的分类指标警告和PyTorch的NCCL支持问题,现在转向了CUDA内存管理。可能用户正在处理多GPU训练,或者在单GPU使用中遇到了显存问题。 `torch.cuda.empty_cache()`的作用是释放当前由缓存分配器持有的未使用内存PyTorch默认情况下会缓存一部分内存以提高分配效率,这个函数可以释放那些未被使用的缓存内存。但用户的问题是,这个函数是否只影响当前的一个GPU。 根据PyTorch的文档,当使用多GPU时,每个GPU都有自己的缓存分配器。所以,如果用户有多个GPU,`torch.cuda.empty_cache()`只会释放当前设备(即通过`torch.cuda.current_device()`指定的那个)的缓存。要清空所有GPU的缓存,需要遍历每个设备,切换当前设备,然后分别调用该函数。 用户可能是在多GPU环境下运行代码,发现显存没有完全释放,或者遇到显存不足的问题。需要确认用户是否在使用多GPU,以及是否正确地切换了设备来清空每个GPU的缓存。 另外,用户可能对PyTorch内存管理机制不太熟悉,所以需要解释清楚这个函数的行为,以及在不同情况下的正确使用方法。可能还需要提醒用户,即使调用了这个函数,显存的释放也依赖于是否有未使用的缓存,实际使用的张量不会被释放。 最后,给出示例代码,说明如何为所有GPU清空缓存,并强调正确使用设备切换的重要性。还要提醒用户,显存管理需要结合其他方法,如手动删除变量、运行垃圾回收等,才能有效管理显存使用。</think>--- 在 PyTorch 中,`torch.cuda.empty_cache()` 的作用是释放当前 CUDA 设备(即当前选中的 GPU)的**未使用缓存内存**。其行为取决于 **GPU 设备的选择**和 **多 GPU 环境**的配置。以下是详细说明: --- ### **1.GPU 场景** - 如果你只有一个 GPU,或者代码中未显式指定 GPU 设备: `torch.cuda.empty_cache()` 会清空当前默认 GPU 的缓存内存。 例如: ```python import torch # 默认使用 GPU 0(仅单卡时) a = torch.randn(1000, 1000, device="cuda") # 占用显存 del a # 删除变量 torch.cuda.empty_cache() # 释放 GPU 0 的未使用缓存 ``` --- ### **2.GPU 场景** - 如果你有多个 GPU,且代码中显式切换了设备: 需要**依次选中每个 GPU 并单独调用** `empty_cache()`,才能清空所有 GPU 的缓存。 例如: ```python import torch # 清空 GPU 0 的缓存 torch.cuda.set_device(0) torch.cuda.empty_cache() # 清空 GPU 1 的缓存 torch.cuda.set_device(1) torch.cuda.empty_cache() ``` --- ### **3. 关键注意事项** 1. **缓存释放的范围**: `empty_cache()` **仅释放由 PyTorch 缓存分配器管理的未占用内存**,不会释放正在被张量占用的显存。 - 已分配的张量必须手动删除(如 `del tensor`)或超出作用域后,其显存才会被缓存分配器回收。 - 调用 `empty_cache()` 后,这些回收的内存才会真正释放回系统。 2. **多进程分布式训练**: 在分布式训练(如使用 `torch.distributed` 或 `DataParallel`)时,每个进程可能绑定到不同的 GPU。 - 每个进程需独立调用 `empty_cache()` 清理自己绑定的 GPU 缓存。 - 例如: ```python # 每个进程仅清理自己绑定的 GPU torch.cuda.empty_cache() ``` 3. **自动缓存管理**: PyTorch 默认会缓存部分显存以提高分配效率。频繁调用 `empty_cache()` 可能导致性能下降,建议仅在显存不足时手动调用。 --- ### **4. 验证显存释放** 可以使用 `torch.cuda.memory_summary()` 或以下代码查看显存状态: ```python import torch # 查看当前 GPU 的显存使用情况(单位:字节) print(torch.cuda.memory_allocated()) # 当前已分配的显存 print(torch.cuda.memory_reserved()) # 当前缓存分配器保留的显存(包括未使用的) ``` --- ### **总结** | 场景 | 行为 | |------------|----------------------------------------------------------------------| | **单 GPU** | 清空当前 GPU 的未使用缓存。 | | **多 GPU** | 需遍历所有 GPU,分别调用 `empty_cache()` 才能清空每个设备的缓存。 | --- ### **最佳实践** - 显存不足时手动调用 `empty_cache()`,但避免在循环中频繁使用。 - 结合显存监控工具(如 `nvidia-smi` 或 PyTorch 内置函数)诊断显存泄漏。 - 多 GPU 场景显式指定设备并分别清理: ```python for i in range(torch.cuda.device_count()): torch.cuda.set_device(i) torch.cuda.empty_cache() ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值