我正在开发一个 Kotlin 多平台应用程序,我使用 Ktor 来进行网络调用,并使用 SqlDelite 来管理本地数据源。
因此,当您创建 KMM 项目时,我尝试实现与构建 Android Studio 相同的结构。
我的问题是我用来异步连接的 api,我需要传递两个标头才能在其中进行身份验证。我尝试在我的 HttpClient 中使用标头,并在我的客户端的 get 方法中使用参数。
我不知道这是正确的方法,还是看起来不是。
关于我的后端设置有以下类:
UnsplashApi.kt
class UnsplashApi(private val client: HttpClient = ktorClient) {
suspend fun getPets(): List<UnsplashPhoto> = client.get(UNSPLASH_URL){
parameter("query", "cats")
parameter("page", 1)
parameter("per_page", 10)
}
//TODO("Actualizar el endpoint para incorporar la funcionalidad de buscar por nombre")
suspend fun getPetsByName(pet: String): List<UnsplashPhoto> = client.get(UNSPLASH_URL){
headers{
append(HttpHeaders.Accept, "v1")
append("Authorization:","Client-ID $CLIENT_ID")
}
parameter("query", pet)
parameter("page", 1)
parameter("per_page", 10)
}
}
internal val kotlinxSerializer = KotlinxSerializer(
Json(
JsonConfiguration(isLenient = true, ignoreUnknownKeys = true)
)
)
internal val ktorClient = HttpClient {
defaultRequest {
header("Authorization:", "Client-ID $CLIENT_ID")
header("Accept-Version:","v1")
}
install(JsonFeature) {
serializer = kotlinxSerializer
}
install(Logging) {
logger = Logger.DEFAULT
level = LogLevel.ALL
}
}
PetsRepository.kt
internal expect fun cache(): PetsDatabase
class PetsRepository(
private val api: UnsplashApi,
private val queries: PetsDBQueries = cache().petsDBQueries
) {
constructor() : this(api = UnsplashApi())
/**
* If [force] is set to true, attempt to load data from remote api.
* If remote api is not available. throw [RefreshDataException]
*
* If [force] is set to false, attempt to load data from local cache.
* If local cache is not available, propagate the exception encountered
*/
fun fetchMembersAsFlow(force: Boolean): Flow<List<Pets>> {
return if (force) getMembersFromRemote() as Flow<List<Pets>> else getMembersFromCache()
}
private fun cacheMembers(pets: List<Pets>) {
queries.deleteAll()
pets.forEach { pet ->
queries.insertPet(
pet.id,
pet.description,
pet.url,
pet.email
)
}
}
/**
* Retorna un Flow con una lista de Pets, y guardando en la BD local
* los pets recuperados de la llamada de red.
*/
private fun getMembersFromRemote(): Flow<List<UnsplashPhoto>> {
println("Getting members from remote")
return flow {
val unsplashPhotos = api.getPets()
Log.d("info", "size pets llamada de red: ${unsplashPhotos.size}")
Log.d("info", "pets $unsplashPhotos")
//TODO(""Convert UnplashPhoto o Pets")
var pets : List<Pets> = unsplashPhotos.map {
Pets(it.id!!.toLong(), it.description!!, it.urls.full, it.user.name)
}
cacheMembers(pets)
emit(api.getPets())
}
.catch { error(RefreshDataException()) }
.flowOn(applicationDispatcher)
}
private fun getMembersFromCache(): Flow<List<Pets>> {
println("Getting members from cache")
fun loadMembers() = queries.selectAll()
.executeAsList()
.map { Pets(id = it.id, description = it.description, url = it.url, email = it.emailUser) }
return flow { emit(loadMembers()) }
.catch { error(RefreshDataException()) }
.flowOn(applicationDispatcher)
}
}
关于我的 androidApp 文件夹分类:
PetsViewModel.kt
class PetsViewModel(
private val repository: PetsRepository
) : ViewModel() {
private val _pets = MutableLiveData<List<Pets>>()
val pets: LiveData<List<Pets>> = _pets
private val _error = MutableLiveData<Throwable>()
val error: LiveData<Throwable> = _error
private val _isRefreshing = MutableLiveData<Boolean>()
val isRefreshing: LiveData<Boolean> = _isRefreshing
init {
loadPets()
}
fun loadPets(force: Boolean = false) {
viewModelScope.launch {
repository.fetchMembersAsFlow(force)
.onStart {
_isRefreshing.value = true
}.onCompletion {
_isRefreshing.value = false
}.catch {
_error.value = it
Log.d("info", "STACKTRACE: ${it.stackTrace.contentToString()} + ${it.cause}")
}.collect {
_pets.value = it
}
}
}
}
宠物适配器.kt
class PatsAdapter(var pets: List<Pets>) : RecyclerView.Adapter<PatsAdapter.MemberViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MemberViewHolder {
val itemView =
LayoutInflater.from(parent.context).inflate(R.layout.list_item_member, parent, false)
return MemberViewHolder(itemView)
}
override fun getItemCount() = pets.size
override fun onBindViewHolder(holder: MemberViewHolder, position: Int) {
holder.bind(pets[position])
}
class MemberViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(pet: Pets) {
Picasso.get().load(pet.url).into(itemView.memberAvatar)
itemView.tvItemDescription.text = pet.description
itemView.tvItemAutor.text = pet.email
}
}
}
MainActivity.kt
class MainActivity : AppCompatActivity() {
private val repository by lazy {
(application as Application).membersRepository
}
private val viewModel by lazy { PetsViewModel(repository) }
private lateinit var adapter: PatsAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(layout.activity_main)
platformMessage.text = createPlatformMessage()
setupRecyclerView()
viewModel.pets.observe(this, Observer {
if (it.isEmpty()) {
Toast.makeText(this, R.string.empty_cache, Toast.LENGTH_LONG).show()
} else {
showData(it)
}
})
viewModel.error.observe(this, Observer {
showError(it)
})
viewModel.isRefreshing.observe(this, Observer {
pullToRefresh.isRefreshing = it
})
pullToRefresh.setOnRefreshListener {
viewModel.loadPets(force = true)
}
}
private fun showData(pets: List<Pets>) {
adapter.pets = pets
adapter.notifyDataSetChanged()
}
private fun showError(error: Throwable) {
val errorMessage = when (error) {
is RefreshDataException -> getString(string.refresh_data_error)
else -> getString(string.unknown_error)
}
Toast.makeText(this, errorMessage, Toast.LENGTH_LONG).show()
}
private fun setupRecyclerView() {
membersRecyclerView.layoutManager = LinearLayoutManager(this)
adapter = PatsAdapter(emptyList())
membersRecyclerView.adapter = adapter
}
}
应用程序.kt
class Application : Application() {
val membersRepository by lazy { PetsRepository() }
override fun onCreate() {
super.onCreate()
appContext = this
if (BuildConfig.DEBUG) {
Timber.plant(Timber.DebugTree())
}
}
}
我不包括 sqlDelite 的部分,因为与这个问题的主要目标无关,这是 Api.kt 类的结构,其中 Ktor 用于建立网络连接。
[编辑]
添加了请求的logcat:
2021-12-21 11:33:23.706 6813-6866/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-1] INFO io.ktor.client.HttpClient - REQUEST: https://api.unsplash.com/?client_id=7DQCWc0Hr_GGIOUeMAVKxqvz9lsVtCpvauRvXJnNq_E%2Fsearch%2Fphotos&query=cats&page=1&per_page=10
2021-12-21 11:33:23.707 6813-6866/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-1] INFO io.ktor.client.HttpClient - METHOD: HttpMethod(value=GET)
2021-12-21 11:33:23.708 6813-6866/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-1] INFO io.ktor.client.HttpClient - COMMON HEADERS
2021-12-21 11:33:23.709 6813-6866/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-1] INFO io.ktor.client.HttpClient - -> Accept-Version: v1
2021-12-21 11:33:23.709 6813-6866/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-1] INFO io.ktor.client.HttpClient - -> Accept: application/json
2021-12-21 11:33:23.709 6813-6866/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-1] INFO io.ktor.client.HttpClient - -> Accept-Charset: UTF-8
2021-12-21 11:33:23.709 6813-6866/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-1] INFO io.ktor.client.HttpClient - CONTENT HEADERS
2021-12-21 11:33:23.711 6813-6866/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-1] INFO io.ktor.client.HttpClient - BODY Content-Type: null
2021-12-21 11:33:23.763 6813-6866/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-1] INFO io.ktor.client.HttpClient - BODY START
2021-12-21 11:33:23.763 6813-6866/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-1] INFO io.ktor.client.HttpClient -
2021-12-21 11:33:23.763 6813-6866/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-1] INFO io.ktor.client.HttpClient - BODY END
2021-12-21 11:33:23.799 6813-6878/com.jshvarts.kmp.android D/NetworkSecurityConfig: No Network Security Config specified, using platform default
2021-12-21 11:33:23.800 6813-6878/com.jshvarts.kmp.android I/DpmTcmClient: RegisterTcmMonitor from: $Proxy0
[编辑]
像这样更改我的 api.kt 类后:
class UnsplashApi(private val client: HttpClient = ktorClient) {
suspend fun getPets(): List<UnsplashPhoto> = client.get(UNSPLASH_URL){
//parameter("client_id", CLIENT_ID)
parameter("query", "cats")
parameter("page", 1)
parameter("per_page", 10)
}
//TODO("Actualizar el endpoint para incorporar la funcionalidad de buscar por nombre")
suspend fun getPetsByName(pet: String): List<UnsplashPhoto> = client.get(UNSPLASH_URL){
parameter("query", pet)
parameter("page", 1)
parameter("per_page", 10)
}
}
internal const val UNSPLASH_URL = "https://api.unsplash.com/search/photos"
private const val CLIENT_ID : String = ....
internal val kotlinxSerializer = KotlinxSerializer(
Json(
JsonConfiguration(isLenient = true, ignoreUnknownKeys = true)
)
)
internal val ktorClient = HttpClient {
defaultRequest {
header("Accept-Version","v1")
header("Authorization", "Client-ID $CLIENT_ID")
}
install(JsonFeature) {
serializer = kotlinxSerializer
}
install(Logging) {
logger = Logger.DEFAULT
level = LogLevel.ALL
}
}
我开始在 logcat 中得到这个:
2021-12-21 11:53:32.715 7396-7471/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-7] INFO io.ktor.client.HttpClient - RESPONSE: 200 OK
2021-12-21 11:53:32.716 7396-7471/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-7] INFO io.ktor.client.HttpClient - METHOD: HttpMethod(value=GET)
2021-12-21 11:53:32.718 7396-7450/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-1] INFO io.ktor.client.HttpClient - BODY Content-Type: application/json
2021-12-21 11:53:32.718 7396-7450/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-1] INFO io.ktor.client.HttpClient - BODY START
2021-12-21 11:53:32.725 7396-7471/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-7] INFO io.ktor.client.HttpClient - FROM: https://api.unsplash.com/search/photos?query=cats&page=1&per_page=10
2021-12-21 11:53:32.725 7396-7471/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-7] INFO io.ktor.client.HttpClient - COMMON HEADERS
2021-12-21 11:53:32.725 7396-7471/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-7] INFO io.ktor.client.HttpClient - -> Accept-Ranges: bytes
2021-12-21 11:53:32.725 7396-7471/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-7] INFO io.ktor.client.HttpClient - -> Access-Control-Allow-Headers: *
2021-12-21 11:53:32.726 7396-7471/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-7] INFO io.ktor.client.HttpClient - -> Access-Control-Allow-Origin: *
2021-12-21 11:53:32.726 7396-7471/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-7] INFO io.ktor.client.HttpClient - -> Access-Control-Expose-Headers: Link,X-Total,X-Per-Page,X-RateLimit-Limit,X-RateLimit-Remaining
2021-12-21 11:53:32.726 7396-7471/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-7] INFO io.ktor.client.HttpClient - -> Access-Control-Request-Method: *
2021-12-21 11:53:32.726 7396-7471/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-7] INFO io.ktor.client.HttpClient - -> Age: 445
2021-12-21 11:53:32.726 7396-7471/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-7] INFO io.ktor.client.HttpClient - -> Cache-Control: no-cache, no-store, must-revalidate
2021-12-21 11:53:32.726 7396-7471/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-7] INFO io.ktor.client.HttpClient - -> Connection: keep-alive
2021-12-21 11:53:32.726 7396-7471/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-7] INFO io.ktor.client.HttpClient - -> Content-Type: application/json
2021-12-21 11:53:32.727 7396-7471/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-7] INFO io.ktor.client.HttpClient - -> Date: Tue, 21 Dec 2021 10:53:33 GMT
2021-12-21 11:53:32.727 7396-7471/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-7] INFO io.ktor.client.HttpClient - -> Etag: W/"9545b4e1f85112ca6cc4c543c08a7a37"
2021-12-21 11:53:32.727 7396-7471/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-7] INFO io.ktor.client.HttpClient - -> Link: <https://api.unsplash.com/search/photos?page=1000&per_page=10&query=cats>; rel="last", <https://api.unsplash.com/search/photos?page=2&per_page=10&query=cats>; rel="next"
2021-12-21 11:53:32.727 7396-7471/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-7] INFO io.ktor.client.HttpClient - -> Server: Cowboy
2021-12-21 11:53:32.727 7396-7471/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-7] INFO io.ktor.client.HttpClient - -> Strict-Transport-Security: max-age=31536000; includeSubDomains
2021-12-21 11:53:32.727 7396-7471/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-7] INFO io.ktor.client.HttpClient - -> Vary: Accept-Encoding, Origin,Authorization,Accept-Language,client-geo-region,Accept
2021-12-21 11:53:32.728 7396-7471/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-7] INFO io.ktor.client.HttpClient - -> Via: 1.1 vegur, 1.1 varnish, 1.1 varnish
2021-12-21 11:53:32.728 7396-7471/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-7] INFO io.ktor.client.HttpClient - -> Warning: The tags property in this endpoint is deprecated. https://changelog.unsplash.com/deprecations/2021/07/12/tags-search-deprecation.html
2021-12-21 11:53:32.728 7396-7471/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-7] INFO io.ktor.client.HttpClient - -> X-Android-Received-Millis: 1640084012689
2021-12-21 11:53:32.728 7396-7471/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-7] INFO io.ktor.client.HttpClient - -> X-Android-Response-Source: NETWORK 200
2021-12-21 11:53:32.728 7396-7471/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-7] INFO io.ktor.client.HttpClient - -> X-Android-Selected-Protocol: http/1.1
2021-12-21 11:53:32.728 7396-7471/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-7] INFO io.ktor.client.HttpClient - -> X-Android-Sent-Millis: 1640084012597
2021-12-21 11:53:32.729 7396-7471/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-7] INFO io.ktor.client.HttpClient - -> X-Cache: MISS, HIT
2021-12-21 11:53:32.729 7396-7471/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-7] INFO io.ktor.client.HttpClient - -> X-Cache-Hits: 0, 1
2021-12-21 11:53:32.729 7396-7471/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-7] INFO io.ktor.client.HttpClient - -> X-Per-Page: 10
2021-12-21 11:53:32.729 7396-7471/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-7] INFO io.ktor.client.HttpClient - -> X-Ratelimit-Limit: 50
2021-12-21 11:53:32.729 7396-7471/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-7] INFO io.ktor.client.HttpClient - -> X-Ratelimit-Remaining: 49
2021-12-21 11:53:32.729 7396-7471/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-7] INFO io.ktor.client.HttpClient - -> X-Request-Id: 6d958f7f-491a-42b9-8f48-47670b8c5836
2021-12-21 11:53:32.729 7396-7471/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-7] INFO io.ktor.client.HttpClient - -> X-Runtime: 0.086386
2021-12-21 11:53:32.730 7396-7471/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-7] INFO io.ktor.client.HttpClient - -> X-Served-By: cache-iad-kcgs7200028-IAD, cache-mad22057-MAD
2021-12-21 11:53:32.730 7396-7471/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-7] INFO io.ktor.client.HttpClient - -> X-Timer: S1640084013.381487,VS0,VE1
2021-12-21 11:53:32.730 7396-7471/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-7] INFO io.ktor.client.HttpClient - -> X-Total: 10000
2021-12-21 11:53:32.730 7396-7471/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-7] INFO io.ktor.client.HttpClient - -> X-Unsplash-Version: v1
2021-12-21 11:53:32.887 7396-7451/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-2] INFO io.ktor.client.HttpClient - {"total":10000,"total_pages":1000,"results":[{"id":"ZCHj_2lJP00","created_at":"2020-06-15T00:30:27-04:00","updated_at":"2021-12-20T07:12:05-05:00","promoted_at":"2020-06-15T04:16:29-04:00","width":5304,"height":7952,"color":"#a6d9d9","blur_hash":"LRJcqDIUL3s..mX8rXRPOZnirWXT","description":null,"alt_description":"white and brown long fur cat","urls":{"raw":"https://images.unsplash.com/photo-1592194996308-7b43878e84a6?ixid=MnwyMjczNDJ8MHwxfHNlYXJjaHwxfHxjYXRzfGVufDB8fHx8MTY0MDA4MzU2OA\u0026ixlib=rb-1.2.1","full":"https://images.unsplash.com/photo-1592194996308-7b43878e84a6?crop=entropy\u0026cs=srgb\u0026fm=jpg\u0026ixid=MnwyMjczNDJ8MHwxfHNlYXJjaHwxfHxjYXRzfGVufDB8fHx8MTY0MDA4MzU2OA\u0026ixlib=rb-1.2.1\u0026q=85","regular":"https://images.unsplash.com/photo-1592194996308-7b43878e84a6?crop=entropy\u0026cs=tinysrgb\u0026fit=max\u0026fm=jpg\u0026ixid=MnwyMjczNDJ8MHwxfHNlYXJjaHwxfHxjYXRzfGVufDB8fHx8MTY0MDA4MzU2OA\u0026ixlib=rb-1.2.1\u0026q=80\u0026w=1080","small":"https://images.unsplash.com/photo-1592194996308-7b43878e84a6?crop=entropy\u0026cs=tinysrgb\u0026fit=max\u0026fm=jpg\u0026ixid=MnwyMjczNDJ8MHwxfHNlYXJjaHwxfHxjYXRzfGVufDB8fHx8MTY0MDA4MzU2OA\u0026ixlib=rb-1.2.1\u0026q=80\u0026w=400","thumb":"https://images.unsplash.com/photo-1592194996308-7b43878e84a6?crop=entropy\u0026cs=tinysrgb\u0026fit=max\u0026fm=jpg\u0026ixid=MnwyMjczNDJ8MHwxfHNlYXJjaHwxfHxjYXRzfGVufDB8fHx8MTY0MDA4MzU2OA\u0026ixlib=rb-1.2.1\u0026q=80\u0026w=200"},"links":{"self":"https://api.unsplash.com/photos/ZCHj_2lJP00","html":"https://unsplash.com/photos/ZCHj_2lJP00","download":"https://unsplash.com/photos/ZCHj_2lJP00/download?ixid=MnwyMjczNDJ8MHwxfHNlYXJjaHwxfHxjYXRzfGVufDB8fHx8MTY0MDA4MzU2OA","download_location":"https://api.unsplash.com/photos/ZCHj_2lJP00/download?ixid=MnwyMjczNDJ8MHwxfHNlYXJjaHwxfHxjYXRzfGVufDB8fHx8MTY0MDA4MzU2OA"},"categories":[],"likes":882,"liked_by_user":false,"current_user_collections":[],"sponsorship":null,"topic_submissions":{"animals":{"status":"approved","approved_on":"2020-06-16T07:38:49-04:00"},"wallpapers":{"status":"approved","approved_on":"2021-04-23T06:55:04-04:00"}},"user":{"id":"1LMzZNX562k","updated_at":"2021-12-21T05:20:13-05:00","username":"alvannee","name":"Alvan Nee","first_name":"Alvan","last_name":"Nee","twitter_username":"Alvan Nee","portfolio_url":null,"bio":"I really love unsplash!!!!!","location":"Shanghai, China","links":{"self":"https://api.unsplash.com/users/alvannee","html":"https://unsplash.com/@alvannee","photos":"https://api.unsplash.com/users/alvannee/photos","likes":"https://api.unsplash.com/users/alvannee/likes","portfolio":"https://api.unsplash.com/users/alvannee/portfolio","following":"https://api.unsplash.com/users/alvannee/following","followers":"https://api.unsplash.com/users/alvannee/followers"},"profile_image":{"small":"https://images.unsplash.com/profile-1617947361627-4a8765a9b014image?ixlib=rb-1.2.1\u0026q=80\u0026fm=jpg\u0026crop=faces\u0026cs=tinysrgb\u0026fit=crop\u0026h=32\u0026w=32","medium":"https://images.unsplash.com/profile-1617947361627-4a8765a9b014image?ixlib=rb-1.2.1\u0026q=80\u0026fm=jpg\u0026crop=faces\u0026cs=tinysrgb\u0026fit=crop\u0026h=64\u0026w=64","large":"https://images.unsplash.com/profile-1617947361627-4a8765a9b014image?ixlib=rb-1.2.1\u0026q=80\u0026fm=jpg\u0026crop=faces\u0026cs=tinysrgb\u0026fit=crop\u0026h=128\u0026w=128"},"instagram_username":"alvan_nee","total_collections":0,"total_likes":68,"total_photos":191,"accepted_tos":true,"for_hire":false,"social":{"instagram_username":"alvan_nee","portfolio_url":null,"twitter_username":"Alvan Nee","paypal_email":null}},"tags":[{"type":"landing_page","title":"cat","source":{"ancestry":{"type":{"slug":"images","pretty_slug":"Images"},"category":{"slug":"animals","pretty_slug":"Animals"},"subcategory":{"slug":"cat","pretty_slug":"Cat"}},"title":"Cat Images \u0026 Pictures","subtitle":"Download free cat images","description":"9 lives isn't enough to capture the amazing-ness of cats. You need high-quality, professio
2021-12-21 11:53:32.887 7396-7451/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-2] INFO io.ktor.client.HttpClient - BODY END
2021-12-21 11:53:32.894 7396-7471/com.jshvarts.kmp.android W/System.err: [DefaultDispatcher-worker-7] INFO io.ktor.client.HttpClient - RESPONSE https://api.unsplash.com/search/photos?query=cats&page=1&per_page=10 failed with exception: kotlinx.serialization.json.JsonDecodingException: Unexpected JSON token at offset 0: Expected '[, kind: LIST'.
我正在阅读一些论坛,可能与解码响应的需要有关。
正如我所说,我真的只是开始 which ktor 并且我不知道如何正确实施
提前感谢!