线程的应用场景


Android是单线程模型,Activity、Service、Broadcast等组件的创建,都是在主线程完成的,即UI线程。但如果需要执行一些耗时的操作时,比如:I/O的读写、大文件的读写、数据库操作以及网络上传和下载等操作都需要很长的时间,如果主线程中有较多的耗时任务,就会降低界面的响应速度,甚至失去响应,如果失去响应超过 5秒,系统就会提示强行关闭程序。解决办法就是,使用子线程Thread。



线程与进程的区别


线程是进程内的一个执行单元,也是进程内的可调度实体。


两者的区别:


1.地址空间:每个进程都至少存在一个线程,它们共享进程的地址空间,而进程有自己独立的地址空间。


2.线程是CPU处理器调度的基本单位,但进程不是。


3.进程是资源分配,并且拥有单位的,同一个进程内的线程可以共享进程里的资源。



线程间通信


Handler机制,是Runnable 和 Activity交互的桥梁。在run中发送Message,在Handler里通过不同的Message,执行不同的任务。 



涉及的类:


Handler:负责消息的发送和处理,通过它可以实现子线程 和 主线程之间的通信。


Looper:  负责管理已接收的线程消息和消息队列。


Message: 消息载体。


MessageQueue:消息队列,遵循先进先出的原则,它用来保存待处理的线程消息。



线程安全问题


当使用多个子线程来同时访问一个数据时,会出现线程安全的问题,比如:多个线程同时操作同一个数据,会导致数据不一致的问题。所以我们一般通过同步机制来解决这样的问题:synchronized(){ }同步代码块 和synchronized 同步方法。



子线程中更新UI的几种方式


handle.handleMessage(Message msg);


handle.post(Runnable r);


runOnUiThread(Runnable r);


View.post(Runnable r);


子线程更新UI的详细解释看这里: 面试官:子线程 真的不能更新UI ?



Handler机制

1. Handler定义


Handler是一套Android消息传递机制。


2. Handler的作用


在多线程应用场景中,将子线程中需要更新UI的操作消息,传递到UI主线程,从而实现子线程通知UI更新最终实现异步消息处理。


    

handler和process_handler和process


3 .相关概念


Message: 消息载体,里面存储这线程消息。


Handler. : 负责消息的发送和处理,子线程中使用sendMessage() 发送消息;在handleMessage()中处理。


MessageQueue:消息队列,遵循先进先出的原则,存储着sendMessage()发送来的子线程消息。


Looper: 消息循环器,负责从MessageQueue中循环取消息,再将取出的消息分发给 handleMessage(),来处理消息。每个线程只能有一个Looper (它是通过ThreadLocal实现的唯一性) ,即多个子线程可以借助同一个Handler进行通信。



4. Handler工作机制


1. Handler机制的工作流程:


  • Handler初始化: 在主线程中创建Looper、MessageQueue、Handler,创建完成后,Looper则自动进入消息循环状态 。同时,Handler自动绑定Looper和MessageQueue。如果消息队列为空,则线程阻塞。主线程中由于在ActivityThread的main()方法中已经初始化过了,所以就不需要在初始化Looper和MessageQueue了。
  • 子线程发送消息:子线程中通过Handler向消息队列MessageQueue中发送消息。
  • 消息循环:           Looper 循环取出MessageQueue中的Message消息。Looper 将循环取出的消息分发给Handler中的handleMessage()方法。
  • 接收处理消息:    在handleMessage(Message msg)方法中处理消息。

2. Looper的作用


  • 创建Looper对象
  • 创建MessageQueue对象
  • 让Looper对象持有当前线程

    Looper相关方法:


  • Looper.prepare()———为当前线程创建一个Looper;
  • Looper.loop() ——— 开启消息循环;
  • Looper.prepareMainLooper() ——— 为主线程创建Looper时使用,在ActivityThread有用到。
  • Looper.getMainLooper() ——— 通过该方法可以获取主线程的Looper。
  • Looper.quit() ——— 退出Looper循环。
  • Looper.quitSafely() ——— 自己创建的Looper,在不使用的时候,需要退出。

3. Thread、Handler、Looper的对应关系


  • 一个Thread线程只能绑定到一个Looper循环器上,但可以有多个Handler实例处理者。
  • 一个Looper循环器可以绑定多个Handler实例。比如主线程的main()方法中创建了Looper和Handler对象,但是我们开发过程中仍然可以创建其他Handler对象。
  • 一个Handler对象处理者,只能绑定到一个Looper循环器中。

5. Handler的使用


Handler的使用有两种方式:handler.sendMessage (Message msg)、handler.post (Runnable r)。


5.1 子线程向主线程发送消息


public class MainActivity extends Activity {
    private static final String TAG = "MainActivity";

    private Handler mHandler;
    private Button btnSendeToMainThread;
    private static final int MSG_SUB_TO_MAIN= 100;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btnSendeToMainThread = (Button) findViewById(R.id.btn_sendto_mainthread);
        
        // 1.创建Handler,并重写handleMessage方法
        mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                // 处理消息
                switch (msg.what) {
                    case MSG_SUB_TO_MAIN:
                        // 打印出处理消息的线程名和Message.obj
                        Log.e(TAG, "接收到消息: " + Thread.currentThread().getName() + ","+ msg.obj);
                        break;
                    default:
                        break;
                }
            }
        };
        btnSendeToMainThread.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 创建一个子线程,在子线程中发送消息
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Message msg = Message.obtain();
                        msg.what = MSG_SUB_TO_MAIN;
                        msg.obj = "这是一个来自子线程的消息";
                        // 2.发送消息
                        mHandler.sendMessage(msg);
                    }
                }).start();
            }
        });
    }
}


5.2 主线程向子线程发送消息


// 创建一个子线程,并在子线程中创建一个Handler,且重写handleMessage
new Thread(new Runnable() {
    @Override
    public void run() {
        //子线程中创建Handler接收器,就必须创建Looper。
        Looper.prepare();
        subHandler = new Handler() {
             @Override
             public void handleMessage(Message msg) {
                  super.handleMessage(msg);
                  // 处理消息
                  switch (msg.what) {
                      case MSG_MAIN_TO_SUB:
                           Log.e(TAG, "接收到消息:" + Thread.currentThread().getName() + ","+ msg.obj);
                           break;
                      default:
                           break;
                  }
              }
         };
         Looper.loop();
    }
}).start();

btnSendToSubThread = (Button) findViewById(R.id.btn_sendto_subthread);
btnSendToSubThread.setOnClickListener(new View.OnClickListener() {
     @Override
     public void onClick(View v) {
          Message msg = Message.obtain();
          msg.what = MSG_MAIN_TO_SUB;
          msg.obj = "这是一个来自主线程的消息";
          // 主线程中发送消息
          subHandler.sendMessage(msg);
     }
});

6. Handler导致内存泄漏分析


下面的两种写法会导致内存泄漏。


/**
 * 方式1:新建Handler子类(普通内部类)
 */  
public class MainActivity extends AppCompatActivity {
    public static final String TAG = "carson:";
    private Handler showhandler;
    // 主线程创建时便自动创建Looper & 对应的MessageQueue
    // 之后执行Loop()进入消息循环
    @Override
    protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
         //1. 实例化自定义的Handler类对象->>分析1
         //注:此处并无指定Looper,故自动绑定当前线程(主线程)的 Looper、MessageQueue
         showhandler = new FHandler();
         // 2. 启动子线程1
         new Thread() {
             @Override
             public void run() {
                  try {
                       Thread.sleep(1000);
                  } catch (InterruptedException e) {
                       e.printStackTrace();
                  }
                  // a. 定义要发送的消息
                  Message msg = Message.obtain();
                  msg.what = 1;// 消息标识
                  msg.obj = "AA";// 消息存放
                  // b. 传入主线程的Handler & 向其MessageQueue发送消息
                  showhandler.sendMessage(msg);
             }
          }.start();

          // 3. 启动子线程2
          new Thread() {
              @Override
              public void run() {
                  try {
                       Thread.sleep(5000);
                  } catch (InterruptedException e) {
                       e.printStackTrace();
                  }
                  // a. 定义要发送的消息
                  Message msg = Message.obtain();
                  msg.what = 2;// 消息标识
                  msg.obj = "BB";// 消息存放
                  // b. 传入主线程的Handler & 向其MessageQueue发送消息
                  showhandler.sendMessage(msg);
              }
           }.start();
    }

    // 分析1:自定义Handler子类
    class FHandler extends Handler {
        // 通过复写handlerMessage() 从而确定更新UI的操作
        @Override
        public void handleMessage(Message msg) {
              switch (msg.what) {
                  case 1:
                        Log.d(TAG, "收到线程1的消息");
                     break;
                  case 2:
                        Log.d(TAG, " 收到线程2的消息");
                     break;
              }
        }
    }
}
/**
 * 方式2:匿名Handler内部类
 */
public class MainActivity extends AppCompatActivity {
   public static final String TAG = "carson:";
   private Handler showhandler;

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

       //1. 通过匿名内部类实例化的Handler类对象
       showhandler = new  Handler(){
           @Override
           public void handleMessage(Message msg) {
                switch (msg.what) {
                    case 1:
                        Log.d(TAG, "收到线程1的消息");
                        break;
                    case 2:
                        Log.d(TAG, " 收到线程2的消息");
                        break;
                }
            }
       };

       // 2. 启动子线程1
       new Thread() {
           @Override
           public void run() {
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               // a. 定义要发送的消息
               Message msg = Message.obtain();
               msg.what = 1;// 消息标识
               msg.obj = "AA";// 消息存放
               // b. 传入主线程的Handler & 向其MessageQueue发送消息
               showhandler.sendMessage(msg);
           }
        }.start();

        // 3. 启动子线程2
        new Thread() {
            @Override
            public void run() {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // a. 定义要发送的消息
                Message msg = Message.obtain();
                msg.what = 2;// 消息标识
                msg.obj = "BB";// 消息存放
                // b. 传入主线程的Handler & 向其MessageQueue发送消息
                showhandler.sendMessage(msg);
            }
        }.start();
    }
}


      

1. 储备知识


在Java中,非静态内部类匿名内部类 都是会默认持有外部类的引用的。


主线程中的Looper对象的生命周期 = 该应用程序的生命周期


2. 内存泄漏原因描述


当Handler消息队列MessageQueue中,还有未处理的消息或者正在处理的消息时,消息队列中的Message会持有Handler实例的引用,而Handler实例又持有着外部类MainActivity的引用。此时如果外部类MainActivity被销毁,但由于上述的引用关系,垃圾回收器(GC)无法回收MainActivity,从而造成内存泄漏。


最终原因,是由于Looper的生命周期 > MainActivity的生命周期。


3. 内存泄漏的解决方案


     解决方案一:


     静态内部类 + 弱引用


     原理:静态内部类,默认不会持有外部类的引用。从而使得逐层的引用关系不复存在;


     弱引用对象拥有的生命周期很短暂。垃圾回收器执行扫描时,一旦发现了具有弱引用的对象,


     不管内存空间是否充足,都会回收这个弱引用对象的内存。


     解决方案二:


     外部类被销毁时,清空MessageQueue消息队列。


     这样不仅使得逐层的引用关系不复存在,同时使得Looper的生命周期与外部类实现了同步。


     清除消息队列的方法是:removeCallbackAndMessages(null);



7. Handler中的Looper如何停止


可以通过调用 handler.getLooper().quit(),执行完退出之后,Looper.loop() 后面的代码才会执行。否则后面的代码是永远都不会执行的。


public class LooperActivity extends AppCompatActivity {
    private static final String TAG = "LooperActivity";
    private Button btn;
    private Handler mHandler;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_looper);
        btn = (Button) findViewById(R.id.btn);
        // 开启一个子线程,去执行异步任务
        new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                mHandler = new Handler() {
                    @Override
                    public void handleMessage(Message msg) {
                        super.handleMessage(msg);
                    }
                };
                Log.e(TAG, "Looper.loop之前" );
                // Looper.loop方法是一个死循环
                Looper.loop();
                // 得不到执行
                Log.e(TAG, "Looper.loop之后" );
            }
        }).start();
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 调用Looper的quit方法,停止Looper
                mHandler.getLooper().quit();
            }
        });
    }
}


8. Looper 死循环为什么不会导致应用卡死?

Launch桌面的图标第一次启动 Activity 时,会最终走到 ActivityThread 的 main 方法,在 main 方法里面创建了 Looper 和 MessageQueue 来处理主线程的消息,然后 Looper.loop 方法就进入死循环。我们的 Activity 的生命周期都是通过 Handler 机制来处理的。在看看看 loop 方法循环:

主线程主要依靠的就是消息循环,一旦退出消息循环,那么应用也就退出了。 Looper.loop() 方法可能会引起主线程的阻塞,但只要它的消息循环没有被阻塞,能一直处理消息事件,就不会产生 ANR 异常。

造成 ANR 异常的不是主线程阻塞,而是主线程中 Looper 的消息处理过程发生了任务阻塞从而无法响应手势操作,不能及时刷新UI。

阻塞与ANR无必然关系,虽然主线程在没有消息可处理的时候是阻塞状态的,但是只要保证有消息的时候能够得到立即处理,程序就不会发生 ANR。

总结:应用卡死跟这个Looper 没有关系,应用在没有消息需要处理的时候,Looper 它是在睡眠,释放线程;卡死是 ANR,而 Looper 是睡眠。

9. MessageQueue源码详解


1. 源码-----插入消息


boolean enqueueMessage(Message msg, long when) {
    //即不允许添加同步消息屏障,实现异步消息了。
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }
    if (msg.isInUse()) {
        throw new IllegalStateException(msg + " This message is already in use.");
    }
    synchronized (this) {
        //. . . . . .
        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        //当队列为空,第一次发来的 message,或者新发来的 message 的时间更小的情况,就走if块,添加到链表头部。
        if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            msg.next = p;
            mMessages = msg;
            //如果此时Looper处于阻塞状态,则唤醒。并循环执行 message 的读取。
            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.
            // 将发来的 message 插入到 queue 队列的中间或者尾部。
            // 这种情况下,我们不需要唤醒事件队列。除非有一个消息屏障在队列的头部,并且它是队列中最早的异步消息。
            
            // 判断当前发送来的 message 是否是一个异步消息,如果是异步消息,并且队列 queue头部含有 消息屏障
            // p.target = null, 同时当前 queue是处于阻塞状态。则设为 “需要唤醒队列”。 
            //这里的 p 消息,也有可能是 postAsyncBarrier()中传到 消息队列里的,因此此处需要处理异步消息的情况
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            // 执行一个 for循环,并通过时间的对比,将新发来的消息,插入到队列中适当的位置。
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
                //如果队列头部有了 target=null 的一个消息屏障,并且当前发送过来的 message 是一个异步消息。
                // 如果添加 message 之前,队列中还有其他异步消息等待处理,则就不唤醒 queue。否则就唤醒。
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p;
            prev.next = msg;
        }
        // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

2. 源码——Looper

public static void loop() {
    boolean slowDeliveryDetected = false;
    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        try {
            // 将事件交由 Handler处理
            msg.target.dispatchMessage(msg);
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        // 回收msg,以便 Message.obtain() 实现复用。
        msg.recycleUnchecked();
    }
}

3. 源码——MessageQueue

Message next() {
    // mPtr,Native层的 MessageQueue。
    final long ptr = mPtr;
    if (ptr == 0) {
        return null;
    }
    int nextPollTimeoutMillis = 0;
    for (;;) {
        // Native层设置阻塞时间—延迟消息的时间
        nativePollOnce(ptr, nextPollTimeoutMillis);
        synchronized (this) {
            // Try to retrieve the next message.  Return if found.
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages; // 获取链表头部信息
            if (msg != null && msg.target == null) {
                // Stalled by a barrier.  Find the next asynchronous message in the queue.
                // 循环遍历,查找第一个 异步消息。会直接忽略 queue 中的其他消息。
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            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 {
                    // 返回一个即时消息,或者返回一个已到时间的 延迟消息。
                    mBlocked = false; // 设置为 queue未阻塞状态
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    msg.markInUse();
                    return msg;
                }
            } else {
                // queue 队列中没有消息时,会返回 -1,表示 nativePollOnce() 会一直阻塞 
                nextPollTimeoutMillis = -1;
            }
        }
    }
}

4. 延迟消息Handler.postDelay(new Runnable(), 2000)


//使用方式
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
    @Override
    public void run() {
       //延迟一秒后,执行这里面的内容
    }
},1000);
handler.removeCallback();

传入的Runnable,最终存储在了mesage.callback 中。也是一种延迟消息,当到时间时,looper循环取出后,会执行Handler中的dispatchMessage()方法。判断如果 callback 不为空,就执行 callback.run()。callback就是一个Rnnable对象。



Hanlder同步消息屏障

原理:Android 源码分析 - Handler的同步屏障机制


实例:Android中异步消息和同步屏障


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

    final Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            System.out.println("打印: " + msg.what);
            switch (msg.what){
                case 999:
                    //第四步 异步消息执行完之后必需,移除消息屏障,否则会一直阻塞 queue。
                    removeSyncBarrier();
                    break;
            }
        }
    };

    new Thread(new Runnable() {
        int max = 0;

        @Override
        public void run() {
            while (max != 10) {
                handler.sendEmptyMessageDelayed(max, 5000 + 500 * max);
                max++;
            }
        }
    }).start();

    new Thread(new Runnable() {
        @Override
        public void run() {
            Message message = Message.obtain();
            message.what = 999;
            // 第一步 设置 message为 异步消息
            message.setAsynchronous(true);
            // 第二步 设置消息屏障
            postSyncBarrier();
            // 第三步 发送消息
            handler.sendMessageDelayed(message, 0);
        }
    }).start();
}

private int token = 0;//设置消息屏障时返回的token,用于删除消息屏障时使用。

// 反射执行投递同步屏障
public void postSyncBarrier() {
    Method method = null;
    try {
        method = MessageQueue.class.getDeclaredMethod("postSyncBarrier");
        token = (int) method.invoke(Looper.getMainLooper().getQueue());
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
}

// 反射执行移除同步屏障
public void removeSyncBarrier() {
    Method method = null;
    try {
        method = MessageQueue.class.getDeclaredMethod("removeSyncBarrier", int.class);
        method.invoke(Looper.getMainLooper().getQueue(), token);
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
}

源码分析 --- 消息屏障


步骤一、Message设置为异步消息


步骤二 、在 queue中设置消息屏障


// 8.0之后该方法改为 @hide隐藏了,因此需要通过反射获取。
public int postSyncBarrier() {
    return postSyncBarrier(SystemClock.uptimeMillis());
}
//注意⚠️ 这里添加message的时候,target没有设置,默认为null — — 即消息屏障
private int postSyncBarrier(long when) {
    // Enqueue a new sync barrier token.
    // We don't need to wake the queue because the purpose of a barrier is to stall it.
    synchronized (this) {
        final int token = mNextBarrierToken++;
        final Message msg = Message.obtain();
        msg.markInUse();
        msg.when = when;
        msg.arg1 = token;
        Message prev = null;
        Message p = mMessages;
        // 从链表的头部开始,找出队列中最后一个message,或者找出queue 中间的延迟时间晚于当前message延迟时间的
        if (when != 0) {
            while (p != null && p.when <= when) {
                prev = p;
                p = p.next;
            }
        }
        if (prev != null) { // 如果queue队列前面有其他message,则将当前 message插入到这两个 message中间 
            msg.next = p;
            prev.next = msg;
        } else { // 如果前面没有 message,则将当前的 message设置为最头部的 message。
            msg.next = p;
            mMessages = msg;
        }
        // 返回一个 token,用于接下来的 移除工作。
        return token;
    }
}

步骤三、发送消息


步骤四、移除消息屏障


public void removeSyncBarrier(int token) {
    // Remove a sync barrier token from the queue.
    // If the queue is no longer stalled by a barrier then wake it.
    synchronized (this) {
        Message prev = null;
        Message p = mMessages;
        while (p != null && (p.target != null || p.arg1 != token)) {
            prev = p;
            p = p.next;
        }
        if (p == null) {
            throw new IllegalStateException("The specified message queue synchronization "
                    + " barrier token has not been posted or has already been removed.");
        }
        final boolean needWake;
        if (prev != null) {
            prev.next = p.next;
            needWake = false;
        } else {
            mMessages = p.next;
            needWake = mMessages == null || mMessages.target != null;
        }
        p.recycleUnchecked();

        // If the loop is quitting then it is already awake.
        // We can assume mPtr != 0 when mQuitting is false.
        if (needWake && !mQuitting) {
            nativeWake(mPtr);
        }
    }
}