Обновить ListView в основном потоке из другого потока

У меня есть отдельный поток для получения данных из Интернета. После этого я хотел бы обновить ListView в основном потоке, вызвав adapter.notifyDataSetChanged(). Но это не работает. Любой обходной путь для этого? Спасибо.

Ответ 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);

Эта работа для меня идеально.