{"id":1655,"date":"2022-12-21T13:41:30","date_gmt":"2022-12-21T05:41:30","guid":{"rendered":"https:\/\/badgameshow.com\/fly\/?p=1655"},"modified":"2022-12-22T13:18:30","modified_gmt":"2022-12-22T05:18:30","slug":"%e3%80%90retrofit%e3%80%91android-post-raw-json-httplogginginterceptor-%e7%af%84%e4%be%8b","status":"publish","type":"post","link":"https:\/\/badgameshow.com\/fly\/%e3%80%90retrofit%e3%80%91android-post-raw-json-httplogginginterceptor-%e7%af%84%e4%be%8b\/","title":{"rendered":"\u3010Retrofit\u3011Android POST Raw JSON + HttpLoggingInterceptor \u7bc4\u4f8b"},"content":{"rendered":"<h1>\u3010Retrofit\u3011Android POST Raw JSON + HttpLoggingInterceptor \u7bc4\u4f8b<\/h1>\n<h4>Retrofit \u662f\u4e00\u500b Android \u548c Java \u7684 HTTP \u5ba2\u6236\u7aef\uff0c\u65e8\u5728\u7c21\u5316 HTTP API \u9023\u63a5\u548c\u8acb\u6c42\u5efa\u69cb\u3002<\/h4>\n<h4>\u5b83\u4f7f\u7528\u7c21\u6613\u7684 API \u683c\u5f0f\uff08\u4e0d\u9700\u8981 XML \u6216 JSON \u5e8f\u5217\u5316\uff09\uff0c\u540c\u6642\u63d0\u4f9b\u8fc5\u901f\u7684\u52a0\u8f09\u6642\u9593\u548c\u65b9\u4fbf\u7684\u5eab\u6ac3\u7d44\u7e54\u3002\u6709\u6642\u53ef\u80fd\u9700\u8981\u4f7f\u7528\u5e8f\u5217\u5316\uff0cRetrofit \u4e5f\u652f\u6301\u5728 API \u5c64\u9762\u4e0a\u5b9a\u7fa9\u7de8\u78bc\u683c\u5f0f\u5230 Java \u985e\u578b\uff0c\u4e26\u652f\u6301\u5e38\u898b\u7684\u8a2a\u554f\u63a7\u5236\u4fe1\u606f\uff08\u4f8b\u5982\u8a2d\u5099 ID\uff09\u3002<\/h4>\n<hr \/>\n<h4>\u6587\u7ae0\u76ee\u9304<\/h4>\n<ol>\n<li><a href=\"#a\">Retrofit &#038; okhttp \u5c0e\u5165<\/a><\/li>\n<li><a href=\"#b\">Retrofit Interceptor<\/a><\/li>\n<li><a href=\"#c\">Retrofit RetrofitUtil<\/a><\/li>\n<li><a href=\"#d\">Retrofit Model<\/a><\/li>\n<li><a href=\"#e\">Retrofit Service<\/a><\/li>\n<li><a href=\"#f\">Retrofit<\/a><\/li>\n<li><a href=\"#g\">Developer Documents Retrofit<\/a><\/li>\n<\/ol>\n<hr \/>\n<p><a id=\"a\"><\/a><\/p>\n<h4>1.Paging3 &amp; Retrofit &amp; Fragment KTX \u5c0e\u5165<\/h4>\n<h5>AndroidManifest.xml<\/h5>\n<pre data-language=XML><code class=\"language-markup line-numbers\">&lt;uses-permission android:name=\"android.permission.INTERNET\"\/&gt;\n<\/code><\/pre>\n<h5>build.gradle<\/h5>\n<pre><code class=\"language-groovy line-numbers\">dependencies {\n    implementation \"androidx.fragment:fragment-ktx:1.5.5\"\n    implementation 'com.squareup.retrofit2:retrofit:2.9.0'\n    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'\n    implementation 'com.squareup.okhttp3:okhttp:4.10.0'\n}\n<\/code><\/pre>\n<p><a id=\"b\"><\/a><\/p>\n<h4>2.Retrofit Interceptor<\/h4>\n<h5>HttpLoggingInterceptor.kt<\/h5>\n<pre><code class=\"language-kotlin line-numbers\">class HttpLoggingInterceptor : Interceptor {\n    private val logger = Logger.DEFAULT\n    private var level = Level.NONE\n\n    enum class Level {\n        NONE,\n        HEADERS,\n        BODY\n    }\n\n    interface Logger {\n        fun log(message: String)\n\n        companion object {\n            val DEFAULT: Logger = object : Logger {\n                override fun log(message: String) {\n                    Log.w(\"RDClient\", \"logger &gt;&gt; \\n<span class=\"katex math inline\">message\")\n                }\n            }\n        }\n    }\n\n    fun setLevel(level: Level): HttpLoggingInterceptor {\n        this.level = level\n        return this\n    }\n\n    @Throws(IOException::class)\n    override fun intercept(chain: Interceptor.Chain): Response {\n        val level = level\n        val request = chain.request()\n        if (level ==== Level.NONE) {\n            return chain.proceed(request)\n        }\n        val logBody = level == Level.BODY\n        val logHeaders = logBody || level == Level.HEADERS\n        val requestBody = request.body\n        val hasRequestBody = requestBody != null\n        val connection = chain.connection()\n        val protocol = connection?.protocol() ?: Protocol.HTTP_1_1\n        var requestStartMessage = \"--&gt; \" + request.method + ' ' + request.url + ' ' + protocol\n        if (!logHeaders &amp;&amp; hasRequestBody) {\n            requestStartMessage += \" (\" + requestBody!!.contentLength() + \"-byte body)\"\n        }\n        logger.log(requestStartMessage)\n\n        if (logHeaders) {\n            if (hasRequestBody) {\n                if (requestBody!!.contentType() != null) {\n                    logger.log(\"Content-Type: \" + requestBody.contentType())\n                }\n                if (requestBody.contentLength() != -1L) {\n                    logger.log(\"Content-Length: \" + requestBody.contentLength())\n                }\n            }\n            val headers = request.headers\n            var i = 0\n            val count = headers.size\n\n            while(i&lt;count) {\n                val name = headers.name(i)\n                if (!\"Content-Type\".equals(name, ignoreCase = true) &amp;&amp; !\"Content-Length\".equals(name, ignoreCase = true)) {\n                    logger.log(name + \": \" + headers.value(i))\n                }\n                i++\n            }\n\n            if (!logBody || !hasRequestBody) {\n                logger.log(\"--&gt; END \" + request.method)\n            } else if (bodyEncoded(request.headers)) {\n                logger.log(\"--&gt; END \" + request.method + \" (encoded body omitted)\")\n            } else {\n                val buffer = Buffer()\n                requestBody!!.writeTo(buffer)\n                var charset = UTF8\n                val contentType = requestBody.contentType()\n                if (contentType != null) {\n                    charset = contentType.charset(UTF8)\n                }\n                logger.log(\"\")\n                if (isPlaintext(buffer)) {\n                    logger.log(\n                        buffer.readString(\n                            Objects.requireNonNull(\n                                charset\n                            )\n                        )\n                    )\n                    logger.log(\"--&gt; END \" + request.method + \" (\" + requestBody.contentLength() + \"-byte body)\")\n                } else {\n                    logger.log(\"--&gt; END \" + request.method + \" (binary \" + requestBody.contentLength() + \"-byte body omitted)\")\n                }\n            }\n        }\n        val startNs = System.nanoTime()\n        val response = chain.proceed(request)\n        val tookMs =\n            TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs)\n        val responseBody = response.body\n        val contentLength =\n            responseBody?.contentLength()\n        val bodySize =\n            if (contentLength != -1L) \"<\/span>contentLength-byte\" else \"unknown-length\"\n        logger.log(\n            \"&lt;-- \" + response.code + ' ' + response.message + ' ' + response.request.url + \" (\" + tookMs + \"ms\" + (if (!logHeaders) \", \" +\n                    bodySize + \" body\" else \"\") + ')'\n        )\n        if (logHeaders) {\n            val headers = response.headers\n            var i = 0\n            val count = headers.size\n            while (i &lt; count) {\n                logger.log(headers.name(i) + \": \" + headers.value(i))\n                i++\n            }\n            if (bodyEncoded(response.headers)) {\n                logger.log(\"&lt;-- END HTTP (encoded body omitted)\")\n            } else {\n                val source = responseBody!!.source()\n                source.request(Long.MAX_VALUE) \/\/ Buffer the entire body.\n                val buffer = source.buffer()\n                var charset = UTF8\n                val contentType = responseBody.contentType()\n                if (contentType != null) {\n                    charset = try {\n                        contentType.charset(UTF8)\n                    } catch (e: UnsupportedCharsetException) {\n                        logger.log(\"\")\n                        logger.log(\"Couldn't decode the response body; charset is likely malformed.\")\n                        logger.log(\"&lt;-- END HTTP\")\n                        return response\n                    }\n                }\n                if (!isPlaintext(buffer)) {\n                    logger.log(\"\")\n                    logger.log(\"&lt;-- END HTTP (binary \" + buffer.size + \"-byte body omitted)\")\n                    return response\n                }\n                if (contentLength != 0L) {\n                    logger.log(\"\")\n                    logger.log(\n                        buffer.clone().readString(\n                            Objects.requireNonNull(charset)\n                        )\n                    )\n                }\n                logger.log(\"&lt;-- END HTTP (\" + buffer.size + \"-byte body)\")\n            }\n        }\n        return response\n    }\n\n    private fun bodyEncoded(headers: Headers): Boolean {\n        val contentEncoding = headers[\"Content-Encoding\"]\n        return contentEncoding != null &amp;&amp; !contentEncoding.equals(\"identity\", ignoreCase = true)\n    }\n\n    companion object {\n        private val UTF8 = StandardCharsets.UTF_8\n        fun isPlaintext(buffer: Buffer): Boolean {\n            return try {\n                val prefix = Buffer()\n                val byteCount = if (buffer.size &lt; 64) buffer.size else 64\n                buffer.copyTo(prefix, 0, byteCount)\n                for (i in 0..15) {\n                    if (prefix.exhausted()) {\n                        break\n                    }\n                    if (Character.isISOControl(prefix.readUtf8CodePoint())) {\n                        return false\n                    }\n                }\n                true\n            } catch (e: EOFException) {\n                false\n            }\n        }\n    }\n}\n<\/code><\/pre>\n<p><a id=\"c\"><\/a><\/p>\n<h4>3.Retrofit RetrofitUtil<\/h4>\n<h5>RetrofitUtils.kt<\/h5>\n<pre><code class=\"language-kotlin line-numbers\">class RetrofitUtils {\n\n    companion object {\n        private const val DEFAULT_TIMEOUT = 30L\n        val instance: RetrofitUtils by lazy { RetrofitUtils() }\n    }\n\n    fun &lt;T&gt; getService(clazz: Class&lt;T&gt;): T {\n        return retrofit.create(clazz)\n    }\n\n    private fun getClient(): OkHttpClient {\n        val logInterceptor = HttpLoggingInterceptor()\n\n        if (BuildConfig.DEBUG) {\n            logInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY)\n        } else {\n            logInterceptor.setLevel(HttpLoggingInterceptor.Level.NONE)\n        }\n\n        return OkHttpClient.Builder()\n            .readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)\n            .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)\n            .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)\n            .addInterceptor(logInterceptor)\n            .retryOnConnectionFailure(true).build()\n    }\n\n    private val retrofit = Retrofit.Builder()\n        .addConverterFactory(GsonConverterFactory.create())\n        .baseUrl(\"https:\/\/api.openai.com\/v1\/\")\n        .client(getClient())\n        .build()\n}\n<\/code><\/pre>\n<p><a id=\"d\"><\/a><\/p>\n<h4>4.Retrofit Model<\/h4>\n<h5>CompletionsSub.kt<\/h5>\n<pre><code class=\"language-kotlin line-numbers\">data class CompletionsSub(\n    val model: String,\n    val prompt: String,\n    val max_tokens: Int\n)\n<\/code><\/pre>\n<h5>CompletionsRec.kt<\/h5>\n<pre><code class=\"language-kotlin line-numbers\">data class CompletionsRec(\n    val created: Int,\n    val model: String,\n    val choices: List&lt;Choices&gt;,\n    val usage: Usage\n)\n\ndata class Choices(\n    val text: String,\n    val index: Int,\n    val finish_reason: String\n)\n\ndata class Usage(\n    val prompt_tokens: Int,\n    val completion_tokens: Int,\n    val total_tokens: Int\n)\n<\/code><\/pre>\n<p><a id=\"e\"><\/a><\/p>\n<h4>5.Retrofit Service<\/h4>\n<h5>OpenAIService.kt<\/h5>\n<pre><code class=\"language-kotlin line-numbers\">interface OpenAIService {\n\n    @Headers(\"Authorization: Bearer sk-8yqIPe85wc5YKZINDCT3BlbkFJrB\")\n    @POST(\"completions\")\n    suspend fun completions(@Body completionsSub: CompletionsSub) : CompletionsRec\n\n    @FormUrlEncoded\n    @POST(\"completions\")\n    suspend fun completions(@Field(\"model\") phone: String, @Field(\"type\") type: String): CompletionsRec\n\n}\n<\/code><\/pre>\n<p><a id=\"f\"><\/a><\/p>\n<h4>6.Retrofit<\/h4>\n<h5>MainActivity.kt<\/h5>\n<pre><code class=\"language-kotlin line-numbers\">class MainActivity : AppCompatActivity() {\n\n    private lateinit var binding: ActivityMainBinding\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        setContentView(R.layout.activity_main)\n\n        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)\n\n        val service = RetrofitUtils.instance.getService(OpenAIService::class.java)\n\n        lifecycleScope.launch {\n\n            service.completions(CompletionsSub(\n                \"text-davinci-003\",\n                \"\u54c8\u56c9\",\n                500\n            ))\n\n            service\n                .completions(\n                    \"text-davinci-003\",\n                    \"\u54c8\u56c9\"\n                )\n        }\n\n    }\n}\n<\/code><\/pre>\n<p><a id=\"g\"><\/a><\/p>\n<h4>7.Developer Documents Retrofit<\/h4>\n<p><a class=\"wp-editor-md-post-content-link\" href=\"https:\/\/square.github.io\/retrofit\/\" title=\"Open in Documents Retrofit\" target=\"_blank\" rel=\"noopener\">Open in Documents Retrofit<\/a><\/p>\n\n<div style=\"font-size: 0px; height: 0px; line-height: 0px; margin: 0; padding: 0; clear: both;\"><\/div>","protected":false},"excerpt":{"rendered":"<p>\u3010Retrofit\u3011Android POST Raw JSON + HttpLoggingIntercepto &hellip; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"pgc_sgb_lightbox_settings":"","footnotes":""},"categories":[10],"tags":[13,224,38],"class_list":["post-1655","post","type-post","status-publish","format-standard","hentry","category-retrofit","tag-android","tag-interceptor","tag-retrofit"],"_links":{"self":[{"href":"https:\/\/badgameshow.com\/fly\/wp-json\/wp\/v2\/posts\/1655","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/badgameshow.com\/fly\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/badgameshow.com\/fly\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/badgameshow.com\/fly\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/badgameshow.com\/fly\/wp-json\/wp\/v2\/comments?post=1655"}],"version-history":[{"count":12,"href":"https:\/\/badgameshow.com\/fly\/wp-json\/wp\/v2\/posts\/1655\/revisions"}],"predecessor-version":[{"id":1670,"href":"https:\/\/badgameshow.com\/fly\/wp-json\/wp\/v2\/posts\/1655\/revisions\/1670"}],"wp:attachment":[{"href":"https:\/\/badgameshow.com\/fly\/wp-json\/wp\/v2\/media?parent=1655"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/badgameshow.com\/fly\/wp-json\/wp\/v2\/categories?post=1655"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/badgameshow.com\/fly\/wp-json\/wp\/v2\/tags?post=1655"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}