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

    }

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

        }
    });
}
4

2 に答える 2

3

まず、 への呼び出しをpopulateListonPostExecuteメソッドに移動しますAsyncTask。また、スリープを削除するように書き直しpopulateListて、UI スレッドで実行されていると仮定します (runOnUiThread呼び出しを削除し、メソッドの本体を にrun直接移動しpopulateListます)。

が作業を終了するまで、AsyncTaskが完了しないようにします。完了フラグと、同期できるロック オブジェクトを定義することから始めます。doInBackgroundAppDataManager

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

次にAppDataManager、作業が完了したときに別のオブジェクトへのコールバックを提供するようにクラスを変更します。フィールドを定義し、callbackAPI とメソッドを更新します。

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

更新で 5 つのネットワーク トランザクションを実行していることに気付きましたAppDataManager。したがって、 のコールバック メソッド内ですぐにコールバックを実行する代わりにajax()、カウンターをデクリメントし、カウンターが 0 に達した場合にのみコールバックします。work()を呼び出すループに入る前に、カウンターを 5 に初期化しますgetHtml()。カウンターは別のスレッドによって変更されるため、カウンターへのアクセスを同期する必要があります。(または、AtomicIntegerカウンターに を使用することもできます。

于 2012-11-13T15:16:32.617 に答える
0

スレッドが共通点に到達するのを待っている場合、 CyclicBarrierは解決策のようです

また、スレッドを作成するのではなく、呼び出すスレッドを作成する代わりに、populatelist()publishProgress ()runOnUiThreadを確認します。

于 2012-11-13T14:35:20.640 に答える