问题
I have already read all questions about this. And it's not the same case. Believe me.
I know an AsyncTask doInBackground function has his own thread, and you must touch the view in onPreExecute and onPostExecute.
Besides, I'm using isCancelled and isFinishing to really be sure the task is alive. And with all these things, the app is still crashing.
This is the code:
public class MainActivity {
private Activity activity;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.Main);
activity = this;
new MyTask(activity).execute();
}
private class MyTask extends AsyncTask<Void, Void, User[]> {
// ...
@Override
protected void onPreExecute() {
}
@Override
protected User[] doInBackground(String... params) {
// Api call
// return results
}
@Override
protected void onPostExecute(User[] users) {
if (isCancelled() || isFinishing()) return;
findViewById(R.id.panelViews).removeAllViews();
findViewById(R.id.element).setText("something");
// ... more
}
}
}
As you can see:
- the touch of the view is in the onPostExecute
- I use the "isCancelled" in case the user cancels the task exiting the screen
- The same with "isFinishing"
And even now I'm getting the error "Only the original thread that created a view hierarchy can touch its views" in the line when I try to change something of the view (in the example a simple text)
If I cancel (pressing back for example) when it's trying to get the API results is OK, and it works. If I cancel exactly when the API results come, and before it starts changing things, it crashes (I inserted a "sleep" in the onPostExecute to accomplish this test).
The error code is in line 112, that is the "findViewById(R.id.panelViews).removeAllViews();" described above:
android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRoot.checkThread(ViewRoot.java:2988)
at android.view.ViewRoot.requestLayout(ViewRoot.java:648)
at android.view.View.requestLayout(View.java:8416)
at android.view.View.requestLayout(View.java:8416)
at android.view.View.requestLayout(View.java:8416)
at android.view.View.requestLayout(View.java:8416)
at android.view.View.requestLayout(View.java:8416)
at android.widget.ScrollView.requestLayout(ScrollView.java:1303)
at android.view.View.requestLayout(View.java:8416)
at android.view.View.requestLayout(View.java:8416)
at android.view.ViewGroup.removeAllViews(ViewGroup.java:2354)
at com.test.activities.MainActivity$MyTask.onPostExecute(MainActivity.java:112)
at com.test.activities.MainActivity$MyTask.onPostExecute(MainActivity.java:1)
at android.os.AsyncTask.finish(AsyncTask.java:417)
at android.os.AsyncTask.access$300(AsyncTask.java:127)
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:429)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:130)
at android.os.HandlerThread.run(HandlerThread.java:60)
回答1:
Looking at the call stack, following call indicates that AsyncTask class was loaded on non-UI thread.
at android.os.HandlerThread.run(HandlerThread.java:60)
One of the Threading Rules mentioned in AsyncTask reference documentation is:
The AsyncTask class must be loaded on the UI thread. This is done automatically as of JELLY_BEAN.
Here is a bug related to this issue.
One of the solution is, load the AsyncTask
class manually in Application onCreate
method. This ensures the class gets loaded on main thread, before any application components uses it.
public class MyApplication extends Application {
@Override
public void onCreate() {
try {
Class.forName("android.os.AsyncTask");
} catch (ClassNotFoundException e) {
}
super.onCreate();
}
}
Remember to specify MyApplication
in AndroidManifest
file.
回答2:
The solution in this answer should force the code to be run on the ui thread. It uses the Activity#runOnUiThread(Runnable)
function. Your code would end up like this:
@Override
protected void onPostExecute(User[] users) {
if (isCancelled() || isFinishing()) return;
runOnUiThread(new Runnable() {
@Override
public void run() {
findViewById(R.id.panelViews).removeAllViews();
findViewById(R.id.element).setText("something");
// ... more
}
});
}
回答3:
This occurs when you start an AsyncTask
from a background thread. The onPreExecute
and onPostExecute
don't automatically run on the main thread but in reality the thread the AsyncTask
was started from.
In other words, if you were already within a background thread when you called execute
on the AsyncTask
then that same thread is where the onPostExecute
method is called from meaning you would get that exception.
回答4:
Try this, This should solve your problem, perform your task in runOnUiThread()
@Override
protected void onPostExecute(User[] users) {
runOnUiThread(new Runnable() {
public void run() {
if (isCancelled() || isFinishing()) return;
findViewById(R.id.panelViews).removeAllViews();
findViewById(R.id.element).setText("something");
}//run
}//runOnUiThread
}//onPostExecute
来源:https://stackoverflow.com/questions/23589673/cant-touch-views-from-onpostexecutes-asynctask