【问题标题】:CalledFromWrongThreadException modifying a view using PhoneGap based ActivityCalledFromWrongThreadException 使用基于 PhoneGap 的 Activity 修改视图
【发布时间】:2013-06-21 08:28:44
【问题描述】:

我的 Android 应用使用基于 PhoneGap 的 Activity 进行登录。但是在用户登录后,在下一个活动中,我在后台运行了一个任务。但是当登录成功方法尝试编辑视图时(PhoneGap活动关闭后),它总是抛出

android.view.ViewRoot$CalledFromWrongThreadException:只有创建视图层次结构的原始线程才能接触其视图。

我的代码是:

// BeginActivity extends DroidGap
public class BeginLoginActivity extends BeginActivity 
{
    @Override
    protected String getFirstUrl() {
        Intent intent = getIntent();
        String pageState = intent.getStringExtra(SplashActivity.EXTRA_PAGE_STATE);

        if (pageState == null) {
            pageState = "login";
        }

        return "file:///android_asset/www/login.html#" +pageState;
    }

    //invoked at login.html via javascript
    public void gotoMain() {
        Intent intent = new Intent(this, MainNative.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent);
        finish();
    }
}

在 MainNative 类中,我编写了一个在后台运行的任务

public class MainNative extends Activity {
    ...
    public void loadItems() {
        ...
        new WatchlistHelper (this).execute(getItemId());
    }

    public void setWatchedStatus(Boolean _true) {
        // this is where the WebViewCoreThread is thrown, 
        // whenever a phonegap based Activity previously has been opened
        // and it run well if there's no phonegap Activity has run
        watchlistButton_.setImageResource(_true ? R.drawable.native_rating_important
                : R.drawable.native_rating_not_important);
        watchlistButton_.setTag(_true);
        watchlistButton_.setVisibility(VISIBLE);
        watchlistLoading_.setVisibility(GONE);
    }
    ...
}

WatchlistHelper:

public static class WatchlistHelper extends AsyncTask<String, Void, Boolean> 
{
    private MainNative mContext_;

    ...

    @Override
    protected Boolean doInBackground(String... _ids) {
        // My code that run in background
        return isTrue;
    }

    @Override
    protected void onPostExecute(Boolean _isTrue) {
        mContext_.setWatchedStatus(_isTrue);
    }
}

来自 logcat 的日志:

06-21 14:45:57.462: E/AndroidRuntime(265): FATAL EXCEPTION: WebViewCoreThread
06-21 14:45:57.462: E/AndroidRuntime(265): android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
06-21 14:45:57.462: E/AndroidRuntime(265):  at android.view.ViewRoot.checkThread(ViewRoot.java:2802)
06-21 14:45:57.462: E/AndroidRuntime(265):  at android.view.ViewRoot.requestLayout(ViewRoot.java:594)
06-21 14:45:57.462: E/AndroidRuntime(265):  at android.view.View.requestLayout(View.java:8125)
06-21 14:45:57.462: E/AndroidRuntime(265):  at android.view.View.requestLayout(View.java:8125)
06-21 14:45:57.462: E/AndroidRuntime(265):  at android.view.View.requestLayout(View.java:8125)
06-21 14:45:57.462: E/AndroidRuntime(265):  at android.widget.RelativeLayout.requestLayout(RelativeLayout.java:254)
06-21 14:45:57.462: E/AndroidRuntime(265):  at android.view.View.requestLayout(View.java:8125)
06-21 14:45:57.462: E/AndroidRuntime(265):  at android.view.View.requestLayout(View.java:8125)
06-21 14:45:57.462: E/AndroidRuntime(265):  at android.view.View.requestLayout(View.java:8125)
06-21 14:45:57.462: E/AndroidRuntime(265):  at android.widget.ScrollView.requestLayout(ScrollView.java:1200)
06-21 14:45:57.462: E/AndroidRuntime(265):  at android.view.View.requestLayout(View.java:8125)
06-21 14:45:57.462: E/AndroidRuntime(265):  at android.widget.RelativeLayout.requestLayout(RelativeLayout.java:254)
06-21 14:45:57.462: E/AndroidRuntime(265):  at android.view.View.requestLayout(View.java:8125)
06-21 14:45:57.462: E/AndroidRuntime(265):  at android.view.View.requestLayout(View.java:8125)
06-21 14:45:57.462: E/AndroidRuntime(265):  at android.widget.RelativeLayout.requestLayout(RelativeLayout.java:254)
06-21 14:45:57.462: E/AndroidRuntime(265):  at android.view.View.requestLayout(View.java:8125)
06-21 14:45:57.462: E/AndroidRuntime(265):  at android.view.View.requestLayout(View.java:8125)
06-21 14:45:57.462: E/AndroidRuntime(265):  at android.widget.ImageView.setImageResource(ImageView.java:275)
06-21 14:45:57.462: E/AndroidRuntime(265):  at com.posaurus.android.elements.MainNative.setWatchedStatus(MainNative.java:160)
06-21 14:45:57.462: E/AndroidRuntime(265):  at com.posaurus.android.elements.MainNative$WatchlistHelper.onPostExecute(MainNative.java:205)
06-21 14:45:57.462: E/AndroidRuntime(265):  at com.posaurus.android.elements.MainNative$WatchlistHelper.onPostExecute(MainNative.java:1)
06-21 14:45:57.462: E/AndroidRuntime(265):  at android.os.AsyncTask.finish(AsyncTask.java:417)
06-21 14:45:57.462: E/AndroidRuntime(265):  at android.os.AsyncTask.access$300(AsyncTask.java:127)
06-21 14:45:57.462: E/AndroidRuntime(265):  at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:429)
06-21 14:45:57.462: E/AndroidRuntime(265):  at android.os.Handler.dispatchMessage(Handler.java:99)
06-21 14:45:57.462: E/AndroidRuntime(265):  at android.os.Looper.loop(Looper.java:123)
06-21 14:45:57.462: E/AndroidRuntime(265):  at android.webkit.WebViewCore$WebCoreThread.run(WebViewCore.java:621)
06-21 14:45:57.462: E/AndroidRuntime(265):  at java.lang.Thread.run(Thread.java:1096)

【问题讨论】:

    标签: android multithreading cordova


    【解决方案1】:

    this PhoneGap document here:

    线程

    WebView 中的 JavaScript 不在 UI 线程上运行。它运行在 WebCore 线程。 execute 方法也在 WebCore 上运行 线程。

    问题是gotoMain() 正在WebCore 线程上运行,这意味着您正试图在WebCore 线程which is incorrect 上启动MainNative 活动。

    您应该能够通过修改您的 gotoMain() 方法来解决此问题,以确保它在 UI 线程上工作:

    public void gotoMain() {
        // run code on the UI thread:
        runOnUiThread(new Runnable() {
            public void run() {
                Intent intent = new Intent(BeginLoginActivity.this, MainNative.class);
                intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                startActivity(intent);
                finish();
            }
        });
    }
    

    编辑:您也可能正在运行into this problem。在旧版本的 Android(Jelly Bean 之前)中,有必要确保在主线程上加载了 AsyncTask class。根据您的 PhoneGap 应用程序的设置方式,您的代码第一次引用 AsyncTask(强制加载类)可能在后台线程上。如果发生这种情况,AsyncTask 类将基本上无法使用。如果您查看我链接到的答案,您可以将一行代码放在您的应用程序中的任何位置(1)早期运行,并且(2)您知道在 ma​​in 上运行thread ... 将此代码放在主线程上,然后将强制类加载到正确的线程上,并修复问题。

    Class.forName("android.os.AsyncTask");
    

    【讨论】:

    • 显然不是“登录成功方法正在尝试编辑视图”。在MainNativeActivity打开后,它会加载一个数据,然后在成功请求后,它会膨胀watchlistButton_所在的布局,(到这一步,修改视图是可以的)然后它运行一个后台任务及其回调调用setWatchedStatus(Boolean _true) 设置watchlistButton_ 图像。
    • @MorillaThaisa,我不太明白你想说什么。您要么在 WebCore 线程上调用 BeginLoginActivity#gotoMain(),要么在 WebCore 线程上调用 MainNative#loadItems()。其中任何一个都是错误的。我不能告诉你更多,因为你没有显示调用gotoMain()loadItems() 的代码。你也没有向我们展示你说你用watchListButton_膨胀布局的代码。
    • 这是怎么回事。首先,BeginLoginActivity#gotoMain() 在 WebCore 线程上被调用。然后,MainNative 活动打开,在其onCreate 方法中它会膨胀视图。视图准备好后,调用MainNative#loadItems() 并在后台运行任务WatchlistHelper。在WatchlistHelper#onPostExecute 上,它调用MainNative#setWatchedStatus(boolean) 来更改/编辑视图,这是WebViewCoreThread 抛出的地方。对不起,我的英语不好。但是,无论如何,谢谢你的答案。我在每个涉及视图的代码上都输入了代码 runOnUiThread
    • @MorillaThaisa,正如我在回答中所说,在 WebCore 线程上从gotoMain() 开始您的MainNative 活动是错误的。请参阅我的答案中的链接。您无需使用涉及视图的runOnUiThread() everywhere。你只需要确保你使用javascript调用Java的地方,你说的是gotoMain(),你在那里使用runOnUiThread()(见答案)。您还应该记住接受赞成对您有帮助的答案。这就是您在堆栈溢出时说“谢谢”的方式,您会发现它使将来更容易从其他人那里获得答案。
    • 我已经尝试了您答案中的代码,但错误仍然相同。我只赞成它。我也试过停止线程,不起作用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-07
    • 2017-11-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多