java - 在 android 的 AsyncTask 中创建额外的线程并使用 ExecutorService 更新 UI,我做错了吗?

标签 java android multithreading

我正在开发一个 Android 应用程序,我有一个 Activity,我在该 Activity 上显示两个 TableLayout。第一个 TableLayout 包含比赛的记分牌(类型 1),第二个 TableLayout 包含另一场比赛的记分牌(类型 2)。

我从同一个 XML 布局文件中扩充了这两个 TableLayout。我用来自 doInBackground() (AsyncTask) 的数据填充它们。但是,填充部分非常繁重,所以我将其放入自定义同步方法(在 AsyncTask 类中)并将膨胀的 TableLayout 对象作为参数传递给此方法。

我使用 ExecutorService 对象从 do​​InBackground() 方法调用所述方法。这个 ExecutorService 对象是用 Executors.newSingleThreadExecutor() 创建的。我将第一个方法调用放在一个 Runnable 中,将第二个方法调用放在另一个 Runnable 中,然后在这些 runnable 上触发 ExecutorService.execute()。

在该方法中,我通过对传入的 TableLayout 对象参数调用 findViewById() 并在这些 View 上调用 TextView.setText() 等方法来访问 UI 元素。这在大多数情况下都可以正常工作,但我开始看到一些异常(exception)情况,例如

ERROR/AndroidRuntime(409): FATAL EXCEPTION: pool-9-thread-1
    android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

ERROR/AndroidRuntime(378): FATAL EXCEPTION: pool-2-thread-1
    java.lang.NullPointerException

这让我相信我一定是在倒退着做这件事。我将在下面包含一个简化的代码 fragment ,以帮助描述我目前正在做的事情:

class AddRacesTask extends AsyncTask<Void, View, Void> {
    synchronized void populateTable(ArrayList<Race> data, TableLayout table) {
       TextView headerText = table.findViewById(R.id...);
       headerText.setText("Header");

       for(Race race : data) {
          TableRow row = getLayoutInflater().inflate(R.layout....);

          TextView pos = row.findViewById(R.id....);
          pos.setText(race.getPos());

          TextView person = row.findViewById(R.id....);
          person.setText(race.getPerson());

          // etc.
          // etc..
          // etc...

          table.addView(row);

       }

       publishProgress(table);

    }

    @Override
    private Void doInBackground(Void... voids) {
       ExecutorService executor = Executors.newSingleThreadExecutor();

       executor.execute(new Runnable() {
          @Override
          public void run() {
             TableLayout raceType1Layout = getLayoutInflater().inflate(R.layout...);
             populateTable(data, raceType1Layout);
          }
       });

       executor.execute(new Runnable() {
          @Override
          public void run() {
             TableLayout raceType2Layout = getLayoutInflater().inflate(R.layout...);
             populateTable(data, raceType1Layout);
          }
       });
    }

    @Override
    protected void onProgressUpdate(View... values) {
       if(values[0] instanceof TableLayout) {
          container.addView(values[0]);
       }
    }
}

我欢迎任何反馈。此外,如果您需要更多内容,我很乐意发布我的实际源代码,但我认为简化和干净的版本会更容易。

最佳答案

您需要实现一个自定义适配器,用 Race 对象填充您的自定义 View 。您使用自定义线程或 AsyncTask 在后台接收数据,并分别通过 Handler 或 onPostExecute 方法传递给 UI 线程。然后,您可以调用 Adapter.clear 方法并在

for(Race r: ArrayList<Race>)
Adapter.add(r) 

关于java - 在 android 的 AsyncTask 中创建额外的线程并使用 ExecutorService 更新 UI,我做错了吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11809270/

相关文章:

java - 是否有任何 Java 内核/桌面应用程序?

java - 在不同用户上创建地理围栏

java - 棋盘游戏的 GUI 操作

android - 强制安装新的解析器

Android Studio RssFeed

java - 在 Android 中将文件写入外部存储失败

node.js - 如何让 NodeJS 应用程序永远运行(不是 forever 模块)

multithreading - 为什么 Go 的 LockOSThread 不锁定这个 OS 线程?

java - 我尝试创建可在 Eclipse 上使用的 Glassfish 服务器有什么问题吗?

c++ pthread : how to set io_service. run() 作为 pthread_create 中的参数?