如何从Volley请求返回列表结果并传递至其他类?
解决Volley异步响应返回列表的问题
你遇到的核心问题是Volley的请求是异步执行的,所以你不能像同步方法那样直接在getMovies里立即返回结果——当你执行return results的时候,Volley的请求还没完成,结果根本没被赋值,这就是为什么会出现"result must be initialized"的错误,同时响应会滞留在回调里。
下面我给你两种可行的解决方案,优先推荐用Kotlin协程的方式,因为它能让异步代码看起来像同步代码,更易维护:
方案一:用Kotlin协程+CompletableDeferred包装Volley请求
我们可以用CompletableDeferred把Volley的回调式异步操作转换成挂起函数,这样就能在suspend fun getMovies里直接获取结果:
改造后的完整代码
import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext class RemoteDataSourceManager: ServiceCall { override suspend fun getMovies(context: Context): Result<List<MovieEntity>> { // 在IO线程执行网络请求,避免阻塞主线程 return withContext(Dispatchers.IO) { getRemoteMovies(context) } } // 把这个方法改成挂起函数,用CompletableDeferred等待Volley响应 private suspend fun getRemoteMovies(context: Context): Result<List<MovieEntity>> { val deferred = CompletableDeferred<Result<List<MovieEntity>>>() val listOfMovies = ArrayList<MovieEntity>() val url = "https://api.themoviedb.org/3/movie/popular?api_key=f1e256985ebc2be710bf1f4ed754da11&language=en-US&page=1" val request: JsonObjectRequest = JsonObjectRequest( Request.Method.GET, url, null, Response.Listener { response -> try { val results = response.getJSONArray("results") for (movieIndex in 0 until results.length()) { val eachMovie = results.getJSONObject(movieIndex) val movieImage = "https://image.tmdb.org/t/p/w500${eachMovie.getString("poster_path")}" val title = eachMovie.getString("title") val rating = eachMovie.getInt("vote_average") val overView = eachMovie.getString("overview") val releaseDate = eachMovie.getString("release_date") val movieId = eachMovie.getInt("id").toString() val newMovie = MovieEntity( title, movieImage, rating, overView, releaseDate ).apply { this.movieId = movieId this.id = movieId.toInt() } listOfMovies.add(newMovie) } // 请求成功,把结果传给deferred deferred.complete(Result.Success(listOfMovies.toList())) } catch (e: Exception) { Log.i("error", "Response $e") // 请求失败,传递异常 deferred.completeExceptionally(e) } }, Response.ErrorListener { error -> val errorMessage = VolleyErrorHandler.instance.erroHandler(error) Log.i("VolleyError", errorMessage) // 错误处理,传递异常 deferred.completeExceptionally(error) }) context.applicationContext.let { VolleySingleton.getInstance(it).addToRequestQueue(request) } // 挂起当前协程,直到Volley请求完成并返回结果 return deferred.await() } override suspend fun saveMovies(movieEntity: MovieEntity) { TODO("not implemented") } // 原来的VolleyCallBack接口可以移除了,因为我们用协程替代了回调 }
关键说明:
- CompletableDeferred:它相当于一个异步结果的"容器",Volley请求成功时调用
deferred.complete()传递结果,失败时调用deferred.completeExceptionally()传递异常。 - withContext(Dispatchers.IO):网络请求属于IO操作,我们在IO调度器执行,避免阻塞主线程。
- deferred.await():挂起当前协程,直到Volley请求完成,然后返回结果——这就实现了你想要的"同步式"获取异步结果的效果。
方案二:改进原有回调方式(适合不想用协程的场景)
如果你暂时不想用协程,那需要调整回调的逻辑,让getMovies不直接返回结果,而是通过回调把结果传递给调用方:
改造示例
class RemoteDataSourceManager: ServiceCall { // 修改getMovies为接收回调,而不是返回结果 fun getMovies(context: Context, callback: (Result<List<MovieEntity>>) -> Unit) { getRemoteMovies(context) { result -> callback(result) } } private fun getRemoteMovies(context: Context, callback: (Result<List<MovieEntity>>) -> Unit) { val listOfMovies = ArrayList<MovieEntity>() val url = "https://api.themoviedb.org/3/movie/popular?api_key=f1e256985ebc2be710bf1f4ed754da11&language=en-US&page=1" val request: JsonObjectRequest = JsonObjectRequest( Request.Method.GET, url, null, Response.Listener { response -> try { // ... 解析数据的逻辑和之前一样 ... callback(Result.Success(listOfMovies.toList())) } catch (e: Exception) { Log.i("error", "Response $e") callback(Result.Error(e)) // 假设你的Result有Error类型 } }, Response.ErrorListener { error -> val errorMessage = VolleyErrorHandler.instance.erroHandler(error) Log.i("VolleyError", errorMessage) callback(Result.Error(error)) }) context.applicationContext.let { VolleySingleton.getInstance(it).addToRequestQueue(request) } } // ... 其他方法 ... } // 调用方式 remoteDataSourceManager.getMovies(context) { result -> when(result) { is Result.Success -> { // 拿到列表,存入数据库 saveToDatabase(result.data) } is Result.Error -> { // 处理错误 } } }
关键说明:
这种方式下,你不能让getMovies直接返回结果,而是要把结果通过回调函数传递给调用方,因为Volley是异步的,结果只能在回调里拿到。
总结
优先选择方案一的协程方式,因为它能让你的异步代码逻辑更清晰,避免回调嵌套(回调地狱),而且符合Kotlin现代异步编程的最佳实践。
内容的提问来源于stack exchange,提问作者Darotudeen
相关产品推荐
相关产品推荐

