Android AsyncTask cannot resolve CalledFromWrongThreadException问题解决方案

来源:互联网 发布:vb精简版6.0怎么安装 编辑:程序博客网 时间:2024/06/07 13:55

Android 在JELLY_BEAN之前的版本调用AsyncTask时,如果首次不是在主线程加载,可能会报如下错误:cannot resolve CalledFromWrongThreadException。可参考如下Stackoverflow中介绍的解决方法:

----------------------------------------------------

From: http://stackoverflow.com/questions/18478347/android-runonuithread-asynctask-cannot-resolve-calledfromwrongthreadexception/18479289#18479289

-----

I'm working for an Android app and implementing a ProgressBar by using AsyncTask class.

The problem is that on some devices, it causes "CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views." in onPostExecute. On those devices, the problem occurs 100%. On other devices, it works fine.

Starting the AsyncTask

From the Android Docs

Threading rules

There are a few threading rules that must be followed for this class to work properly:

  • The AsyncTask class must be loaded on the UI thread. This is done automatically as of JELLY_BEAN.
  • The task instance must be created on the UI thread.
  • execute(Params...) must be invoked on the UI thread.
  • Do not call onPreExecute(), onPostExecute(Result), doInBackground(Params...), onProgressUpdate(Progress...) manually.
  • The task can be executed only once (an exception will be thrown if a second execution is attempted.)

You don't show where you normally instantiate, and execute the task, so make sure that you do this in code that's already on the UI/main thread. Note that the first bullet point above might explain why this works for you on some devices, and not on others.

Creating the View Hierarchy

The message tells you

Only the original thread that created a view hierarchy can touch its views.

and you're assuming that this is because your async task is (strangely) trying to modify the UI on a background thread. However, it is possible that you get this error because the async task modifies the UI on the main thread, but the UI (ProgressBar) was not created correctly in the first place.

See this question for an example of how you can erroneously create the view on the wrong thread (anything other than the main thread), and get this same error.

More

I would, however, like to see exactly where you are logging the thread ID, and what value(s) you're getting. If you check out my first two suggestions, and they don't solve your problem, then we may need more information.

You also mention a Handler (?), but don't show how or where you use that. Normally, using AsyncTask removes the need to use Handler, so I'm a little worried about how you might be using that.

Update

Per the discussion in comments below, it looks like the issue here is the one discussed in this question. Some code, probably running on a background thread, is first to cause the AsyncTaskclass to be loaded. The original (pre-Jelly Bean) implementation of AsyncTask required class loading to occur on the main thread (as mentioned in the Threading Rules above). The simple workaround is to add code on the main thread (e.g. in Application#onCreate()) that forces early, deterministic class loading of AsyncTask:

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

From: http://stackoverflow.com/questions/14978924/what-is-the-difference-between-loading-and-creating-asynctask-on-the-ui-thread/18501792#18501792

-----

While reading AsyncTask documentation, the part on Threading rules, I found this:

  • The AsyncTask class must be loaded on the UI thread. This is done automatically as of JELLY_BEAN.
  • The task instance must be created on the UI thread.
  • execute(Params...) must be invoked on the UI thread.

What is meant by "loading"? It's not instantiating or executing, as the documentation talked about those later.


I don't believe that this answer is actually correct.

It wouldn't make sense for the documentation to separately list instantiation and loading, if those things were actually the same. I believe this statement

The AsyncTask class must be loaded on the UI thread.

is referring to Java Class Loading. In other words, the AsyncTask class itself needs to be loaded on the main thread. In Jelly Bean (or later), this is automatic. But, in older versions of Android, there is the potential for this class to be loaded on another thread, which can cause problems.

See this Google discussion for more information. Basically, there are conditions (for example, code using IntentService) that can cause the AsyncTask to be first loaded on the wrong (non-main) thread.

The simplest fix for this, prior to Jelly Bean, seems to be to use something like:

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

in the Application's onCreate() method, to force class loading to happen when you want it to.


Creating the AsyncTask instance is probably what you think it is ... instantiating it:

MyAsyncTask task = new MyAsyncTask();

and that should also be run on the main thread.


0 0
原创粉丝点击