并行运行长作业,并且只有在所有作业完成后才能更新UI

时间:2014-07-23 08:52:03

标签: java android multithreading android-asynctask parallel-processing

我正在构建一个Android应用程序,我在架构上遇到了一些问题 - 如何在不同的线程上实现多个调用。

我有两种方法:

ConnectAndGetId() //takes 2-3 seconds
GetTokenID(ID)    //takes 2-3 seconds

首先,我需要致电ConnectAndGetId(),然后在获得结果ID后致电GetTokenID(ID)。 获得tokenID后,我需要调用4种方法并将其传递给tokenID

getNames (tokenID) //takes 4 second
getPhones (tokenID) //takes 7 seconds
getIds(tokenID) //takes 2 seconds
getDetailObject(tokenID) //takes 5 seconds

我的想法是在获得 ALL 4方法的结果后显示数据 ONLY 。没有必要逐个执行它们,因为它需要花费很多时间(18秒),我想并行运行它们,最后用所有数据来更新UI

我想做下一件事:
开始AsyncTask并致电ConnectAndGetId,在onPostExecute()开始另一个AsyncTask并在那里GetTokenID(ID)运行。在我运行GetTokenID(ID)后,我将返回 结果到UI线程,并且返回的数据我将开始4个新的threads,每个线程将调用4个方法中的一个。我可以计算完成的线程,当我得到所有4个结果时,我可以用处理程序更新UI

这是正确的方法吗?也许我不应该为4种方法创建4个线程?否则创建一个handlerThread并将4个方法传递给他的处理程序,这样Thread就可以将它们集中在一起 - 但它将是一个接一个的。

这是正确的想法还是可以做得更好?

3 个答案:

答案 0 :(得分:1)

这是正常的做法。但我建议您使用Executor但不要创建Threads。样品:

public class TasksSample {
    public static void execute(){
        ExecutorService executorService = Executors.newFixedThreadPool(4);
        executorService.submit(new Task("A"));
        executorService.submit(new Task("B"));
        executorService.submit(new Task("C"));
        executorService.submit(new Task("D"));
        executorService.shutdown();
        try {
            executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("all tasks finished");
    }

    private static class Task implements Runnable{

        private String taskId;

        public Task(String taskId) {
            this.taskId = taskId;
        }

        @Override
        public void run() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("finished task: " + taskId);
        }
    }
}

答案 1 :(得分:0)

确实非常好的问题。我认为你的aproach是正确的,但我不会在onPostExecute()上创建GetTokenID线程。我认为最好使用Listener来确保您获得所需的结果。在下一步中,我还将使用一个可以调用你的4种方法的监听器。

问题是:您是否需要4种方法来完成更新UI?那么你应该使用一个只有你的4个方法完成时才会进入的同步块的监听器。您可以使用检查这些方法是否已获得结果的方法以及4完成后更新UI。

如果您不需要4个方法来完成更新UI,我只需在UI中放入ProgressDialog并逐个方法地更新它。在最后一个中,关闭ProgressDialog并取消阻止UI。

我希望它能帮助你解决问题,祝你好运!

答案 2 :(得分:0)

关于如果运行四个线程适合您的情况,您只能判断。它增加了程序的复杂性,并且需要对相关数据进行多线程访问保护和同步。还要考虑并行访问是否可以真正加快计算速度。如果限制因素是单线程计算速度,并行访问只会加速,如果限制在于数据带宽,则不会看到任何显着的速度增益。

对于完成一个UI更新的四个线程的问题,您可以尝试使用CyclicBarrier锁定所有线程,直到最后一个完成执行。

以下是一个示例:

// Create a CyclicBarrier with 4 parties and a target action to update UI

CyclicBarrier barrier = new CyclicBarrier(4, new Runnable(){
    public void run(){
        //Do your UI updates here (remember any direct UI must be on UI thread)
    } 
});

// Pass above CyclicBarrier to your four threads


// Skeleton of Runnable instance for each of the four threads.

Runnable r = new Runnable(){
    public void run(){
        // Run desired long-running method

        // getNames, getPhones, etc

        // Call await() from CyclicBarrier instance (exception handling not shown)

        barrier.await();
    }
};

使用上面的代码,每个完成的线程将阻塞await方法,直到第四个线程调用await(),其中所有线程都解除阻塞,最后一个线程运行runnable,在开始时传递给CyclicBarrier。