`

android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thre

 
阅读更多

 

当应用程序启动,创建了一个叫“main”的线程,用于管理UI相关,又叫UI线程。其他线程叫工作线程(Work Thread)。

Single Thread Model

 

  • 一个组件的创建并不会新建一个线程,他们的创建都在UI线程中进行,包括他们的回调方法,如onKeyDown()
  • 当在UI线程中进行某些耗时的操作时,将会阻塞UI线程,一般阻塞超过5秒就会显示一个ANR对话框。
  • UI线程是非线程安全的,所以,不能在工作线程中操作UI元素。

     

    两个原则

    • Do not block the UI thread (不要阻塞UI线程)
    • Do not access the Android UI toolkit from outside the UI thread (不要在工作线程中操作UI元素)

     

    在工作线程更新UI方法

    • Activity.runOnUiThread(Runnable)
    • Handler
      • sendMessage(Message)
      • post(Runnable)
    • AsyncTask
      • execute()
      • doInBackground()
      • onPostExecute()

     

    例子程序

    • HandlerActivity01
      • 在工作线程中进行UI操作。
    • HandlerActivity02
      • Handler的两个重要方法:sendMessagepost
    • HandlerActivity03
      • 官方推荐最佳方法。

     

    HandlerActivity01主要代码:

    Java代码   收藏代码
    1. btnEnd.setOnClickListener(new View.OnClickListener() {  
    2.       
    3.     @Override  
    4.     public void onClick(View v) {  
    5.         new Thread(new Runnable() {  
    6.   
    7.             @Override  
    8.             public void run()  
    9.             {  
    10.                 //在新建的线程(工作线程)中改变Button的文字  
    11.                 btnEnd.setText("Text Changed in Sub Thread");  
    12.             }  
    13.               
    14.         }).start();  
    15.     }  
    16. });  

     

     这是一种错误的做法,运行程序,会报错误:

    Java代码   收藏代码
    1. android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.   

     

     

     

     

     

     HandlerActivity02主要代码:

    Java代码   收藏代码
    1. public class HandlerActivity02 extends Activity  
    2. {  
    3.   
    4.     private int title = 0;  
    5.     Button btnStart,btnEnd;  
    6.   
    7.     private Handler mHandler = new Handler()  
    8.     {  
    9.         public void handleMessage(Message msg)  
    10.         {  
    11.             //更新UI  
    12.             switch (msg.what)  
    13.             {  
    14.                 case 1:  
    15.                     updateTitle();  
    16.                     break;  
    17.             }  
    18.         };  
    19.     };  
    20.       
    21.     public void onCreate(Bundle savedInstanceState)  
    22.     {  
    23.         super.onCreate(savedInstanceState);  
    24.         setContentView(R.layout.main);  
    25.           
    26.         btnStart = (Button)findViewById(R.id.start);  
    27.         btnEnd = (Button)findViewById(R.id.end);  
    28.         //新启动一个线程,进行耗时操作  
    29.         Timer timer = new Timer();  
    30.         //每六秒执行一次MyTask的run方法  
    31.         timer.scheduleAtFixedRate(new MyTask(this), 16000);  
    32.     }  
    33.   
    34.     private class MyTask extends TimerTask  
    35.     {  
    36.         private Activity context;  
    37.         MyTask(Activity context)  
    38.         {  
    39.             this.context = context;  
    40.         }  
    41.           
    42.         @Override  
    43.         public void run()  
    44.         {  
    45.             //耗时操作略....   
    46.               
    47.             //更新UI方法  1  
    48.             Message message = new Message();  
    49.             message.what = 1;  
    50.             mHandler.sendMessage(message);  
    51.               
    52.             //更新UI方法  2  
    53.             mHandler.post(updateThread);  
    54.               
    55.             //更新UI方法  3  
    56.             context.runOnUiThread(updateThread);  
    57.         }  
    58.     }  
    59.   
    60.     public void updateTitle()  
    61.     {  
    62.         setTitle("Welcome to Mr Wei's blog " + title);  
    63.         title++;  
    64.     }  
    65.       
    66.     Runnable updateThread = new Runnable()   
    67.     {  
    68.   
    69.         @Override  
    70.         public void run()  
    71.         {  
    72.             //更新UI  
    73.             btnStart.setText(String.valueOf(title));  
    74.             btnEnd.setText(String.valueOf(title));  
    75.         }  
    76.           
    77.     };  
    78. }  

     这里有个容易出错的地方,在更新UI方法2和3中,我们传入的参数是一个Runnable对象,一般认为这就会启动一个新的线程,而且常有人在这个Runnable对象的run方法中进行耗时操作。看过这块的源码就会知道,其实,android只是调用了这个Runnable对象的run方法而已,并没有启动新的线程,而且我们不应该在run方法中进行耗时操作,因为这个run方法最终是在UI线程里面执行的。也就是说,run方法里面只应该放更新UI的代码,handleMessage方法也一样。

     

    如果你要看这部分源代码的话,相信这个图对你会有帮助:

     

     

     

    HandlerActivity03主要代码:

    Java代码   收藏代码
    1. public class HandlerActivity03 extends Activity  
    2. {  
    3.     Button btnStart;  
    4.     @Override  
    5.     protected void onCreate(Bundle savedInstanceState)  
    6.     {  
    7.         // TODO Auto-generated method stub  
    8.         super.onCreate(savedInstanceState);  
    9.         setContentView(R.layout.main);  
    10.           
    11.         btnStart = (Button)findViewById(R.id.start);  
    12.         btnStart.setOnClickListener(new View.OnClickListener() {  
    13.               
    14.             @Override  
    15.             public void onClick(View v) {  
    16.                 //开始执行AsyncTask,并传入某些数据  
    17.                 new LongTimeTask().execute("New Text");  
    18.             }  
    19.         });  
    20.     }  
    21.       
    22.     private class LongTimeTask extends AsyncTask<String, Void , String>  
    23.     {  
    24.   
    25.         @Override  
    26.         protected String doInBackground(String... params)  
    27.         {  
    28.             try  
    29.             {  
    30.                 //线程睡眠5秒,模拟耗时操作,这里面的内容Android系统会自动为你启动一个新的线程执行  
    31.                 Thread.sleep(5000);  
    32.             }  
    33.             catch (InterruptedException e)  
    34.             {  
    35.                 e.printStackTrace();  
    36.             }  
    37.             return params[0];  
    38.         }  
    39.   
    40.         @Override  
    41.         protected void onPostExecute(String result)  
    42.         {  
    43.             //更新UI的操作,这里面的内容是在UI线程里面执行的  
    44.             btnStart.setText(result);  
    45.         }  
    46.           
    47.     }  
    48.   
    49. }  

     

     这个方法确实挺好,因为它为你封装了许多操作,你只需要记住在doInBackground方法中写耗时操作的代码,在onPostExecute方法中写更新UI的方法就行了。

     

    转载From: http://chen592969029.iteye.com/blog/1467237

分享到:
评论

相关推荐

    问题:android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original

    在Android开发中,`android.view.ViewRootImpl$CalledFromWrongThreadException` 是一个常见的异常,它通常发生在尝试在非UI线程(主线程)中修改用户界面元素时。这个异常表明,只有创建视图的原始线程(即主线程)...

    开新线程引发的常见3个异常

    2.android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. 3.java.lang.RuntimeException: Can't create handler inside thread ...

    Android 子线程更新UI

    android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. 这个异常为何不是 Only the main thread that created a view hierarchy ...

    Android实现在子线程中更新Activity中UI的方法

    本文实例讲述了Android实现在子线程中更新Activity中...ERROR/AndroidRuntime(1222): android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its

    详解Android UI更新的几种方法

    如果是在WT进行UI的更新,则会抛出异常,android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.(只有创建这个View的原

    invalidate

    在这种情况下,需要开启新的线程,但不能直接访问View对象,因为这会报android.view.ViewRoot$CalledFromWrongThreadException异常。为了解决这个问题,需要创建一个继承了android.os.Handler的子类,并重写...

    Android应用源码之五种效果的Toast-IT计算机-毕业设计.zip

    否则可能会导致`Android.view.ViewRootImpl$CalledFromWrongThreadException`异常。 7. **不同类型的Toast**:五种效果可能包括信息提示、警告、成功状态、错误状态等,每种类型对应不同的图标和颜色,以增强用户...

    安卓Android源码——点按钮添加TableRow源码.zip

    4. **运行时动态更新UI**:因为是运行时添加,所以需要确保所有操作都在UI线程上执行,否则可能会抛出`android.view.ViewRootImpl$CalledFromWrongThreadException`异常。可以使用`runOnUiThread()`或者`Handler`来...

    ThreadUtils

    - **主线程**:Android应用的UI更新必须在主线程(也称为应用或UI线程)中进行,否则会抛出`Android.view.ViewRootImpl$CalledFromWrongThreadException`异常。 - **工作线程**:用于执行耗时操作,如网络请求、...

    android中的Handler(2)

    - 不要在非UI线程中更新UI,这将抛出` android.view.ViewRootImpl$CalledFromWrongThreadException`异常。 8. **源码分析**: 对于进阶学习,了解`Handler`、`Looper`和`MessageQueue`的源码有助于深入理解它们的...

    常见handlerapi

    4. **子线程更新UI问题**:Android系统规定,只有主线程可以修改UI元素,子线程直接修改会导致`android.view.ViewRootImpl$CalledFromWrongThreadException`异常。为了解决这个问题,提供了以下方法: - `...

    Hand;er的使用

    构建了一个轻量级的消息通信机制,使得我们可以在不同的线程间进行数据交互,更新UI,避免了直接在非UI线程操作UI导致的`Android.view.ViewRootImpl$CalledFromWrongThreadException`异常。 `Handler`的工作原理...

    android demo,test印_handle_message_obtainMessage,消息处理和函数名和行号的打

    此外,避免在非UI线程中更新UI,否则会导致` android.view.ViewRootImpl$CalledFromWrongThreadException`异常。 了解并熟练掌握Handler机制对于Android开发者来说至关重要,它能够帮助我们实现复杂的异步操作,...

    android的倒计时功能

    Android UI操作必须在主线程(UI线程)中执行,否则会抛出`android.view.ViewRootImpl$CalledFromWrongThreadException`异常。因此,在子线程中进行倒计时计算后,我们需要使用`Handler`或`runOnUiThread`来更新UI。...

    【Android】CalledFromWrongThreadException 深入源码分析

    在使用 dialog 的时候,因为线程问题,在调用 dismiss() 方法的时候,出现如下常见的 crash–Only the original thread that created a view hierarchy can touch its views.,堆栈信息如下: threadName:main,...

    拦截android系统异常

    - 由于异常处理是在主线程中进行的,因此在显示对话框或执行其他UI操作时,需要确保在正确的线程中进行,否则可能会出现`Android.view.ViewRootImpl$CalledFromWrongThreadException`的错误。 - 记录异常信息时,...

    android service toast 01

    然而,Service通常运行在自己的工作线程中,这意味着在Service中直接使用Toast可能会导致`android.view.ViewRootImpl$CalledFromWrongThreadException`异常。为了解决这个问题,有以下两种常见方法: 1. 使用`...

    Android线程和进程介绍.pdf

    如果在非UI线程中尝试修改UI元素,会抛出`android.view.ViewRoot$CalledFromWrongThreadException`异常。 为了确保用户界面的响应性,避免主线程被长时间阻塞,耗时的操作如网络请求、数据库操作等应当在其他线程中...

Global site tag (gtag.js) - Google Analytics