关于android中post请求的问题之前已经被问过,但是我尝试过的所有解决方案都不能正常工作。最重要的是,其中很多似乎也过于复杂。我想做的就是在特定的场景上发布一些带有一些身体参数的帖子。有什么简单的方法可以做到吗?
让我解释一下我使用 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()
函数中获取响应。
我找到的一个简单的版本就是这个。
如果您想发送不同的文本内容,则必须设置一些标题。
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()
}