Android面试七(线程及线程间通信)

目录

一,线程基本概念

二,异步消息机制Handler

三,面试时会问到的问题

 


一,线程基本概念

1,线程的概念

线程是进程中的多条执行线路.

2.开启线程的三种方法
第一种:继承Thread类,覆盖run方法,使用start方法开启线程,使用简单。

第二种:实现Runnable接口,重写run方法,创建runnable实例作为target,传入Thread中,即new Thread(runnable).start();这样耦合度低,类的扩展性更好。

第三种:匿名内部类new Thread(){...}或者new Thread(new Runnable(){...})方法体内重写run方法。

run():直接调用该方法还是单线程,是同步执行,由main主线程来调用run方法。

start():调用start会通知线程规划器,此线程已准备就绪,等待调用线程对象的run方法。系统会安排时间来调用Thread中的run方法。

停止线程的方法:通过设置一个flag标记,run方法返回线程结束;调用interrupt方法。

3,线程的状态

new:新建状态,new出来了,但还没有调用start;

runnable:可运行状态,调用start进入可运行状态,可能运行也可能没有运行,取决于操作系统的调度;

blocked(阻塞状态):被锁阻塞,暂时不活动,当线程进入synchronized关键字修饰的方法或者代码块获取锁时的状态;

waitting(等待状态):不活动,不运行任何代码,等待线程调度器调度,当wait或者sleep后进入该状态;

timed_waitting:超时等待,在指定时间自行返回;

terminated(结束).:包含正常终止和异常终止。

通过getState()方法获取状态

Android面试七(线程及线程间通信)_第1张图片

4,线程的控制方法(线程调度方法)

线程调度器是一个操作系统服务,负责为Runnable状态的线程分配CPU时间。

1,setName(String name):设置线程名称

2,setPriority(int num):设置优先级,默认5,最大10;优先级高的在运行时会有优先权,但这不会保证高优先级的线程会在低优先级的线程前执行。

3,Thread.sleep():睡眠,睡眠后进入blocked(阻塞)状态;

4,t1.join():是Thread的方法,合并线程,就是将t1线程合并到调用join方法的线程t中,则t线程执行到join方法时必须等待t1线程执行完成后,才能继续运行,这样就同步运行了,而不是异步。

public class ThreadJoinDemo {

    public static void main(String[] args) {
        final Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(System.currentTimeMillis()+"首先执行线程1");
            }
        });

        final Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    thread1.join();
                    Thread.sleep(1000);
                    System.out.println(System.currentTimeMillis()+"然后执行线程2");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    thread2.join();
                    Thread.sleep(1000);
                    System.out.println(System.currentTimeMillis()+"最后执行线程3");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        System.out.println(System.currentTimeMillis()+"虽然开启了线程3,但需等待线程2执行完毕");
        thread3.start();
        System.out.println(System.currentTimeMillis()+"虽然开启了线程2,但需等待线程1执行完毕");
        thread2.start();
        System.out.println(System.currentTimeMillis()+"开启线程1");
        thread1.start();
    }
}

执行结果,无论三个线程的开启顺序咋样,都是会限制性线程1,然后线程2,最后线程3:

1588304517995虽然开启了线程3,但需等待线程2执行完毕
1588304517996虽然开启了线程2,但需等待线程1执行完毕
1588304517996开启线程1
1588304518020首先执行线程1
1588304519024然后执行线程2
1588304520025最后执行线程3

5,wait():是Object的方法,作用是让当前线程进入waiting(等待)状态,同时,wait也会让当前线程释放它所持有的锁。直到其他线程调用此对象的notify()方法或者notifyAll()方法,当前线程就会被唤醒,进入就绪状态;

6,notify()和notifyAll():Object的方法,唤醒当前对象上的等待的单个线程/所有线程。

7,wait(long timeout):让当前线程处于blocked(阻塞)状态,直到其他线程调用此对象的notify()方法或notifyAll()方法,或者超过指定的时间量,当前线程被唤醒进入就绪状态。

(wait方法、notify方法都被定义在Object类中,这样Java的每一个类都有用于线程间通信的基本方法)

public class Test {
    private static Object myLock1 = new Object();//第一个锁
    private static Object myLock2 = new Object();//第二个锁
    private static boolean t1Run = false;//线程1是否运行的标志
    private static boolean t2Run = false;//线程2是否运行的标志
    public static void main(String[] args) {
        final Thread thread1 = new Thread(() -> {
            synchronized (myLock1){//获得锁1
                System.out.println(System.currentTimeMillis()+"首先执行线程1");
                t1Run = true;
                myLock1.notify();//唤醒在锁1上等待的线程
            }

        });

        final Thread thread2 = new Thread(() -> {
            synchronized (myLock1){
                try {
                    if(!t1Run) {//如果线程1还没有执行
                        System.out.println(System.currentTimeMillis() + "线程1还没有运行,线程2先等待");
                        myLock1.wait();//线程2进入等待状态,同时释放所持有的锁1
                        System.out.println(System.currentTimeMillis() + "线程2等待线程1执行结束了");
                    }
                    synchronized (myLock2){//获取锁2,在这步前线程1肯定已经执行结束了
                        Thread.sleep(1000);
                        System.out.println(System.currentTimeMillis()+"然后执行线程2");
                        t2Run = true;
                        myLock2.notify();//唤醒锁2对象上的线程
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        });

        Thread thread3 = new Thread(() -> {
            synchronized (myLock2){
                try {
                    if(!t2Run){
                        System.out.println(System.currentTimeMillis()+"线程2还没有运行,线程3先等待");
                        myLock2.wait();
                        System.out.println(System.currentTimeMillis() + "线程3等待线程2执行结束了");
                    }
                    System.out.println(System.currentTimeMillis()+"最后执行线程3");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        });

        thread1.start();
        thread3.start();
        thread2.start();
    }
}

执行结果:(打印顺序不一定,但一定是线程1,2,3依次执行)

1588347849498线程2还没有运行,线程3先等待
1588347849498首先执行线程1
1588347850499然后执行线程2
1588347850500线程3等待线程2执行结束了
1588347850500最后执行线程3

5,线程的同步锁

synchronized:可以同步代码块,方法;

最好不要锁定run方法,因为只有等当前线程执行完run方法释放锁后,其他线程才会执行run方法。在run方法里面加锁也不好,因为这样每个线程都会有一个锁,不会实现同步代码块的。

volatile:设置的是变量的可见性,所有线程都会直接读取该变量并且不会缓存它,保证线程读取的变量是和内存中。

6.线程池的好处

https://juejin.im/post/5b3cf259e51d45194e0b7204

(1)重用线程池的线程,避免总是创建销毁线程浪费时间,浪费性能。

(2)控制线程最大并发数,避免大量线程因为抢占资源造成堵塞现象。

(3)简单管理线程,可以定时执行或者间隔时间段执行

来源于Java中的空接口Executor,线程池真正实现的是ThreadPoolExecutor

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime, TimeUnit unit,BlockingQueue workQueue,ThreadFactory threadFactory) {
         this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, defaultHandler);
}

corePoolSize:核心线程数,默认情况下,及时是闲置状态,也一直存活。当把ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true时,当闲置的核心线程等待新任务时间超过keepAliveTime时,会被自动回收。

maximunPoolSize:最大线程数,超过这个数量时,新任务会被阻塞。

keepAliveTime:非核心线程数闲置的时间

unit:keepAliveTime的时间单位

workQueue:线程池中的任务队列

threadFactory:线程工厂,为线程池提供创建新线程的功能。

种类:

FixedThreadPool(可控最大并发数线程池):线程数量固定,即使闲置状态也不会被回收,除非线程池关闭,只有核心线程,没有非核心线程。

newFixedThreadPool(int nThreads, ThreadFactory threadFactory){
return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,
                                          new LinkedBlockingQueue(),threadFactory);
}

CachedThreadPool(可回收缓存线程池):线程数量不固定,只有非核心线程,当线程都活跃时,就创建新线程,否则利用空闲线程,空闲线程超过60s时会被回收。适合处理大量且耗时较少的任务。

newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,
                                       new SynchronousQueue());
}

scheduleThreadPool(支持定时与周期性任务的线程池):核心线程固定,非核心线程不固定,当非核心线程空闲时,立即被回收。适合执行定时任务或有固定周期的任务。

SingleThreadExecutor(单线程化线程池):只有1个核心线程,没有非核心线程,确保所有任务能够在同一线程且按顺序执行,不用考虑同步问题

    private static void testExecutor(){
        //首先创建线程池,这儿拿单线程化线程池举例
        ExecutorService executorService = Executors.newSingleThreadExecutor();

        Thread thread1 = new Thread(() -> {
            System.out.println(System.currentTimeMillis()+"首先执行线程1");
        });
        Thread thread2 = new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(System.currentTimeMillis()+"然后执行线程2");
        });
        Thread thread3 = new Thread(() -> {
            System.out.println(System.currentTimeMillis()+"最后执行线程3");
        });
        //提交线程
        executorService.submit(thread1);
        executorService.submit(thread2);
        executorService.submit(thread3);
        //关闭线程池
        executorService.shutdown();
    }

输出结果:会按顺序依次执行

1588349278957首先执行线程1
1588349279959然后执行线程2
1588349279959最后执行线程3

7,handlerThread的使用和原理

继承Thread,相当于在Thread中生成了一个loop循环器,可以进行Looper循环,即handlerThread=handler+Thread+looper。

HandlerThread继承Thread后,重写了run方法,可以看到,显示通过Looper.prepare()创建线程,然后采用同步代码,保证线程安全,一个线程没有执行完,则looper就是阻塞状态,执行完后,通过notifyAll()方法通知线程进入就绪状态:

public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

通知的就是getLooper()方法,如果Looper轮询器还在工作,则让线程等待,知道run方法执行notifyall()通知后,,才会返回Looper。

public Looper getLooper() {
    if (!isAlive()) {
        return null;
    }
    
    // If the thread has been started, wait until the looper has been created.
    synchronized (this) {
        while (isAlive() && mLooper == null) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }
    }
    return mLooper;
}
 

特点:

(1)通过获取handlerThread的loop对象,传递给handler对象,在handlerMessage方法中执行异步任务

(2)不会阻塞,减少对性能消耗。但是不能同时多任务处理,需等待处理。

(3)和线程池注重并发不同,只是串行队列,内部只有一个线程。

使用:

(1)先写一个类TestHandler继承Handler,并重写handlerMessage方法

(2)初始化HandlerThread,并启动

        HandlerThread handlerThread = new HandlerThread("test_thread");
        handlerThread.start();//必须得先启动线程,不然没有创建handler,也就没有Looper对象
        TestHandler handler = new TestHandler(handlerThread.getLooper());
        handler.sendEmptyMessage(0);
(3)当不需要的时候记得调用HandlerThread.quit()方法移除可能未完成的操作避免内存泄露等危险养成一种良好的习惯。

8.IntentService和Service有什么区别

IntentService继承Service,两个都是服务。IntentService可以执行耗时操作,Service不能直接执行耗时操作,否则会ANR。

(1)创建一个测试类继承IntentService并重写onHandleIntent方法,用来处理相应事务

public class TestIntentService extends IntentService {..重写onHandleIntent(Intent intent)方法..}

(2)正常启动service即可
    startService(new Intent().setClass(MainActivity.this, TestIntentService.class).putExtra("content", "传输的数据"));

(3)在IntentService的onCreate方法中会开启一个HandlerThread线程,内部loop会创建一个ServiceHandler,在onstart方法中,会把intent转到ServiceHandler中,

    public void onCreate() {
        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

public void onStart(Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

(4)ServiceHandler继承Handler,重写了handleMessage方法,可以看到调用了我们一开始重写的onHandleIntent方法,执行完毕后stopSelf结束service。

private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }

Service不能直接执行耗时操作,否则会ANR,只能新开线程执行耗时操作。

二,异步消息机制Handler

1,异步消息机制

总共由四部分组成:handler,message,MessageQueue,Looper

(1)在主线程中创建 Handler 对象,并重写 handleMessage() 方法。

(2)子线程进行 UI 操作时,创建 Message 对象,通过第一步创建的Handler 发送消息,handler.sendMessage(message),handler将消息发送到MessageQueue中。

(3)Looper 通过loop()循环从 MessageQueue 中取出待处理消息。

(4)looper将取出的message分发回 Handler 的 handleMessage() 方法中处理。

原理:

https://blog.csdn.net/fnhfire_7030/article/details/79518819

2,什么是handler

handler通过发送和处理Message和Runnable对象来关联相对应线程的MessageQueue。

(Message传递的是消息,MessageQueue是一个消息队列,Handler机制里Lopper轮询器会不断的从消息队列里获取消息,交给handler处理消息)

(1),可以让对应的message和runnable在未来的某个时间点进行相应的处理

(2),让自己想要处理的耗时操作放在子线程,让更新UI的操作放在主线程。

3,handler的使用(post(runnable)和 sendMessage(mesage))

(1),post(runnable)

(1.1)首先创建Handler()

Handler handler = new Handler();

(1.2)然后新开线程,创建Runnable对象,通过post方法,将runnable传到handler当中,handler会在合适的时候让主线程运行runnable更新UI的代码。

class DownloadThread extends Thread{
    @Override
    public void run() {
        try{
            System.out.println("开始下载文件");
            Thread.sleep(2000);
            System.out.println("文件下载完成");
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    //更新UI的一些操作
                }
            };
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

(1.3)在主线程中开启DownloadThread线程

DownloadThread downloadThread = new DownloadThread();

downloadThread .start();

(2),sendMessage(mesage)

(2.1)创建handler,并复写handleMessage方法

private Handler handler = new Handler(){
    @Override
    public void handleMessage(@NonNull Message msg) {
        switch (msg.what){
            case 1:
                //执行UI操作
                break;
        }
    }
};

(2.2)在新开线程中创建message对象,通过what,org1,org2进行赋值,然后handler发送该消息,handler通过其handleMessage的方法进行UI的处理

class DownloadThread extends Thread{
    @Override
    public void run() {
        try{
            System.out.println("开始下载文件");
            Thread.sleep(2000);
            System.out.println("文件下载完成");
            Message msg = new Message();
            msg.what = 1;//自定义的识别码,handler可以识别不同的额msg
            handler.sendMessage(msg);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

(2.3)在主线程中开启DownloadThread线程

DownloadThread downloadThread = new DownloadThread();

downloadThread .start();

4,handler的内部机制原理

Android面试七(线程及线程间通信)_第2张图片

Handler:

创建handler对象时,则持有了当前线程的Looper和MessageQueue对象

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

Looper:

每个线程都有的,通过prepare()创建Looper对象,保存在本线程的ThreadLocal中,保证每个线程的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));
    }

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

通过loop()开启循环,不断调用MessageQueue.next()方法获取Message,进行消息的分发msg.target.dispatchMessage(msg),msg这个属性target就是Handler,其实就是通过handler的dispatchMessage()方法发送消息给消息队列,最终调用handleMessage()。
 

5,handler引起的内存泄漏

当Activity要回收时,handler没有被回收,对Activity的引用也不会释放。即静态内部类持有外部类的匿名引用,导致外部Activity无法释放。

解决方法:handler内部持有外部activity的弱引用,并把handler改为静态内部类,在activity的onDestroy方法中调用mHandler.removeCallback()方法。

private Handler mHandler = new WeakHandler(this);

static class WeakHandler extends Handler {
    private WeakReference mActivity;
    TestHandler(Activity activity) {
        mActivity = new WeakReference<>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        Activity activity = mActivity.get();
        if (activity != null) {
            //TODO:
        }
    }
}

6,handlerThread

1,产生背景:

开启Thread子线程进行耗时操作时,多次创建和销毁线程很耗系统资源。

2,本质

handler+Thread+Looper

本质上是一个线程类,继承了Thread;

但他有自己的内部Looper对象,可以进行looper循环;

通过获取HandlerThread的looper对象传递给Handler对象,可以在handleMessage方法中执行异步任务;

优点是不会堵塞,减少对性能的消耗,缺点是不能同时进行多任务处理,处理效率低。

与线程池注重并发不同,HandlerThread是一个串行队列,背后只有一个线程。

三,面试时会问到的问题

1,简要说下进程和线程的区别

进程是程序运行和资源分配的基本单位,一个程序至少有一个进程,进程在执行过程中拥有独立的内存单元;

线程是CPU调度和分派的基本单位,是能独立运行的基本单位,一个进程至少有一个线程,多个线程共享进程的内存资源,但每个线程都拥有单独的栈内存用来存储本地数据。

2,Thread类中的start()方法和run()方法的区别

start方法用来启动线程,内部调用了run方法,run方法只是直接调用run方法,并没有开启新线程。

3,在多线程中,什么是上下文切换?

上下文切换时存储和恢复CPU状态的过程,它使得线程执行能够从中断点恢复执行。上下文切换是多任务操作系统和多线程环境的基本特征。

4,有三个线程,如果保证线程顺序执行?

(方法1)在线程3调用t1.join()方法,在线程2调用t1.join()方法。

(方法2)使用synchronized锁住Object对象,然后使Object的wait方法使当前线程进入等待状态并释放锁(对象监视器),在要执行的线程执行完任务后调用notify方法,等待该线程剩余代码执行完毕后释放锁(对象监视器),然后唤醒之前进入等待状态的线程。

(方法3)使用SingleThreadExecutor(单线程化线程池)

5,产生死锁的条件

死锁指两个以上的线程永远阻塞的状态。

(1)互斥条件:一个资源每次只能被一个进程使用

(2)请求与保持条件:一个进程因请求资源而阻塞时,对已经获得的资源保持不放。

(3)循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

6,什么是线程局部变量ThreadLocal?

ThreadLocal用于创建线程的本地变量,属于线程自身所有,这是一种实现线程安全的方式。一个对象的所有线程都会共享它的全局变量,所以这些线程是不安全的,我们可以使用同步技术。但是当我们不想使用同步的时候,可以选择ThreadLocal变量,因为ThreadLocal是一种以空间换时间的做法在每个Thread内部维护了一个ThreadLocal,ThreadLocalMap把数据进行隔离,数据不共享,自然就不会有线程安全的问题了。

7,简要说下线程间通信的原理

通过Handler机制

(1)在Handler构造方法中:Looper通过Looper.myLooper()方法获取一个Looper对象,通过mLooper.mQueue拿到与这个Looper对应的MessageQueue;

(2)然后开启死循环,对消息队列进行不停的获取,获取到一个消息后,交给Message.target.dispatchMessage()方法对消息进行处理。

8,post和sendMessage的区别

sendMessage一般和handleMessage配合使用,在sendMessage中发送消息,在handleMessage中接收消息并进行UI的处理;

post方法直接在runnable的run方法中更新UI,但两者本质上没有区别,都是发送消息到消息队列中,只是post的方式更简单些。

public final boolean post(Runnable r)
{
   return  sendMessageDelayed(getPostMessage(r), 0);
}
//获得了message实例,将r赋给callback,接下来还是和sendMessage一致的操作,进入sendMessageDelayed
 private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    //最终还是执行到sendMessageAtTime这个方法里面
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

//接下来看下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);
}

接下来看下post和sendMessage是如何进行消息的处理的

public void dispatchMessage(Message msg) {
  //如果是post,callback不为空,直接进入handleCallback
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
   //如果是sendMessage,且创建handler时没有传入callback,则callback为空,直接进入handleMessage,也就是我们自己复写的处理Message的方法
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

//直接run并不会启动新线程,所以这就是post的runnable里面可以直接更新UI的原因
   private static void handleCallback(Message message) {
    message.callback.run();
}

9,为什么主线程一直loop死循环但不会ANR(Application Not Responding)?

首先说下造成ANR的原因:

(1)当前事件没有机会处理:主线程正在处理前一个事件,没有及时的完成或者Looper被某种原因阻塞住了。

(2)当前事件正在处理,但没有及时完成。

所以为了避免ANR,采用了Handler消息处理机制,耗时操作在子线程运行。

先看下主线程中的Looper.loop()循环,在主线程入口类ActivityThread.java的入口函数中:

//先看下入口类的入口函数main函数
public static final void main(String[] args) {
    ...
    //创建Looper和MessageQueue
    Looper.prepareMainLooper();
    ...
    //轮询器开始轮询
    Looper.loop();
    ...
}

//再看下Looper.loop()方法
   while (true) {
       //取出消息队列的消息,可能会阻塞
       Message msg = queue.next(); // might block
       ...
       //解析消息,分发消息
       msg.target.dispatchMessage(msg);
       ...
    }

可以看到,如果主线程没有loop循环,那么主线程执行完毕就会退出,那谁来等待用户的操作啊,比如触碰屏幕等!

也就是说,ActivityThread的main方法主要就是做消息循环,我们的代码就是在这个循环里面去执行,一旦退出循环,那么这个程序也就退出了。

也就是说,Android是由事件驱动的,loop()不断的接收事件、处理事件,这样每一个点击触摸或者activity的生命周期都是运行在Looper.loop()的控制下,所以这儿不能混淆概念,Looper.loop()方法本身不会引起主线程的阻塞,只能说当某一个消息或者对消息的处理阻塞了Looper.loop(),才会造成ANR。

而且,主线程的Looper从消息队列读取消息,当读取完所有消息时,主线程会处于阻塞状态,当系统唤醒主线程如点击事件传递给了主线程或者其他线程通过Handler向主线程的消息队列中存放了一条消息时,主线程会被唤醒。当主线程处理完成后,会再次进入睡眠状态,所以loop循环不会对CPU性能造成过多的消耗。

看下handleMessage()的部分代码:

 public void handleMessage(Message msg) {
        if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
        switch (msg.what) {
            case LAUNCH_ACTIVITY: {
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
                r.packageInfo = getPackageInfoNoCheck(r.activityInfo.applicationInfo, r.compatInfo);
                handleLaunchActivity(r, null);
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            }
            break;
            case RELAUNCH_ACTIVITY: {
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
                ActivityClientRecord r = (ActivityClientRecord) msg.obj;
                handleRelaunchActivity(r);
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            }
            break;
            case PAUSE_ACTIVITY:
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
                handlePauseActivity((IBinder) msg.obj, false, (msg.arg1 & 1) != 0, msg.arg2, (msg.arg1 & 2) != 0);
                maybeSnapshot();
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                break;
            case PAUSE_ACTIVITY_FINISHING:
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
                handlePauseActivity((IBinder) msg.obj, true, (msg.arg1 & 1) != 0, msg.arg2, (msg.arg1 & 1) != 0);
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                break;
            ...........
        }
    }

可以看到,activity的生命周期都是依靠主线程的Looper.loop,当收到不同的Message时采用相应的措施,如果这个消息处理时间过长,比如在onCreate()方法处理耗时操作,那么下一次的消息比如用户点击的消息就不能处理了,这时候整个循环就会产生卡顿,时间一长就ANR了。

 

 


 

你可能感兴趣的:(Android面试)