Android消息机制Handler的工作过程详解

综述

  在Android系统中,出于对性能优化的考虑,对于Android的UI操作并不是线程安全的。也就是说若是有多个线程来操作UI组件,就会有可能导致线程安全问题。所以在Android中规定只能在UI线程中对UI进行操作。这个UI线程是在应用第一次启动时开启的,也称之为主线程(Main Thread),该线程专门用来操作UI组件,在这个UI线程中我们不能进行耗时操作,否则就会出现ANR(Application Not Responding)现象。如果我们在子线程中去操作UI,那么程序就回给我们抛出异常。这是因为在ViewRootImpl中对操作UI的线程进行检查。如果操作UI的线程不是主线程则抛出异常(对于在检查线程之前在非UI线程已经操作UI组件的情况除外)。所以这时候我们若是在子线程中更新UI的话可以通过Handler来完成这一操作。

Handler用法简介

  在开发中,我们对Handler的使用也基本上算是家常便饭了。在这里我们就简单的说一下Handler的几种用法示例,就不在具体给出Demo进行演示。在这里我们只针对后面这一种情形来看一下Handler的使用:在子线程完成任务后通过Handler发送消息,然后在主线程中去操作UI。
  一般来说我们会在主线程中创建一个Handler的匿名内部类,然后重写它的handleMessage方法来处理我们的UI操作。代码如下所示。

private Handler mHandler = new Handler(){
  @Override
  public void handleMessage(Message msg) {
    switch (msg.what){
      //根据msg.what的值来处理不同的UI操作
      case WHAT:
        break;
      default:
        super.handleMessage(msg);
        break;
    }

  }
};

  我们还可以不去创建一个Handler的子类对象,直接去实现Handler里的CallBack接口,Handler通过回调CallBack接口里的handleMessage方法从而实现对UI的操作。

private Handler mHandler = new Handler(new Handler.Callback() {
  @Override
  public boolean handleMessage(Message msg) {

    return false;
  }
});

  然后我们就可以在子线程中发送消息了。

new Thread(new Runnable() {
  @Override
  public void run() {
    //子线程任务
    ...
    //发送方式一 直接发送一个空的Message
    mHandler.sendEmptyMessage(WHAT);
    //发送方式二 通过sendToTarget发送
    mHandler.obtainMessage(WHAT,arg1,arg2,obj).sendToTarget();
    //发送方式三 创建一个Message 通过sendMessage发送
    Message message = mHandler.obtainMessage();
    message.what = WHAT;
    mHandler.sendMessage(message);
  }
}).start();

  在上面我们给出了三种不同的发送方式,当然对于我们还可以通过sendMessageDelayed进行延时发送等等。如果我们的Handler只需要处理一条消息的时候,我们可以通过post一系列方法进行处理。

private Handler mHandler = new Handler();

new Thread(new Runnable() {
  @Override
  public void run() {
    mHandler.post(new Runnable() {
      @Override
      public void run() {
        //UI操作
        ...
      }
    });
  }
}).start();

  在Handler中处理UI操作时,上面的Handler对象必须是在主线程创建的。如果我们想在子线程中去new一个Handler对象的话,就需要为Handler指定Looper。

private Handler mHandler;

new Thread(new Runnable() {
  @Override
  public void run() {
    mHandler = new Handler(Looper.getMainLooper()){
      @Override
      public void handleMessage(Message msg) {
        super.handleMessage(msg);
        //UI操作
        ...
      }
    };

  }
}).start();

  对于这个Looper是什么,下面我们会详细介绍。对于Handler的使用依然存在一个问题,由于我们创建的Handler是一个匿名内部类,他会隐式的持有外部类的一个对象(当然内部类也是一样的),而往往在子线程中是一个耗时的操作,而这个线程也持有Handler的引用,所以这个子线程间接的持有这个外部类的对象。我们假设这个外部类是一个Activity,而有一种情况就是我们的Activity已经销毁,而子线程仍在运行。由于这个线程持有Activity的对象,所以,在Handler中消息处理完之前,这个Activity就一直得不到回收,从而导致了内存泄露。如果内存泄露过多,则会导致OOM(OutOfMemory),也就是内存溢出。那么有没有什么好的解决办法呢?
  我们可以通过两种方案来解决,第一种方法我们在Activity销毁的同时也杀死这个子线程,并且将相对应的Message从消息队列中移除;第二种方案则是我们创建一个继承自Handler的静态内部类。因为静态内部类不会持有外部类的对象。可是这时候我们无法去访问外部类的非静态的成员变量,也就无法对UI进行操作。这时候我们就需要在这个静态内部类中使用弱引用的方式去指向这个Activity对象。下面我们看一下示例代码。

static class MyHandler extends Handler{
  private final WeakReference<MyActivity> mActivity;

  public MyHandler(MyActivity activity){
    super();
    mActivity = new WeakReference<MyActivity>(activity);
  }

  @Override
  public void handleMessage(Message msg) {
    MyActivity myActivity = mActivity.get();
    if (myActivity!=null){
      myActivity.textView.setText("123456789");
    }
  }
}

Handler工作过程

  在上面我们简单的说明了Handler是如何使用的。那么现在我们就来看一下这个Handler是如何工作的。在Android的消息机制中主要是由Handler,Looper,MessageQueue,Message等组成。而Handler得运行依赖后三者。那么我们就来看一下它们是如何联系在一起的。

Looper

  在一个Android应用启动的时候,会创建一个主线程,也就是UI线程。而这个主线程也就是ActivityThread。在ActivityThread中有一个静态的main方法。这个main方法也就是我们应用程序的入口点。我们来看一下这个main方法。

public static void main(String[] args) {

  ......

  Looper.prepareMainLooper();

  ActivityThread thread = new ActivityThread();
  thread.attach(false);

  ......

  Looper.loop();

  throw new RuntimeException("Main thread loop unexpectedly exited");
}

  在上面代码中通过prepareMainLooper方法为主线程创建一个Looper,而loop则是开启消息循环。从上面代码我们可以猜想到在loop方法中应该存在一个死循环,否则给我们抛出RuntimeException。也就是说主线程的消息循环是不允许被退出的。下面我们就来看一下这个Looper类。
  首先我们看一下Looper的构造方法。

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

  在这个构造方法中创建了一个消息队列。并且保存当前线程的对象。其中quitAllowed参数表示是否允许退出消息循环。但是我们注意到这个构造方法是private,也就是说我们自己不能手动new一个Looper对象。那么我们就来看一下如何创建一个Looper对象。之后在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));
}

  在这里新建了一个Looper对象,然后将这个对象保存在ThreadLocal中,当我们下次需要用到Looper的之后直接从这个sThreadLocal中取出即可。在这里简单说明一下ThreadLocal这个类,ThreadLocal它实现了本地变量存储,我们将当前线程的数据存放在ThreadLocal中,若是有多个变量共用一个ThreadLocal对象,这时候在当前线程只能获取该线程所存储的变量,而无法获取其他线程的数据。在Looper这个类中为我们提供了myLooper来获取当前线程的Looper对象。从上面的方法还能够看出,一个线程只能创建一次Looper对象。然后我们在看一下这个prepare在哪里被使用的。

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

public static void prepareMainLooper() {
  prepare(false);
  synchronized (Looper.class) {
    if (sMainLooper != null) {
      throw new IllegalStateException("The main Looper has already been prepared.");
    }
    sMainLooper = myLooper();
  }
}

    prepare方法:这个是用于在子线程中创建一个Looper对象,在子线程中是可以退出消息循环的。
    prepareMainLooper方法:这个方法在上面的ActivityThread中的main方法中我们就已经见到过了。它是为主线程创建一个Looper,在主线程创建Looper对象中,就设置了不允许退出消息循环。并且将主线程的Looper保存在sMainLooper中,我们可以通过getMainLooper方法来获取主线程的Looper。
  在ActivityThread中的main方法中除了创建一个Looper对象外,还做了另外一件事,那就是通过loop方法开启消息循环。那么我们就来看一下这个loop方法做了什么事情。

public static void loop() {
  final Looper me = myLooper();
  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(); // might block
    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);

    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.recycleUnchecked();
  }
}

  第2~6行:获取当前线程中的Looper,并从Looper中获得消息队列。
  第10~11行:确保当前线程属于当前进程,并且记录真实的token。clearCallingIdentity的实现是在native层,对于具体是如何实现的就不在进行分析。
  第14~18行:从消息队列中取出消息,并且只有当取出的消息为空的时候才会跳出循环。
  第27行:将消息重新交由Handler处理。
  第35~42行:确保调用过程中线程没有被销毁。
  第44行:对消息进行回收处理。
  和我们刚才猜想的一样,在loop中确实存在一个死循环,而唯一退出该循环的方式就是消息队列返回的消息为空。然后我们通过消息队列的next()方法获得消息。msg.target是发送消息的Handler,通过Handler中的dispatchMessage方法又将消息交由Handler处理。消息处理完成之后便对消息进行回收处理。在这里我们也能够通过quit和quitSafely退出消息循环。

public void quit() {
  mQueue.quit(false);
}

public void quitSafely() {
  mQueue.quit(true);
}

  我们可以看出对于消息循环的退出,实际上就是调用消息队列的quit方法。这时候从MessageQueue的next方法中取出的消息也就是null了。下面我们来看一下这个MessageQueue。

MessageQueue

  MessageQueue翻译为消息队里,在这个消息队列中是采用单链表的方式实现的,提高插入删除的效率。对于MessageQueue在这里我们也只看一下它的入队和出队操作。
  MessageQueue入队方法。

boolean enqueueMessage(Message msg, long when) {

  ......

  synchronized (this) {

    ......

    msg.markInUse();
    msg.when = when;
    Message p = mMessages;
    boolean 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;
}

  在这里我们简单说一下这个入队的方法。消息的插入过程是在第13~36行完成了。在这里首先判断首先判断消息队列里有没有消息,没有的话则将当前插入的消息作为队头,并且这时消息队列如果处于等待状态的话则将其唤醒。若是在中间插入,则根据Message创建的时间进行插入。
  MessageQueue出队方法。

Message next() {

  ......

  int nextPollTimeoutMillis = 0;
  for (;;) {
    if (nextPollTimeoutMillis != 0) {
      Binder.flushPendingCommands();
    }

    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.
        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 {
          // 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;
        }
      } else {
        // No more messages.
        nextPollTimeoutMillis = -1;
      }

      // Process the quit message now that all pending messages have been handled.
      if (mQuitting) {
        dispose();
        return null;
      }

      ......
    }

    .....
  }
}

  第11行:nativePollOnce方法在native层,若是nextPollTimeoutMillis为-1,这时候消息队列处于等待状态。
  第25~42行:按照我们设置的时间取出消息。
  第43~45行:这时候消息队列中没有消息,将nextPollTimeoutMillis设为-1,下次循环消息队列则处于等待状态。
  第48~52行:退出消息队列,返回null,这时候Looper中的消息循环也会终止。
  最后我们在看一下退出消息队列的方法:

void quit(boolean safe) {
  if (!mQuitAllowed) {
    throw new IllegalStateException("Main thread not allowed to quit.");
  }

  synchronized (this) {
    if (mQuitting) {
      return;
    }
    mQuitting = true;

    if (safe) {
      removeAllFutureMessagesLocked();
    } else {
      removeAllMessagesLocked();
    }

    // We can assume mPtr != 0 because mQuitting was previously false.
    nativeWake(mPtr);
  }
}

  从上面我们可以看到主线程的消息队列是不允许被退出的。并且在这里通过将mQuitting设为true从而退出消息队列。也使得消息循环被退出。到这里我们介绍了Looper和MessageQueue,就来看一下二者在Handler中的作用。

Handler

  在这里我们首先看一下Handler的构造方法。

public Handler(Callback callback, boolean async) {

  ......

  mLooper = Looper.myLooper();
  if (mLooper == null) {
    throw new RuntimeException(
      "Can't create handler inside thread that has not called Looper.prepare()");
  }
  mQueue = mLooper.mQueue;
  mCallback = callback;
  mAsynchronous = async;
}

  从这个构造方法中我们可以看出在一个没有创建Looper的线程中是无法创建一个Handler对象的。所以说我们在子线程中创建一个Handler时首先需要创建Looper,并且开启消息循环才能够使用这个Handler。但是在上面的例子中我们确实在子线程中new了一个Handler对象。我们再来看一下上面那个例子的构造方法。

public Handler(Looper looper, Callback callback, boolean async) {
  mLooper = looper;
  mQueue = looper.mQueue;
  mCallback = callback;
  mAsynchronous = async;
}

  在这个构造方法中我们为Handler指定了一个Looper对象。也就说在上面的例子中我们在子线程创建的Handler中为其指定了主线程的Looper,也就等价于在主线程中创建Handler对象。下面我们就来看一下Handler是如何发送消息的。
  对于Handler的发送方式可以分为post和send两种方式。我们先来看一下这个post的发送方式。

public final boolean post(Runnable r)
{
  return sendMessageDelayed(getPostMessage(r), 0);
}

  在这里很明显可以看出来,将post参数中的Runnable转换成了Message对象,然后还是通过send方式发出消息。我们就来看一下这个getPostMessage方法。

private static Message getPostMessage(Runnable r) {
  Message m = Message.obtain();
  m.callback = r;
  return m;
}

  在这里也是将我们实现的Runnable交给了Message对象的callback属性。并返回该Message对象。
  既然post发送也是由send发送方式进行的,那么我们一路找下去,最终消息的发送交由sendMessageAtTime方法进行处理。我们就来看一下这个sendMessageAtTime方法。

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);
}

  然后再来看一下enqueueMessage方法。

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
  msg.target = this;
  if (mAsynchronous) {
    msg.setAsynchronous(true);
  }
  return queue.enqueueMessage(msg, uptimeMillis);
}

  到这里我们可以看出来了所谓通过Handler发送消息只不过是在Looper创建的消息队列中插入一条消息而已。而在Looper中只不过通过loop取出消息,然后交由Handler中的dispatchMessage方发进行消息分发处理。下面我们来看一下dispatchMessage方法。

public void dispatchMessage(Message msg) {
  if (msg.callback != null) {
    handleCallback(msg);
  } else {
    if (mCallback != null) {
      if (mCallback.handleMessage(msg)) {
        return;
      }
    }
    handleMessage(msg);
  }
}

  这里面的逻辑也是非常的简单,msg.callback就是我们通过post里的Runnable对象。而handleCallback也就是去执行Runnable中的run方法。

private static void handleCallback(Message message) {
  message.callback.run();
}

  mCallback就是我们所实现的回调接口。最后才是对我们继承Handler类中重写的handleMessage进行执行。可见其中的优先级顺序为post>CallBack>send;
  到这里我们对整个Handler的工作过程也就分析完了。现在我们想要通过主线程发送消息给子线程,然后由子线程接收消息并进行处理。这样一种操作也就很容易实现了。我们来看一下怎么实现。

package com.example.ljd.myapplication;

import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;

public class MyActivity extends AppCompatActivity {

  private final String TAG = "MyActivity";
  public Handler mHandler;
  public Button button;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    button = (Button) findViewById(R.id.send_btn);
    button.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        if (mHandler != null){
          mHandler.obtainMessage(0,"你好,我是从主线程过来的").sendToTarget();
        }
      }
    });
    new Thread(new Runnable() {
      @Override
      public void run() {
        //在子线程中创建一个Looper对象
        Looper.prepare();
        mHandler = new Handler(){
          @Override
          public void handleMessage(Message msg) {
            if (msg.what == 0){
              Log.d(TAG,(String)msg.obj);
            }
          }
        };
        //开启消息循环
        Looper.loop();
      }
    }).start();

  }
}

  点击按钮我们看一下运行结果。

总结

  在这里我们重新整理一下我们的思路,看一下这个Handler的整个工作流程。在主线程创建的时候为主线程创建一个Looper,创建Looper的同时在Looper内部创建一个消息队列。而在创键Handler的时候取出当前线程的Looper,并通过该Looper对象获得消息队列,然后Handler在子线程中发送消息也就是在该消息队列中添加一条Message。最后通过Looper中的消息循环取得这条Message并且交由Handler处理。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android6.0 消息机制原理解析

    消息都是存放在一个消息队列中去,而消息循环线程就是围绕这个消息队列进入一个无限循环的,直到线程退出.如果队列中有消息,消息循环线程就会把它取出来,并分发给相应的Handler进行处理:如果队列中没有消息,消息循环线程就会进入空闲等待状态,等待下一个消息的到来.在编写Android应用程序时,当程序执行的任务比较繁重时,为了不阻塞UI主线程而导致ANR的发生,我们通常的做法的创建一个子线程来完成特定的任务.在创建子线程时,有两种选择,一种通过创建Thread对象来创建一个无消息循环的子线程:还有一

  • Android消息循环机制源码深入理解

    Android消息循环机制源码 前言: 搞Android的不懂Handler消息循环机制,都不好意思说自己是Android工程师.面试的时候一般也都会问这个知识点,但是我相信大多数码农肯定是没有看过相关源码的,顶多也就是网上搜搜,看看别人的文章介绍.学姐不想把那个万能的关系图拿出来讨论. 近来找了一些关于android线程间通信的资料,整理学习了一下,并制作了一个简单的例子. andriod提供了 Handler 和 Looper 来满足线程间的通信.例如一个子线程从网络上下载了一副图片,当它下

  • Android中Handler消息传递机制

    Handler 是用来干什么的? 1)执行计划任务,可以在预定的时间执行某些任务,可以模拟定时器 2)线程间通信.在Android的应用启动时,会创建一个主线程,主线程会创建一个消息队列来处理各种消息.当你创建子线程时,你可以在你的子线程中拿到父线程中创建的Handler 对象,就可以通过该对象向父线程的消息队列发送消息了.由于Android要求在UI线程中更新界面,因此,可以通过该方法在其它线程中更新界面. 出于性能优化考虑,Android的UI操作并不是线程安全的,这意味着如果有多个线程并发

  • Android 消息机制以及handler的内存泄露

    Handler 每个初学Android开发的都绕不开Handler这个"坎",为什么说是个坎呢,首先这是Android架构的精髓之一,其次大部分人都是知其然却不知其所以然.今天看到Handler.post这个方法之后决定再去翻翻源代码梳理一下Handler的实现机制. 异步更新UI 先来一个必背口诀"主线程不做耗时操作,子线程不更新UI",这个规定应该是初学必知的,那要怎么来解决口诀里的问题呢,这时候Handler就出现在我们面前了(AsyncTask也行,不过本质

  • Android中利用App实现消息推送机制的代码

    1.消息推送机制 服务器器端需要变被动为主动,通知客户一些开发商认为重要的信息,无论应用程序是否正在运行或者关闭. 我想到了一句话:don't call me,i will call you! qq今天在右下角弹出了一个对话框:"奥巴马宣布本拉登挂了...",正是如此. 自作聪明,就会带点小聪明,有人喜欢就有人讨厌. 2.独立进程 无论程序是否正在运行,我们都要能通知到客户,我们需要一个独立进程的后台服务. 我们需要一个独立进程的后台服务. 在androidmanifest.xml中注

  • Android 消息机制问题总结

    Android的消息机制几乎是面试必问的话题,当然也并不是因为面试,而去学习,更重要的是它在Android的开发中是必不可少的,占着举足轻重的地位,所以弄懂它是很有必要的.下面就来说说最基本的东西. Looper 作用: 关联起Thread 循环取出消息 1.Looper是否可以直接实例化? Looper构造方法是私有的,其中做了两件事 创建一个MessageQueue 得到与之对应的Thread private Looper(boolean quitAllowed) { mQueue = ne

  • 学习Android Handler消息传递机制

    Android只允许UI线程修改Activity里的UI组件.当Android程序第一次启动时,Android会同时启动一条主线程(Main Thread),主线程主要负责处理与UI相关的事件,如用户的按键事件.屏幕绘图事件,并把相关的事件分发到对应的组件进行处理.所以,主线程通常又被称为UI线程. Android只允许UI线程修改Activity里的UI组件,这样会导致新启动的线程无法动态改变界面组件的属性值.但在实际的Android程序开发中,尤其是涉及动画的游戏开发中,需要让新启动的线程周

  • 深入浅析Android消息机制

    在Android中,线程内部或者线程之间进行信息交互时经常会使用消息,这些基础的东西如果我们熟悉其内部的原理,将会使我们容易.更好地架构系统,避免一些低级的错误. 每一个Android应用在启动的时候都会创建一个线程,这个线程被称为主线程或者UI线程,Android应用的所有操作默认都会运行在这个线程中. 但是当我们想要进行数据请求,图片下载,或者其他耗时操作时,是不可能在这个UI线程做的,因为Android在3.0以后的版本已经禁止了这件事情,直接抛出一个异常.所以我们需要一个子线程来处理那些

  • Android的消息机制

    一.简介 Android的消息机制主要是指Handler的运行机制,那么什么是Handler的运行机制那?通俗的来讲就是,使用Handler将子线程的Message放入主线程的Messagequeue中,在主线程使用. 二.学习内容 学习Android的消息机制,我们需要先了解如下内容. 消息的表示:Message 消息队列:MessageQueue 消息循环,用于循环取出消息进行处理:Looper 消息处理,消息循环从消息队列中取出消息后要对消息进行处理:Handler 平常我们接触的大多是H

  • Android消息机制Handler的工作过程详解

    综述 在Android系统中,出于对性能优化的考虑,对于Android的UI操作并不是线程安全的.也就是说若是有多个线程来操作UI组件,就会有可能导致线程安全问题.所以在Android中规定只能在UI线程中对UI进行操作.这个UI线程是在应用第一次启动时开启的,也称之为主线程(Main Thread),该线程专门用来操作UI组件,在这个UI线程中我们不能进行耗时操作,否则就会出现ANR(Application Not Responding)现象.如果我们在子线程中去操作UI,那么程序就回给我们抛

  • Android消息机制Handler用法总结

    1.简述 Handler消息机制主要包括: MessageQueue. Handler. Looper.Message. Message:需要传递的消息,可以传递数据: MessageQueue:消息队列,但是它的内部实现并不是用的队列,而是通过单链表的数据结构来维护消息列表,因为单链表在插入和删除上比较有优势.主要功能是向消息池投递消息( MessageQueue.enqueueMessage)和取走消息池的消息( MessageQueue.next).  Handler:消息辅助类,主要功能

  • Android消息机制Handler深入理解

    目录 概述 Handler的使用 Handler架构 Handler的运行流程 源码分析 在子线程创建Handler 主线程的Looper Looper Handler 分发消息 总结 概述 Handler是Android消息机制的上层接口.通过它可以轻松地将一个任务切换到Handler所在的线程中去执行.通常情况下,Handler的使用场景就是更新UI. Handler的使用 在子线程中,进行耗时操作,执行完操作后,发送消息,通知主线程更新UI. public class Activity e

  • Android入门之Handler的使用教程详解

    目录 简介 项目结构 代码 前端 后端 简介 我们在前面的Android教程中已经提到过这么一件事:Android在启动后会有一个主线程.它不允许任何子线程去改变主UI线程里的内容. 这么做是为了妨止,万一有一个带有大事务的线程导致了渲染组件时间过长最终导致Android UI出现“闪退”.“崩溃”的保护机制. 而实际我们在Android操作里的确是会有一些“耗时”的事情而采用异步线程,如:首页打开时调用第三方地图定位API.调用第三方银行API来显示你的余额.调用第三方社保显示你的当前社保缴纳

  • Android 消息分发使用EventBus的实例详解

    Android 消息分发使用EventBus的实例详解 1. AndroidStudio使用 dependencies { //最新版本 compile 'org.greenrobot:eventbus:3.0.0' //可以翻倍提高EventBus使用效率 provided 'de.greenrobot:eventbus-annotation-processor:3.0.0-beta1' } 2. 在基类Activity中配置 /** * Activity基类 */ protected Eve

  • Android应用框架之应用启动过程详解

    在Android的应用框架中,ActivityManagerService是非常重要的一个组件,尽管名字叫做ActivityManagerService,但通过之前的博客介绍,我们知道,四大组件的创建都是有AMS来完成的,其实不仅是应用程序中的组件,连Android应用程序本身也是AMS负责启动的.AMS本身运行在一个独立的进程中,当系统决定要在一个新的进程中启动一个Activity或者Service时就会先启动这个进程.而AMS启动进程的过程是从startProcessLocked启动的. 1

  • python进程间通信Queue工作过程详解

    Process之间有时需要通信,操作系统提供了很多机制来实现进程间的通信. 1. Queue的使用 可以使用multiprocessing模块的Queue实现多进程之间的数据传递,Queue本身是一个消息列队程序,首先用一个小实例来演示一下Queue的工作原理: import multiprocessing q = multiprocessing.Queue(3) # 初始化的Queue对象,最多能put三条消息 q.put("消息1") q.put("消息2")

  • Android消息个数提醒控件使用详解

    前言 在QQ中有消息个数提醒的控件,虽然现在没用到,但是以后可能会用到,所以就实现它,也不难. 实现 效果图如下: 先贴源码了: public class TipNumberView extends TextView { private Paint mBgPaint ; PaintFlagsDrawFilter pfd; public TipNumberView(Context context, AttributeSet attrs) { super(context, attrs); //初始化

  • Android 消息机制详解及实例代码

    Android 消息机制 1.概述 Android应用启动时,会默认有一个主线程(UI线程),在这个线程中会关联一个消息队列(MessageQueue),所有的操作都会被封装成消息队列然后交给主线程处理.为了保证主线程不会退出,会将消息队列的操作放在一个死循环中,程序就相当于一直执行死循环,每循环一次,从其内部的消息队列中取出一个消息,然后回调相应的消息处理函数(handlerMessage),执行完成一个消息后则继续循环,若消息队列为空,线程则会阻塞等待.因此不会退出.如下图所示: Handl

随机推荐