これが失敗する理由を誰かに説明してもらいたいのですが...
私のアクティビティには、関数 setThrobber をエクスポートする内部クラス AppHelper があります。簡単にするために、すべての初期化コードなどを省略しました。
この関数 setThrobber は、UI スレッドの外部で、通常はネットワークの読み込み中に、進行状況を報告するために呼び出されることを意図しています。次に、クラスは、setThrobber メソッドで内部クラス AppHelper を使用して、ネットワーク ローダー スレッドから呼び出します。
驚いたことに、最初のアプローチは失敗し (最後のエラーを参照)、2 番目のアプローチは成功します。最初のスレッドが UI スレッドで実行されず、2 番目のスレッドが実行されるのはなぜですか? さらに奇妙なのは、エラー スタック トレースでは、Android が Called From Wrong Thread 例外をスローしたにもかかわらず、UI スレッドから呼び出されたように見えることです。スレッドの観点から、両方のコード チャンクが同等ではないのはなぜですか?
PD- 同じ結果で handler.post() も試しました! PD2- AppHelper クラスは onCreate 内でインスタンス化されます
前もって感謝します !!
public class MyApplication extends Activity {
private ProgressDialog progressDialog;
void setThrobber_internal (String message) {
progressDialog.setMessage(message);
}
public class AppHelper {
public setThrobber(final String msg) {
MyApplication.this.runOnUiThread(new Runnable() {
@Override
public void run() {
setThrobber_internal(msg);
// This throws CalledFromWrongThread (!!)
}
});
}
}
}
対
public class MyApplication extends Activity {
private ProgressDialog progressDialog;
private void setThrobber_internal(final String msg) {
// runUiThread here instead of in inner class wrapper
runOnUiThread(new Runnable() {
@Override
public void run() {
progressDialog.setMessage(msg);
}
});
}
public class AppHelper {
public void setThrobber(final String msg) {
setThrobber_internal(msg); // this works OK
}
}
}
最初の状況のスタック トレース:
E/AndroidRuntime(17677): FATAL EXCEPTION: main
E/AndroidRuntime(17677): android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
E/AndroidRuntime(17677): at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:4039)
E/AndroidRuntime(17677): at android.view.ViewRootImpl.invalidateChild(ViewRootImpl.java:722)
E/AndroidRuntime(17677): at android.view.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:771)
E/AndroidRuntime(17677): at android.view.ViewGroup.invalidateChild(ViewGroup.java:4005)
E/AndroidRuntime(17677): at android.view.View.invalidate(View.java:8576)
E/AndroidRuntime(17677): at android.view.View.invalidate(View.java:8527)
E/AndroidRuntime(17677): at android.widget.TextView.checkForRelayout(TextView.java:6760)
E/AndroidRuntime(17677): at android.widget.TextView.setText(TextView.java:3306)
E/AndroidRuntime(17677): at android.widget.TextView.setText(TextView.java:3162)
E/AndroidRuntime(17677): at android.widget.TextView.setText(TextView.java:3137)
E/AndroidRuntime(17677): at com.android.internal.app.AlertController.setMessage(AlertController.java:261)
E/AndroidRuntime(17677): at android.app.AlertDialog.setMessage(AlertDialog.java:185)
E/AndroidRuntime(17677): at android.app.ProgressDialog.setMessage(ProgressDialog.java:314)
----------------------------------
E/AndroidRuntime(17677): at com.regaliz.libneo.NativeStory.setThrobber_internal(NativeStory.java:269)
E/AndroidRuntime(17677): at com.regaliz.libneo.NativeStory$AppHelper$8.run(NativeStory.java:865)
----------------------------------
E/AndroidRuntime(17677): at android.os.Handler.handleCallback(Handler.java:605)
E/AndroidRuntime(17677): at android.os.Handler.dispatchMessage(Handler.java:92)
E/AndroidRuntime(17677): at android.os.Looper.loop(Looper.java:137)
追加コードのリクエスト:
AppHelper クラスはメイン アクティビティ内でインスタンス化され、アクティビティ内の他の子クラスに渡され、WeakReference で保持されます (これは問題ではないことを確認してください)。
失敗した AppHelper を使用するクラスは次のことを行います。
public void story_loadfonts(String jsonFonts) {
final AppHelper appHelper=mWeakAppHelper.get(); // apphelper stored in a weak ref
try {
.
.
.
new Thread(new Runnable() {
@Override
public void run() {
try {
for (int i=0; i<NUMFONTS; i++) {
load_font_from_network(i);
appHelper.setThrobber("LOADING FONT "+i+"/"+NUMFONTS);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}).start();
} catch (Exception e) {
return;
}
}