为什么说android UI操作不是线程安全的

分类:Android 1357人阅读 评论(0) 收藏 举报

目录(?)[+]

可能在非UI线程中刷新界面的时候,UI线程(或者其他非UI线程)也在刷新界面,这样就导致多个界面刷新的操作不能同步,导致线程不安全。

1、为什么说invalidate()不能直接在线程中调用?
2、它是怎么违背单线程的?
3、androidui为什么说不是线程安全的?
4、androidui操作为什么一定要在UI线程中执行?

1、为什么说invalidate()不能直接在线程中调用?
答:
Android提供了Invalidate方法实现界面刷新,但是Invalidate不能直接在非UI主线程中调用,因为他是违背了单线程模型:AndroidUI操作并不是线程安全的,并且这些操作必须在UI线程中调用。例如:在非UI线程中调用invalidate会导致线程不安全,也就是说可能在非UI线程中刷新界面的时候,UI线程(或者其他非UI线程)也在刷新界面,这样就导致多个界面刷新的操作不能同步,导致线程不安全
2、它是怎么违背单线程的?
答:一个Android程序开始运行时,就有一个主线程MainThread被创建。该线程主要负责UI界面的显示、更新和控件交互,所以又叫UIThread。由于只有UI线程更新界面所以说
Android是单线程模型。一个Android程序创建之初,一个Process呈现的是单线程模型--即MainThread,在非主线程(UI线程)外调invalidate()刷新界面出现异常,即是说用其他的线程更新UI,android中是不被允许的。
3、androidui为什么说不是线程安全的?
答:androidUI中提供invalidate()来更新界面,而invalidate()方法是线程不安全。
4、androidui操作为什么一定要在UI线程中执行?
答:UI主线程是更新UI界面的,更新了界面才能看到运行的效果。
再者AndroidUI操作并不是线程安全的并且这些操作必须在UI线程中执行。如果在子线程中直接修改UI,会导致异常。




UI线程及Android的单线程模型原则

  当应用启动,系统会创建一个主线程(main thread)

  这个主线程负责向UI组件分发事件(包括绘制事件),也是在这个主线程里,你的应用和Android的UI组件(components from the Android UI toolkit (components from theandroid.widgetandandroid.viewpackages))发生交互。

  所以main thread也叫UI thread也即UI线程

  系统不会为每个组件单独创建线程,在同一个进程里的UI组件都会在UI线程里实例化,系统对每一个组件的调用都从UI线程分发出去

  结果就是,响应系统回调的方法(比如响应用户动作的onKeyDown()和各种生命周期回调)永远都是在UI线程里运行。

  当App做一些比较重(intensive)的工作的时候,除非你合理地实现,否则单线程模型的performance会很poor。

  特别的是,如果所有的工作都在UI线程,做一些比较耗时的工作比如访问网络或者数据库查询,都会阻塞UI线程导致事件停止分发(包括绘制事件)。对于用户来说,应用看起来像是卡住了,更坏的情况是,如果UI线程blocked的时间太长(大约超过5秒),用户就会看到ANR(application not responding)的对话框。

  另外,Andoid UI toolkit并不是线程安全的,所以你不能从非UI线程来操纵UI组件。你必须把所有的UI操作放在UI线程里,所以Android的单线程模型有两条原则:

  1.不要阻塞UI线程。

  2.不要在UI线程之外访问Android UI toolkit(主要是这两个包中的组件:android.widgetandandroid.view)。

使用Worker线程

  根据单线程模型的两条原则,首先,要保证应用的响应性,不能阻塞UI线程,所以当你的操作不是即时的那种(not instantaneous),你应该把他们放进单另的线程中(叫做background或者叫worker线程)。

  比如点击按钮后,下载一个图片然后在ImageView中展示:

public void onClick(View v) {    new Thread(new Runnable() {        public void run() {            Bitmap b = loadImageFromNetwork("http://example.com/image.png");            mImageView.setImageBitmap(b);        }    }).start();}

  这段代码用新的线程来处理网络操作,但是它违反了第二条原则:

  Do not access the Android UI toolkit from outside the UI thread.

  从非UI线程访问UI组件会导致未定义和不能预料的行为

  为了解决这个问题,Android提供了一些方法,从其他线程访问UI线程:

  • Activity.runOnUiThread(Runnable)
  • View.post(Runnable)
  • View.postDelayed(Runnable, long)

  比如,上面这段代码可以这么改:

public void onClick(View v) {    new Thread(new Runnable() {        public void run() {            final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png");            mImageView.post(new Runnable() {                public void run() {                    mImageView.setImageBitmap(bitmap);                }            });        }    }).start();}

  这么改之后就是线程安全的了。

  但是,当操作变得复杂的时候,这种代码会变得非常复杂,为了处理非UI线程和UI线程之间更加复杂的交互,可以考虑在worker线程中使用一个Handler,来处理UI线程中传来的消息。

  也可以继承这个类AsyncTask。

Communicating with the UI Thread

  只有在UI线程中的对象才能操作UI线程中的对象,为了将非UI线程中的数据传送到UI线程,可以使用一个Handler运行在UI线程中。

  Handler是Android framework中管理线程的部分,一个Handler对象负责接收消息然后处理消息。

  你可以为一个新的线程创建一个Handler,也可以创建一个Handler然后将它和已有线程连接。

  如果你将一个Handler和你的UI线程连接,处理消息的代码就将会在UI线程中执行。

  可以在你创建线程池的类的构造方法中实例化Handler的对象,然后用全局变量存储这个对象。

  要和UI线程连接,实例化Handler的时候应该使用Handler(Looper)这个构造方法。

  这个构造方法使用了一个Looper对象,这是Android系统中线程管理的framework的另一个部分。

  当你用一个特定的Looper实例来创建一个Handler时,这个Handler就运行在这个Looper的线程中。

  在Handler中,要覆写handleMessage()方法。Android系统会在Handler管理的相应线程收到新消息时调用这个方法

  一个特定线程的所有Handler对象都会收到同样的方法。(这是一个“一对多”的关系)。

更多相关文章

  1. Android中ListView的使用及优化
  2. android webview模拟网页post操作
  3. [置顶] Android实用代码集
  4. Android(安卓)线程间通信------handler
  5. android 服务与多线程
  6. Service
  7. Android应用程序线程消息循环模型分析(4)
  8. Android实现界面跳转
  9. Android(安卓)定义全局LOG打印日志,并显示调用者相关信息

随机推荐

  1. android沉浸式状态栏实现,android沉浸式状
  2. android hander 线程用法
  3. gradle clean报错Could not find manifes
  4. android HandlerThread使用小例
  5. Android 的AsyncTask使用
  6. >>>> Android adb shell后面可用的常用命
  7. [置顶] editext文本输入框 软键盘下个按
  8. Android手动回收bitmap,引发Canvas: tryi
  9. Android通过PopupMenu定义弹出菜单的位置
  10. Android UI Fundamentals Develop and De