作为 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异常。

 

更多相关文章

  1. 调试方法-Unity3D对各个target平台的模拟
  2. Android 的线程跟线程池
  3. Android读取工程内嵌资源文件的两种方法
  4. Android Studio:10分钟教会你做百度地图定位!并解决SDK22中方法报
  5. Android——Json和Gson分别是什么,以及Json 数据的解析方法
  6. Android 性能优化——通过线程提高性能
  7. android开发打印票据或文档的方法:android打印文档的类

随机推荐

  1. [置顶] 调用Android发短信接口Intent.ACT
  2. Internal error. Please report to https
  3. Android多点触摸缩放图片-android学习之
  4. 对View DrawingCache的理解
  5. Android ListView+image的使用
  6. [Android] 代码实现按钮/图片自旋转(中心
  7. Andorid在布局文件中中文加粗
  8. (20120808)(01)android菜单与对话框--之日期
  9. android 获得屏幕宽度 高度
  10. 在AndroidManifest.xml文件中的android:w