Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >腾讯云流式TTS语音合成客户端实现

腾讯云流式TTS语音合成客户端实现

原创
作者头像
用户1530353
修改于 2019-08-28 17:21:31
修改于 2019-08-28 17:21:31
9.7K40
代码可运行
举报
文章被收录于专栏:小蝌蚪展览小蝌蚪展览
运行总次数:0
代码可运行

腾讯云流式TTS介绍

接入文档链接:https://cloud.tencent.com/document/api/441/19499

该接口传入参数为json,目前还不支持云api3.0鉴权,输出协议采用了http chunk协议,数据格式包括opus压缩后的分片和pcm原始音频流,本文将从鉴权开始,详细介绍流式tts的客户端实现。

接口鉴权

1.构造json请求参数,为了方便将参数排序,使用TreeMap存储参数

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 mRequestMap.put("Action", "TextToStreamAudio");
 mRequestMap.put("Text", text);
 mRequestMap.put("SessionId", "session-1234");
 mRequestMap.put("AppId", "1255824371");
 mRequestMap.put("Timestamp", "" + System.currentTimeMillis() / 1000L);
 mRequestMap.put("Expired", "" + (System.currentTimeMillis() / 1000L + 600));
 mRequestMap.put("Speed", "0");
 mRequestMap.put("SecretId", SECRET_ID);
 mRequestMap.put("VoiceType", 0 + "");
 mRequestBody =  (new JSONObject(mRequestMap)).toString();                     
                                         

2.生成签名串,按要求拼接字符串后加密即可,这里需要注意仔细阅读鉴权文档的说明,不然很容易出错

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private static String generateSign(TreeMap<String, String> params) {
        String paramStr = "POST" + DOMAIN_NAME + "?";
        StringBuilder builder = new StringBuilder(paramStr);
        for (Map.Entry<String, String> entry : params.entrySet()) {
            builder.append(String.format(Locale.CHINESE, "%s=%s", entry.getKey(), String.valueOf(entry.getValue())))
                    .append("&");
        }

        //去掉最后一个&
        builder.deleteCharAt(builder.lastIndexOf("&"));

        String sign = "";
        String source = builder.toString();
        System.out.println(source);
        Mac mac = null;
        try {
            mac = Mac.getInstance("HmacSHA1");
            SecretKeySpec keySpec = new SecretKeySpec(SECRET_KEY.getBytes(), "HmacSHA1");
            mac.init(keySpec);
            mac.update(source.getBytes());
            sign = Base64.encodeToString(mac.doFinal(), 2);
        } catch (NoSuchAlgorithmException | InvalidKeyException e) {
            e.printStackTrace();
        }

        System.out.println("生成签名串:" + sign);
        return sign;
    }

到这里我们就获得了一个完整的签名串,接下来就是本文的重点点部分了,网络请求和网络解析

chunk分块传输编码

这里由于腾讯云采用了http chunk协议返回,不同于常规的http诸如json返回,采用多段分片返回数据的方式。消息体由数量未定的块组成,并以最后一个大小为0的块为结束。

每一个非空的块都以该块包含数据的字节数(字节数16进制以表示)开始,跟随一个CRLF (回车及换行),然后是数据本身,最后块CRLF结束。在一些实现中,块大小和CRLF之间填充有白空格(0x20)。

最后一块是单行,由块大小(0),一些可选的填充白空格,以及CRLF。最后一块不再包含任何数据,但是可以发送可选的尾部,包括消息头字段。

消息最后以CRLF结尾。一个完整的chunk返回示例如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked

25
This is the data in the first chunk

1C
and this is the second one

3
con

8
sequence

0

如果对chunk协议希望有一个完整的了解,可以参考这篇wiki:分块传输编码

请求TTS数据

代码如下,我们直接获取返回数据数据流管道,用于数据读取

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private static InputStream obtainResponseStreamWithJava(String postJsonBody, TreeMap<String, String> requestMap) throws IOException {
        //发送POST请求
        URL url = new URL(SERVER_URL);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        String authorization = generateSign(requestMap);
        conn.setRequestMethod("POST");
        conn.setRequestProperty("Content-Type", "application/json");
        conn.setRequestProperty("Authorization", authorization);
        conn.connect();
        OutputStream out = conn.getOutputStream();
        out.write(postJsonBody.getBytes("UTF-8"));
        out.flush();
        out.close();
        if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {//todo
            Log.w(TAG, "HTTP Code: " + conn.getResponseCode());
        }
//        String result = new String(toByteArray(conn.getInputStream()), "UTF-8");
        InputStream inputStream = conn.getInputStream();
        return inputStream;
    }

OPUS

根据官网的文档得知,数据分为两种,opus压缩和pcm原始音频流,题主了解到opus拥有较好的压缩比(10:1),可以很好的节省传输时间和网络带宽。

opus是开源库,但是是用C++编写的,由于Android5.0以上才支持opus格式的播发,所以如果需要兼容5.0的系统,需要编译so库。opus源码地址

TTS数据解析

这里主要参考官网的java示例,循环读取数据,按以下格式说明不断读取头/序号/长度/音频数据,直到到达数据末尾。

tts分片格式
tts分片格式

代码示例如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private void processProtocolBufferStream(final InputStream inputStream) throws DeserializationException {
            final long start = System.currentTimeMillis();

            YoutuOpusDecoder decoder = null;

            List<PcmData> pcmCache = new ArrayList<>();
            boolean fillSuccess;
            int pbPkgCount = -1;

            while (!Thread.currentThread().isInterrupted()) {
                pbPkgCount++;
                try {
                    //read head
                    byte[] headBuffer = new byte[4];
                    fillSuccess = fill(inputStream, headBuffer);
                    if (!fillSuccess) {
                        throw new ReadBufferException(String.format("read PB pkg#%s size header fail, break;", pbPkgCount));
                    }
                    //read seq
                    byte[] seqBuffer = new byte[4];
                    fillSuccess = fill(inputStream, seqBuffer);
                    if (!fillSuccess) {
                        throw new ReadBufferException(String.format("read PB pkg#%s size header fail, break;", pbPkgCount));
                    }
                    int seq = bytesToInt(seqBuffer);
                    //read pkg size
                    byte[] pbPkgSizeHeader = new byte[4];
                    fillSuccess = fill(inputStream, pbPkgSizeHeader);
                    if (!fillSuccess) {
                        throw new ReadBufferException(String.format("read PB pkg#%s size header fail, break;", pbPkgCount));
                    }
                    int pbPkgSize = bytesToInt(pbPkgSizeHeader);
                    Log.i(TAG, String.format("PB pkg#%s size = %s", pbPkgCount, pbPkgSize));
                    if (pbPkgCount == 0) {
                        sTimeEnd = System.currentTimeMillis();
                        sTimeCost = sTimeEnd - sTimeStart;
                    }
                    if (pbPkgSize <= 0) {
                        throw new ReadBufferException(String.format("PB pkg#%s size %s <= 0, break;", pbPkgCount, pbPkgSize));
                    } else if (pbPkgSize > 5000) {
                        throw new ReadBufferException(String.format("PB pkg#%s size %s > 5000 bytes, too large, break;", pbPkgCount, pbPkgSize));
                    }

                    //read pb pkg
                    byte[] pbPkg = new byte[pbPkgSize];
                    fillSuccess = fill(inputStream, pbPkg);
                    if (!fillSuccess) {
                        throw new ReadBufferException(String.format("read PB pkg#%s fail, break;", pbPkgCount));
                    }

                    //init decoder
                    if (decoder == null) {
                        decoder = new YoutuOpusDecoder();
                        decoder.config();
                    }
                    //decode
                    Log.i("DEBUG-1", "seq:" + seq);
                    Pair<Integer, short[]> pair = decoder.decodeTTSData(seq, pbPkg);
                    short[] pcm = pair.second;

                    Log.d(TAG, (pcm == null ? "fail decode #" : "decode #") + pbPkgCount);

                    //packaging pcm
                    if (pcm == null) {
                        pcm = new short[0];
                    }
                    PcmData pcmData = new PcmData(pcm, seq == -1);

                    //stop check
                    if (Thread.currentThread().isInterrupted()) {
                        Log.w(TAG, "pcm data ready, but thread is interrupted, break;");
                        break;
                    }

                    //init player
                    if (mOpusPlayer == null) {
                        mOpusPlayer = new OpusPlayer();
                        mOpusPlayer.setPcmSampleRate(16000);
                        mOpusPlayer.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
                            @Override
                            public void uncaughtException(Thread thread, Throwable ex) {
                                if (mTtsExceptionHandler != null) {
                                    mTtsExceptionHandler.onPlayException(thread, ex);
                                }
                            }
                        });
                    }

                    //enqueue
                    if (pbPkgCount < mCacheCount) {//缓冲
                        pcmCache.add(pcmData);
                    } else {//enqueue
                        for (PcmData d : pcmCache) {
                            mOpusPlayer.enqueue(d);
                        }
                        pcmCache.clear();
                        mOpusPlayer.enqueue(pcmData);
                    }

                    //end
                    if (seq == -1) {
                        long ms = System.currentTimeMillis() - start;
                        Log.d(TAG, "finish last pb pkg#" + pbPkgCount + ", total cast time " + ms + " ms");
                        break;
                    }
                } catch (Exception e) {
                    if (mOpusPlayer != null) {
                        mOpusPlayer.forceStop();
                    }
                    if (e instanceof InterruptedIOException) {
                        Log.i(TAG, "Interrupted while reading server response InputStream", e);// 正常流程, 无需抛出异常
                    } else {
                        throw new DeserializationException(e);
                    }
                }
            }
        }

其中,按小端字节读取方式如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 /**
     * 从 InputStream 读取内容到 buffer, 直到 buffer 填满
     *
     * @return 如果 InputStream 内容不足以填满 buffer, 则返回 false.
     * @throws IOException 可能抛出的异常
     */
    private static boolean fill(InputStream in, byte[] buffer) throws IOException {
        int length = buffer.length;
        int hasRead = 0;
        while (true) {
            int offset = hasRead;
            int count = length - hasRead;
            int currentRead = in.read(buffer, offset, count);
            if (currentRead >= 0) {
                hasRead += currentRead;
                if (hasRead == length) {
                    return true;
                }
            }
            if (currentRead == -1) {
                return false;
            }
        }
    }

TTS语音播放

TTS完成解析的数据都经由YoutuOpusDecoder类进行播放,此处主要封装了两个功能,第一个功能是封装了AudioTrack播放pcm原始音频,第二个是将解析完成的音频不断送入播放器

完整代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class OpusPlayer {
    private static final String TAG = "OpusPlayer";

    private BlockingQueue<PcmData> mPcmQueue = new LinkedBlockingQueue<>();
    private volatile Thread mPlayThread;
    private int mPcmSampleRate;
    private UncaughtExceptionHandler mUncaughtExceptionHandler;

    public void setUncaughtExceptionHandler(UncaughtExceptionHandler handler) {
        mUncaughtExceptionHandler = handler;
    }

    public void setPcmSampleRate(int pcmSampleRate) {
        mPcmSampleRate = pcmSampleRate;
    }
    

    public void enqueue(PcmData pcmData) {
        mPcmQueue.add(pcmData);

        if (mPlayThread == null) {
            mPlayThread = new Thread(new Runnable() {

                PcmPlayer mPlayer;

                @Override
                public void run() {
                    Log.d(TAG, getThreadLogPrefix() + "start");
                    int playerPrepareFailCount = 0;
                    int playCount = 0;
                    long start = System.currentTimeMillis();

                    while (!Thread.currentThread().isInterrupted()) {
                        
                        //准备播放器
                        boolean isPlayerReady = preparePlayerIfNeeded();
                        if (!isPlayerReady) {
                            releasePlayer();
                            playerPrepareFailCount++;
                            if (playerPrepareFailCount > 5) {
                                releasePlayer();
                                throw new RuntimeException("prepare player fail too many times, abort.");//不再尝试了
                            } else {
                                Log.w(TAG, getThreadLogPrefix() + "prepare player fail, retry.");
                                continue;//再尝试
                            }
                        }

                        //出队
                        PcmData pcmData;
                        try {
                            pcmData = mPcmQueue.take();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                            Log.d(TAG, getThreadLogPrefix() + "force stop");
                            break;
                        }
                        
                        //播放
                        if (pcmData != null) {
                            try {
                                short[] pcm = pcmData.getPcm();
                                if (pcm != null) {
                                    mPlayer.play(pcm);
                                    Log.d(TAG, getThreadLogPrefix() + "play #" + playCount);
                                } else {
                                    Log.d(TAG, getThreadLogPrefix() + "play #" + playCount + " fail, pcm == null !!");
                                }
                                if (pcmData.isLastOne()) {
                                    Log.d(TAG, getThreadLogPrefix() + "finish all task, will stop");
                                    break;
                                }
                                playCount++;
                            } catch (AudioTrackException e) {
                                e.printStackTrace();
                                releasePlayer();//下一个循环会尝试重新初始化 player
                            }
                        } else {
                            Log.w(TAG, getThreadLogPrefix() + "mPcmQueue.take() == null, nothing to play");
                        }
                    }

                    releasePlayer();
                    long time = System.currentTimeMillis() - start;
                    Log.d(TAG, getThreadLogPrefix() + "stop, ran " + time + " ms");
                }

                /**
                 * @return true: player is ready
                 */
                boolean preparePlayerIfNeeded() {
                    if (mPlayer == null) {
                        mPlayer = new PcmPlayer();
                        try {
                            mPlayer.prepare(AudioManager.STREAM_MUSIC, mPcmSampleRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
                        } catch (AudioTrackException e) {
                            e.printStackTrace();
                            releasePlayer();
                        }
                    }
                    return mPlayer != null;
                }

                void releasePlayer() {
                    if (mPlayer != null) {
                        mPlayer.release();
                        mPlayer = null;
                    }
                }

            });
            mPlayThread.setPriority(Thread.NORM_PRIORITY - 1);//播放耗时最长, 优先级比解码线程低一点, 可以让出多一点时间给解码线程
            mPlayThread.setName(TAG + ".mPlayThread");
            if (mUncaughtExceptionHandler != null) {
                mPlayThread.setUncaughtExceptionHandler(mUncaughtExceptionHandler);
            }
            mPlayThread.start();
        }
    }

    private static String getThreadLogPrefix() {
        Thread currentThread = Thread.currentThread();
        String s = currentThread.getName() + "#" + currentThread.getId() + ": ";
        return s;
    }
    
    public void forceStop() {
        if (mPlayThread != null && !mPlayThread.isInterrupted()) {
            mPlayThread.interrupt();
            mPlayThread = null;
        }
        mPcmQueue.clear();
    }

    public static class PcmData {
        private final short[] mPcm;
        private final boolean mIsLastOne;

        public PcmData(short[] pcm, boolean isLastOne) {
            mPcm = pcm;
            mIsLastOne = isLastOne;
        }

        short[] getPcm() {
            return mPcm;
        }

        boolean isLastOne() {
            return mIsLastOne;
        }
    }


}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
4 条评论
热度
最新
请问 YoutuOpusDecoder 类需要什么jar包
请问 YoutuOpusDecoder 类需要什么jar包
33点赞举报
写错了 应该是opusplayer
写错了 应该是opusplayer
回复回复点赞举报
PcmData类呢,是什么jar
PcmData类呢,是什么jar
回复回复点赞举报
查看全部3条回复
推荐阅读
编辑精选文章
换一批
【玩转腾讯云】【腾讯云语音合成TTS】短视频批量生成器
疫情期间发现一个有趣的现象,有一类短视频父母刷抖音的时候经常会看到,这类视频只有一个或多个简单的背景图片,配合一段文字录音,讲一段新闻、故事、或者鸡汤。我想可能是他们对纯文本或者纯语音的内容都不感兴趣,更容易接受短视频这样简单的内容形式。又想到腾讯云有语音合成的产品,加上ffmpeg等视频处理工具,是不是可以批量生成一些这类短视频呢。
01player
2020/05/04
8K2
【百度AI语音合成】会员到访门店语音提醒
然后就能看到创建完的应用和 APPID、API KEY 以及 Secret KEY了
小帅丶
2021/12/28
7230
【百度AI语音合成】会员到访门店语音提醒
Android 百度语音合成 (含离线、在线、API合成方式,详细步骤+源码)
  我之前写过百度的语音识别,也写过讯飞的语音识别与合成,而有读者看完后说没有百度的语音合成,想在用百度语音识别的同时使用百度的语音合成。所以就有了这篇文章,我的文章也是区别于其他人的文章,所以我有自己的风格。
晨曦_LLW
2021/05/10
3.1K0
Android 百度语音合成 (含离线、在线、API合成方式,详细步骤+源码)
Android 音频PCM数据的采集和播放,读写音频wav文件
本文目的:使用 AudioRecord 和 AudioTrack 完成音频PCM数据的采集和播放,并读写音频wav文件
AnRFDev
2021/02/01
3.9K0
Android多媒体之认识MP3与内置媒体播放(MediaPlayer)
零、前言 作为90后,mp3格式的音乐可谓灵魂之友。 小时候带着耳机,躺在桌子上听歌看月亮心情依稀。 当某个旋律想起,还会不会浮现某个风景,某个人……, 今天全程单曲播放——梁静茹-勇气(献上
张风捷特烈
2019/02/25
2.2K0
Android多媒体之认识MP3与内置媒体播放(MediaPlayer)
腾讯云语音合成TTS试用
随着人工智能技术的飞速发展,人机交互的方式也在不断革新。腾讯云语音合成(TTS)技术,作为AI领域的一项重要应用,正在以前所未有的速度改变我们的生活和工作方式。大家好,我是AI大眼萌,今天就让我们一起探索这项技术的魅力和潜力!
AI大眼萌
2024/06/30
2.5K0
腾讯云语音合成TTS试用
java 执行shell命令及日志收集避坑指南
有时候我们需要调用系统命令执行一些东西,可能是为了方便,也可能是没有办法必须要调用。涉及执行系统命令的东西,则就不能做跨平台了,这和java语言的初衷是相背的。
烂猪皮
2021/01/14
2.8K0
JAVA是HttpURLConnection进行多线程文件下载
在开发中,需要实现一个文件下载的方法,对下载时间有一点要求,对于小文件来说,问题不大,单线程下载既可;
JQ实验室
2022/09/06
6940
Android开发笔记(一百零八)智能语音
如今越来越多的app用到了语音播报功能,例如地图导航、天气预报、文字阅读、口语训练等等。语音技术主要分两块,一块是语音转文字,即语音识别;另一块是文字转语音,即语音合成。 对中文来说,和语音播报相关的一个技术是汉字转拼音,想想看,拼音本身就是音节拼读的标记,每个音节对应一段音频,那么一句的拼音便能用一连串的音频流合成而来。汉字转拼音的说明参见《Android开发笔记(八十三)多语言支持》。 语音合成通常也简称为TTS,即TextToSpeech(从文本到语言)。语音合成技术把文字智能地转化为自然语音流,当然为了避免机械合成的呆板和停顿感,语音引擎还得对语音流进行平滑处理,确保输出的语音音律流畅、感觉自然。
aqi00
2019/01/18
5.9K1
音视频开发之旅(35) -FFmpeg + AudioTrack 实现音频解码和播放
上一篇我们了解了FFmpeg解码流程、关键函数和结构体,实现了视频解码器。这篇我们来实现下音频的解码器。解码流程和视频的基本一致。FFmpeg解码的音频裸数据是PCM格式,android上播放PCM音频数据可以通过AudioTrack和OpenSL ES来实现。
音视频开发之旅
2021/03/02
2K0
音视频开发之旅(35) -FFmpeg + AudioTrack 实现音频解码和播放
android客户端处理音频文件
之前介绍了很多音频知识,最终我们还是希望能够在终端应用到我们的算法,本文主要介绍基础的在Android客户端如何处理我们的音频(wav)格式文件,主要介绍文件的读取,写入和播放。后续再介绍如何进行stft等频域特征提取以及模型的infer方法~
languageX
2021/08/12
1.7K4
【Android 音视频开发打怪升级:FFmpeg音视频编解码篇】四、Android FFmpeg+OpenSL ES音频解码播放
在上篇文章中,详细介绍了 FFmepg 的播放流程,以及抽象了解码流程框架,整合视频和音频解码流程的共同点,形成了 BaseDecoder 类。通过继承 BaseDecoder 实现了视频解码子类 VideoDeocder,并整合到了 Player 中,实现了视频的播放渲染。
开发的猫
2020/05/27
1.7K0
【Android 音视频开发打怪升级:FFmpeg音视频编解码篇】四、Android FFmpeg+OpenSL ES音频解码播放
Unity 接入科大讯飞语音识别及语音合成
首先需要到科大讯飞官网开发者控制台创建一个应用,创建成功后获得服务接口认证信息,我们只需用到其中的AppID。
CoderZ
2022/08/29
4K1
Unity 接入科大讯飞语音识别及语音合成
基于RTMP的智慧数字人|AI数字人传输技术方案探讨
随着智慧数字人、AI数字人的兴起,越来越多的公司着手构建​全息、真实感数字角色等技术合成的数字仿真人虚拟形象,通过“虚拟形象+语音交互(T-T-S、ASR)+自然语言理解(NLU)+深度学习”,构建适用于数字客服、虚拟展厅讲解、 智慧城市、智慧医疗、智慧教育等场景,通过人机可视化语音交互,释放人员基础劳动力,降低运营成本,提升智慧交互体验。​
音视频牛哥
2022/03/26
4950
Android音频播放(本地/网络)绘制数据波形,根据特征有节奏的改变颜色
上一期刚刚掀完桌子没多久<a href="http://www.jianshu.com/p/2448e2903b07">《Android MP3录制,波形显示,音频权限兼容与播放》</a>,就有小伙伴问我:“一个音频的网络地址,如何根据这个获取它的波形图?”··· WTF(ノಠ益ಠ)ノ彡┻━┻,那一瞬间那是热泪盈眶啊,为什么我就没想到呢···反正肯定不是为了再水一篇文章就对了<( ̄︶ ̄)>。 </p> <a href="https://github.com/CarGuo/RecordWave">我是DEMO
GSYTech
2018/08/22
3.7K0
Android音频播放(本地/网络)绘制数据波形,根据特征有节奏的改变颜色
Android平台基于RTMP或RTSP的一对一音视频互动技术方案探讨
随着智能门禁等物联网产品的普及,越来越多的开发者对音视频互动体验提出了更高的要求。目前市面上大多一对一互动都是基于WebRTC,优点不再赘述,我们这里先说说可能需要面临的问题:WebRTC的服务器部署非常复杂,可以私有部署,但是非常复杂。传输基于UDP,很难保证传输质量,由于UDP是不可靠的传输协议,在复杂的公网网络环境下,各种突发流量、偶尔的传输错误、网络抖动、超时等等都会引起丢包异常,都会在一定程度上影响音视频通信的质量,难以应对复杂的互联网环境,如跨区跨运营商、低带宽、高丢包等场景,行话说的好:从demo到实用,中间还差1万个WebRTC。
音视频牛哥
2021/11/22
7030
应用自动更新封装-Android
应用更新应该是现在每个应用必备的一个功能。正是通过不断的更新,不断的调优,才使我们的应用更完善。当然在各大应用市场中,它们已经帮我们实现了这项功能,但是有一个问题,当我们的应用是在某度市场下载的应用,如果那天我们不在使用某度市场,而是用别的市场,之前的发布的市场无法通知我们的应用,那么是不是我们就无法更新了。所以封装一个自己的应用自动更新还是比较有必要的。那么今天我们就来学习一下,如何封装自己的应用自动更新功能。
g小志
2018/09/11
7940
应用自动更新封装-Android
Android开发笔记(六十三)HTTP访问的通信方式
输入输出流在java中很常用,从文件读写到内存读写到网络通信都会用到。在之前的《Android开发笔记(三十三)文本文件和图片文件的读写》中,我们学习了文件流FileOutputStream和FileInputStream,以及缓存流BufferedOutputStream和BufferedInputStream。这些输入输出流都继承自InputStream和OutputStream,下面是它们的常用方法: InputStream的常用方法 available : 获取输入流的大小 read : 从输入流中读取数据 close : 关闭输入流 OutputStream的常用方法 write : 往输出流写数据 flush : 刷新输出流 close : 关闭输出流 java在进行http访问操作时,发送数据使用OutputStream,接收数据使用InputStream。如果采用HttpURLConnection,InputStream对象可从HttpURLConnection的getInputStream方法获得;如果采用HttpClient,InputStream对象可从HttpEntity的getContent方法获得。下面是http访问时与InputStream有关的加工操作: 1、从InputStream对象中读取字符串。首先把输入流的数据读到字节流ByteArrayOutputStream,然后调用字节流的toByteArray方法得到字节数组,最后调用String的构造函数根据指定编码从字节数组构造返回字符串; 2、从InputStream对象中读取图像。调用BitmapFactory的decodeStream方法即可返回Bitmap图像数据。 3、从InputStream对象中解压gzip压缩数据。引入GZIPInputStream从输入流构造解压流,然后再从解压流中读取数据。
aqi00
2019/01/18
1.3K0
Android平台GB28181设备接入端对接编码前后音视频源类型浅析
今天主要对Android平台GB28181设备接入模块支持的接入数据类型,做个简单的汇总:
音视频牛哥
2022/09/05
4740
Android多媒体之认识声音、录音与播放(PCM)
一、对声音的简单认识 1、模拟信号[摘录于此] 模拟信号传输过程中就是利用传感器把各种自然界各种连续的信号转换为几乎一模一样的电信号。 比如说话声音,原本是声带的震动。经过麦克风的采集,将声波信号转换
张风捷特烈
2019/01/07
3.8K1
推荐阅读
相关推荐
【玩转腾讯云】【腾讯云语音合成TTS】短视频批量生成器
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验