Android ThreadPoolExecutor 完成后如何调用RecyclerVIew notifyDataSetChanged()?

Android How Do I Call RecyclerVIew notifyDataSetChanged() after ThreadPoolExecutor finishes?

我正在按照此 tutorial 学习 ThreadPoolExecutor。为了演示它的用法,我做了一个简单的 android 项目,它有一个会显示一些字符串的回收视图。最初,Strings(String[] myDataset = new String[10]) 数组有 10 个空值。我的 threadPoolExecutor 生成一些随机字符串并填充数组。因此,无论何时生成新字符串并将其放入数组中,我都应该调用 notifyDataSetChanged() 以便 recyclerView 更新并显示这些随机字符串。

问题

我不明白如何调用 notifyDataSetChanged() 所以我被压住了。我得到这个例外:

Caused by: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

因为我知道 AsyncTask,我知道这个错误意味着我不能在后台线程中调用这个方法,但我必须在主线程中调用它 thread/ui(所以在 AsyncTask 中,它看起来像这样:

@Override
    protected void onPostExecute(String result) {
      weakReference.get().notifyDataSetChanged(); // something like that
    }

)。我需要它的 ThreadPoolExecutor 对应物。我做了 google 并找到了 但我不知道该怎么做。

必要的代码段如下:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
    private String[] myDataset;
    private final ThreadPoolExecutor threadPoolExecutor;
    private Future future;

    private Runnable getRunnable(final int i) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                String randomString = MyAdapter.getRandomString(i)+" "+i; // <--create random string
                Log.e(TAG, randomString);
                myDataset[i] = randomString;
                try { Thread.sleep(3000); }
                catch (InterruptedException e) { e.printStackTrace(); }
            }
        };
        return runnable;
    }

    public void doSomeBackgroundWork(){
        Runnable[] commands = new Runnable[myDataset.length];
        for(int i1=0; i1<commands.length; i1++) {
            final int j1 = i1;
            commands[j1] = () -> {
                String randomString = MyAdapter.getRandomString(j1)+" "+j1;
                Log.e(TAG, randomString);
                myDataset[j1] = randomString;
                try { Thread.sleep(3000); }
                catch (InterruptedException e) { e.printStackTrace(); }

// notifyDataSetChanged();           // <-------- Error. Where/How should I call it?
            };

            threadPoolExecutor.execute(commands[j1]);
        }
    }

public MyAdapter(String[] myDataset) {
        this.myDataset = myDataset;  // {null, null, ... ...}
        this.threadPoolExecutor = DefaultExecutorSupplier.getInstance().forBackgroundTasks(); // returns new ThreadPoolExecutor( ... parameters... );
//        future[i] = threadPoolExecutor.submit(command); future[i].cancel(true); use it like this
        doSomeBackgroundWork();
    }

// ... the rest of the recyclerview related code

}

有人能帮帮我吗?感谢阅读。

在所有需要从另一个线程(AsyncTask 也使用它)与 UI线程通信的情况下,引擎盖下都有一个 Handler class

一些可能的选择:

  1. 使用Handler,连接到main looper:

    Handler handler = new Handler(getMainLooper()); handler.post(new Runnable() { @Override public void run() { notifyDataSetChanged(); } });

  2. 使用您提到的"runOnUiThread":

    runOnUiThread(new Runnable() { @Override public void run() { notifyDataSetChanged(); } });

  3. 使用UI-View(例如RecyclerView)的"post"方法:

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

如果有人需要,这是我根据sergiy-tikhonov的回答所做的。

    public void doSomeBackgroundWork(){
        Runnable[] commands = new Runnable[myDataset.length];
        for(int i1=0; i1<commands.length; i1++) {
            final int j1 = i1;
            commands[j1] = () -> {
                String randomString = MyAdapter.getRandomString(j1)+" "+j1;
                Log.e(TAG, randomString);
                myDataset[j1] = randomString;
                try { Thread.sleep(3000); }
                catch (InterruptedException e) { e.printStackTrace(); }
                // notifyDataSetChanged();           // <-------- Error
                recyclerViewWeakReference.get().post(new Runnable() {  // <---- this is the change
                       @Override
                       public void run() {
                           notifyDataSetChanged();
                       }
                });
            };

            threadPoolExecutor.execute(commands[j1]);
        }
    }

如您所见,我尝试了第三个选项。首先,我在父片段中创建了一个 WeakReference<RecyclerView> recyclerViewWeakReference = new WeakReference<RecyclerView>(myRecyclerView)(或者 activity,如果你正在使用它的话)。然后我将弱引用传递给 MyAdapter。我使用 weakReference 因为那是你用 AsyncTask 做的,所以我的直觉提醒我这样做。希望对您有所帮助。