Android 主线程到底是什么、如何抛出ANR
作为 android 者对主线程的操作在开发中是非常频繁的,主线程是非常重要的线程,因为我们所有的UI界面都是通过主线程更新、绘制的。所以我们要足够了解他。从源码的角度看看为何ui必须在主线程更新、直接创建的handler为何就把线程给切换到了主线程呢?、主线程阻塞anr是如何抛出的?
目录
- Android 主线程是在哪里创建的?
- 在android开发中为什么子线程不能更新UI界面?
- 直接创建的Handler接收到的消息是运行在那个线程中的?
- 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 其实两种方式
- 我们创建ViewRootImpl时在子线程中创建。
- 反射修改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的产生主要涉及到一下场景:
- Service Timeout:服务在20s内未执行完成;
- BroadcastQueue Timeout:比如前台广播在10s内执行完成
- ContentProvider Timeout:内容提供者执行超时
- 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异常。