有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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

我正在学习ThreadPoolExecutor,方法如下tutorial。为了演示它的用法,我做了一个简单的安卓项目,它有一个recyclerview,可以显示一些字符串。最初,字符串数组(String[] myDataset = new String[10])有10个空值。我的threadPoolExecutor生成一些随机字符串并填充数组。因此,每当生成一个新字符串并将其放入数组中时,我应该调用notifyDataSetChanged(),以便recyclerView将更新并显示这些随机字符串

问题

我不知道如何调用notifyDataSetChanged(),所以我被束缚住了。我得到了一个例外:

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

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

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

)。我需要它的线程池执行器对应。我在谷歌上找到了this,但我不知道怎么做

必要的代码段如下所示:

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

}

有人能帮我吗?谢谢你的阅读


共 (2) 个答案

  1. # 1 楼答案

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

    一些可能的选择:

    1. 使用连接到主活套的处理程序:

      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视图的“post”方法(例如,RecyclerView):

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

  2. # 2 楼答案

    如果有人需要,以下是我根据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)(如果您正在使用该片段,则为活动)。然后我将弱引用传递到MyAdapter。我使用了weakReference,因为这是使用AsyncTask所做的,所以我的直觉提醒我这样做。我希望这是有帮助的