一:各自的作用

handler 用于发送消息 和处理消息
  Looper:持有消息队列,在loop()方法中不断的循环处理消息队列中的消息
  消息队列:存放消息的一种数据结构
  子线程:子线程只能是没有Looper 对象,如果需要在子线程中处理消息,那么是需要自己在子线程中 初始化looper,并调用looper.loop()方法进行循环处理这个子线中的消息。
  主线程:可参考ActivityThread类中的main()方法,系统已经为我们创建好了looper,并且也已经帮我们调用了looper.loop()方法,循环处理主线程中的消息

二:关系

主线程中 已经初始化了looper对象,并且调用了looper.loop()方法处理消息。所以是主线程中 持有一个ooper对象,looper对象持有消息队列。
  looper.myLooper(); 获取当前进程的looper对象
  looper.getMainLooper() 获取主线程的looper对象(也就是当前进程的looper对象) 
  Handler,作用已经说了。handler的创建必须是在looper已经初始化了,我们在Activity 或者是Fragment 中创建handler对象时,直接new Handler()那是因为这些操作都是在主线程中执行的,主线程已经帮助我们做好了(你可以测试一下 在子线程中 没有初始化looper的时候 创建handler对象,系统会告你没有looper初始化)。查看创建handler的源码,可以看到handler是获得了应用的looper(也就是主线程的looper对象),那当然也获得了主线程中looper的消息队列。这样handler 就能将其他线程需要改变UI操作的消息发送到主线程的消息队列中了。

三:为何安卓系统会使用这种机制

你肯定知道不同线程操作同一个数据可能会出现这个数据与预期结果不一致情况,这种现象就是线程不安全。你可能很快的想到 可以加一把锁就能避免这样的问题。的确可以同加一把锁避免这种线程不安全的问题。
   现在咱们来回到安卓系统中说的 所有的UI操作都必须放在UI线程中执行,我们假设可以在子线程更新UI,那么就会出现这样子的一种况:其中一个子线程更新界面还没有完成,另外一个子线程就又去更新UI了,这样子会造成子UI界面错乱。那么你可能会说,我们可以实行加锁机制啊,让更新UI的代码不能并发执行。如果每一个子线程都加锁,那么毫无疑问程序的性能将会大大下降。因此主要的原因总结起来就是两点:1)为了防止界面错乱 2)为了防止程序性能下降
 因此android不允许在子线程中更新UI。为了解决这个这个问题,android就设计出这样子的一套消息处理机制,让我们尽管在子线程中发送更新UI的消息,而不用去关心多线程问题。在主线程的消息队里中采取轮询更新处理。