关闭

Android 中进程、线程的概念

标签: android 进程 线程
1353人阅读 评论(0) 收藏 举报
分类:

一,Android 进程和线程

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

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

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

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


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


除了我们熟悉的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则是等待时间,一旦设置的时间到了就会被唤醒。










0
0
查看评论
发表评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场

Android进程和线程的区别

恩恩,今天参加阿里的面试,被问到Android中进程和线程的区别,着实把我伤脑了,表示平时开发的时候只知道线程,并没有去仔细关注过进程,所以下来特地去查了以下资料,先说说线程: (1)在Androi...
  • qq_17475155
  • qq_17475155
  • 2016-03-15 20:02
  • 4845

android 中 任务、进程和线程的区别

 任务、进程和线程     关于Android中的组件和应用,之前涉及,大都是静态的概念。而当一个应用运行起来,就难免会需要关心进程、线程这样的概念。在Android中,组件的动态运行,有一个...
  • sinat_29255093
  • sinat_29255093
  • 2016-07-03 22:40
  • 1044

android中的进程与线程的理解

一个android应用就是一个Linux进程,每个应用在各自的进程中运行,互不干扰,比较安全。 一个应用对应一个主线程,就是通常所说的UI线程,android遵守的就是单线程模型,所以说Ui操作不是...
  • yuxlong2010
  • yuxlong2010
  • 2011-12-06 12:47
  • 4445

Android进程与线程区别

进程,常被定义为程序的执行,可以把一个进程看成一个独立的程序,在内存中有其完备的数据空间和代码空间。一个进程所拥有的数据和变量只属于他自己。          线程,某一进程中一路单独运行的程序...
  • u014297278
  • u014297278
  • 2015-07-05 12:04
  • 1202

Android中的线程与进程之间的关系简单解释

一、Android中的进程 当一个程序第一次启动的时候,Android会启动一个LINUX进程和一个主线程。默认的情况下,所有该程序的组件都将在该进程和线程中运行。 同时,Android会为每个...
  • u011895534
  • u011895534
  • 2015-07-14 09:49
  • 1760

Android线程学习总结

Android进程线程学习总结 Android程序启动时,Android会分配一个LINUX进程和一个主线程。默认的情况下,所有该程序的组件都将在该进程和线程中运行。 可以在manifest中设置组件...
  • xiang_freedom
  • xiang_freedom
  • 2016-03-10 15:58
  • 512

Android的线程详解(几种实现方法及区别)

Android的线程详解(几种实现方法及区别)
  • qq979418391
  • qq979418391
  • 2016-01-19 13:27
  • 4269

android 多进程下的坑

android 多进程下的坑 一个进程的启动会分配一个android虚拟机,同时申请自己的堆栈空间,这样导致就可能存在异常的坑点。常见的案例即为android启动一个独立进程的service进行后台...
  • Sqlite_Developer
  • Sqlite_Developer
  • 2017-03-07 18:00
  • 254

Android进程和线程详解

当应用程序组件启动,且应用程序没有其他组件运行时,Android系统为这个应用程序启动一个新的Linux进程,并开始运行一个主线程。默认情况下,同一应用程序的所有组件都在同一进程的主线程中运行。如果应...
  • jennyliliyang
  • jennyliliyang
  • 2017-12-04 20:14
  • 190

Android中的线程和线程池及其源码分析:

一.基本的知识点: #线程:  什么是线程:   线程的几种状态       实现方式和区别: ##什么是线程   什么是进程  ...
  • kunkun5love
  • kunkun5love
  • 2017-03-10 01:47
  • 602
    个人资料
    • 访问:35862次
    • 积分:1724
    • 等级:
    • 排名:千里之外
    • 原创:133篇
    • 转载:38篇
    • 译文:0篇
    • 评论:9条
    最新评论