欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android 主线程到底是什么、如何抛出ANR

程序员文章站 2022-07-14 17:34:03
...

作为 android 者对主线程的操作在开发中是非常频繁的,主线程是非常重要的线程,因为我们所有的UI界面都是通过主线程更新、绘制的。所以我们要足够了解他。从源码的角度看看为何ui必须在主线程更新、直接创建的handler为何就把线程给切换到了主线程呢?、主线程阻塞anr是如何抛出的?

目录

  1. Android 主线程是在哪里创建的?
  2. 在android开发中为什么子线程不能更新UI界面?
  3. 直接创建的Handler接收到的消息是运行在那个线程中的?
  4. android 主线程是如何捕获ANR异常的?

1.Android 主线程是在哪里创建的?

android的主线程是在 app 进程创建时候就创建了,即当系统 调用 fork()方法之后就会创建一个新的进程和进程中地一个线程称为主线程。

2.在android开发中为什么子线程不能更新UI界面?

代码主要在ViewRootImpl中,从构造方法开始

public ViewRootImpl(Context context, Display display) {
      
       // 保存当前创建 ViewRootImpl 对象的线程
        mThread = Thread.currentThread();

对象创建完成之后在调用view root 遍历方法/ 更新ui时 都会去检测线程是否是创建ViewRootImpl对象的线程。

 void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }


  @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            // 检测
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }

其实更新UI界面并不是非的在main线程中,子线程不能更新ui界面的原因是因为当我们进程fork()并启动时创建的地一个线程就是main线程,然后就调用了ActivityThread 中的 main()方法开始loop循环处理handler接受到的消息。application 创建完之后会等待 AMS 回调ApplicationThread 跨进程接口,当收到Launcher activity信号后application就会创建activity然后一直到ViewRootImpl中通知WMS创建窗口并且更新绘制各个子View控件,在ViewRootImpl的构造方法内已经保存了当前创建的 thread 对象,由于ViewRootImpl创建是在main线程中的,所以thread这个对象其实就是main线程。当我们再次调用更新View的方法时例如invalidate()/ requestLayout()等触发更新界面时都会调用checkThread()方法去验证当前的 thread 是不是ViewRootImpl构造方法中保存的那个线程,不是就会抛出异常。android 加检测线程的原因其实是为了防止多线程访问下触发线程安全问题,但是为了降低锁带来的开销和系统的复杂度,就强制限制必须是我们创建 ViewRootImpl 对象的线程。所以如果我们想在子线程中更新UI 其实两种方式

  1. 我们创建ViewRootImpl时在子线程中创建。
  2. 反射修改checkThread()方法的结果。 

3.直接创建的Handler接收到的消息是运行在那个线程中的?

例如:android ActivityThread 中创建的 Handler没有指定当前的looper对象那他是怎么保证能通知到主线程的?对应的 HandlerThread 中创建的Handler 却指定了一个looper?

        // 直接 new handler 调用无参构造方法
        Handler handler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        };

直接 new handler 调用无参构造方法

   public Handler(Callback callback, boolean async) {
        // 最终使用的是当前线程的 looper       
        mLooper = Looper.myLooper();
    }

最终 Looper.myLooper(); 会到ThreadLocal 中去获取当前线程里面的 Looper 对象,所以直接new 空参构造方法,当前线程切换到的是 Handler 对象被创建的线程中。

因为ActivityThread中先调用了Looper.prepare()这时候会为当前线程创建Looper和MessageQueue对象,再创建的Handler时候当前Handler就会去调用当前的Looper和MessageQueue对象。
HandlerThread 在当前线程中也是先调用了Looper.prepare(),但是Handler的创建是交给调用者调用方法触发的,这个方法可以在任何线程中调用,所以这时候在创建Handler对象时候就需要指定 传入 looper对象。也可以调整一下Handler的创建位置,放到run()方法内调用就不用传looper了,但是run()中创建对象不是好的编程方式。

4.android 主线程是如何捕获ANR异常的?

一个anr的产生主要涉及到一下场景:

  1. Service Timeout:服务在20s内未执行完成;
  2. BroadcastQueue Timeout:比如前台广播在10s内执行完成
  3. ContentProvider Timeout:内容提供者执行超时
  4. inputDispatching Timeout: 输入事件分发超时5s,包括按键分发事件的超时。

以上四种场景均会导致anr的产生,这里之分析 Service Timeout 的anr,其他场景类似。在service启动的时候会去检测anr,当我们startService() 之后,系统会调到 realStartServiceLocked() 方法

private final void realStartServiceLocked(ServiceRecord r,
        ProcessRecord app, boolean execInFg) throws RemoteException {
    ...
    //发送delay消息(SERVICE_TIMEOUT_MSG)
    bumpServiceExecutingLocked(r, execInFg, "create");
    try {
        ...
        //最终执行服务的onCreate()方法
        app.thread.scheduleCreateService(r, r.serviceInfo,
                mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                app.repProcState);
    } catch (DeadObjectException e) {
        mAm.appDiedLocked(app);
        throw e;
    } finally {
        ...
    }
}

bumpServiceExecutingLocked()中

private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) {
    ... 
    scheduleServiceTimeoutLocked(r.app);
}

void scheduleServiceTimeoutLocked(ProcessRecord proc) {
    if (proc.executingServices.size() == 0 || proc.thread == null) {
        return;
    }
    long now = SystemClock.uptimeMillis();
    Message msg = mAm.mHandler.obtainMessage(
            ActivityManagerService.SERVICE_TIMEOUT_MSG);
    msg.obj = proc;
    
    //当超时后仍没有remove该SERVICE_TIMEOUT_MSG消息,则执行service Timeout流程
    mAm.mHandler.sendMessageAtTime(msg,
        proc.execServicesFg ? (now+SERVICE_TIMEOUT) : (now+ SERVICE_BACKGROUND_TIMEOUT));
}

最后进入当前进程的 ActivityThread 的 handleCreateService(CreateServiceData data) 

   private void handleCreateService(CreateServiceData data) {
        ...
        java.lang.ClassLoader cl = packageInfo.getClassLoader();
        Service service = (Service) cl.loadClass(data.info.name).newInstance();
        ...

        try {
            //创建ContextImpl对象
            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
            context.setOuterContext(service);
            //创建Application对象
            Application app = packageInfo.makeApplication(false, mInstrumentation);
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManagerNative.getDefault());
            //调用服务onCreate()方法 
            service.onCreate();
            
            //取消AMS.MainHandler的延时消息
            ActivityManagerNative.getDefault().serviceDoneExecuting(
                    data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
        } catch (Exception e) {
            ...
        }
    }

Service 的启动创建流程和 activity 的启动创建流程非常相似,当创建完成之后调用service的onCreate()方法、然后调用AMS.MainHandler取消延时消息。如果启动service 和 调用service 的 onCreate() 非常耗时就会抛出anr异常。