Android翻新NetworkBoundResource返回null
我正在尝试使用改型和NetworkBoundResource()从第三方API检索数据。但是,当此NetworkBoundResource运行时,它返回null 我试图获取的JSON如下所示:Android翻新NetworkBoundResource返回null,android,kotlin,retrofit,Android,Kotlin,Retrofit,我正在尝试使用改型和NetworkBoundResource()从第三方API检索数据。但是,当此NetworkBoundResource运行时,它返回null 我试图获取的JSON如下所示: { "id":93 "name":"Business" "total":587 "has_next":true &qu
{
"id":93
"name":"Business"
"total":587
"has_next":true
"podcasts":[]
}
希望有人能帮忙!相关代码如下
Podcasts.kt
@Keep
@Entity(
tableName = "podcasts"
)
@Parcelize
data class Podcast(
@PrimaryKey
@ColumnInfo(name = "id")
val id: String,
@ColumnInfo(name = "title")
val title: String,
@ColumnInfo(name = "publisher")
val publisher: String,
@ColumnInfo(name = "image")
val image: String,
@ColumnInfo(name = "thumbnal")
val thumbnail: String,
@ColumnInfo(name = "url")
val url: String,
@ColumnInfo(name = "total_episodes")
@Json(name = "total_episodes")
val total: Int,
@ColumnInfo(name = "favourite")
var favourite: Boolean = false,
) : Parcelable
@Entity(
tableName = "best_podcasts",
indices = [Index(
value = ["id"],
unique = true
)]
)
data class BestPodcasts(
@PrimaryKey
@ColumnInfo(name = "id")
val id: String,
@ColumnInfo(name = "podcasts")
val podcasts: List<Podcast>
)
保持
@实体(
tableName=“播客”
)
@包裹
数据类播客(
@主键
@ColumnInfo(name=“id”)
valid:String,
@ColumnInfo(name=“title”)
val标题:字符串,
@ColumnInfo(name=“publisher”)
val publisher:String,
@ColumnInfo(name=“image”)
val image:String,
@ColumnInfo(name=“thumbnal”)
val缩略图:字符串,
@ColumnInfo(name=“url”)
val url:String,
@ColumnInfo(name=“剧集总数”)
@Json(name=“total_剧集”)
val总计:整数,
@ColumnInfo(name=“收藏夹”)
var favorite:Boolean=false,
):可包裹
@实体(
tableName=“最佳播客”,
索引=[Index(
值=[“id”],
唯一=真
)]
)
数据类最佳播客(
@主键
@ColumnInfo(name=“id”)
valid:String,
@ColumnInfo(name=“播客”)
val播客:列表
)
网络边界资源
abstract class NetworkBoundResource<ResultType, RequestType>
@MainThread constructor(private val appExecutors: AppExecutors) {
//
private val result = MediatorLiveData<Resource<ResultType>>()
init {
result.value = Resource.loading(null)
@Suppress("LeakingThis")
val dbSource = loadFromDb()
result.addSource(dbSource) { data ->
result.removeSource(dbSource)
if (shouldFetch(data)) {
fetchFromNetwork(dbSource)
} else {
result.addSource(dbSource) { newData ->
setValue(Resource.success(newData))
}
}
}
}
@MainThread
private fun setValue(newValue: Resource<ResultType>) {
if (result.value != newValue) {
result.value = newValue
}
}
private fun fetchFromNetwork(dbSource: LiveData<ResultType>) {
val apiResponse = createCall()
// we re-attach dbSource as a new source, it will dispatch its latest value quickly
result.addSource(dbSource) { newData ->
setValue(Resource.loading(newData))
}
result.addSource(apiResponse) { response ->
result.removeSource(apiResponse)
result.removeSource(dbSource)
when (response) {
is ApiSuccessResponse -> {
appExecutors.diskIO().execute {
saveCallResult(processResponse(response))
appExecutors.mainThread().execute {
// we specially request a new live data,
// otherwise we will get immediately last cached value,
// which may not be updated with latest results received from network.
result.addSource(loadFromDb()) { newData ->
setValue(Resource.success(newData))
}
}
}
}
is ApiEmptyResponse -> {
appExecutors.mainThread().execute {
// reload from disk whatever we had
result.addSource(loadFromDb()) { newData ->
setValue(Resource.success(newData))
}
}
}
is ApiErrorResponse -> {
onFetchFailed()
result.addSource(dbSource) { newData ->
setValue(Resource.error(response.errorMessage, newData))
}
}
}
}
}
fun asLiveData() = result as LiveData<Resource<ResultType>>
@WorkerThread
protected open fun processResponse(response: ApiSuccessResponse<RequestType>) = response.body
@WorkerThread
protected abstract fun saveCallResult(item: RequestType)
@MainThread
protected abstract fun shouldFetch(data: ResultType?): Boolean
@MainThread
protected abstract fun loadFromDb(): LiveData<ResultType>
@MainThread
protected abstract fun createCall(): LiveData<ApiResponse<RequestType>>
protected open fun onFetchFailed() {
Log.d("AHHHH", "Fetch failed")
}
}
class RemoteDataSource {
private val podcastApiService = PodcastApi.retrofitService
fun downloadBestPodcasts(): LiveData<ApiResponse<BestPodcasts>> {
return podcastApiService.downloadBestPodcasts()
}
}
private val retrofit = Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(LiveDataCallAdapterFactory())
.baseUrl(BASE_URL)
.build()
interface WebService {
@GET("best_podcasts")
fun downloadBestPodcasts(
@Header("X-ListenAPI-Key") apiKey: String = BuildConfig.API_KEY,
@Query("genre") genreId: Int? = null
): LiveData<ApiResponse<BestPodcasts>>
}
抽象类NetworkBoundResource
@主线程构造函数(私有val appExecutors:appExecutors){
//
private val result=MediatorLiveData()
初始化{
result.value=Resource.loading(null)
@抑制(“泄漏此”)
val dbSource=loadFromDb()
result.addSource(dbSource){data->
result.removeSource(dbSource)
if(shouldFetch(数据)){
fetchFromNetwork(dbSource)
}否则{
result.addSource(dbSource){newData->
setValue(资源成功(新数据))
}
}
}
}
@主线
private-fun-setValue(newValue:Resource){
if(result.value!=newValue){
result.value=newValue
}
}
私有网络(dbSource:LiveData){
val apiResponse=createCall()
//我们将dbSource作为一个新源重新连接,它将快速发送其最新值
result.addSource(dbSource){newData->
setValue(资源加载(新数据))
}
result.addSource(apiResponse){response->
result.removeSource(apiResponse)
result.removeSource(dbSource)
何时(回应){
是APIssuccessResponse->{
appExecutors.diskIO().execute{
saveCallResult(processResponse(response))
appExecutors.mainThread().execute{
//我们特别要求提供新的实时数据,
//否则我们将立即获得最后一个缓存值,
//可能不会使用从网络接收的最新结果更新。
result.addSource(loadFromDb()){newData->
setValue(资源成功(新数据))
}
}
}
}
是ApipEmptyResponse->{
appExecutors.mainThread().execute{
//从磁盘重新加载我们所拥有的一切
result.addSource(loadFromDb()){newData->
setValue(资源成功(新数据))
}
}
}
是ApiErrorResponse->{
onFetchFailed()
result.addSource(dbSource){newData->
setValue(Resource.error(response.errorMessage,newData))
}
}
}
}
}
fun asLiveData()=结果为LiveData
@工作线程
受保护的open fun processResponse(响应:APIssuccessResponse)=响应.body
@工作线程
受保护的抽象趣味saveCallResult(项:RequestType)
@主线
受保护的抽象fun shouldFetch(数据:ResultType?):布尔值
@主线
受保护的抽象数据loadFromDb():LiveData
@主线
受保护的抽象调用():LiveData
受保护的open fun onFetchFailed(){
Log.d(“ahhh”,“获取失败”)
}
}
RemoteDataSource
abstract class NetworkBoundResource<ResultType, RequestType>
@MainThread constructor(private val appExecutors: AppExecutors) {
//
private val result = MediatorLiveData<Resource<ResultType>>()
init {
result.value = Resource.loading(null)
@Suppress("LeakingThis")
val dbSource = loadFromDb()
result.addSource(dbSource) { data ->
result.removeSource(dbSource)
if (shouldFetch(data)) {
fetchFromNetwork(dbSource)
} else {
result.addSource(dbSource) { newData ->
setValue(Resource.success(newData))
}
}
}
}
@MainThread
private fun setValue(newValue: Resource<ResultType>) {
if (result.value != newValue) {
result.value = newValue
}
}
private fun fetchFromNetwork(dbSource: LiveData<ResultType>) {
val apiResponse = createCall()
// we re-attach dbSource as a new source, it will dispatch its latest value quickly
result.addSource(dbSource) { newData ->
setValue(Resource.loading(newData))
}
result.addSource(apiResponse) { response ->
result.removeSource(apiResponse)
result.removeSource(dbSource)
when (response) {
is ApiSuccessResponse -> {
appExecutors.diskIO().execute {
saveCallResult(processResponse(response))
appExecutors.mainThread().execute {
// we specially request a new live data,
// otherwise we will get immediately last cached value,
// which may not be updated with latest results received from network.
result.addSource(loadFromDb()) { newData ->
setValue(Resource.success(newData))
}
}
}
}
is ApiEmptyResponse -> {
appExecutors.mainThread().execute {
// reload from disk whatever we had
result.addSource(loadFromDb()) { newData ->
setValue(Resource.success(newData))
}
}
}
is ApiErrorResponse -> {
onFetchFailed()
result.addSource(dbSource) { newData ->
setValue(Resource.error(response.errorMessage, newData))
}
}
}
}
}
fun asLiveData() = result as LiveData<Resource<ResultType>>
@WorkerThread
protected open fun processResponse(response: ApiSuccessResponse<RequestType>) = response.body
@WorkerThread
protected abstract fun saveCallResult(item: RequestType)
@MainThread
protected abstract fun shouldFetch(data: ResultType?): Boolean
@MainThread
protected abstract fun loadFromDb(): LiveData<ResultType>
@MainThread
protected abstract fun createCall(): LiveData<ApiResponse<RequestType>>
protected open fun onFetchFailed() {
Log.d("AHHHH", "Fetch failed")
}
}
class RemoteDataSource {
private val podcastApiService = PodcastApi.retrofitService
fun downloadBestPodcasts(): LiveData<ApiResponse<BestPodcasts>> {
return podcastApiService.downloadBestPodcasts()
}
}
private val retrofit = Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(LiveDataCallAdapterFactory())
.baseUrl(BASE_URL)
.build()
interface WebService {
@GET("best_podcasts")
fun downloadBestPodcasts(
@Header("X-ListenAPI-Key") apiKey: String = BuildConfig.API_KEY,
@Query("genre") genreId: Int? = null
): LiveData<ApiResponse<BestPodcasts>>
}
类远程数据源{
private val PodcastApi服务=PodcastApi.service
乐趣下载BestPodcast():LiveData{
return podcastApiService.downloadBestPodcasts()
}
}
数据存储库
class DataRepository(
private val localDataSource: LocalDataSource,
private val remoteDataSource: RemoteDataSource,
private val appExecutors: AppExecutors
) {
fun downloadBestPodcasts(shouldFetch: Boolean): LiveData<Resource<BestPodcasts>> {
return object :
NetworkBoundResource<BestPodcasts, BestPodcasts>(appExecutors) {
override fun saveCallResult(item: BestPodcasts) {
localDataSource.insertBestPodcasts(item)
}
override fun shouldFetch(data: BestPodcasts?) = data == null || shouldFetch
override fun loadFromDb(): LiveData<BestPodcasts> =
localDataSource.getBestPodcasts()
override fun createCall() = remoteDataSource.downloadBestPodcasts()
}.asLiveData()
}
}
类数据存储库(
私有val localDataSource:localDataSource,
私有val remoteDataSource:remoteDataSource,
私人val上诉人:上诉人
) {
有趣的下载BestPodcast(shouldFetch:Boolean):LiveData{
返回对象:
NetworkBoundResource(appExecutors){
覆盖有趣的saveCallResult(项目:BestPodcast){
localDataSource.InsertBestPodcast(项目)
}
重写fun shouldFetch(数据:BestPodcasts?=data==null | | shouldFetch)
重写fun loadFromDb():LiveData=
localDataSource.getBestPodcasts()
重写fun createCall()=remoteDataSource.DownloadBestPodcast()
}.asLiveData()
}
}
WebService
abstract class NetworkBoundResource<ResultType, RequestType>
@MainThread constructor(private val appExecutors: AppExecutors) {
//
private val result = MediatorLiveData<Resource<ResultType>>()
init {
result.value = Resource.loading(null)
@Suppress("LeakingThis")
val dbSource = loadFromDb()
result.addSource(dbSource) { data ->
result.removeSource(dbSource)
if (shouldFetch(data)) {
fetchFromNetwork(dbSource)
} else {
result.addSource(dbSource) { newData ->
setValue(Resource.success(newData))
}
}
}
}
@MainThread
private fun setValue(newValue: Resource<ResultType>) {
if (result.value != newValue) {
result.value = newValue
}
}
private fun fetchFromNetwork(dbSource: LiveData<ResultType>) {
val apiResponse = createCall()
// we re-attach dbSource as a new source, it will dispatch its latest value quickly
result.addSource(dbSource) { newData ->
setValue(Resource.loading(newData))
}
result.addSource(apiResponse) { response ->
result.removeSource(apiResponse)
result.removeSource(dbSource)
when (response) {
is ApiSuccessResponse -> {
appExecutors.diskIO().execute {
saveCallResult(processResponse(response))
appExecutors.mainThread().execute {
// we specially request a new live data,
// otherwise we will get immediately last cached value,
// which may not be updated with latest results received from network.
result.addSource(loadFromDb()) { newData ->
setValue(Resource.success(newData))
}
}
}
}
is ApiEmptyResponse -> {
appExecutors.mainThread().execute {
// reload from disk whatever we had
result.addSource(loadFromDb()) { newData ->
setValue(Resource.success(newData))
}
}
}
is ApiErrorResponse -> {
onFetchFailed()
result.addSource(dbSource) { newData ->
setValue(Resource.error(response.errorMessage, newData))
}
}
}
}
}
fun asLiveData() = result as LiveData<Resource<ResultType>>
@WorkerThread
protected open fun processResponse(response: ApiSuccessResponse<RequestType>) = response.body
@WorkerThread
protected abstract fun saveCallResult(item: RequestType)
@MainThread
protected abstract fun shouldFetch(data: ResultType?): Boolean
@MainThread
protected abstract fun loadFromDb(): LiveData<ResultType>
@MainThread
protected abstract fun createCall(): LiveData<ApiResponse<RequestType>>
protected open fun onFetchFailed() {
Log.d("AHHHH", "Fetch failed")
}
}
class RemoteDataSource {
private val podcastApiService = PodcastApi.retrofitService
fun downloadBestPodcasts(): LiveData<ApiResponse<BestPodcasts>> {
return podcastApiService.downloadBestPodcasts()
}
}
private val retrofit = Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(LiveDataCallAdapterFactory())
.baseUrl(BASE_URL)
.build()
interface WebService {
@GET("best_podcasts")
fun downloadBestPodcasts(
@Header("X-ListenAPI-Key") apiKey: String = BuildConfig.API_KEY,
@Query("genre") genreId: Int? = null
): LiveData<ApiResponse<BestPodcasts>>
}
private val reformation=reformation.Builder()
.addConverterFactor