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"); } }); }
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); } }); }
解决方法
首先,我将把populateList的调用移动到AsyncTask的onPostExecute方法中.我还会重写populateList以删除睡眠并假设它在UI线程上运行(消除runOnUiThread调用并将run方法的主体直接移动到populateList中).
现在,为了防止AsyncTask完成doInBackground,直到AppDataManager完成其工作.首先定义一个完成标志和一个可以同步的锁定对象:
private class mTask extends AsyncTask<List<ApplicationInfo>,Void> { boolean complete; static Object LOCK = new Object(); . . . }
然后修改AppDataManager类,以在完成工作时提供对另一个对象的回调.定义字段回调并更新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); if (callback != null) { callback.run(); } } }); }
protected Void doInBackground(List<ApplicationInfo>... params) { appDataManager = new AppDataManager(MainActivity.this,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中做了五个网络事务.因此,不是在ajax()的回调方法中立即运行回调,而是递减计数器,并且只有在计数器达到0时才回调.在进入调用getHtml()的循环之前,在work()中将计数器初始化为5.由于计数器将由单独的线程修改,因此需要同步对它的访问. (或者,您可以使用AtomicInteger
作为计数器.