Android 中进程、线程的概念

时间:2022-02-18 14:57:33

一,Android 进程和线程

进程-Process 是程序的一个运行实例,以区别于“程序”这一静态的概念,线程-Thread则是cpu调度的基本单位。

Android中的程序和进程具体是个什么概念呢?

对于Android的应用程序来说,通常接触的都是Activity,Service,Receive,ContentProvider等组件,很容易认为系统的四大组件就是进程的载体,实际上,它们不能算是完整的进程实例,最多只能算是进程的组成部分,从AndroidManifest.xml中可以看出,在这些组件的最外围还有一个application 标签,四大组件只是 application 的零件。我们以一个有IDE向导生成的简单的工程来看看一个应用中的进程和线程。

在自动生成的源码中MainActivity.java中的onCreate()函数入口处加上断点,如图:

Android 中进程、线程的概念

那么,这个Activity启动后,会产生几个Thread 呢,如图:

Android 中进程、线程的概念

除了我们熟悉的main thread外,还有2个Binder Thread,因为应用程序启动过程中需要跟系统进程ActivityManagerService、WindowManagerService等通信,需要Binder线程。那么主线程是怎么产生?可以看到主线程是有ZygoteInit启动,经过一系列的调用最终执行了Activity本身的onCreate()函数,从这里知道,主线程就是ActivityThread。

frameworks/base/core/java/android/app/ActivityThread.java

public static void main(String[] args) {

Looper.prepareMainLooper(); 主线程会调用这个函数,普通线程调用prepare()

ActivityThread thread = new ActivityThread();

  thread.attach(false); 这个函数将于windowManagerService 建立联系,因为Activity是有界面显示的

if (sMainThreadHandler == null) {

  sMainThreadHandler = thread.getHandler();主线程对应的handler

}

 Looper.loop(); 主循环开始

}

在ActivityThread内部,还有一个ApplicationThread,这个ApplicationThread是应用进程跟ActivityManagerService进行跨进程通信的桥梁,比如AMS启动一个Activity,实际是先由ApplicationThread的scheduleLaunchActivity,然后到ActivityThread的performLaunchActivity。

实际上,所有应用程序的主线程都是Activity Thread,这里使用的组件是Activity,如果换成Service也是一样的,并且在一个应用程序中,主线程只有一个,对于同一个AndroidManifest.xml中定义的组件,除非有特别的指明(可以指定一个组件运行在某个进程空间,使用android:process属性,也可以在<application>标签中加入这个属性,指明想要依存的进程环境),否则它们都运行于同一个进程中。

二,Handler,MessageQueue,Runnable,Looper

1,Handler 代码路径:framework/base/core/java/android/os/Handler.java

public class Handler{

final MessageQueue mQueue;

final Looper mLooper;

IMessenger mMessenger;

final Callback mCallback;。。。

}

这里提到的几个主要元素的关系,简单说就是:Looper 不断从MessageQueue中取出消息message,然后交给Handler来处理。

每个Thread只对应一个Looper

每个Looper只对应一个MessageQueue

每个MessageQueue中有N个Message

每个Message最多指定一个Handler来处理事件

每个Thread可以对应多个Handler

Handler是经常会使用到的一个类,主要有两个方面的作用:一是处理Message,二是将某个message压入messageQueue中。Looper从MessageQueue中取出一个Message后,首先会调用Handler.dispatchMessage进行消息派发,默认的派发流程:

   /**

     *Handle system messages here.

     */

    publicvoid dispatchMessage(Message msg) {

        if(msg.callback != null) {

           handleCallback(msg);

        }else {

           if (mCallback != null) {

               if (mCallback.handleMessage(msg)) {

                    return;

               }

           }

           handleMessage(msg);

        }

    }

由这个函数看出,Handler的扩展子类可以通过重载dispatchMessage或handleMessage来改变它的默认行为。

处理优先级是消息本身的callback,最有优先权,其次是handler的callback,

其中msg的callback是在其生成Message对象时设置的,比如post(Runnable r)时,会先把runnable转成message,同时把runnable设置为msg的callback;

handler的callback是在实例化handler对象时。作为Handler的构造函数的参数,传入的一个实现了Handler.Callback的对象。


把一个消息压入消息队列的相应功能函数:

public final boolean post(Runnable r)

 public final boolean postAtTime(Runnable r, long uptimeMillis)

 public final boolean sendMessage(Message msg)

public final boolean sendMessageDelayed(Message msg, long delayMillis)。。。

以第一个函数为例:

    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }首先要把Runnable 对象,封装成一个Message,接着通过对应的send函数推送到messageQueue中,

    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }这里将Runnable对象设置为Message的回调。

整个过程中,message通过handler压入messagequeue中,然后由looper从messagequeue中取出消息,还是交给了handler来处理,为什么不直接操作,而是大费周折的转了这么一圈?这里体现了程序设计的一个良好习惯“有序性”。

2,MessageQueue,源码路径:frameworks/base/core/java/android/os/MessageQueue.java

就是一个消息队列,具有“队列”的一些常规操作:

新建队列:由本地方法nativeInit来完成。

元素入队: boolean enqueueMessage(Message msg, long when)

元素出队:Message next()

删除元素:void removeMessages(Handler h, int what, Object object)

3,Looper,源码路径:frameworks/base/core/java/android/os/

从Looper的源码看出,Looper中包含了一个MessageQueue队列。

使用Looper的线程,有三个步骤:

1)Looper的准备工作(prepare

2)创建处理消息的Handler

3)Looper开始运作(loop)

首先:Looper.prepare() Looper中有一个很重要的成员变量,   

 // sThreadLocal.get() will return null unless you've called prepare().

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();ThreadLocal对象是一个特殊的全局变量,因为它的“全局”性只限于自己所在的线程,而外界的线程(即使在同一进程)一概无法访问到他,这从侧面说明,每个线程的Looper都是独立的。它实际就是针对每个Thread的特定数据存储空间。

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

这个函数里的判断保证了一个Thread只会有一个Looper实例存在,sThreadLocal创建了一个只针对当前线程的Looper对象。

在使用Looper的线程里,肯定会创建Handler对象,所以mHandler是Thead实现类的成员变量,那么,Handler如何与Looper关联起来的呢?从Handler的构造函数分析:

public Handler()

public Handler(Callback callback)

public Handler(Looper looper, Callback callback) 

public Handler(Looper looper, Callback callback, boolean async)

之所以有这么多构造函数,是因为Handler有如下内部变量需要初始化:

 final MessageQueue mQueue;

 final Looper mLooper;

 final Callback mCallback;

以其中一个构造函数为例:

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

  mLooper = Looper.myLooper();  还是通过sThreadLocal.get来获取当前线程中的Looper实例。

mQueue = mLooper.mQueue; mQueue是Looper跟handler之间沟通的桥梁。这样Handler和Looper,MessageQueue就联系起来了,后续Handler执行post/send系列函数时,会将消息投递到mQueue也即是mLooper.mQueue中。

3,UI主线程 Activitythread

源码路径:frameworks/base/core/java/android/app/ActivityThread.java

   public static void main(String[] args) {
  ...
        Looper.prepareMainLooper();


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


        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        Looper.loop();

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

注意区别,普通线程调用prepare(),这里调用prepareMainLooper(),主线程的Handler是从当前线程中获得的thread.getHandler();。

    public static void prepareMainLooper() { //这个是Looper.java中的方法
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }prepareMainLooper也需要调用prepare,参数false表示该线程不允许退出。经过prepare后,myLooper就得到一个本地线程<ThreadLocal>的Looper对象,然后赋值给sMainLooper,从这里看,主线程和其他线程的Looper对象没有本质的区别。

作为主线程,它这么做的目的,就是其他线程如果要获取主线程的Looper,只需要调用getMainLooper()即可。

作为普通线程,它生成的Looper对象,只能在线程内通过myLooper()访问。

在来看下Looper.loop的是实现,也是Looper.java中的方法

 public static void loop() {
        final Looper me = myLooper(); 函数myLooper()则是调用sThreadLocal.get()来获取与之匹配的Looper实例,其实就是取出在prepare中创建的那个Looper对象。
        final MessageQueue queue = me.mQueue; //Looper中自带了一个MessageQueue

        for (;;) {
            Message msg = queue.next(); // might block从MessageQueue中取出消息,可能会阻塞,如果当前消息队列中没有消息,说明线程要退出了
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            msg.target.dispatchMessage(msg); 开始分发消息,这里的target就是一个handler,所以dispatch最终调用的是handler中的处理函数

            msg.recycleUnchecked(); 消息处理完毕,进行回收
        }
    }

在来看一个函数,Looper的构造函数:

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed); //new 了MessageQueue,也就是Looper在创建是,消息队列也同时被创建出来
        mThread = Thread.currentThread(); Looper与当前线程建立了对应关系
    }

4,Thread类

1)Thread类的内部原理,源码路径  libcore/libart/src/main/java/java/lang/Thread.java

public class Thread implements Runnable{

Thread实现了Runnable,也就是说线程是“可执行的代码”。

libcore/libuni/src/main/java/java/lang/Runnable.java  Runnable是一个抽象的接口,唯一的方法就是run方法

public interface Runnable {
    /**
     * Starts executing the active part of the class' code. This method is
     * called when a thread is started that has been created with a class which
     * implements {@code Runnable}.
     */
    public void run();
}

通常我们是这样使用Thread的:

方法1,定义一个类继承自Thread,重写它的run方法,然后调用start

MyThread thr= new Mythread();

thr.start();

方法2,直接实现Runnable,

new Thread(Runnable target).start();

两种方法都是通过start启动,它会间接调用run方法,

    public synchronized void start() {
        checkNotStarted();
        hasBeenStarted = true;
        nativeCreate(this, stackSize, daemon); 这里是真正创建一个cpu线程的地方,在此之前,一直都是运行在“老线程”中,实际上在新线程中运行的只有Run方法。
    }

2)Thread的休眠与唤醒,来看一下与此相关的控制方法:

首先是:wait notify notifyAll,这三个函数是Object类定义的,意味着它们是任何类的共有“属性”,下面的方法是Handler.java中的内部类:BlockingRunnable的,涉及到等待时,会调用它

        public boolean postAndWait(Handler handler, long timeout) {
            if (!handler.post(this)) {
                return false;
            }
            synchronized (this) {
                if (timeout > 0) {
                    final long expirationTime = SystemClock.uptimeMillis() + timeout;
                    while (!mDone) {
                        long delay = expirationTime - SystemClock.uptimeMillis();
                        if (delay <= 0) {
                            return false; // timeout
                        }
                        try {
                            wait(delay);
                        } catch (InterruptedException ex) {
                        }
                    }
                } else {
                    while (!mDone) {
                        try {
                            wait();
                        } catch (InterruptedException ex) {
                        }
                    }
                }
            }
            return true;
        }这个函数在Handler中的作用就是“投递并等待”,所以函数开头就把一个runnable(this)post到了handler所在的looper中。如果timeout大于0,说明是有条件限制的等待,这样可以避免异常时间下的“死等”;如果timeout等于0,无限期等待,直到有人来唤醒他。线程进入等待调用的是wait(),唤醒他就是notify/notifyAll,那么什么时候执行的唤醒操作呢?

    private static final class BlockingRunnable implements Runnable {
        public BlockingRunnable(Runnable task) {
            mTask = task;
        }

        @Override
        public void run() {
            try {
                mTask.run();
            } finally {
                synchronized (this) {
                    mDone = true;
                    notifyAll(); BlockingRunnable 对象在执行run函数时,同时也做了个特殊的操作,通知所有在等待的人,我运行OK了。
                }
            }
        }

然后,interrupt,如果说wait是一种“自愿”的行为,那么interrupt就是*的了,调用一个线程的interrupt这个方法,就是中断它的执行过程。

join方法的几个原型:

 public final void join()

 public final void join(long millis)

public final void join(long millis, int nanos)

比如:Thread t1;Thread t2;

t1.start();

t1.join();

t2.start(); 它希望的目的是只有当t1线程执行完成时,才接着执行后面的t2.start(),这样就保证了两个线程的顺序执行。带有参数的join多了一个限制,假如在规定的时间内t1没有执行完成,那么会继续执行后面的语句,防止无限等待。

最后,sleep方法,它和wait类似,都属于自愿的行为,只是wait是等待某个object,而sleep则是等待时间,一旦设置的时间到了就会被唤醒。