Android进程/线程管理——深入源码解读+分析
来源:互联网 发布:ppt软件有哪些 编辑:程序博客网 时间:2024/06/04 18:27
一. 为什么要了解进程/线程
从操作系统的角度,进程是操作系统管理的一个实体,操作系统为其定义了虚拟地址空间,进程之间的数据调度等。从应用开发的角度,我们写的程序就必须运行在一个进程实例中。同时对于进程,如果采用了多线程方法,也涉及到多线程的管理。所以学习并掌握一个平台,了解进程/线程是十分基础且必须的一件事情。
二 . 进程
一般来说,一个程序包对应于一个进程。(当然,这也不是不可改变的,我们可以通过NDK提供的本地接口,来fork子进程。)多个Activity共享一个主线程(ActivityThread),Service也是寄存于ActivityThread中的。Activity和Service一样,他们启动是都需要两个Binder线程的支持。
三. 线程
对于一个Activity必定会包含一个main thread,此外还有可能有一些binder thread。同时Zygote系统进程负责为每个新启动的应用fork新的进程。此外,Zygote为Activity创建的主线程是ActivityThread。
四. Handler,MessageQueue,Looper,ActivityThread, Thread
Looper不断从MessageQueue中获取Message(或者是Runnable),然后交由Handler处理。
Handler。(frameworks/base/core/java/android/os/Handler.java)
使用示例:(在主线程中创建一个Handler,在子线程中发送Message。后面有在子线程中使用Handler的例子)
定义handler
handler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);switch (msg.what) {case 0:popupWindowLogo.showAtLocation(logoPop,Gravity.CENTER_HORIZONTAL, 10, 60);break;case 1:popupWindowWelcome.showAtLocation(welcomePop,Gravity.CENTER_HORIZONTAL, 10, 100);break;case 2:if (null != popupWindowLogo && popupWindowLogo.isShowing())popupWindowLogo.dismiss();if (null != popupWindowWelcome&& popupWindowWelcome.isShowing())popupWindowWelcome.dismiss();break;case 3:initUserInfo();default:break;}}};
定义Thread
class PauseRun implements Runnable {@Overridepublic void run() {try {Thread.sleep(1000);handler.sendEmptyMessage(0);Thread.sleep(1000);handler.sendEmptyMessage(1);Thread.sleep(1000);handler.sendEmptyMessage(2);Thread.sleep(1000);handler.sendEmptyMessage(3);} catch (InterruptedException e) {e.printStackTrace();}}}
分析如下:
1)Handler将消息压入MessageQueue中。以上示例使用的是sendEmptyMessage()函数,除此之外,send系的函数还有sendMessageAtFrontOfQueue(Message msg); sendMessageAtTime(Message msg, long uptimeMillis); sendMessageDelayed(Message msg, long delayMillis);等。对应的还有post系的函数,包括post(Runnable r), postAtTime(Runnable r, long uptimeMillis)。
send和post二者主要的区别在于,send传递的直接是Message,而post传递的是其他类型的消息,如Runnable。post会先将该信息转换为Message,然后在通过send系的函数(先调用sendMessageDelayed,再调用sendMessageAtTime将消息压入MessageQueue)。
2)Thread和Handler的关系。从以上示例中,可以看出一个Thread可以对应于多个Handler。同样的,也可以从Thread类和Handler类之间的关系中看出
Looper{ MessageQueue mQueue; Thread mThread;}Handler{ MessageQueue mQueue; Looper mLooper; Callback mCallback;}MessageQueue{ Message mMessages;}Message{ Handler target;}
3)我们在Thread中不能直接操作UI元素,只能在Handler中进行处理。所以Handler也给我们提供了一种在多线程环境下操作UI元素的方式。
MessageQueue。(frameworks/base/core/java/android/os)
MessageQueue主要负责维护消息队列,其中提供了对该队列的各种操作:新建(构造函数+nativeInit()),元素入队(enqueueMessage(Message msg, long when)),元素出队(next()),删除元素(removeMessage(Handler h, int what, Object object)),销毁队列(removeMessage(Handler h, Runnable r, Object object))。
Looper。(frameworks/base/core/java/android/os)
Looper.prepare()源码如下:
public static void prepare() { prepare(true);//true表示可以退出; ActivityThread中调用的是perpareMainLooper(), 其中会调用perpare(false)。说明ActivityThread的loop不能退出。 } 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)); }
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
注:其中sThreadLocal的定义如下:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
ThreadLocal可以为每个线程维护变量,并使其成为该线程相关的独立副本。所以Looper利用ThreadLocal使得每个线程拥有彼此独立的Looper实例。
Looper.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.recycle(); } }
其中myLooper()用来获取在prepare()中建立的本线程相关的Looper实例:
public static Looper myLooper() { return sThreadLocal.get(); }
Handler类中存在下面几个成员变量:
Handler{ final MessageQueue mQueue; final Looper mLooper; final Callback mCallback;}我们在建立Handler实例的时候,就已经给这几个变量赋值了:
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; }所以,通过如下代码,就能使Looper, MessageQueue, Handler联系起来:(一般开发人员也是这么在用)
class LooperThread extends Thread { public Handler mHandler; public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here } }; Looper.loop(); } }
ActivityThread。(frameworks/base/core/java/android/app/ActivityThread.java)
ActivityThread是应用程序的主线程。在该主线程中主要的工作如下:
public static void main(String[] args) { SamplingProfilerIntegration.start(); // CloseGuard defaults to true and can be quite spammy. We // disable it here, but selectively enable it later (via // StrictMode) on debug builds, but using DropBox, not logs. CloseGuard.setEnabled(false); Environment.initForCurrentUser(); // Set the reporter for event logging in libcore EventLogger.setReporter(new EventLoggingReporter()); Security.addProvider(new AndroidKeyStoreProvider()); Process.setArgV0("<pre-initialized>"); Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } AsyncTask.init(); if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
其中prepareMainLooper()会建立主线程相关的Looper以及MessageQueue。
public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } }
对于主线程来说,其Looper是可以全局访问的(其他线程也可以访问):在Looper类中定义了指向主线程Looper的sMainLooper对象引用。
private static Looper sMainLooper; // guarded by Looper.class其他线程通过调用getMainLooper()函数即可访问:
public static Looper getMainLooper() { synchronized (Looper.class) { return sMainLooper; } }
所以,当前进程中的各个线程都可以访问主线程的消息队列。
Thread。(/*libcore/libdvm/src/main/java/java/lang/Thread.java*/)
Java中多线程采用抢占式调度。(另一种调度模式是分时调度:分时会平分CPU时间,抢占则根据优先级分配CPU时间)
synchronized。该关键字主要用于java多线程中同步,它可以保证只有一个线程执行其修饰范围内的部分。作用范围可以是一个代码块,一个方法,一个对象,一个类的字面常量(如 Person.class)(普通变量不得行)。
- 当其修饰一个对象时(synchronized(this)),只能有一个线程能执行synchronized修饰的同步代码块,其他线程须等到该线程执行完成后,才能进入并执行。对于类中的其他非同步代码块,其他线程可以访问。
- (接上)对于其他的同步代码,其他线程对其访问将处于阻塞状态。因为synchronized(this)修饰该对象,也就是说当前运行的线程获取了该对象的对象锁。其他所有线程只有得到了该对象锁才能访问该对象中的方法。
- synchronized方法块的出现是为了避免synchronized修饰一个大的方法而引发的程序运行效率降低。
- synchronized修饰对象实例和类的字面常量是不同的。后者范围更大,对于所有该类的实例都会产生同步效果,即,所有该类的实例共享一个对象锁。而前者的各个对象实例的对象锁是分离的。注:类的静态(static)synchronized函数也可以达到和类字面常量同样的效果,即,取得该类的全局锁。然而,这两种锁并不具有包含关系,他们只是作用的范围不一样,当两种同步语句作用于统一各类的不同方法时,多线程中是可以同时访问这两个方法的,即,并不具有同步关系。
//synchronized方法public synchronized void accessVal(int newVal); //synchronized方法块synchronized(syncObject) { //允许访问控制的代码 }
每个对象实例或者类都有一个锁。Synchronized关键字将某些方法,变量保护起来,防止多线程访问冲突,以免造成不可预期的程序错误。一个类(对象)可以比喻成一个房间,类(对象)中的方法好比是抽屉。synchronized方法是带锁的抽屉,所有的抽屉共用一把钥匙,且在房间门口放着,只有获得了对象锁才能访问该抽屉。所以对于想要访问带锁抽屉的人(线程)必须要先获取到门口的钥匙,等到访问抽屉结束,则要将钥匙归还,从而后面的人(线程)才可以继续访问带锁的抽屉。而非synchronized方法是不带锁的抽屉,所以大家都可以访问。
说到多线程,不得不说多线程协同工作的方法,wait(), notity(), notifyAll(), interrupt(), join(), sleep()等方法,是java中的多线程控制(同步)与调度方法,下面对他们进行详细的分析。
wait()
该方法是Object类的成员方法。也就是说java中所有的对象都可以调用此方法,类似于没有对象都对应于一个对象锁。所以每当我们谈论wait()方法都必须要跟对象锁挂钩。该线程进入等待状态后会释放该对象锁。
notify()/notifyAll()
该方法相对于wait()方法。使得等待状态的线程可以重新运行。值得注意的是,notify()方法是随机任意的选择一个在等待该object的线程来唤醒,具体看虚拟机的实现方式。而notifyAll()则是唤醒等待该object的全部线程。
以下是wait()和notifyAll()的使用示例:
程序大概意思:两个线程协同工作,操作整型num。当第一个线程对num操作到50时,线程1进入等待状态,并释放对象锁。此时线程2获得对象锁,并开始操作num,将其从100减到1。线程2完成该操作后,调用notifyAll()函数,唤醒线程1,让其完成余下工作:将num从50继续减到1。
class Third {int num = 100;boolean wait = true;String str = new String();public synchronized void noti() {notifyAll();wait = false;}public void A() {synchronized (this) {while (num > 0) {System.out.println(Thread.currentThread().getName()+ "this is " + num--);if (num == 50 && wait)try {wait();} catch (InterruptedException e1) {// TODO Auto-generated catch blocke1.printStackTrace();}}}}public void B() {synchronized (this) {num = 100;while (num > 0) {System.out.println(Thread.currentThread().getName()+ "this is " + num--);}num = 50;noti();}}}
class TxtThread implements Runnable {public TxtThread(Third obj) {third = obj;}Third third;public void run() {third.A();}}class TxtThread2 implements Runnable {public TxtThread2(Third obj) {third = obj;}Third third;public void run() {third.B();}}
public class Test {public static void main(String args[]) throws IOException,InterruptedException {Third th = new Third();TxtThread tt = new TxtThread(th);TxtThread2 t2 = new TxtThread2(th);new Thread(tt).start();new Thread(t2).start();}}总结:
- 对于wait(), notify(), notifyAll()的调用,必须对于同步代码,也就是说,调用这些方法前要获取到对象锁。
- 对于wait(), notify(), notifyAll()的调用,必须要对应于同一个对象锁,否则没有同步效果。
- 对于wait(), notify(), notifyAll()的调用,对象锁不能是类字面常量对应的锁。否则wait()方法会出错,同时notify()/notifyAll()方法也会出错。
interrupt()
比较好理解,此处不做说明。
join()
该方法主要用于保证线程运行的顺序。
示例如下:
public static void main(String args[]) throws IOException,InterruptedException { ThreadTest t1 = new ThreadTest("Thread1"); ThreadTest2 t2 = new ThreadTest2("Thread2"); ThreadTest3 t3 = new ThreadTest3("Thread3"); t3.start(); t3.join(); t2.start(); t2.join(3000); t1.start();}该例子可以保证,线程t2只有在线程t3运行结束后才能运行;线程t1会等待线程t2运行3000ms,如果超时,线程t1也会开始运行。
sleep()
也是使线程处于等待状态,于wait()不同,wait()一般是等待某个object(上面已经详细介绍过)。而sleep()则是等待一段时间,且sleep()不会释放锁(如果获得了的话)。- Android进程/线程管理——深入源码解读+分析
- Android源码自学 --- 进程和线程管理
- Android init进程——源码分析
- Android源码分析—深入认识AsyncTask内部机制
- android多进程深入分析
- 《深入理解Android内核设计思想》学习笔记:第五章 Android进程、线程管理
- 深入Android内核——Android init.c源码深入分析
- 【Linux Kernel 进程管理】深入分析fork
- 【进阶android】Volley源码分析——Volley的线程
- Glance源码深入解读
- OkHttp源码深入解读
- 第二部分—进程和线程管理
- Android Init进程源码分析
- Android uevent进程源码分析
- Android Init进程源码分析
- Android uevent进程源码分析
- Android Init进程源码分析
- Android Init进程源码分析
- Mybatis第六弹
- spring基础
- codeforces 540C Ice Cave【BFS】
- hdoj 1052 Tian Ji -- The Horse Racing
- Github Pages + Jekyll 独立博客一小时快速搭建&上线指南
- Android进程/线程管理——深入源码解读+分析
- 【c++】二分法求多项式单根
- Linux应用层查看系统时间的方法
- C++之tinyXML使用
- Mybatis第六弹
- 罗升阳:那两年炼就的Android内功修养
- 对某一个软件的注册分析
- Ubuntu中文输入法IBUS的安装
- GCC设置函数属性为constructor和destructor