在 Android 应用程序的 Kotlin 中发出发布请求的最简单方法是什么

问题描述 投票:0回答:2

关于android中post请求的问题之前已经被问过,但是我尝试过的所有解决方案都不能正常工作。最重要的是,其中很多似乎也过于复杂。我想做的就是在特定的场景上发布一些带有一些身体参数的帖子。有什么简单的方法可以做到吗?

android kotlin post
2个回答
0
投票

让我解释一下我使用 Retrofit 的请求调用结构。

build.gradle(应用程序)

// Retrofit + GSON
implementation 'com.squareup.okhttp3:logging-interceptor:4.4.0'
implementation "com.squareup.retrofit2:retrofit:2.9.0"
implementation "com.squareup.retrofit2:converter-gson:2.9.0"

ApiClient.kt

object ApiClient {

    private const val baseUrl = ApiInterface.BASE_URL
    private var retrofit: Retrofit? = null
    private val dispatcher = Dispatcher()

    fun getClient(): Retrofit? {

        val logging = HttpLoggingInterceptor()

        if (BuildConfig.DEBUG)
            logging.level = HttpLoggingInterceptor.Level.BODY
        else
            logging.level = HttpLoggingInterceptor.Level.NONE

        if (retrofit == null) {
            retrofit = Retrofit.Builder()
                .client(OkHttpClient().newBuilder().readTimeout(120, TimeUnit.SECONDS)
                    .connectTimeout(120, TimeUnit.SECONDS).retryOnConnectionFailure(false)
                    .dispatcher(
                        dispatcher
                    ).addInterceptor(Interceptor { chain: Interceptor.Chain? ->
                        val newRequest = chain?.request()!!.newBuilder()
                        return@Interceptor chain.proceed(newRequest.build())
                    }).addInterceptor(logging).build()
                )
                .baseUrl(baseUrl)
                .addConverterFactory(GsonConverterFactory.create())
                .build()
        }
        return retrofit
    }
}

ApiClient
将用于初始化Retrofit单例对象,还初始化日志拦截器,以便您可以使用关键字“okhttp”跟踪logcat中的请求和响应。

SingleEnqueueCall.kt

object SingleEnqueueCall {

    var retryCount = 0
    lateinit var snackbar: Snackbar

    fun <T> callRetrofit(
        activity: Activity,
        call: Call<T>,
        apiName: String,
        isLoaderShown: Boolean,
        apiListener: IGenericCallBack
    ) {

        snackbar = Snackbar.make(
            activity.findViewById(android.R.id.content),
            Constants.CONST_NO_INTERNET_CONNECTION, Snackbar.LENGTH_INDEFINITE
        )

        if (isLoaderShown)
            activity.showAppLoader()
        snackbar.dismiss()

        call.enqueue(object : Callback<T> {
            override fun onResponse(call: Call<T>, response: Response<T>) {
                hideAppLoader()

                if (response.isSuccessful) {
                    retryCount = 0
                    apiListener.success(apiName, response.body())

                } else {
                    when {
                        response.errorBody() != null -> try {

                            val json = JSONObject(response.errorBody()!!.string())
                            Log.e("TEGD", "JSON==> " + response.errorBody())
                            Log.e("TEGD", "Response Code==> " + response.code())
                            val error = json.get("message") as String
                            apiListener.failure(apiName, error)

                        } catch (e: Exception) {
                            e.printStackTrace()
                            Log.e("TGED", "JSON==> " + e.message)
                            Log.e("TGED", "Response Code==> " + response.code())
                            apiListener.failure(apiName, Constants.CONST_SERVER_NOT_RESPONDING)
                        }
                        else -> {

                            apiListener.failure(apiName, Constants.CONST_SERVER_NOT_RESPONDING)
                            return
                        }
                    }
                }
            }

            override fun onFailure(call: Call<T>, t: Throwable) {
                hideAppLoader()
                val callBack = this

                if (t.message != "Canceled") {
                    Log.e("TGED", "Fail==> " + t.localizedMessage)

                    if (t is UnknownHostException || t is IOException) {
                        snackbar.setAction("Retry") {
                            snackbar.dismiss()
                            enqueueWithRetry(activity, call, callBack, isLoaderShown)
                        }
                        snackbar.show()
                        apiListener.failure(apiName, Constants.CONST_NO_INTERNET_CONNECTION)

                    } else {
                        retryCount = 0
                        apiListener.failure(apiName, t.toString())
                    }

                } else {
                    retryCount = 0
                }
            }
        })
    }

    fun <T> enqueueWithRetry(
        activity: Activity,
        call: Call<T>,
        callback: Callback<T>,
        isLoaderShown: Boolean
    ) {
        activity.showAppLoader()
        call.clone().enqueue(callback)
    }
}

SingleEnqueueCall
将用于调用改造,它非常通用,用
onFailure()
函数编写,通过将
Call
传递给它,我们可以与
ApiName
参数一起调用API,因此该函数可用于任何可能的调用,通过
ApiName
,我们可以在响应中区分结果来自哪个 API。

常量.kt

object Constants {
    const val CONST_NO_INTERNET_CONNECTION = "Please check your internet 
    connection"
    const val CONST_SERVER_NOT_RESPONDING = "Server not responding! 
    Please try again later"

    const val USER_REGISTER = "/api/User/register"
}

ApiInterface.kt

interface ApiInterface {
    companion object {
        const val BASE_URL = "URL_LINK"
    }

    @POST(Constants.USER_REGISTER)
    fun userRegister(@Body userRegisterRequest: UserRegisterRequest): 
    Call<UserRegisterResponse>
}

用户注册请求.kt

data class UserRegisterRequest(
    val Email: String,
    val Password: String
)

用户注册响应.kt

data class UserRegisterResponse(
    val Message: String,
    val Code: Int
)

IGenericCallBack.kt

interface IGenericCallBack {

    fun success(apiName: String, response: Any?)
    fun failure(apiName: String, message: String?)
}

MyApplication.kt

class MyApplication : Application() {

    companion object {
        lateinit var apiService: ApiInterface
    }

    override fun onCreate() {
        super.onCreate()

        apiService = ApiClient.getClient()!!.create(ApiInterface::class.java)
    }
}

MyApplication
是在应用程序启动时初始化 Retrofit 的应用程序类。

AndroidManifest.xml

android:name=".MyApplication"

您必须将以上标签写在

AndroidManifest
Application
标签中。

MainActivity.kt

class MainActivity : AppCompatActivity(), IGenericCallBack {
    
    private lateinit var binding: ActivityMainBinding
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
    
        val call = MyApplication.apiService.userRegister(UserRegisterRequest(email, password))
        SingleEnqueueCall.callRetrofit(this, call, Constants.USER_REGISTER, true, this)
    }

    override fun success(apiName: String, response: Any?) {

        val model = response as UserRegisterResponse   
    }

    override fun failure(apiName: String, message: String?) {

        if (message != null) {
            showToastMessage(message)
        }
    }
}

首先,我们使用

object
中定义的API创建一个调用
ApiInterface
并传递参数(如果有)。然后使用
SingleEnqueueCall
,我们使用
IGenericCallBack
将调用与 ApiName 和接口侦听器
this
一起传递给改造。请记住将其实现到相应的 Activity 或片段,如上所述。

其次,您将获得 API 的响应,无论是在

success()
还是
failure()
函数中,都会被
IGenericCallBack

覆盖

P.S:您可以通过使用

success()
函数中的 ApiName 参数来区分哪个 API 获得了响应。

override fun success(apiName: String, response: Any?) {

      when(ApiName) {
           Constants.USER_REGISTER -> {
                 val model = response as UserRegisterResponse
            }
      }
}

整个概念是关注可重用性,现在每个 API 调用都必须使用 API 内部的

ApiInterface
创建一个调用变量,然后通过
SingleEnqueueCall
调用该 API,并在
success()
failure()
函数中获取响应。


0
投票

我找到的一个简单的版本就是这个。

如果您想发送不同的文本内容,则必须设置一些标题。

import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse

fun sendPost(urlString: String = "http://127.0.0.1:8080/test",
             bodyContent: String = "test") {

    val client = HttpClient.newBuilder().build();
    val request = HttpRequest.newBuilder()
        .uri(URI.create(urlString))
        //.header("Content-Type", "application/json")
        .POST(HttpRequest.BodyPublishers.ofString(bodyContent))
        .build()
    val response = client.send(request, HttpResponse.BodyHandlers.ofString())
    val answer = response.body()
    println(answer)
}

这里有一个带有“URL”的更复杂的版本,但我注意到“URL”显示“‘构造函数 URL(String!)’ 已弃用”。如果在 Kotlin 中使用请注意。

import java.io.*
import java.net.URL
import java.net.URLEncoder
import javax.net.ssl.HttpsURLConnection

fun sendPost(url: String, postParams: String): String {
    val USER_AGENT = "Mozilla/5.0"
    
    // build connection
    val obj = URL(url)
    conn = obj.openConnection() as HttpsURLConnection

    // post requst
    conn!!.useCaches = false
    conn!!.requestMethod = "POST"

    //set content header
    conn!!.setRequestProperty("Content-Type", "application/x-www-form-urlencoded")
    //set other headers (can be deleted if not needed
    conn!!.setRequestProperty("Host", "accounts.google.com")
    conn!!.setRequestProperty("User-Agent", USER_AGENT)
    conn!!.setRequestProperty(
        "Accept",
        "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
    )
    conn!!.setRequestProperty("Accept-Language", "en-US,en;q=0.5")
    conn!!.setRequestProperty("Connection", "keep-alive")
    conn!!.setRequestProperty("Referer", url)
    conn!!.setRequestProperty("Content-Length", Integer.toString(postParams.length))
    
    // enable sending a body and getting a body
    conn!!.doOutput = true
    conn!!.doInput = true
    
    // Send post request
    val wr = DataOutputStream(conn!!.outputStream)
    
    //sending the body (a string in this example 
    wr.writeBytes(postParams)
    wr.flush()
    wr.close()
    
    // can be removed, but I had it to give the server time to responde
    try {
        Thread.sleep(100)
    } catch (e: Exception){
        throw e
    }
    // for debugging
    val responseCode = conn!!.responseCode
    println("\nSending 'POST' request to URL : $url")
    println("Post parameters : $postParams")
    println("Response Code : $responseCode")

    // getting the answer String
    val `in` = BufferedReader(InputStreamReader(conn!!.inputStream))
    var inputLine: String?
    val response = StringBuffer()
    while (`in`.readLine().also { inputLine = it } != null) {
        response.append(inputLine)
    }
    `in`.close()
    // System.out.println(response.toString());
    return response.toString()
}

© www.soinside.com 2019 - 2024. All rights reserved.