【发布时间】:2021-02-03 02:20:17
【问题描述】:
我使用这个 CodeLabs 教程来学习如何从 Google Books API 发出 HTTP 请求 https://codelabs.developers.google.com/codelabs/kotlin-android-training-internet-data/#4
现在,我正在尝试访问 Google Books API 吐出的嵌套 JSON 对象 即
"items": [{
"kind": "books#volume",
"id": "mOsbHQAACAAJ",
"volumeInfo" : {
"description": "Young wizard Harry Potter finds himself back at the miserable Hogwarts School of Witchcraft and Wizardry. He doesn't realize the difficulty of the task that awaits him. Harry must pull out all the stops in order to find his missing friend. No Canadian Rights for the Harry Potter Series HARRY POTTER and all related characters and elements are trademarks of and (c) Warner Bros. Entertainment Inc. Harry Potter publishing rights (c) J. K. Rowling. (s05)",
"imageLinks": {
"smallThumbnail": "http://books.google.com/books/content?id=mOsbHQAACAAJ&printsec=frontcover&img=1&zoom=5&source=gbs_api",
"thumbnail": "http://books.google.com/books/content?id=mOsbHQAACAAJ&printsec=frontcover&img=1&zoom=1&source=gbs_api"
}
},
我只想要描述和缩略图属性。
我的API服务接口是
package com.example.customapp.network
import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import retrofit2.Call
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
import retrofit2.http.GET
//Code from https://codelabs.developers.google.com/codelabs/kotlin-android-training-internet-data/#3
private const val BASE_URL = "https://www.googleapis.com/books/v1/"
private val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
private val retrofit = Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.baseUrl(BASE_URL)
.build()
interface BookApiService {
//Get annotation specifies the endpoint for this web service method.
//when getProperties() method is invoked, Retrofit appends the endpoint 'book' to the base URL
//And creates a Call object. The Call object is used to start the request.
@GET("volumes?q='harry+potter")
suspend fun getProperties(): BookProperty
}
object BookApi {
val retrofitService: BookApiService by lazy {
retrofit.create(BookApiService::class.java)
}
}
}
我的 BookProperty.kt 是
data class BookProperty(@field:Json(name = "items" ) val bookDetail: List<BookDetail>)
data class BookDetail(@field:Json(name = "volumeInfo") val volumeInfo: VolumeInfo)
data class VolumeInfo(@field:Json(name = "description") val description: String, @field:Json(name= "imageLinks") val imageLink: ImageLink)
data class ImageLink(@field:Json(name = "thumbnail") val thumbnail: String)
我正在从我的 ViewModel 调用 API
val readAllData: LiveData<List<BookItem>>
private val repository: BookRepository
private val _response = MutableLiveData<String>()
val response: LiveData<String>
get() = _response
init {
val bookDao = BookDatabase.getDatabase(application).bookDao()
repository = BookRepository(bookDao)
readAllData = repository.readAllData
}
fun addBook(book: BookItem) {
viewModelScope.launch(Dispatchers.IO) {
repository.addBook(book)
}
}
fun updateBook(book: BookItem) {
viewModelScope.launch(Dispatchers.IO) {
repository.updateBook(book)
}
}
fun getBookDetailProperties() {
viewModelScope.launch {
try {
//calling get properties from the BookApi service creates and starts the network call
//on a background thread
var listResult = BookApi.retrofitService.getProperties()
_response.value = "${
listResult.bookDetail[0].volumeInfo.description} book properties received"
} catch (e: Exception) {
_response.value = "Failure: ${e.message}"
}
}
}
每次我在我的 CRUD 应用程序上更新一个项目时,即当我点击一个按钮时,我都会尝试发出一个 HTTP 请求,但我似乎无法得到任何响应。这是我发起 API 调用的 UpdateFragment。
class UpdateFragment : Fragment() {
//Read up on delegation
//https://codelabs.developers.google.com/codelabs/kotlin-bootcamp-classes/#7
//UpdateFragmentArgs is a class that is automatically generated
//when we created an argument for our Update Fragment in the nav graph
//UpdateFragmentArgs will contain our current book
//we can also use bundle
private val args by navArgs<UpdateFragmentArgs>()
private lateinit var mBookViewModel: BookViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_update, container, false)
//So the keyboard doesn't push the EditText fields up
this.activity?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN)
Glide
.with(this)
.load(args.currentBook.image)
.into(view.bookImageDetail)
mBookViewModel = ViewModelProvider(this).get(BookViewModel::class.java)
view.apply {
updateInputName.setText(args.currentBook.title)
updateInputAuthor.setText(args.currentBook.author)
updateBookDesc.text = args.currentBook.desc
updateRatingBar.rating = args.currentBook.rating.toFloat()
updateBookCompleted.isChecked = args.currentBook.finished
updateBookCompleted.text =
if (updateBookCompleted.isChecked) getString(R.string.book_completed) else getString(
R.string.book_not_completed
)
updateDateCreated.text = getString(R.string.date_created, args.currentBook.dateCreated)
}
view.updateBtn.setOnClickListener {
updateItem()
}
view.updateBookCompleted.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
view.updateBookCompleted.text = getString(R.string.book_completed)
} else {
view.updateBookCompleted.text = getString(R.string.book_not_completed)
}
}
return view
}
private fun updateItem() {
val bookName = updateInputName.text.toString()
val bookAuthor = updateInputAuthor.text.toString()
val bookRating = updateRatingBar.rating.toDouble()
val bookFinished = updateBookCompleted.isChecked
if (inputCheck(bookName, bookAuthor)) {
//***Initiate API call here ****
mBookViewModel.getBookDetailProperties()
//Get description and image from API
mBookViewModel.response.observe(viewLifecycleOwner, {
println("Get resp " + it)
})
//Create book object
val updatedBook = BookItem(
args.currentBook.id,
bookName,
bookAuthor,
args.currentBook.desc,
args.currentBook.image,
bookRating,
args.currentBook.dateCreated,
bookFinished
)
//update current book
mBookViewModel.updateBook(updatedBook)
Toast.makeText(requireContext(), "Updated book successfully!", Toast.LENGTH_SHORT)
.show()
//navigate back
findNavController().navigate(R.id.action_updateFragment_to_listFragment)
}
}
private fun inputCheck(bookName: String, authorName: String): Boolean {
return !(TextUtils.isEmpty(bookName) && TextUtils.isEmpty(authorName))
}
}
问题是我无法从 API 调用中获得任何响应 - 我不确定这是否是因为 JSON 中的嵌套对象。请帮我解释一下,我还是 Kotlin 编程的新手。
【问题讨论】:
-
它必须被询问,因为它很容易被忽视。这是一个全新的项目吗?还记得在 AndroidManifest.xml 中声明 Internet 权限吗?
android.permission.INTERNET此外,该代码实验室的第 3 步要求您打开飞行模式。之后你记得禁用它吗? -
@Conti 感谢您的回复 - 是的,这是一个全新的项目,但是是的,我已经在 AndroidManifest.xml 中声明了 Internet 权限并且我从未打开过飞行模式
标签: android kotlin retrofit2 moshi