死磕Handler

handler做为Android开发最中重要的模型之一,须要理解其工做原理包括Handler,Message,MessageQueue,Looper之间的关系,以下是一个网上摘抄的图片,MessageQueue是一个消息队列,Looper是一个循环体,Message是消息,而Handler是消息的具柄; java

下面一步一步用代码来看:linux

Message

Message中有4个变量参数,what ,arg1,arg2,obj;常常使用其中的参数做为判断handleMessage的判断条件,其中通常有以下3种方式建立Message: (1)经过Message的构造器模式建立:缓存

Message msg = new Message();
msg.arg1 = 1;
msg.arg2 = 2;
msg.obj = 3;
msg.what = 4;
handle.sendMessage(msg);
复制代码

(2)经过Message的obtain函数去操做:bash

Message msg = Message.obtain();
msg.what=1;
msg.arg1=2;
msg.arg2=3;
msg.obj=4;
handler.sendMessage(msg);
复制代码

(3)经过Obtain(handler)方式去建立 :多线程

Message msg = Message.obtain(handler);
msg.what=1; msg.arg1=2;
msg.arg2=3; msg.obj=4;
msg.sendToTarget();
复制代码

推荐使用后面2种的obtain方式去建立,由于这种效率会高一些,减小了重复建立Message对象; 看源码:Message#obtain()并发

public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }
复制代码

发现其实经过obtain是优先从缓存的Message池中去取,当取不到时候才会建立一个新的Message,所以效率更高,推荐使用;less

Handler

常见的建立Handler方式以下:异步

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };
}
复制代码

此中方式建立Handler时候会形成内存泄露,缘由是由于非静态的匿名内部类和非静态内部类是能够持有一个外部类的引用的;由于在Java.class编译时候会将非静态的内部类和外部类编译成2个文件存储,非静态的内部类能够访问外部类的成员变量和成员方法这个是为何呢?就是由于非静态的内部类是持有外部类的一个隐式引用,而静态内部类和外部类之间没有,两个几乎至关于两个独立的Class文件;编译后的代码应该差很少以下:async

class Outer$Inner{
	final Outer this$0;
	
	public Outer$Inner(Outer outer){
		this.this$0 = outer;
		super();
	}
}
复制代码

至于这个引用为何会形成内存泄露呢?先说下根本缘由吧,是由于handler持用Activity的引用,而Handler中若是消息没有处理完,Activity在销毁时候是没有办法被GC回收的,GC回收是根据对象可达性,这个Handler是绑定在主线程,主线程是应用入口,当Handler处理不完此消息必然形成Activity没法回收,最终内存泄露;以下这种会形成5分钟没法回收Activity:ide

public class HandlerActivity extends Activity{
 
	//可能引入泄露的方法
	private final Handler mLeakyHandler = new Handler(){
	
		@Override
		public void handleMessage(Message msg){
		
			//.....
		
		}
	};
	
	@Override
	protected void onCreate(Bundle savedInstanceState){
		super.onCreate(savedInstanceState);
		
		//延迟5分钟发送消息
		mLeakyHandler.postDelayed(new Runnable(){
		
			@Override
			public void run(){
				//......
			}
			
		}, 1000*60*5);
		
	
	}
}
复制代码

知道缘由解决方法也跟着出来了: 1.使用静态的内部类替代非静态内部类; 2.在onDestory方法时移除Handler中的消息也能够解决;

private static class InnerHandler extends Handler{
		
		private final WeakReference<HandlerActivity> mActivity;
		
		public InnerHandler(HandlerActivity activity){
			mActivity = new WeakReference<HandlerActivity>(activity);
		}
		
		@Override
		public void handleMessage(Message msg){
			HandlerActivity activity = mActivity.get();
			if(activity != null){
				//.....
			}
		}
	}
复制代码
//清空当前Handler队列全部消息
 @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);
    }
复制代码

来看下handler的构造方法:

public Handler() {
        this(null, false);
    }
    
public Handler(Looper looper) {
        this(looper, null, false);
    }
    
 public Handler(Callback callback) {
        this(callback, false);
    }
    
public Handler(boolean async) {
        this(null, async);
    }
复制代码

代码最多见的是使用1,2两种方式去建立handler,若是handler在主线程建立,好比不是在new Thread的线程内建立的话,那么handler的Looper绑定的是主线程的Looper,在子线程中使用时候须要本身Looper.loop<->Looper.prepare配套; public Handler(Callback callback)能够用户拦截回调: 举个例子:

public class MainActivity extends Activity {
	private TextView textView;
	private Handler handler = new Handler(new Handler.Callback() {
		
		@Override
		public boolean handleMessage(Message msg) {
			Toast.makeText(getApplicationContext(), "callback handlemessage", 1000).show();    //代码1
			return true;   //这里返回值须要注意 // 代码3
		}
	}){
		public void handleMessage(Message msg) {
			Toast.makeText(getApplicationContext(), "handler handlemessage", 1000).show();  //代码2
		};
	};
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		textView =(TextView) findViewById(R.id.textView);
	}
	
	public void show(View view){
		handler.sendEmptyMessage(1);
	}
	
}
复制代码

上面的代码中使用了public Handler(Callback callback)的构造函数。咱们这里能够进行消息传递的拦截。当咱们的“代码3”中return false的时候 “代码1” “代码2”会依次执行,当“代码3”中return true的时候“代码1”会先执行 ,可是“代码2”不会执行,此时有点相似事件传递中返回true事件消费,false继续向上传递的意思。咱们可使用Handler的这个构造方法,来进行消息传递的拦截。 具体实现源码Handler#dispatchMessage以下:

/** * Handle system messages here. */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                //若是这里返回true,那么handleMessage是不会执行了;
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
复制代码

还有一个public Handler(boolean async)设置异步的,对于API没开放,设置后会影响消息是否为异步消息,不能保证插入的消息有序性; 源码以下:

public Handler(Callback callback, boolean async) {
        ...
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
    
  private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
    
    
     /** * Returns true if the message is asynchronous, meaning that it is not * subject to {@link Looper} synchronization barriers. * * @return True if the message is asynchronous. * * @see #setAsynchronous(boolean) */
    public boolean isAsynchronous() {
        return (flags & FLAG_ASYNCHRONOUS) != 0;
    }
复制代码

下面说常见的几种handler发消息的方法:

public final boolean post(Runnable r) {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
    
public final boolean postAtTime(Runnable r, long uptimeMillis) {
        return sendMessageAtTime(getPostMessage(r), uptimeMillis);
    }  
    
public final boolean postDelayed(Runnable r, long delayMillis) {
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }
 
 public final boolean postAtFrontOfQueue(Runnable r) {
        return sendMessageAtFrontOfQueue(getPostMessage(r));
    }
    
  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);
    }
    
 public final boolean sendMessageAtFrontOfQueue(Message msg) {
        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, 0);
    }
    
   private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }   
复制代码

能够看出来 postxxx-->sendxxx-->enqueueMessage将消息放入到消息队列里面

MessageQueue

消息队列,消息队列的实现就是一个队列从里面取消息,都知道Loop会让循环不断的去取消息队列里面消息,实现方式也就是一个死循环,可是光死循环会形成cpu负荷,所以须要休眠控制下减小cpu,那消息队列如何控制休眠的呢;其实就是经过linux的epoll机制完成的,C++类Looper中的睡眠和唤醒机制是经过pollOnce和wake函数提供的,它们又是利用操做系统(Linux内核)的epoll机制来完成的,这里深刻很少展开,只从一个地方看mBlocked:

// Indicates whether next() is blocked waiting in pollOnce()     //with a non-zero timeout.
    private boolean mBlocked;
复制代码

mBlocked这个标识位控制着是否休眠唤醒;

先看唤醒时机enqueueMessage:

boolean enqueueMessage(Message msg, long when) {
        ...
            //这里处理当消息经过needWake控制是否进入休眠
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue. Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                //消息时间未到,开死循环等待
                for (;;) {
                    prev = p;
                    p = p.next;
                    //时间到了退出
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            //唤醒操做
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }
复制代码

休眠时机 Message next():

Message next() {
            ...
            //使用pollOnce取消息时候会休眠
            nativePollOnce(ptr, nextPollTimeoutMillis);
            ...
            if (msg != null) {
                    if (now < msg.when) {
                        // Next message is not ready. Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                }
                  if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run. Loop and wait some more.
                    mBlocked = true;
                    continue;
                }
        }

复制代码

Looper

一个循环处理消息队列的东东; 使用方式也简单,以下是将当前Handler和Looper关联,若是在主线程,关联是主线程,若是是子线程管理就子线程;

//子线程
        Looper.prepare();
        Handler handler = new Handler();
        Looper.loop();
        
        //子线程
        Looper looper = .....;
        //主线程
        Handler handler = new Handler(looper);
复制代码

子线程建立Handler通常使用HandlerThread方式:

public class HandlerThreadActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler);
        HandlerThread handlerThread = new HandlerThread("HandlerThread");
        handlerThread.start();

        Handler mHandler = new Handler(handlerThread.getLooper()){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                Log.d("HandlerThreadActivity.class","uiThread2------"+Thread.currentThread());//子线程
            }
        };

        Log.d("HandlerThreadActivity.class","uiThread1------"+Thread.currentThread());//主线程
        mHandler.sendEmptyMessage(1);
    }
}
复制代码

看看源码:

public static void prepare() {
        prepare(true);
    }

private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
复制代码

Looper对象实例化就是在Looper.prepare完成的,而且将当前线程绑定到mThread上,如今回过头看Handler建立时候就明白为何没有单独开线程建立时候,绑定是主线程上;来看Handler建立方法:

public Handler(Callback callback, boolean async) {
        ...
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
    
public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
    
      static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
复制代码

到这里仍是没有直观的找到绑定的线程;可是咱们忽略了一个ActivityThread,ActivityThread是Android程序的入口,一直运行,看下其main函数:

public static void main(String[] args) {
        ...
        Looper.prepareMainLooper();
        ...
        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();
}


public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
            // MIUI ADD
            sMainLooper.enableAnrMonitor(true);
        }
    }
复制代码

Handler,Looper在程序启动的时候就已经开始工做了,也就是说咱们本身的ThreadLocal.get()获取的是ActivityThread线程即主线程; 在看Looper.loop:

public static void loop() {
        ...
        boolean slowDeliveryDetected = false;
        //死循环,不断的取消息队列中消息
        for (;;) {
            //消息队列处理消息时候由于nativePollOnce阻塞休眠
            Message msg = queue.next(); // might block
            //无消息时候退出
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            
             try {
                //处理消息
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            ...
        }
    }
    
    
    public void dispatchMessage(Message msg) {
        //若是是post方式传入一个runnable执行便可
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            //若是是public Handler(Callback callback)执行此处
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                //事件分发,是否本身消费掉
                    return;
                }
            }
            handleMessage(msg);
        }
    }
    
    private static void handleCallback(Message message) {
        message.callback.run();
    }
    
复制代码

这里更加明白了在ondestroy时候removeCallbacksAndMessages的含义了, 就是移除添加的postxxx,sendMessagexxx等消息; 至此整个Looper,Message,Handler,MessageQueue到此应该梳理比较清楚了;

还有一个类:ThreadLocal,在源码的Looper绑定线程时候看到,

private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }
    
public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
    
        static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();  
 
复制代码

ThreadLocal是一个本地线程存放副本变量的工具,不一样线程其变量值互不干扰,适用于多线程高并发场景完成多线程调用互不干扰的变量的值,能够相似理解为多进程操做同一个静态变量,其值也是互不干扰;

看看ThreadLocal代码:

public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
    
 public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
复制代码

实现原理就是set时候先从ThreadLocalMap去找,取不到再存放,get也是相似原理,看源码就明白了;

相关文章
相关标签/搜索