【Android】非同期処理後にページ遷移をしたい
- 評価
- クリップ 1
- VIEW 4,731
おこなっていることは、
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ページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
checkベストアンサー
+1
遷移後のアクティビティを呼び出せていないから起こるコードを見るに、遷移先のアクティビティは関係ないと思います。
例外が発生しているのは
AsyncDetail
の以下の部分です。
protected void onPreExecute() {
super.onPreExecute();
this.callback.preExecute(); // ココ
}
エラー内容は、このcallback
がnull
だったため、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);
と記述していますが、このthis
はAsyncTaskCallback
を実装していますか?
恐らく実装していないのではないでしょうか?
しかし、下の部分のコンストラクタがあるためコンパイルエラーにはならず、結果、ビルドはできる状態になったのだと思います。
そもそも、
ダイアログで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/04 18:43
half_sleepingさんのおっしゃられるように
認識がだいぶおかしかったようです。
改めて、コードを改変して色々と実行してみたのですが、
違ったエラーで苦戦してしまいました。
よろしければご教授のほど、
よろしくお願いいたします。
2015/10/04 21:30 編集
もうしわけございません。
元のコードに少々誤りがございました。
Intentのインスタンスを生成するときに
contextを第一引数としていましたが、
Contextの生成のコードを載せておりませんでした。
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setPositiveButton(R.string.detail, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
AsyncDetail asyncDetail = new AsyncDetail(new AsyncTaskCallback() {
@Override
public void postExecute(String result) {
Intent intent = new Intent(context , DetailActivity.class);
intent.putExtra("result", result);
startActivity(intent);
}
});
getActivity().finish();
asyncDetail.execute("URL", id);
}
})
return builder.create();
}
//ここを書き忘れておりました
@Override
public void onAttach(Activity activity){
super.onAttach(activity);
context = activity;
}
また、スタックトレースは以下のようになっております。
java.lang.IllegalStateException: Fragment AnnotationDialogFragment{206bef1d} not attached to Activity
W/System.err﹕ at android.app.Fragment.startActivity(Fragment.java:1068)
W/System.err﹕ at android.app.Fragment.startActivity(Fragment.java:1054)
W/System.err﹕ at パッケージ名.AnnotationDialogFragment$2$1.postExecute(AnnotationDialogFragment.java:41) //startActivity(intent)
W/System.err﹕ at パッケージ名.AsyncDetail.onPostExecute(AsyncDetail.java:36)
W/System.err﹕ at パッケージ名.AsyncDetail.onPostExecute(AsyncDetail.java:15)
W/System.err﹕ at android.os.AsyncTask.finish(AsyncTask.java:636)
W/System.err﹕ at android.os.AsyncTask.access$500(AsyncTask.java:177)
W/System.err﹕ at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:653)
W/System.err﹕ at android.os.Handler.dispatchMessage(Handler.java:102)
10-04 21:17:23.422 2701-2701/com.example.mac_gusami.pr_sample0917 W/System.err﹕ at android.os.Looper.loop(Looper.java:135)
W/System.err﹕ at android.app.ActivityThread.main(ActivityThread.java:5254)
W/System.err﹕ at java.lang.reflect.Method.invoke(Native Method)
W/System.err﹕ at java.lang.reflect.Method.invoke(Method.java:372)
W/System.err﹕ at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
W/System.err﹕ at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
デバッグをしてみても、
contextにMapsActivityが格納されており、
MapsActivityからDetailActivityに遷移するという認識です。
よろしくお願いいたします。
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
画面遷移の処理はおこなわず、
非同期処理だけおこない、
元のActivity側で画面遷移の処理をおこなったら
期待した処理ができました。
この度は、長々とお付き合いいただきありがとうございました。