Android线程模型

来源:互联网 发布:java求亦或 编辑:程序博客网 时间:2024/05/17 05:18
当一个程序第一次启动的时候,Android会启动一个LINUX进程和一个主线程。默认的情况下,所有该程序的组件都将在该进程和线程中运行。同时,Android会为每个应用程序分配一个单独的LINUX用户。Android会劲量保留一个正在运行进程,只在内存资源出现不足时,Android会尝试停止一些进程从而释放足够的资源给其他新的进程使用, 也能保证用户正在访问的当前进程有足够的资源去及时的响应用户的事件。Android会 根据进程中运行的组件类别以及组件的状态来判断该进程的重要性,Android会 首先停止那些不重要的进程。按照重要性从高到低一共有五个级别:

       前台进程
       前台进程是用户当前正在使用的进程。只有一些前台进程可以在任何时候都存在。他们是最后一个被结束的,当内存低到根本连他们都不能运行的时候。一般来说, 在这种情况下,设备会进行内存调度,中止一些前台进程来保持对用户交互的响应。

       可见进程
       可见进程不包含前台的组件但是会在屏幕上显示一个可见的进程是的重要程度很高,除非前台进程需要获取它的资源,不然不会被中止。

       服务进程
       运行着一个通过startService() 方法启动的service,这个service不属于上面提到的2种更高重要性的。service所在的进程虽然对用户不是直接可见的,但是他们执行了用户非常关注的任务(比如播放mp3,从网络下载数据)。只要前台进程和可见进程有足够的内存,系统不会 回收他们。

       后台进程
      运行着一个对用户不可见的activity(调用过 onStop() 方法).这些进程对用户体验没有直接的影响,可以在服务进程、可见进程、前台进 程需要内存的时候回收。通常,系统中会有很多不可见进程在运行,他们被保存在LRU (least recently used) 列表中,以便内存不足的时候被第一时间回收。如果一个activity正 确的执行了它的生命周期,关闭这个进程对于用户体验没有太大的影响。

       空进程
       未运行任何程序组件。运行这些进程的唯一原因是作为一个缓存,缩短下次程序需要重新使用的启动时间。系统经常中止这些进程,这样可以调节程序缓存和系统缓 存的平衡。

       Android 对进程的重要性评级的时候,选取它最高的级别。另外,当被另外的一个进程依赖的时候,某个进程的级别可能会增高。一个为其他进程服务的进程永远不会比被服 务的进程重要级低。因为服务进程比后台activity进程重 要级高,因此一个要进行耗时工作的activity最好启动一 个service来做这个工作,而不是开启一个子进程――特别 是这个操作需要的时间比activity存在的时间还要长的时 候。例如,在后台播放音乐,向网上上传摄像头拍到的图片,使用service可 以使进程最少获取到“服务进程”级别的重要级,而不用考虑activity目 前是什么状态。broadcast receivers做费时的工作的时候,也应该启用一个服务而不是开一个线程。

       2单线程模型
       当一个程序第一次启动时,Android会同时启动一个对应的 主线程(Main Thread),主线程主要负责处理与UI相关的事件,如:用户的按键事件,用户接触屏幕的事件以及屏幕绘图事 件,并把相关的事件分发到对应的组件进行处理。所以主线程通常又被叫做UI线 程。在开发Android应用时必须遵守单线程模型的原则: Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行。

       单线程模型会在没有考虑到它的影响的情况下引起Android应用程序性能低下,因为 所有的任务都在同一个线程中执行,如果执行一些耗时的操作,如访问网络或查询数据库,会阻塞整个用户界面。当在执行一些耗时的操作的时候,不能及时地分发 事件,包括用户界面重绘事件。从用户的角度来看,应用程序看上去像挂掉了。更糟糕的是,如果阻塞应用程序的时间过长(5秒钟)Android会向用户提示一些信息,即打开一个“应用程序没有相应(application not responding)”ANR 的对话框。

       其实单线程模型就是默认情况下android把所有操作都放在主线程也就是UI Thread线程中来执行 如果你想 O上边那段不是说它会阻塞用户界面嘛  那我可以另起一个线程来执行一些操作 没错你的想法非常good 。很给力。那么接下来 你就会尝试另起一个线程来 执行一些操作。OK 结果就有两种可能 一:你在另外开启的那个线程中执行了一些后台的操作 比如开启一个服务啊。神马的。那么恭喜你 你成功了。 二:第二种可能结果就是 你会收到一个华丽的异常 。这个例子很简单

java代码:
<?xml version="1.0" encoding="utf-8"?>  
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
android:orientation="vertical"  
android:layout_width="fill_parent"  
android:layout_height="fill_parent"  
>  
<TextView   
    android:id="@+id/textview01"   
    android:layout_width="fill_parent"   
    android:layout_height="wrap_content"   
    android:text="@string/hello"  
    />  
<Button  
    android:id="@+id/myButton"  
    android:layout_width="wrap_content"  
    android:layout_height="wrap_content"  
    android:layout_below="@id/textview01"  
    android:text="异常测试"  
    />  
<TextView  
    android:id="@+id/myTextView"  
    android:textSize="15pt"  
    android:layout_toRightOf="@id/myButton"  
    android:layout_alignTop="@id/myButton"  
    android:textColor="#FF0000"  
    android:layout_width="wrap_content"  
    android:layout_height="wrap_content"/>  
      
</RelativeLayout>  

java代码:

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class Activity01 extends Activity {
private Button myButton;
private TextView myTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);
  myButton = (Button) findViewById(R.id.myButton);
  myTextView = (TextView) findViewById(R.id.myTextView);
  myButton.setOnClickListener(new MyButtonListener());
}
class MyButtonListener implements OnClickListener {
  @Override
  public void onClick(View v) {
   new Thread() {
    @Override
    public void run() {
     // 我们在这里更新了UI 设置了TextView的值
     myTextView.setText("张三");
    }
   }.start();
  }
}
}

android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. 就是这样一个异常 这个异常告诉我们不可以再子线程中更新UI元素 比如我们上边那个例子设置了一个TextView的值。所有与UI相关的操作都不可以在子线程中执行都必须在UI线程中执行。这个异常大家以后可能会经常遇到多加注意就是了。
   
    我们继续,下面我们说说线程交互方面的东西。就比如我们想做点这些事情上边不是说了不能在子线程中更新UI 那比如我想在子线程中定义 一个字符串 然后通过发消息的方式 Message 把这个字符串 发送给主线程也就是UI线程 让UI线程来 设置这个TextView的值为我们刚刚在子线程中定义的字符串。 或者我想在子线程中开启音乐服务,或者把它停止该怎么做呢?  要知道怎么做先让我们了解 一下这几个对象吧!它们分别是

Message Queue

    在单线程模型下,为 了解决类似的问题,Android设 计了一个Message Queue(消息队列), 线程间可以通过该Message Queue并结合Handler和Looper组 件进行信息交换。
l  Message Queue 


     Message Queue是一个消息队列,用来存放通过Handler发 布的消息。消息队列通常附属于某一个创建它的线程,可以通过Looper.myQueue()得 到当前线程的消息队列。如果没有消息队列对象则会抛出空指针异常 。Android在 第一次启动程序时会默认会为UI thread创建一个关联的消息队列,用来管理程序的一些上层组件,activities,broadcast receivers 等等。你可以在自己的子线程中创建Handler与UI thread通讯。也就是说我们程序一启动我们的UI线程也就是主线程就会有一个消息队列 ,而如果我们自己另外开启的一个子线程就不会有MessageQueue(消息队列)对象。这一点大家务必知道。

       Handler

       通过Handler你 可以发布或者处理一个消息或者是一个Runnable的 实例。每个Handler都 会与唯一的一个线程以及该线程的消息队列关联。当你创建一个新的Handler时候,默认情况下,它将关联到创建它的这个线程和该线程的消息队列。也就是说,如果你通过Handler发 布消息的话,消息将只会发送到与它关联的这个消息队列,当然也只能处理该消息队列中的消息。这里大家就得理解一下了 也就是说我们 一个Handler对应一个线程以及附属于该线程的消息队列。就比如 我们现在有一个Handler对象这个Handler在UI线程中创建 叫xh_Handler 那么我们根据上边的说明 应该可以告诉我 这个xh_Handler是和那个线程关联的?  如果我用这个Handler发消息的话它将发给那个线程的消息队列? 如果你知道了 我想一般情况下你肯定是知道的。说明你很懂。我们继续。这时我们用这个Handler对象发送消息

       Handler对象也可以发送消息哦,它有发送消息的方法。下面就说到了 待会大家看示例程序也会发现 主要就是看它怎么使用。

       Handler的主要的方法有:

       1)   public final boolean sendMessage(Message msg)

       把消息放入该Handler所 关联的消息队列,放置在消息队列尾部。
       这里请允许我多说一句就是 我们把消息放进去的一端 消息不会阻塞但是处理消息的一端就有可能会阻塞。

       2)   public void handleMessage(Message msg)

       关联该消息队列的线 程将通过调用Handler的handleMessage方 法来接收和处理消息,通常需要子类化Handler来 实现handleMessage。

        Looper
        Looper扮演着一个Handler和 消息队列之间通讯桥梁的角色。程序组件首先通过Handler把 消息传递给Looper,Looper把 消息放入队列。Looper也 把消息队列里的消息广播给所有的Handler,Handler接 受到消息后调用handleMessage进 行处理。

       1)   可以通过Looper类 的静态方法Looper.myLooper得 到当前线程的Looper实 例,如果当前线程未关联一个Looper实 例,该方法将返回空(null)它不会抛空指针异常。

       2)   可以通过静态方法Looper. getMainLooper方法得到主线程的Looper实 例 这里需要注意一下 主线程默认是有一个Looper对象的。但是我们自己定义的子线程没有的。那么我们怎么在子线程中得到Looper对象呢?如果一个线程中调用Looper.prepare(),那么系统就会自动的为该线程建立一个消息队列,然后调用 Looper.loop();之后就进入了消息循环,这个之后就可以发消息、取消息、和处理消息。这个如何发送消息和如何处理消息可以再其他的线程中通过 Handle来做,但前提是我们的Hanle知道这个子线程的Looper,但是你如果不是在子线程运行 Looper.myLooper(),一般是得不到子线程的looper的。


原创粉丝点击