【问题标题】:Update ListView in the main thread from another thread从另一个线程更新主线程中的 ListView
【发布时间】:2010-05-27 09:07:55
【问题描述】:

我有一个单独的线程正在运行以从 Internet 获取数据。之后,我想通过调用 adapter.notifyDataSetChanged() 来更新主线程中的 ListView。但它不起作用。有什么解决方法吗?谢谢。

【问题讨论】:

标签: android


【解决方案1】:

使用 AsyncTask (http://developer.android.com/reference/android/os/AsyncTask.html)。

onPostExecute(...) 方法中调用 adapter.notifyDataSetChanged()

更多详情,请阅读:http://android-developers.blogspot.com/2009/05/painless-threading.html

【讨论】:

    【解决方案2】:

    总体上好的建议是 http://android-developers.blogspot.com/2009/05/painless-threading.html

    我个人使用我的自定义线程(扩展 Thread 的类),但通过 Message 向 UI 线程发送响应。所以在线程的run()函数中有:

    Message msg;
    msg = Message.obtain();
    msg.what = MSG_IMG_SET;                     
    mExtHandler.sendMessage(msg);
    

    UI 线程定义了一个消息处理程序。

    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 更容易。

    【讨论】:

    • 我忘了说:你也可以在消息中添加一个对象,比如:msg.obj = someBitmap;并在处理程序中接收它(例如,它可以使用接收到的位图更新数组或列表,并通知适配器列表的基础数据已更改:mArrayAdapter.notifyDataSetChanged();)。在这种情况下,不会发生竞争条件。
    • 当心 - 这里有龙!这个内部 Handler 类有一个对外部类 this 的隐式引用。最好将该 Handler 设为静态并将 WeakReference 指向外部类。
    • @MichaelKrause,为什么这里有问题?处理程序的生命周期不会长于活动的
    • @AliTofigh 这是对mImagesProgressHandler的引用
    【解决方案3】:

    或者,将消息发布到列表视图的消息队列(将在 UI 线程上执行):

    list.post(new Runnable() {                  
        @Override
        public void run() {
           adapter.notifyDataSetChanged();
    
        }
    }); 
    

    【讨论】:

      【解决方案4】:

      假设 yourActivity 是您的小部件已放入其中的活动, yourView 是小部件和 adapter 是小部件的适配器:

      yourActivity.runOnUiThread(new Runnable() {
       public void run() {    
               try{
                      adapter.notifyDataSetChanged();
               }
               catch{}
       }
      }
      

      【讨论】:

      • 感谢上帝,我找到了这个答案。
      【解决方案5】:

      您可以为此使用RxJavaRetrofit 的组合。

      您可以这样做。我将使用 GitHub API 作为示例。

      为您的 HTTP API 创建一个 Java 接口。

      public interface GitHubService {
          @GET("users/{user}/repos")
          Observable<List<Repo>> listRepos(@Path("user") String user);
      }
      

      使用 Observable 会将响应转换为流。每个事件都是一个 repos 列表。

      使用 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 - 它告诉操作系统使用哪个线程来监听响应。我们在主线程上执行此操作,并在收到响应时更新 UI。

      3 - 这行代码展示了 Rx 的真正魔力。这条简单的小线将响应列表转换为单个对象。因此,我们将对每个对象做出反应,而不是对整个列表做出反应。你可以阅读更多关于它的信息here

      4 - 这一行实际上定义了将对事件显示什么样的“反应”。您可以在此处更新适配器。

      一个更复杂的订阅者看起来像这样 -

      new Subscriber<Repo>() {
          @Override
          public void onCompleted() {
      
          }
      
          @Override
          public void onError(Throwable e) {
      
          }
      
          @Override
          public void onNext(Repo repo) {
      
          }
      }
      

      附: - 我在上面的例子中使用了 lambdas。你可以通过here添加。

      【讨论】:

        【解决方案6】:

        这对我有用

        this.runOnUiThread(() -> {
                    chatAdapter.notifyItemInserted(chatMessages.size()-1);
        
                    binding.chatMessages.scrollToPosition(chatMessages.size()-1);
                });
        

        其中this 指的是当前包含适配器和recyclerview 的Activity。

        【讨论】:

          【解决方案7】:

          这里的诀窍是你放的位置

          mAdapter.notifyDataSetChanged();

          这里是一个简单的例子:

          mRecyclerView = (RecyclerView) rootView.findViewById(R.id.recycler_view);
          mAdapter = new myAdapter(......);
          mAdapter.notifyDataSetChanged();
          mRecyclerView.setAdapter(mAdapter);
          

          这对我来说非常适合。

          【讨论】:

          • 这个答案并没有真正回答与线程有关的任何事情
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-01-04
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多