什么是线程,线程有什么用

高德厚

进程与线程  一般来说,Android中为一个应用程序开启一个进程进行执行,在这个应用程序中的所有组件,通过单独的线程进行执行,而其中所有的线程,共享该应用程序进程的资源。

当一个应用程序启动的时候,Android系统启动一个新的Linux应用程序的进程和一个执行线程。

默认情况下,一个应用程序运行中的所有组件运行在相同的进程和线程中,这里的线程一般称为主线程。

如果一个应用程序的组件开始的时候,已经存在一个进程,那么应用程序会在与它相同的执行线程中开始这个组件。

进程  默认情况下,同一应用程序下的所有组件运行在同一进程中,大多数应用程序不应该改变这个。

然而,如果需要控制那个进程属于那个组件,可以在AndroidManifest.xml文件中进行配置。

一般来说,组件元素:activity、service、receiver、provider均支持一个android:process属性,可以设置这个属性让不同的组件单独运行在自己的进程中,也可以使用这个属性使不同的应用程序组件运行在相同的进程中,并共享相同的Linux用户ID和赋予同样的证书。

  Tips:application元素也支持android:process属性,用于设置所有的组件。

  Android在内存较低的情况下,会关闭一些优先级较低的进程以增大内存运行更重要的进程,而在这个进程中的所有线程,也会被同时销毁。

在内存足够的情况下,Android系统会视图尽可能保持应用程序进程,以达到下次的运行的快速启动,但最终需要移除旧的进程,回收内存用于新的或更重要的进程。

通过进程的优先级来判断是否被回收,一般会回收优先级低的进程,以给优先级高的进程腾出资源。

  下面是五类Android进程,他们的优先级顺序排列:

    Foreground process:前台进程。

    Visible prcess:可见进程。

    Service process:服务进程。

    Background process:后台进程。

    Empty process:空进程。

  Tips:一个进程的优先级是可以变化的。

线程  当应用程序启动时,系统会创建一个执行线程在这个应用程序的的进程中,一般被称为主线程。

这个线程是非常重要的,因为它负责把事件分发给响应的用户组件,包括绘制事件等,因此主线程又被称为UI线程。

系统并不会为每个组件创建一个单独的线程,而是在UI线程中,完成这些组件的初始化的,因此系统回调方法是运行在UI线程中,如click事件。

  当程序执行比较复杂的工作来应对用户交互的时候,哪怕应用程序被正确的执行了,单线程模式也可能会导致运行性能很低下。

举例来说,如果一切的应用功能都发生在UI线程中,当执行耗时操作的时候,如访问网络或查询数据,均会阻塞UI先,将导致其他的事件不被分发到事件队列中,包括屏幕绘制事件。

导致从用户的角度来看,应用程序死掉了。

而在Android系统中,当UI线程被阻塞超过几秒钟(大约是5秒)的时候,会弹出应用程序没有响应的对话框,造成用户体验差,可能会迫使用户决定退出你的应用或者干脆直接卸载它。

  此外,Android的UI ToolKit包下的所有组件都不是线程安全的,所以,不能在一个单独的工作线程中操作这些UI组件,必须在UI线程中操作。

因此,对于单线程模型,Android有两个规则:

    不能阻塞UI线程 不能在工作线程中访问Android UI ToolKit包下的组件。

  对于耗时的操作,应该放在单独的线程中。

例如:下面通过一个Demo监听按钮点击事件,下载一个图片,从单独的线程中,并显示在一个ImageView中。

1 btnError2.setOnClickListener(new View.OnClickListener() { 2 @Override 3 public void onClick(View v) { 4 // 增加一个线程访问网络 5 new Thread(new Runnable() { 6 @Override 7 public void run() { 8 // 获取地址下的图片 9 Bitmap btm=loadImageFromNetwork(http://ww4.sinaimg.cn/bmiddle/786013a5jw1e7akotp4bcj20c80i3aao.jpg);10 imageView1.setImageBitmap(btm); 11 }12 }).start();13 14 }15 });  起初,这似乎是合理的,启动了一个新线程来访问网络,但是它违反了规则二,不能在Android UI主线程之外修改UI组件,而在click中new Thread的是一个工作线程,在工作线程中无法操作UI组件,以上Demo会报错。

  要修正上面的错误,Android提供几种方法可以从其他线程中访问UI线程: Activity.runOnUiThread(Runnable):运行在指定的UI线程上,如果当前线程是UI线程,那么立即执行,如果当前线程不是UI线程,则发布到UI线程的事件队列中。

View.post(Runnable):将事件发布到UI线程中,立即被执行。

View.postDelayed(Runnanle,long):将事件发布到UI线程中,延迟被执行,延迟数为传递的long参数。

  下面通过两个Dem来通过上面介绍的方法来操作UI组件:  Activity.runOnUiThread: 1 btnRunOnUiThread.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 final Bitmap btm=loadImageFromNetwork(http://ww4.sinaimg.cn/bmiddle/786013a5jw1e7akotp4bcj20c80i3aao.jpg);10 MainActivity.this.runOnUiThread(new Runnable() { 11 @Override12 public void run() {13 imageView1.setImageBitmap(btm); 14 }15 });16 }17 }).start();18 }19 });  效果演示:

  View.post 1 btnPost.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 final Bitmap btm=loadImageFromNetwork(http://ww1.sinaimg.cn/bmiddle/88ff29e8jw1e7pjnpfxbrj20dp0a90tb.jpg);10 imageView1.post(new Runnable() {11 12 @Override13 public void run() {14 // TODO Auto-generated method stub15 imageView1.setImageBitmap(btm);16 }17 });18 }19 }).start(); 20 }21 });  效果演示: