質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.96%

【Android】非同期処理後にページ遷移をしたい

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 5,868

GH_usami13

score 22

【10月6日質問編集】

おこなっていることは、
dialogfragmentでonClickをすると非同期処理をおこない、 
非同期処理が終了したら
Activityの遷移をおこなっております。

しかし、fragmentで非同期処理をおこなうと、
Activityとの接続が切れて
新たなActivityを呼ぼうとした時に
getActivityがnullになってしまい、
遷移できずにエラーになってしまいます。 

dialogfragmentで処理しているコードが下記になります。
public class AnnotationDialogFragment extends DialogFragment {

    public String name;
    public String snippet;
    Context context;

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {

        int index = snippet.indexOf(":");
        final String id = snippet.substring(0, index);
        snippet = snippet.substring(index + 1);

        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setTitle(name);
        builder.setMessage(snippet);
        builder.setPositiveButton(R.string.detail, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(final DialogInterface dialog, int which) {
                AsyncDetail asyncDetail = new AsyncDetail(new AsyncTaskCallback() {
                    @Override
                    public void postExecute(String result) {     //この時点でgetActivityはnull
                        Intent intent = new Intent(getActivity() , DetailActivity.class);
                        intent.setClassName("パッケージ名", "パッケージ名.DetailActivity");
                        intent.putExtra("result", result);
                        startActivity(intent);
                    }
                } );
                asyncDetail.execute("URL", id);
            }
        });
        builder.setNegativeButton(R.string.close, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                // TODO Auto-generated method stub
            }
        });
        return builder.create();
    }

非同期処理をおこなうAsyncTaskを継承したクラスが下記になります。
public class AsyncDetail extends AsyncTask<String, Integer, String> {

    public AsyncTaskCallback callback = null;

    public AsyncDetail(AsyncTaskCallback _callback) {
        this.callback = _callback;
    }

    private HttpClient hClient = new DefaultHttpClient();

    // バックグラウンドで処理
    @Override
    protected String doInBackground(String... params) {
        String uri = params[0];
        int id = Integer.parseInt( params[1] );
        return PostLocations(uri, id);
    }

    // バックグラウンド処理が終了した後にメインスレッドに渡す処理
    protected void onPostExecute(String result) {
        super.onPostExecute(result);
        this.callback.postExecute(result);
    }

    // 位置情報(JSON)をダウンロード
    private String PostLocations(String uri, int id) {
        try {
            HttpPost post = new HttpPost(uri);
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("id", id);
            StringEntity se = new StringEntity(jsonObject.toString(), "UTF-8");
            post.setHeader("Accept", "application/json");
            post.setHeader("Content-Type", "application/json");
            post.setEntity(se);
            HttpResponse resp = hClient.execute(post);

            if (resp.getStatusLine().getStatusCode() < 400) {
                String str = EntityUtils.toString(resp.getEntity(), HTTP.UTF_8);
                return str;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

}

コールバックインターフェースを下記に記します。
public interface AsyncTaskCallback {
    void postExecute(String result);
}

非同期処理をさせてもActivityを保持させていられるやり方か、
新たにActivityを生成させるやり方を知りたいです。

また、これらの書き方はよくないということでありましたら、
どんな方法が良いのかお教えいただけますと幸いです。

皆様のお力添えのほど、よろしくお願い致します。
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

遷移後のアクティビティを呼び出せていないから起こる
コードを見るに、遷移先のアクティビティは関係ないと思います。

例外が発生しているのはAsyncDetailの以下の部分です。
protected void onPreExecute() {
    super.onPreExecute();
    this.callback.preExecute();    // ココ
}
エラー内容は、このcallbacknullだったため、preExecuteを実行できなかった、という意味です。

理由は上記の通りですが、何故nullなのかは
public void onClick(DialogInterface dialog, int which) {
    AsyncDetail asyncTask = new AsyncDetail(this);
    asyncTask.execute("URL", id);
    getActivity().finish();
}
public AsyncDetail(android.content.DialogInterface.OnClickListener onClickListener) {
    // TODO Auto-generated constructor stub
    hClient = new DefaultHttpClient();
}
この2つの部分に原因があると思われます。

上の部分で、new AsyncDetail(this);と記述していますが、このthisAsyncTaskCallbackを実装していますか?
恐らく実装していないのではないでしょうか?
しかし、下の部分のコンストラクタがあるためコンパイルエラーにはならず、結果、ビルドはできる状態になったのだと思います。


そもそも、
ダイアログでonClickすることで非同期処理をおこない、  
非同期処理が終了したら、 
新たなアクティビティで処理後の内容を出力したいです。 
この目的とコードがチグハグです。
質問文のコードでは、遷移前と遷移後、どちらでも非同期処理を行おうとしています。

遷移前に、非同期処理を行い、非同期処理が終了したら遷移。
@Override
public void onClick(DialogInterface dialog, int which) {
    AsyncDetail asyncTask = new AsyncDetail(new AsyncTaskCallback() {
        public void postExecute(String result) {
            Intent intent = new Intent(getApplicationContext(), DetailActivity.class);
            intent.putExtra("RESULT", result);
            startActivity(intent);
        }
        // 他 略
    });
    asyncTask.execute("URL", id);
    getActivity().finish();
}
遷移後にデータを表示。
public class DetailActivity extends Activity {
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_detail);

        Intent intent = getIntent();
        String result = intent.getStringExtra("RESULT");
        // 出力処理など
    }
}
目的にそうとしたら、大まかなコードですが、こんな感じになるのではないでしょうか。


AsyncDetailにも怪しいコードがありますが、質問内容とは離れるため、ひとまずはノータッチです。すいません。



2015/10/4 追記
例外のスタックトレースが無いので、推測になってしまいますが、原因は以下の部分かもしれません。
public void onClick(DialogInterface dialog, int which) {
    AsyncDetail asyncDetail = new AsyncDetail(new AsyncTaskCallback() {
        @Override
        public void postExecute(String result) {
            Intent intent = new Intent(getActivity(), DetailActivity.class);    // ココ
            intent.putExtra("result", result);
            startActivity(intent);
        }
    });
    getActivity().finish();        // ココ
    asyncDetail.execute("URL", id);
}

非同期処理開始前に、Activityをfinishしているのに、非同期処理完了時に、getActivity()でActivityを取得しようとしています。
getActivity().finish();を非同期処理完了時に実行するか、別の方法でContextを取得してみてください。




Fragmentのコードを確認したところ、Fragment#startActivity(Intent)はFragment自身が設置されているActivityのActivity#startActivity()を呼ぶような処理をしています。
なので、Fragment#startActivity(Intent)を呼ぶ前に、設置されているActivityのAcitivity#finish()呼んでしまった場合、Activity#startActivity()が呼び出せなくなるのだと思います。
実際のソースコードでは呼ぶ前にnullチェックをしており、nullの場合に件の例外を投げるようになっていました。

ですので、上に書いたように、getActivity().finish();を非同期処理完了時に実行するのがいいと思います。



getActivity().finish();postExecute内に移してみてください。

public void onClick(DialogInterface dialog, int which) {
    AsyncDetail asyncDetail = new AsyncDetail(new AsyncTaskCallback() {
        @Override
        public void postExecute(String result) {
            Intent intent = new Intent(getActivity(), DetailActivity.class);
            intent.putExtra("result", result);
            startActivity(intent);
            getActivity().finish();    // ここに移す
        }
    });
    // getActivity().finish();
    asyncDetail.execute("URL", id);
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/10/07 02:51

    ご丁寧に解説していただきありがとうございます。
    非同期処理をおこなった瞬間、FragmantとActivityとの接続が切れてしまって、
    非同期処理後にActivityを呼ぼうと思っても、
    getActivity()がnullになってしまい、
    前回のコメントで記載したようなエラーになってしまうようですね。

    どうにかActivityを保持させて非同期処理後にそのActivityから新しいActivity(DetailActivity)へ遷移できないものかと調べてはいるのですが、
    half_sleepingさんがおっしゃられるように、
    非同期処理後にgetActivity().finish();おこなう方法がわかりませんでした。

    大変ご迷惑おかけしますが、
    もう少し具体的にお教えいただくことは可能でしょうか?

    また、他の方法をとった方がよろしいのでしょうか?
    (例)AsyncTaskLoader

    何卒よろしくお願いいたします。

    キャンセル

  • 2015/10/07 16:36

    コメントありがとうございます。

    getActivity().finish();
    をそちらに記載しても変わらず、startActivity(intent)でエラーになってしまいます。

    非同期処理を開始した時点でActivityとFragmentが破棄されてしまい、
    postExecute()に返ってきた時には
    どことも接続がされていない状態のようです。

    非同期前にActivityを保持させて、
    非同期処理後に保持させてあるActivityを呼び出して
    という処理を
    http://www.garunimo.com/program/p17.xhtml
    等でおこなったのですが、
    同じ結果となってしまいました。

    キャンセル

  • 2015/10/07 18:00

    今回は、setPositiveButtonのclick時には
    画面遷移の処理はおこなわず、
    非同期処理だけおこない、
    元のActivity側で画面遷移の処理をおこなったら
    期待した処理ができました。

    この度は、長々とお付き合いいただきありがとうございました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.96%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる