5

AsyncTask 类启动了许多其他线程,因为我使用的是第三方库。我不确定库打开了多少线程,但我想等待它们全部完成,然后再用它们获取的数据填充我的列表视图。

目前我正在睡 10000 毫秒,但这不切实际,因为我不知道它的列表有多大。

这个问题的正确解决方案是什么?

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");
         }
         });

    }

应用数据管理器

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);

        }
    });
}
4

2 回答 2

3

首先,我会将调用移至populateListonPostExecuteAsyncTask. 我还将重写populateList以删除睡眠并假设它在 UI 线程上运行(消除runOnUiThread调用并将run方法的主体直接移动到populateList.

现在防止AsyncTask完成doInBackground,直到AppDataManager完成它的工作。首先定义一个完成标志和一个可以同步的锁定对象:

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

然后修改AppDataManager该类以在工作完成时提供对另一个对象的回调。定义一个字段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();
            }
        }
    });
}

现在修改您的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;
}

这应该可以完成这项工作。但是,您可能应该详细说明这一点以处理各种错误(例如,为 提供错误通知机制AppDataManager)。

更新我终于注意到你在AppDataManager. 因此,不要立即在 for 的回调方法中运行回调,而是ajax()减少一个计数器,并且仅在计数器达到 0 时回调。work()在进入调用的循环之前将计数器初始化为 5 getHtml()。由于计数器将由单独的线程修改,因此需要同步对其的访问。(或者,您可以将 anAtomicInteger用于计数器。

于 2012-11-13T15:16:32.617 回答
0

如果您正在等待线程达到一个共同点,那么CyclicBarrier似乎是解决方案

此外,我不会创建一个线程来调用populatelist()它本身创建线程runOnUiThread,而是查看publishProgress()

于 2012-11-13T14:35:20.640 回答