{"id":206,"date":"2024-03-13T16:28:02","date_gmt":"2024-03-13T08:28:02","guid":{"rendered":"https:\/\/www.jishuge.cn\/?p=206"},"modified":"2024-03-13T16:28:02","modified_gmt":"2024-03-13T08:28:02","slug":"jetpack-compose-%e4%b8%8a%e4%bc%a0%e7%9b%b8%e5%86%8c%e5%9b%be%e7%89%87%e5%88%b0%e6%9c%8d%e5%8a%a1%e5%99%a8","status":"publish","type":"post","link":"https:\/\/blog.jishuge.cn\/?p=206","title":{"rendered":"Jetpack Compose  \u4e0a\u4f20\u76f8\u518c\u56fe\u7247\u5230\u670d\u52a1\u5668"},"content":{"rendered":"\n<h3 class=\"wp-block-heading\">\u4e00\u3001\u5b9a\u4e49\u7ec4\u4ef6<\/h3>\n\n\n\n<p>\u5b9a\u4e49\u4e00\u4e2aComposable\u7ec4\u4ef6\uff0c\u5f53\u70b9\u51fbComposable\u7ec4\u4ef6\u4e2d\u7684\u6309\u94ae\u65f6\u5019\uff0c\u5c31\u8df3\u8f6c\u5230\u76f8\u518c\u9009\u62e9\uff0c\u7136\u540e\u9009\u4e2d\u56fe\u7247\u4e0a\u4f20\u3002<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import android.net.Uri\nimport android.util.Log\nimport androidx.activity.compose.rememberLauncherForActivityResult\nimport androidx.activity.result.contract.ActivityResultContracts\nimport androidx.compose.foundation.Image\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.material3.Button\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.runtime.setValue\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.platform.LocalContext\nimport androidx.compose.ui.tooling.preview.Preview\nimport coil.compose.rememberAsyncImagePainter\nimport com.google.gson.JsonObject\nimport com.xiangzhenming.taojin.network.ImageUploadNetwork\nimport okhttp3.MediaType.Companion.toMediaTypeOrNull\nimport okhttp3.MultipartBody\nimport okhttp3.RequestBody.Companion.toRequestBody\nimport retrofit2.Call\nimport retrofit2.Response\n\t\n\t\n\t\n\t@Composable\n    fun UploadImageView() {\n\t\tval context = LocalContext.current\n        val contentResolver = context.contentResolver\n\t\tvar imageUri by remember { mutableStateOf&lt;Uri?>(null) }\n\t\tval launcher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->\n\t\t  if (uri != null) {\n            imageUri = uri\n\t\t\tcontentResolver.openInputStream(uri).use { inputStream ->\n\t\t\t\n\t\t\t\n\t\t\t\t\/\/ \u521b\u5efaRequestBody\u5bf9\u8c61\n\t\t\t\tval requestBody = inputStream?.let {\n\t\t\t\t\tit.readBytes().toRequestBody(\"image\/jpeg\".toMediaTypeOrNull())\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t \/\/ \u521b\u5efaMultipartBody.Part\u5bf9\u8c61\n\t\t\t\tval part = requestBody?.let {\n\t\t\t\t\tMultipartBody.Part.createFormData(\n\t\t\t\t\t\t\"file\", \/\/ API\u63a5\u53e3\u8981\u6c42\u7684\u53c2\u6570\u540d\n\t\t\t\t\t\turi.lastPathSegment ?: \"${System.currentTimeMillis()}.jpg\", \/\/ \u6587\u4ef6\u540d\uff0c\u9ed8\u8ba4\u4f7f\u7528URI\u7684\u6700\u540e\u4e00\u6bb5\u4f5c\u4e3a\u6587\u4ef6\u540d\n\t\t\t\t\t\tit\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\/\/ \u5047\u8bbeapiService\u662f\u4f60\u7684Retrofit\u670d\u52a1\u5b9e\u4f8b\n\t\t\t\tif (part != null) {\n\t\t\t\t\tImageUploadNetwork(\"\").apiService.uploadImage(part)\n\t\t\t\t\t\t.enqueue(object : retrofit2.Callback&lt;JsonObject> {\n\n\t\t\t\t\t\t\toverride fun onResponse(call: Call&lt;JsonObject>, response: Response&lt;JsonObject>) {\n\t\t\t\t\t\t\t\tLog.e(\"UploadImage\", \"onResponse: ${response.body().toString()}:\")\n\t\t\t\t\t\t\t\tif (response.isSuccessful) {\n\t\t\t\t\t\t\t\t\tLog.e(\"UploadImage\", \"onResponse: success:\")\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tLog.e(\"UploadImage\", \"onResponse: fail${response.code()}\")\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\toverride fun onFailure(call: Call&lt;JsonObject>, t: Throwable) {\n\t\t\t\t\t\t\t\tLog.e(\"UploadImage\", \"onFailure: ${t.message}\")\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t}\n\t\t  }\n\t\t}\n\t\t\n\t\t\n\t\t\n\t\tColumn(\n            Modifier\n                .fillMaxSize()\n                .background(Color.White),\n            horizontalAlignment = Alignment.CenterHorizontally,\n            verticalArrangement = Arrangement.Center\n        )\n        {\n            Button(onClick = {\n                launcher.launch(\"image\/*\")\n            }) {\n                Text(text = \"\u9009\u62e9\u7167\u7247\")\n            }\n            Image(painter = rememberAsyncImagePainter(model = imageUri), contentDescription = null)\n        }\n\t\t\n\t\t\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\u4e0a\u4f20\u63a5\u53e3<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">interface ImageUploadService {\n\n    companion object {\n        const val BASE_URL = \"https:\/\/www.jishuge.cn\/\"\n        const val PREFIX_URL = \"\/upload\/android\"\n    }\n\n    @Multipart\n    @POST(\"${PREFIX_URL}\/reward\/upload\/image\")\n    fun uploadImage( @Part file: MultipartBody.Part): Call&lt;JsonObject>\n\n\n}<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class ImageUploadNetwork {\n\n\n    val apiService: ImageUploadService by lazy {\n        initRetrofit()\n    }\n\n    private val gson: Gson = GsonBuilder().setLenient().create()\n\n    private fun initRetrofit(): ImageUploadService {\n\n        val okHttpClient = OkHttpClient.Builder()\n            .addInterceptor(LoggingInterceptor())\n            .connectTimeout(10, TimeUnit.SECONDS)\n            .readTimeout(20, TimeUnit.SECONDS)\n            .retryOnConnectionFailure(false)\n            .build()\n\n        val retrofit = Retrofit.Builder()\n            .addConverterFactory(GsonConverterFactory.create(gson))\n            .baseUrl(ImageUploadService.BASE_URL)\n            .client(okHttpClient)\n            .build()\n\n        return retrofit.create(ImageUploadService::class.java)\n    }\n}<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class LoggingInterceptor : Interceptor {\n    @Volatile\n    var level: Level = Level.BASIC \/\/ \u8bbe\u7f6e\u9ed8\u8ba4\u7684\u65e5\u5fd7\u7ea7\u522b\n\n    enum class Level {\n        BASIC, \/\/ \u53ea\u6253\u5370\u57fa\u672c\u4fe1\u606f\n        HEADERS, \/\/ \u6253\u5370\u57fa\u672c\u4fe1\u606f\u4ee5\u53ca\u8bf7\u6c42\/\u54cd\u5e94\u5934\u4fe1\u606f\n        BODY \/\/ \u6253\u5370\u6240\u6709\u4fe1\u606f\uff08\u57fa\u672c\u4fe1\u606f\u3001\u8bf7\u6c42\/\u54cd\u5e94\u5934\u53ca\u8bf7\u6c42\/\u54cd\u5e94\u4f53\uff09\n    }\n\n    override fun intercept(chain: Interceptor.Chain): Response {\n        val request = chain.request()\n\n        when (level) {\n            Level.BASIC -> logBasic(request)\n            Level.HEADERS -> logHeaders(request)\n            Level.BODY -> logBodyAndHeaders(request, chain)\n        }\n\n        val response = chain.proceed(request)\n\n        when (level) {\n            Level.BASIC -> logBasic(response)\n            Level.HEADERS -> logHeaders(response)\n            Level.BODY -> logBodyAndHeaders(response)\n        }\n\n        return response\n    }\n\n    private fun logBasic(request: Request) {\n        Log.d(\"LoggingInterceptor\", \"Sending request ${request.url}\")\n    }\n\n    private fun logHeaders(request: Request) {\n        Log.d(\"LoggingInterceptor\", \"Sending request ${request.url}\")\n        Log.d(\"LoggingInterceptor\", \"Request Headers: ${request.headers}\")\n    }\n\n    private fun logBodyAndHeaders(request: Request, chain: Interceptor.Chain) {\n        logHeaders(request)\n        val requestBody = request.body ?: return\n        val bodyString = requestBody.toString()\n        Log.d(\"LoggingInterceptor\", \"Request Body: $bodyString\")\n    }\n\n    private fun logBasic(response: Response) {\n        Log.d(\"LoggingInterceptor\", \"Received response for ${response.request.url} with code ${response.code}\")\n    }\n\n    private fun logHeaders(response: Response) {\n        Log.d(\"LoggingInterceptor\", \"Received response for ${response.request.url} with code ${response.code}\")\n        Log.d(\"LoggingInterceptor\", \"Response Headers: ${response.headers}\")\n    }\n\n    private fun logBodyAndHeaders(response: Response) {\n        logHeaders(response)\n        if (response.body != null) {\n            val bodyString = response.body?.string()\n            Log.d(\"LoggingInterceptor\", \"Response Body: $bodyString\")\n        }\n    }\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\u670d\u52a1\u5668\u7aef\u8fd4\u56de\u7684\u503c\u683c\u5f0f\uff1a<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">{\n    \"code\": 0,\n    \"msg\": \"ok\",\n    \"data\": \"https:\/\/www.jishuge.cn\/image\/2024-03-13\/b69935ce-912e-4d31-b1e5-a15a3da85f82-1710318431942.jpg\",\n    \"count\": 0,\n    \"version\": \"1.0.0\",\n    \"timestamp\": 1710318433\n}<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>\u4e00\u3001\u5b9a\u4e49\u7ec4\u4ef6 \u5b9a\u4e49\u4e00\u4e2aComposable\u7ec4\u4ef6\uff0c\u5f53\u70b9\u51fbComposable\u7ec4\u4ef6\u4e2d\u7684\u6309\u94ae\u65f6\u5019\uff0c\u5c31\u8df3\u8f6c\u5230\u76f8\u518c\u9009\u62e9\uff0c &hellip; <a href=\"https:\/\/blog.jishuge.cn\/?p=206\" class=\"more-link\">\u7ee7\u7eed\u9605\u8bfb<span class=\"screen-reader-text\">Jetpack Compose  \u4e0a\u4f20\u76f8\u518c\u56fe\u7247\u5230\u670d\u52a1\u5668<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":159,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[19,23],"tags":[],"class_list":["post-206","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-android","category-kotlin"],"_links":{"self":[{"href":"https:\/\/blog.jishuge.cn\/index.php?rest_route=\/wp\/v2\/posts\/206","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.jishuge.cn\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.jishuge.cn\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.jishuge.cn\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.jishuge.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=206"}],"version-history":[{"count":2,"href":"https:\/\/blog.jishuge.cn\/index.php?rest_route=\/wp\/v2\/posts\/206\/revisions"}],"predecessor-version":[{"id":218,"href":"https:\/\/blog.jishuge.cn\/index.php?rest_route=\/wp\/v2\/posts\/206\/revisions\/218"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.jishuge.cn\/index.php?rest_route=\/wp\/v2\/media\/159"}],"wp:attachment":[{"href":"https:\/\/blog.jishuge.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=206"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.jishuge.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=206"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.jishuge.cn\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=206"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}