1

これが失敗する理由を誰かに説明してもらいたいのですが...

私のアクティビティには、関数 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;
    }
}
4

1 に答える 1