Java基础技术核心归纳(四)

来源:互联网 发布:阿里云icp备案系统 编辑:程序博客网 时间:2024/06/06 03:03

Java基础技术核心归纳(四)
转载请声明出处:http://blog.csdn.net/andrexpert/article/details/77931466


Android                                          Java                                            数据结构
Android基础技术核心归纳(一)     Java基础技术核心归纳(一)     数据结构基础知识核心归纳(一) 
Android基础技术核心归纳(二)     Java基础技术核心归纳(二)     数据结构基础知识核心归纳(二)  
Android基础技术核心归纳(三)     Java基础技术核心归纳(三)    数据结构基础知识核心归纳(三)  
Android基础技术核心归纳(四)    Java基础技术核心归纳(四)


1.Java命名规则

(1)变量:以$、字母、下划线开头,后面以数字、字母、下划线。标识符不能是关键字和保留字,字符长度没有限制。
(2)常量:即static final,常量所有字母要大写;
(3)方法:方法命名变量类似,如count(),getSum();
(4)类:类名的所有单词首字母均大写. 如Person{}  , DataCenter{};
(5)包名:用小写的倒置域名来命名. 格式: 前缀 + 项目名 + 模块名 + 层如:  org.itfuture.domain.sorts 
2.null关键字
(1)null是代表不确定的对象。Java中,null是一个关键字,用来标识一个不确定的对象。因此可以将null赋给引用类型变量,但不可以将null赋给基本类型变量。
典型实例:
public class HelloWord { public void say(){  System.out.println("1"); } public static void main(String[] args) {  ((HelloWord)null).say(); }}
说明:编译通过,运行时出现"java.lang.NullPointerException"异常。如果将say()方法修改为static静态方法时,执行" ((HelloWord)null).say(); "输出1。
(2)null本身不是对象,也不是Objcet的实例。
(3)在定义变量的时候,如果定义后没有给变量赋值,则Java在运行时会自动给变量赋值。赋值原则是整数类型int、byte、short、long的自动赋值为0,带小数点的float、double自动赋值为0.0,boolean的自动赋值为false,其他各供引用类型变量自动赋值为null(String s默认值为null)。
典型实例:
public class HelloWord { public static void main(String[] args) {   String s;  System.out.println("s="+s); }}
说明:由于局部变量String s没有初始化,代码不能编译通过。如果将String s改为静态成员变量,那么当类加载时会自动给s赋初值null,输出"s=null"。
(4)容器类型与null。
    List:允许重复元素,可以加入任意多个null。
    Set:不允许重复元素,最多可以加入一个null。
    Map:Map的key最多可以加入一个null,value字段没有限制。
    数组:基本类型数组,定义后,如果不给定初始值,则java运行时会自动给定值。引用类型数        
                组,不给定初始值,则所有的元素值为null。
(5)释放内存。让一个非null的引用类型变量指向null。这样这个对象就不再被任何对象应用了。等待JVM垃圾回收机制去回收
3.Java构造方法被调用情况
    当一个子类继承于父类时,实例化子类,首先会执行父类默认无参数构造方法,然后再执行子类用于实例化对象的构造方法。
class People{  String name;  public People() {                     //第一个执行   System.out.println(1);  }  public People(String name) { //第三个执行   System.out.println(2);   this.name = name;  }}public class HelloWord extends People{ People father; public HelloWord(String name) {  //第二个执行  System.out.println(3);          this.name = name;     father = new People(name+";F"); } public HelloWord() {  System.out.println(4); } public static void main(String[] args) {   new HelloWord("milk"); }}
4.Java的实例化对象过程
    学习JAVA这门面向对象的语言,实质就是不断地创建类,并把类实例化为对象并调用方法。对于初学JAVA的人总搞清楚对象是如何实例化的,假如类之间存在继承关系,那就更糊涂了。下面我们通过两个例题来说明对象的实例化过程。

       编译并运行该程序会有以下输出 Static Block Employee Company:china soft Non-Static Block Employee phone:0755-51595599 Employee(String) Empoloyee() 下面我们来对结果做分析: 1 在代码34行实例化对象时, 先对给(1)静态变量分配内存空间并初始化,然后执行静态块。 因此,在这里会输出: Static Block Employee Company:china soft Employee Company:china soft的输出也说明是先初始化静态变量然后再执行静态块,否则company将为null。 (2)然后在内存中为Employee分配内存空间,并做默认初始化(即所有变量初始化为默认值,这里都初始化为null)。 3 默认初始化完成后,开始显示初始化。即执行第5行,将phone初始化"0755-51595599",并且执行非静态方法块;因此在这里会有以下输出: Non-Static Block Employee phone:0755-51595599 4 最后才调用默认构造函数,在默认构造函数中调用了带参数的构造函数,所以在这里先输出带参数构造函数中的:Employee(String),然后才输出:Empoloyee()。
例2:

      上面的代码中Manager继承了前面写的Employee类,当我们编译并运行Manager类,会产生以下的输出: 
Static Block Employee Company:china soft Sub Static Block Manager department:sale      //静态代码块
Non-Static Block Employee phone:0755-51595599 Employee(String) Empoloyee()   //父类:代码块、无参构造方法
Sub Non-Static Block Manager salary:8000 Manager(String) Manager() //子类:代码块、构造方法
 下面我们对结果做分析: 1 在行34实例化对象时,由于Manager继承了Employee,所以先从父类Employee开始;(1)先给父类静态变量分配内存空间并初始化,然后执行父类静态块。(2)然后再给子类静态变量分配内存空间并初始化,然后执行子类静态块,所以会得到以下输出: Static Block Employee Company:china soft Sub Static Block Manager department:sale
(3)然后在内存中为父类Employee分配内存空间,并做默认初始化;再(4)为子类Manager分配内存空间,并做默认初始化。 3默认初始化完成后,从父类开始显示初始化并执行非静态方法块和构造函数,然后再子类开始显示初始化并执行非静态方法块和构造函数。因此会产生以下的输出: Non-Static Block Employee phone:0755-51595599 Employee(String) Empoloyee() Sub Non-Static Block Manager salary:8000 Manager(String) Manager()
总结以上内容,可以得到对象初始化过程: 
1、如果存在继承关系,就先父类后子类; 
2 、如果在类内有静态变量和静态代码块,就先静态后非静态代码块,最后才是构造函数; 
3 、继承关系中,必须要父类初始化完成后,才初始化子类。
5.经典String str = new String("abc")内存分配问题
(1)String的特性
◆String类是final的,不可被继承。
◆String类是的本质是字符数组char[], 并且其值不可改变。
◆String类对象有个特殊的创建的方式,就是直接指定比如String x = "abc","abc"就表示一个字符串对象。而x是"abc"对象的地址,也叫做"abc"对象的引用。
◆String对象可以通过“+”串联。串联后会生成新的字符串。
◆Java运行时会维护一个String Pool(String池),即字符串常量池。String池用来存放运行时中产生的各种字符串,并且池中的字符串的内容不重复。而一般对象不存在这个缓冲池,并且创建的对象仅仅存在于方法的堆栈区。
◆创建字符串的方式很多,归纳起来有三类:
    其一,使用new关键字创建字符串,比如String s1 = new String("abc");
    其二,直接指定。比如String s2 = "abc";
    其三,使用串联生成新的字符串。比如String s3 = "ab" + "c"; 
(2)String对象的创建
    总体原则:无论使用哪种方式创建一个字符串对象s,运行中的JVM会检查字符串常量池中是否存在内容相同的字符串对象,不存在,则在池中创建一个字符串对象s,即重新分配内存,否则,不在池中添加。
1. String str1 = "abc"; 
    System.out.println(str1 == "abc");
步骤: 
1) 栈中开辟一块空间存放引用str1;
2) String池中开辟一块空间,存放String常量"abc"; 
3) 引用str1指向池中String常量"abc";
4) str1所指代的地址即常量"abc"所在地址,输出为true;
 
2. String str2 = new String("abc"); 
    System.out.println(str2 == "abc");
步骤: 
1) 栈中开辟一块空间存放引用str2; 
2) 堆中开辟一块空间存放一个新建的String对象"abc"; 
3) 引用str2指向堆中的新建的String对象"abc";
4) str2所指代的对象地址为堆中地址,而常量"abc"地址在池中,输出为false;
 
3. String str3 = new String("abc"); 
    System.out.println(str3 == str2);
步骤: 
1) 栈中开辟一块空间存放引用str3;
2) 堆中开辟一块新空间存放另外一个(不同于str2所指)新建的String对象; 
3) 引用str3指向另外新建的那个String对象 ;
4) str3和str2指向堆中不同的String对象,地址也不相同,输出为false;
 
4. String str4 = "a" + "b"; 
    //等价于final String s1 = “a”;
    //         final String s2 = "b";
    //         String str4 = s1+s2;
    System.out.println(str4 == "ab");
步骤: 
1) 栈中开辟一块空间存放引用str4; 
2) 根据编译器合并已知量的优化功能,池中开辟一块空间,存放合并后的String常量"ab"; 
3) 引用str4指向池中常量"ab";
4) str4所指即池中常量"ab",输出为true;
 
5. final String s = "a"; //注意:这里s用final修饰,相当于一个常量
    String str5 = s + "b"; 
    System.out.println(str5 == "ab");
步骤: 
同四
 
6. 
String s1 = "a";     String s2 = "b";     String str6 = s1 + s2;     System.out.println(str6 == "ab");
步骤: 
1) 栈中开辟一块中间存放引用s1,s1指向池中String常量"a", 
2) 栈中开辟一块中间存放引用s2,s2指向池中String常量"b", 
3) 栈中开辟一块中间存放引用str6, 
4) s1 + s2通过StringBuilder的最后一步toString()方法还原一个新的String对象"ab",因此堆中开辟一块空间存放此对象, 
5) 引用str6指向堆中(s1 + s2)所还原的新String对象, 
6) str6指向的对象在堆中,而常量"ab"在池中,输出为false
 
7. String str7 = "abc".substring(0, 2);   
步骤: 
1) 栈中开辟一块空间存放引用str7, 
2) substring()方法还原一个新的String对象"ab"(不同于str6所指),堆中开辟一块空间存放此对象,
3) 引用str7指向堆中的新String对象,
 
8. String str8 = "abc".toUpperCase(); 
步骤: 
1) 栈中开辟一块空间存放引用str6, 
2) toUpperCase()方法还原一个新的String对象"ABC",池中并未开辟新的空间存放String常量"ABC", 
3) 引用str8指向堆中的新String对象
 
9.String s="abc"; 
   String s1=s; 
   System.out.println(s1=="abc"); 
   s=s+"hello"; 
   System.out.println(s1=="abc"); 
   System.out.println(s=="abc");
步骤:
1)栈中开辟一块空间存放s;
2)Sting池中开辟一块空间用于存放"abc",栈中开辟一块空间存放变量s1;
3)系统输出true,在堆中开辟一块空间用于存放"abchello";
4)引用s指向堆中的"abchello";
5)系统输出true,然后输出false;
典型实例:

6.堆与栈的区别
   Java把内存划分成两种:一种是栈内存,一种是堆内存。  栈与堆都是Java用来在Ram中存放数据的地方。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。
(1) Java的堆:
◆分配:是一个运行时数据区,堆内存用来存放由new创建的对象和数组。
◆释放:堆内存不需要程序代码来显式的释放,堆是由垃圾回收来负责的。
◆优点:是可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,Java的垃圾收集器会自动收走这些不再使用的数据。
◆缺点:由于要在运行时动态分配内存,存取速度较慢。 
(2)Java的栈
◆分配:是一个运行时数据区,在函数中定义的一些基本类型(8种)的变量和对象的引用变量都在函数的栈内存中分配。当在一段代码块定义一个变量时,Java就在栈中为这个变量分配内存空间。
◆释放:当超过变量的作用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。
◆优点:无需动态分配,存取速度比堆要快,仅次于寄存器,栈数据可以共享。
◆缺点:存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。

参考:http://blog.csdn.net/ycwload/article/details/2650059
    http://www.nowcoder.com/questionTerminal/4148c53d7e284f19b0f61bc0ada248a8
7.Handler实现原理
    Android的消息传递机制是另一种形式的"事件处理",由于Android应用规定只允许UI线程修改Activity里的UI组件,而这种机制主要是为了解决Android应用的多线程并发操作UI导致安全问题。Andriod提供了Handler和Looper来满足线程间的通信.Handler先进先出原则.Looper类用来管理特定线程内对象之间的消息交换(Message Exchange). 
一、Handler工作原理
1.Handler消UI线程中息传递机制类介绍
(1)Handler:使用Handler对象来与Looper沟通,即它把消息发送给给Looper管理的MessageQueue,并负责处理Looper分给它的消息。
(2)Message:Handler接收和处理的消息对象;
(3)Looper:一个线程可以产生一个Looper对象,由它来管理此线程里的MessageQueue(消息队列)。Looper会调用prepare()和loop()方法,在当前执行的线程中保存一个Looper实例,这个实例会保存一个MessageQueue对象,然后当前线程进入一个无限循环中去,不断从MessageQueue中读取Handler发来的消息。
  
  private Looper(){        mQueue = new MessageQueue();        mRun = true;        mThread = Thread.currentThread();    }    //prepare的作用是将Looper中的空线程指向当前线程    //空消息队列指向当前线程的消息队列    public static final void prepare(){        if(sThreadLocal.get()!=null){            throw new RuntimeException("");        }        sThreadLocal.set(new Looper());    }
    可知,其他类无法通过Looper的构造器创建Looper对象,只能通过Looper的静态方法prepare()来创建子线程的Looper对象。
(4)MessageQueue:消息队列。用来存放线程放入的消息,它采用先进先出的方式来管理Message(消息)。
2.Android Handler机制工作原理
(1)Handler创建消息
    每一个消息都需要被指定的Handler处理,通过Handler创建消息便可以完成此功能。Android消息机制中引入了消息池。Handler创建消息时首先查询消息池中是否有消息存在,如果有直接从消息池中取得,如果没有则重新初始化一个消息实例。使用消息池的好处是:消息不被使用时,并不作为垃圾回收,而是放入消息池,可供下次Handler创建消息时使用。消息池提高了消息对象的复用,减少系统垃圾回收的次数。消息的创建流程如图所示。

(2)Handler发送消息 
    UI主线程初始化第一个Handler时会通过ThreadLocal创建一个Looper,该Looper与UI主线程一一对应。使用ThreadLocal的目的是保证每一个线程只创建唯一一个Looper。之后其他Handler初始化的时候直接获取第一个Handler创建的Looper。Looper初始化的时候会创建一个消息队列MessageQueue。至此,主线程、消息循环、消息队列之间的关系是1:1:1。
Handler、Looper、MessageQueue的初始化流程如图所示:

Hander持有对UI主线程消息队列MessageQueue和消息循环Looper的引用,子线程可以通过Handler将消息发送到UI线程的消息队列MessageQueue中。
(3)Handler处理消息
    UI主线程通过Looper循环查询消息队列UI_MQ,当发现有消息存在时会将消息从消息队列中取出。首先分析消息,通过消息的参数判断该消息对应的Handler,然后将消息分发到指定的Handler进行处理。 子线程通过Handler、Looper与UI主线程通信的流程如图所示。 

二、源码解析
1、Looper
    对于Looper主要是prepare()---创建唯一的Looper对象,并在构造函数中创建与Looper对象相对应的消息队列两个方法;Loop()方法循环的查找消息队列中是否有消息对象。
首先看prepare()方法
public static final void prepare() {          if (sThreadLocal.get() != null) {    //判断线程(如UI线程是否已经创建了Looper对象)            throw new RuntimeException("Only one Looper may be created per thread");          }     sThreadLocal.set(new Looper(true));  //创建一个Looper对象,并将其保存到ThreadLocal变量中}  
    sThreadLocal是一个ThreadLocal对象,可以在一个线程中存储变量。可以看到,在第5行,将一个Looper的实例放入了ThreadLocal,并且2-4行判断了sThreadLocal是否为null,否则抛出异常。这也就说明了Looper.prepare()方法不能被调用两次,同时也保证了一个线程中只有一个Looper实例~相信有些哥们一定遇到这个错误。
下面看Looper的构造方法:
private Looper(boolean quitAllowed) {          mQueue = new MessageQueue(quitAllowed);          mRun = true;          mThread = Thread.currentThread();  }  
在构造方法中,创建了一个MessageQueue(消息队列)。
然后我们看loop()方法:
public static void loop() {      final Looper me = myLooper();    //获得Looper对象        if (me == null) {              throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");          }          final MessageQueue queue = me.mQueue; //获得消息队列           // Make sure the identity of this thread is that of the local process,          // and keep track of what that identity token actually is.          Binder.clearCallingIdentity();          final long ident = Binder.clearCallingIdentity();            for (;;) {              Message msg = queue.next(); // 获得消息(先进先出)            if (msg == null) {     //消息为空循环等待                // No message indicates that the message queue is quitting.                  return;              }                // This must be in a local variable, in case a UI event sets the logger              Printer logging = me.mLogging;              if (logging != null) {                  logging.println(">>>>> Dispatching to " + msg.target + " " +                          msg.callback + ": " + msg.what);              }                              msg.target.dispatchMessage(msg);//将消息发送到相应的Handler对象              if (logging != null) {                  logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);              }                // Make sure that during the course of dispatching the              // identity of the thread wasn't corrupted.              final long newIdent = Binder.clearCallingIdentity();              if (ident != newIdent) {                  Log.wtf(TAG, "Thread identity changed from 0x"                          + Long.toHexString(ident) + " to 0x"                          + Long.toHexString(newIdent) + " while dispatching to "                          + msg.target.getClass().getName() + " "                          + msg.callback + " what=" + msg.what);              }                msg.recycle();          }  }  
第2行:
public static Looper myLooper() {return sThreadLocal.get();}
方法直接返回了sThreadLocal存储的Looper实例,如果me为null则抛出异常,也就是说looper方法必须在prepare方法之后运行。
第6行:拿到该looper实例中的mQueue(消息队列)
13到45行:就进入了我们所说的无限循环。
14行:取出一条消息,如果没有消息则阻塞。
27行:使用调用 msg.target.dispatchMessage(msg);把消息交给msg的target的dispatchMessage方法去处理。Msg的target是什么呢?其实就是handler对象,下面会进行分析。
44行:释放消息占据的资源。
(1)Looper()构造方法主要作用:
与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue。
(2)loop()方法,不断从MessageQueue中去取消息,交给消息的target(Handler对象)的dispatchMessage去处理。好了,我们的异步消息处理线程已经有了消息队列(MessageQueue),也有了在无限循环体中取出消息的哥们,现在缺的就是发送消息的对象了,于是乎:Handler登场了。
2、Handler
    使用Handler之前,我们都是初始化一个实例,比如用于更新UI线程,我们会在声明的时候直接初始化,或者在onCreate中初始化Handler实例。所以我们首先看Handler的构造方法,看其
(1)如何与MessageQueue联系上的,它在子线程中发送的消息(一般发送消息都在非UI线程)
(2)怎么发送到MessageQueue中的。
*****************将Handler对象与Looper、MessageQueue关联public Handler() {          this(null, false);  }  /*构造方法*/public Handler(Callback callback, boolean async) {          if (FIND_POTENTIAL_LEAKS) {              final Class<? extends Handler> klass = getClass();              if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&                      (klass.getModifiers() & Modifier.STATIC) == 0) {                  Log.w(TAG, "The following Handler class should be static or leaks might occur: " +                      klass.getCanonicalName());              }          }            mLooper = Looper.myLooper();  //01.获得当前Looper实例        if (mLooper == null) {              throw new RuntimeException(                  "Can't create handler inside thread that has not called Looper.prepare()");          }          mQueue = mLooper.mQueue;  //02.获得Looper相关联的消息队列MessageQueue        mCallback = callback;          mAsynchronous = async;      }  
14行:通过Looper.myLooper()获取了当前线程保存的Looper实例,然后在19行又获取了这个Looper实例中保存的MessageQueue(消息队列),这样就保证了handler的实例与我们Looper实例中MessageQueue关联上了。
*****************发送消息分析然后看我们最常用的sendMessage方法public final boolean sendMessage(Message msg)   {       return sendMessageDelayed(msg, 0);   }  public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {       Message msg = Message.obtain();   //创建消息对象     msg.what = what;   //将消息保存到消息对象的what变量中     return sendMessageDelayed(msg, delayMillis);   }  public final boolean sendMessageDelayed(Message msg, long delayMillis)     {         if (delayMillis < 0) {             delayMillis = 0;         }         return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);     }  public boolean sendMessageAtTime(Message msg, long uptimeMillis) {         MessageQueue queue = mQueue;   //将Looper对应的消息队列绑定到本地消息队列       if (queue == null) {             RuntimeException e = new RuntimeException(                     this + " sendMessageAtTime() called with no mQueue");             Log.w("Looper", e.getMessage(), e);             return false;         }         return enqueueMessage(queue, msg, uptimeMillis);    //将消息存放到消息队列中去   }  
辗转反则最后调用了sendMessageAtTime,在此方法内部有直接获取MessageQueue然后调用了enqueueMessage方法,我们再来看看此方法:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {         msg.target = this;   //handler对象,即msg.target就是handler对象       if (mAsynchronous) {             msg.setAsynchronous(true);         }         //uptimeMillis后将消息存放到消息队列中       return queue.enqueueMessage(msg, uptimeMillis);     }  
enqueueMessage中首先为meg.target赋值为this,【如果大家还记得Looper的loop方法会取出每个msg然后交给msg,target.dispatchMessage(msg)去处理消息】,也就是把当前的handler作为msg的target属性。最终会调用queue的enqueueMessage的方法,也就是说handler发出的消息,最终会保存到消息队列中去。
*****************处理消息分析
现在已经很清楚了Looper会调用prepare()和loop()方法,在当前执行的线程中保存一个Looper实例,这个实例会保存一个MessageQueue对象,然后当前线程进入一个无限循环中去,不断从MessageQueue中读取Handler发来的消息。然后再回调创建这个消息的handler中的dispathMessage方法,下面我们赶快去看一看这个方法:即
msg,target.dispatchMessage(msg)public void dispatchMessage(Message msg) {          if (msg.callback != null) {              handleCallback(msg);          } else {              if (mCallback != null) {                  if (mCallback.handleMessage(msg)) {                      return;                  }              }              handleMessage(msg);  //是个空方法,有用户实现        }      }  
可以看到,第10行,调用了handleMessage方法,下面我们去看这个方法:
/**    * Subclasses must implement this to receive messages.    */    public void handleMessage(Message msg) {    }      
    可以看到这是一个空方法,为什么呢,因为消息的最终回调是由我们控制的,我们在创建handler的时候都是复写handleMessage方法,然后根据msg.what进行消息处理。
例如:
private Handler mHandler = new Handler()      {          public void handleMessage(android.os.Message msg)          {              switch (msg.what)              {              case value:                                    break;                default:                  break;              }          };      };  
到此,这个流程已经解释完毕,让我们首先总结一下
(1)首先Looper.prepare()在本线程中保存一个Looper实例,然后该实例中保存一个MessageQueue对象;因为Looper.prepare()在一个线程中只能调用一次,所以MessageQueue在一个线程中只会存在一个。
(2)Looper.loop()会让当前线程进入一个无限循环,不断从MessageQueue的实例中读取消息,然后回调msg.target.dispatchMessage(msg)方法,该方法包含调用handleMessage()。
(3)Handler的构造方法,会首先得到当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue想关联。
(4)Handler的sendMessage方法,会给msg的target赋值为handler自身,然后加入MessageQueue中。
(5)在构造Handler实例时,我们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)最终调用的方法。
    在Activity中,我们并没有显示的调用Looper.prepare()和Looper.loop()方法,为啥Handler可以成功创建呢,这是因为在Activity的启动代码中,已经在当前UI线程默认调用了Looper.prepare()和Looper.loop()方法,为UI线程创建与之相关的Looper对象及配套的消息队列MessageQueue.
3、Handler post
今天有人问我,你说Handler的post方法创建的线程和UI线程有什么关系?
其实这个问题也是出现这篇博客的原因之一;这里需要说明,有时候为了方便,我们会直接写如下代码:
mHandler.post(new Runnable()          {              @Override              public void run()              {                  Log.e("TAG", Thread.currentThread().getName());                  mTxt.setText("yoxi");              }          });  
然后run方法中可以写更新UI的代码,其实这个Runnable并没有创建什么线程,而是发送了一条消息,下面看源码:
public final boolean post(Runnable r)     {        return  sendMessageDelayed(getPostMessage(r), 0);     }  private static Message getPostMessage(Runnable r) {        Message m = Message.obtain();        m.callback = r;  //将Runnable对象保存到Message对象中,由Handler处理消息      return m;    }  
可以看到,在getPostMessage中,得到了一个Message对象,然后将我们创建的Runable对象作为callback属性,赋值给了此message.
注:产生一个Message对象,可以new  ,也可以使用Message.obtain()方法;两者都可以,但是更建议使用obtain方法,因为Message内部维护了一个Message池用于Message的复用,避免使用new 重新分配内存。
public final boolean sendMessageDelayed(Message msg, long delayMillis)     {         if (delayMillis < 0) {             delayMillis = 0;         }         return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);     }  public boolean sendMessageAtTime(Message msg, long uptimeMillis) {         MessageQueue queue = mQueue;         if (queue == null) {             RuntimeException e = new RuntimeException(                     this + " sendMessageAtTime() called with no mQueue");             Log.w("Looper", e.getMessage(), e);             return false;         }         return enqueueMessage(queue, msg, uptimeMillis);     }  
最终和handler.sendMessage一样,调用了sendMessageAtTime,然后调用了enqueueMessage方法,给msg.target赋值为handler,最终加入MessagQueue.
可以看到,这里msg的callback和target都有值,那么会执行哪个呢?
其实上面已经贴过代码,就是dispatchMessage方法:
public void dispatchMessage(Message msg) {         if (msg.callback != null) {             handleCallback(msg);         } else {             if (mCallback != null) {                 if (mCallback.handleMessage(msg)) {                     return;                 }             }             handleMessage(msg);         }     }  
第2行,如果不为null,则执行callback回调,也就是我们的Runnable对象。
参考:http://blog.csdn.net/itachi85/article/details/8035333
 Looper、Handler、Message三者关系:http://blog.csdn.net/lmj623565791/article/details/38377229
8.AsyncTask工作原理
1.AsyncTask实现原理

    AsyncTask(异步任务),是android提供的轻量级封装过的后台任务类,AsyncTask异步任务允许开发者定义一个运行在单独线程中的任务,还能在任务的不同阶段提供回调函数。这些回调函数被设计成无需使用runOnUiThread方法即可更新UI。AsyncTask的本质是一个线程池,所有提交的异步任务都会在这个静态线程池中的工作线程内的doInBackground(mParams)方法执行,当工作线程需要跟UI线程交互时,工作线程会通过向在UI线程创建的Handler(AsyncTask内部的InternalHandler)传递消息的方式,调用相关的回调函数,从而实现UI界面的更新。
    -----源码
2.AsyncTask优缺点(先介绍实现原理)
(1)优点:AsyncTask实现容易,通过AsyncTask类开发者可以很容易在其他线程中执行耗时任务,也可以在需要时很方便地和主线程(UI线程)通信。
(2)缺点:
◆该类的实例只能使用一次,每次执行操作都要新建一个MyAsyncTask对象。因此,它不适合哪些频繁的操作,因为这么快速聚集需要垃圾回收的对象,并最终导致应用程序卡顿。◆AsyncTask不能对操作设置执行时间,也无法间隔一段时间执行操作,它适合文件下载。
◆最大并发线程数目不超过5个,因为在使用多个异步操作并需要进行UI通信时,会变得很复杂。
3.注意事项
    使用AsyncTask类,以下是几条必须遵守的准则:
     (1)Task的实例必须在UI thread中创建;
     (2)execute方法必须在UI thread中调用;
     (3)不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)这几个方法;
     (4)该task只能被执行一次,否则多次调用时将会出现异常
二、源码分析
1.实例演示

AsyncTask.java
public class AsyncTaskActivity extends Activity {      private ImageView mImageView;      private Button mButton;      private ProgressBar mProgressBar;            @Override      public void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);          setContentView(R.layout.main);                    mImageView= (ImageView) findViewById(R.id.imageView);          mButton = (Button) findViewById(R.id.button);          mProgressBar = (ProgressBar) findViewById(R.id.progressBar);          mButton.setOnClickListener(new OnClickListener() {                       @Override              public void onClick(View v) {                  GetCSDNLogoTask task = new GetCSDNLogoTask();                  task.execute("http://csdnimg.cn/www/images/csdnindex_logo.gif");              }          });      }            class GetCSDNLogoTask extends AsyncTask<String,Integer,Bitmap> {//继承AsyncTask          /处理后台执行的任务,在后台线程执行          @Override          protected Bitmap doInBackground(String... params) {/            publishProgress(0);//将会调用onProgressUpdate(Integer... progress)方法              HttpClient hc = new DefaultHttpClient();              publishProgress(30);              HttpGet hg = new HttpGet(params[0]);//获取csdn的logo              final Bitmap bm;              try {                  HttpResponse hr = hc.execute(hg);                  bm = BitmapFactory.decodeStream(hr.getEntity().getContent());              } catch (Exception e) {                                    return null;              }              publishProgress(100);              //mImageView.setImageBitmap(result); 不能在后台线程操作ui              return bm;          }            //在调用publishProgress之后被调用,在ui线程执行          protected void onProgressUpdate(Integer... progress) {            mProgressBar.setProgress(progress[0]);//更新进度条的进度           }          //后台任务执行完之后被调用,在ui线程执行           protected void onPostExecute(Bitmap result) {             if(result != null) {                   Toast.makeText(AsyncTaskActivity.this, "成功获取图片", Toast.LENGTH_LONG).show();                   mImageView.setImageBitmap(result);               }else {                   Toast.makeText(AsyncTaskActivity.this, "获取图片失败", Toast.LENGTH_LONG).show();               }           }                      protected void onPreExecute () {//在 doInBackground(Params...)之前被调用,在ui线程执行               mImageView.setImageBitmap(null);               mProgressBar.setProgress(0);//进度条复位           }                      protected void onCancelled () {//在ui线程执行               mProgressBar.setProgress(0);//进度条复位           }                }          }  
(1) AsyncTask<Params,Progress,Result>定义了三种泛型类型
    ◆Params-启动任务执行的输入参数类型,比如HTTP请求的URL;
    ◆Progress-后台任务执行的百分比,一般为Integer类型
    ◆Result-后台执行任务最终返回的结果类型
(2)AsyncTask实现异步义任务
    ◆onPreExecute():在执行后台任务之前被UI线程调用,完成一些初始化工作
    ◆doInBackground(Params…):执行后台耗时任务。在子线程中调用,不能直接操作UI.
    ◆onProgressUpdate(Integer... progress):在调用publicProgress(Progress…)后被调用,用
        于更新任务的进度,在UI线程中被调用。
    ◆onPostExecute(Result):将doInBackgraound得到的结果操作UI。在UI线程中被调用,相
        当于Handler方式处理UI.
2.源码分析
在分析实现流程之前,我们先了解一下AsyncTask有哪些成员变量。
private static final int CORE_POOL_SIZE =5;//5个核心工作线程  
private static final int MAXIMUM_POOL_SIZE = 128;//最多128个工作线程  
private static final int KEEP_ALIVE = 1;//空闲线程的超时时间为1秒  
  
private static final BlockingQueue<Runnable> sWorkQueue =  
        new LinkedBlockingQueue<Runnable>(10);//等待队列  
  
private static final ThreadPoolExecutorsExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,  
        MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue,sThreadFactory);//线程池是静态变量,所有的异步任务都会放到这个线程池的工作线程内执行。  


回到例子中,点击按钮之后会新建一个GetCSDNLogoTask对象:
GetCSDNLogoTask task = new GetCSDNLogoTask();  
此时会调用父类AsyncTask的构造函数:
AsyncTask.java
public AsyncTask() {          mWorker = new WorkerRunnable<Params, Result>() {              public Result call() throws Exception {                  Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);                  return doInBackground(mParams);              }          };          mFuture = new FutureTask<Result>(mWorker) {              @Override              protected void done() {                  Message message;                  Result result = null;                    try {                      result = get();  //获得异步任务执行的结果                } catch (InterruptedException e) {                      android.util.Log.w(LOG_TAG, e);                  } catch (ExecutionException e) {                      throw new RuntimeException("An error occured while executing doInBackground()",                              e.getCause());                  } catch (CancellationException e) {   //通过Handler对象向UI线程发送结果对应的消息                    message = sHandler.obtainMessage(MESSAGE_POST_CANCEL,                              new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null));                      message.sendToTarget();//取消任务,发送MESSAGE_POST_CANCEL消息                      return;                  } catch (Throwable t) {                      throw new RuntimeException("An error occured while executing "                              + "doInBackground()", t);                  }                    message = sHandler.obtainMessage(MESSAGE_POST_RESULT,                          new AsyncTaskResult<Result>(AsyncTask.this, result));//完成任务,发送MESSAGE_POST_RESULT消息并传递result对象                  message.sendToTarget();              }          };      }  
    WorkerRunnable类实现了callable接口的call()方法,该函数会调用我们在AsyncTask子类中实现的doInBackground(mParams)方法,由此可见,WorkerRunnable封装了我们要执行的异步任务。FutureTask中的protected void done() {}方法实现了异步任务状态改变后的操作。当异步任务被取消,会向UI线程传递MESSAGE_POST_CANCEL消息,当任务成功执行,会向UI线程传递MESSAGE_POST_RESULT消息,并把执行结果传递到UI线程。
    由此可知,AsyncTask在构造的时候已经定义好要异步执行的方法doInBackground(mParams)和任务状态变化后的操作(包括失败和成功)。
当创建完GetCSDNLogoTask对象后,执行
task.execute("http://csdnimg.cn/www/images/csdnindex_logo.gif");  
此时会调用AsyncTask的execute(Params...params)方法
AsyncTask.java
public final AsyncTask<Params,Progress, Result> execute(Params... params) {          if (mStatus != Status.PENDING) {              switch (mStatus) {                /*由如下代码可知,execute只能被执行一次               *     否则回报"Cannot execute task:the taskis already running"错误                 */                case RUNNING:                      throw newIllegalStateException("Cannot execute task:"                              + " the taskis already running.");                  case FINISHED:                      throw newIllegalStateException("Cannot execute task:"                              + " the taskhas already been executed "                              + "(a task canbe executed only once)");              }          }          mStatus = Status.RUNNING;           onPreExecute();//运行在ui线程,在提交任务到线程池之前执行          mWorker.mParams = params;          sExecutor.execute(mFuture);//提交任务到线程池          return this;  }  
    当任务正在执行或者已经完成,会抛出IllegalStateException,由此可知我们不能够重复调用execute(Params...params)方法。在提交任务到线程池之前,调用了onPreExecute()方法。然后才执行sExecutor.execute(mFuture)是任务提交到线程池。
前面我们说到,当任务的状态发生改变时(1、执行成功2、取消执行3、进度更新),工作线程会向UI线程的Handler传递消息。在《Android异步处理三:Handler+Looper+MessageQueue深入详解》一文中我们提到,Handler要处理其他线程传递过来的消息。在AsyncTask中,InternalHandler是在UI线程上创建的,它接收来自工作线程的消息,当接收到消息之后,AsyncTask会调用自身相应的回调方法。实现代码如下:
AsyncTask.java
private static class InternalHandler extends Handler {         @SuppressWarnings({"unchecked","RawUseOfParameterizedType"})          @Override          public voidhandleMessage(Message msg) {              AsyncTaskResult result =(AsyncTaskResult) msg.obj;              switch (msg.what) {                  case MESSAGE_POST_RESULT:                      // There is onlyone result                      result.mTask.finish(result.mData[0]);//执行任务成功                      break;                  caseMESSAGE_POST_PROGRESS:                     result.mTask.onProgressUpdate(result.mData);//进度更新                      break;                  caseMESSAGE_POST_CANCEL:                      result.mTask.onCancelled();//取消任务                      break;              }          }      }     
参考:
AsyncTask的实现原理:http://blog.csdn.net/mylzc/article/details/6774131#comments