33

私はAndroidでAsyncTasksを使用していて、問題に取り組んでいます。

簡単な例として、1つのAsyncTaskを持つアクティビティを取り上げます。バックグラウンドでのタスクは見事なことは何もしません。8秒間スリープするだけです。

onPostExecute()メソッドのAsyncTaskの最後で、ボタンの表示ステータスをView.VISIBLEに設定しているだけで、結果を確認するだけです。

これで、AsyncTaskの動作中に(8秒のスリープウィンドウ内で)ユーザーが電話の向きを変更することを決定するまで、これはうまく機能します。

Androidアクティビティのライフサイクルを理解しており、アクティビティが破棄されて再作成されることを知っています。

ここで問題が発生します。AsyncTaskはボタンを参照しており、最初にAsyncTaskを開始したコンテキストへの参照を保持しているようです。

この古いコンテキスト(ユーザーが向きを変更したため)がnullになり、AsyncTaskが表示しようとしているボタンへの参照用にNPEをスローすることを期待します。

代わりに、NPEはスローされず、AsyncTaskはボタン参照がnullではないと見なし、表示に設定します。結果?画面には何も起きていません!

更新:WeakReferenceアクティビティを維持し、構成の変更が発生したときに切り替えることで、これに取り組んでいます。これは面倒です。

コードは次のとおりです。

public class Main extends Activity {

    private Button mButton = null;
    private Button mTestButton = null;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mButton = (Button) findViewById(R.id.btnStart);
        mButton.setOnClickListener(new OnClickListener () {
            @Override
            public void onClick(View v) {
                new taskDoSomething().execute(0l);
            }
        });
        mTestButton = (Button) findViewById(R.id.btnTest);   
    }

    private class TaskDoSomething extends AsyncTask<Long, Integer, Integer> 
    {
        @Override
        protected Integer doInBackground(Long... params) {
            Log.i("LOGGER", "Starting...");
            try {
                Thread.sleep(8000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 0;
        }

        @Override
        protected void onPostExecute(Integer result) {
            Log.i("LOGGER", "...Done");
            mTestButton.setVisibility(View.VISIBLE);
        }
    }
}

それを実行してみて、AsyncTaskが機能している間に電話の向きを変更してください。

4

4 に答える 4

23

AsyncTask は、Activity が取り壊されて再起動された後に再利用されるようには設計されていません。あなたが述べたように、内部の Handler オブジェクトは古くなります。Romain Guy による Shelf の例では、現在実行中の AsyncTask を単純にキャンセルし、向きの変更後に新しいタスクを再起動します。

スレッドを新しいアクティビティに渡すことは可能ですが、多くの配管が追加されます。これを行う方法について一般的に合意された方法はありませんが、ここで私の方法について読むことができます: http://foo.jasonhudgins.com/2010/03/simple-progressbar-tutorial.html

于 2010-05-30T17:39:04.910 に答える
3

コンテキストのみが必要で、UI に使用しない場合は、単純に ApplicationContext を AsyncTask に渡すことができます。たとえば、システム リソースのコンテキストが必要になることがよくあります。

AsyncTask から UI を更新しようとしないでください。面倒になる可能性があるため、構成の変更を自分で処理しないようにしてください。UI を更新するには、ブロードキャスト レシーバーを登録してブロードキャストを送信します。

上記のように、アクティビティとは別のパブリック クラスとして AsyncTask も用意する必要があります。これにより、テストがはるかに簡単になります。残念ながら、Android プログラミングは悪い慣行を強化することが多く、公式の例は役に立ちません。

于 2012-07-11T09:37:55.670 に答える
2

これを回避するには、ここで回答givinを使用できます:https ://stackoverflow.com/a/2124731/327011

ただし、アクティビティ(縦向きと横向きのレイアウトが異なる)を破棄する必要がある場合は、AsyncTaskをパブリッククラスにして(Androidをプライベートにしない理由をここで読んでください:AsyncTaskの推奨事項:プライベートクラスまたはパブリッククラス?)、メソッドsetActivityは、現在のアクティビティが破棄/作成されるたびに、現在のアクティビティへの参照を設定します。

ここで例を見ることができます:外部クラスのAndroid AsyncTask

于 2011-12-07T15:02:44.843 に答える
2

これは、方向の変更時にアクティビティが破棄/再作成されるのを常に防ぐように導くタイプのものです。

<Activity>これを行うには、マニフェスト ファイルのタグにこれを追加します。

android:configChanges="orientation|keyboardHidden" 

Activity クラスで onConfigurationChanged をオーバーライドします。

@Override
public void onConfigurationChanged(final Configuration newConfig)
{
    // Ignore orientation change to keep activity from restarting
    super.onConfigurationChanged(newConfig);
}
于 2010-01-23T21:05:49.620 に答える