首页 > 解决方案 > 如何在 kotlin 协程中正确使用暂停而不是延迟?

问题描述

我正在开发新闻应用程序,并且自从 Retrofit 2.6.0 发布并支持挂起功能以来,我已经实现了 kotlin 协程。我已经在延迟函数上实现了挂起函数并遵循了本教程,但是我得到了空数据我想知道我在哪里犯了错误?我必须进行更正才能正确显示数据。on stacktrace getting following exception kotlinx.coroutines.JobCancellationException: Job was cancelled;job=JobImpl{取消}@677a578

我在 SportInterface.kt 中定义了我的终点

@GET("v2/top-headlines?country=us&apiKey=da331087e3f3462bb534b3b0917cbee9")
     suspend fun getNewsAsync(): SportNewsResponse

以前它在下面

 @GET("v2/top-headlines?country=us&apiKey=da331087e3f3462bb534b3b0917cbee9")
     fun getNewsAsync(): Deferred<SportNewsResponse>

并按照以下方式在 NewsRepository 中获取结果。

interface NewsRepository {
    // Suspend is used to await the result from Deferred
   suspend fun getNewsList(): UseCaseResult<List<Article>>
}


@Suppress("UNCHECKED_CAST")
class NewsRepositoryImpl(private val sportsNewsApi: SportNewsInterface) : NewsRepository {
    override suspend fun getNewsList(): UseCaseResult<List<Article>> {

        return try {
            val result = sportsNewsApi.getNewsAsync().articles
            UseCaseResult.Success(result)
        } catch (ex: Exception) {
            UseCaseResult.Error(ex)
        }
    }
}

在我的 MainViewModel.kt 下面

@Suppress("UNCHECKED_CAST")
class MainViewModel(val newsRepository: NewsRepository) : ViewModel(), CoroutineScope {
    // Coroutine's background job
    val job = Job()
    // Define default thread for Coroutine as Main and add job
    override val coroutineContext: CoroutineContext = Dispatchers.Main + job

    val showLoading = MutableLiveData<Boolean>()
    val sportList = MutableLiveData <List<Article>>()
    val showError = SingleLiveEvent<String>()

    fun loadNews() {
        // Show progressBar during the operation on the MAIN (default) thread
        showLoading.value = true
        // launch the Coroutine
        launch {
            // Switching from MAIN to IO thread for API operation
            // Update our data list with the new one from API
            val result = withContext(Dispatchers.IO) {
                newsRepository?.getNewsList()
            }
            // Hide progressBar once the operation is done on the MAIN (default) thread
            showLoading.value = false
            when (result) {

                is UseCaseResult.Success<*> -> {
                    sportList.value = result.data as List<Article>
                }
                is Error -> showError.value = result.message
            }
        }
    }

    override fun onCleared() {
        super.onCleared()
        // Clear our job when the linked activity is destroyed to avoid memory leaks
        job.cancel()
    }
}

在我的 SportNewsResponse.kt 数据类下方

data class SportNewsResponse(
    val articles: List<Article>,
    val status: String,
    val totalResults: Int
)

下面的文章.kt

@Entity(tableName = "news_table")
data class Article(@ColumnInfo(name = "author")val author: String,
                   val content: String,
                   val description: String,
                   val publishedAt: String,
                   val source: Source,
                   val title: String,
                   val url: String,
                   val urlToImage: String
)

在我来自服务器的 jsonResponse 下面

{
    "status": "ok",
    "totalResults": 38,
    "articles": [
        {
            "source": {
                "id": "cnbc",
                "name": "CNBC"
            },
            "author": "Holly Ellyatt",
            "title": "Russia is now not the only pressing issue that NATO has to deal with - CNBC",
            "description": "Heads of state and government are meeting in the U.K. this week for the 70th anniversary of the military alliance NATO.",
            "url": "https://www.cnbc.com/2019/12/02/nato-summit-alliance-has-more-pressing-issues-than-russia-now.html",
            "urlToImage": "https://image.cnbcfm.com/api/v1/image/106272467-1575218599700gettyimages-997112494.jpeg?v=1575218712",
            "publishedAt": "2019-12-02T07:39:00Z",
            "content": "US president Donald Trump is seen during his press conference at the 2018 NATO Summit in Brussels, Belgium on July 12, 2018.\r\nAs heads of state and government meet in the U.K. this week for the 70th anniversary of the military alliance NATO, discussions are l… [+8623 chars]"
        },
        {
            "source": {
                "id": null,
                "name": "Chron.com"
            },
            "author": "Aaron Wilson",
            "title": "Bill O'Brien gets game ball from Deshaun Watson after Texans' win over Patriots - Chron",
            "description": "In an emotional moment, Texans coach Bill O'Brien was presented with the game ball by quarterback Deshaun Watson following a pivotal win over the New England Patriots.",
            "url": "https://www.chron.com/sports/texans/article/Bill-O-Brien-Deshaun-Watson-Texans-Patriots-14874678.php",
            "urlToImage": "https://s.hdnux.com/photos/01/07/23/50/18692664/3/rawImage.jpg",
            "publishedAt": "2019-12-02T06:16:00Z",
            "content": "<ul><li>Houston Texans head coach Bill O'Brien on the sidelines during the fourth quarter of an NFL game against the New England Patriots at NRG Stadium Sunday, Dec. 1, 2019, in Houston.\r\nHouston Texans head coach Bill O'Brien on the sidelines during the four… [+1583 chars]"


        }
    ]
}

在我的改造实施 appmodules.kt 下面

const val BASE_URL = "https://newsapi.org/"

val appModules = module {
    // The Retrofit service using our custom HTTP client instance as a singleton
    single {
        createWebService<SportNewsInterface>(
            okHttpClient = createHttpClient(),
            factory = RxJava2CallAdapterFactory.create(),
            baseUrl = BASE_URL
        )
    }
    // Tells Koin how to create an instance of CatRepository
    factory<NewsRepository> { (NewsRepositoryImpl(sportsNewsApi = get())) }
    // Specific viewModel pattern to tell Koin how to build MainViewModel
    viewModel { MainViewModel (newsRepository = get ())  }
}

/* Returns a custom OkHttpClient instance with interceptor. Used for building Retrofit service */
fun createHttpClient(): OkHttpClient {
    val client = OkHttpClient.Builder()
    client.readTimeout(5 * 60, TimeUnit.SECONDS)
    return client.addInterceptor {
        val original = it.request()
        val requestBuilder = original.newBuilder()
        requestBuilder.header("Content-Type", "application/json")
        val request = requestBuilder.method(original.method, original.body).build()
        return@addInterceptor it.proceed(request)
    }.build()
}

/* function to build our Retrofit service */
inline fun <reified T> createWebService(
    okHttpClient: OkHttpClient,
    factory: CallAdapter.Factory, baseUrl: String
): T {
    val retrofit = Retrofit.Builder()
        .baseUrl(baseUrl)
        .addConverterFactory(GsonConverterFactory.create(GsonBuilder().setLenient().create()))
        .addCallAdapterFactory(CoroutineCallAdapterFactory())
        .addCallAdapterFactory(factory)
        .client(okHttpClient)
        .build()
    return retrofit.create(T::class.java)

}

在 TopHeadlinesFragment.kt 下方

class TopHeadlinesFragment : Fragment() {

    private val viewModel by viewModel<MainViewModel>()
    private lateinit var topHeadlinesAdapter: TopHeadlinesAdapter
   // private   val newsRepository: NewsRepository by inject()



    //3
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val view = inflater.inflate(
            R.layout.fragment_top_headlines
            , container, false
        )


        val recyclerView = view.findViewById(R.id.recyclerView) as RecyclerView
        val pb = view.findViewById(R.id.pb) as ProgressBar
        topHeadlinesAdapter = TopHeadlinesAdapter(recyclerView.context)
        recyclerView.layoutManager = LinearLayoutManager(context)
        recyclerView.adapter = topHeadlinesAdapter
        initViewModel()

        return view
    }

    private fun initViewModel() {
        viewModel?.sportList?.observe(this, Observer { newList ->
            topHeadlinesAdapter.updateData(newList)
        })

        viewModel?.showLoading?.observe(this, Observer { showLoading ->
            pb.visibility = if (showLoading) View.VISIBLE else View.GONE
        })

        viewModel?.showError?.observe(this, Observer { showError ->
            (showError)
        })

        viewModel?.loadNews()
    }
}

标签: androidkotlinmvvmkotlin-coroutinesretrofit2.6

解决方案


推荐阅读