详解Android进程和线程

2019-12-10 17:56:15王振洲

系统绝对不会为每个组件实例创建单独的线程。运行于同一进程的所有组件均在 UI 线程中实例化,并且对每个组件的系统调用均由该线程进行分派。因此,响应系统回调的方法(例如,报告用户操作的 onKeyDown() 或生命周期回调方法)始终在进程的 UI 线程中运行。

例如,当用户触摸屏幕上的按钮时,应用的 UI 线程会将触摸事件分派给小工具,而小工具反过来又设置其按下状态,并将无效请求发布到事件队列中。UI 线程从队列中取消该请求并通知小工具应该重绘自身。

在应用执行繁重的任务以响应用户交互时,除非正确实施应用,否则这种单线程模式可能会导致性能低下。 特别地,如果 UI 线程需要处理所有任务,则执行耗时很长的操作(例如,网络访问或数据库查询)将会阻塞整个 UI。一旦线程被阻塞,将无法分派任何事件,包括绘图事件。从用户的角度来看,应用显示为挂起。 更糟糕的是,如果 UI 线程被阻塞超过几秒钟时间(目前大约是 5 秒钟),用户就会看到一个让人厌烦的“应用无响应”(ANR) 对话框。如果引起用户不满,他们可能就会决定退出并卸载此应用。

此外,Android UI 工具包并非线程安全工具包。因此,您不得通过工作线程操纵 UI,而只能通过 UI 线程操纵用户界面。因此,Android 的单线程模式必须遵守两条规则:

1、不要阻塞 UI 线程
2、不要在 UI 线程之外访问 Android UI 工具包

工作线程

根据上述单线程模式,要保证应用 UI 的响应能力,关键是不能阻塞 UI 线程。如果执行的操作不能很快完成,则应确保它们在单独的线程(“后台”或“工作”线程)中运行。

例如,以下代码演示了一个点击侦听器从单独的线程下载图像并将其显示在 ImageView 中:

public void onClick(View v) {
  new Thread(new Runnable() {
    public void run() {
      Bitmap b = loadImageFromNetwork("http://www.easck.com/pre>
	
	

乍看起来,这段代码似乎运行良好,因为它创建了一个新线程来处理网络操作。 但是,它违反了单线程模式的第二条规则:不要在 UI 线程之外访问 Android UI 工具包—此示例从工作线程(而不是 UI 线程)修改了 ImageView。这可能导致出现不明确、不可预见的行为,但要跟踪此行为困难而又费时。