简体   繁体   English

我的AsyncTask类中还包含其他线程。 在执行以下方法之前,我如何等待它及其所有线程完成?

[英]My AsyncTask class has other threads in it. How can i wait for it and all of its threads to finish before executing a following method?

The AsyncTask class starts many other threads because i am using a third party library. AsyncTask类启动了许多其他线程,因为我使用的是第三方库。 I am not sure how many threads the library opens but i want to wait for them all to finish before populating my listview with the data they fetch. 我不确定该库打开了多少个线程,但是我想等待它们全部完成,然后使用它们获取的数据填充我的列表视图。

At the moment I am sleeping 10000 milliseconds but this is not practical because I don't know how big the list it. 目前我正在睡觉10000毫秒,但这不切实际,因为我不知道它有多大。

What is the correct solution for this problem? 这个问题的正确解决方案是什么?

task = new mTask();
        task.execute(appsList);
        new Thread(new Runnable() {
            public void run() {
                populateList();
            }
        }).start();

    }

    private class mTask extends AsyncTask<List<ApplicationInfo>, Void, Void> {
        ProgressDialog progress;

        @Override
        protected void onPreExecute() {
            progress = new ProgressDialog(MainActivity.this);
            progress.setIndeterminate(true);
            progress.show();
            super.onPreExecute();
        }

        @SuppressWarnings("deprecation")
        @Override
        protected Void doInBackground(List<ApplicationInfo>... params) {
            appDataManager = new AppDataManager(MainActivity.this,
                    mySQLiteAdapter, MainActivity.this);
            appDataManager.work(params[0]);
            return null;
        }

        @Override
        protected void onPostExecute(Void result) {
            mySQLiteAdapter.close();
            progress.dismiss();
            super.onPostExecute(result);
        }
    }

    @SuppressWarnings("deprecation")
    public void populateList() {
        try {
            Thread.sleep(10000)
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        runOnUiThread(new Runnable() {

            public void run() {

                Cursor cursor;
                mySQLiteAdapter.openToRead();
                cursor = mySQLiteAdapter.queueAll();

                ArrayList<String> appsList = new ArrayList<String>();
                if (cursor.moveToFirst()) {
                    do {
                        appsList.add(cursor.getString(1));
                    } while (cursor.moveToNext());
                }
                cursor.moveToFirst();

                ArrayAdapter<String> adp = new ArrayAdapter<String>(
                        MainActivity.this, android.R.layout.simple_list_item_1,
                        appsList);
                listContent.setAdapter(adp);
                cursor.close();
                mySQLiteAdapter.close();

                Log.i("finished", "finished");
         }
         });

    }

AppDataManager AppDataManager

public void work(List<ApplicationInfo> appsList) {
    List<ApplicationInfo> appList = appsList;
    mySQLiteAdapter.openToWrite();
    mySQLiteAdapter.deleteAll();
    mySQLiteAdapter.close();
    for (int i = 0; i < 5; i++) {
        String name = appList.get(i).name;
        String pack = appList.get(i).packageName;
        // TODO AsyncTask
        getHtml(pack, name);
    }

}

public void getHtml(final String pack, final String name) {
    String url = MARKET_URL + pack;
            //AndroidQuery library. fetch html
    aq.ajax(url, String.class, 1000, new AjaxCallback<String>() {
        @Override
        public void callback(String url, String htm, AjaxStatus status) {
            Log.i("status", status.getMessage());
            parseHtml(htm, pack, name);

        }
    });
}

First, I would move the call to populateList into the onPostExecute method of your AsyncTask . 首先,我将把populateList的调用移动到AsyncTaskonPostExecute方法中。 I would also rewrite populateList to remove the sleep and assume that it is running on the UI thread (eliminate the runOnUiThread call and move the body of the run method directly into populateList ). 我还会重写populateList以删除sleep并假设它在UI线程上运行(消除runOnUiThread调用并将run方法的主体直接移动到populateList )。

Now to prevent the AsyncTask from completing doInBackground until the AppDataManager finishes its work. 现在,为了防止AsyncTask完成doInBackground直到AppDataManager完成其工作。 Start by defining a completion flag and a lock object on which you can synchronize: 首先定义一个完成标志和一个可以同步的锁定对象:

private class mTask extends AsyncTask<List<ApplicationInfo>, Void, Void> {
    boolean complete;
    static Object LOCK = new Object();
    . . .
}

Then modify the AppDataManager class to provide a call-back to another object when the work is done. 然后修改AppDataManager类,以在完成工作时提供对另一个对象的回调。 Define a field callback and update your api and methods: 定义字段callback并更新api和方法:

public void work(List<ApplicationInfo> appsList, Runnable callback) {
    this.callback = callback; // define a field named "callback"
    List<ApplicationInfo> appList = appsList;
    mySQLiteAdapter.openToWrite();
    mySQLiteAdapter.deleteAll();
    mySQLiteAdapter.close();
    for (int i = 0; i < 5; i++) {
        String name = appList.get(i).name;
        String pack = appList.get(i).packageName;
        // TODO AsyncTask
        getHtml(pack, name);
    }

}

public void getHtml(final String pack, final String name) {
    String url = MARKET_URL + pack;
            //AndroidQuery library. fetch html
    aq.ajax(url, String.class, 1000, new AjaxCallback<String>() {
        @Override
        public void callback(String url, String htm, AjaxStatus status) {
            Log.i("status", status.getMessage());
            parseHtml(htm, pack, name);
            if (callback != null) {
                callback.run();
            }
        }
    });
}

Now modify your doInBackground method to wait for the flag to be set: 现在修改doInBackground方法以等待设置标志:

protected Void doInBackground(List<ApplicationInfo>... params) {
    appDataManager = new AppDataManager(MainActivity.this,
            mySQLiteAdapter, MainActivity.this);
    appDataManager.work(params[0], new Runnable() {
        public void run() {
            synchronized (LOCK) {
                complete = true;
                LOCK.notifyAll();
            }
        }
    });
    // wait for appDataManager.work() to finish...
    synchronized (LOCK) {
        while (!complete) {
            LOCK.wait();
        }
    }
    return null;
}

This should do the job. 这应该做的工作。 However, you probably should elaborate on this to deal with errors of various sorts (eg, provide an error notification mechanism for AppDataManager ). 但是,您可能应该详细说明这一点以处理各种错误(例如,为AppDataManager提供错误通知机制)。

UPDATE I finally noticed that you are doing five network transactions in AppDataManager . 更新我终于注意到你在AppDataManager中做了五个网络事务。 So instead of running the callback immediately inside the callback method for ajax() , decrement a counter and only call back once the counter reaches 0. Initialize the counter to 5 in work() before you enter the loop that calls getHtml() . 因此,不是在ajax()的回调方法中立即运行回调,而是递减计数器,并且只有在计数器达到0时才回调。在进入调用getHtml()的循环之前,在work()中将计数器初始化为5。 Since the counter will be modified by separate threads, access to it needs to be synchronized. 由于计数器将由单独的线程修改,因此需要同步对它的访问。 (Alternatively, you can use an AtomicInteger for the counter. (或者,您可以使用AtomicInteger作为计数器。

CyclicBarrier seems like the solution if you're waiting for threads to reach a common point 如果您正在等待线程达到共同点, CyclicBarrier似乎是解决方案

Also, instead of creating a thread to call populatelist() which is itselt creating the thread runOnUiThread , I'd look at publishProgress() 另外,不是创建一个线程来调用populatelist() ,而是创建线程runOnUiThread ,我会看看publishProgress()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM