onSaveInstanceState执行时机

onSaveInstanceState执行时机

我们已经分析过Activity的启动流程,从中也分析了Activity的生命周期。而其中有一个生命周期方法:onSaveInstanceState方法,今天我们主要讲解一下onSaveInstanceState方法的执行时机。
可能部分同学对Activity的onSaveInstanceState方法不是特别熟悉,这里我们简单介绍一下。onSaveInstanceState方法是Activity的成员方法,主要用于在Activity销毁时保存Activity相关的对象信息,而其执行的时机不是我们主动调用的,而是Android系统的framework帮忙调用的,而其调用的时机,可以参考android系统的介绍:

This method is called before an activity may be killed so that when it comes back some time in the future it can restore its state. For example, if activity B is launched in front of activity A, and at some point activity A is killed to reclaim resources, activity A will have a chance to save the current state of its user interface via this method so that when the user returns to activity A, the state of the user interface can be restored via {@link #onCreate} or {@link #onRestoreInstanceState}.

可以发现onSaveInstanceState方法会在Activity将要被kill的时候执行。O(∩_∩)O哈哈~,可能跟以前讲解的内容不是太对,我们看过不少文章都是说onSaveInstanceStatex方法会在Activity容易被销毁的时候执行。那么这里明明说的是当Activity被销毁的时候就会执行onSaveInstanceState方法,那么具体的情况是如何的呢?我们具体看一下源码吧,哈哈。

通过分析Activity的生命周期方法,我们知道onSaveInstanceState方法在onPause方法之后执行在onStop方法之前执行。这里我们首先看一下onPause方法的源码逻辑。

Activity在执行onPause方法的时候回回调ActivityThread的handlePauseActivity方法,不太熟悉的同学可以参考: android源码解析之(十四)–>Activity启动流程,文章中有对Activity生命周期的详细讲解。

好吧,先具体看一下ActivityThread.handlePauseActivity的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
private void handlePauseActivity(IBinder token, boolean finished,
boolean userLeaving, int configChanges, boolean dontReport) {
ActivityClientRecord r = mActivities.get(token);
if (r != null) {
//Slog.v(TAG, "userLeaving=" + userLeaving + " handling pause of " + r);
if (userLeaving) {
performUserLeavingActivity(r);
}

r.activity.mConfigChangeFlags |= configChanges;
performPauseActivity(token, finished, r.isPreHoneycomb());

// Make sure any pending writes are now committed.
if (r.isPreHoneycomb()) {
QueuedWork.waitToFinish();
}

// Tell the activity manager we have paused.
if (!dontReport) {
try {
ActivityManagerNative.getDefault().activityPaused(token);
} catch (RemoteException ex) {
}
}
mSomeActivitiesChanged = true;
}
}

在方法体中我们除了执行一些其他的操作,然后在handlePauseActivity方法体中调用了performPauseActivity方法,这个方法就是具体执行回调pauseActivity操作的方法,既然这样我们在看一下performPauseActivity方法的实现:

1
2
3
4
5
final Bundle performPauseActivity(IBinder token, boolean finished,
boolean saveState) {
ActivityClientRecord r = mActivities.get(token);
return r != null ? performPauseActivity(r, finished, saveState) : null;
}

可以发现在performPauseActivity方法中首先判断ActivityClientRecord是否为空,然后又调用了performPauseActivity方法的重载方法:

Toast加载绘制流程

Toast加载绘制流程

前面我们分析了Activity、Dialog、PopupWindow的加载绘制流程,相信大家对整个Android系统中的窗口绘制流程已经有了一个比较清晰的认识了,这里最后再给大家介绍一下Toast的加载绘制流程。

其实Toast窗口和Activity、Dialog、PopupWindow有一个不太一样的地方,就是Toast窗口是属于系统级别的窗口,他和输入框等类似的,不属于某一个应用,即不属于某一个进程,所以自然而然的,一旦涉及到Toast的加载绘制流程就会涉及到进程间通讯,看过前面系列文章的同学应该知道,Android间的进程间通讯采用的是Android特有的Binder机制,所以Toast的加载绘制流程也会涉及到Binder进程间通讯。

Toast的显示流程其实内部还是通过Window的窗口机制实现加载绘制的,只不过由于是系统级别的窗口,在显示过程中涉及到了进程间通讯等机制。

下面我们来具体看一下Toast窗口的简单使用。

1
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();

上面的代码是Toast的典型使用方式,通过makeText方法创建出一个Toast对象,然后调用show方法将Toast窗口显示出来。

下面我们来看一下makeText方法的具体实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static Toast makeText(Context context, CharSequence text, @Duration int duration) {
Toast result = new Toast(context);

LayoutInflater inflate = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null);
TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message);
tv.setText(text);

result.mNextView = v;
result.mDuration = duration;

return result;
}

方法体不是很长,在makeText方法中,我们首先通过Toast对象的构造方法,创建了一个新的Toast对象,这样我们就先来看一下Toast的构造方法做了哪些事。

1
2
3
4
5
6
7
8
public Toast(Context context) {
mContext = context;
mTN = new TN();
mTN.mY = context.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.toast_y_offset);
mTN.mGravity = context.getResources().getInteger(
com.android.internal.R.integer.config_toastDefaultGravity);
}

可以看到这里初始化了Toast对象的成员变量mContext和mTN,这里的mContext是一个Context类型的成员变量,那mTN是什么东西呢?

1
private static class TN extends ITransientNotification.Stub

从类的源码定义来看,我们知道TN是一个继承自ITransientNotification.Stub的类,这里我们暂时只用知道他的继承关系就好了,知道其是一个Binder对象,可以用于进程间通讯,然后回到我们的makeText方法,在调用了Toast的构造方法创建了Toast对象之后,我们又通过context.getSystemService方法获取到LayoutInflater,然后通过调用LayoutInflater的inflate方法加载到了Toast的布局文件:

PopupWindow加载绘制流程

PopupWindow加载绘制流程

在前面的几篇文章中我们分析了Activity与Dialog的加载绘制流程,取消绘制流程,相信大家对Android系统的窗口绘制机制有了一个感性的认识了,这篇文章我们将继续分析一下PopupWindow加载绘制流程。

在分析PopupWindow之前,我们将首先说一下什么是PopupWindow?理解一个类最好的方式就是看一下这个类的定义,这里我们摘要了一下Android系统中PopupWindow的类的说明:

A popup window that can be used to display an arbitrary view. The popup window is a floating container that appears on top of the current
activity.

一个PopupWindow能够被用于展示任意的View,PopupWindow是一个悬浮的容易展示在当前Activity的上面。
简单来说PopupWindow就是一个悬浮在Activity之上的窗口,可以用展示任意布局文件。

在说明PopupWindow的加载绘制机制之前,我们还是先写一个简单的例子用于说明一下PopupWindow的简单用法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static View showPopupWindowMenu(Activity mContext, View anchorView, int layoutId) {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(layoutId, null);
popupWindow = new PopupWindow(view, DisplayUtil.dip2px(mContext, 148), WindowManager.LayoutParams.WRAP_CONTENT);
popupWindow.setBackgroundDrawable(mContext.getResources().getDrawable(R.drawable.menu_bg));
popupWindow.setFocusable(true);
popupWindow.setOutsideTouchable(true);

int[] location = new int[2];
anchorView.getLocationOnScreen(location);
popupWindow.setAnimationStyle(R.style.popwin_anim_style);
popupWindow.showAtLocation(anchorView, Gravity.NO_GRAVITY,
location[0] - popupWindow.getWidth() + anchorView.getWidth() - DisplayUtil.dip2px(mContext, 12),
location[1] + anchorView.getHeight() - DisplayUtil.dip2px(mContext, 10));

popupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
@Override
public void onDismiss() {
popupWindow = null;
}
});
return view;
}

可以看到我们首先通过LayoutInflater对象将布局文件解析到内存中View对象,然后创建了一个PopupWindow对象,可以看到传递了三个参数,一个是View对象,一个是PopupWindow的宽度和高度。

这里就是PopupWindow的初始化流程的开始了,好吧,我们来看一下PopupWindow的构造方法的实现:

1
2
3
public PopupWindow(View contentView, int width, int height) {
this(contentView, width, height, false);
}

可以看到这里调用了PopupWindow的重载构造方法,好吧,继续看一下这个重载构造方法的实现逻辑:

SystemServer进程启动流程(转)

SystemServer进程启动流程(转)

转载请标明出处:一片枫叶的专栏

上面一文中我们讲过android系统中比较重要的几个进程:init进程,Zygote进程,SystemServer进程已经各种应用进程,其中Zygote进程是整个android系统的根进程,包含SystemServer进程已经各种应用进程在内的进程都是通过Zygote进程fork出来的,具体可参见: android源码解析之(八)–>Zygote进程启动流程
那么SystemServer进程是做什么用的呢?

其实SystemServer进程主要的作用是在这个进程中启动各种系统服务,比如ActivityManagerService,PackageManagerService,WindowManagerService服务,以及各种系统性的服务其实都是在SystemServer进程中启动的,而当我们的应用需要使用各种系统服务的时候其实也是通过与SystemServer进程通讯获取各种服务对象的句柄的。

由上一篇文章我们知道SystemServer进程其实也是有Zygote进程fork出来的,并且执行其main方法,那么这里我们以android23的源码为例,看一下SystemServer的main方法的执行逻辑:

1
2
3
4
5
6
/**
* The main entry point from zygote.
*/
public static void main(String[] args) {
new SystemServer().run();
}

这里比较简单,只是new出一个SystemServer对象并执行其run方法,查看SystemServer类的定义我们知道其实final类型的,所以我们一般不能重写或者继承。

然后我们查看run方法:

Zygote进程启动流程(转)

Zygote进程启动流程(转)

大家都知道android系统的Zygote进程是所有的android进程的父进程,包括SystemServer和各种应用进程都是通过Zygote进程fork出来的。Zygote(孵化)进程相当于是android系统的根进程,后面所有的进程都是通过这个进程fork出来的,而Zygote进程则是通过linux系统的init进程启动的,也就是说,android系统中各种进程的启动方式

init进程 –> Zygote进程 –> SystemServer进程 –>各种应用进程

  • init进程:linux的根进程,android系统是基于linux系统的,因此可以算作是整个android操作系统的第一个进程;

  • Zygote进程:android系统的根进程,主要作用:可以作用Zygote进程fork出SystemServer进程和各种应用进程;

  • SystemService进程:主要是在这个进程中启动系统的各项服务,比如ActivityManagerService,PackageManagerService,WindowManagerService服务等等;

  • 各种应用进程:启动自己编写的客户端应用时,一般都是重新启动一个应用进程,有自己的虚拟机与运行环境;

本文主要介绍一下Zygote进程的启动流程,关于SystenServer进程和各种应用进程的启动方式会在以后的文章中介绍。

init进程在启动Zygote进程时一般都会调用ZygoteInit类的main方法,因此我们这里看一下该方法的具体实现(基于android23源码);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
public static void main(String argv[]) {
try {
RuntimeInit.enableDdms();
// Start profiling the zygote initialization.
SamplingProfilerIntegration.start();

boolean startSystemServer = false;
String socketName = "zygote";
String abiList = null;
for (int i = 1; i < argv.length; i++) {
if ("start-system-server".equals(argv[i])) {
startSystemServer = true;
} else if (argv[i].startsWith(ABI_LIST_ARG)) {
abiList = argv[i].substring(ABI_LIST_ARG.length());
} else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
socketName = argv[i].substring(SOCKET_NAME_ARG.length());
} else {
throw new RuntimeException("Unknown command line argument: " + argv[i]);
}
}

if (abiList == null) {
throw new RuntimeException("No ABI list supplied.");
}

registerZygoteSocket(socketName);
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
SystemClock.uptimeMillis());
preload();
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
SystemClock.uptimeMillis());

// Finish profiling the zygote initialization.
SamplingProfilerIntegration.writeZygoteSnapshot();

// Do an initial gc to clean up after startup
gcAndFinalize();

// Disable tracing so that forked processes do not inherit stale tracing tags from
// Zygote.
Trace.setTracingEnabled(false);

if (startSystemServer) {
startSystemServer(abiList, socketName);
}

Log.i(TAG, "Accepting command socket connections");
runSelectLoop(abiList);

closeServerSocket();
} catch (MethodAndArgsCaller caller) {
caller.run();
} catch (RuntimeException ex) {
Log.e(TAG, "Zygote died with exception", ex);
closeServerSocket();
throw ex;
}
}

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