Kotlin中Retrofit GET请求偶尔无返回问题排查求助
Hey there! Let's break down why your Retrofit GET request is acting inconsistent—since you're coming from PHP/JS, there are a few Android/Kotlin-specific gotchas tripping you up here. Let's go through them step by step, with fixes you can implement right away.
First: Your Connector Class is Creating New Retrofit/OkHttp Instances Every Time
Looking at your mainConnector class, you're creating a brand new OkHttpClient and Retrofit object every time you make a request. This is bad practice because:
- It wastes system resources (connections, threads)
- It bypasses OkHttp's connection pooling, which can lead to unstable connections
- Kotlin class/method naming is off:
fun MainConnector()is a regular method, not a constructor, which adds confusion
Fix this by making your Retrofit/OkHttp setup a singleton (only created once):
interface ApiService { companion object { const val BASE_URL = "你的基础URL(记得以/结尾!)" } // Avoid nullable types unless your API explicitly returns null lists @GET("main") fun getPosts(): Call<List<Products>> } class MainConnector { // Singleton pattern to reuse Retrofit/OkHttp instances companion object { private val retrofit: Retrofit by lazy { val logging = HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY } val okHttpClient = OkHttpClient.Builder() .addInterceptor(logging) // Add timeouts to prevent silent failures from slow networks .connectTimeout(30, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS) .writeTimeout(30, TimeUnit.SECONDS) .build() Retrofit.Builder() .baseUrl(ApiService.BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .client(okHttpClient) .build() } val apiService: ApiService by lazy { retrofit.create(ApiService::class.java) } } }
Second: You're Ignoring Failure Cases & Overusing !!
Your onFailure method is empty—this is a critical mistake! When requests fail (timeouts, network drops, server errors), you get no feedback at all. Also, using !! everywhere forces non-null checks, which will crash your app silently if any field is null, making you think the request "returned nothing" when it actually crashed.
Fix your Activity code with proper error handling and safe null checks:
val apiService = MainConnector.apiService val call = apiService.getPosts() call.enqueue(object : Callback<List<Products>> { override fun onResponse(call: Call<List<Products>>, response: Response<List<Products>>) { // First check if the server returned a successful status code (200-299) if (response.isSuccessful) { val postList = response.body() if (postList != null && postList.isNotEmpty()) { val listView: ListView = findViewById(R.id.listview) listView.isClickable = true // No need to re-create the list—use the parsed data directly val adapter = MyMainListAdapter(this@MainActivity, R.layout.custom_main_list, postList.toMutableList()) listView.adapter = adapter listView.setOnItemClickListener { _, _, position, _ -> val product = postList[position] val intent = Intent(this@MainActivity, ProductActivity::class.java) // Safe handling for nullable image lists val imageList = product.image?.let { ArrayList(it) } ?: ArrayList() intent.putStringArrayListExtra("image", imageList) // Pass other fields safely (no need for !! here) intent.putExtra("name", product.pname) intent.putExtra("createdAt", product.created_at) intent.putExtra("description", product.description) // ... add the rest of your fields here startActivity(intent) } } else { // Server returned an empty list—notify the user Toast.makeText(this@MainActivity, "暂无数据", Toast.LENGTH_SHORT).show() } } else { // Server returned an error (404, 500, etc.) val errorMsg = "请求失败:状态码 ${response.code()} - ${response.message()}" Log.e("MainActivity", errorMsg) Toast.makeText(this@MainActivity, errorMsg, Toast.LENGTH_SHORT).show() } } override fun onFailure(call: Call<List<Products>>, t: Throwable) { // Log the full error to debug what's going wrong val errorMsg = "请求失败:${t.message}" Log.e("MainActivity", errorMsg, t) // Prints full stack trace for debugging Toast.makeText(this@MainActivity, errorMsg, Toast.LENGTH_SHORT).show() } })
Third: Additional Checks to Rule Out Other Issues
- Verify Network Permissions: Make sure your
AndroidManifest.xmlincludes the internet permission:<uses-permission android:name="android.permission.INTERNET" /> - Check Gson Parsing: Ensure your
Productsclass fields exactly match the API's JSON response (case-sensitive!). If a field name doesn't match, Gson will parse it as null. Use@SerializedNameif needed:data class Products( @SerializedName("admindescription") val admindescription: String, // ... rest of your fields ) - Compare Request Details: Use the
HttpLoggingInterceptoroutput in Logcat to compare your Retrofit request headers/URL with the web browser's request. Some servers block requests from non-browser User-Agents—you can add a custom User-Agent to OkHttp if needed:val okHttpClient = OkHttpClient.Builder() .addInterceptor { chain -> val request = chain.request().newBuilder() .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36") .build() chain.proceed(request) } // ... other config
Why This Fixes Your Problem
- Reusing Retrofit/OkHttp instances ensures stable connection pooling and reduces resource waste.
- Timeout settings prevent silent failures from slow or unstable networks.
- Proper error handling in
onFailureandonResponsetells you exactly why a request failed, instead of leaving you guessing. - Safe null checks avoid hidden crashes that look like "no data returned".
内容的提问来源于stack exchange,提问作者AhmadVahedi

