如何将Alexa语音服务集成到自定义Android应用程序

时间:2018-05-25 03:10:18

标签: android kotlin alexa

我计划开发一个与Alexa语音服务集成的Android应用程序来开发像Reverb这样的应用程序。以下是我的尝试......

  1. 经过检查的AVS设备SDK,可以获得在Android中实施它的正确指南。

  2. 已检查https://github.com/willblaschko/AlexaAndroid,无法使其正常工作。

  3. 计划实施自己,下面就是我所做的。

    一个。集成登录框架,能够成功登录并获取令牌。

    湾创建了录音机,能够在本地录制和播放。

    ℃。创建了将音频发送到https://avs-alexa-eu.amazon.com/v20160207/events

  4. 的请求

    [增订]

    将shortArray更改为Byte数组后我得到了响应,但现在问题是MediaPlayer无法播放响应mp3,在准备时出错

    package com.example.anoopmohanan.alexaandroid
    
    import android.content.Context
    import android.media.*
    import android.media.AudioFormat.ENCODING_PCM_16BIT
    import android.media.AudioFormat.CHANNEL_CONFIGURATION_MONO
    import android.os.Environment
    import android.os.Environment.getExternalStorageDirectory
    import java.io.*
    import com.example.anoopmohanan.alexaandroid.ResponseParser.getBoundary
    import okhttp3.*
    import org.jetbrains.anko.doAsync
    import org.json.JSONObject
    import java.nio.file.Files.exists
    import okhttp3.OkHttpClient
    import java.net.HttpURLConnection
    import android.os.Looper
    import android.os.PowerManager
    import android.util.Log
    import okio.BufferedSink
    import okhttp3.RequestBody
    import org.apache.commons.io.FileUtils
    import org.jetbrains.anko.Android
    import org.jetbrains.anko.runOnUiThread
    import org.jetbrains.anko.toast
    import java.util.*
    import okhttp3.ResponseBody
    import okio.Buffer
    import com.example.anoopmohanan.alexaandroid.SoundRecorder.LoggingInterceptor
    import android.media.MediaDataSource
    
    
    
    
    
    
    class SoundRecorder(context: Context) {
    
        private var appcontext: Context? = null
        private var recording = false
        val MEDIA_JSON = MediaType.parse("application/json; charset=utf-8")
        val MEDIA_TYPE_AUDIO = MediaType.parse("application/octet-stream")
        var accessToken = ""
        private var mediaPlayer: MediaPlayer? = null
        private var streamToSend:ByteArray? = null
        private val client = OkHttpClient.Builder()
                .addInterceptor(LoggingInterceptor())
                .build()
    
        init {
            this.appcontext = context
    
        }
        fun startRecording(accessToken: String){
            this.accessToken = accessToken
            doAsync {
                startRecord()
            }
    
        }
    
        fun stopRecording(){
    
            doAsync {
                stopRecord()
            }
    
        }
    
        fun playRecording(){
    
            doAsync {
                playRecord()
            }
    
        }
        private fun stopRecord(){
    
            recording = false
            //val file = File(Environment.getExternalStorageDirectory(), "test.pcm")
            //sendAuio(file)
        }
    
        fun playRecord() {
    
            val file = File(Environment.getExternalStorageDirectory(), "speech2.mp3")
    
                val mplayer = MediaPlayer()
                mplayer.setDataSource(file.path)
                mplayer.prepare()
                mplayer.start()
    //        val file = File(Environment.getExternalStorageDirectory(), "test.pcm")
    //
    //        val shortSizeInBytes = java.lang.Short.SIZE / java.lang.Byte.SIZE
    //
    //        val bufferSizeInBytes = (file.length() / shortSizeInBytes).toInt()
    //        val audioData = ByteArray(bufferSizeInBytes)
    //
    //        try {
    //            val inputStream = FileInputStream(file)
    //            val bufferedInputStream = BufferedInputStream(inputStream)
    //            val dataInputStream = DataInputStream(bufferedInputStream)
    //
    //            var i = 0
    //            while (dataInputStream.available() > 0) {
    //                audioData[i] = dataInputStream.readByte()
    //                i++
    //            }
    //
    //            dataInputStream.close()
    //
    //            val audioTrack = AudioTrack.Builder()
    //                    .setAudioAttributes(AudioAttributes.Builder()
    //                            .setUsage(AudioAttributes.USAGE_MEDIA)
    //                            .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
    //                            .build())
    //                    .setAudioFormat(AudioFormat.Builder()
    //                            .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
    //                            .setSampleRate(16000)
    //                            .setChannelMask(AudioFormat.CHANNEL_OUT_MONO).build())
    //                    .setBufferSizeInBytes(bufferSizeInBytes)
    //                    .setTransferMode(AudioTrack.MODE_STREAM)
    //                    .build()
    //
    //            audioTrack.play()
    //            audioTrack.write(audioData, 0, bufferSizeInBytes)
    //
    //
    //        } catch (e: FileNotFoundException) {
    //            e.printStackTrace()
    //        } catch (e: IOException) {
    //            e.printStackTrace()
    //        }
    
        }
    
    
    
        private fun startRecord() {
    
            val file = File(Environment.getExternalStorageDirectory(), "test.pcm")
    
            try {
                file.createNewFile()
    
                val outputStream = FileOutputStream(file)
                val bufferedOutputStream = BufferedOutputStream(outputStream)
                val dataOutputStream = DataOutputStream(bufferedOutputStream)
    
                val minBufferSize = AudioRecord.getMinBufferSize(16000,
                        AudioFormat.CHANNEL_IN_MONO,
                        AudioFormat.ENCODING_PCM_16BIT)
    
    
                val audioData = ByteArray(minBufferSize)
    
                val audioRecord = AudioRecord(MediaRecorder.AudioSource.MIC,
                        16000,
                        AudioFormat.CHANNEL_IN_MONO,
                        AudioFormat.ENCODING_PCM_16BIT,
                        800)
                if (audioRecord.recordingState != AudioRecord.RECORDSTATE_STOPPED){
    
                    this.appcontext?.runOnUiThread {
                        toast("No recording source available")
                    }
    
                    return
                }
                recording = true
                audioRecord.startRecording()
                if (audioRecord.recordingState != AudioRecord.RECORDSTATE_RECORDING){
    
                    this.appcontext?.runOnUiThread {
                        toast("Someone is still recording")
                    }
                    recording = false
                    audioRecord.stop()
                    audioRecord.release()
                    return
                }
    
                this.appcontext?.runOnUiThread {
                    toast("Recording started hurray")
                }
                while (recording) {
                    audioRecord.read(audioData, 0, minBufferSize)
                    dataOutputStream.write(audioData,0,minBufferSize)
    //                for (i in 0 until numberOfShort) {
    //                    dataOutputStream.writeShort(audioData[i].toInt())
    //                }
                }
    
                audioRecord.stop()
                audioRecord.release()
                dataOutputStream.close()
                sendAuio(file)
    
            } catch (e: IOException) {
    
                e.printStackTrace()
            }
    
        }
    
        fun sendAuio(audio: File){
    
            streamToSend = audio.readBytes()
            val requestBody = MultipartBody.Builder()
                    .setType(MultipartBody.FORM)
                    .addFormDataPart("metadata","metadata",RequestBody.create(MEDIA_JSON, generateSpeechMetadata()))
                    .addFormDataPart("audio", "test.pcm",
                            RequestBody.create(MEDIA_TYPE_AUDIO, streamToSend))
                    .build()
    
            val request = Request.Builder()
                    .url("https://avs-alexa-eu.amazon.com/v20160207/events")
                    .addHeader("Authorization","Bearer $accessToken")
                    .post(requestBody)
                    .build()
            print (request.body().toString())
            client.newCall(request).execute().use({ response ->
                if (!response.isSuccessful()) throw IOException("Unexpected code $response")
    
                val items = if (response.code() == HttpURLConnection.HTTP_NO_CONTENT)
                    AvsResponse()
                else
                    ResponseParser.parseResponse(response.body()!!.byteStream(), getBoundary(response))
                if (items.size > 0){
    
                    handle(items)
                }
                System.out.println("[TRACE]"+response.body()!!.string())
            })
        }
    
        @Throws(IOException::class)
        fun toByteArray(`in`: InputStream): ByteArray {
            val out = ByteArrayOutputStream()
            var read = 0
            val buffer = ByteArray(1024)
            while (read != -1) {
                read = `in`.read(buffer)
                if (read != -1)
                    out.write(buffer, 0, read)
            }
            out.close()
            return out.toByteArray()
        }
        private fun generateSpeechMetadata(): String {
            val messageId = UUID.randomUUID().toString();
            val dialogId = UUID.randomUUID().toString();
    
            return  "{\"event\": {\"header\": {\"namespace\": \"SpeechRecognizer\",\"name\": \"Recognize\",\"messageId\": \"$messageId\",\"dialogRequestId\": \"$dialogId\"},\"payload\": {\"profile\": \"CLOSE_TALK\", \"format\": \"AUDIO_L16_RATE_16000_CHANNELS_1\"}},\"context\": [{\"header\": {\"namespace\": \"AudioPlayer\",\"name\": \"PlaybackState\"},\"payload\": {\"token\": \"\",\"offsetInMilliseconds\": 0,\"playerActivity\": \"FINISHED\"}}, {\"header\": {\"namespace\": \"SpeechSynthesizer\",\"name\": \"SpeechState\"},\"payload\": {\"token\": \"\",\"offsetInMilliseconds\": 0,\"playerActivity\": \"FINISHED\"}}, { \"header\" : { \"namespace\" : \"Alerts\", \"name\" : \"AlertsState\" }, \"payload\" : { \"allAlerts\" : [ ], \"activeAlerts\" : [ ] } }, {\"header\": {\"namespace\": \"Speaker\",\"name\": \"VolumeState\"},\"payload\": {\"volume\": 25,\"muted\": false}}]}"
    //        return "{\n" +
    //                "\"messageHeader\": {\n" +
    //                "\"deviceContext\": [\n" +
    //                "{\n" +
    //                "\"name\": \"playbackState\",\n" +
    //                "\"namespace\": \"AudioPlayer\",\n" +
    //                "\"payload\": {\n" +
    //                "\"streamId\": \"\",\n" +
    //                "\"offsetInMilliseconds\": \"\",\n" +
    //                "\"playerActivity\": \"IDLE\"\n" +
    //                "}\n" +
    //                "}\n" +
    //                "]\n" +
    //                "},\n" +
    //                "\"messageBody\": {\n" +
    //                "\"profile\": \"doppler-scone\",\n" +
    //                "\"locale\": \"en-us\",\n" +
    //                "\"format\": \"audio/L16; rate=16000; channels=1\"\n" +
    //                "}\n" +
    //                "}"
        }
    
    
        private val audioRequestBody = object : RequestBody() {
            override fun contentType(): MediaType? {
                return MediaType.parse("application/octet-stream")
            }
    
            @Throws(IOException::class)
            override fun writeTo(sink: BufferedSink?) {
                //while our recorder is not null and it is still recording, keep writing to POST data
                sink!!.write(streamToSend)
            }
        }
    
        inner class LoggingInterceptor : Interceptor {
            @Throws(IOException::class)
            override fun intercept(chain: Interceptor.Chain): Response {
                val request = chain.request()
    
                val t1 = System.nanoTime()
                Log.d("OkHttp", String.format("--> Sending request %s on %s%n%s", request.url(), chain.connection(), request.headers()))
    
                val requestBuffer = Buffer()
                request.body()?.writeTo(requestBuffer)
                Log.d("OkHttp", requestBuffer.readUtf8())
    
                val response = chain.proceed(request)
    
                val t2 = System.nanoTime()
                Log.d("OkHttp", String.format("<-- Received response for %s in %.1fms%n%s", response.request().url(), (t2 - t1) / 1e6, response.headers()))
    
                val contentType = response.body()?.contentType()
                val content = response.body()?.string()
                Log.d("OkHttp", content)
    
                val wrappedBody = ResponseBody.create(contentType, content)
                return response.newBuilder().body(wrappedBody).build()
            }
        }
    
    
        private fun getMediaPlayer(): MediaPlayer? {
            if (mediaPlayer == null) {
                mediaPlayer = MediaPlayer()
            }
            return mediaPlayer
        }
    
        fun handle(items:AvsResponse){
    
            for (item in items){
                handle(item)
            }
        }
        fun handle(item: AvsItem){
    
    
            if (item is AvsSpeakItem){
    
            }else{
                return
            }
            //cast our item for easy access
            //write out our raw audio data to a file
            val path = File(Environment.getExternalStorageDirectory(), "speech.mp3")
    
            //path.deleteOnExit()
            //val path = File(appcontext!!.getCacheDir(), System.currentTimeMillis().toString() + ".mp3")
            //var fos: FileOutputStream? = null
            try {
    //            fos = FileOutputStream(path)
    //            fos!!.write(item.audio)
    //            fos.close()
    
                path.createNewFile()
                path.writeBytes(item.audio)
    //            var ds = ByteArrayMediaDataSource(item.audio)
    //            val fis = FileInputStream(path)
    //            //play our newly-written file
    //            val mplayer = MediaPlayer()
    //            mplayer.setDataSource(path.path)
    //            mplayer.prepare()
    //            mplayer.start()
    //            getMediaPlayer()?.setDataSource(fis.fd)
    //            getMediaPlayer()?.prepare()
    //            getMediaPlayer()?.start()
            } catch (e: IOException) {
                e.printStackTrace()
    
            } catch (e: IllegalStateException) {
                e.printStackTrace()
            }
    
        }
    
    
        inner class ByteArrayMediaDataSource(private val data: ByteArray?) : MediaDataSource() {
    
            init {
                assert(data != null)
            }
    
            @Throws(IOException::class)
            override fun readAt(position: Long, buffer: ByteArray, offset: Int, size: Int): Int {
                System.arraycopy(data, position.toInt(), buffer, offset, size)
                return size
            }
    
            @Throws(IOException::class)
            override fun getSize(): Long {
                return data?.size?.toLong()!!
            }
    
            @Throws(IOException::class)
            override fun close() {
                // Nothing to do here
            }
        }
    }
    

    来自ALEXA的回应

    --------abcde123
    Content-Type: application/json; charset=UTF-8
    
    {"directive":{"header":{"namespace":"SpeechSynthesizer","name":"Speak","messageId":"d58c83fe-377f-4d1d-851b-a68cf5686280","dialogRequestId":"d83b8496-e6a5-4fc5-b07b-32f70acd1f15"},"payload":{"url":"cid:2aed5305-081d-4624-b2ba-ef51eba6aa32_1353236331","format":"AUDIO_MPEG","token":"amzn1.as-ct.v1.Domain:Application:Knowledge#ACRI#2aed5305-081d-4624-b2ba-ef51eba6aa32"}}}
    --------abcde123
    Content-ID: <2aed5305-081d-4624-b2ba-ef51eba6aa32_1353236331>
    Content-Type: application/octet-stream
    
    ID3#TSSELavf57.71.100dUC\[QwwD뻝q    \[D*An. .`>X>>@|L?&|>4=A?w'<<Ɨd"_h{[M̢8j8ǙbA
    
    @=$T2bX2LM~NBPp˧Eh$`hoV
    
    ԁh3}ɧ2/_74g挺`;*
    W`dq՗HY)@mN<fG?l74(
        ˚z
    BD)jy7uӹ\ۭc|>)qcsZQN} QppҜ{_]>iWw$Yd!.VEi]
    <ƹYK.l6$̄)uLPGⰞRTFK>cr{\UK.|-EWGWȍ{3UN6]1tX@jr`5ka5/d V
    WzLٺt?Aɬg%Я;la N%
    A`r:ۙJSD3\´P y%eWOG5As
    ۪<*qfom
    d !JjadR"=2vg?ZzrKZrV*Y2ش!XFOI EP99
    Fdmy;ꡐ^JJ5С/\   _-LuafVMFZT-TUadRwO(A.2>sʳ!Y&)h`x<RR<AC|pTi"`k9#ɱX*%AP0CR7u+<VmFq ,ł)EhH굫.<hd#%[6h$kRO'IZ.VMX>!fqi+`:'j-Z6X *C
    0S'9)yd&ĽX+)d Ӳ@Xz3 M xNgV9Vc׿?:ot\w}&dZk)b.`C$w1*\y?O՗ql\6d\&R=bcQt]
    r*U{ztUT-| b%BN'<^潦P2Dtc1dھ]KN!*gxxv[dp0 ЈaR'id \@G_=f:fř6~pdcg.k/_E0lY
    (XvoR.w*0U>/9_`ra1ANo^8&Ո{2d5Y{қ|Xo6`J|   !mcx".~_Da_,êJgt7,xkdO,Ӭ4e 葅*J
    wd0FsKb[g#1TN*ydJBhJ    .p}䁤`%JHj~"kJl`P_Q#&6_F!Ίrw|Ȇ%vs 0$F    P8n*c#eWO@  dEM/dZY4N[j\Y]t~b3ݚKJo4Qff.|RSxLVo%$ߟ;I"XjRRC׾YK#PĹ?&w3B\4 D@\Jη!dk>Do
    C5 0*6G7`Y4םke.Sv(J SIH$&pf|fP!rې3  %o@%gN`cM,E/}PV*(d}aڠNц3!qɿhinvhֹ`,+?_hEAi*Uu)LB)̈j{Rs$yTYQ*)_
    ]M4Pa6dĒLJyw*)3x÷/@s.Æ
    (J%{iwhNi^hAyW+į8SɜJg )%8Wa6uaZ gZT   vWUdğד]:C
    '52CON.kYp,c9HO#JZ3T|{8+Q䫚YYkytơʫ6tݼt$V@z9O*7`BqdįʸT?Tc=YP&c:PLB~Ȧ&3r*kg9CY$pJMN鱆cz,eM0V!I7uD8_c9odĿKɖy3|Z+r`w-{c
    ww*nej5h8DLHO-
    }g['cT
    U :$5WTGVNo+ٚ@v7j-!G[ZOdondê099E     ڀn҂c@R4&@fEkSvGxժI+7Jѐ>L)u8:
    Fv*$ܷ{s4ğ7! ddҤ6
    \a`dvFSeT#"uVVhbԧRarEr\]C(rMfarw\j7n[Xon*1x i
    J?mL0Kh.Rsd&CtVWxxny:fN
    ǃ>T
    .)":`pUÂȆ^#z@1·fRu:;KN
    sN< |zXqs<nWP&O3qēXx-FTd,јϐxE$LUf{%dHĤK4RX[9g^fY7"הv6
    #t*u-#PhySi犂>v@KDan
    F[0pxԐÍL)1YlG
    ÄdĨ#1i.%u7_shnA A,G)KJL8HEgc_X}Sr6E;#_%9@!
    aSi@5dF E#'Z=&XrPMH4<f7/T"vdĞ!`JFL0ʊS2TKαYiFgƞ`C'?(Lt   bW$"ᾇ0V5)ǗqWX}(-H*=[bi3(ܷdĚ!SJɎtoZd`$c("2ݩEUSJjzBׂLߍA%a@d'dZ(ʺ?j}\C;8 gjV
    oz3/`dĠN`dė^L)Xc 8, 7>xzMTT| ZRBe#M7X7"(8n2f    Nc@AJ3" 0A
    @   3qxf;CAA7db8#@eps
    EdĬ52_E S0/i 
    [M94<@١n2("6k97L+ƪHSjW]Z6Uoi??3967ī?_K1NDN9dX3<6`>Vn#"jM(^BN  GEc:29.XnOB(3qNyjH$~s˧~8ԯ&ˬ<\(.:s%5(p3:yt
    e^;DGY'd~T1GP1kkDs#bj /R!r0PU
    fRϪ\)p٢4l*%:H'zmᠯ<`NjNe%l\y2k(7Rm%fDd⺲T0G%z;*&e_2֏SLZvv$_զ76!#r?mc2Up]gU;zgǝCTY jēnJ    BQ,$@ŶZxr$Q,.d+9VI\^)[pPXh*@z8vYáY"$Ǹ*
    üK;㡯5*ƈ#A`sJ*]_"M=L\ǖ֗d'×^y2uR[DdA!`
    aLQ$HQJOe,ڰP*x=]J%. "!hI&NX2T>rp4\0P%*D(wT%~nZLAMdW~@I\E3.99.5LAME3.99.5UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUd|HUUUUUUULAME3.99.5UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUd|HUUUUUUULAME3.99.5UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUd|HUUUUUUULAME3.99.5UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUd|HUUUUUUULAME3.99.5UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUd|HUUUUUUULAME3.99.5UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUd|HUUUUUUULAME3.99.5UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUd|HUUUUUUULAME3.99.5UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUd|HUUUUUUULAME3.99.5UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUd|HUUUUUUULAME3.99.5UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUd|HUUUUUUULAME3.99.5UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUd|HUUUUUUULAME3.99.5UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUd|HUUUUUUULAME3.99.5UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUd|HUUUUUUU0aAۇL>!*d2XqD ` 1 @l|9P $+t0\F f0N_6b18a`1HT:a`d|H@wp@0
    0:pLOwfĨ.fV\O`,1qMLKBȰ@-    LJ91G{`/#UT1FSj-m3C}bSd99AU
    3#^d0]sP3ƀDVd1G8Na&a?(vfPc|\hQMdqD0iA   yQH 6DRH\f*0BO
    ܾhl\ERdĚ5k_*]eJ,e@0h(:Ko&ɲi&QU>
    Zn#ET&I]"r[SScv#yQG#ڭ% XiOiȵ4tjyѵdf{dG_8Ϧ˪Os1Ӫ~b}=@
    uEH͋(|a}%ڮ[lr\ʢobx
    Ç~>
    [2MwۙrjdW>N<8#ZT})1sB
    zȀs뫳AAsK#\R:Zh"BOR¯bw..\jk!,G6Ʉ%Bbh+|ۚfsN\8`\ЖL cCdh.t9xڽww1LeJ-OgBݑCH2(D`3S c...DaD4>-5i;t8cmr7`o-А    aI~_*d1${ڴE0jpA?D1ypr|H|JAQ˲8-2;[J22Ȁݝu|;DdE}^$C)   87ԬfS!nA{o1BJEId" 6E!ܻǜ2VwnqQy90I(Цa3]]]t0 _NgWI
    eV3khuv[OcGyEMYP1중ƻր)~= &]d!>k
    ߟ$Ѻ>
    YaH
    o>G]-կW+?fԬok!DXY,I(SrFH@$Ʌ2:2\Ŵ&(
    <K2d4>*6l+_~5M
    AAy'X84wSYƍ brTrʈ
    eMw7N@ƭV]o!{ǹL"b]Bρh16dDPRJS_\n6aOPi,zL
    :;%P5(ۍ9/   6qoD֏X01}p\u@TIKc`,R+L$YLOdS2^xľw8ɯiKR/QDsf}zo{MA%  gbU\}
    Ⴠʨ-':@qt>$(QM{Hɱ"]<~O_^dc>zGHX].S
    ۘ%VFr(@Q,p@mSfO{u9 *}u}ִOt#ws>qd$Ttޢ,(ld{TɊ*kJ3r_RU˶6~*^ba..-:C٦]}?*6O*8WBFlHU1.st/dČ\
    Ix  )v1S3m{P.:6\>Q2t̰kQ6ȿ[8tˎ!ڑ*]O.''#vZZZ,In>5u*P*pNc0Wbi(
    c4  iCdĕ
    
    ž8X6O5!HH%TGZDoYJ_r
    -7{
    .D2YaWoU]JG+d;*5A.{9S`@VX
    8cLML?dģ!#]zYyAHLH:VؠT<jN!YSX]E`Y־WzP>̺>ӿr'V+`;pJ)A6mA@RJ&Qdġ!W[@"Cn
    X&X4"To;2gr3m#0iȖ
    Ϙ*ǚִn|?^$y<1^w-R3 ,7ASFqKCdWOWUrdĝ5Mx)O>>7
    ŷ3}_5ky+1yC7Y^qD\<<9l@5K("YʕItj"M51ĐfbtL8nxAdH,t:hOG_AJ$.Bj^j#-yL)#!,
    rD>VGVvL-k&/Z̒OJMK&f|ţ֟P$G"g   }Q
    Idƾ~\C1Xөbfd8&u>nXAGۨO`CDrTPE)B>_OE-ݤ ?jd&^oW$Q;Wmad C~OBOazRp0TF0}޿Eҳ'ݷduĎQs9ḨawWH]dRXԬ~ZXH%MKg)DRd2ܲ)~o:ԷwFld"sl$DLSR?ӷ
    YdI8    U8%sY %~p̎]kYSkz-1@pMܟډ9$䳳To4d@bdm[0dϺjjy(תj^_6ݷؤĮ
     "_F(]$P0P@U/fFȠv68%atvaː^@=80pse$]L%@ 1dM/:`lTږI\
    hNhv
    L`-4/oִWTʂ"ȧ~p($':|0403['4yP5*=zi֕@Wf\ysacd!#6+@Y\qoU,\|pa9aQ<M ѱkXCmuk$EqAP8$#ozo׋Zo׋&8yOohHiVq9dz~(s%WisOPjWIg9/5T,jD-XeQ@H҆!6>LJ%i^zPY|jݢlew<b6@V-K0rՠօLd#ڥtEɟʲ&<ܻN$U-Gӎ?H -/sÀ֑@MjWBi9x%<'Bqb [LP   d2r^HPNB"Q`PPqN.A"R!`AqqsvOSQՃTRMB3M(|&e^L4<6bإA=Qt#1gBP_dC^
    OS;OYȏ4yϿaIJG   g橣vs۩[44&?IGJ%"Lη/;_߶e!Þ_j! YSyQ5VIk @q`dO4;^n  'ÃzL$v"4_I.yKvQɕ㥕Vy'ͱtvb1얰Qp@iң;j}){|n[V%FCf-}r:]G3eG&0PQ
    Ddm+ZY_fwkljbFzGP9Rjr(T :
    V2
    2/nܳPZ\oj>gԲiʆ*~,*)c1RT:Z%qoZvnޞg{e VIdH֐>,B뤋i^v>+vTL#
    C᤬бaXeDM,gGR'_n<|5bH:ࣃ\Un
    Umm^C۩Cj@4e39
    ق2dėɒZy~=LՈ
    ՊC7WQ{+ۤH0\
    e6kΜcGe}K]u^;Tk 6tmbD8sM    Ʒb?
    Wmi 8dĦ%YZȟ``9#(I   xP9,(Lܲy@M2z 
    08bh`C,[ckZv7OU'6:Brm){ׁ,#_dēI>XXO^1JrmLe3I}syL͂%NgFZ{Lؕ}T|xE$]K.U{ !#  3m?]lGjV{0Ɏ|\w-W!3ݠ]qoXdĨq6Dc)B_ݕfK<U&2nYݵ<",XQ*b8w%&:&X5IesLBsh{IAC0ݲ⯩^    ̨0:/rpcdĽ>348D;r؍ӯ*\s0}B%   PyT6n*y(MUuj$1?=\)Ît^q,gg1l)d"KC 8%s(E2 ʁF t2!<:
    :0<k¤5\!JSv ΅JDI$0ŤSIB!7_'"k"XHAyԪU4Qh^<=Od"{>{;ُO>Da Ly7\F
    z`A!2Is ޿s6 ՌHq;"r>QH-Fz)Q|B1t߬*_)Ld%c:L    J^  ۧ+65d
    a|:Ppr]is2=V;nfDkŮdh\%h%@!0`vcJͲ?cj_d{ݞyB^o2q\:G
    )E0)иzH_Ff(=Z1<Â/òPm[@8!(NCViE@6+kXmΙ*SL7
    ƵUi_S?+D5sd!ɅLd
    a3m6    
    hDdj$؉տ}/g#Z
    C測AC"
    (-13~71&8I*Hh.ͬY4td\
    Z@$$"]MMv,7{zbToShI&&)%uvmk
    n"35j_KO֖Cfwf;C) dR&d=k6rKu3s2%Cȝ/ɻ>X%·CJ6<
    IXkYj=dN
    CA`hyVX%
    x8Wo MvV͊SD^)W*0V0RXR+dĆ9jOX,tj˚àQ̆iQãAJnV6tΑc
    (Äj~֥_ۛ'9ĬgPL,֯q)e7\PG
    >Ċ@۾6'w}MEdĔJpSL(A=@s`e0pd09<R
    fny5bhO.
    )\!:]"XZge;!/4sD"LSH%ԍQsuNOR]p\M$\bdl67dħ5._ݐZihN uN'fFP
    erE0Im=s
     =rD3SgY,VqVE5u+mc:n
    0<CH Gq-T0XNfbۤMV_QmdS!f؀htYkcd_M*4IP11v8/ՄFA*JNknhc9)J _G
    96ఀܡٌIB`'$AJikӈ5rAjR4dO":f^Bʢ堠xHY?zTюp.>L((h
    
    F@
    08.|@t +sik
    MY&'j`*X'%  (dIE**bDX9i[}Q?Msru^<\3];X7!ʔfR(os,2S is
    ƋUY4"~]t=~YMoyU`GhdYR^˪p"ㄠl2;X8&zwBjJyAwjB^&-D$8gߘpbj["oQٹKjV[t)Bc,mO?սJke
    ciT~ndj>DҶ+`-a[V?+uBrGjKjаw%R?% odxD6[;g+p&/`Tf\S7CULOoҖ    nku+3""J* @s-dyj:~yF%@ADBB"@:o
    _MޔBDsܓ$pc  #;g|F:܊b#Ë;$maZE$jdĊc^C?z?׹BNthH\G#A:@CQ
    H   &gJ
        fmֽUSddsI5k2PUffg-KoRfga{ffݙ_j$dė:h$;L4'ݸpT'<+ܨ\IG-tvǶFq;[JQ}KRZx5h <0`2:|P8[RV#dĥ:2v;pM!9IP&P
    ډsCCSzDIg_hO=9 B co|meb"ϐe)fQuJ7fr]ꚶjQdĵG{%PRA'VJy^;PYD\*
        \31RߘkF`/(  EJfHX\uMNH" xj\9Vϫ9*0:5V2Q\Md2D\Õu,Y)T6;cp4]8\[gE\FF!;uJݼ
    T3%OeńIhտb  +MEDCÿ2_x{^BeدOd9.
    fMHS?]+˼ϙ`,y
    !{OC5*(i:V.f{9M\}@?I7#Nݸzx<X`襳9;Fa(*d"HHD%(,(F8*H廑S/L!
    GS܋ 8h69[>!r!Т$H(V<S.Z%'    I
    ;<87)֊
    dI6F(P{')?nQ<( P+b
    huiUDǕCG@b  }p1ކ/C?iuc/}NuY6TףsH1ů'$2ʓpB+f&Q7tReZVd!Vw׫:!LfH,q-*i"bA
    GEەI:IK.Qңs_n׹[yzWv5B>V˗+ašd V,|;ݙJ)XFCX4;-PZCƁ%
    E@+4G<vExlj מ%?M8poz9[GVr%S7_?d +DؘBc;YXC^ei$`hYR<eX7
    d*dm5g>uPzlROtZ ]r  Җ}BIF̼SR4OԽd ╖ʸ6#3)}KF?BΨa1+UGejLxuPH Lem#mUWKsN{7BexhxuxULq})^iNO"oD,[=6nY_d ]J\'ZߔtX|ONYGaHNa*#-HwAVŷזAysR^ISׯOon1V(cyF6"ڬ5~C]dz_)jϋ#Kd .6X_Qɬn7u3NF_gIۻK$g=4;0t
    J9gˎa:!X`D*J&w.S*5EZHdsw"`W9d-:ɞ
    Zw}$PqbYįb<U9T(!Llȴͧ]{4sP:vԅ!~1YRa]+Ri@T>]0mHI]dĴ'|V
    jRq%
    vpbAr}4&ىg'X:40KbIh瑏XA"}F9b?̀*8T,@Ta$,Ya"h_dę$#
    ~EHkv1]֥ӱ1yZxXqy-4֏K:Ճ:TNDP{ScR,QF"'F`.R *RATsq܀d±ݬdċ!+^lZ;:T,1j
    f3V˹ݻ?YuW>5(Jrf3tABG$Ot"Ψo̴3d-0#t{oJ *7mq37<0Ddĉ>M#OҬ4I6aNTyTsWi[!̥d,oYJL]A\; Ti_5$Ua!'L]@r57,yi4"hoMuӵ(_?
    @P9gdĝzD@5^X=v;jD0k-XΏF&gR|3[_׼MYnRVន%Es?e#܅BiSXWo(qlܖQmukݙBU^  "+10dĪXC{BUf'*']REJ,]WX~[-hH2
    C@\ꄶ&@i{0a1nC98G.>dĹX]X*#:P|O<lԴW|
    
    Pe%r!A<r|f      1,B`H, 8FLAMu9udC_[d<bUdҊ豋d5:_*)ZFInqՁFM$L-ɀ7
    LՑ}>+<?H/dӠrJdDj/jZ~s2^͉u>.[5qWM zvXu*sE&&B0VͯoZ'KABr#dwن0/7"qǚ0K`9kft));9V侗'7
    [d*2H3MS{;&hx蟑=
    2%'^٢,c.!Y1bj6[dĆD*6O}:hT#XX>KzĹV
    &?N e8 %
    QtcR-s__+"!ùۅJ^N]Q=M2Ѥu-ZpbdČ#D6R+`4ڷ ʛ+,P|GO+
    dEL1\~+F}*C#L
    q3
    :
    &*(?cPƘ&s,b=gQ5O$ny% }kh1dĂ!^|Ka5WC-n-HK5g55&
    Rnǝ'I,!̡Ӈg]rەzKB?#@fwǀaXg15)/dNDVcbW0jח4c_Ϭ{G)x+[gHʝ
    HB
    @^7"@wx{bkSt1X#.?@`a[\9{/ 9#T;)mS!Ⱥj$TTXdē6xzs۹&0Eg4VvTkн=c@lj9ڢ2OwH3X.c]IFKk것³΋-[B.*P;dĤĢ)ZEs]IS_5hC_][
    ?1){܊E4:9F<MvsU˹X#/+R()Šq:F_9Ns2eCD\IRdj|$DĐQOdĴN
    
    :c)>Tm}ۦWov{zU)Y|twQmxߨj>GeRyl1u"</wbr2--*o*hs(Scь
    [:<_}[dľ#y9oVv4V
    fKM@P82VuX}Vh8FaUD$v0ceAT>WNՂyg3=ncu<T*
    D@֠ds6
    D,
    N2VY*yen5Xt"(1HAar%*LMzDfW99FCnRAqzp`hÊxA1C ѫViAqd޷H̼Gp^_N+NUxvX;ҫJALb92ܵhgo>fYNj5={Z3ӉbYw&PEI(\6o5D2d IaIvgrQs~by!")|Di:RdžlE%s#|E@\9d3H3zfPhlsL&et3d)D3q"Z*GVHܖf(P98@z A`b|(0| H;-}!!df>܅rNV1jV7dN,;*d!xöޕy{ }QCS"21jP:FvVSEnjyۘ/%h/MpuzϚ0}n`WʁlVv@\qoO͏5+9߉e`4$WPՅd لm&óf-(}n
    ;CRȶu| 1xٚx-T^dBQ=k
    #,:*]~L"8\OzeLBN{
    H^EM]bV FoʸAnNSܡrdi),:[ {*Jd{$V
    Êܒn*BlFFTd]r)
    lYυs~NѦ_?<j7eH#@}4iNv!DidJ6ѣ}d!֔'xzu:
    H!0`8CC`    ƀLAD0/?&Sy!U^a<YR+#E0,2XF͡jJS(KZw1i1V8ҋN^Qd.aKuDˈNY J&@
    C(kM8V8`e
    ($V܌9#\8#ɟb*өcIiU%hÒYWP(bI!d#Rbhʼ.<`cy
    `-w|/ӄ_n098'.0ؠ|SJP{K "z߸u!6TQҥ|M2'bw꾔>'evסsd!:bd-, o]~jZՂ.1)ħ@c pphXp-`Q
    "DXI{ێˑsD!P:T@YGhFC CU`uL2w߬V/מz-gvdz`(ļ8tnKOC{&\(Cle4[I%xPli   !mxlc3/rV諐.JO=r<*gޛ
    ̢zUN;
    P{N3k
    P!Lo&Cn'~DRd"d\8WQs{ohtN.Tk#[r/K_3"q꽍plSU4fba&p*sNyq՞ Yצ)׾TM*d;L6]xZrz9E:ae,ZÆ1:;k;<[JS4צ/Ǧ?=_WS~"=FPɩ*>ĩ\k\LBe/Zdă4d:xs+"VIԨ
    ƃx*;\&,2wAd7j9%}G4~5YOhDMζ!.ڧbfʱ3R4!ipL8^AsG2-Q!d4)sZyP<8,:VeDA̝EpY8SBOِCռ]\]ClWcgQf.BPA~ga9wٻD2^c:xfXdPP˵ƈildr@Khn+īo=g    0^h)dob@cLDD!<麱_K1"cS0YHAB{( 
    !o=g(^E$%ERYdHA)'iL
    pʧ8X&/T|;SȎTsj3203N_4l?|3_'TwHÞ;|>JxVlj+1-@K}$iLw]iDad%~a^IiXGܼwB2ʼn!Ͱȧ2YhY;?" *EǂN7FXpC$,~`K=xwmqDTDh.ufW+K:}DbH5~gj@Ȣ>Bd4Kj0JU]9[}#'I[=]>R~ߩDžG#CTF&jNwge]kqR䋌,,?K&*eH   
    Hp3tUDsWje(Z=01dEH;e4+JJݷk[e`S$X[ewxݵ(Li!6EM
    26`'q9Ăn֫SoeOGQqdU2
    FԓgJt   nnLRkF"Veߜ
    }WpcrNQ{K͛{lK$/p<w!#I#)$Kޱb;b'R%dgF]O0xwS<j2*!L5hT9I'%ʮO
    J;ZT4PCd2m{22Rj
    R2611r;%$4PmDY2ENe"Nwdz3T1cȢL@@ކ
    $B<@ȩ:$2ELHDJzɥU1.
    ,A0""]!eQ&9U>2
    (.YVfUST)KJgKGoRRd-{1b1jRJR)t1ig1))JR1gR/C~`E(
    LAME3.99.5dBHLAME3.99.5d|HLAME3.99.5d|HLAME3.99.5d|HLAME3.99.5d|HLAME3.99.5d|HLAME3.99.5d|HLAME3.99.5d|HLAME3.99.5d|HLAME3.99.5d|HLAME3.99.5d|H
    --------abcde123--
    

1 个答案:

答案 0 :(得分:1)

我想通了,我正在使用一个用于OKHttp的LoggingInterceptor,它用于将字节转换为字符串并记录它,由于某种原因,它无法识别某些字符并且它为这些字符提供了一些垃圾值由于某种原因,这些值仍然存在于字节数组中......也就是用来提供格式错误的mp3文件。在移除拦截器之后......它开始工作......所以第1步已经完成......还有更多工作要做。