在我的项目中,我遇到了一些有关在MVVM模式中使用Kotlin,Retrofit和RxJava返回嵌套JSON数据的问题。
我对RxJava,MVVM和Retrofit还是比较陌生,但仍然很困惑,但是最后我想得到一个当前嵌套在JSON中的选择类别列表。
JSON响应看起来像这样...
{
"status": "ok",
"totalResults": 38,
"articles": [
{
"source": {
"id": "business-insider",
"name": "Business Insider"
},
"author": "Hayley Peterson",
"title": "Amazon executive was killed after colliding with a van delivering the company's packages, report reveals - Business Insider",
"description": "\"I heard a scream, immediately followed by a crash,\" the van's driver testified, according to the report.",
"url": "https://www.businessinsider.com/amazons-joy-covey-killed-company-delivery-van-report-2019-12",
"urlToImage": "https://image.businessinsider.com/5e01376e855cc215577a09f1?width=1200&format=jpeg",
"publishedAt": "2019-12-23T22:32:01Z",
"content": "The former Amazon executive Joy Covey was killed after colliding with a van delivering Amazon packages, according to an explosive investigation into the company's logistics network by BuzzFeed News and ProPublica. \r\nCovey was Amazon's first chief financial of… [+1462 chars]"
},
...
我的数据类看起来像这样...
data class Base (
@SerializedName("status") val status : String,
@SerializedName("totalResults") val totalResults : Int,
@SerializedName("articles") val articles : List<Story>
)
data class Story (
@SerializedName("source") val source : Source,
@SerializedName("author") val author : String,
@SerializedName("title") val title : String,
@SerializedName("description") val description : String,
@SerializedName("url") val url : String,
@SerializedName("urlToImage") val urlToImage : String,
@SerializedName("publishedAt") val publishedAt : String,
@SerializedName("content") val content : String
)
data class Source (
@SerializedName("id") val id : String,
@SerializedName("name") val name : String
)
我首先将我的API与@GET注释一起使用,以使此代码获得头条新闻...
interface StoriesApi {
@GET("v2/top-headlines?country=us&apiKey=###MYKEY###")
fun getStories(): Single<List<Story>>
}
我的StoriesService依次使用哪一个来获取Single>
class StoriesService {
@Inject
lateinit var api: StoriesApi
init {
DaggerApiComponent.create().inject(this)
}
fun getStories(): Single<List<Story>> {
return api.getStories()
}
}
最后,我使用下面的代码在ViewModel中调用它...
private fun fetchStories() {
loading.value = true
disposable.add(
storiesService.getStories()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(object: DisposableSingleObserver<List<Story>>() {
override fun onSuccess(value: List<Story>?) {
stories.value = value
storyLoadError.value = false
loading.value = false
}
override fun onError(e: Throwable?) {
storyLoadError.value = true
loading.value = false
}
})
)
}
有什么办法我只能输入JSON的文章部分,这样我就不必对整个JSON响应进行过多摆弄?我最终只想获得文章的Single>,而不是“ status”和“ totalResults”。
您实际上似乎在这里没有使用Retrofit。这是在改造和协同程序中的操作方法-如果您不熟悉该主题,则可能需要研究协同程序:
data class ApiResultModel (
val totalResults : Int
)
data class Story (
val source : Source,
val author : String,
val title : String,
val description : String,
val url : String,
val urlToImage : String,
val publishedAt : String,
val content : String
)
构建api接口:
interface StoriesApiService {
@GET("top-headlines" - you insert just the end point here, the search parameters should go bellow because you might need to change them)
fun getStories(country: String = us,
apiKey: String = {your api key}
): Deferred<ApiResultModel>
}
然后创建一个包含所有这些的对象MyApi:
private val okHttpClient = OkHttpClient.Builder()
.build()
private val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
private val retrofit = Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.baseUrl({the base url of the API})
.client(okHttpClient)
.build()
object MyApi {
val storiesApiService: StoriesApiService by lazy {
retrofit.create(StoriesApiService::class.java)
}
}
然后,为了访问来自API的数据,您将需要一个协程范围来进行调用:
private val job = Job()
private val coroutineScope = CoroutineScope(Job() + Dispatchers.Main)
coroutineScope.launch {
val apiResult = MyApi.storiesApiService.getstories().await()
}
该代码可能仍然存在一些拼写错误,因此,如果您打算使用此方法进行API调用,请再次检查。
您还应该观看由Google开发人员创建的有关Udacity的免费教程。我使用了与他们相同的方法,但是他们有更多的解释。
请尝试一下
interface StoriesApi {
@GET("v2/top-headlines?country=us&apiKey=###MYKEY###")
fun getStories(): Single<Story> /* change this */
}
class StoriesService {
@Inject
lateinit var api: StoriesApi
init {
DaggerApiComponent.create().inject(this)
}
/* Change this*/
fun getStories(): Single<Story> {
return api.getStories()
}
}
和视图模型
.subscribeWith(object: DisposableSingleObserver<Story>() {
override fun onSuccess(value: Story?) {
//this a list of articles List that you won
stories.value = value.articles
storyLoadError.value = false
loading.value = false
}
override fun onError(e: Throwable?) {
storyLoadError.value = true
loading.value = false
}
})
希望获得帮助
步骤:1创建改造单例类
object RetrofitClient {
var loggingInterceptor = HttpLoggingInterceptor()
.setLevel(HttpLoggingInterceptor.Level.BASIC)
var okHttpClient = OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.build()
var service: ApiInterface
init {
val retrofit = Retrofit.Builder().baseUrl(BASE_URL)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build()
service = retrofit.create(ApiInterface::class.java)
}
}
步骤:2 Api界面定义您的api
interface ApiInterface {
@GET("comments")
fun getComments(
@Query("id") postId: Int
): Observable<List<Comments>> (For rx java adapter)
@GET("comments")
fun getComments(
@Query("id") postId: Int
): Call<List<Comments>> (For retrofit call adapter)
}
步骤:3存储库
class CommentsRepository {
private val TAG = AuthRepository::class.java.getSimpleName()
private var apiRequest = RetrofitClient.service
var data = MutableLiveData<List<Comments>>()
fun getPostComments(id: Int): LiveData<List<Comments>> {
data = MutableLiveData()
apiRequest.getComments(id).enqueue(object : Callback<List<Comments>> {
override fun onResponse(
call: Call<List<Comments>>,
response: Response<List<Comments>>
) {
if (response.isSuccessful) {
data.value = response.body()
}
}
override fun onFailure(call: Call<List<Comments>>, t: Throwable) {
Log.e(TAG, "FFFeeeild:: " + t.message)
}
})
return data
}
fun getPostComments(id: Int): LiveData<List<Comments>> { //For call object
data = MutableLiveData()
apiRequest.getComments(id)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<List<Comments>> {
override fun onComplete() {
}
override fun onSubscribe(d: Disposable) {
}
override fun onNext(commentList: List<Comments>) {
data.value = commentList
}
override fun onError(e: Throwable) {
Log.e(TAG, e.toString())
}
})
return data
}
}
步骤:4 ViewModel
class CommentsViewModel(application: Application):AndroidViewModel(application){
private val repository: CommentsRepository = CommentsRepository()
private var postId: Int = -1
lateinit var userPost: LiveData<List<Comments>>
fun getPostComments(id: Int): LiveData<List<Comments>> {
this.postId = id
userPost = repository.getPostComments(id)
return userPost
}
}
步骤:5观察活动或破裂中的实时数据
class CommentsActivity : AppCompatActivity() {
private lateinit var viewModel: CommentsViewModel
viewModel = ViewModelProvider(this ).get(CommentsViewModel::class.java)
viewModel.getPostComments(1).observe(this, object : Observer<List<Comments>> {
override fun onChanged(commentsList: List<Comments>?) {
commentsList?.forEach {
it.apply {
var content = ""
content += "ID: $id\n"
content += "Post ID: $postId\n"
content += "Name: $name\n"
content += "Email: $email\n"
content += "Text: $text\n\n"
text_view_result.append(content)
}
}
}
})
}
还请确保添加必要的库。
// Retrofit
def retrofitVersion = "2.5.0"
implementation "com.squareup.retrofit2:retrofit:$retrofitVersion"
implementation "com.squareup.retrofit2:converter-gson:$retrofitVersion"
// ViewModel and LiveData
def lifecycle_version = '2.2.0-alpha03'
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
implementation "com.squareup.okhttp3:okhttp:4.2.2"
implementation 'com.squareup.okhttp3:logging-interceptor:3.12.1'
//Rx android library
implementation 'io.reactivex.rxjava2:rxjava:2.1.9'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
希望它将帮助您让我知道是否还有任何疑问或遇到任何问题。