gpt4 book ai didi

android - 从错误的线程+内部类调用..奇怪的行为

转载 作者:行者123 更新时间:2023-11-30 03:37:21 27 4
gpt4 key购买 nike

我想有人向我解释为什么这会失败...

我的 Activity 有一个内部类 AppHelper,它导出一个函数 setThrobber。为简单起见,我省略了所有初始化代码等。

此函数 setThrobber 旨在在 UI 线程外部调用,通常在网络加载期间,以报告进度...然后,这些类使用内部类 AppHelper 和方法 setThrobber 来这样做,来自网络加载器线程。

令我惊讶的是,第一种方法失败了(见末尾的错误)而第二种方法成功了。为什么第一个不在 UI 线程中执行而第二个是???更奇怪的是,在错误堆栈跟踪中看起来它是从 UI 线程调用的,即使 Android 抛出 Called From Wrong Thread 异常。为什么从线程的角度来看,两个代码块不等价?

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
}
}
}

第一种情况的Stack trace:

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 类在主 Activity 中实例化,并传递给 Activity 中的其他子类,用 Wea​​kReference 保持它(已检查这不是问题)

  • 使用失败的 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;
}
}

最佳答案

查看 Android: Accessing UI Element from timer thread 中打勾的答案, 我想知道问题是否与 runOnUiThread 的位置有关调用已创建。

根据 android 开发者页面,runOnUiThread :

Runs the specified action on the UI thread. If the current thread is the UI thread, then the action is executed immediately. If the current thread is not the UI thread, the action is posted to the event queue of the UI thread.

注释来自 Android Java runOnUiThread()那个电话 post在 Activity 的处理程序上与调用 runOnUithread 基本相同.

因此,问题是与第一种情况关联的处理程序与第二种情况中的处理程序有何不同。

在第二种情况下,我怀疑您一定会发布到与主要 Activity 关联的处理程序。

在第二种情况下,我推断是与创建 AppHelper 的线程关联的处理程序,根据您的说法,这应该是相同的。

编辑:根据与问题作者的进一步互动,关键如下:

似乎onPause有效地停止 Activity UI 线程死机,并且 onResume创建一个新的。因此,我们可以访问它的处理程序也被这个过程更新。

(最终的,弱的)appHolder 实例与旧的 UI 线程处理程序相关联,因此如果 runOnUiThread在其中执行,指的是旧持有人(通过 runOnUiThread )。这是第一种情况。

但是,在第二种情况下,调用 runOnUiThread不再在该代码中执行(调用前 onPause ),而是在主要 Activity 内的方法中执行,该方法将更新处理程序 runOnUiThread电话。

简而言之:确保调用 runOnUiThread (实际上是 handler.post() )总是以保证他们使用最新的 live 的方式完成。 Activity 的版本(及其 UI 线程),并且不链接到以前的版本。

关于android - 从错误的线程+内部类调用..奇怪的行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16444060/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com