Я действительно запутался, и мне нужна ваша помощь! Это всего лишь мое второе приложение, и я впервые работаю с REST API. Я просто пытаюсь отобразить некоторую информацию о пользователе, такую как имя и изображение профиля. Он отлично работает, когда я использую код в основном действии, но как только я использую для него другой класс, вызов API терпит неудачу, и код очень похож, поэтому я ничего не знаю. Поскольку Twitter использует Retrofit в своем собственном руководстве, я также использую его.
Мой класс, расширяющий TwitterApiClient, файл также включает интерфейс для пользовательской службы:
import android.util.Log
import com.twitter.sdk.android.core.*
import com.twitter.sdk.android.core.models.User
import retrofit2.Call
import retrofit2.http.GET
import retrofit2.http.Query
class MyTwitterApiClient(session: TwitterSession) : TwitterApiClient(session) {
fun getCustomService() : GetUsersShowAPICustomService {
return getService(GetUsersShowAPICustomService::class.java)
}
}
interface GetUsersShowAPICustomService {
@GET("/1.1/users/show.json")
fun show(@Query("user_id") userId: Long) : Call<User>
}
Мой метод в MainActivity выглядит так:
private fun loadTwitterAPI(userID: Long) {
MyTwitterApiClient(session).getCustomService().show(userID).enqueue(object : Callback<User>() {
override fun success(result: Result<User>?) {
text.text = (
"Name: "+result!!.data.name
+"\nLocation: "+result!!.data.location
+"\nFriends: "+result!!.data.friendsCount
)
Picasso.with(baseContext).load(result!!.data.profileImageUrl).resize(250, 250).into(imageView)
}
override fun failure(exception: TwitterException?) {
}
})
}
Это отлично работает, но я не хочу, чтобы сам вызов был в моей основной деятельности, и я создал сопутствующий объект в своем классе, расширяющий TwitterApi, который должен просто вызываться с параметром TwitterSession в качестве параметра, и он должен возвращать объект класса Пользователь, который содержит все важные данные.
Сопутствующий объект внутри класса MyTwitterApiClient выглядит следующим образом:
companion object {
fun start(session: TwitterSession): User {
val userID = session.userId
var data: User? = null
MyTwitterApiClient(session).getCustomService().show(userID).enqueue(object : Callback<User>() {
override fun success(result: Result<User>?) {
data = result!!.data
}
override fun failure(exception: TwitterException?) {
throw exception!!
}
})
return data!!
}
}
Новый метод в MainActivity выглядит так:
private fun loadTwitterAPI(userID: Long) {
val t = MyTwitterApiClient.start(session)
text.text = (
"Name: "+t.name
+"\nLocation: "+t.location
+"\nFriends: "+t.friendsCount
)
Picasso.with(baseContext).load(t.profileImageUrl).resize(250, 250).into(imageView)
}
В ходе тестирования я обнаружил, что ни метод успеха, ни метод отказа не вызываются. И я вообще не понимаю, почему он не вызывает никакого метода и просто не работает.
Если кто-то здесь уже работал с чем-то подобным или у него есть совет для меня, это было бы очень полезно!
Привет
Кстати: ошибка, которая в конце концов приводит к сбою моего приложения, представляет собой исключение NullPointerException, поскольку метод Success не вызывается, и в конце возвращается null.
Вставить в мои файлы:
Основная активность: https://pastebin.com/hWByYUFT
MyTwitterApiClient: https://pastebin.com/85xH284K
Activity_main.xml: https://pastebin.com/vkzbkL81
зависимости в build.gradle: https://pastebin.com/CpX7cwkS
Он будет содержать мой ключ Twitter API, поэтому я не могу загрузить весь проект, но я могу мгновенно опубликовать ссылки pastebin на все мои файлы.
Вы можете удалить ключ API, в любом случае это не проблема!
Теперь я добавил ссылки Pastebin. Мой проект не содержит ничего другого, пока я застрял в этой проблеме...
Хорошо, начиная с вашего кода:
fun start(session: TwitterSession): User {
val userID = session.userId
var data: User? = null
MyTwitterApiClient(session).getCustomService().show(userID).enqueue(object : Callback<User>() {
override fun success(result: Result<User>?) {
data = result!!.data
}
override fun failure(exception: TwitterException?) {
throw exception!!
}
})
return data!!
}
Здесь вы возвращаете data, как будто это было назначено. Ваш TwitterApiClient выполняет асинхронную задачу, поэтому данные из data = result!!.data не будут правильно считаны из
text.text = (
"Name: "+t.name
+"\nLocation: "+t.location
+"\nFriends: "+t.friendsCount
)
Потому что t это null тогда. Его данные еще не установлены. Это будет когда-нибудь в будущем в асинхронном обратном вызове success().
Похоже, ваша основная проблема связана с тем, как работать с асинхронными задачами и как уведомлять о результатах. Много источников об этом. LiveData, RxJava, EventBus могут быть лидами.
Кстати, причина, по которой ваш код работал в MainActivity, заключалась в том, что вы устанавливали текст после того, как пришел результат (в success()), поэтому t было приятно читать.
Удачи и приятного обучения!
Я также подумал об этом и реализовал цикл while для тестирования, который заставлял основной поток спать в течение 1 секунды, прежде чем возвращать данные, если данные равны нулю, но он просто продолжал спать. В любом случае, спасибо за ваш ответ, он проясняет мою проблему.
И кое-что, чтобы не выглядеть полным неудачником: в моем первом приложении я действительно работал с AsyncTask, и это было очень просто, но Retrofit — это совсем другое. Я просто хотел сделать это как можно быстрее и мало читал о модернизации, а просто следил за новостями из твиттера. Извините за это и извините, что отняли ваше время!
AsyncTask снимает данные в onPostExecute в основном потоке, поэтому вам не пришлось играть с проблемами потоков. Не жалей, я был там. Это большая веха, чтобы перейти и понять многопоточность. Мой совет: не используйте AsynTask, изучите многопоточность. Удачного кодирования :)
Спасибо за совет! Я знаю основы многопоточности, но недостаточно глубоко, как кажется... Теперь я углублюсь в это!
Привет, Чак, не могли бы вы поделиться своим полным кодом на github, пожалуйста?