Android异常处理流程

Android异常处理流程

前面的几篇文章都是讲解的android中的窗口显示机制,包括Activity窗口加载绘制流程,Dialog窗口加载绘制流程,PopupWindow窗口加载绘制流程,Toast窗口加载绘制流程等等。整个Android的界面显示的原理都是类似的,都是通过Window对象控制View组件,实现加载与绘制流程。

这篇文章休息一下,不在讲解Android的窗口绘制机制,穿插的讲解一下Android系统的异常处理流程。O(∩_∩)O哈哈~

开发过android项目的童鞋对android系统中错误弹窗,force close应该不陌生了,当我们的App异常崩溃时,就会弹出一个force close的弹窗,告诉我们App崩溃,以及一下简易的错误信息:
这里写图片描述

那么这里的force close弹窗是如何弹出的呢?

还有我们在开发App的过程中,经常会自定义Application,自定义UncaughtExceptionHandler实现App的全局异常处理,那么这里的UncaughtExceptionHandler是如何实现对异常的全局处理的呢?(可参考: 在Android中自定义捕获Application全局异常

带着这两个问题,我们开始今天的异常流程分析。

在android应用进程的启动流程中我们在经过一系列的操作之后会调用RuntimeInit.zygoteInit方法(可参考:Android应用程序进程启动过程的源代码分析

而我们也是从这里开始分析我们的Android系统异常处理流程,好了,让我们先来看一下zygoteInit方法的具体实现:

1
2
3
4
5
6
7
8
9
10
11
public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");

Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "RuntimeInit");
redirectLogStreams();

commonInit();
nativeZygoteInit();
applicationInit(targetSdkVersion, argv, classLoader);
}

可以看到在方法体中我们调用了commonInit方法,这个方法是用于初始化操作的,继续看一下commonInit方法的实现:

1
2
3
4
5
private static final void commonInit() {
...
Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler());
...
}

可以看到在这里我们调用了Thread.setDefaultUncaughtExceptionHandler方法,这样当我们的进程出现异常的时候,异常信息就会被我们新创建的UncaughtHandler所捕获。

看过我们前面写过的关于Android全局异常处理文章的童鞋应该知道,我们实现对Android异常全局处理的操作也是通过设置Thread.setDefaultUncaughtExceptionHandler来实现的,具体可参考: 在Android中自定义捕获Application全局异常
所以Android系统默认的异常信息都会被这里的UncaughtHandler所捕获并被其uncaughtException方法回调,所以若我们不重写Thread.setDefaultUncaughtExceptionHandler方法,那么这里的UncaughtHandler就是我们默认的异常处理操作 这样我们看一下UncaughtHandler的具体实现:

Dialog取消绘制流程

Dialog取消绘制流程

上几篇文章中我们分析了Dialog的加载绘制流程,也分析了Acvityi的加载绘制流程,说白了Android系统中窗口的展示都是通过Window对象控制,通过ViewRootImpl对象执行绘制操作来完成的,那么窗口的取消绘制流程是怎么样的呢?这篇文章就以Dialog为例说明Window窗口是如何取消绘制的。

有的同学可能会问前几篇文章介绍Activity的加载绘制流程的时候为何没有讲Activity的窗口取消流程,这里说明一下。那是因为当时说明的重点是Activity的加载与绘制流程,而取消绘制流程由于混杂在Activity的生命周期管理,可能不太明显,所以这里将Window窗口的取消绘制流程放在Dialog中,其实他们的取消绘制流程都是相似的,看完Dialog的取消绘制流程之后,再看一下Activity的取消绘制流程就很简单了。

还记得我们上一篇文章关于Dialog的例子么?我们通过AlertDialog.Builder创建了一个AlertDialog,并通过Activity中的按钮点击事件来显示这个AlertDialog,而在AlertDialog中定义了一个“知道了”按钮,点击这个按钮就会触发alertDialog.cancel方法,通过执行这个方法,我们的alertDialog就不在显示了,很明显的,cancel方法执行过程中就执行了取消绘制的逻辑,这里我们先看一下我们的例子核心代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
title.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this.getApplication());
builder.setIcon(R.mipmap.ic_launcher);
builder.setMessage("this is the content view!!!");
builder.setTitle("this is the title view!!!");
builder.setView(R.layout.activity_second);
builder.setPositiveButton("知道了", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
alertDialog.cannel();
}
});
alertDialog = builder.create();
alertDialog.show();
}
});

这里的title就是我们自己的Activity中的一个TextView,通过注册这个TextView的点击事件,来显示一个AlertDialog,通过注册AlertDialog中按钮的点击事件,执行alertDialog的cancel方法。

好吧,看一下Dialog的cannel方法的具体实现:

1
2
3
4
5
6
7
8
public void cancel() {
if (!mCanceled && mCancelMessage != null) {
mCanceled = true;
// Obtain a new message so this dialog can be re-used
Message.obtain(mCancelMessage).sendToTarget();
}
dismiss();
}

可以看到方法体中,若当前Dialog没有取消,并且设置了取消message,则调用Message.obtain(mCancel).sendToTarget(),前面已经分析过这里的sendToTarget方法会回调我们注册的异步消息处理逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
public void setOnCancelListener(final OnCancelListener listener) {
if (mCancelAndDismissTaken != null) {
throw new IllegalStateException(
"OnCancelListener is already taken by "
+ mCancelAndDismissTaken + " and can not be replaced.");
}
if (listener != null) {
mCancelMessage = mListenersHandler.obtainMessage(CANCEL, listener);
} else {
mCancelMessage = null;
}
}

可以看到如果我们在初始化AlertDialog.Builder时,设置了setOnCancelListener,那么我们就会执行mListenersHandler的异步消息处理,好吧,这里看一下mListenersHandler的定义:

Dialog加载绘制流程

Dialog加载绘制流程

前面两篇文章,我们分析了Activity的布局文件加载、绘制流程,算是对整个Android系统中界面的显示流程有了一个大概的了解,其实Android系统中所有的显示控件(注意这里是控件,而不是组件)的加载绘制流程都是类似的,包括:Dialog的加载绘制流程,PopupWindow的加载绘制流程,Toast的显示原理等,上一篇文章中,我说在介绍了Activity界面的加载绘制流程之后,就会分析一下剩余几个控件的显示控制流程,这里我打算先分析一下Dialog的加载绘制流程。

可能有的同学问这里为什么没有Fragment?其实严格意义上来说Fragment并不是一个显示控件,而只是一个显示组件。为什么这么说呢?其实像我们的Activity,Dialog,PopupWindow以及Toast类的内部都管理维护着一个Window对象,这个Window对象不但是一个View组件的集合管理对象,它也实现了组件的加载与绘制流程,而我们的Fragment组件如果看过源码的话,严格意义上来说,只是一个View组件的集合并通过控制变量实现了其特定的生命周期,但是其由于并没有维护Window类型的成员变量,所以其不具备组件的加载与绘制功能,因此其不能单独的被绘制出来,这也是我把它称之为组件而不是控件的原因。(在分析完这几个控件的加载绘制流程之后,有时间的话,也会分析一下Fragment的相关源码)

好吧,开始我们今天关于Dialog的讲解,相信大家在平时的开发过程中经常会使用到Dialog弹窗,使用Dialog可以在Activity弹出弹窗,确认消息等。为了更好的分析Dialog的源码,我们这里暂时写一个简单的demo,看一下Dialog的使用实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
title.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {

AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setIcon(R.mipmap.ic_launcher);
builder.setMessage("this is the content view!!!");
builder.setTitle("this is the title view!!!");
builder.setView(R.layout.activity_second);
builder.setPositiveButton("知道了", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
alertDialog.dismiss();
}
});
alertDialog = builder.create();
alertDialog.show();
}
});

我们在Activity中获取一个textView组件,并监听TextView的点击事件,并在点击事件中,初始化一个AlertDialog弹窗,并执行AlertDialog的show方法展示弹窗,在弹窗中定义一个按钮,并监听弹窗按钮的点击事件,若用户点击了弹窗的按钮,则执行AlertDialog的dismiss方法,取消展示AlertDialog。好吧,我们来看一下这个弹窗弹出的展示结果:
这里写图片描述
可以看到我们定义的icon,title,message和button都已经显示出来了,这时候我们点击弹窗按钮知道了,这时候弹窗就会消失了。

一般我们使用Dialog的大概流程都是这样的,可能定制Dialog的时候有一些定制化的操作,但是基本操作流程还是这样的。

那么我们先来看一下AlertDialog.Builder的构造方法,这里的Builder是AlertDialog的内部类,用于封装AlertDialog的构造过程,看一下Builder的构造方法:

1
2
3
public Builder(Context context) {
this(context, resolveDialogTheme(context, 0));
}

好吧,这里调用的是Builder的重载构造方法:


:D 一言句子获取中...