У меня есть отдельный поток для получения данных из Интернета. После этого я хотел бы обновить ListView в основном потоке, вызвав adapter.notifyDataSetChanged(). Но это не работает. Любой обходной путь для этого? Спасибо.
Обновить ListView в основном потоке из другого потока
Ответ 1
Используйте AsyncTask (http://developer.android.com/reference/android/os/AsyncTask.html).
Вызвать метод adapter.notifyDataSetChanged() в методе onPostExecute (...).
Подробнее читайте здесь: http://android-developers.blogspot.com/2009/05/painless-threading.html
Ответ 2
Общий хороший совет http://android-developers.blogspot.com/2009/05/painless-threading.html
Лично я использую свой собственный поток (класс, расширяющий Thread), но отправляю ответ на поток пользовательского интерфейса через Message. Таким образом, в функции run run() есть:
Message msg;
msg = Message.obtain();
msg.what = MSG_IMG_SET;
mExtHandler.sendMessage(msg);
Пользовательский интерфейс определяет обработчик сообщений.
private Handler mImagesProgressHandler;
public void onCreate(Bundle bundle) {
mImagesProgressHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case LoadImagesThread.MSG_IMG_SET:
mArrayAdapter.setBitmapList(mImagesList);
mArrayAdapter.notifyDataSetChanged();
break;
case LoadImagesThread.MSG_ERROR:
break;
}
super.handleMessage(msg);
}
};
Это проще, чем AsyncTask.
Ответ 3
Или отправьте сообщение в очередь сообщений listview (которая будет выполняться в потоке пользовательского интерфейса):
list.post(new Runnable() {
@Override
public void run() {
adapter.notifyDataSetChanged();
}
});
Ответ 4
Вы можете использовать комбинацию RxJava и Retrofit для этой цели.
Вот как вы можете это сделать. Я буду использовать API GitHub для примера.
Создайте Java-интерфейс для вашего HTTP-API.
public interface GitHubService {
@GET("users/{user}/repos")
Observable<List<Repo>> listRepos(@Path("user") String user);
}
Использование Observable преобразует ответ в поток. Каждое событие является списком репозиториев.
Используйте класс Retrofit для создания реализации интерфейса GitHubService. Вы можете или не можете предоставлять пользовательский HTTP-клиент.
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.client(okHttpClient) // OkHttp Client
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
GitHubService service = retrofit.create(GitHubService.class);
Теперь появляется часть Rx. Вы должны добавить подписчика для прослушивания ответов, отправленных сервером. Другими словами, отреагируйте на это.
service.listRepos("octocat")
.subscribeOn(Schedulers.io()) // 1
.observeOn(AndroidSchedulers.mainThread()) // 2
.flatMap(Observable::from) // 3
.subscribe(repoList -> { // 4
// Update the list view
// notifyDataSetChanged
});
Вот что делают строки, прокомментированные цифрами -
1 - указывает операционной системе, какой поток используется для выполнения вызова. Мы выбираем для этого поток IO.
2 - Он сообщает OS, какой поток будет использоваться для прослушивания ответа. Мы делаем это в основном потоке и обновляем пользовательский интерфейс при получении ответа.
3 - Эта строка демонстрирует истинную магию Rx. Эта простая маленькая строка преобразует список ответов на отдельные объекты. Следовательно, мы будем реагировать на каждый объект, а не на весь список. Вы можете прочитать об этом здесь.
4 - Эта строка фактически определяет, какая "реакция" будет показана событию. Здесь вы можете обновить адаптер.
Более сложный подписчик выглядит так:
new Subscriber<Repo>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(Repo repo) {
}
}
P.S. - Я использовал лямбды в приведенных выше примерах. Вы можете добавить это через здесь.
Ответ 5
Предположим,
yourActivity
- это активность, которую ваш виджет помещает в нее,
yourView
- виджет и
adapter
- это адаптер виджета:
yourActivity.runOnUiThread(new Runnable() {
public void run() {
try{
adapter.notifyDataSetChanged();
}
catch{}
}
}
Ответ 6
Трюк здесь - позиция, в которую вы положили
mAdapter.notifyDataSetChanged();
Вот простой пример:
mRecyclerView = (RecyclerView) rootView.findViewById(R.id.recycler_view);
mAdapter = new myAdapter(......);
mAdapter.notifyDataSetChanged();
mRecyclerView.setAdapter(mAdapter);
Эта работа для меня идеально.