转载请标明出处:一片枫叶的专栏
上一篇文章中我们讲解了关于SystemServer进程相关的知识,我们知道SystemServer进程主要用于启动系统的各种服务,二者其中就包含了负责启动Launcher的服务,LauncherAppService。具体更多关于SystenServer的启动流程可以参见: android源码解析之(九)–>SystemServer进程启动流程
本文我们将讲解Launcher相关的知识。Launcher程序就是我们平时看到的桌面程序,它其实也是一个android应用程序,只不过这个应用程序是系统默认第一个启动的应用程序,这里我们就简单的分析一下Launcher应用的启动流程。
不同的手机厂商定制android操作系统的时候都会更改Launcher的源代码,我们这里以android23的源码为例大致的分析一下Launcher的启动流程。
通过前一篇文章我们知道在SystemServer进程的启动过程中会调用其main静态方法,开始执行整个SystemServer的启动流程,在其中通过调用三个内部方法分别启动boot service、core service和other service。在调用startOtherService方法中就会通过调用mActivityManagerService.systemReady()方法,那么我们看一下其具体实现:
mActivityManagerService.systemReady(new Runnable() {
@Override
public void run() {
/**
* 执行各种SystemService的启动方法,各种SystemService的systemReady方法...
*/
...
}
});
可以发现这个方法传递了一个Runnable参数,里面执行了各种其他服务的systemReady方法,这里不是我们关注的重点,我们看一下在ActivityManagerService中systemReady方法的具体实现,方法体比较长,我就不在这里贴出代码了,主要的逻辑就是做一些ActivityManagerService的ready操作
public void systemReady(final Runnable goingCallback) {
...
// Start up initial activity.
mBooting = true;
startHomeActivityLocked(mCurrentUserId, "systemReady");
...
}
重点是在这个方法体中调用了startHomeActivityLocked方法,看其名字就是说开始执行启动homeActivity的操作,好了,既然如此,我们再看一下startHomeActivityLocked的具体实现:
boolean startHomeActivityLocked(int userId, String reason) {
if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
&& mTopAction == null) {
return false;
}
Intent intent = getHomeIntent();
ActivityInfo aInfo =
resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
if (aInfo != null) {
intent.setComponent(new ComponentName(
aInfo.applicationInfo.packageName, aInfo.name));
aInfo = new ActivityInfo(aInfo);
aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
ProcessRecord app = getProcessRecordLocked(aInfo.processName,
aInfo.applicationInfo.uid, true);
if (app == null || app.instrumentationClass == null) {
intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
mStackSupervisor.startHomeActivity(intent, aInfo, reason);
}
}
return true;
}
首先是调用getHomeIntent()方法,看一下getHomeIntent是如何实现构造Intent对象的:
Intent getHomeIntent() {
Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
intent.setComponent(mTopComponent);
if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
intent.addCategory(Intent.CATEGORY_HOME);
}
return intent;
}
可以发现,启动Launcher的Intent对象中添加了Intent.CATEGORY_HOME常量,这个其实是一个launcher的标志,一般系统的启动页面Activity都会在androidmanifest.xml中配置这个标志。比如我们在github中的android launcher源码中查看其androidmanifest.xml文件:
可以发现其Activity的定义intentfilter中就是定义了这样的category。不同的手机厂商可能会修改Launcher的源码,但是这个category一般是不会更改的。
继续回到我们的startHomeActivityLocked方法,我们发现经过一系列的判断逻辑之后最后调用了mStackSupervisor.startHomeActivity方法,然后我们可以查看一下该方法的具体实现逻辑:
void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason) {
moveHomeStackTaskToTop(HOME_ACTIVITY_TYPE, reason);
startActivityLocked(null , intent, null , aInfo,
null , null , null ,
null , 0 , 0 , 0 ,
null , 0 , 0 ,
0 , null , false ,
false ,
null , null , null );
if (inResumeTopActivity) {
scheduleResumeTopActivities();
}
}
发现其调用的是scheduleResumeTopActivities()方法,这个方法其实是关于Activity的启动流程的逻辑了,这里我们不在详细的说明,关于Activity的启动流程可以参考我的:Android源码解析之(十四)–>Activity启动流程
因为我们的Launcher启动的Intent是一个隐士的Intent,所以我们会启动在androidmanifest.xml中配置了相同catogory的activity,android M中配置的这个catogory就是LauncherActivity。
LauncherActivity继承与ListActivity,我们看一下其Layout布局文件:
可以看到我们现实的桌面其实就是一个ListView控件,然后看一下其onCreate方法:
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
mPackageManager = getPackageManager();
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setProgressBarIndeterminateVisibility(true);
}
onSetContentView();
mIconResizer = new IconResizer();
mIntent = new Intent(getTargetIntent());
mIntent.setComponent(null);
mAdapter = new ActivityAdapter(mIconResizer);
setListAdapter(mAdapter);
getListView().setTextFilterEnabled(true);
updateAlertTitle();
updateButtonText();
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
setProgressBarIndeterminateVisibility(false);
}
}
可以看到在LauncherActivity的onCreate方法中初始化了一个PackageManager,其主要作用就是从中查询出系统所有已经安装的应用列表,应用包名,应用图标等信息。然后将这些信息注入到Adapter中,这样就可以将系统应用图标和名称显示出来了。
在系统的回调方法onListItemClick中
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
Intent intent = intentForPosition(position);
startActivity(intent);
}
这也就是为什么我们点击了某一个应用图标之后可以启动某一项应用的原因了,我们看一下这里的intentForPosition是如何实现的。
protected Intent intentForPosition(int position) {
ActivityAdapter adapter = (ActivityAdapter) mAdapter;
return adapter.intentForPosition(position);
}
这里又调用了adapter的intentForPosition方法:
public Intent intentForPosition(int position) {
if (mActivitiesList == null) {
return null;
}
Intent intent = new Intent(mIntent);
ListItem item = mActivitiesList.get(position);
intent.setClassName(item.packageName, item.className);
if (item.extras != null) {
intent.putExtras(item.extras);
}
return intent;
}
可以看到由于adapter的每一项中都保存了应用的包名可启动Activity名称,所以这里在初始化Intent的时候,直接将这些信息注入到Intent中,然后调用startActivity,就将这些应用启动了(关于startActivity是如何启动的下面的文章中我将介绍)。
总结:
Launcher的启动流程
Zygote进程 –> SystemServer进程 –> startOtherService方法 –> ActivityManagerService的systemReady方法 –> startHomeActivityLocked方法 –> ActivityStackSupervisor的startHomeActivity方法 –> 执行Activity的启动逻辑,执行scheduleResumeTopActivities()方法。。。。
因为是隐士的启动Activity,所以启动的Activity就是在AndroidManifest.xml中配置catogery的值为:
public static final String CATEGORY_HOME = "android.intent.category.HOME";
可以发现android M中在androidManifest.xml中配置了这个catogory的activity是LauncherActivity,所以我们就可以将这个Launcher启动起来了
- LauncherActivity中是以ListView来显示我们的应用图标列表的,并且为每个Item保存了应用的包名和启动Activity类名,这样点击某一项应用图标的时候就可以根据应用包名和启动Activity名称启动我们的App了。
另外对android源码解析方法感兴趣的可参考我的:
android源码解析之(一)–>android项目构建过程
android源码解析之(二)–>异步消息机制
android源码解析之(三)–>异步任务AsyncTask
android源码解析之(四)–>HandlerThread
android源码解析之(五)–>IntentService
android源码解析之(六)–>Log
android源码解析之(七)–>LruCache
android源码解析之(八)–>Zygote进程启动流程
android源码解析之(九)–>SystemServer进程启动流程
本文以同步至github中:github.com/yipianfengy…,欢迎star和follow
转载请标明出处:一片枫叶的专栏
本节主要是通过分析Activity的启动过程介绍应用程序进程的启动流程。关于Android的应用进程在android guide中有这样的一段描述:
By default, every application runs in its own Linux process. Android starts the process when any of the application’s components need to be executed, then shuts down the process when it’s no longer needed or when the system must recover memory for other applications.
每一个android应用默认都是在他自己的linux进程中运行。android操作系统会在这个android应用中的组件需要被执行的时候启动这个应用进程,并且会在这个应用进程没有任何组件执行或者是系统需要为其他应用申请更多内存的时候杀死这个应用进程。所以当我们需要启动这个应用的四大组件之一的时候如果这个应用的进程还没有启动,那么就会先启动这个应用程序进程。
在上一篇文章中我们简要的介绍了Launcher的启动流程,在SystemServer进程执行完成,各种系统服务启动完成之后,会调用ActivityManagerService中的systemReady()方法,在systemReady()方法中会执行Launcher启动的相关逻辑了,具体可以参考: android源码解析之(十)–>Launcher启动流程
Launcher应用程序在启动过程中会通过PackageManagerService服务请求查询系统所有的已安装应用的包名,图标和应用名称等信息,然后填充到Launcher中的Adapter中,这样点击某一项应用图标的时候就可以根据该图标的包名和启动Activity的类名初始化Intent对象,然后调用startActivity(Intent)启动相关的应用程序了。
其实android中应用进程可以通过许多方式启动,比如启动一个Activity,启动一个Service,启动一个ContentProvider或者是一个BroadcastReceiver,也就是说我们可以通过启动四大组件的方式启动应用进程,在应用进程没有启动的时候,如果我们通过启动这些组件,这时候系统会判断当前这些组件所需要的应用进程是否已经启动,若没有的话,则会启动应用进程。
这里我们通过Launcher简单分析一下应用进程的启动流程。通过上一篇Launcher启动流程,我们知道每一个launcher中的图标对应着一个应用报名和启动activity类名,查看LauncherActivity中的图标点击事件:
protected void onListItemClick(ListView l, View v, int position, long id) {
Intent intent = intentForPosition(position);
startActivity(intent);
}
在通过应用包名和启动activity类名构造完成Intent之后,我们调用了startActivity方法来启动这个activity,很明显的,当前这个应用并没有启动,也就是说我们调用的startActivity方法不单单为我们启动了这个activity也同时在启动activity之前启动了这个应用进程,好了,那我们这里就以这个方法为入口分析一下应用进程的启动流程。
跟踪代码到Activity,发现其调用了startActivity的重载方法:
@Override
public void startActivity(Intent intent) {
this.startActivity(intent, null);
}
继续跟进:
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
if (options != null) {
startActivityForResult(intent, -1, options);
} else {
startActivityForResult(intent, -1);
}
}
很明显的我们此时传递的options为空:
public void startActivityForResult(Intent intent, int requestCode) {
startActivityForResult(intent, requestCode, null);
}
好吧,最后调用的还是这个重载方法:
public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
if (mParent == null) {
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
if (ar != null) {
mMainThread.sendActivityResult(
mToken, mEmbeddedID, requestCode, ar.getResultCode(),
ar.getResultData());
}
if (requestCode >= 0) {
mStartedActivity = true;
}
cancelInputsAndStartExitTransition(options);
} else {
if (options != null) {
mParent.startActivityFromChild(this, intent, requestCode, options);
} else {
mParent.startActivityFromChild(this, intent, requestCode);
}
}
}
可以发现这里调用了mInstrumentation.execStartActivity方法,这里先简单介绍一下Instrumentation对象,他是Android系统中应用程序端操作Activity的具体操作类,这里的操作段是相对于ActivityManagerService服务端来说的。也就是说当我们在执行对Activity的具体操作时,比如回调生命周期的各个方法都是借助于Instrumentation类来实现的。
好了,下面我们继续看一下Instrumentation的execStartActivity方法:
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
...
try {
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess();
int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
return null;
}
这里主要关注这个代码:
int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options)
这断代码实际上是进程间通讯,我们可以发现ActivityManagerNative继承于Binder接口,所以ActivityManagerNative就是一个Binder对象,然后上面一节我们介绍SystemServer进程的时候对ActivityManagerService有过了解,发现其继承于ActivityManagerNative,好吧,了解过Binder机制的童鞋就知道了,ActivityManagerService就是这个Binder机制的服务器端而ActivityManagerNative就是这个Binder机制的客户端,所以我们这里调用的startActivity实际上是讲参数传递给ActivityManagerService并执行ActivityManagerService的startActivity方法。
既然这样,我们看一下ActivityManagerService的startActivity方法:
@Override
public final int startActivity(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle options) {
return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
resultWho, requestCode, startFlags, profilerInfo, options,
UserHandle.getCallingUserId());
}
调用了startActivityAsUser方法,然后我们继续看一下startActivityAsUser方法:
@Override
public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle options, int userId) {
enforceNotIsolatedCaller("startActivity");
userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
false, ALLOW_FULL_ONLY, "startActivity", null);
return mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent,
resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
profilerInfo, null, null, options, false, userId, null, null);
}
继续查看startActivityMayWait方法:
final int startActivityMayWait(IApplicationThread caller, int callingUid,
String callingPackage, Intent intent, String resolvedType,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
IBinder resultTo, String resultWho, int requestCode, int startFlags,
ProfilerInfo profilerInfo, WaitResult outResult, Configuration config,
Bundle options, boolean ignoreTargetSecurity, int userId,
IActivityContainer iContainer, TaskRecord inTask) {
...
int res = startActivityLocked(caller, intent, resolvedType, aInfo,
voiceSession, voiceInteractor, resultTo, resultWho,
requestCode, callingPid, callingUid, callingPackage,
realCallingPid, realCallingUid, startFlags, options, ignoreTargetSecurity,
componentSpecified, null, container, inTask);
...
return res;
}
}
这个方法的逻辑比较多,我们重点关注的是其调用了startActivityLocked方法,也就是说在初始化其他逻辑之后,这个方法会调用startActivityLocked方法:
err = startActivityUncheckedLocked(r, sourceRecord, voiceSession, voiceInteractor,
startFlags, true, options, inTask);
代码量也是比较大的,在方法体中调用了startActivityUncheckedLocked方法,然后我们继续跟进startActivityUncheckedLocked方法:
targetStack.startActivityLocked(r, newTask, doResume, keepCurTransition, options);
然后我们查看startActivityLocked方法的实现:
if (doResume) {
mStackSupervisor.resumeTopActivitiesLocked(this, r, options);
}
可以发现其调用了resumeTopActivitiesLocked方法:
stack.resumeTopActivityLocked(null);
继续跟进:
final boolean resumeTopActivityLocked(ActivityRecord prev) {
return resumeTopActivityLocked(prev, null);
}
然后我们看一下resumeTopActivityLocked方法的实现:
result = resumeTopActivityInnerLocked(prev, options);
继续查看resumeTopActivityInnerLocked方法的实现:
mStackSupervisor.startSpecificActivityLocked(next, true, true);
可以发现在方法体中执行了相关逻辑判断与初始化操作之后调用了startSpecificActivityLocked方法:
mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
"activity", r.intent.getComponent(), false, false, true)
可以发现在方法体中调用了startProcessLocked方法,从名字可以看出来这个方法就是启动进程的。
final ProcessRecord startProcessLocked(String processName,
ApplicationInfo info, boolean knownToBeDead, int intentFlags,
String hostingType, ComponentName hostingName, boolean allowWhileBooting,
boolean isolated, boolean keepIfLarge) {
return startProcessLocked(processName, info, knownToBeDead, intentFlags, hostingType,
hostingName, allowWhileBooting, isolated, 0 , keepIfLarge,
null , null , null ,
null );
}
查看startProcessLocked方法的实现:
checkTime(startTime, "startProcess: stepping in to startProcess");
startProcessLocked(
app, hostingType, hostingNameStr, abiOverride, entryPoint, entryPointArgs);
checkTime(startTime, "startProcess: done starting proc!");
查看startProcessLocked方法的具体实现;
checkTime(startTime, "startProcess: asking zygote to start proc")
Process.ProcessStartResult startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
app.info.dataDir, entryPointArgs)
checkTime(startTime, "startProcess: returned from zygote!")
查看关键代码,这里调用了Process.start方法:
public static final ProcessStartResult start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
int debugFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
String instructionSet,
String appDataDir,
String[] zygoteArgs) {
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
debugFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
throw new RuntimeException(
"Starting VM process through Zygote failed", ex);
}
}
这里的processClass就是要启动的进程的名称,这里传递的就是ActivityThread:
"android.app.ActivityThread"
具体的Process启动进程的Native层代码这里不做过多的分析,这个方法就是启动了AcitivtyThread进程并执行了ActivityThread的main方法,所以我们经常说的进程的启动方法就是ActivityThread的main方法就是这里体现的。
总结:
android应用进程会在需要启动其组件的时候启动,当没有任何组件运行或者是系统内存较低的时候应用进程会被杀死。
在启动应用四大组件的时候若发现当前应用的进程没有启动,则会首先启动应用程序的进程。
我们可以为应用程序配置多个进程,每个进程都有自己的JVM和运行环境,各个进程之间的通讯需要通过Binder机制。
Launcher启动的过程也是先启动Launcher进程再启动其Activity组件。
另外对android源码解析方法感兴趣的可参考我的:
android源码解析之(一)–>android项目构建过程
android源码解析之(二)–>异步消息机制
android源码解析之(三)–>异步任务AsyncTask
android源码解析之(四)–>HandlerThread
android源码解析之(五)–>IntentService
android源码解析之(六)–>Log
android源码解析之(七)–>LruCache
android源码解析之(八)–>Zygote进程启动流程
android源码解析之(九)–>SystemServer进程启动流程
android源码解析之(十)–>Launcher启动流程
本文以同步至github中:github.com/yipianfengy…,欢迎star和follow
转载请标明出处:一片枫叶的专栏
最近有同学问我关于Manifest何时被系统解析的问题,正好也分析到这一块了,索性这一章就讲解一下android系统何时解析Manifest吧,这里的Manifest指的是android安装文件apk中的androidManifest.xml文件是何时被解析的。
大家应该都知道,Android系统启动之后,我们就可以在一个应用中打开另一个从未打开过的应用,或者是在一个应用中发送广播,如果另外一个应用设置了这个广播的接收器,那么这个应用进程就会被启动并接收该广播并作出相应的处理,这样的例子很多,我们可以猜测到Android系统在启动的时候就会抓取到了系统中所有安装的应用信息(应该是解析apk文件的Manifest信息),即在Android系统的启动过程中就已经解析了系统中安装应用的androidManifest.xml文件并保存起来了,那么这个过程具体是如何的呢?
其实android系统启动过程中解析Manifest的流程是通过PackageManagerService服务来实现的。这里我们重点分析一下PackageManagerService服务是如何解析Manifest的。
首先看一下在SystemServer进程启动过程中是如何启动PackageManagerService服务的:
private void startBootstrapServices() {
...
mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
mFirstBoot = mPackageManagerService.isFirstBoot();
mPackageManager = mSystemContext.getPackageManager();
...
}
在SystemServer进程启动过程中会调用SystemServer类的startBootstrapServices方法(主要用于启动ActivityManagerService服务和PackageManagerService服务),然后会在这个方法中会调用PackageManagerService.main静态方法,这个方法主要是用来初始化PackageManagerService服务并执行相关逻辑的。下面我来看一下main方法的具体逻辑:
public static PackageManagerService main(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
PackageManagerService m = new PackageManagerService(context, installer,
factoryTest, onlyCore);
ServiceManager.addService("package", m);
return m;
}
可以发现main方法的实现逻辑主要是创建了一个PackageManagerService对象,并将这个对象添加到ServierManager中为其他组件提供服务。好吧,看来PackageManagerService的初始化操作主要是在PackageManagerService的构造方法中了,下面我们来看一下其构造方法的实现逻辑:
File dataDir = Environment.getDataDirectory();
mAppDataDir = new File(dataDir, "data");
mAppInstallDir = new File(dataDir, "app");
mAppLib32InstallDir = new File(dataDir, "app-lib");
mAsecInternalPath = new File(dataDir, "app-asec").getPath();
mUserAppDataDir = new File(dataDir, "user");
mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
PackageManagerService的构造方法代码量比较大,这里就不贴出所有的代码了,我们主要和解析Manifest相关的主要代码,在构造方法中有这样几段代码。可以发现在构造方法中,解析了系统中几个apk的安装目录,这几个目录就是系统中安装apk的目录,android系统会默认解析这几个目录下apk文件,也就是说如果我们android手机在其他的目录下存在apk文件系统是不会默认解析的,反过来说,如果我们把我们的apk文件移动到这几个目录下,那么重新启动操作系统,该apk文件就会被系统解析并执行相关的逻辑操作,具体做什么操作呢?我们看下面的实现。
/ overlay packages if they reside in VENDOR_OVERLAY_DIR.
File vendorOverlayDir = new File(VENDOR_OVERLAY_DIR);
scanDirLI(vendorOverlayDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_TRUSTED_OVERLAY, 0);
// Find base frameworks (resource packages without code).
scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED,
scanFlags | SCAN_NO_DEX, 0);
// Collected privileged system packages.
final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0);
// Collect ordinary system packages.
final File systemAppDir = new File(Environment.getRootDirectory(), "app");
scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
// Collect all vendor packages.
File vendorAppDir = new File("/vendor/app");
try {
vendorAppDir = vendorAppDir.getCanonicalFile();
} catch (IOException e) {
// failed to look up canonical path, continue with original one
}
scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
// Collect all OEM packages.
final File oemAppDir = new File(Environment.getOemDirectory(), "app");
scanDirLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
在我们刚刚的PackageManagerService.mani方法中,解析完刚刚的几个系统目录之后系统会调用scanDirLI方法,那么这个方法主要是做什么用的呢?看它的名字应该是遍历这个系统目录。好吧,这个方法主要就是用于解析上面几个目录下的apk文件的。不信?我们看一下scanDirLI方法的具体实现:
private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) {
final File[] files = dir.listFiles();
if (ArrayUtils.isEmpty(files)) {
Log.d(TAG, "No files in app dir " + dir);
return;
}
if (DEBUG_PACKAGE_SCANNING) {
Log.d(TAG, "Scanning app dir " + dir + " scanFlags=" + scanFlags
+ " flags=0x" + Integer.toHexString(parseFlags));
}
for (File file : files) {
final boolean isPackage = (isApkFile(file) || file.isDirectory())
&& !PackageInstallerService.isStageName(file.getName());
if (!isPackage) {
continue;
}
try {
scanPackageLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,
scanFlags, currentTime, null);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to parse " + file + ": " + e.getMessage());
if ((parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
e.error == PackageManager.INSTALL_FAILED_INVALID_APK) {
logCriticalInfo(Log.WARN, "Deleting invalid package at " + file);
if (file.isDirectory()) {
mInstaller.rmPackageDir(file.getAbsolutePath());
} else {
file.delete();
}
}
}
}
}
可以放下其首先会遍历该目录下的所有文件,并判断是否是apk文件,如果是apk文件则调用scanPackageLI方法,scanPackageLI方法的名字很明显,就是用于解析这个apk文件的。
继续看一下scanPakcageLI方法的实现:
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
long currentTime, UserHandle user) throws PackageManagerException {
if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
parseFlags |= mDefParseFlags;
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setOnlyCoreApps(mOnlyCore);
pp.setDisplayMetrics(mMetrics);
if ((scanFlags & SCAN_TRUSTED_OVERLAY) != 0) {
parseFlags |= PackageParser.PARSE_TRUSTED_OVERLAY;
}
final PackageParser.Package pkg;
try {
pkg = pp.parsePackage(scanFile, parseFlags);
} catch (PackageParserException e) {
throw PackageManagerException.from(e);
}
...
}
好吧,这个方法也比较复杂,这里只是列出重点相关的代码,我们可以发现在这个方法中创建了一个PackagerParser对象,并调用了parsePackage方法,这个方法其实就是解析Manifest的主要方法,我们可以看一下其具体的实现:
public Package parsePackage(File packageFile, int flags) throws PackageParserException {
if (packageFile.isDirectory()) {
return parseClusterPackage(packageFile, flags);
} else {
return parseMonolithicPackage(packageFile, flags);
}
}
可以发现,若我们解析的File对象是一个文件夹则执行调用parseClusterPackage方法,否则调用执行parseMonolithicPackage方法,很明显的因为我们这里解析的是apk文件(在上一方法中我们循环遍历得到了apk文件,这里的File对象就代表了一个个的apk文件信息),所以这里会执行parseMonolithicPackage方法,然后我们来看一下parseMonolithicPackage方法:
public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
if (mOnlyCoreApps) {
final PackageLite lite = parseMonolithicPackageLite(apkFile, flags);
if (!lite.coreApp) {
throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"Not a coreApp: " + apkFile);
}
}
final AssetManager assets = new AssetManager();
try {
final Package pkg = parseBaseApk(apkFile, assets, flags);
pkg.codePath = apkFile.getAbsolutePath();
return pkg;
} finally {
IoUtils.closeQuietly(assets);
}
}
可以看出,这里又调用了parseBaseApk方法:
private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
...
final Package pkg = parseBaseApk(res, parser, flags, outError);
...
}
可以看出,这个parseBaseApk方法调用了其重载的parseBaseApk方法:
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
if (tagName.equals("application")) {
if (foundApp) {
if (RIGID_PARSER) {
outError[0] = " has more than one ";
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
} else {
Slog.w(TAG, " has more than one ");
XmlUtils.skipCurrentTag(parser);
continue;
}
}
foundApp = true;
if (!parseBaseApplication(pkg, res, parser, attrs, flags, outError)) {
return null;
}
} else if (tagName.equals("overlay")) {
pkg.mTrustedOverlay = trustedOverlay;
sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestResourceOverlay);
pkg.mOverlayTarget = sa.getString(
com.android.internal.R.styleable.AndroidManifestResourceOverlay_targetPackage);
pkg.mOverlayPriority = sa.getInt(
com.android.internal.R.styleable.AndroidManifestResourceOverlay_priority,
-1);
sa.recycle();
if (pkg.mOverlayTarget == null) {
outError[0] = " does not specify a target package";
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
}
if (pkg.mOverlayPriority < 0 || pkg.mOverlayPriority > 9999) {
outError[0] = " priority must be between 0 and 9999";
mParseError =
PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
}
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("key-sets")) {
if (!parseKeySets(pkg, res, parser, attrs, outError)) {
return null;
}
} else if (tagName.equals("permission-group")) {
if (parsePermissionGroup(pkg, flags, res, parser, attrs, outError) == null) {
return null;
}
} else if (tagName.equals("permission")) {
if (parsePermission(pkg, res, parser, attrs, outError) == null) {
return null;
}
} else if (tagName.equals("permission-tree")) {
if (parsePermissionTree(pkg, res, parser, attrs, outError) == null) {
return null;
}
} else if (tagName.equals("uses-permission")) {
if (!parseUsesPermission(pkg, res, parser, attrs)) {
return null;
}
} else if (tagName.equals("uses-permission-sdk-m")
|| tagName.equals("uses-permission-sdk-23")) {
if (!parseUsesPermission(pkg, res, parser, attrs)) {
return null;
}
} else if (tagName.equals("uses-configuration")) {
ConfigurationInfo cPref = new ConfigurationInfo();
sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestUsesConfiguration);
cPref.reqTouchScreen = sa.getInt(
com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqTouchScreen,
Configuration.TOUCHSCREEN_UNDEFINED);
cPref.reqKeyboardType = sa.getInt(
com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqKeyboardType,
Configuration.KEYBOARD_UNDEFINED);
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqHardKeyboard,
false)) {
cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD;
}
cPref.reqNavigation = sa.getInt(
com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqNavigation,
Configuration.NAVIGATION_UNDEFINED);
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqFiveWayNav,
false)) {
cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV;
}
sa.recycle();
pkg.configPreferences = ArrayUtils.add(pkg.configPreferences, cPref);
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("uses-feature")) {
FeatureInfo fi = parseUsesFeature(res, attrs);
pkg.reqFeatures = ArrayUtils.add(pkg.reqFeatures, fi);
if (fi.name == null) {
ConfigurationInfo cPref = new ConfigurationInfo();
cPref.reqGlEsVersion = fi.reqGlEsVersion;
pkg.configPreferences = ArrayUtils.add(pkg.configPreferences, cPref);
}
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("feature-group")) {
FeatureGroupInfo group = new FeatureGroupInfo();
ArrayList features = null;
final int innerDepth = parser.getDepth();
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
final String innerTagName = parser.getName();
if (innerTagName.equals("uses-feature")) {
FeatureInfo featureInfo = parseUsesFeature(res, attrs);
featureInfo.flags |= FeatureInfo.FLAG_REQUIRED;
features = ArrayUtils.add(features, featureInfo);
} else {
Slog.w(TAG, "Unknown element under : " + innerTagName +
" at " + mArchiveSourcePath + " " +
parser.getPositionDescription());
}
XmlUtils.skipCurrentTag(parser);
}
if (features != null) {
group.features = new FeatureInfo[features.size()];
group.features = features.toArray(group.features);
}
pkg.featureGroups = ArrayUtils.add(pkg.featureGroups, group);
} else if (tagName.equals("uses-sdk")) {
if (SDK_VERSION > 0) {
sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestUsesSdk);
int minVers = 0;
String minCode = null;
int targetVers = 0;
String targetCode = null;
TypedValue val = sa.peekValue(
com.android.internal.R.styleable.AndroidManifestUsesSdk_minSdkVersion);
if (val != null) {
if (val.type == TypedValue.TYPE_STRING && val.string != null) {
targetCode = minCode = val.string.toString();
} else {
targetVers = minVers = val.data;
}
}
val = sa.peekValue(
com.android.internal.R.styleable.AndroidManifestUsesSdk_targetSdkVersion);
if (val != null) {
if (val.type == TypedValue.TYPE_STRING && val.string != null) {
targetCode = minCode = val.string.toString();
} else {
targetVers = val.data;
}
}
sa.recycle();
if (minCode != null) {
boolean allowedCodename = false;
for (String codename : SDK_CODENAMES) {
if (minCode.equals(codename)) {
allowedCodename = true;
break;
}
}
if (!allowedCodename) {
if (SDK_CODENAMES.length > 0) {
outError[0] = "Requires development platform " + minCode
+ " (current platform is any of "
+ Arrays.toString(SDK_CODENAMES) + ")";
} else {
outError[0] = "Requires development platform " + minCode
+ " but this is a release platform.";
}
mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
return null;
}
} else if (minVers > SDK_VERSION) {
outError[0] = "Requires newer sdk version #" + minVers
+ " (current version is #" + SDK_VERSION + ")";
mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
return null;
}
if (targetCode != null) {
boolean allowedCodename = false;
for (String codename : SDK_CODENAMES) {
if (targetCode.equals(codename)) {
allowedCodename = true;
break;
}
}
if (!allowedCodename) {
if (SDK_CODENAMES.length > 0) {
outError[0] = "Requires development platform " + targetCode
+ " (current platform is any of "
+ Arrays.toString(SDK_CODENAMES) + ")";
} else {
outError[0] = "Requires development platform " + targetCode
+ " but this is a release platform.";
}
mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
return null;
}
pkg.applicationInfo.targetSdkVersion
= android.os.Build.VERSION_CODES.CUR_DEVELOPMENT;
} else {
pkg.applicationInfo.targetSdkVersion = targetVers;
}
}
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("supports-screens")) {
sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestSupportsScreens);
pkg.applicationInfo.requiresSmallestWidthDp = sa.getInteger(
com.android.internal.R.styleable.AndroidManifestSupportsScreens_requiresSmallestWidthDp,
0);
pkg.applicationInfo.compatibleWidthLimitDp = sa.getInteger(
com.android.internal.R.styleable.AndroidManifestSupportsScreens_compatibleWidthLimitDp,
0);
pkg.applicationInfo.largestWidthLimitDp = sa.getInteger(
com.android.internal.R.styleable.AndroidManifestSupportsScreens_largestWidthLimitDp,
0);
supportsSmallScreens = sa.getInteger(
com.android.internal.R.styleable.AndroidManifestSupportsScreens_smallScreens,
supportsSmallScreens);
supportsNormalScreens = sa.getInteger(
com.android.internal.R.styleable.AndroidManifestSupportsScreens_normalScreens,
supportsNormalScreens);
supportsLargeScreens = sa.getInteger(
com.android.internal.R.styleable.AndroidManifestSupportsScreens_largeScreens,
supportsLargeScreens);
supportsXLargeScreens = sa.getInteger(
com.android.internal.R.styleable.AndroidManifestSupportsScreens_xlargeScreens,
supportsXLargeScreens);
resizeable = sa.getInteger(
com.android.internal.R.styleable.AndroidManifestSupportsScreens_resizeable,
resizeable);
anyDensity = sa.getInteger(
com.android.internal.R.styleable.AndroidManifestSupportsScreens_anyDensity,
anyDensity);
sa.recycle();
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("protected-broadcast")) {
sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestProtectedBroadcast);
String name = sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestProtectedBroadcast_name);
sa.recycle();
if (name != null && (flags&PARSE_IS_SYSTEM) != 0) {
if (pkg.protectedBroadcasts == null) {
pkg.protectedBroadcasts = new ArrayList();
}
if (!pkg.protectedBroadcasts.contains(name)) {
pkg.protectedBroadcasts.add(name.intern());
}
}
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("instrumentation")) {
if (parseInstrumentation(pkg, res, parser, attrs, outError) == null) {
return null;
}
} else if (tagName.equals("original-package")) {
sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestOriginalPackage);
String orig =sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestOriginalPackage_name, 0);
if (!pkg.packageName.equals(orig)) {
if (pkg.mOriginalPackages == null) {
pkg.mOriginalPackages = new ArrayList();
pkg.mRealPackage = pkg.packageName;
}
pkg.mOriginalPackages.add(orig);
}
sa.recycle();
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("adopt-permissions")) {
sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestOriginalPackage);
String name = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestOriginalPackage_name, 0);
sa.recycle();
if (name != null) {
if (pkg.mAdoptPermissions == null) {
pkg.mAdoptPermissions = new ArrayList();
}
pkg.mAdoptPermissions.add(name);
}
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("uses-gl-texture")) {
XmlUtils.skipCurrentTag(parser);
continue;
} else if (tagName.equals("compatible-screens")) {
XmlUtils.skipCurrentTag(parser);
continue;
} else if (tagName.equals("supports-input")) {
XmlUtils.skipCurrentTag(parser);
continue;
} else if (tagName.equals("eat-comment")) {
XmlUtils.skipCurrentTag(parser);
continue;
} else if (RIGID_PARSER) {
outError[0] = "Bad element under : "
+ parser.getName();
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
} else {
Slog.w(TAG, "Unknown element under : " + parser.getName()
+ " at " + mArchiveSourcePath + " "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
}
在这个parseBaseApk方法中有一个while循环,该循环主要就是用于解析AndroidManifest.xml文件中的节点信息。在开始解析application节点的时候,同时调用了parseBaseApplication方法,该方法解析了application节点下的activity,service,broadcast,contentprovier等组件的定义信息:
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
if (tagName.equals("activity")) {
Activity a = parseActivity(owner, res, parser, attrs, flags, outError, false,
owner.baseHardwareAccelerated);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.activities.add(a);
} else if (tagName.equals("receiver")) {
Activity a = parseActivity(owner, res, parser, attrs, flags, outError, true, false);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.receivers.add(a);
} else if (tagName.equals("service")) {
Service s = parseService(owner, res, parser, attrs, flags, outError);
if (s == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.services.add(s);
} else if (tagName.equals("provider")) {
Provider p = parseProvider(owner, res, parser, attrs, flags, outError);
if (p == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.providers.add(p);
} else if (tagName.equals("activity-alias")) {
Activity a = parseActivityAlias(owner, res, parser, attrs, flags, outError);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.activities.add(a);
} else if (parser.getName().equals("meta-data")) {
if ((owner.mAppMetaData = parseMetaData(res, parser, attrs, owner.mAppMetaData,
outError)) == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
} else if (tagName.equals("library")) {
sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestLibrary);
String lname = sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestLibrary_name);
sa.recycle();
if (lname != null) {
lname = lname.intern();
if (!ArrayUtils.contains(owner.libraryNames, lname)) {
owner.libraryNames = ArrayUtils.add(owner.libraryNames, lname);
}
}
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("uses-library")) {
sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestUsesLibrary);
String lname = sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestUsesLibrary_name);
boolean req = sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestUsesLibrary_required,
true);
sa.recycle();
if (lname != null) {
lname = lname.intern();
if (req) {
owner.usesLibraries = ArrayUtils.add(owner.usesLibraries, lname);
} else {
owner.usesOptionalLibraries = ArrayUtils.add(
owner.usesOptionalLibraries, lname);
}
}
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("uses-package")) {
XmlUtils.skipCurrentTag(parser);
} else {
if (!RIGID_PARSER) {
Slog.w(TAG, "Unknown element under : " + tagName
+ " at " + mArchiveSourcePath + " "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
} else {
outError[0] = "Bad element under : " + tagName;
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
}
}
这样,经过这里循环遍历,整个androidManifest的节点信息就被解析并保存在了Package对象中。可以看到我们平时在Manifest中定义的各种节点,其实都是在这里有所体现。当androidManifest.xml文件被解析完成之后会调用我们刚刚介绍的scanPackageLI的重载方法,将解析完成的Package对象信息保存的Setting对象中,这个对象用于保存app的安装信息,具体实现是在方法:
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
long currentTime, UserHandle user) throws PackageManagerException
当解析完成manifest文件之后会调用其重载方法:
// Note that we invoke the following method only if we are about to unpack an application
PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanFlags
| SCAN_UPDATE_SIGNATURE, currentTime, user);
这样,解析的manifest文件信息就会被保存到Settings中,并持久化,然后执行安装apk的操作,我们可以看一下该重载方法的具体实现:
private PackageParser.Package scanPackageLI(PackageParser.Package pkg, int parseFlags,
int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
boolean success = false;
try {
final PackageParser.Package res = scanPackageDirtyLI(pkg, parseFlags, scanFlags,
currentTime, user);
success = true;
return res;
} finally {
if (!success && (scanFlags & SCAN_DELETE_DATA_ON_FAILURES) != 0) {
removeDataDirsLI(pkg.volumeUuid, pkg.packageName);
}
}
}
可以发现其内部调用了scanPackageDirtyLI方法,这个方法就是实际实现持久化manifest信息并安装APK操作的:
private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg, int parseFlags,
int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
...
// And now re-install the app.
ret = createDataDirsLI(pkg.volumeUuid, pkgName, pkg.applicationInfo.uid,
pkg.applicationInfo.seinfo);
...
}
可以发现其内部调用了createDataDirLI,该方法主要实现安装apk的操作。
private int createDataDirsLI(String volumeUuid, String packageName, int uid, String seinfo) {
int[] users = sUserManager.getUserIds();
int res = mInstaller.install(volumeUuid, packageName, uid, uid, seinfo);
if (res < 0) {
return res;
}
for (int user : users) {
if (user != 0) {
res = mInstaller.createUserData(volumeUuid, packageName,
UserHandle.getUid(user, uid), user, seinfo);
if (res < 0) {
return res;
}
}
}
return res;
}
查看该方法的实现:
public int install(String uuid, String name, int uid, int gid, String seinfo) {
StringBuilder builder = new StringBuilder("install");
builder.append(' ');
builder.append(escapeNull(uuid));
builder.append(' ');
builder.append(name);
builder.append(' ');
builder.append(uid);
builder.append(' ');
builder.append(gid);
builder.append(' ');
builder.append(seinfo != null ? seinfo : "!");
return mInstaller.execute(builder.toString());
}
怎么样?很熟悉吧,这里的Installer其实调用的就是我们平时运行android项目很熟悉的install命令,原来android系统安装apk文件底层都是调用的adb命令。
总结:
android系统启动之后会解析固定目录下的apk文件,并执行解析,持久化apk信息,重新安装等操作;
解析Manifest流程:Zygote进程 –> SystemServer进程 –> PackgeManagerService服务 –> scanDirLI方法 –> scanPackageLI方法 –> PackageParser.parserPackage方法;
解析完成Manifest之后会将apk的Manifest信息保存在Settings对象中并持久化,然后执行重新安装的操作;
另外对android源码解析方法感兴趣的可参考我的:
android源码解析之(一)–>android项目构建过程
android源码解析之(二)–>异步消息机制
android源码解析之(三)–>异步任务AsyncTask
android源码解析之(四)–>HandlerThread
android源码解析之(五)–>IntentService
android源码解析之(六)–>Log
android源码解析之(七)–>LruCache
android源码解析之(八)–>Zygote进程启动流程
android源码解析之(九)–>SystemServer进程启动流程
android源码解析之(十)–>Launcher启动流程
android源码解析之(十一)–>应用进程启动流程
本文以同步至github中:github.com/yipianfengy…,欢迎star和follow
转载请标明出处:一片枫叶的专栏
上一篇文章中给大家分析了一下android系统启动之后调用PackageManagerService服务并解析系统特定目录,解析apk文件并安装的过程,这个安装过程实际上是没有图形界面的,底层调用的是我们平时比较熟悉的adb命令,那么我们平时安装apk文件的时候大部分是都过图形界面安装的,那么这种方式安装apk具体的流程是怎样的呢?
下面我们就来具体看一下apk的具体安装过程,相信大家都知道如果我们想在代码里执行apk的安装,那么一般都是这样:
Intent intent = new Intent(Intent.ACTION_VIEW)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.setDataAndType(Uri.parse("file://" + path),"application/vnd.android.package-archive")
context.startActivity(intent)
这样,我们就会打开安装apk文件的程序并执行安装逻辑了,那么这段代码具体是打开那个activity呢?好吧,从这个问题开始,我们来解析apk的安装流程…
这里跟大家简单介绍一下android的源码,平时我们使用的android.jar里面的java源码只是android系统源码的一部分,还有好多源码并没有打入到android.jar中,这里为大家推荐一个android源码的地址:github.com/android
里面根据android系统的不同模块包含了许多android模块的源码。
这里我们找到platform_packages_apps_packageinstaller库,这里面就是android系统安装程序的源码了。
这里我们找到其androidManifest.xml,然后我们来看一下其具体的定义:
...
好吧,这里我们大概看一下Activity的定义,这里我们重点看一下PackageInstallerActivity的定义:
恩?这里不就是我们刚刚定义的启动安装Apk activity的intent filter?好吧,所以说一开始我们调用的startActivity其实启动的就是PackageInstallerActivity,那么下面我们就看一下PackageInstellerActivity的具体实现:
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
mPm = getPackageManager();
mInstaller = mPm.getPackageInstaller();
mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
final Intent intent = getIntent();
if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) {
final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
final PackageInstaller.SessionInfo info = mInstaller.getSessionInfo(sessionId);
if (info == null || !info.sealed || info.resolvedBaseCodePath == null) {
Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring");
finish();
return;
}
mSessionId = sessionId;
mPackageURI = Uri.fromFile(new File(info.resolvedBaseCodePath));
mOriginatingURI = null;
mReferrerURI = null;
} else {
mSessionId = -1;
mPackageURI = intent.getData();
mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
mReferrerURI = intent.getParcelableExtra(Intent.EXTRA_REFERRER);
}
final boolean unknownSourcesAllowedByAdmin = isUnknownSourcesAllowedByAdmin();
final boolean unknownSourcesAllowedByUser = isUnknownSourcesEnabled();
boolean requestFromUnknownSource = isInstallRequestFromUnknownSource(intent);
mInstallFlowAnalytics = new InstallFlowAnalytics();
mInstallFlowAnalytics.setContext(this);
mInstallFlowAnalytics.setStartTimestampMillis(SystemClock.elapsedRealtime());
mInstallFlowAnalytics.setInstallsFromUnknownSourcesPermitted(unknownSourcesAllowedByAdmin
&& unknownSourcesAllowedByUser);
mInstallFlowAnalytics.setInstallRequestFromUnknownSource(requestFromUnknownSource);
mInstallFlowAnalytics.setVerifyAppsEnabled(isVerifyAppsEnabled());
mInstallFlowAnalytics.setAppVerifierInstalled(isAppVerifierInstalled());
mInstallFlowAnalytics.setPackageUri(mPackageURI.toString());
if (DeviceUtils.isWear(this)) {
showDialogInner(DLG_NOT_SUPPORTED_ON_WEAR);
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_NOT_ALLOWED_ON_WEAR);
return;
}
final String scheme = mPackageURI.getScheme();
if (scheme != null && !"file".equals(scheme) && !"package".equals(scheme)) {
Log.w(TAG, "Unsupported scheme " + scheme);
setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI);
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_FAILED_UNSUPPORTED_SCHEME);
finish();
return;
}
final PackageUtil.AppSnippet as;
if ("package".equals(mPackageURI.getScheme())) {
mInstallFlowAnalytics.setFileUri(false);
try {
mPkgInfo = mPm.getPackageInfo(mPackageURI.getSchemeSpecificPart(),
PackageManager.GET_PERMISSIONS | PackageManager.GET_UNINSTALLED_PACKAGES);
} catch (NameNotFoundException e) {
}
if (mPkgInfo == null) {
Log.w(TAG, "Requested package " + mPackageURI.getScheme()
+ " not available. Discontinuing installation");
showDialogInner(DLG_PACKAGE_ERROR);
setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
mInstallFlowAnalytics.setPackageInfoObtained();
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_FAILED_PACKAGE_MISSING);
return;
}
as = new PackageUtil.AppSnippet(mPm.getApplicationLabel(mPkgInfo.applicationInfo),
mPm.getApplicationIcon(mPkgInfo.applicationInfo));
} else {
mInstallFlowAnalytics.setFileUri(true);
final File sourceFile = new File(mPackageURI.getPath());
PackageParser.Package parsed = PackageUtil.getPackageInfo(sourceFile);
if (parsed == null) {
Log.w(TAG, "Parse error when parsing manifest. Discontinuing installation");
showDialogInner(DLG_PACKAGE_ERROR);
setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
mInstallFlowAnalytics.setPackageInfoObtained();
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_FAILED_TO_GET_PACKAGE_INFO);
return;
}
mPkgInfo = PackageParser.generatePackageInfo(parsed, null,
PackageManager.GET_PERMISSIONS, 0, 0, null,
new PackageUserState());
mPkgDigest = parsed.manifestDigest;
as = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile);
}
mInstallFlowAnalytics.setPackageInfoObtained();
setContentView(R.layout.install_start);
mInstallConfirm = findViewById(R.id.install_confirm_panel);
mInstallConfirm.setVisibility(View.INVISIBLE);
PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet);
mOriginatingUid = getOriginatingUid(intent);
if (!requestFromUnknownSource) {
initiateInstall();
return;
}
final boolean isManagedProfile = mUserManager.isManagedProfile();
if (!unknownSourcesAllowedByAdmin
|| (!unknownSourcesAllowedByUser && isManagedProfile)) {
showDialogInner(DLG_ADMIN_RESTRICTS_UNKNOWN_SOURCES);
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_BLOCKED_BY_UNKNOWN_SOURCES_SETTING);
} else if (!unknownSourcesAllowedByUser) {
showDialogInner(DLG_UNKNOWN_SOURCES);
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_BLOCKED_BY_UNKNOWN_SOURCES_SETTING);
} else {
initiateInstall();
}
}
这里我们主要先看一下PackageInstallerActivity的onCreate方法:
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
mPm = getPackageManager();
mInstaller = mPm.getPackageInstaller();
mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
final Intent intent = getIntent();
if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) {
final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
final PackageInstaller.SessionInfo info = mInstaller.getSessionInfo(sessionId);
if (info == null || !info.sealed || info.resolvedBaseCodePath == null) {
Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring");
finish();
return;
}
mSessionId = sessionId;
mPackageURI = Uri.fromFile(new File(info.resolvedBaseCodePath));
mOriginatingURI = null;
mReferrerURI = null;
} else {
mSessionId = -1;
mPackageURI = intent.getData();
mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
mReferrerURI = intent.getParcelableExtra(Intent.EXTRA_REFERRER);
}
final boolean unknownSourcesAllowedByAdmin = isUnknownSourcesAllowedByAdmin();
final boolean unknownSourcesAllowedByUser = isUnknownSourcesEnabled();
boolean requestFromUnknownSource = isInstallRequestFromUnknownSource(intent);
mInstallFlowAnalytics = new InstallFlowAnalytics();
mInstallFlowAnalytics.setContext(this);
mInstallFlowAnalytics.setStartTimestampMillis(SystemClock.elapsedRealtime());
mInstallFlowAnalytics.setInstallsFromUnknownSourcesPermitted(unknownSourcesAllowedByAdmin
&& unknownSourcesAllowedByUser);
mInstallFlowAnalytics.setInstallRequestFromUnknownSource(requestFromUnknownSource);
mInstallFlowAnalytics.setVerifyAppsEnabled(isVerifyAppsEnabled());
mInstallFlowAnalytics.setAppVerifierInstalled(isAppVerifierInstalled());
mInstallFlowAnalytics.setPackageUri(mPackageURI.toString());
if (DeviceUtils.isWear(this)) {
showDialogInner(DLG_NOT_SUPPORTED_ON_WEAR);
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_NOT_ALLOWED_ON_WEAR);
return;
}
final String scheme = mPackageURI.getScheme();
if (scheme != null && !"file".equals(scheme) && !"package".equals(scheme)) {
Log.w(TAG, "Unsupported scheme " + scheme);
setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI);
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_FAILED_UNSUPPORTED_SCHEME);
finish();
return;
}
final PackageUtil.AppSnippet as;
if ("package".equals(mPackageURI.getScheme())) {
mInstallFlowAnalytics.setFileUri(false);
try {
mPkgInfo = mPm.getPackageInfo(mPackageURI.getSchemeSpecificPart(),
PackageManager.GET_PERMISSIONS | PackageManager.GET_UNINSTALLED_PACKAGES);
} catch (NameNotFoundException e) {
}
if (mPkgInfo == null) {
Log.w(TAG, "Requested package " + mPackageURI.getScheme()
+ " not available. Discontinuing installation");
showDialogInner(DLG_PACKAGE_ERROR);
setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
mInstallFlowAnalytics.setPackageInfoObtained();
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_FAILED_PACKAGE_MISSING);
return;
}
as = new PackageUtil.AppSnippet(mPm.getApplicationLabel(mPkgInfo.applicationInfo),
mPm.getApplicationIcon(mPkgInfo.applicationInfo));
} else {
mInstallFlowAnalytics.setFileUri(true);
final File sourceFile = new File(mPackageURI.getPath());
PackageParser.Package parsed = PackageUtil.getPackageInfo(sourceFile);
if (parsed == null) {
Log.w(TAG, "Parse error when parsing manifest. Discontinuing installation");
showDialogInner(DLG_PACKAGE_ERROR);
setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
mInstallFlowAnalytics.setPackageInfoObtained();
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_FAILED_TO_GET_PACKAGE_INFO);
return;
}
mPkgInfo = PackageParser.generatePackageInfo(parsed, null,
PackageManager.GET_PERMISSIONS, 0, 0, null,
new PackageUserState());
mPkgDigest = parsed.manifestDigest;
as = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile);
}
mInstallFlowAnalytics.setPackageInfoObtained();
setContentView(R.layout.install_start);
mInstallConfirm = findViewById(R.id.install_confirm_panel);
mInstallConfirm.setVisibility(View.INVISIBLE);
PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet);
mOriginatingUid = getOriginatingUid(intent);
if (!requestFromUnknownSource) {
initiateInstall();
return;
}
final boolean isManagedProfile = mUserManager.isManagedProfile();
if (!unknownSourcesAllowedByAdmin
|| (!unknownSourcesAllowedByUser && isManagedProfile)) {
showDialogInner(DLG_ADMIN_RESTRICTS_UNKNOWN_SOURCES);
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_BLOCKED_BY_UNKNOWN_SOURCES_SETTING);
} else if (!unknownSourcesAllowedByUser) {
showDialogInner(DLG_UNKNOWN_SOURCES);
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_BLOCKED_BY_UNKNOWN_SOURCES_SETTING);
} else {
initiateInstall();
}
}
可以发现,在onCreate方法中,首先执行一些初始化操作,获取PackageManager和Installer、UserManager等对象,然后会根据当前Intent的信息最一些逻辑判断并弹出消息弹窗,我们可以看一下具体的消息弹窗类型:
private static final int DLG_BASE = 0;
private static final int DLG_UNKNOWN_SOURCES = DLG_BASE + 1;
private static final int DLG_PACKAGE_ERROR = DLG_BASE + 2;
private static final int DLG_OUT_OF_SPACE = DLG_BASE + 3;
private static final int DLG_INSTALL_ERROR = DLG_BASE + 4;
private static final int DLG_ALLOW_SOURCE = DLG_BASE + 5;
private static final int DLG_ADMIN_RESTRICTS_UNKNOWN_SOURCES = DLG_BASE + 6;
private static final int DLG_NOT_SUPPORTED_ON_WEAR = DLG_BASE + 7;
可以发现当分析Intent对象的时候,如果可以得到这样几种结果:不知道apk的来源,package信息错误,存储空间不够,安装时报,来源正确,允许未知来源的apk文件,在wear上不支持等,这样根据不同的消息类型会弹出不同的消息弹窗:
@Override
public Dialog onCreateDialog(int id, Bundle bundle) {
switch (id) {
case DLG_UNKNOWN_SOURCES:
return new AlertDialog.Builder(this)
.setTitle(R.string.unknown_apps_dlg_title)
.setMessage(R.string.unknown_apps_dlg_text)
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Log.i(TAG, "Finishing off activity so that user can navigate to settings manually");
finish();
}})
.setPositiveButton(R.string.settings, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Log.i(TAG, "Launching settings");
launchSecuritySettings();
}
})
.setOnCancelListener(this)
.create();
case DLG_ADMIN_RESTRICTS_UNKNOWN_SOURCES:
return new AlertDialog.Builder(this)
.setTitle(R.string.unknown_apps_dlg_title)
.setMessage(R.string.unknown_apps_admin_dlg_text)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
finish();
}
})
.setOnCancelListener(this)
.create();
case DLG_PACKAGE_ERROR :
return new AlertDialog.Builder(this)
.setTitle(R.string.Parse_error_dlg_title)
.setMessage(R.string.Parse_error_dlg_text)
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
finish();
}
})
.setOnCancelListener(this)
.create();
case DLG_OUT_OF_SPACE:
CharSequence appTitle = mPm.getApplicationLabel(mPkgInfo.applicationInfo);
String dlgText = getString(R.string.out_of_space_dlg_text,
appTitle.toString());
return new AlertDialog.Builder(this)
.setTitle(R.string.out_of_space_dlg_title)
.setMessage(dlgText)
.setPositiveButton(R.string.manage_applications, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent("android.intent.action.MANAGE_PACKAGE_STORAGE");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
finish();
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Log.i(TAG, "Canceling installation");
finish();
}
})
.setOnCancelListener(this)
.create();
case DLG_INSTALL_ERROR :
CharSequence appTitle1 = mPm.getApplicationLabel(mPkgInfo.applicationInfo);
String dlgText1 = getString(R.string.install_failed_msg,
appTitle1.toString());
return new AlertDialog.Builder(this)
.setTitle(R.string.install_failed)
.setNeutralButton(R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
finish();
}
})
.setMessage(dlgText1)
.setOnCancelListener(this)
.create();
case DLG_ALLOW_SOURCE:
CharSequence appTitle2 = mPm.getApplicationLabel(mSourceInfo);
String dlgText2 = getString(R.string.allow_source_dlg_text,
appTitle2.toString());
return new AlertDialog.Builder(this)
.setTitle(R.string.allow_source_dlg_title)
.setMessage(dlgText2)
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
setResult(RESULT_CANCELED);
finish();
}})
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
SharedPreferences prefs = getSharedPreferences(PREFS_ALLOWED_SOURCES,
Context.MODE_PRIVATE);
prefs.edit().putBoolean(mSourceInfo.packageName, true).apply();
startInstallConfirm();
}
})
.setOnCancelListener(this)
.create();
case DLG_NOT_SUPPORTED_ON_WEAR:
return new AlertDialog.Builder(this)
.setTitle(R.string.wear_not_allowed_dlg_title)
.setMessage(R.string.wear_not_allowed_dlg_text)
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
setResult(RESULT_OK);
finish();
}
})
.setOnCancelListener(this)
.create();
}
return null;
}
消息弹窗的主要作用,用于提示用户当前安装apk文件的特性。都知道android系统在android apk文件之前会解析器manifest文件,这个操作也是早onCreate方法中执行的:
PackageParser.Package parsed = PackageUtil.getPackageInfo(sourceFile)
我们具体看一下getPackageInfo方法的实现:
public static PackageParser.Package getPackageInfo(File sourceFile) {
final PackageParser parser = new PackageParser();
try {
PackageParser.Package pkg = parser.parseMonolithicPackage(sourceFile, 0);
parser.collectManifestDigest(pkg);
return pkg;
} catch (PackageParserException e) {
return null;
}
}
好吧,到了这里是不是代码变得很熟悉了?parseMonolithicPackage就是我们上一节分析的android系统解析manifest文件的过程,具体的可参考:blog.csdn.net/qq_23547831…
而collectManifestDigest方法,我们这里简单的介绍一下,其主要是要争apk的签名是否正确。好吧通过这两部我们就把apk文件的manifest和签名信息都解析完成并保存在了Package中。
接着往下走,在所有的解析完成之后我们会在onCreate方法中执行initiateInstall();方法,刚方法的主要作用是初始化安装。
private void initiateInstall() {
String pkgName = mPkgInfo.packageName;
// Check if there is already a package on the device with this name
// but it has been renamed to something else.
String[] oldName = mPm.canonicalToCurrentPackageNames(new String[] { pkgName });
if (oldName != null && oldName.length > 0 && oldName[0] != null) {
pkgName = oldName[0];
mPkgInfo.packageName = pkgName;
mPkgInfo.applicationInfo.packageName = pkgName;
}
// Check if package is already installed. display confirmation dialog if replacing pkg
try {
// This is a little convoluted because we want to get all uninstalled
// apps, but this may include apps with just data, and if it is just
// data we still want to count it as "installed".
mAppInfo = mPm.getApplicationInfo(pkgName,
PackageManager.GET_UNINSTALLED_PACKAGES);
if ((mAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
mAppInfo = null;
}
} catch (NameNotFoundException e) {
mAppInfo = null;
}
mInstallFlowAnalytics.setReplace(mAppInfo != null);
mInstallFlowAnalytics.setSystemApp(
(mAppInfo != null) && ((mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0));
// If we have a session id, we're invoked to verify the permissions for the given
// package. Otherwise, we start the install process.
if (mSessionId != -1) {
startInstallConfirm();
} else {
startInstall();
}
}
好吧,这里面有调用了startInstallConfirm方法,然后我们看一下startInstallConfirm方法的实现:
private void startInstallConfirm() {
...
//初始化安装确认界面
...
}
好吧,这个方法的实现比较简单,主要的实现逻辑就是现实该activity的用户界面,平时我们安装某一个应用的时候会弹出一个安装确认页面,还有一个确认和取消按钮,有印象么?其实就是在这里执行的界面初始化操作。
好吧,一般情况下在apk安装确认页面,我们会点击确认按钮执行安装逻辑吧?那么这里我们找一下确认按钮的点击事件:
public void onClick(View v) {
if (v == mOk) {
if (mOkCanInstall || mScrollView == null) {
mInstallFlowAnalytics.setInstallButtonClicked();
if (mSessionId != -1) {
mInstaller.setPermissionsResult(mSessionId, true);
mInstallFlowAnalytics.setFlowFinishedWithPackageManagerResult(
PackageManager.INSTALL_SUCCEEDED);
finish();
} else {
startInstall();
}
} else {
mScrollView.pageScroll(View.FOCUS_DOWN);
}
} else if (v == mCancel) {
setResult(RESULT_CANCELED);
if (mSessionId != -1) {
mInstaller.setPermissionsResult(mSessionId, false);
}
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_CANCELLED_BY_USER);
finish();
}
}
很明显了,这里当我们点击确认按钮的时候会执行startInstall方法,也就是开始执行安装逻辑:
private void startInstall() {
// Start subactivity to actually install the application
Intent newIntent = new Intent()
newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
mPkgInfo.applicationInfo)
newIntent.setData(mPackageURI)
newIntent.setClass(this, InstallAppProgress.class)
newIntent.putExtra(InstallAppProgress.EXTRA_MANIFEST_DIGEST, mPkgDigest)
newIntent.putExtra(
InstallAppProgress.EXTRA_INSTALL_FLOW_ANALYTICS, mInstallFlowAnalytics)
String installerPackageName = getIntent().getStringExtra(
Intent.EXTRA_INSTALLER_PACKAGE_NAME)
if (mOriginatingURI != null) {
newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI)
}
if (mReferrerURI != null) {
newIntent.putExtra(Intent.EXTRA_REFERRER, mReferrerURI)
}
if (mOriginatingUid != VerificationParams.NO_UID) {
newIntent.putExtra(Intent.EXTRA_ORIGINATING_UID, mOriginatingUid)
}
if (installerPackageName != null) {
newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME,
installerPackageName)
}
if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true)
newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT)
}
if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI)
startActivity(newIntent)
finish()
}
可以发现,点击确认按钮之后我们调用启用了一个新的Activity–>InstallAppProgress,这个Activity主要用于执行apk的安装逻辑了。
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
Intent intent = getIntent();
mAppInfo = intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
mInstallFlowAnalytics = intent.getParcelableExtra(EXTRA_INSTALL_FLOW_ANALYTICS);
mInstallFlowAnalytics.setContext(this);
mPackageURI = intent.getData();
final String scheme = mPackageURI.getScheme();
if (scheme != null && !"file".equals(scheme) && !"package".equals(scheme)) {
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_FAILED_UNSUPPORTED_SCHEME);
throw new IllegalArgumentException("unexpected scheme " + scheme);
}
mInstallThread = new HandlerThread("InstallThread");
mInstallThread.start();
mInstallHandler = new Handler(mInstallThread.getLooper());
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BROADCAST_ACTION);
registerReceiver(
mBroadcastReceiver, intentFilter, BROADCAST_SENDER_PERMISSION, null );
initView();
}
可以发现InstallAppProcess这个Activity的onCreate方法中主要初始化了一些成员变量,并调用initView方法,我们在iniTView方法中可以看到:
void initView() {
...
mInstallHandler.post(new Runnable() {
@Override
public void run() {
doPackageStage(pm, params);
}
});
...
}
经过一些view的初始化操作之后调用了doPackageStage方法,该方法主要是通过调用PackageInstaller执行apk文件的安装,这里就不在详细的介绍了,在apk文件安装完成之后PackageInstaller会发送一个安装完成的广播,刚刚我们在onCreate方法中注册了一个广播接收器,其可以用来接收apk安装完成的广播:
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final int statusCode = intent.getIntExtra(
PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE);
if (statusCode == PackageInstaller.STATUS_PENDING_USER_ACTION) {
context.startActivity((Intent)intent.getParcelableExtra(Intent.EXTRA_INTENT));
} else {
onPackageInstalled(statusCode);
}
}
};
这样apk安装完成之后,这里的广播接收器会接收到广播并执行onPackageInstalled方法,执行后续的处理逻辑,那么我们来看一下onPackageInstalled方法的具体实现逻辑:
void onPackageInstalled(int statusCode) {
Message msg = mHandler.obtainMessage(INSTALL_COMPLETE)
msg.arg1 = statusCode
mHandler.sendMessage(msg)
}
好吧,这里是发送Handler异步消息,我们来看一下异步消息的处理逻辑:
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case INSTALL_COMPLETE:
mInstallFlowAnalytics.setFlowFinishedWithPackageManagerResult(msg.arg1)
if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
Intent result = new Intent()
result.putExtra(Intent.EXTRA_INSTALL_RESULT, msg.arg1)
setResult(msg.arg1 == PackageInstaller.STATUS_SUCCESS
? Activity.RESULT_OK : Activity.RESULT_FIRST_USER,
result)
finish()
return
}
// Update the status text
mProgressBar.setVisibility(View.INVISIBLE)
// Show the ok button
int centerTextLabel
int centerExplanationLabel = -1
LevelListDrawable centerTextDrawable =
(LevelListDrawable) getDrawable(R.drawable.ic_result_status)
if (msg.arg1 == PackageInstaller.STATUS_SUCCESS) {
mLaunchButton.setVisibility(View.VISIBLE)
centerTextDrawable.setLevel(0)
centerTextLabel = R.string.install_done
// Enable or disable launch button
mLaunchIntent = getPackageManager().getLaunchIntentForPackage(
mAppInfo.packageName)
boolean enabled = false
if(mLaunchIntent != null) {
List list = getPackageManager().
queryIntentActivities(mLaunchIntent, 0)
if (list != null && list.size() > 0) {
enabled = true
}
}
if (enabled) {
mLaunchButton.setOnClickListener(InstallAppProgress.this)
} else {
mLaunchButton.setEnabled(false)
}
} else if (msg.arg1 == PackageInstaller.STATUS_FAILURE_STORAGE){
showDialogInner(DLG_OUT_OF_SPACE)
return
} else {
// Generic error handling for all other error codes.
centerTextDrawable.setLevel(1)
centerExplanationLabel = getExplanationFromErrorCode(msg.arg1)
centerTextLabel = R.string.install_failed
mLaunchButton.setVisibility(View.INVISIBLE)
}
if (centerTextDrawable != null) {
centerTextDrawable.setBounds(0, 0,
centerTextDrawable.getIntrinsicWidth(),
centerTextDrawable.getIntrinsicHeight())
mStatusTextView.setCompoundDrawablesRelative(centerTextDrawable, null,
null, null)
}
mStatusTextView.setText(centerTextLabel)
if (centerExplanationLabel != -1) {
mExplanationTextView.setText(centerExplanationLabel)
mExplanationTextView.setVisibility(View.VISIBLE)
} else {
mExplanationTextView.setVisibility(View.GONE)
}
mDoneButton.setOnClickListener(InstallAppProgress.this)
mOkPanel.setVisibility(View.VISIBLE)
break
default:
break
}
}
}
可以发现,当apk安装完成之后,我们会更新UI,显示完成和打开按钮,是不是和我们平时安装apk的逻辑对应上了?这时候我们可以看一下这两个按钮的点击事件。
public void onClick(View v) {
if(v == mDoneButton) {
if (mAppInfo.packageName != null) {
Log.i(TAG, "Finished installing "+mAppInfo.packageName);
}
finish();
} else if(v == mLaunchButton) {
startActivity(mLaunchIntent);
finish();
}
}
好吧,比较简单,点击完成按钮,直接finish掉这个activity,点击打开,则直接调用startActivity启动安装的应用,然后直接finish自身。
总结:
代码中执行intent.setDataAndType(Uri.parse(“file://” + path),”application/vnd.android.package-archive”);可以调起PackageInstallerActivity;
PackageInstallerActivity主要用于执行解析apk文件,解析manifest,解析签名等操作;
InstallAppProcess主要用于执行安装apk逻辑,用于初始化安装界面,用于初始化用户UI。并调用PackageInstaller执行安装逻辑;
InstallAppProcess内注册有广播,当安装完成之后接收广播,更新UI。显示apk安装完成界面;
另外对android源码解析方法感兴趣的可参考我的:
android源码解析之(一)–>android项目构建过程
android源码解析之(二)–>异步消息机制
android源码解析之(三)–>异步任务AsyncTask
android源码解析之(四)–>HandlerThread
android源码解析之(五)–>IntentService
android源码解析之(六)–>Log
android源码解析之(七)–>LruCache
android源码解析之(八)–>Zygote进程启动流程
android源码解析之(九)–>SystemServer进程启动流程
android源码解析之(十)–>Launcher启动流程
android源码解析之(十一)–>应用进程启动流程
android源码解析之(十二)–>系统启动并解析Manifest的流程
本文以同步至github中:github.com/yipianfengy…,欢迎star和follow
转载请标明出处:一片枫叶的专栏
好吧,终于要开始讲解Activity的启动流程了,Activity的启动流程相对复杂一下,涉及到了Activity中的生命周期方法,涉及到了Android体系的CS模式,涉及到了Android中进程通讯Binder机制等等,
首先介绍一下Activity,这里引用一下Android guide中对Activity的介绍:
An activity represents a single screen with a user interface. For example, an email application might have one activity that shows a list of new emails, another activity to compose an email, and another activity for reading emails. Although the activities work together to form a cohesive user experience in the email application, each one is independent of the others. As such, a different application can start any one of these activities (if the email application allows it). For example, a camera application can start the activity in the email application that composes new mail, in order for the user to share a picture.
英文不太好,这里就不献丑了,这里介绍的Activity的大概意思就是说,activity在Android系统中代表的就是一个屏幕,一个App就是由许多个不同的Acitivty组成的,并且不同进程之间的Activity是可以相互调用的。
在介绍Activity的启动流程之前,我们先介绍几个概念:
protected void onCreate(Bundle savedInstanceState);
protected void onRestart();
protected void onStart();
protected void onResume();
protected void onPause();
protected void onStop();
protected void onDestory();
以上为Activity生命周期中的各个时期的回调方法,在不同的方法中我们可以执行不同的逻辑。
关于Activity生命周期的详细介绍可以参考: Android activity的生命周期
activity启动时可以设置不同的启动模式,主要是:standrand,singleTop,singleTask,instance等四种启动模式,不同的启动模式在启动Activity时会执行不同的逻辑,系统会按不同的启动模式将Activity存放到不同的activity栈中。
关于Activity启动模式的详细介绍,可以参考: Android任务和返回栈完全解析
在Manifest.xml中定义Activity的时候,Activity默认是属于进程名称为包名的进程的,当然这时候是可以指定Activity的启动进程,所以在Activity启动时首先会检测当前Activity所属的进程是否已经启动,若进程没有启动,则首先会启动该进程,并在该进程启动之后才会执行Activity的启动过程。
Intent启动Activity的方式
Intent启动Activity分为两种,显示启动和隐士启动,显示启动就是在初始化Intent对象的时候直接引用需要启动的Activity的字节码,显示引用的好处就是可以直接告诉Intent对象启动的Activity对象不需要执行intent filter索引需要启动哪一个Activity,但是显示引用不能启动其他进程的Activity对象,因为无法获取其他进程的Activity对象的字节码,而隐式启动则可以通过配置Intent Filter启动其他进程的Activity对象,因此在应用内,我们一般都是使用显示启动的方式启动Activity,而如果需要启动其他应用的Activity时,一般使用隐式启动的方式。
Android Framework层的CS模式
通过前几篇文章的介绍我们知道android系统在启动过程中会执行这样的逻辑:
Zygote进程 –> SystemServer进程 –> 各种系统服务 –> 应用进程
在Actvity启动过程中,其实是应用进程与SystemServer进程相互配合启动Activity的过程,其中应用进程主要用于执行具体的Activity的启动过程,回调生命周期方法等操作,而SystemServer进程则主要是调用其中的各种服务,将Activity保存在栈中,协调各种系统资源等操作。Android系统进程间通讯Binder机制
Android系统存了Zygote进程和SystemServer进程以及各种应用进程等,为了能够实现各种进程之间的通讯,Android系统采用了自己的进程间通讯方式Binder机制。其中主要涉及到了四种角色:Binder Client,Binder Server,Binder Manager, Binder driver。各种角色之间的关系可以参考下面这张图的介绍:
好吧,前面我们介绍了一些Activity启动过程中需要的相关知识点,下面我们开始Activity启动流程的讲解。。。。
还记得前面我们讲过的Launcher启动流程么?可以参考:android源码解析之(十)–>Launcher启动流程
在这篇文章中我们说Launcher启动之后会将各个应用包名和icon与app name保存起来,然后执行icon的点击事件的时候调用startActivity方法:
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
Intent intent = intentForPosition(position);
startActivity(intent);
}
protected Intent intentForPosition(int position) {
ActivityAdapter adapter = (ActivityAdapter) mAdapter;
return adapter.intentForPosition(position);
}
public Intent intentForPosition(int position) {
if (mActivitiesList == null) {
return null;
}
Intent intent = new Intent(mIntent);
ListItem item = mActivitiesList.get(position);
intent.setClassName(item.packageName, item.className);
if (item.extras != null) {
intent.putExtras(item.extras);
}
return intent;
}
可以发现,我们在启动Activity的时候,执行的逻辑就是创建一个Intent对象,然后初始化Intent对象,使用隐式启动的方式启动该Acvitity,这里为什么不能使用显示启动的方式呢?
这是因为Launcher程序启动的Activity一般都是启动一个新的应用进程,该进程与Launcher进程不是在同一个进程中,所以也就无法引用到启动的Activity字节码,自然也就无法启动该Activity了。
继续,我们查看startActivity方法的具体实现:
一:开始请求执行启动Activity
MyActivity.startActivity()
Activity.startActivity()
Activity.startActivityForResult
Instrumentation.execStartActivty
ActivityManagerNative.getDefault().startActivityAsUser()
在我们的Activity中调用startActivity方法,会执行Activity中的startActivity
@Override
public void startActivity(Intent intent) {
this.startActivity(intent, null);
}
然后在Activity中的startActivity方法体里调用了startActivity的重载方法,这里我们看一下其重载方法的实现:
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
if (options != null) {
startActivityForResult(intent, -1, options);
} else {
startActivityForResult(intent, -1);
}
}
由于在上一步骤中我们传递的Bunde对象为空,所以这里我们执行的是else分支的逻辑,所以这里调用了startActivityForResult方法,并且传递的参数为intent和-1.
注意:通过这里的代码我们可以发现,其实我们在Activity中调用startActivity的内部也是调用的startActivityForResult的。那么为什么调用startActivityForResult可以在Activity中回调onActivityResult而调用startActivity则不可以呢?可以发现其主要的区别是调用startActivity内部调用startActivityForResult传递的传输requestCode值为-1,也就是说我们在Activity调用startActivityForResult的时候传递的requestCode值为-1的话,那么onActivityResult是不起作用的。
实际上,经测试requestCode的值小于0的时候都是不起作用的,所以当我们调用startActivityForResult的时候需要注意这一点。
好吧,我们继续往下看,startActivityForResult方法的具体实现:
public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
if (mParent == null) {
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
if (ar != null) {
mMainThread.sendActivityResult(
mToken, mEmbeddedID, requestCode, ar.getResultCode(),
ar.getResultData());
}
if (requestCode >= 0) {
// If this start is requesting a result, we can avoid making
// the activity visible until the result is received. Setting
// this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
// activity hidden during this time, to avoid flickering.
// This can only be done when a result is requested because
// that guarantees we will get information back when the
// activity is finished, no matter what happens to it.
mStartedActivity = true;
}
cancelInputsAndStartExitTransition(options);
// TODO Consider clearing/flushing other event sources and events for child windows.
} else {
if (options != null) {
mParent.startActivityFromChild(this, intent, requestCode, options);
} else {
// Note we want to go through this method for compatibility with
// existing applications that may have overridden it.
mParent.startActivityFromChild(this, intent, requestCode);
}
}
}
可以发现由于我们是第一次启动Activity,所以这里的mParent为空,所以会执行if分之,然后调用mInstrumentation.execStartActivity方法,并且这里需要注意的是,有一个判断逻辑:
if (requestCode >= 0) {
mStartedActivity = true;
}
通过注释也验证了我们刚刚的说法即,调用startActivityForResult的时候只有requestCode的值大于等于0,onActivityResult才会被回调。
然后我们看一下mInstrumentation.execStartActivity方法的实现。在查看execStartActivity方法之前,我们需要对mInstrumentation对象有一个了解?什么是Instrumentation?Instrumentation是android系统中启动Activity的一个实际操作类,也就是说Activity在应用进程端的启动实际上就是Instrumentation执行的,那么为什么说是在应用进程端的启动呢?实际上acitivty的启动分为应用进程端的启动和SystemServer服务进程端的启动的,多个应用进程相互配合最终完成了Activity在系统中的启动的,而在应用进程端的启动实际的操作类就是Intrumentation来执行的,可能还是有点绕口,没关系,随着我们慢慢的解析大家就会对Instrumentation的认识逐渐加深的。
可以发现execStartActivity方法传递的几个参数:
this,为启动Activity的对象;
contextThread,为Binder对象,是主进程的context对象;
token,也是一个Binder对象,指向了服务端一个ActivityRecord对象;
target,为启动的Activity;
intent,启动的Intent对象;
requestCode,请求码;
options,参数;
这样就调用了Imstrument.execStartActivity方法了:
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
...
try {
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess();
int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
return null;
}
我们发现在这个方法中主要调用ActivityManagerNative.getDefault().startActivity方法,那么ActivityManagerNative又是个什么鬼呢?查看一下getDefault()对象的实现:
static public IActivityManager getDefault() {
return gDefault.get();
}
好吧,相当之简单直接返回的是gDefault.get(),那么gDefault又是什么呢?
private static final Singleton gDefault = new Singleton() {
protected IActivityManager create() {
IBinder b = ServiceManager.getService("activity");
if (false) {
Log.v("ActivityManager", "default service binder = " + b);
}
IActivityManager am = asInterface(b);
if (false) {
Log.v("ActivityManager", "default service = " + am);
}
return am;
}
};
可以发现启动过asInterface()方法创建,然后我们继续看一下asInterface方法的实现:
static public IActivityManager asInterface(IBinder obj) {
if (obj == null) {
return null;
}
IActivityManager in =
(IActivityManager)obj.queryLocalInterface(descriptor);
if (in != null) {
return in;
}
return new ActivityManagerProxy(obj);
}
好吧,最后直接返回一个ActivityManagerProxy对象,而ActivityManagerProxy继承与IActivityManager,到了这里就引出了我们android系统中很重要的一个概念:Binder机制。我们知道应用进程与SystemServer进程属于两个不同的进程,进程之间需要通讯,android系统采取了自身设计的Binder机制,这里的ActivityManagerProxy和ActivityManagerNative都是继承与IActivityManager的而SystemServer进程中的ActivityManagerService对象则继承与ActivityManagerNative。简单的表示:
Binder接口 –> ActivityManagerNative/ActivityManagerProxy –> ActivityManagerService;
这样,ActivityManagerNative与ActivityManagerProxy相当于一个Binder的客户端而ActivityManagerService相当于Binder的服务端,这样当ActivityManagerNative调用接口方法的时候底层通过Binder driver就会将请求数据与请求传递给server端,并在server端执行具体的接口逻辑。需要注意的是Binder机制是单向的,是异步的,也就是说只能通过client端向server端传递数据与请求而不同等待服务端的返回,也无法返回,那如果SystemServer进程想向应用进程传递数据怎么办?这时候就需要重新定义一个Binder请求以SystemServer为client端,以应用进程为server端,这样就是实现了两个进程之间的双向通讯。
好了,说了这么多我们知道这里的ActivityManagerNative是ActivityManagerService在应用进程的一个client就好了,通过它就可以滴啊用ActivityManagerService的方法了。
继续往下卡,我们调用的是:
int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options)
这里通过我们刚刚的分析,ActivityManagerNative.getDefault()方法会返回一个ActivityManagerProxy对象,那么我们看一下ActivityManagerProxy对象的startActivity方法:
public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(caller != null ? caller.asBinder() : null);
data.writeString(callingPackage);
intent.writeToParcel(data, 0);
data.writeString(resolvedType);
data.writeStrongBinder(resultTo);
data.writeString(resultWho);
data.writeInt(requestCode);
data.writeInt(startFlags);
if (profilerInfo != null) {
data.writeInt(1);
profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
data.writeInt(0);
}
if (options != null) {
data.writeInt(1);
options.writeToParcel(data, 0);
} else {
data.writeInt(0);
}
mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
reply.readException();
int result = reply.readInt();
reply.recycle();
data.recycle();
return result;
}
这里就涉及到了具体的Binder数据传输机制了,我们不做过多的分析,知道通过数据传输之后就会调用SystemServer进程的ActivityManagerService的startActivity就好了。
以上其实都是发生在应用进程中,下面开始调用的ActivityManagerService的执行时发生在SystemServer进程。
二:ActivityManagerService接收启动Activity的请求
ActivityManagerService.startActivity()
ActvityiManagerService.startActivityAsUser()
ActivityStackSupervisor.startActivityMayWait()
ActivityStackSupervisor.startActivityLocked()
ActivityStackSupervisor.startActivityUncheckedLocked()
ActivityStackSupervisor.startActivityLocked()
ActivityStackSupervisor.resumeTopActivitiesLocked()
ActivityStackSupervisor.resumeTopActivityInnerLocked()
好吧,代码量比较大,慢慢看,首先看一下ActivityManagerService.startActivity的具体实现;
@Override
public final int startActivity(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle options) {
return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
resultWho, requestCode, startFlags, profilerInfo, options,
UserHandle.getCallingUserId());
}
可以看到,该方法并没有实现什么逻辑,直接调用了startActivityAsUser方法,我们继续看一下startActivityAsUser方法的实现:
@Override
public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle options, int userId) {
enforceNotIsolatedCaller("startActivity");
userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
false, ALLOW_FULL_ONLY, "startActivity", null);
return mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent,
resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
profilerInfo, null, null, options, false, userId, null, null);
}
可以看到这里只是进行了一些关于userid的逻辑判断,然后就调用mStackSupervisor.startActivityMayWait方法,下面我们来看一下这个方法的具体实现:
final int startActivityMayWait(IApplicationThread caller, int callingUid,
String callingPackage, Intent intent, String resolvedType,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
IBinder resultTo, String resultWho, int requestCode, int startFlags,
ProfilerInfo profilerInfo, WaitResult outResult, Configuration config,
Bundle options, boolean ignoreTargetSecurity, int userId,
IActivityContainer iContainer, TaskRecord inTask) {
...
int res = startActivityLocked(caller, intent, resolvedType, aInfo,
voiceSession, voiceInteractor, resultTo, resultWho,
requestCode, callingPid, callingUid, callingPackage,
realCallingPid, realCallingUid, startFlags, options, ignoreTargetSecurity,
componentSpecified, null, container, inTask);
...
return res;
}
这个方法中执行了启动Activity的一些其他逻辑判断,在经过判断逻辑之后调用startActivityLocked方法:
final int startActivityLocked(IApplicationThread caller,
Intent intent, String resolvedType, ActivityInfo aInfo,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
IBinder resultTo, String resultWho, int requestCode,
int callingPid, int callingUid, String callingPackage,
int realCallingPid, int realCallingUid, int startFlags, Bundle options,
boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,
ActivityContainer container, TaskRecord inTask) {
int err = ActivityManager.START_SUCCESS;
...
err = startActivityUncheckedLocked(r, sourceRecord, voiceSession, voiceInteractor,
startFlags, true, options, inTask);
...
return err;
}
这个方法中主要构造了ActivityManagerService端的Activity对象–>ActivityRecord,并根据Activity的启动模式执行了相关逻辑。然后调用了startActivityUncheckedLocked方法:
final int startActivityUncheckedLocked(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags,
boolean doResume, Bundle options, TaskRecord inTask) {
...
ActivityStack.logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task);
targetStack.mLastPausedActivity = null;
targetStack.startActivityLocked(r, newTask, doResume, keepCurTransition, options);
if (!launchTaskBehind) {
// Don't set focus on an activity that's going to the back.
mService.setFocusedActivityLocked(r, "startedActivity");
}
return ActivityManager.START_SUCCESS;
}
startActivityUncheckedLocked方法中只要执行了不同启动模式不同栈的处理,并最后调用了startActivityLocked的重载方法:
final void startActivityLocked(ActivityRecord r, boolean newTask,
boolean doResume, boolean keepCurTransition, Bundle options) {
...
if (doResume) {
mStackSupervisor.resumeTopActivitiesLocked(this, r, options);
}
}
这个startActivityLocked方法主要执行初始化了windowManager服务,然后调用resumeTopActivitiesLocked方法:
boolean resumeTopActivitiesLocked(ActivityStack targetStack, ActivityRecord target,
Bundle targetOptions) {
if (targetStack == null) {
targetStack = mFocusedStack;
}
boolean result = false;
if (isFrontStack(targetStack)) {
result = targetStack.resumeTopActivityLocked(target, targetOptions);
}
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ArrayList stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
if (stack == targetStack) {
continue;
}
if (isFrontStack(stack)) {
stack.resumeTopActivityLocked(null);
}
}
}
return result;
}
可以发现经过循环逻辑判断之后,最终调用了resumeTopActivityLocked方法:
final boolean resumeTopActivityLocked(ActivityRecord prev) {
return resumeTopActivityLocked(prev, null);
}
然后调用:
final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {
if (mStackSupervisor.inResumeTopActivity) {
return false;
}
boolean result = false;
try {
mStackSupervisor.inResumeTopActivity = true;
if (mService.mLockScreenShown == ActivityManagerService.LOCK_SCREEN_LEAVING) {
mService.mLockScreenShown = ActivityManagerService.LOCK_SCREEN_HIDDEN;
mService.updateSleepIfNeededLocked();
}
result = resumeTopActivityInnerLocked(prev, options);
} finally {
mStackSupervisor.inResumeTopActivity = false;
}
return result;
}
继续调用resumeTopActivityInnerLocked方法:
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, Bundle options) {
...
if (mResumedActivity != null) {
if (DEBUG_STATES) Slog.d(TAG_STATES,
"resumeTopActivityLocked: Pausing " + mResumedActivity);
pausing |= startPausingLocked(userLeaving, false, true, dontWaitForPause);
} ...
return true;
}
经过一系列处理逻辑之后最终调用了startPausingLocked方法,这个方法作用就是让系统中栈中的Activity执行onPause方法。
三:执行栈顶Activity的onPause方法
ActivityStack.startPausingLocked()
IApplicationThread.schudulePauseActivity()
ActivityThread.sendMessage()
ActivityThread.H.sendMessage();
ActivityThread.H.handleMessage()
ActivityThread.handlePauseActivity()
ActivityThread.performPauseActivity()
Activity.performPause()
Activity.onPause()
ActivityManagerNative.getDefault().activityPaused(token)
ActivityManagerService.activityPaused()
ActivityStack.activityPausedLocked()
ActivityStack.completePauseLocked()
ActivityStack.resumeTopActivitiesLocked()
ActivityStack.resumeTopActivityLocked()
ActivityStack.resumeTopActivityInnerLocked()
ActivityStack.startSpecificActivityLocked
好吧,方法比较多也比较乱,首先来看startPausingLocked方法:
final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping, boolean resuming,
boolean dontWait) {
...
if (prev.app != null && prev.app.thread != null) {
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev);
try {
EventLog.writeEvent(EventLogTags.AM_PAUSE_ACTIVITY,
prev.userId, System.identityHashCode(prev),
prev.shortComponentName);
mService.updateUsageStats(prev, false);
prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
userLeaving, prev.configChangeFlags, dontWait);
} catch (Exception e) {
// Ignore exception, if process died other code will cleanup.
Slog.w(TAG, "Exception thrown during pause", e);
mPausingActivity = null;
mLastPausedActivity = null;
mLastNoHistoryActivity = null;
}
} else {
mPausingActivity = null;
mLastPausedActivity = null;
mLastNoHistoryActivity = null;
}
...
}
可以看到这里执行了pre.app.thread.schedulePauseActivity方法,通过分析不难发现这里的thread是一个IApplicationThread类型的对象,而在ActivityThread中也定义了一个ApplicationThread的类,其继承了IApplicationThread,并且都是Binder对象,不难看出这里的IAppcation是一个Binder的client端而ActivityThread中的ApplicationThread是一个Binder对象的server端,所以通过这里的thread.schedulePauseActivity实际上调用的就是ApplicationThread的schedulePauseActivity方法。
这里的ApplicationThread可以和ActivityManagerNative对于一下:
通过ActivityManagerNative –> ActivityManagerService实现了应用进程与SystemServer进程的通讯
通过AppicationThread <– IApplicationThread实现了SystemServer进程与应用进程的通讯
然后我们继续看一下ActivityThread中schedulePauseActivity的具体实现:
public final void schedulePauseActivity(IBinder token, boolean finished,
boolean userLeaving, int configChanges, boolean dontReport) {
sendMessage(
finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY,
token,
(userLeaving ? 1 : 0) | (dontReport ? 2 : 0),
configChanges);
}
发送了PAUSE_ACTIVITY_FINISHING消息,然后看一下sendMessage的实现方法:
private void sendMessage(int what, Object obj, int arg1, int arg2) {
sendMessage(what, obj, arg1, arg2, false);
}
调用了其重载方法:
private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
if (DEBUG_MESSAGES) Slog.v(
TAG, "SCHEDULE " + what + " " + mH.codeToString(what)
+ ": " + arg1 + " / " + obj);
Message msg = Message.obtain();
msg.what = what;
msg.obj = obj;
msg.arg1 = arg1;
msg.arg2 = arg2;
if (async) {
msg.setAsynchronous(true);
}
mH.sendMessage(msg);
}
最终调用了mH的sendMessage方法,mH是在ActivityThread中定义的一个Handler对象,主要处理SystemServer进程的消息,我们看一下其handleMessge方法的实现:
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
...
case PAUSE_ACTIVITY_FINISHING:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
handlePauseActivity((IBinder)msg.obj, true, (msg.arg1&1) != 0, msg.arg2,
(msg.arg1&1) != 0);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
...
}
可以发现其调用了handlePauseActivity方法:
private void handlePauseActivity(IBinder token, boolean finished,
boolean userLeaving, int configChanges, boolean dontReport) {
ActivityClientRecord r = mActivities.get(token);
if (r != null) {
if (userLeaving) {
performUserLeavingActivity(r);
}
r.activity.mConfigChangeFlags |= configChanges;
performPauseActivity(token, finished, r.isPreHoneycomb());
if (r.isPreHoneycomb()) {
QueuedWork.waitToFinish();
}
if (!dontReport) {
try {
ActivityManagerNative.getDefault().activityPaused(token);
} catch (RemoteException ex) {
}
}
mSomeActivitiesChanged = true;
}
}
然后在方法体内部通过调用performPauseActivity方法来实现对栈顶Activity的onPause生命周期方法的回调,可以具体看一下他的实现:
final Bundle performPauseActivity(IBinder token, boolean finished,
boolean saveState) {
ActivityClientRecord r = mActivities.get(token);
return r != null ? performPauseActivity(r, finished, saveState) : null;
}
然后调用其重载方法:
final Bundle performPauseActivity(ActivityClientRecord r, boolean finished,
boolean saveState) {
...
mInstrumentation.callActivityOnPause(r.activity);
...
return !r.activity.mFinished && saveState ? r.state : null;
}
这样回到了mInstrumentation的callActivityOnPuase方法:
public void callActivityOnPause(Activity activity) {
activity.performPause();
}
呵呵,原来最终回调到了Activity的performPause方法:
final void performPause() {
mDoReportFullyDrawn = false;
mFragments.dispatchPause();
mCalled = false;
onPause();
mResumed = false;
if (!mCalled && getApplicationInfo().targetSdkVersion
>= android.os.Build.VERSION_CODES.GINGERBREAD) {
throw new SuperNotCalledException(
"Activity " + mComponent.toShortString() +
" did not call through to super.onPause()");
}
mResumed = false;
}
终于,太不容易了,回调到了Activity的onPause方法,哈哈,Activity生命周期中的第一个生命周期方法终于被我们找到了。。。。也就是说我们在启动一个Activity的时候最先被执行的是栈顶的Activity的onPause方法。记住这点吧,面试的时候经常会问到类似的问题。
然后回到我们的handlePauseActivity方法,在该方法的最后面执行了ActivityManagerNative.getDefault().activityPaused(token);方法,这是应用进程告诉服务进程,栈顶Activity已经执行完成onPause方法了,通过前面我们的分析,我们知道这句话最终会被ActivityManagerService的activityPaused方法执行。
@Override
public final void activityPaused(IBinder token) {
final long origId = Binder.clearCallingIdentity();
synchronized(this) {
ActivityStack stack = ActivityRecord.getStackLocked(token);
if (stack != null) {
stack.activityPausedLocked(token, false);
}
}
Binder.restoreCallingIdentity(origId);
}
可以发现,该方法内部会调用ActivityStack的activityPausedLocked方法,好吧,继续看一下activityPausedLocked方法的实现:
final void activityPausedLocked(IBinder token, boolean timeout) {
...
if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to PAUSED: " + r
+ (timeout ? " (due to timeout)" : " (pause complete)"));
completePauseLocked(true);
...
}
然后执行了completePauseLocked方法:
private void completePauseLocked(boolean resumeNext) {
...
if (resumeNext) {
final ActivityStack topStack = mStackSupervisor.getFocusedStack();
if (!mService.isSleepingOrShuttingDown()) {
mStackSupervisor.resumeTopActivitiesLocked(topStack, prev, null);
} else {
mStackSupervisor.checkReadyForSleepLocked();
ActivityRecord top = topStack.topRunningActivityLocked(null);
if (top == null || (prev != null && top != prev)) {
// If there are no more activities available to run,
// do resume anyway to start something. Also if the top
// activity on the stack is not the just paused activity,
// we need to go ahead and resume it to ensure we complete
// an in-flight app switch.
mStackSupervisor.resumeTopActivitiesLocked(topStack, null, null);
}
}
}
...
}
经过了一系列的逻辑之后,又调用了resumeTopActivitiesLocked方法,又回到了第二步中解析的方法中了,这样经过
resumeTopActivitiesLocked –>
ActivityStack.resumeTopActivityLocked() –>
resumeTopActivityInnerLocked –>
startSpecificActivityLocked
好吧,我们看一下startSpecificActivityLocked的具体实现:
void startSpecificActivityLocked(ActivityRecord r,
boolean andResume, boolean checkConfig) {
ProcessRecord app = mService.getProcessRecordLocked(r.processName,
r.info.applicationInfo.uid, true);
r.task.stack.setLaunchTime(r);
if (app != null && app.thread != null) {
try {
if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0
|| !"android".equals(r.info.packageName)) {
app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode,
mService.mProcessStats);
}
realStartActivityLocked(r, app, andResume, checkConfig);
return;
} catch (RemoteException e) {
Slog.w(TAG, "Exception when starting activity "
+ r.intent.getComponent().flattenToShortString(), e);
}
}
mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
"activity", r.intent.getComponent(), false, false, true);
}
可以发现在这个方法中,首先会判断一下需要启动的Activity所需要的应用进程是否已经启动,若启动的话,则直接调用realStartAtivityLocked方法,否则调用startProcessLocked方法,用于启动应用进程。
这样关于启动Activity时的第三步骤就已经执行完成了,这里主要是实现了对栈顶Activity执行onPause
方法,而这个方法首先判断需要启动的Activity所属的进程是否已经启动,若已经启动则直接调用启动Activity的方法,否则将先启动Activity的应用进程,然后在启动该Activity。
四:启动Activity所属的应用进程
关于如何启动应用进程,前面的一篇文章已经做了介绍,可参考: android源码解析之(十一)–>应用进程启动流程 这里在简单的介绍一下
ActivityManagerService.startProcessLocked()
Process.start()
ActivityThread.main()
ActivityThread.attach()
ActivityManagerNative.getDefault().attachApplication()
ActivityManagerService.attachApplication()
好吧,首先看一下startProcessLocked()方法的具体实现:
private final void startProcessLocked(ProcessRecord app,
String hostingType, String hostingNameStr) {
startProcessLocked(app, hostingType, hostingNameStr, null ,
null , null );
}
然后回调了其重载的startProcessLocked方法:
private final void startProcessLocked(ProcessRecord app, String hostingType,
String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
...
boolean isActivityProcess = (entryPoint == null);
if (entryPoint == null) entryPoint = "android.app.ActivityThread";
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
app.processName);
checkTime(startTime, "startProcess: asking zygote to start proc");
Process.ProcessStartResult startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
app.info.dataDir, entryPointArgs);
checkTime(startTime, "startProcess: returned from zygote!");
...
}
可以发现其经过一系列的初始化操作之后调用了Process.start方法,并且传入了启动的类名“android.app.ActivityThread”:
public static final ProcessStartResult start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
int debugFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
String instructionSet,
String appDataDir,
String[] zygoteArgs) {
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
debugFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
throw new RuntimeException(
"Starting VM process through Zygote failed", ex);
}
}
然后调用了startViaZygote方法:
private static ProcessStartResult startViaZygote(final String processClass,
final String niceName,
final int uid, final int gid,
final int[] gids,
int debugFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
String instructionSet,
String appDataDir,
String[] extraArgs)
throws ZygoteStartFailedEx {
synchronized(Process.class) {
...
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
}
}
继续查看一下zygoteSendArgsAndGetResult方法的实现:
private static ProcessStartResult zygoteSendArgsAndGetResult(
ZygoteState zygoteState, ArrayList args)
throws ZygoteStartFailedEx {
try {
/**
* See com.android.internal.os.ZygoteInit.readArgumentList()
* Presently the wire format to the zygote process is:
* a) a count of arguments (argc, in essence)
* b) a number of newline-separated argument strings equal to count
*
* After the zygote process reads these it will write the pid of
* the child or -1 on failure, followed by boolean to
* indicate whether a wrapper process was used.
*/
final BufferedWriter writer = zygoteState.writer;
final DataInputStream inputStream = zygoteState.inputStream;
writer.write(Integer.toString(args.size()));
writer.newLine();
int sz = args.size();
for (int i = 0; i < sz; i++) {
String arg = args.get(i);
if (arg.indexOf('\n') >= 0) {
throw new ZygoteStartFailedEx(
"embedded newlines not allowed");
}
writer.write(arg);
writer.newLine();
}
writer.flush();
ProcessStartResult result = new ProcessStartResult();
result.pid = inputStream.readInt();
if (result.pid < 0) {
throw new ZygoteStartFailedEx("fork() failed");
}
result.usingWrapper = inputStream.readBoolean();
return result;
} catch (IOException ex) {
zygoteState.close();
throw new ZygoteStartFailedEx(ex);
}
}
可以发现其最终调用了Zygote并通过socket通信的方式让Zygote进程fork除了一个新的进程,并根据我们刚刚传递的”android.app.ActivityThread”字符串,反射出该对象并执行ActivityThread的main方法。这样我们所要启动的应用进程这时候其实已经启动了,但是还没有执行相应的初始化操作。
为什么我们平时都将ActivityThread称之为ui线程或者是主线程,这里可以看出,应用进程被创建之后首先执行的是ActivityThread的main方法,所以我们将ActivityThread成为主线程。
好了,这时候我们看一下ActivityThread的main方法的实现逻辑。
public static void main(String[] args) {
...
Process.setArgV0("");
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
在main方法中主要执行了一些初始化的逻辑,并且创建了一个UI线程消息队列,这也就是为什么我们可以在主线程中随意的创建Handler而不会报错的原因,这里提出一个问题,大家可以思考一下:子线程可以创建Handler么?可以的话应该怎么做?
然后执行了ActivityThread的attach方法,这里我们看一下attach方法执行了那些逻辑操作。
private void attach(boolean system) {
...
final IActivityManager mgr = ActivityManagerNative.getDefault();
try {
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
// Ignore
}
...
}
刚刚我们已经分析过ActivityManagerNative是ActivityManagerService的Binder client,所以这里调用了attachApplication实际上就是通过Binder机制调用了ActivityManagerService的attachApplication,具体调用的过程,我们看一下ActivityManagerService是如何实现的:
@Override
public final void attachApplication(IApplicationThread thread) {
synchronized (this) {
int callingPid = Binder.getCallingPid();
final long origId = Binder.clearCallingIdentity();
attachApplicationLocked(thread, callingPid);
Binder.restoreCallingIdentity(origId);
}
}
可以发现其回调了attachApplicationLocked方法,我们看一下这个方法的实现逻辑。
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) {
...
// See if the top visible activity is waiting to run in this process...
if (normalMode) {
try {
if (mStackSupervisor.attachApplicationLocked(app)) {
didSomething = true;
}
} catch (Exception e) {
Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
badApp = true;
}
}
...
return true;
}
该方法执行了一系列的初始化操作,这样我们整个应用进程已经启动起来了。终于可以开始activity的启动逻辑了,O(∩_∩)O哈哈~
五:执行启动Acitivity
ActivityStackSupervisor.attachApplicationLocked()
ActivityStackSupervisor.realStartActivityLocked()
IApplicationThread.scheduleLauncherActivity()
ActivityThread.sendMessage()
ActivityThread.H.sendMessage()
ActivityThread.H.handleMessage()
ActivityThread.handleLauncherActivity()
ActivityThread.performLauncherActivity()
Instrumentation.callActivityOnCreate()
Activity.onCreate()
ActivityThread.handleResumeActivity()
ActivityThread.performResumeActivity()
Activity.performResume()
Instrumentation.callActivityOnResume()
Activity.onResume()
ActivityManagerNative.getDefault().activityResumed(token)
首先看一下attachApplicationLocked方法的实现:
boolean attachApplicationLocked(ProcessRecord app) throws RemoteException {
final String processName = app.processName;
boolean didSomething = false;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
ArrayList stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
if (!isFrontStack(stack)) {
continue;
}
ActivityRecord hr = stack.topRunningActivityLocked(null);
if (hr != null) {
if (hr.app == null && app.uid == hr.info.applicationInfo.uid
&& processName.equals(hr.processName)) {
try {
if (realStartActivityLocked(hr, app, true, true)) {
didSomething = true;
}
} catch (RemoteException e) {
Slog.w(TAG, "Exception in new application when starting activity "
+ hr.intent.getComponent().flattenToShortString(), e);
throw e;
}
}
}
}
}
if (!didSomething) {
ensureActivitiesVisibleLocked(null, 0);
}
return didSomething;
}
可以发现其内部调用了realStartActivityLocked方法,通过名字可以知道这个方法应该就是用来启动Activity的,看一下这个方法的实现逻辑:
final boolean realStartActivityLocked(ActivityRecord r,
ProcessRecord app, boolean andResume, boolean checkConfig)
throws RemoteException {
...
app.forceProcessStateUpTo(mService.mTopProcessState);
app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
new Configuration(stack.mOverrideConfig), r.compat, r.launchedFromPackage,
task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results,
newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo);
...
return true;
}
可以发现与第三步执行栈顶Activity onPause时类似,这里也是通过调用IApplicationThread的方法实现的,这里调用的是scheduleLauncherActivity方法,所以真正执行的是ActivityThread中的scheduleLauncherActivity,所以我们看一下ActivityThread中的scheduleLauncherActivity的实现:
@Override
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
int procState, Bundle state, PersistableBundle persistentState,
List pendingResults, List pendingNewIntents,
boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
updateProcessState(procState, false)
ActivityClientRecord r = new ActivityClientRecord()
r.token = token
r.ident = ident
r.intent = intent
r.referrer = referrer
r.voiceInteractor = voiceInteractor
r.activityInfo = info
r.compatInfo = compatInfo
r.state = state
r.persistentState = persistentState
r.pendingResults = pendingResults
r.pendingIntents = pendingNewIntents
r.startsNotResumed = notResumed
r.isForward = isForward
r.profilerInfo = profilerInfo
r.overrideConfig = overrideConfig
updatePendingConfiguration(curConfig)
sendMessage(H.LAUNCH_ACTIVITY, r)
}
好吧,还是那套逻辑,ActivityThread接收到SystemServer进程的消息之后会通过其内部的Handler对象分发消息,经过一系列的分发之后调用了ActivityThread的handleLaunchActivity方法:
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
Bundle oldState = r.state;
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed);
}
...
}
可以发现这里调用了performLauncherActivity,看名字应该就是执行Activity的启动操作了。。。
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
...
activity.mCalled = false;
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
...
if (!r.activity.mFinished) {
activity.performStart();
r.stopped = false;
}
...
return activity;
}
可以发现这里我们需要的Activity对象终于是创建出来了,而且他是以反射的机制创建的,现在还不太清楚为啥google要以反射的方式创建Activity,先不看这些,然后在代码中其调用Instrumentation的callActivityOnCreate方法。
public void callActivityOnCreate(Activity activity, Bundle icicle,
PersistableBundle persistentState) {
prePerformCreate(activity);
activity.performCreate(icicle);
postPerformCreate(activity);
}
然后执行activity的performCreate方法。。。。好吧,都转晕了。。。
final void performCreate(Bundle icicle) {
onCreate(icicle);
mActivityTransitionState.readState(icicle);
performCreateCommon();
}
O(∩_∩)O哈哈~,第二个生命周期方法出来了,onCreate方法。。。。
在回到我们的performLaunchActivity方法,其在调用了mInstrumentation.callActivityOnCreate方法之后又调用了activity.performStart();方法,好吧,看一下他的实现方式:
final void performStart() {
mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
mFragments.noteStateNotSaved();
mCalled = false;
mFragments.execPendingActions();
mInstrumentation.callActivityOnStart(this);
if (!mCalled) {
throw new SuperNotCalledException(
"Activity " + mComponent.toShortString() +
" did not call through to super.onStart()");
}
mFragments.dispatchStart();
mFragments.reportLoaderStart();
mActivityTransitionState.enterReady(this);
}
好吧,还是通过Instrumentation调用callActivityOnStart方法:
public void callActivityOnStart(Activity activity) {
activity.onStart();
}
然后是直接调用activity的onStart方法,第三个生命周期方法出现了,O(∩_∩)O哈哈~
还是回到我们刚刚的handleLaunchActivity方法,在调用完performLaunchActivity方法之后,其有吊用了handleResumeActivity方法,好吧,看名字应该是回调Activity的onResume方法的。
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume) {
unscheduleGcIdler();
mSomeActivitiesChanged = true;
ActivityClientRecord r = performResumeActivity(token, clearHide);
if (r != null) {
final Activity a = r.activity;
if (localLOGV) Slog.v(
TAG, "Resume " + r + " started activity: " +
a.mStartedActivity + ", hideForNow: " + r.hideForNow
+ ", finished: " + a.mFinished);
final int forwardBit = isForward ?
WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;
boolean willBeVisible = !a.mStartedActivity;
if (!willBeVisible) {
try {
willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
a.getActivityToken());
} catch (RemoteException e) {
}
}
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
} else if (!willBeVisible) {
if (localLOGV) Slog.v(
TAG, "Launch " + r + " mStartedActivity set");
r.hideForNow = true;
}
cleanUpPendingRemoveWindows(r);
if (!r.activity.mFinished && willBeVisible
&& r.activity.mDecor != null && !r.hideForNow) {
if (r.newConfig != null) {
r.tmpConfig.setTo(r.newConfig);
if (r.overrideConfig != null) {
r.tmpConfig.updateFrom(r.overrideConfig);
}
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Resuming activity "
+ r.activityInfo.name + " with newConfig " + r.tmpConfig);
performConfigurationChanged(r.activity, r.tmpConfig);
freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(r.tmpConfig));
r.newConfig = null;
}
if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward="
+ isForward);
WindowManager.LayoutParams l = r.window.getAttributes();
if ((l.softInputMode
& WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)
!= forwardBit) {
l.softInputMode = (l.softInputMode
& (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))
| forwardBit;
if (r.activity.mVisibleFromClient) {
ViewManager wm = a.getWindowManager();
View decor = r.window.getDecorView();
wm.updateViewLayout(decor, l);
}
}
r.activity.mVisibleFromServer = true;
mNumVisibleActivities++;
if (r.activity.mVisibleFromClient) {
r.activity.makeVisible();
}
}
if (!r.onlyLocalRequest) {
r.nextIdle = mNewActivities;
mNewActivities = r;
if (localLOGV) Slog.v(
TAG, "Scheduling idle handler for " + r);
Looper.myQueue().addIdleHandler(new Idler());
}
r.onlyLocalRequest = false;
if (reallyResume) {
try {
ActivityManagerNative.getDefault().activityResumed(token);
} catch (RemoteException ex) {
}
}
} else {
try {
ActivityManagerNative.getDefault()
.finishActivity(token, Activity.RESULT_CANCELED, null, false);
} catch (RemoteException ex) {
}
}
}
可以发现其resumeActivity的逻辑调用到了performResumeActivity方法,我们来看一下performResumeActivity是如何实现的。
public final ActivityClientRecord performResumeActivity(IBinder token,
boolean clearHide) {
ActivityClientRecord r = mActivities.get(token)
if (localLOGV) Slog.v(TAG, "Performing resume of " + r
+ " finished=" + r.activity.mFinished)
if (r != null && !r.activity.mFinished) {
if (clearHide) {
r.hideForNow = false
r.activity.mStartedActivity = false
}
try {
r.activity.onStateNotSaved()
r.activity.mFragments.noteStateNotSaved()
if (r.pendingIntents != null) {
deliverNewIntents(r, r.pendingIntents)
r.pendingIntents = null
}
if (r.pendingResults != null) {
deliverResults(r, r.pendingResults)
r.pendingResults = null
}
r.activity.performResume()
EventLog.writeEvent(LOG_AM_ON_RESUME_CALLED,
UserHandle.myUserId(), r.activity.getComponentName().getClassName())
r.paused = false
r.stopped = false
r.state = null
r.persistentState = null
} catch (Exception e) {
if (!mInstrumentation.onException(r.activity, e)) {
throw new RuntimeException(
"Unable to resume activity "
+ r.intent.getComponent().toShortString()
+ ": " + e.toString(), e)
}
}
}
return r
}
在方法体中,最终调用了r.activity.performResume();方法,好吧,这个方法是Activity中定义的方法,我们需要在Activity中查看这个方法的具体实现:
final void performResume() {
...
mInstrumentation.callActivityOnResume(this);
...
}
好吧,又是熟悉的味道,通过Instrumentation来调用了callActivityOnResume方法。。。
public void callActivityOnResume(Activity activity) {
activity.mResumed = true;
activity.onResume();
if (mActivityMonitors != null) {
synchronized (mSync) {
final int N = mActivityMonitors.size();
for (int i=0; i
O(∩_∩)O哈哈~,第四个生命周期方法出现了,onResume方法。。。
终于回调onResume方法了,这时候我们的界面应该已经展示出来了,照理来说我们的Activity应该已经启动完成了,但是还没有,哈哈,别着急。
有一个问题,Activity a 启动 Activity b 会触发那些生命周期方法?
你可能会回答?b的onCreate onStart方法,onResume方法 a的onPause方法和onStop方法,咦?对了onStop方法还没回调呢,O(∩_∩)O哈哈~,对了缺少的就是对onStop方法的回调啊。
好吧,具体的逻辑我们下一步再说
六:栈顶Activity执行onStop方法
Looper.myQueue().addIdleHandler(new Idler())
Idler.queueIdle()
ActivityManagerNative.getDefault().activityIdle()
ActivityManagerService.activityIdle()
ActivityStackSupervisor.activityIdleInternalLocked()
ActivityStack.stopActivityLocked()
IApplicationThread.scheduleStopActivity()
ActivityThread.scheduleStopActivity()
ActivityThread.sendMessage()
ActivityThread.H.sendMessage()
ActivityThread.H.handleMessage()
ActivityThread.handleStopActivity()
ActivityThread.performStopActivityInner()
ActivityThread.callCallActivityOnSaveInstanceState()
Instrumentation.callActivityOnSaveInstanceState()
Activity.performSaveInstanceState()
Activity.onSaveInstanceState()
Activity.performStop()
Instrumentation.callActivityOnStop()
Activity.onStop()
回到我们的handleResumeActivity方法,在方法体最后有这样的一代码:
Looper.myQueue().addIdleHandler(new Idler())
这段代码是异步消息机制相关的代码,我们可以看一下Idler对象的具体实现:
private class Idler implements MessageQueue.IdleHandler {
@Override
public final boolean queueIdle() {
ActivityClientRecord a = mNewActivities;
boolean stopProfiling = false;
if (mBoundApplication != null && mProfiler.profileFd != null
&& mProfiler.autoStopProfiler) {
stopProfiling = true;
}
if (a != null) {
mNewActivities = null;
IActivityManager am = ActivityManagerNative.getDefault();
ActivityClientRecord prev;
do {
if (localLOGV) Slog.v(
TAG, "Reporting idle of " + a +
" finished=" +
(a.activity != null && a.activity.mFinished));
if (a.activity != null && !a.activity.mFinished) {
try {
am.activityIdle(a.token, a.createdConfig, stopProfiling);
a.createdConfig = null;
} catch (RemoteException ex) {
}
}
prev = a;
a = a.nextIdle;
prev.nextIdle = null;
} while (a != null);
}
if (stopProfiling) {
mProfiler.stopProfiling();
}
ensureJitEnabled();
return false;
}
}
这样当Messagequeue执行add方法之后就会回调其queueIdle()方法,我们可以看到在方法体中其调用了ActivityManagerNative.getDefault().activityIdle(),好吧,熟悉了Binder机制以后我们知道这段代码会执行到ActivityManagerService的activityIdle方法:
@Override
public final void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
final long origId = Binder.clearCallingIdentity();
synchronized (this) {
ActivityStack stack = ActivityRecord.getStackLocked(token);
if (stack != null) {
ActivityRecord r =
mStackSupervisor.activityIdleInternalLocked(token, false, config);
if (stopProfiling) {
if ((mProfileProc == r.app) && (mProfileFd != null)) {
try {
mProfileFd.close();
} catch (IOException e) {
}
clearProfilerLocked();
}
}
}
}
Binder.restoreCallingIdentity(origId);
}
然后在activityIdle方法中又调用了ActivityStackSupervisor.activityIdleInternalLocked方法:
final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
Configuration config) {
...
// Stop any activities that are scheduled to do so but have been
// waiting for the next one to start.
for (int i = 0; i < NS; i++) {
r = stops.get(i);
final ActivityStack stack = r.task.stack;
if (stack != null) {
if (r.finishing) {
stack.finishCurrentActivityLocked(r, ActivityStack.FINISH_IMMEDIATELY, false);
} else {
stack.stopActivityLocked(r);
}
}
}
...
return r;
}
可以发现在其中又调用了ActivityStack.stopActivityLocked方法:
final void stopActivityLocked(ActivityRecord r) {
if (DEBUG_SWITCH) Slog.d(TAG_SWITCH, "Stopping: " + r);
if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
|| (r.info.flags&ActivityInfo.FLAG_NO_HISTORY) != 0) {
...
r.app.thread.scheduleStopActivity(r.appToken, r.visible, r.configChangeFlags);
...
}
}
好吧,又是相同的逻辑通过IApplicationThread.scheduleStopActivity,最终调用了ActivityThread.scheduleStopActivity()方法。。。。
public final void scheduleStopActivity(IBinder token, boolean showWindow,
int configChanges) {
sendMessage(
showWindow ? H.STOP_ACTIVITY_SHOW : H.STOP_ACTIVITY_HIDE,
token, 0, configChanges);
}
然后执行sendMessage方法,最终执行H(Handler)的sendMessage方法,并被H的handleMessge方法接收执行handleStopActivity方法。。。
private void handleStopActivity(IBinder token, boolean show, int configChanges) {
...
performStopActivityInner(r, info, show, true);
...
}
然后我们看一下performStopActivityInner的实现逻辑:
private void performStopActivityInner(ActivityClientRecord r,
StopInfo info, boolean keepShown, boolean saveState) {
...
if (!r.activity.mFinished && saveState) {
if (r.state == null) {
callCallActivityOnSaveInstanceState(r);
}
}
if (!keepShown) {
try {
r.activity.performStop();
} catch (Exception e) {
if (!mInstrumentation.onException(r.activity, e)) {
throw new RuntimeException(
"Unable to stop activity "
+ r.intent.getComponent().toShortString()
+ ": " + e.toString(), e);
}
}
r.stopped = true;
}
}
}
好吧,看样子在这个方法中执行了两个逻辑,一个是执行Activity的onSaveInstance方法一个是执行Activity的onStop方法,我们先看一下callCallActivityOnSaveInstanceState的执行逻辑:
private void callCallActivityOnSaveInstanceState(ActivityClientRecord r) {
r.state = new Bundle()
r.state.setAllowFds(false)
if (r.isPersistable()) {
r.persistentState = new PersistableBundle()
mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state,
r.persistentState)
} else {
mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state)
}
}
好吧,又是通过Instrumentation来执行。。。
public void callActivityOnSaveInstanceState(Activity activity, Bundle outState,
PersistableBundle outPersistentState) {
activity.performSaveInstanceState(outState, outPersistentState);
}
又间接调用了Activity的performSaveInstanceState方法:
final void performSaveInstanceState(Bundle outState) {
onSaveInstanceState(outState);
saveManagedDialogs(outState);
mActivityTransitionState.saveState(outState);
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState);
}
呵呵,这里调用到了,我们以前经常会重写的onSaveInstanceState方法。
然后我们看一下performStopActivityInner中调用到的Activity方法的performStop方法:
final void performStop() {
mDoReportFullyDrawn = false;
mFragments.doLoaderStop(mChangingConfigurations );
if (!mStopped) {
if (mWindow != null) {
mWindow.closeAllPanels();
}
if (mToken != null && mParent == null) {
WindowManagerGlobal.getInstance().setStoppedState(mToken, true);
}
mFragments.dispatchStop();
mCalled = false;
mInstrumentation.callActivityOnStop(this);
if (!mCalled) {
throw new SuperNotCalledException(
"Activity " + mComponent.toShortString() +
" did not call through to super.onStop()");
}
synchronized (mManagedCursors) {
final int N = mManagedCursors.size();
for (int i=0; i
还是通过Instrumentation来实现的,调用了它的callActivityOnStop方法。。
public void callActivityOnStop(Activity activity) {
activity.onStop();
}
O(∩_∩)O哈哈~,最后一个生命周期方法终于出来了,onStop()…..
总结:
Activity的启动流程一般是通过调用startActivity或者是startActivityForResult来开始的
startActivity内部也是通过调用startActivityForResult来启动Activity,只不过传递的requestCode小于0
Activity的启动流程涉及到多个进程之间的通讯这里主要是ActivityThread与ActivityManagerService之间的通讯
ActivityThread向ActivityManagerService传递进程间消息通过ActivityManagerNative,ActivityManagerService向ActivityThread进程间传递消息通过IApplicationThread。
ActivityManagerService接收到应用进程创建Activity的请求之后会执行初始化操作,解析启动模式,保存请求信息等一系列操作。
ActivityManagerService保存完请求信息之后会将当前系统栈顶的Activity执行onPause操作,并且IApplication进程间通讯告诉应用程序继承执行当前栈顶的Activity的onPause方法;
ActivityThread接收到SystemServer的消息之后会统一交个自身定义的Handler对象处理分发;
ActivityThread执行完栈顶的Activity的onPause方法之后会通过ActivityManagerNative执行进程间通讯告诉ActivityManagerService,栈顶Actiity已经执行完成onPause方法,继续执行后续操作;
ActivityManagerService会继续执行启动Activity的逻辑,这时候会判断需要启动的Activity所属的应用进程是否已经启动,若没有启动则首先会启动这个Activity的应用程序进程;
ActivityManagerService会通过socket与Zygote继承通讯,并告知Zygote进程fork出一个新的应用程序进程,然后执行ActivityThread的mani方法;
在ActivityThead.main方法中执行初始化操作,初始化主线程异步消息,然后通知ActivityManagerService执行进程初始化操作;
ActivityManagerService会在执行初始化操作的同时检测当前进程是否有需要创建的Activity对象,若有的话,则执行创建操作;
ActivityManagerService将执行创建Activity的通知告知ActivityThread,然后通过反射机制创建出Activity对象,并执行Activity的onCreate方法,onStart方法,onResume方法;
ActivityThread执行完成onResume方法之后告知ActivityManagerService onResume执行完成,开始执行栈顶Activity的onStop方法;
ActivityManagerService开始执行栈顶的onStop方法并告知ActivityThread;
ActivityThread执行真正的onStop方法;
另外对android源码解析方法感兴趣的可参考我的:
android源码解析之(一)–>android项目构建过程
android源码解析之(二)–>异步消息机制
android源码解析之(三)–>异步任务AsyncTask
android源码解析之(四)–>HandlerThread
android源码解析之(五)–>IntentService
android源码解析之(六)–>Log
android源码解析之(七)–>LruCache
android源码解析之(八)–>Zygote进程启动流程
android源码解析之(九)–>SystemServer进程启动流程
android源码解析之(十)–>Launcher启动流程
android源码解析之(十一)–>应用进程启动流程
android源码解析之(十二)–>系统启动并解析Manifest的流程
android源码解析之(十三)–>apk安装流程
本文以同步至github中:github.com/yipianfengy…,欢迎star和follow
转载请标明出处:一片枫叶的专栏
继续我们的源码解析,上一篇文章我们介绍了Activity的启动流程,一个典型的场景就是Activity a 启动了一个Activity b,他们的生命周期回调方法是:
onPause(a) –> onCreate(b) –> onStart(b) –> onResume(b) –> onStop(a)
而我们根据源码也验证了这样的生命周期调用序列,那么Activity的销毁流程呢?它的生命周期的调用顺序又是这样的呢?
这里我们我做一个简单的demo,让一个Activity a启动Activity b,然后在b中调用finish()方法,它们的生命周期执行顺序是:
onPause(b)
onRestart(a)
onStart(a)
onResume(a)
onStop(b)
onDestory(b)
好吧,根据我们测试的生命周期方法的回调过程开始对Activity销毁流程的分析,一般而言当我们需要销毁Activity的时候都会调用其自身的finish方法,所以我们的流程开始是以finish方法开始的。
一:请求销毁当前Activity
MyActivity.finish()
Activity.finish()
ActivityManagerNative.getDefault().finishActivity()
ActivityManagerService.finishActivity()
ActivityStack.requestFinishActivityLocked()
ActivityStack.finishActivityLocked()
ActivityStack.startPausingLocked()
首先我们在自己的Activity调用了finish方法,它实际上调用的是Activity的finish方法:
public void finish() {
finish(false);
}
然后我们可以发现其调用了finish方法的重载方法,并且传递了一个参数值:
private void finish(boolean finishTask) {
if (mParent == null) {
int resultCode;
Intent resultData;
synchronized (this) {
resultCode = mResultCode;
resultData = mResultData;
}
if (false) Log.v(TAG, "Finishing self: token=" + mToken);
try {
if (resultData != null) {
resultData.prepareToLeaveProcess();
}
if (ActivityManagerNative.getDefault()
.finishActivity(mToken, resultCode, resultData, finishTask)) {
mFinished = true;
}
} catch (RemoteException e) {
}
} else {
mParent.finishFromChild(this);
}
}
好吧,这个参数值似乎并没什么用。。。这里就不在讨论了,然后调用了ActivityManagerNative.getDefault().finishActivity方法,好吧,根据上一篇文章的介绍,我们知道了ActivityManagerNative是一个Binder对象,这里调用的方法最终会被ActivityManagerService执行,所以这了的finishActivity最终被执行的是ActivityManagerService.finishActivity方法,好吧,我们来看一下ActivityManagerService的finishActivity方法的执行逻辑。。。
@Override
public final boolean finishActivity(IBinder token, int resultCode, Intent resultData, boolean finishTask) {
...
res = tr.stack.requestFinishActivityLocked(token, resultCode,resultData, "app-request", true);
...
}
这里我们可以发现,经过一系列逻辑判断之后,最终调用了ActivityStack的requestFinishActivityLocked方法,这里应该就是执行finish Activity的逻辑了。
final boolean requestFinishActivityLocked(IBinder token, int resultCode,
Intent resultData, String reason, boolean oomAdj) {
ActivityRecord r = isInStackLocked(token);
if (DEBUG_RESULTS || DEBUG_STATES) Slog.v(TAG_STATES,
"Finishing activity token=" + token + " r="
+ ", result=" + resultCode + ", data=" + resultData
+ ", reason=" + reason);
if (r == null) {
return false;
}
finishActivityLocked(r, resultCode, resultData, reason, oomAdj);
return true;
}
这个方法体里面又调用了finishActivityLocked方法,那我们继续看一下finishActivityLocked方法的实现:
final boolean finishActivityLocked(ActivityRecord r, int resultCode, Intent resultData,
String reason, boolean oomAdj) {
...
startPausingLocked(false, false, false, false);
...
return false;
}
好吧,在这里调用了startPausingLocked方法,看名字应该是开始要执行Activity的onPause方法请求了,然后我们看一下startPausingLocked方法的实现:
final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping, boolean resuming, boolean dontWait) {
...
try {
EventLog.writeEvent(EventLogTags.AM_PAUSE_ACTIVITY,
prev.userId, System.identityHashCode(prev),
prev.shortComponentName);
mService.updateUsageStats(prev, false);
prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
userLeaving, prev.configChangeFlags, dontWait);
} catch (Exception e) {
// Ignore exception, if process died other code will cleanup.
Slog.w(TAG, "Exception thrown during pause", e);
mPausingActivity = null;
mLastPausedActivity = null;
mLastNoHistoryActivity = null;
}
...
}
这样从应用程序调用finish方法,ActivityManagerService接收请求并执行startPausingLocked方法。
二:执行当前Activity的onPause方法
IApplicationThread.schedulePauseActivity()
ActivityThread.schedulePauseActivity()
ActivityThread.sendMessage()
ActivityThread.H.sendMessage()
ActivityThread.H.handleMessage()
ActivityThread.handlePauseActivity()
ActivityThread.performPauseActivity()
Instrumentation.callActivityOnPause()
Activity.performPause()
Activity.onPause()
ActivityManagerNative.getDefault().activityPaused()
ActivityManagerService.activityPaused()
ActivityStack.activityPausedLocked()
ActivityStack.completePauseLocked()
在方法startPausingLocked中我们调用了:prev.app.thread.schedulePauseActivity这里实际上调用的是IApplicationThread的schedulePauseActivity方法,IApplicationThread也是一个Binder对象,它是ActivityThread中ApplicationThread的Binder client端,所以最终会调用的是ApplicationThread的schedulePauseActivity方法,好吧我们看一下ActivityThread的schedulePauseActivity方法的具体实现:
public final void schedulePauseActivity(IBinder token, boolean finished, boolean userLeaving, int configChanges, boolean dontReport) {
sendMessage(
finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY,
token, (userLeaving ? 1 : 0) | (dontReport ? 2 : 0),
configChanges);
}
然后调用了ActivityThread的sendMessage方法:
private void sendMessage(int what, Object obj, int arg1, int arg2) {
sendMessage(what, obj, arg1, arg2, false);
}
然后又回调了sendMessage的重载方法。。
private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
if (DEBUG_MESSAGES) Slog.v(
TAG, "SCHEDULE " + what + " " + mH.codeToString(what)
+ ": " + arg1 + " / " + obj);
Message msg = Message.obtain();
msg.what = what;
msg.obj = obj;
msg.arg1 = arg1;
msg.arg2 = arg2;
if (async) {
msg.setAsynchronous(true);
}
mH.sendMessage(msg);
}
最终调用mH发送异步消息,然后在mH的handleMessge方法中处理异步消息并调用handlePauseActivity方法:
private void handlePauseActivity(IBinder token, boolean finished,
boolean userLeaving, int configChanges, boolean dontReport) {
ActivityClientRecord r = mActivities.get(token);
if (r != null) {
if (userLeaving) {
performUserLeavingActivity(r);
}
r.activity.mConfigChangeFlags |= configChanges;
performPauseActivity(token, finished, r.isPreHoneycomb());
if (r.isPreHoneycomb()) {
QueuedWork.waitToFinish();
}
if (!dontReport) {
try {
ActivityManagerNative.getDefault().activityPaused(token);
} catch (RemoteException ex) {
}
}
mSomeActivitiesChanged = true;
}
}
好吧,这里回调了performPauseActivity方法,上篇文章中我们已经分析过了这段代码:
performPauseActivity()
Instrumentation.callActivityOnPause()
Activity.performPause()
Activity.onPause()
这样我们就回调了第一个生命周期方法:onPause。。。
在handlePauseActivity方法中我们调用了ActivityManagerNative.getDefault().activityPaused(token)方法,好吧又是回调ActivityManagerService的方法,这样最终会调用ActivityManagerService的activityPaused方法:
@Override
public final void activityPaused(IBinder token) {
final long origId = Binder.clearCallingIdentity();
synchronized(this) {
ActivityStack stack = ActivityRecord.getStackLocked(token);
if (stack != null) {
stack.activityPausedLocked(token, false);
}
}
Binder.restoreCallingIdentity(origId);
}
这样,我们继续看一下activityPausedLocked方法的实现:
final void activityPausedLocked(IBinder token, boolean timeout) {
...
completePauseLocked(true);
...
}
里面又经过一系列的逻辑判断之后,开始执行completePauseLocked方法:
private void completePauseLocked(boolean resumeNext) {
... mStackSupervisor.resumeTopActivitiesLocked(topStack, null, null);
...
}
这样栈顶Activity的onPause操作就执行完成了,接下来就就是开始执行上一个Activity的onResume操作了。。。
三:执行上一个Activity的onResume操作
这样调用了ActivityStackSupervisor.resumeTopActivitiesLocked方法。。,又开始调用这个方法,通过上一篇文章的介绍,我们知道这个方法实际上是执行Activity的初始化,我们看一下其具体的调用过程:
ActivityStack.resumeTopActivityLocked()
ActivityStack.resumeTopInnerLocked()
IApplicationThread.scheduleResumeActivity()
ActivityThread.scheduleResumeActivity()
ActivityThread.sendMessage()
ActivityTherad.H.sendMessage()
ActivityThread.H.handleMessage()
ActivityThread.H.handleResumeMessage()
Activity.performResume()
Activity.performRestart()
Instrumentation.callActivityOnRestart()
Activity.onRestart()
Activity.performStart()
Instrumentation.callActivityOnStart()
Activity.onStart()
Instrumentation.callActivityOnResume()
Activity.onResume()
好吧,这个过程其实上一篇文章中已经做了介绍,这里不做过多的分析了,通过这样调用过程我们最终执行了当前栈顶Activity上一个Activity的onRestart方法,onStart方法,onResume方法等,下面我们将调用栈顶Activity的onStop方法,onDestory方法。
四:执行栈顶Activity的销毁操作
Looper.myQueue().addIdleHandler(new Idler())
ActivityManagerNative.getDefault().activityIdle()
ActivityManagerService.activityIdle()
ActivityStackSupervisor.activityIdleInternalLocked()
ActivityStack.destroyActivityLocked()
IApplicationThread.scheduleDestoryActivity()
ActivityThread.scheduleDestoryActivity()
ActivityThread.sendMessage()
ActivityThread.H.sendMessage()
ActivityThread.H.handleMessage()
ActivityThread.handleDestoryActivity()
ActivityThread.performDestoryActivity()
Activity.performStop()
Instrumentation.callActivityOnStop()
Activity.onStop()
Instrumentation.callActivityOnDestory()
Activity.performDestory()
Acitivity.onDestory()
ActivityManagerNative.getDefault().activityDestoryed()
ActivityManagerService.activityDestoryed()
ActivityStack.activityDestoryedLocked()
我们在ActivityThread.handleResumeActivity方法中调用了Looper.myQueue().addIdleHandler(new Idler()),下面看一下这个方法的实现:
private class Idler implements MessageQueue.IdleHandler {
@Override
public final boolean queueIdle() {
ActivityClientRecord a = mNewActivities;
boolean stopProfiling = false;
if (mBoundApplication != null && mProfiler.profileFd != null
&& mProfiler.autoStopProfiler) {
stopProfiling = true;
}
if (a != null) {
mNewActivities = null;
IActivityManager am = ActivityManagerNative.getDefault();
ActivityClientRecord prev;
do {
if (localLOGV) Slog.v(
TAG, "Reporting idle of " + a +
" finished=" +
(a.activity != null && a.activity.mFinished));
if (a.activity != null && !a.activity.mFinished) {
try {
am.activityIdle(a.token, a.createdConfig, stopProfiling);
a.createdConfig = null;
} catch (RemoteException ex) {
}
}
prev = a;
a = a.nextIdle;
prev.nextIdle = null;
} while (a != null);
}
if (stopProfiling) {
mProfiler.stopProfiling();
}
ensureJitEnabled();
return false;
}
}
内部有一个queueIdle的回调方法,当它被添加到MessageQueue之后就会回调该方法,我们可以发现在这个方法体中调用了ActivityManagerNative.getDefault.activityIdle方法,通过上一篇文章以及上面的讲解,我们应该知道这了最终调用的是ActivityManagerService.activityIdle方法,好吧,这里看一下activityIdle方法的具体实现:
@Override
public final void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
final long origId = Binder.clearCallingIdentity();
synchronized (this) {
ActivityStack stack = ActivityRecord.getStackLocked(token);
if (stack != null) {
ActivityRecord r =
mStackSupervisor.activityIdleInternalLocked(token, false, config);
if (stopProfiling) {
if ((mProfileProc == r.app) && (mProfileFd != null)) {
try {
mProfileFd.close();
} catch (IOException e) {
}
clearProfilerLocked();
}
}
}
}
Binder.restoreCallingIdentity(origId);
}
可以发现这里又调用了ActivityStackSupervisor.activityIdleInternalLocked方法,然后我们看一下activityIdleInternalLocked方法的具体实现:
final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout, Configuration config) {
....
stack.destroyActivityLocked(r, true, "finish-idle");
....
}
可以看到这里调用ActivityStack.destroyActivityLocked方法,可以看一下其具体实现:
final boolean destroyActivityLocked(ActivityRecord r, boolean removeFromApp, String reason) {
...
r.app.thread.scheduleDestroyActivity(r.appToken, r.finishing, r.configChangeFlags);
...
}
好吧,这里又开始执行IApplicationThread.scheduleDestoryActivity方法,上文已经做了说明这里最终调用的是ActivityThread.scheduleDestroyActivity方法,好吧,看一下ActivityThread.scheduleDestryActivity方法的实现:
public final void scheduleDestroyActivity(IBinder token, boolean finishing, int configChanges) {
sendMessage(H.DESTROY_ACTIVITY, token, finishing ? 1 : 0,
configChanges);
}
这里有开始执行sendMessage方法,通过一系列的调用sendMessage方法最终调用了handleDestroyActivity方法:
private void handleDestroyActivity(IBinder token, boolean finishing,
int configChanges, boolean getNonConfigInstance) {
ActivityClientRecord r = performDestroyActivity(token, finishing,
configChanges, getNonConfigInstance);
if (r != null) {
cleanUpPendingRemoveWindows(r);
WindowManager wm = r.activity.getWindowManager();
View v = r.activity.mDecor;
if (v != null) {
if (r.activity.mVisibleFromServer) {
mNumVisibleActivities--;
}
IBinder wtoken = v.getWindowToken();
if (r.activity.mWindowAdded) {
if (r.onlyLocalRequest) {
r.mPendingRemoveWindow = v;
r.mPendingRemoveWindowManager = wm;
} else {
wm.removeViewImmediate(v);
}
}
if (wtoken != null && r.mPendingRemoveWindow == null) {
WindowManagerGlobal.getInstance().closeAll(wtoken,
r.activity.getClass().getName(), "Activity");
}
r.activity.mDecor = null;
}
if (r.mPendingRemoveWindow == null) {
WindowManagerGlobal.getInstance().closeAll(token,
r.activity.getClass().getName(), "Activity");
}
Context c = r.activity.getBaseContext();
if (c instanceof ContextImpl) {
((ContextImpl) c).scheduleFinalCleanup(
r.activity.getClass().getName(), "Activity");
}
}
if (finishing) {
try {
ActivityManagerNative.getDefault().activityDestroyed(token);
} catch (RemoteException ex) {
}
}
mSomeActivitiesChanged = true;
}
可以看到这里调用了performDestroyActivity方法,用来执行Avtivity的onDestroy方法:
private ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
int configChanges, boolean getNonConfigInstance) {
...
r.activity.performStop();
...
mInstrumentation.callActivityOnDestroy(r.activity);
...
}
然后调用了Activity.performStop()方法,查看performStop方法:
final void performStop() {
...
mInstrumentation.callActivityOnStop(this);
...
}
然后调用了Instrumentation.callActivityOnStop()方法:
public void callActivityOnStop(Activity activity) {
activity.onStop();
}
好吧,终于调用了Activity的onStop方法。。。
我们继续看一下Instrumentation.callActivityOnDestroy()。。。。又是通过Instrumentation来调用Activity的onDestroy方法:
public void callActivityOnDestroy(Activity activity) {
...
activity.performDestroy();
...
}
然后看一下Activity的performDestroy()方法的实现:
final void performDestroy() {
mDestroyed = true;
mWindow.destroy();
mFragments.dispatchDestroy();
onDestroy();
mFragments.doLoaderDestroy();
if (mVoiceInteractor != null) {
mVoiceInteractor.detachActivity();
}
}
O(∩_∩)O哈哈~,终于回调了Activity的onDestroy方法。。。。
总结:
Activity的销毁流程是从finish方法开始的
Activity销毁过程是:onPause –> onRestart –> onStart –> onResume –> onStop –> onDestroy
Activity的销毁流程是ActivityThread与ActivityManagerService相互配合销毁的
另外对android源码解析方法感兴趣的可参考我的:
android源码解析之(一)–>android项目构建过程
android源码解析之(二)–>异步消息机制
android源码解析之(三)–>异步任务AsyncTask
android源码解析之(四)–>HandlerThread
android源码解析之(五)–>IntentService
android源码解析之(六)–>Log
android源码解析之(七)–>LruCache
android源码解析之(八)–>Zygote进程启动流程
android源码解析之(九)–>SystemServer进程启动流程
android源码解析之(十)–>Launcher启动流程
android源码解析之(十一)–>应用进程启动流程
android源码解析之(十二)–>系统启动并解析Manifest的流程
android源码解析之(十三)–>apk安装流程
android源码解析之(十四)–>Activity启动流程
本文以同步至github中:github.com/yipianfengy…,欢迎star和follow
转载请标明出处:一片枫叶的专栏
今天讲讲应用进程Context的创建流程,相信大家平时在开发过程中经常会遇到对Context对象的使用,Application是Context,Activity是Context,Service也是Context,所以有一个经典的问题是一个App中一共有多少个Context?
这个问题的答案是Application + N个Activity + N个Service。
还有就是我们平时在使用Context过程中许多时候由于使用不当,可能会造成内存泄露的情况等等,这个也是需要我们注意的。这里有篇不错的文章:
Android Context 是什么?
好吧,什么叫应用进程Context呢?这是指的是Application所代表的Context的创建流程,还记得我们前几篇写的应用进程创建流程么?
android源码解析之(十一)–>应用进程启动流程
最后我们得出结论,应用进程的起始方法是ActivityThread.main方法,好吧,
由于还未讲解Service相关知识,这里暂时讲解一下Activity与Application中Context对象的创建过程。
首先我们就从ActivityThread.main方法开始看一下Application的创建流程。。。
public static void main(String[] args) {
...
ActivityThread thread = new ActivityThread();
thread.attach(false);
...
}
这里我们发现在方法体中我们创建了一个ActivityThread对象并执行了attach方法:
private void attach(boolean system) {
sCurrentActivityThread = this;
mSystemThread = system;
if (!system) {
ViewRootImpl.addFirstDrawHandler(new Runnable() {
@Override
public void run() {
ensureJitEnabled();
}
});
android.ddm.DdmHandleAppName.setAppName("",
UserHandle.myUserId());
RuntimeInit.setApplicationObject(mAppThread.asBinder());
final IActivityManager mgr = ActivityManagerNative.getDefault();
try {
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
}
BinderInternal.addGcWatcher(new Runnable() {
@Override public void run() {
if (!mSomeActivitiesChanged) {
return;
}
Runtime runtime = Runtime.getRuntime();
long dalvikMax = runtime.maxMemory();
long dalvikUsed = runtime.totalMemory() - runtime.freeMemory();
if (dalvikUsed > ((3*dalvikMax)/4)) {
if (DEBUG_MEMORY_TRIM) Slog.d(TAG, "Dalvik max=" + (dalvikMax/1024)
+ " total=" + (runtime.totalMemory()/1024)
+ " used=" + (dalvikUsed/1024));
mSomeActivitiesChanged = false;
try {
mgr.releaseSomeActivities(mAppThread);
} catch (RemoteException e) {
}
}
}
});
} else {
...
}
}
这里看一下重点实现,我们可以发现在方法体中调用了ActivityManagerNative.getDefault().attachApplication(mAppThread)
看过我的前几篇文章的童鞋应该知道这里就是一个Binder进程间通讯,其实上执行的是ActivityManagerService.attachApplication方法,具体的可以参考前几篇文章的介绍,好吧,既然这样我们看一下ActivityManagerService.attachApplication方法的具体实现。
@Override
public final void attachApplication(IApplicationThread thread) {
synchronized (this) {
int callingPid = Binder.getCallingPid();
final long origId = Binder.clearCallingIdentity();
attachApplicationLocked(thread, callingPid);
Binder.restoreCallingIdentity(origId);
}
}
然后这里面又调用了attachApplicationLocked方法:
private final boolean attachApplicationLocked(IApplicationThread thread, int pid) {
...
thread.bindApplication(processName, appInfo, providers, app.instrumentationClass, profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,
app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace, isRestrictedBackupMode || !normalMode, app.persistent, new Configuration(mConfiguration), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked());
...
可以看到这里面又调用了IApplication.bindApplication,从方法名称中我们可以看出这里应该是绑定Application的方法,跟上面的ActivityManangerNative类似的,前面几篇文章中我们已经做过介绍,IApplicationThread是ActivityThread中ApplicationThread binder对象的客户端,所以这里最终调用的是ApplicationThread的bindApplication方法,既然这样,我们来看一下ApplicationThread的bindApplication的实现:
public final void bindApplication(String processName, ApplicationInfo appInfo,
List providers, ComponentName instrumentationName,
ProfilerInfo profilerInfo, Bundle instrumentationArgs,
IInstrumentationWatcher instrumentationWatcher,
IUiAutomationConnection instrumentationUiConnection, int debugMode,
boolean enableOpenGlTrace, boolean isRestrictedBackupMode, boolean persistent,
Configuration config, CompatibilityInfo compatInfo, Map services,
Bundle coreSettings) {
if (services != null) {
// Setup the service cache in the ServiceManager
ServiceManager.initServiceCache(services);
}
setCoreSettings(coreSettings);
/*
* Two possible indications that this package could be
* sharing its runtime with other packages:
*
* 1.) the sharedUserId attribute is set in the manifest,
* indicating a request to share a VM with other
* packages with the same sharedUserId.
*
* 2.) the application element of the manifest has an
* attribute specifying a non-default process name,
* indicating the desire to run in another packages VM.
*
* If sharing is enabled we do not have a unique application
* in a process and therefore cannot rely on the package
* name inside the runtime.
*/
IPackageManager pm = getPackageManager();
android.content.pm.PackageInfo pi = null;
try {
pi = pm.getPackageInfo(appInfo.packageName, 0, UserHandle.myUserId());
} catch (RemoteException e) {
}
if (pi != null) {
boolean sharedUserIdSet = (pi.sharedUserId != null);
boolean processNameNotDefault =
(pi.applicationInfo != null &&
!appInfo.packageName.equals(pi.applicationInfo.processName));
boolean sharable = (sharedUserIdSet || processNameNotDefault);
// Tell the VMRuntime about the application, unless it is shared
// inside a process.
if (!sharable) {
VMRuntime.registerAppInfo(appInfo.packageName, appInfo.dataDir,
appInfo.processName);
}
}
AppBindData data = new AppBindData();
data.processName = processName;
data.appInfo = appInfo;
data.providers = providers;
data.instrumentationName = instrumentationName;
data.instrumentationArgs = instrumentationArgs;
data.instrumentationWatcher = instrumentationWatcher;
data.instrumentationUiAutomationConnection = instrumentationUiConnection;
data.debugMode = debugMode;
data.enableOpenGlTrace = enableOpenGlTrace;
data.restrictedBackupMode = isRestrictedBackupMode;
data.persistent = persistent;
data.config = config;
data.compatInfo = compatInfo;
data.initProfilerInfo = profilerInfo;
sendMessage(H.BIND_APPLICATION, data);
}
好吧,最后调用了ActivityThread.sendMessage()…
private void sendMessage(int what, Object obj) {
sendMessage(what, obj, 0, 0, false);
}
然后我们看一下其sendMessage的重载方法:
private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
if (DEBUG_MESSAGES) Slog.v(
TAG, "SCHEDULE " + what + " " + mH.codeToString(what)
+ ": " + arg1 + " / " + obj);
Message msg = Message.obtain();
msg.what = what;
msg.obj = obj;
msg.arg1 = arg1;
msg.arg2 = arg2;
if (async) {
msg.setAsynchronous(true);
}
mH.sendMessage(msg);
}
可以发现这里调用了mH的sendMessage方法,最后通过Handler的异步消息机制被mH的handleMessage方法处理,然后根据Message.what选择处理分支,最终调用了ActivityThread的handleBindApplication方法。
private void handleBindApplication(AppBindData data) {
...
// 创建Instrumentation
if (data.instrumentationName != null) {
InstrumentationInfo ii = null
try {
ii = appContext.getPackageManager().
getInstrumentationInfo(data.instrumentationName, 0)
} catch (PackageManager.NameNotFoundException e) {
}
if (ii == null) {
throw new RuntimeException(
"Unable to find instrumentation info for: "
+ data.instrumentationName)
}
mInstrumentationPackageName = ii.packageName
mInstrumentationAppDir = ii.sourceDir
mInstrumentationSplitAppDirs = ii.splitSourceDirs
mInstrumentationLibDir = ii.nativeLibraryDir
mInstrumentedAppDir = data.info.getAppDir()
mInstrumentedSplitAppDirs = data.info.getSplitAppDirs()
mInstrumentedLibDir = data.info.getLibDir()
ApplicationInfo instrApp = new ApplicationInfo()
instrApp.packageName = ii.packageName
instrApp.sourceDir = ii.sourceDir
instrApp.publicSourceDir = ii.publicSourceDir
instrApp.splitSourceDirs = ii.splitSourceDirs
instrApp.splitPublicSourceDirs = ii.splitPublicSourceDirs
instrApp.dataDir = ii.dataDir
instrApp.nativeLibraryDir = ii.nativeLibraryDir
LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
appContext.getClassLoader(), false, true, false)
ContextImpl instrContext = ContextImpl.createAppContext(this, pi)
try {
java.lang.ClassLoader cl = instrContext.getClassLoader()
mInstrumentation = (Instrumentation)
cl.loadClass(data.instrumentationName.getClassName()).newInstance()
} catch (Exception e) {
throw new RuntimeException(
"Unable to instantiate instrumentation "
+ data.instrumentationName + ": " + e.toString(), e)
}
mInstrumentation.init(this, instrContext, appContext,
new ComponentName(ii.packageName, ii.name), data.instrumentationWatcher,
data.instrumentationUiAutomationConnection)
if (mProfiler.profileFile != null && !ii.handleProfiling
&& mProfiler.profileFd == null) {
mProfiler.handlingProfiling = true
File file = new File(mProfiler.profileFile)
file.getParentFile().mkdirs()
Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024)
}
} else {
mInstrumentation = new Instrumentation()
}
...
/ If the app is being launched for full backup or restore, bring it up in
// a restricted environment with the base application class.
Application app = data.info.makeApplication(data.restrictedBackupMode, null)
mInitialApplication = app
...
try {
mInstrumentation.onCreate(data.instrumentationArgs)
}
catch (Exception e) {
throw new RuntimeException(
"Exception thrown in onCreate() of "
+ data.instrumentationName + ": " + e.toString(), e)
}
try {
mInstrumentation.callApplicationOnCreate(app)
} catch (Exception e) {
if (!mInstrumentation.onException(app, e)) {
throw new RuntimeException(
"Unable to create application " + app.getClass().getName()
+ ": " + e.toString(), e)
}
}
} finally {
StrictMode.setThreadPolicy(savedPolicy)
}
}
这个方法的方法体比较长,我们挑重点的看,可以看到方法体中系统通过反射机制创建了Instrumentation对象,并执行了init方法,执行了Insrtumentation对象的初始化。然后我们调用了LockedApk.makeApplication方法创建了Application对象,我们来看一下其具体的实现逻辑:
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
if (mApplication != null) {
return mApplication;
}
Application app = null;
String appClass = mApplicationInfo.className;
if (forceDefaultAppClass || (appClass == null)) {
appClass = "android.app.Application";
}
try {
java.lang.ClassLoader cl = getClassLoader();
if (!mPackageName.equals("android")) {
initializeJavaContextClassLoader();
}
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app);
} catch (Exception e) {
if (!mActivityThread.mInstrumentation.onException(app, e)) {
throw new RuntimeException(
"Unable to instantiate application " + appClass
+ ": " + e.toString(), e);
}
}
mActivityThread.mAllApplications.add(app);
mApplication = app;
if (instrumentation != null) {
try {
instrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
if (!instrumentation.onException(app, e)) {
throw new RuntimeException(
"Unable to create application " + app.getClass().getName()
+ ": " + e.toString(), e);
}
}
}
SparseArray packageIdentifiers = getAssets(mActivityThread)
.getAssignedPackageIdentifiers();
final int N = packageIdentifiers.size();
for (int i = 0; i < N; i++) {
final int id = packageIdentifiers.keyAt(i);
if (id == 0x01 || id == 0x7f) {
continue;
}
rewriteRValues(getClassLoader(), packageIdentifiers.valueAt(i), id);
}
return app;
}
可以发现这里也是以反射的机制创建了Application对象,并创建了一个ContextImpl对象,并将Application与ContextImpl建立关联。。。
继续回到我们的ActivityThread的handleBindApplication方法,在创建了Application对象之后我们调用了Instrumentation的onCreate方法,然后调用了Instrumentation的callApplicationOnCreate方法,我们来看一下其具体实现:
public void callApplicationOnCreate(Application app) {
app.onCreate();
}
咋样?原来Application的onCreate生命周期方法是在这里回调滴啊。
这样我们整个Application的创建执行流程就讲解完了。
总结:
应用进程启动 –> 创建Instrumentation –> 创建Application对象 –> 创建Application相关的ContextImpl对象;
ActivityThread.main方法–> ActivityManagerService.bindApplication方法 –> ActivityThread.handleBindApplication –> 创建Instrumentation,创建Application;
每个应用进程对应一个Instrumentation,对应一个Application;
Instrumentation与Application都是通过java反射机制创建;
Application创建过程中会同时创建一个ContextImpl对象,并建立关联;
接下来我们来看一下Acitivty中的Context创建流程,大家都知道我们Activity的具体创建过程是在ActivityThread的performLaunchActivity,可参见: android源码解析之(十四)–>Activity启动流程,这里我们看一下其具体的实现:
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
Activity activity = null
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader()
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent)
StrictMode.incrementExpectedActivityCount(activity.getClass())
r.intent.setExtrasClassLoader(cl)
r.intent.prepareToEnterProcess()
if (r.state != null) {
r.state.setClassLoader(cl)
}
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e)
}
}
try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation)
...
if (activity != null) {
Context appContext = createBaseContextForActivity(r, activity)
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager())
Configuration config = new Configuration(mCompatConfiguration)
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
+ r.activityInfo.name + " with config " + config)
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor)
if (customIntent != null) {
activity.mIntent = customIntent
}
r.lastNonConfigurationInstances = null
activity.mStartedActivity = false
int theme = r.activityInfo.getThemeResource()
if (theme != 0) {
activity.setTheme(theme)
}
activity.mCalled = false
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState)
} else {
mInstrumentation.callActivityOnCreate(activity, r.state)
}
if (!activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onCreate()")
}
r.activity = activity
r.stopped = true
if (!r.activity.mFinished) {
activity.performStart()
r.stopped = false
}
if (!r.activity.mFinished) {
if (r.isPersistable()) {
if (r.state != null || r.persistentState != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
r.persistentState)
}
} else if (r.state != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state)
}
}
...
return activity
}
这里简要说明一下,Activity也是系统通过反射机制创建的,然后我们通过LockedApk.makeApplication创建一个Application,通过查看源码我们知道若这时候LockedApk中的mApplication不为空则直接返回当前的mApplication又因为当我们创建应用进程的时候Application已经被创建,所以当创建Activity的时候这时候Application肯定不为空,所以这时候返回的就是应用进程创建的时候创建的Application,这也从侧面说明了一个应用进程对应着一个Application。然后我们通过createBaseContextForActivity创建了一个ContextImpl对象。
private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {
int displayId = Display.DEFAULT_DISPLAY;
try {
displayId = ActivityManagerNative.getDefault().getActivityDisplayId(r.token);
} catch (RemoteException e) {
}
ContextImpl appContext = ContextImpl.createActivityContext(
this, r.packageInfo, displayId, r.overrideConfig);
appContext.setOuterContext(activity);
Context baseContext = appContext;
final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
String pkgName = SystemProperties.get("debug.second-display.pkg");
if (pkgName != null && !pkgName.isEmpty()
&& r.packageInfo.mPackageName.contains(pkgName)) {
for (int id : dm.getDisplayIds()) {
if (id != Display.DEFAULT_DISPLAY) {
Display display =
dm.getCompatibleDisplay(id, appContext.getDisplayAdjustments(id));
baseContext = appContext.createDisplayContext(display);
break;
}
}
}
return baseContext;
}
可以发现这里创建了一个ContextImpl对象,并通过ContextImpl的setOuterContext方法,让该ContextImpl持有了Activity的引用,继续往下看,我们调用了activity.attach方法,查看一下该方法的实现逻辑:
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
attachBaseContext(context)
mFragments.attachHost(null )
mWindow = new PhoneWindow(this)
mWindow.setCallback(this)
mWindow.setOnWindowDismissedCallback(this)
mWindow.getLayoutInflater().setPrivateFactory(this)
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode)
}
if (info.uiOptions != 0) {
mWindow.setUiOptions(info.uiOptions)
}
mUiThread = Thread.currentThread()
mMainThread = aThread
mInstrumentation = instr
mToken = token
mIdent = ident
mApplication = application
mIntent = intent
mReferrer = referrer
mComponent = intent.getComponent()
mActivityInfo = info
mTitle = title
mParent = parent
mEmbeddedID = id
mLastNonConfigurationInstances = lastNonConfigurationInstances
if (voiceInteractor != null) {
if (lastNonConfigurationInstances != null) {
mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor
} else {
mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
Looper.myLooper())
}
}
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0)
if (mParent != null) {
mWindow.setContainer(mParent.getWindow())
}
mWindowManager = mWindow.getWindowManager()
mCurrentConfig = config
}
除了一下初始化操作之外,还调用了attachBaseContext方法,让Activity持有了ContextImpl的引用,这样就相当于Activity与ContextImpl对象相互持有了对方的引用,并且Activity是继承与Context。
总结:
Activity中创建ContextImpl对象的具体实现在ActivityThread的performLauncherAcitivty方法中;
Activity的创建伴随着ContextImpl的创建,二者相互持有对方的引用;
创建Activity –> 创建Activity相关ContextImpl对象;
创建应用进程 –> 创建Application –> 创建Application相关ContextImpl对象;
另外对android源码解析方法感兴趣的可参考我的:
android源码解析之(一)–>android项目构建过程
android源码解析之(二)–>异步消息机制
android源码解析之(三)–>异步任务AsyncTask
android源码解析之(四)–>HandlerThread
android源码解析之(五)–>IntentService
android源码解析之(六)–>Log
android源码解析之(七)–>LruCache
android源码解析之(八)–>Zygote进程启动流程
android源码解析之(九)–>SystemServer进程启动流程
android源码解析之(十)–>Launcher启动流程
android源码解析之(十一)–>应用进程启动流程
android源码解析之(十二)–>系统启动并解析Manifest的流程
android源码解析之(十三)–>apk安装流程
android源码解析之(十四)–>Activity启动流程
android源码解析之(十五)–>Activity销毁流程
本文以同步至github中:github.com/yipianfengy…,欢迎star和follow
转载请标明出处:一片枫叶的专栏
好吧,终于要开始讲讲Activity的布局加载流程了,大家都知道在Android体系中Activity扮演了一个界面展示的角色,这也是它与android中另外一个很重要的组件Service最大的不同,但是这个展示的界面的功能是Activity直接控制的么?界面的布局文件是如何加载到内存并被Activity管理的?android中的View是一个怎样的概念?加载到内存中的布局文件是如何绘制出来的?
要想回答这些问题,我们就需要对android的界面加载与绘制流程有所了解,这里我们先来学习一下Activity的布局加载的流程。而至于Acitivty的布局绘制流程我们在下一篇中在做介绍。
其实Activity对界面布局的管理是都是通过Window对象来实现的,Window对象,顾名思义就是一个窗口对象,而Activity从用户角度就是一个个的窗口实例,因此不难想象每个Activity中都对应着一个Window对象,而这个Window对象就是负责加载显示界面的。至于window对象是如何展示不同的界面的,那是通过定义不同的View组件实现不同的界面展示。
废话不多说了,不知道大家是否还记得我们讲过的Activity的启动流程么?不熟悉的童鞋可以参考: android源码解析之(十四)–>Activity启动流程 ,在文章中我们介绍到当ActivityManagerService接收到启动Activity的请求之后会通过IApplicationThread进程间通讯告知ApplicationThread并执行handleLauncherActivity方法,这里我们可以下其具体实现:
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
unscheduleGcIdler();
mSomeActivitiesChanged = true;
if (r.profilerInfo != null) {
mProfiler.setProfiler(r.profilerInfo);
mProfiler.startProfiling();
}
handleConfigurationChanged(null, null);
if (localLOGV) Slog.v(
TAG, "Handling launch of " + r);
WindowManagerGlobal.initialize();
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
Bundle oldState = r.state;
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed);
if (!r.activity.mFinished && r.startsNotResumed) {
try {
r.activity.mCalled = false;
mInstrumentation.callActivityOnPause(r.activity);
if (r.isPreHoneycomb()) {
r.state = oldState;
}
if (!r.activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onPause()");
}
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
if (!mInstrumentation.onException(r.activity, e)) {
throw new RuntimeException(
"Unable to pause activity "
+ r.intent.getComponent().toShortString()
+ ": " + e.toString(), e);
}
}
r.paused = true;
}
} else {
try {
ActivityManagerNative.getDefault()
.finishActivity(r.token, Activity.RESULT_CANCELED, null, false);
} catch (RemoteException ex) {
}
}
}
可以发现这里的handleLauncherActivity方法内部调用了performLaunchActivity方法,这个方法也是具体启动Activity的方法,我们来看一下它的具体实现逻辑:
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
Activity activity = null
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader()
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent)
StrictMode.incrementExpectedActivityCount(activity.getClass())
r.intent.setExtrasClassLoader(cl)
r.intent.prepareToEnterProcess()
if (r.state != null) {
r.state.setClassLoader(cl)
}
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e)
}
}
...
Application app = r.packageInfo.makeApplication(false, mInstrumentation)
if (localLOGV) Slog.v(TAG, "Performing launch of " + r)
if (localLOGV) Slog.v(
TAG, r + ": app=" + app
+ ", appName=" + app.getPackageName()
+ ", pkg=" + r.packageInfo.getPackageName()
+ ", comp=" + r.intent.getComponent().toShortString()
+ ", dir=" + r.packageInfo.getAppDir())
if (activity != null) {
Context appContext = createBaseContextForActivity(r, activity)
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager())
Configuration config = new Configuration(mCompatConfiguration)
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
+ r.activityInfo.name + " with config " + config)
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor)
...
return activity
}
从代码中可以看到这里是通过反射的机制创建的Activity,并调用了Activity的attach方法,那么这里的attach方法是做什么的呢?我们继续来看一下attach方法的实现逻辑:
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
attachBaseContext(context)
mFragments.attachHost(null )
mWindow = new PhoneWindow(this)
mWindow.setCallback(this)
mWindow.setOnWindowDismissedCallback(this)
mWindow.getLayoutInflater().setPrivateFactory(this)
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode)
}
if (info.uiOptions != 0) {
mWindow.setUiOptions(info.uiOptions)
}
mUiThread = Thread.currentThread()
mMainThread = aThread
mInstrumentation = instr
mToken = token
mIdent = ident
mApplication = application
mIntent = intent
mReferrer = referrer
mComponent = intent.getComponent()
mActivityInfo = info
mTitle = title
mParent = parent
mEmbeddedID = id
mLastNonConfigurationInstances = lastNonConfigurationInstances
if (voiceInteractor != null) {
if (lastNonConfigurationInstances != null) {
mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor
} else {
mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
Looper.myLooper())
}
}
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0)
if (mParent != null) {
mWindow.setContainer(mParent.getWindow())
}
mWindowManager = mWindow.getWindowManager()
mCurrentConfig = config
}
可以看到在attach方法这里初始化了一些Activity的成员变量,主要是mWindow对象,并且mWindow的成员实例是PhoneWindow实例,这样也从侧面说明了一个Activity对应着一个Window对象。除了window对象还初始化了一些Activity的其他成员变量,这里不再做讨论,继续回到我们的performLaunchActivity方法,在调用了Activity的attach方法之后又调用了:
mInstrumentation.callActivityOnCreate(activity, r.state)
这里的mInstrumentation是类Instrumentation,每个应用进程对应着一个Instrumentation和一个ActivityThread,Instrumentation就是具体操作Activity回调其生命周期方法的,我们这里看一下它的callActivityOnCreate方法的实现:
public void callActivityOnCreate(Activity activity, Bundle icicle) {
prePerformCreate(activity);
activity.performCreate(icicle);
postPerformCreate(activity);
}
这里代码比较简洁,preOerformCreate方法和postPerformCreate方法我们这里暂时不管,主要的执行逻辑是调用了activity.performCreate方法,我们来看一下Activity的performCreate方法的实现:
final void performCreate(Bundle icicle) {
onCreate(icicle);
mActivityTransitionState.readState(icicle);
performCreateCommon();
}
原来onCreate的生命周期方法是在这里回调的,其实这里的逻辑在前面几篇文章中有讲述,也可以参考前面的文章。
至此我们就回调到了我们Activity的onCreate方法,大家平时在重写onCreate方法的时候,怎么加载布局文件的呢?这里看一下我们的onCreate方法的典型写法:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
无论我们怎么变化,我们的onCreate方法一般都是会调用这两句话的吧?那么这里的两段代码分辨是什么含义呢?我们首先看一下super.onCreate方法的实现逻辑,由于我们的Activity类继承与Activity,所以这里的super.onCreate方法,就是调用的Activity.onCreate方法,好吧,既然这样我们来看一下Activity的onCreate方法:
protected void onCreate(@Nullable Bundle savedInstanceState) {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onCreate " + this + ": " + savedInstanceState);
if (mLastNonConfigurationInstances != null) {
mFragments.restoreLoaderNonConfig(mLastNonConfigurationInstances.loaders);
}
if (mActivityInfo.parentActivityName != null) {
if (mActionBar == null) {
mEnableDefaultActionBarUp = true;
} else {
mActionBar.setDefaultDisplayHomeAsUpEnabled(true);
}
}
if (savedInstanceState != null) {
Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
mFragments.restoreAllState(p, mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.fragments : null);
}
mFragments.dispatchCreate();
getApplication().dispatchActivityCreated(this, savedInstanceState);
if (mVoiceInteractor != null) {
mVoiceInteractor.attachActivity(this);
}
mCalled = true;
}
可以发现,Activity的onCreate方法主要是做了一些Acitivty的初始化操作,那么如果我们不在自己的Activity调用super.onCreate方法呢?好吧,尝试之后,AndroidStudio在打开的Acitivty的onCreate方法中如果不调用super.onCreate方法的话,会报错。。。有木有搞错。。。
FATAL EXCEPTION: main Process: com.example.aaron.helloworld, PID: 18001 android.util.SuperNotCalledException: Activity {com.example.aaron.helloworld/com.example.aaron.helloworld.SecondActivity} did not call through to super.onCreate() at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2422) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2528) at android.app.ActivityThread.access$800(ActivityThread.java:169) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1421) at android.os.Handler.dispatchMessage(Handler.java:111) at android.os.Looper.loop(Looper.java:194) at android.app.ActivityThread.main(ActivityThread.java:5552) at java.lang.reflect.Method.invoke(Native Method) at java.lang.reflect.Method.invoke(Method.java:372) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:964) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:759)
可以看到如果不调用super.onCreate方法的话,会在Activity的performLaunchActivity中报错,我们知道这里的performLaunchActivity方法就是我们启动Activity的时候回回调的方法,我们找找方法体实现中throws的Exception。。。
activity.mCalled = false
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState)
} else {
mInstrumentation.callActivityOnCreate(activity, r.state)
}
if (!activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onCreate()")
}
在Activity的performLaunchActivity方法中,我们在调用了Activity的onCreate方法之后会执行一个判断逻辑,若Activity的mCalled为false,则会抛出我们刚刚捕获的异常,那么这个mCalled成员变量是在什么时候被赋值的呢?好吧,就是在Activity的onCreate方法赋值的,所以我们在实现自己的Activity的时候只有调用了super.onCreate方法才不会抛出这个异常,反过来说,我们实现自己的Actiivty,那么一定要在onCreate方法中调用super.onCreate方法。
然后我们在看一下onCreate中的setContentView方法,这里的参数就是一个Layout布局文件,可以发现这里的setContentView方法就是Acitivty中的setContentView,好吧我们来看一下Activity中setContentView的实现:
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
这里的getWindow方法就是获取Acitivty的mWindow成员变量,从刚刚我们在Activity.attach方法我们知道这里的mWindow的实例是PhoneWindow,所以这里调用的其实是PhoneWindow的setConentView方法,然后我们看一下PhoneWindow的setContentView是如何实现的。
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
这里的mContentParent对象是一个View对象,由于第一次mContentParent为空,所以执行installerDector方法,这里我们看一下installerDector方法的具体实现:
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor();
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
}
...
}
这里的mDector是一个DectorView对象,而DectorView继承与FrameLayout,所以这里的mDector其实就是一个FrameLayout对象,并通过调用generateDector()方法初始化,我们继续看一下generateDector方法的具体实现:
protected DecorView generateDecor() {
return new DecorView(getContext(), -1);
}
好吧,就是通过new的方式创建了一个DectorView对象,然后我们继续看installDector方法:
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
这里初始化了mContentParent对象,这是一个View对象,我们调用了generateLayout方法,好吧,来看一下generateLayout方法的具体实现:
protected ViewGroup generateLayout(DecorView decor) {
...
// Inflate the window decor.
int layoutResource;
int features = getLocalFeatures();
// System.out.println("Features: 0x" + Integer.toHexString(features));
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
layoutResource = R.layout.screen_swipe_dismiss;
} else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogTitleIconsDecorLayout, res, true);
layoutResource = res.resourceId;
} else {
layoutResource = R.layout.screen_title_icons;
}
// XXX Remove this once action bar supports these features.
removeFeature(FEATURE_ACTION_BAR);
// System.out.println("Title Icons!");
} else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
&& (features & (1 << FEATURE_ACTION_BAR)) == 0) {
// Special case for a window with only a progress bar (and title).
// XXX Need to have a no-title version of embedded windows.
layoutResource = R.layout.screen_progress;
// System.out.println("Progress!");
} else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
// Special case for a window with a custom title.
// If the window is floating, we need a dialog layout
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogCustomTitleDecorLayout, res, true);
layoutResource = res.resourceId;
} else {
layoutResource = R.layout.screen_custom_title;
}
// XXX Remove this once action bar supports these features.
removeFeature(FEATURE_ACTION_BAR);
} else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
// If no other features and not embedded, only need a title.
// If the window is floating, we need a dialog layout
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogTitleDecorLayout, res, true);
layoutResource = res.resourceId;
} else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
layoutResource = a.getResourceId(
R.styleable.Window_windowActionBarFullscreenDecorLayout,
R.layout.screen_action_bar);
} else {
layoutResource = R.layout.screen_title;
}
// System.out.println("Title!");
} else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
layoutResource = R.layout.screen_simple_overlay_action_mode;
} else {
// Embedded, so no decoration is needed.
layoutResource = R.layout.screen_simple;
// System.out.println("Simple!");
}
mDecor.startChanging();
View in = mLayoutInflater.inflate(layoutResource, null);
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
mContentRoot = (ViewGroup) in;
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
ProgressBar progress = getCircularProgressBar(false);
if (progress != null) {
progress.setIndeterminate(true);
}
}
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
registerSwipeCallbacks();
}
// Remaining setup -- of background and title -- that only applies
// to top-level windows.
if (getContainer() == null) {
final Drawable background;
if (mBackgroundResource != 0) {
background = getContext().getDrawable(mBackgroundResource);
} else {
background = mBackgroundDrawable;
}
mDecor.setWindowBackground(background);
final Drawable frame;
if (mFrameResource != 0) {
frame = getContext().getDrawable(mFrameResource);
} else {
frame = null;
}
mDecor.setWindowFrame(frame);
mDecor.setElevation(mElevation);
mDecor.setClipToOutline(mClipToOutline);
if (mTitle != null) {
setTitle(mTitle);
}
if (mTitleColor == 0) {
mTitleColor = mTextColor;
}
setTitleColor(mTitleColor);
}
mDecor.finishChanging();
return contentParent;
}
可以发现这里就是通过调用LayoutInflater.inflate方法来加载布局文件到内存中,关于LayoutInflater.inflater是如何加载布局文件的,并且,通过对代码的分析,我们发现PhoneWindow中的几个成员变量:mDector,mContentRoot,mContentParent的关系
mDector –> mContentRoot –> mContentParent(包含)
并且我们来看一下典型的布局文件:
这里就是整个Activity加载的跟布局文件:screen_simple.xml,其中ViewStub对应着Activity中的titleBar而这里的FrameLayout里面主要用于填充内容。
然后我们具体看一下LayoutInflater.inflater方法:
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
return inflate(resource, root, root != null);
}
这里调用了inflate的重载方法。。。
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
final Context inflaterContext = mContext;
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context) mConstructorArgs[0];
mConstructorArgs[0] = inflaterContext;
View result = root;
try {
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG &&
type != XmlPullParser.END_DOCUMENT) {
}
if (type != XmlPullParser.START_TAG) {
throw new InflateException(parser.getPositionDescription()
+ ": No start tag found!");
}
final String name = parser.getName();
if (DEBUG) {
System.out.println("**************************");
System.out.println("Creating root view: "
+ name);
System.out.println("**************************");
}
if (TAG_MERGE.equals(name)) {
if (root == null || !attachToRoot) {
throw new InflateException(" can be used only with a valid "
+ "ViewGroup root and attachToRoot=true");
}
rInflate(parser, root, inflaterContext, attrs, false);
} else {
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
ViewGroup.LayoutParams params = null;
if (root != null) {
if (DEBUG) {
System.out.println("Creating params from root: " +
root);
}
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
temp.setLayoutParams(params);
}
}
if (DEBUG) {
System.out.println("-----> start inflating children");
}
rInflateChildren(parser, temp, attrs, true);
if (DEBUG) {
System.out.println("-----> done inflating children");
}
if (root != null && attachToRoot) {
root.addView(temp, params);
}
if (root == null || !attachToRoot) {
result = temp;
}
}
} catch (XmlPullParserException e) {
InflateException ex = new InflateException(e.getMessage());
ex.initCause(e);
throw ex;
} catch (Exception e) {
InflateException ex = new InflateException(
parser.getPositionDescription()
+ ": " + e.getMessage());
ex.initCause(e);
throw ex;
} finally {
mConstructorArgs[0] = lastContext;
mConstructorArgs[1] = null;
}
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
return result;
}
}
通过分析源码,不难发现,主要是通过循环解析xml文件并将信息解析到内存View对象,布局文件中定义的一个个组件都被顺序的解析到了内存中并被父子View的形式组织起来,这样通过给定的一个root View就可以将整个布局文件中定义的组件全部解析。分析完解析布局文件,回到我们的setContentVIew方法,在调用了installDector方法之后,又调用了:
mLayoutInflater.inflate(layoutResID, mContentParent)
这个方法的含义就是将我们传递的客户端的layoutId对应的布局文件作为mContentParent的子View加载到内存中,这样我们的layoutId作为mContentParent的子View,而mContentParent又是mContentRoot的子View,mContentRoot又是mDector的子View,通过LayoutInflater的inflate方法逐步加载到了内存中,而我们的Activity又持有自身的PhoneWindow的引用,这就相当于我们的Activity持有了我们定义的布局文件的引用,因而Activity的布局文件被加载到了内存中。
总结:
Activity的展示界面的特性是通过Window对象来控制的;
每个Activity对象都对应这个一个Window对象,并且Window对象的初始化在启动Activity的时候完成,在执行Activity的onCreate方法之前;
每个Window对象内部都存在一个FrameLayout类型的mDector对象,它是Acitivty界面的root view;
Activity中的window对象的实例是PhoneWindow对象,PhoneWindow对象中的几个成员变量mDector,mContentRoot,mContentParent都是View组件,它们的关系是:mDector –> mContentRoot –> mContentParent –> 自定义layoutView
LayoutInflater.inflate主要用于将布局文件加载到内存View组件中,也可以设定加载到某一个父组件中;
典型的Activity的onCreate方法中需要调用super.onCreate方法和setContentView方法,若不调用super.onCreate方法,执行启动该Activity的逻辑会报错,若不执行setContentView的方法,该Activity只会显示一个空页面。
好了,关于Activity的布局加载流程我们暂时介绍这么多,下一篇文章,我们将介绍一下Activity的布局显示流程。
另外对android源码解析方法感兴趣的可参考我的:
android源码解析之(一)–>android项目构建过程
android源码解析之(二)–>异步消息机制
android源码解析之(三)–>异步任务AsyncTask
android源码解析之(四)–>HandlerThread
android源码解析之(五)–>IntentService
android源码解析之(六)–>Log
android源码解析之(七)–>LruCache
android源码解析之(八)–>Zygote进程启动流程
android源码解析之(九)–>SystemServer进程启动流程
android源码解析之(十)–>Launcher启动流程
android源码解析之(十一)–>应用进程启动流程
android源码解析之(十二)–>系统启动并解析Manifest的流程
android源码解析之(十三)–>apk安装流程
android源码解析之(十四)–>Activity启动流程
android源码解析之(十五)–>Activity销毁流程
android源码解析(十六)–>应用进程Context创建流程
本文以同步至github中:github.com/yipianfengy…,欢迎star和follow
转载请标明出处:一片枫叶的专栏
这篇文章是承接上一篇文章(Android布局加载流程:android源码解析(十七)–>Activity布局加载流程)来写的,大家都知道Activity在Android体系中扮演者一个界面展示的角色,通过上一篇文章的分析,我们知道Activity是通过Window来控制界面的展示的,一个Window对象就是一个窗口对象,而每个Activity中都有一个相应的Window对象,所以说一个Activity对象也就可以说是一个窗口对象,而Window只是控制着界面布局文件的加载过程,那么界面布局文件的绘制流程是如何的呢?这篇文章主要就是顺着上篇文章的思路,看一下在android系统中Activity的布局文件是如何绘制的。
顺便在这里多说几句,android中所有能显示的东西都是通过Window对象实现了,无论Activity,Dialog,PopupWindow,Toast等。后期我可能也会讲一下Dialog,PopupWindow,Toast等组件的显示过程。
前面有一篇文章中我们介绍过Activity的启动流程,可参考:android源码解析之(十四)–>Activity启动流程
在执行ActivityThread的handleLauncherActivity方法中通过Window对象控制了布局文件的加载流程,而Android体系在执行Activity的onResume方法之前会回调ActivityThread的handleResumeActivity方法:
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume) {
...
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
}
...
if (!r.activity.mFinished && willBeVisible
&& r.activity.mDecor != null && !r.hideForNow) {
if (r.newConfig != null) {
r.tmpConfig.setTo(r.newConfig);
if (r.overrideConfig != null) {
r.tmpConfig.updateFrom(r.overrideConfig);
}
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Resuming activity "
+ r.activityInfo.name + " with newConfig " + r.tmpConfig);
performConfigurationChanged(r.activity, r.tmpConfig);
freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(r.tmpConfig));
r.newConfig = null;
}
if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward="
+ isForward);
WindowManager.LayoutParams l = r.window.getAttributes();
if ((l.softInputMode
& WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)
!= forwardBit) {
l.softInputMode = (l.softInputMode
& (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))
| forwardBit;
if (r.activity.mVisibleFromClient) {
ViewManager wm = a.getWindowManager();
View decor = r.window.getDecorView();
wm.updateViewLayout(decor, l);
}
}
r.activity.mVisibleFromServer = true;
mNumVisibleActivities++;
if (r.activity.mVisibleFromClient) {
r.activity.makeVisible();
}
}
if (!r.onlyLocalRequest) {
r.nextIdle = mNewActivities;
mNewActivities = r;
if (localLOGV) Slog.v(
TAG, "Scheduling idle handler for " + r);
Looper.myQueue().addIdleHandler(new Idler());
}
r.onlyLocalRequest = false;
if (reallyResume) {
try {
ActivityManagerNative.getDefault().activityResumed(token);
} catch (RemoteException ex) {
}
}
...
}
可以看到在在获取了Activity的Window相关参数之后执行了r.activity.makeVisible()方法,看样子这个就是Activity的显示方法,这里我们来具体看一下makeVisible方法的具体实现逻辑:
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager()
wm.addView(mDecor, getWindow().getAttributes())
mWindowAdded = true
}
mDecor.setVisibility(View.VISIBLE)
}
首先判断成员变量mWindowAdded是否为true,可以发现mWindowAdded成员变量只有在执行之后才能赋值为true,所以这里的代码的主要逻辑是该if分支只能执行一次。
这里的ViewManager对象是通过getWindowManager()方法获取的,我们来看一下getWindowManager()方法的具体实现:
public WindowManager getWindowManager() {
return mWindowManager;
}
好吧,原来就是返回的Activity的mWindowManager的成员变量,那么这个mWindowManager的成员变量是什么时候赋值的呢?上一篇文章我们在Activity的attach方法方法中初始化了Activity的相关成员变量,这里也包括了mWindowManager,我们来看一下mWindowManager的赋值过程:
mWindowManager = mWindow.getWindowManager();
好吧,这里的Window.getWindowManager()方法是具体如何实现的呢?
public WindowManager getWindowManager() {
return mWindowManager;
}
那么这里的Window对象的mWindowManager成员变量是具体如何赋值的?
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated
|| SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
好吧,可以发现mWindowManager = ((WindowManagerImpl)vm).createLocalWindowManager(this)原来是在这里赋值的,所以一个Activity对应这一个新的Window,而这个Window对象内部会对应着一个新的WindowManager对象,我们接着往下看,那么createLoclWindowManager方法是如何实现的呢?
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mDisplay, parentWindow);
}
好吧,原来是new出了一个WindowManagerImpl对象,所以回到我们的Activity的makeVisible方法,ViewManager获取的是一个WindowManagerImpl对象,所以Window对象内部的WindowManager对象其实都是一个WindowManagerImpl的实例,都是而且从继承关系上可以看到:
WindowManagerImpl –> WindowManager –> ViewManager;
继续往下看:
wm.addView(mDecor, getWindow().getAttributes())
这里的mDector成员变量,通过上一篇文章的介绍,我们知道,它是Activity的界面根View,而getWindow.getAttrbutes方法是windowManager中定义的Params内部类,该内部类定义了许多的Window类型,由于这里的vm是WindowManagerImpl的实例,我们来看一下这里的addView的具体实现:
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mDisplay, mParentWindow);
}
然后我们具体看一下mGlobal.addView方法,这里的mGlobal是一个WindowManagerGlobal的单例对象,WindowManagerGlobal是Window处理的工具类,那么WindowManagerGlobal的addView具体是如何实现的呢?
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
...
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
if (mSystemPropertyUpdater == null) {
mSystemPropertyUpdater = new Runnable() {
@Override public void run() {
synchronized (mLock) {
for (int i = mRoots.size() - 1; i >= 0; --i) {
mRoots.get(i).loadSystemProperties();
}
}
}
};
SystemProperties.addChangeCallback(mSystemPropertyUpdater);
}
int index = findViewLocked(view, false);
if (index >= 0) {
if (mDyingViews.contains(view)) {
mRoots.get(index).doDie();
} else {
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
}
}
if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
final int count = mViews.size();
for (int i = 0; i < count; i++) {
if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
panelParentView = mViews.get(i);
}
}
}
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
}
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
synchronized (mLock) {
final int index = findViewLocked(view, false);
if (index >= 0) {
removeViewLocked(index, true);
}
}
throw e;
}
}
可以发现在WindowManagerGlobal中存在着三个数据列表:
private final ArrayList mViews = new ArrayList();
private final ArrayList mRoots = new ArrayList();
private final ArrayList mParams =
new ArrayList();
其中mViews主要用于保存Activity的mDector也就是Activity的根View,而mRoots主要用于保存ViewRootImpl,mParams主要用于保存Window的LayoutParams,WindowManagerGlobal主要作为WindowManagerImpl的辅助方法类,用于操作View组件。
最后我们调用了root.setView方法,这个方法很重要我们就是在这里实现了我们的root与ViewRootImpl的关联的,除了实现了mDector与ViewRootImpl的相互关联,我们还调用了requestLayout方法,这里我们看一下setView方法的具体实现:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...
requestLayout();
...
}
可以看到,在方法体中又调用了requestLayout方法,这个方法其实就是调用执行重绘的请求,我们来看一下这个requestLayout方法具体实现:
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
可以看到这里有一个checkThread方法,这个方法是检查当前线程的方法,若当前线程非UI线程,则抛出非UI线程更新UI的错误:
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
相信大家平时在编程的过程中肯定会遇到过这个错误,ViewRootImpl是具体更新View的管理类,所有关于View的更新操作都是在这里执行的,自然而然的对于更新线程的检测是在这个类中添加的,一般在更新UI的时候都会调用这个方法用于检测当前执行更新UI的线程是否是UI线程,否则就会抛出这个异常。
继续回到我们的requestLayout方法,这里又调用了scheduleTraversales方法,我们来看一下这个方法的具体实现:
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
这里mChoreographer.postCallback,内部会调用一个异步消息,用于执行mTraversalRunnable的run方法,这个mTraversalRunnable是一个Runnable对象,我们来看一下mTraversalRunnable类的定义:
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
在TraversalRunnable类的run方法中调用了doTraversal方法,我们来看一下这个方法的具体实现逻辑:
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
好吧,其内部又回调了方法performTraversals方法,这个方法就是整个View的绘制起始方法,从这个方法开始我们的View经过大小测量,位置测量,界面绘制三个逻辑操作之后就可以展示在界面中了。
private void performTraversals() {
...
// 执行View组件的onMeasure方法,主要用于测量View
if (!mStopped || mReportNextDraw) {
boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
(relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
|| mHeight != host.getMeasuredHeight() || contentInsetsChanged) {
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
if (DEBUG_LAYOUT) Log.v(TAG, "Ooops, something changed! mWidth="
+ mWidth + " measuredWidth=" + host.getMeasuredWidth()
+ " mHeight=" + mHeight
+ " measuredHeight=" + host.getMeasuredHeight()
+ " coveredInsetsChanged=" + contentInsetsChanged);
// Ask host how big it wants to be
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
// Implementation of weights from WindowManager.LayoutParams
// We just grow the dimensions as needed and re-measure if
// needs be
int width = host.getMeasuredWidth();
int height = host.getMeasuredHeight();
boolean measureAgain = false;
if (lp.horizontalWeight > 0.0f) {
width += (int) ((mWidth - width) * lp.horizontalWeight);
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width,
MeasureSpec.EXACTLY);
measureAgain = true;
}
if (lp.verticalWeight > 0.0f) {
height += (int) ((mHeight - height) * lp.verticalWeight);
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
MeasureSpec.EXACTLY);
measureAgain = true;
}
if (measureAgain) {
if (DEBUG_LAYOUT) Log.v(TAG,
"And hey let's measure once more: width=" + width
+ " height=" + height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
}
layoutRequested = true;
}
}
}
...
// 主要用于测量View组件的位置
...
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
boolean triggerGlobalLayoutListener = didLayout
|| mAttachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
// By this point all views have been sized and positioned
// We can compute the transparent area
if ((host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0) {
// start out transparent
// TODO: AVOID THAT CALL BY CACHING THE RESULT?
host.getLocationInWindow(mTmpLocation);
mTransparentRegion.set(mTmpLocation[0], mTmpLocation[1],
mTmpLocation[0] + host.mRight - host.mLeft,
mTmpLocation[1] + host.mBottom - host.mTop);
host.gatherTransparentRegion(mTransparentRegion);
if (mTranslator != null) {
mTranslator.translateRegionInWindowToScreen(mTransparentRegion);
}
if (!mTransparentRegion.equals(mPreviousTransparentRegion)) {
mPreviousTransparentRegion.set(mTransparentRegion);
mFullRedrawNeeded = true;
// reconfigure window manager
try {
mWindowSession.setTransparentRegion(mWindow, mTransparentRegion);
} catch (RemoteException e) {
}
}
}
if (DBG) {
System.out.println("======================================");
System.out.println("performTraversals -- after setFrame");
host.debug();
}
}
...
// 主要用于View的绘制过程
...
if (!cancelDraw && !newSurface) {
if (!skipDraw || mReportNextDraw) {
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).startChangingAnimations();
}
mPendingTransitions.clear();
}
performDraw();
}
} else {
if (viewVisibility == View.VISIBLE) {
// Try again
scheduleTraversals();
} else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).endChangingAnimations();
}
mPendingTransitions.clear();
}
}
mIsInTraversal = false;
}
可以看到在方法performTraversals方法,我们调用了performMeasure,performLayout,performDraw三个方法,这几个方法主要用于测量View组件的大小,测量View组件的位置,绘制View组件;
即:测量大小 –> 测量位置 –> 绘制组件
好吧,这里我们调用了performMeasure方法,我们先看一下performMeasure方法的具体实现:
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
try {
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
可以看到在performMeasure方法中我们又调用了mView的measure方法,这里的mView就是我们一开始的Activity的mDector根组件,这里的measure方法就是调用的mDector组件的measure方法:
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
...
onMeasure(widthMeasureSpec, heightMeasureSpec);
...
}
在View的measure方法中,又调用了onMeasure方法,由于我们的mDector对象是一个FrameLayout,所以这里的onMeasure执行的是FrameLayout的onMeasure方法:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();
final boolean measureMatchParentChildren =
MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
mMatchParentChildren.clear();
int maxHeight = 0;
int maxWidth = 0;
int childState = 0;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (mMeasureAllChildren || child.getVisibility() != GONE) {
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
maxWidth = Math.max(maxWidth,
child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
maxHeight = Math.max(maxHeight,
child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
childState = combineMeasuredStates(childState, child.getMeasuredState());
if (measureMatchParentChildren) {
if (lp.width == LayoutParams.MATCH_PARENT ||
lp.height == LayoutParams.MATCH_PARENT) {
mMatchParentChildren.add(child);
}
}
}
}
maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
final Drawable drawable = getForeground();
if (drawable != null) {
maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
}
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
resolveSizeAndState(maxHeight, heightMeasureSpec,
childState << MEASURED_HEIGHT_STATE_SHIFT));
count = mMatchParentChildren.size();
if (count > 1) {
for (int i = 0; i < count; i++) {
final View child = mMatchParentChildren.get(i);
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
final int childWidthMeasureSpec;
if (lp.width == LayoutParams.MATCH_PARENT) {
final int width = Math.max(0, getMeasuredWidth()
- getPaddingLeftWithForeground() - getPaddingRightWithForeground()
- lp.leftMargin - lp.rightMargin);
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
width, MeasureSpec.EXACTLY);
} else {
childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
getPaddingLeftWithForeground() + getPaddingRightWithForeground() +
lp.leftMargin + lp.rightMargin,
lp.width);
}
final int childHeightMeasureSpec;
if (lp.height == LayoutParams.MATCH_PARENT) {
final int height = Math.max(0, getMeasuredHeight()
- getPaddingTopWithForeground() - getPaddingBottomWithForeground()
- lp.topMargin - lp.bottomMargin);
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
height, MeasureSpec.EXACTLY);
} else {
childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
getPaddingTopWithForeground() + getPaddingBottomWithForeground() +
lp.topMargin + lp.bottomMargin,
lp.height);
}
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
}
可以看到这里调用了一个循环逻辑,获取该View的所有子View,并执行所有子View的measure方法,这样又回到View的measure方法,这样经过一系列的循环遍历过程,如果是ViewGroup就会调用其ViewGroup的onMeasure方法,若果是View组件就会调用View的onMeasure方法,我们来看一下View的onMeasure方法:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
可以看到这个方法中调用了setMeasuredDimension方法:
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
boolean optical = isLayoutModeOptical(this);
if (optical != isLayoutModeOptical(mParent)) {
Insets insets = getOpticalInsets();
int opticalWidth = insets.left + insets.right;
int opticalHeight = insets.top + insets.bottom;
measuredWidth += optical ? opticalWidth : -opticalWidth;
measuredHeight += optical ? opticalHeight : -opticalHeight;
}
setMeasuredDimensionRaw(measuredWidth, measuredHeight);
}
好吧,方法体里面又调用了setMeasuredDimensionRaw方法:
private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
mMeasuredWidth = measuredWidth;
mMeasuredHeight = measuredHeight;
mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
}
这样把View组件即其子View的大小测量出来了,并且保存在了成员变量mMeasuredWith和mMeasuredHeight中。
继续回到我们的performTransles方法,然后我们继续看performLayout方法:
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
mLayoutRequested = false;
mScrollMayChange = true;
mInLayout = true;
final View host = mView;
if (DEBUG_ORIENTATION || DEBUG_LAYOUT) {
Log.v(TAG, "Laying out " + host + " to (" +
host.getMeasuredWidth() + ", " + host.getMeasuredHeight() + ")");
}
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");
try {
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
mInLayout = false;
int numViewsRequestingLayout = mLayoutRequesters.size();
if (numViewsRequestingLayout > 0) {
ArrayList validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters,
false);
if (validLayoutRequesters != null) {
mHandlingLayoutInLayoutRequest = true;
int numValidRequests = validLayoutRequesters.size();
for (int i = 0; i < numValidRequests; ++i) {
final View view = validLayoutRequesters.get(i);
Log.w("View", "requestLayout() improperly called by " + view +
" during layout: running second layout pass");
view.requestLayout();
}
measureHierarchy(host, lp, mView.getContext().getResources(),
desiredWindowWidth, desiredWindowHeight);
mInLayout = true;
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
mHandlingLayoutInLayoutRequest = false;
validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters, true);
if (validLayoutRequesters != null) {
final ArrayList finalRequesters = validLayoutRequesters;
getRunQueue().post(new Runnable() {
@Override
public void run() {
int numValidRequests = finalRequesters.size();
for (int i = 0; i < numValidRequests; ++i) {
final View view = finalRequesters.get(i);
Log.w("View", "requestLayout() improperly called by " + view +
" during second layout pass: posting in next frame");
view.requestLayout();
}
}
});
}
}
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
mInLayout = false;
}
可以看到在方法体中,我们看到该方法执行了layout方法,我们看一下该layout方法的实现:
public void layout(int l, int t, int r, int b) {
if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
}
int oldL = mLeft;
int oldT = mTop;
int oldB = mBottom;
int oldR = mRight;
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
onLayout(changed, l, t, r, b);
mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnLayoutChangeListeners != null) {
ArrayList listenersCopy =
(ArrayList)li.mOnLayoutChangeListeners.clone();
int numListeners = listenersCopy.size();
for (int i = 0; i < numListeners; ++i) {
listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
}
}
}
mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
}
可以看到这个方法体中执行了onLayout方法,这个方法就是具体执行测量位置的方法了,由于我们的mDector是一个FrameLayout,所以跟measure类似的,我们看一下FrameLayout的onLayout方法的实现:
我们看到我们定义了一个循环逻辑,获取所有的validLayoutRequesters也就是需要执行Layout方法的View的集合,通过循环执行view的requestLayout方法。这里我们来看一下requestLayout方法的具体实现:
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
layoutChildren(left, top, right, bottom, false );
}
可以看到这里调用了layoutChildren方法,让我们来看一下layoutChildren方法的实现:
void layoutChildren(int left, int top, int right, int bottom,
boolean forceLeftGravity) {
final int count = getChildCount();
final int parentLeft = getPaddingLeftWithForeground();
final int parentRight = right - left - getPaddingRightWithForeground();
final int parentTop = getPaddingTopWithForeground();
final int parentBottom = bottom - top - getPaddingBottomWithForeground();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final int width = child.getMeasuredWidth();
final int height = child.getMeasuredHeight();
int childLeft;
int childTop;
int gravity = lp.gravity;
if (gravity == -1) {
gravity = DEFAULT_CHILD_GRAVITY;
}
final int layoutDirection = getLayoutDirection();
final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.CENTER_HORIZONTAL:
childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
lp.leftMargin - lp.rightMargin;
break;
case Gravity.RIGHT:
if (!forceLeftGravity) {
childLeft = parentRight - width - lp.rightMargin;
break;
}
case Gravity.LEFT:
default:
childLeft = parentLeft + lp.leftMargin;
}
switch (verticalGravity) {
case Gravity.TOP:
childTop = parentTop + lp.topMargin;
break;
case Gravity.CENTER_VERTICAL:
childTop = parentTop + (parentBottom - parentTop - height) / 2 +
lp.topMargin - lp.bottomMargin;
break;
case Gravity.BOTTOM:
childTop = parentBottom - height - lp.bottomMargin;
break;
default:
childTop = parentTop + lp.topMargin;
}
child.layout(childLeft, childTop, childLeft + width, childTop + height);
}
}
}
跟measure类似的,这里也是遍历执行View的layout方法,若是ViewGroup则执行具体的ViewGroup的layout方法,若是View,则执行View的layout方法,好吧,我们看一下View的layout的具体实现逻辑:
public void layout(int l, int t, int r, int b) {
if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
}
int oldL = mLeft;
int oldT = mTop;
int oldB = mBottom;
int oldR = mRight;
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
onLayout(changed, l, t, r, b);
mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnLayoutChangeListeners != null) {
ArrayList listenersCopy =
(ArrayList)li.mOnLayoutChangeListeners.clone();
int numListeners = listenersCopy.size();
for (int i = 0; i < numListeners; ++i) {
listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
}
}
}
mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
}
这样经过layout方法,如果是View组件的话就已经将View组件的位置信息计算出来并保存在对象的成员变量中。
好吧,经过了测量大小与测量位置的逻辑之后,我们最后看一下performTraversals方法中的performDraw方法,这个方法的作用就是执行View组件的绘制逻辑了。
private void performDraw() {
...
draw(fullRedrawNeeded);
...
}
可以看到这里调用了ViewRootImpl的draw方法,然后我们看一下draw方法的实现:
private void draw(boolean fullRedrawNeeded) {
...
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
return;
}
...
}
可以看到这里又调用了drawSoftware方法,看名字这里应该就是调用执行绘制的方法:
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty) {
...
mView.draw(canvas);
...
return true;
}
可以看到这里调用了mView的draw方法,这里的mView是我们的mDector,好吧,看一下draw方法的具体实现:
public void draw(Canvas canvas) {
final int privateFlags = mPrivateFlags;
final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
int saveCount;
if (!dirtyOpaque) {
drawBackground(canvas);
}
final int viewFlags = mViewFlags;
boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
if (!verticalEdges && !horizontalEdges) {
if (!dirtyOpaque) onDraw(canvas);
dispatchDraw(canvas);
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
onDrawForeground(canvas);
return;
}
boolean drawTop = false;
boolean drawBottom = false;
boolean drawLeft = false;
boolean drawRight = false;
float topFadeStrength = 0.0f;
float bottomFadeStrength = 0.0f;
float leftFadeStrength = 0.0f;
float rightFadeStrength = 0.0f;
int paddingLeft = mPaddingLeft;
final boolean offsetRequired = isPaddingOffsetRequired();
if (offsetRequired) {
paddingLeft += getLeftPaddingOffset();
}
int left = mScrollX + paddingLeft;
int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
int top = mScrollY + getFadeTop(offsetRequired);
int bottom = top + getFadeHeight(offsetRequired);
if (offsetRequired) {
right += getRightPaddingOffset();
bottom += getBottomPaddingOffset();
}
final ScrollabilityCache scrollabilityCache = mScrollCache;
final float fadeHeight = scrollabilityCache.fadingEdgeLength;
int length = (int) fadeHeight;
if (verticalEdges && (top + length > bottom - length)) {
length = (bottom - top) / 2;
}
if (horizontalEdges && (left + length > right - length)) {
length = (right - left) / 2;
}
if (verticalEdges) {
topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));
drawTop = topFadeStrength * fadeHeight > 1.0f;
bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));
drawBottom = bottomFadeStrength * fadeHeight > 1.0f;
}
if (horizontalEdges) {
leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));
drawLeft = leftFadeStrength * fadeHeight > 1.0f;
rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));
drawRight = rightFadeStrength * fadeHeight > 1.0f;
}
saveCount = canvas.getSaveCount();
int solidColor = getSolidColor();
if (solidColor == 0) {
final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
if (drawTop) {
canvas.saveLayer(left, top, right, top + length, null, flags);
}
if (drawBottom) {
canvas.saveLayer(left, bottom - length, right, bottom, null, flags);
}
if (drawLeft) {
canvas.saveLayer(left, top, left + length, bottom, null, flags);
}
if (drawRight) {
canvas.saveLayer(right - length, top, right, bottom, null, flags);
}
} else {
scrollabilityCache.setFadeColor(solidColor);
}
if (!dirtyOpaque) onDraw(canvas);
dispatchDraw(canvas);
final Paint p = scrollabilityCache.paint;
final Matrix matrix = scrollabilityCache.matrix;
final Shader fade = scrollabilityCache.shader;
if (drawTop) {
matrix.setScale(1, fadeHeight * topFadeStrength);
matrix.postTranslate(left, top);
fade.setLocalMatrix(matrix);
p.setShader(fade);
canvas.drawRect(left, top, right, top + length, p);
}
if (drawBottom) {
matrix.setScale(1, fadeHeight * bottomFadeStrength);
matrix.postRotate(180);
matrix.postTranslate(left, bottom);
fade.setLocalMatrix(matrix);
p.setShader(fade);
canvas.drawRect(left, bottom - length, right, bottom, p);
}
if (drawLeft) {
matrix.setScale(1, fadeHeight * leftFadeStrength);
matrix.postRotate(-90);
matrix.postTranslate(left, top);
fade.setLocalMatrix(matrix);
p.setShader(fade);
canvas.drawRect(left, top, left + length, bottom, p);
}
if (drawRight) {
matrix.setScale(1, fadeHeight * rightFadeStrength);
matrix.postRotate(90);
matrix.postTranslate(right, top);
fade.setLocalMatrix(matrix);
p.setShader(fade);
canvas.drawRect(right - length, top, right, bottom, p);
}
canvas.restoreToCount(saveCount);
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
onDrawForeground(canvas);
}
整个View的绘制流程还是比较清楚的,整个执行逻辑还有相应的注释,一共大概需要六步,并且在执行draw方法的过程中,如果包含子View,那么也会执行子View的draw方法,好吧,经过这样一系列的执行逻辑之后,mDector以及子View就被绘制出来了。
总结:
Activity执行onResume之后再ActivityThread中执行Activity的makeVisible方法。
View的绘制流程包含了测量大小,测量位置,绘制三个流程;
Activty的界面绘制是从mDector即根View开始的,也就是从mDector的测量大小,测量位置,绘制三个流程;
View体系的绘制流程是从ViewRootImpl的performTraversals方法开始的;
View的测量大小流程:performMeasure –> measure –> onMeasure等方法;
View的测量位置流程:performLayout –> layout –> onLayout等方法;
View的绘制流程:onDraw等方法;
View组件的绘制流程会在onMeasure,onLayout以及onDraw方法中执行分发逻辑,也就是在onMeasure同时执行子View的测量大小逻辑,在onLayout中同时执行子View的测量位置逻辑,在onDraw中同时执行子View的绘制逻辑;
Activity中都对应这个一个Window对象,而每一个Window对象都对应着一个新的WindowManager对象(WindowManagerImpl实例);
另外对android源码解析方法感兴趣的可参考我的:
android源码解析之(一)–>android项目构建过程
android源码解析之(二)–>异步消息机制
android源码解析之(三)–>异步任务AsyncTask
android源码解析之(四)–>HandlerThread
android源码解析之(五)–>IntentService
android源码解析之(六)–>Log
android源码解析之(七)–>LruCache
android源码解析之(八)–>Zygote进程启动流程
android源码解析之(九)–>SystemServer进程启动流程
android源码解析之(十)–>Launcher启动流程
android源码解析之(十一)–>应用进程启动流程
android源码解析之(十二)–>系统启动并解析Manifest的流程
android源码解析之(十三)–>apk安装流程
android源码解析之(十四)–>Activity启动流程
android源码解析之(十五)–>Activity销毁流程
android源码解析(十六)–>应用进程Context创建流程
android源码解析(十七)–>Activity布局加载流程
本文以同步至github中:github.com/yipianfengy…,欢迎star和follow
转载请标明出处:一片枫叶的专栏
前面两篇文章,我们分析了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的使用实例。
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的构造方法:
public Builder(Context context) {
this(context, resolveDialogTheme(context, 0));
}
好吧,这里调用的是Builder的重载构造方法:
public Builder(Context context, int themeResId) {
P = new AlertController.AlertParams(new ContextThemeWrapper(
context, resolveDialogTheme(context, themeResId)));
}
那么这里的P是AlertDialog.Builder中的一个AlertController.AlertParams类型的成员变量,可见在这里执行了P的初始化操作。
public AlertParams(Context context) {
mContext = context;
mCancelable = true;
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
可以看到这里主要执行了AlertController.AlertParams的初始化操作,初始化了一些成员变量。这样执行了一系列操作之后我们的代码:
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this)
就已经执行完成了,然后我们调用了builder.setIcon方法,这里看一下setIcon方法的具体实现:
public Builder setIcon(@DrawableRes int iconId) {
P.mIconId = iconId;
return this;
}
可以看到AlertDialog的Builder的setIcon方法,这里执行的就是给类型为AlertController.AlertParams的P的mIconId赋值为传递的iconId,并且这个方法返回的类型就是Builder。
然后我们调用了builder.setMessage方法,可以看一下builder.setMessage方法的具体实现:
public Builder setMessage(CharSequence message) {
P.mMessage = message;
return this;
}
好吧,这里跟setIcon方法的实现逻辑类似,都是给成员变量的mMessage赋值为我们传递的Message值,且和setIcon方法类似的,这个方法返回值也是Builder。
再看一下builder.setTitle方法:
public Builder setTitle(CharSequence title) {
P.mTitle = title;
return this;
}
可以发现builder的setIcon、setMessage、setTitle等方法都是给Builder的成员变量P的icon,message,title赋值。
然后我们看一下builder.setView方法:
public Builder setView(int layoutResId) {
P.mView = null;
P.mViewLayoutResId = layoutResId;
P.mViewSpacingSpecified = false;
return this;
}
可以发现这里的setView和setIcon,setMessage,setTitle等方法都是类似的,都是将我们传递的数据值赋值给Builder的成员变量P。
然后我们调用了builder.setPositiveButton方法:
builder.setPositiveButton("知道了", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
alertDialog.dismiss();
}
});
好吧,这里我们看一下builder的setPositiveButton的源码:
public Builder setPositiveButton(CharSequence text, final OnClickListener listener) {
P.mPositiveButtonText = text;
P.mPositiveButtonListener = listener;
return this;
}
好吧,可以发现跟上面几个方法还是类似的,都是为Builder的成员变量P的相应成员变量赋值。。。
上面的几行代码我们都是调用的builder.setXXX等方法,主要就是为Builder的成员变量P的相应成员变量值赋值。并且setXX方法返回值都是Builder类型的,因此我们可以通过消息琏的方式连续执行:
builder.setIcon().setMessage().setTitle().setView().setPositiveButton()...
这样代码显得比较简洁,set方法的执行顺序是没有固定模式的,这里多说一下,这种编程方式很优秀,平时我们在设计构造类工具类的时候也可以参考这种模式,构造类有不同的功能或者特性,并且都不是必须的,我们可以通过set方法设置不同的特性值并返回构造类本身。
然后我们调用了builder.create方法,并且这个方法返回了AlertDialog。
public AlertDialog create() {
// Context has already been wrapped with the appropriate theme.
final AlertDialog dialog = new AlertDialog(P.mContext, 0, false)
P.apply(dialog.mAlert)
dialog.setCancelable(P.mCancelable)
if (P.mCancelable) {
dialog.setCanceledOnTouchOutside(true)
}
dialog.setOnCancelListener(P.mOnCancelListener)
dialog.setOnDismissListener(P.mOnDismissListener)
if (P.mOnKeyListener != null) {
dialog.setOnKeyListener(P.mOnKeyListener)
}
return dialog
}
可以看到这里首先构造了一个AlertDialog,我们可以看一下这个构造方法的具体实现:
AlertDialog(Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
super(context, createContextThemeWrapper ? resolveDialogTheme(context, themeResId) : 0,
createContextThemeWrapper);
mWindow.alwaysReadCloseOnTouchAttr();
mAlert = new AlertController(getContext(), this, getWindow());
}
可以看到这里首先调用了super的构造方法,而我们的AlertDialog继承于Dialog,所以这里执行的就是Dialog的构造方法,好吧,继续看一下Dialog的构造方法:
Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
if (createContextThemeWrapper) {
if (themeResId == 0) {
final TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true);
themeResId = outValue.resourceId;
}
mContext = new ContextThemeWrapper(context, themeResId);
} else {
mContext = context;
}
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
final Window w = new PhoneWindow(mContext);
mWindow = w;
w.setCallback(this);
w.setOnWindowDismissedCallback(this);
w.setWindowManager(mWindowManager, null, null);
w.setGravity(Gravity.CENTER);
mListenersHandler = new ListenersHandler(this);
}
可以发现在Dialog的构造方法中直接直接构造了一个PhoneWindow,并赋值给Dialog的成员变量mWindow,从这里可以看出其实Dialog和Activity的显示逻辑都是类似的,都是通过对应的Window变量来实现窗口的加载与显示的。然后我们执行了一些Window对象的初始化操作,比如设置回调函数为本身,然后调用了Window类的setWindowManager方法,并传入了WindowManager,可以发现这里的WindowManager对象是通过方法:
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE)
获取的,而我们的context传入的是Activity对象,所以这里的WindowManager对象其实和Activity获取的WindowManager对象是一致的。然后我们看一下window类的setWindowManager方法:
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated
|| SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
可以看到跟Activity的Window对象的windowManager的获取方式是相同的,都是通过new的方式创建一个新的WindowManagerImpl对象。好吧,继续回到我们的AlertDialog的构造方法中,在构造方法中,我们除了调用Dialog的构造方法之外还执行了:
mAlert = new AlertController(getContext(), this, getWindow());
相当于初始化了AlertDiaog的成员变量mAlert。
继续回到我们的AlertDialog.Builder.create方法,在创建了一个AlertDialog之后,又执行了P.apply(dialog.mAlert);
我们知道这里的P是一个AlertController.AlertParams的变量,而dialog.mAlert是我们刚刚创建的AlertDialog中的一个AlertController类型的变量,我们来看一下apply方法的具体实现:
ublic void apply(AlertController dialog) {
if (mCustomTitleView != null) {
dialog.setCustomTitle(mCustomTitleView);
} else {
if (mTitle != null) {
dialog.setTitle(mTitle);
}
if (mIcon != null) {
dialog.setIcon(mIcon);
}
if (mIconId != 0) {
dialog.setIcon(mIconId);
}
if (mIconAttrId != 0) {
dialog.setIcon(dialog.getIconAttributeResId(mIconAttrId));
}
}
if (mMessage != null) {
dialog.setMessage(mMessage);
}
if (mPositiveButtonText != null) {
dialog.setButton(DialogInterface.BUTTON_POSITIVE, mPositiveButtonText,
mPositiveButtonListener, null);
}
if (mNegativeButtonText != null) {
dialog.setButton(DialogInterface.BUTTON_NEGATIVE, mNegativeButtonText,
mNegativeButtonListener, null);
}
if (mNeutralButtonText != null) {
dialog.setButton(DialogInterface.BUTTON_NEUTRAL, mNeutralButtonText,
mNeutralButtonListener, null);
}
if (mForceInverseBackground) {
dialog.setInverseBackgroundForced(true);
}
// For a list, the client can either supply an array of items or an
// adapter or a cursor
if ((mItems != null) || (mCursor != null) || (mAdapter != null)) {
createListView(dialog);
}
if (mView != null) {
if (mViewSpacingSpecified) {
dialog.setView(mView, mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight,
mViewSpacingBottom);
} else {
dialog.setView(mView);
}
} else if (mViewLayoutResId != 0) {
dialog.setView(mViewLayoutResId);
}
}
看到了么?就是我们在初始化AlertDialog.Builder的时候设置的icon、title、message赋值给了AlertController.AlertParams,这里就是将我们初始化时候设置的属性值赋值给我们创建的Dialog对象的mAlert成员变量。。。。
继续我们的AlertDialog.Builder.create方法,在执行了AlertController.AlertParams.apply方法之后又调用了:
dialog.setCancelable(P.mCancelable)
可以发现这个也是AertController.AlertParams的一个成员变量,我们在初始化AlertDialog.Builder的时候也可以通过设置builder.setCancelable赋值,由于该属性为成员变量,所以默认值为false,而我们并没有通过builder.setCancelable修改这个属性值,所以这里设置的dialog的cancelable的值为false。然后我们的create方法有设置了dialog的cancelListener和dismissListener并返回了我们创建的Dialog对象。这样我们就获取到了我们的Dialog对象,然后我们调用了dialog的show方法用于显示dialog,好吧,这里我们看一下show方法的具体实现:
public void show() {
if (mShowing) {
if (mDecor != null) {
if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR)
}
mDecor.setVisibility(View.VISIBLE)
}
return
}
mCanceled = false
if (!mCreated) {
dispatchOnCreate(null)
}
onStart()
mDecor = mWindow.getDecorView()
if (mActionBar == null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
final ApplicationInfo info = mContext.getApplicationInfo()
mWindow.setDefaultIcon(info.icon)
mWindow.setDefaultLogo(info.logo)
mActionBar = new WindowDecorActionBar(this)
}
WindowManager.LayoutParams l = mWindow.getAttributes()
if ((l.softInputMode
& WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {
WindowManager.LayoutParams nl = new WindowManager.LayoutParams()
nl.copyFrom(l)
nl.softInputMode |=
WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION
l = nl
}
try {
mWindowManager.addView(mDecor, l)
mShowing = true
sendShowMessage()
} finally {
}
}
方法体的内容比较多,我们慢慢看,由于一开始mShowing变量用于表示当前dialog是否正在显示,由于我们刚刚开始调用执行show方法,所以这里的mShowing变量的值为false,所以if分支的内容不会被执行,继续往下看:
if (!mCreated) {
dispatchOnCreate(null);
}
mCreated这个控制变量控制dispatchOnCreate方法只被执行一次,由于我们是第一次执行,所以这里会执行dispatchOnCreate方法,好吧,我们看一下dispatchOnCreate方法的执行逻辑:
void dispatchOnCreate(Bundle savedInstanceState) {
if (!mCreated) {
onCreate(savedInstanceState);
mCreated = true;
}
}
好吧,可以看到代码的执行逻辑很简单就是回调了Dialog的onCreate方法,那么onCreate方法内部又执行了那些逻辑呢?由于我们创建的是AlertDialog对象,该对象继承于Dialog,所以我们这时候需要看一下AlertDialog的onCreate方法的执行逻辑:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mAlert.installContent();
}
可以看到这里面除了调用super.onCreate方法之外就是调用了mAlert.installContent方法,而这里的super.onCreate方法就是调用的Dialog的onCreate方法,Dialog的onCreate方法只是一个空的实现逻辑,所以我们具体来看一下mAlert.installContent的实现逻辑。
public void installContent() {
mWindow.requestFeature(Window.FEATURE_NO_TITLE);
int contentView = selectContentView();
mWindow.setContentView(contentView);
setupView();
setupDecor();
}
可以看到这里实现Window窗口的页面设置布局初始化等操作,这里设置了mWindow对象为NO_TITLE,然后通过调用selectContentView设置Window对象的布局文件。
private int selectContentView() {
if (mButtonPanelSideLayout == 0) {
return mAlertDialogLayout;
}
if (mButtonPanelLayoutHint == AlertDialog.LAYOUT_HINT_SIDE) {
return mButtonPanelSideLayout;
}
return mAlertDialogLayout;
}
可以看到这里通过执行selectContentView方法返回布局文件的id值,这里的默认值是mAlertDialogLayout。看过Activity布局加载流程(android源码解析(十七)–>Activity布局加载流程)的童鞋应该知道,从这个方法开始我们就把指定布局文件的内容加载到内存中的Window对象中。我们这里看一下具体的布局文件。
mAlertDialogLayout = a.getResourceId(
R.styleable.AlertDialog_layout, R.layout.alert_dialog)
也就是R.layout.alert_dialog的布局文件,有兴趣的同学可以看一下该布局文件的源码,O(∩_∩)O哈哈~
继续回到我们的installContent方法,在执行了mWindow.setContentView方法之后,又调用了setupView方法和setupDector方法,这两个方法的主要作用就是初始化布局文件中的组件和Window对象中的mDector成员变量,这里就不在详细的说明。
然后回到我们的show方法,在执行了dispatchOnCreate方法之后我们又调用了onStart方法,这个方法主要用于设置ActionBar,这里不做过多的说明,然后初始化WindowManager.LayoutParams对象,并最终调用我们的mWindowManager.addView()方法。
O(∩_∩)O哈哈~,到了这一步大家如果看了上一篇Acitivty布局绘制流程的话,就应该知道顺着这个方法整个Dialog的界面就会被绘制出来了。
最后我们调用了sendShowMessage方法,可以看一下这个方法的实现:
private void sendShowMessage() {
if (mShowMessage != null) {
Message.obtain(mShowMessage).sendToTarget();
}
}
这里会发送一个Dialog已经显示的异步消息,该消息最终会在ListenersHandler中的handleMessage方法中被执行:
private static final class ListenersHandler extends Handler {
private WeakReference mDialog;
public ListenersHandler(Dialog dialog) {
mDialog = new WeakReference(dialog);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case DISMISS:
((OnDismissListener) msg.obj).onDismiss(mDialog.get());
break;
case CANCEL:
((OnCancelListener) msg.obj).onCancel(mDialog.get());
break;
case SHOW:
((OnShowListener) msg.obj).onShow(mDialog.get());
break;
}
}
}
由于我们的msg.what = SHOW,所以会执行OnShowListener.onShow方法,那么这个OnShowListener是何时赋值的呢?还记得我们构造AlertDialog.Builder么?
alertDialog.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
}
});
这样就为我们的AlertDialog.Builder设置了OnShowListener,可以看一下setOnShowListener方法的具体实现:
public void setOnShowListener(OnShowListener listener) {
if (listener != null) {
mShowMessage = mListenersHandler.obtainMessage(SHOW, listener);
} else {
mShowMessage = null;
}
}
这样就为我们的Dialog中的mListenersHandler构造了Message对象,并且当我们在Dialog中发送showMessage的时候被mListenersHandler所接收。。。。
注:
这里说一下我们平时开发中若创建的Dialog使用的Context对象不是Activity,就会报出:
Process: com.example.aaron.helloworld, PID: 11948 android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application
at android.view.ViewRootImpl.setView(ViewRootImpl.java:690)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:282)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:69)
at android.app.Dialog.show(Dialog.java:298)
at com.example.aaron.helloworld.MainActivity$1.onClick(MainActivity.java:59)
at android.view.View.performClick(View.java:4811)
at android.view.View$PerformClick.run(View.java:20136)
at android.os.Handler.handleCallback(Handler.java:815)
at android.os.Handler.dispatchMessage(Handler.java:104)
at android.os.Looper.loop(Looper.java:194)
at android.app.ActivityThread.main(ActivityThread.java:5552)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:964)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:759)
的异常,这是由于WindowManager.addView方法最终会调用ViewRootImpl.setView方法,而这时候会有mToken的检查,若我们传入的Context对象不是Activity,这时候的mToken为空,就会出现上述问题。。。
总结:
Dialog和Activity的显示逻辑是相似的都是内部管理这一个Window对象,用WIndow对象实现界面的加载与显示逻辑;
Dialog中的Window对象与Activity中的Window对象是相似的,都对应着一个WindowManager对象;
Dialog相关的几个类:Dialog,AlertDialog,AlertDialog.Builder,AlertController,AlertController.AlertParams,其中Dialog是窗口的父类,主要实现Window对象的初始化和一些共有逻辑,而AlertDialog是具体的Dialog的操作实现类,AlertDialog.Builder类是AlertDialog的内部类,主要用于构造AlertDialog,AlertController是AlertDialog的控制类,AlertController.AlertParams类是控制参数类;
构造显示Dialog的一般流程,构造AlertDialog.Builder,然后设置各种属性,最后调用AlertDialog.Builder.create方法获取AlertDialog对象,并且create方法中会执行,构造AlertDialog,设置dialog各种属性的操作。最后我们调用Dialog.show方法展示窗口,初始化Dialog的布局文件,Window对象等,然后执行mWindowManager.addView方法,开始执行绘制View的操作,并最终将Dialog显示出来;
另外对android源码解析方法感兴趣的可参考我的:
android源码解析之(一)–>android项目构建过程
android源码解析之(二)–>异步消息机制
android源码解析之(三)–>异步任务AsyncTask
android源码解析之(四)–>HandlerThread
android源码解析之(五)–>IntentService
android源码解析之(六)–>Log
android源码解析之(七)–>LruCache
android源码解析之(八)–>Zygote进程启动流程
android源码解析之(九)–>SystemServer进程启动流程
android源码解析之(十)–>Launcher启动流程
android源码解析之(十一)–>应用进程启动流程
android源码解析之(十二)–>系统启动并解析Manifest的流程
android源码解析之(十三)–>apk安装流程
android源码解析之(十四)–>Activity启动流程
android源码解析之(十五)–>Activity销毁流程
android源码解析(十六)–>应用进程Context创建流程
android源码解析(十七)–>Activity布局加载流程
android源码解析(十八)–>Activity布局绘制流程
本文以同步至github中:github.com/yipianfengy…,欢迎star和follow
转载请标明出处:一片枫叶的专栏
上几篇文章中我们分析了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方法执行过程中就执行了取消绘制的逻辑,这里我们先看一下我们的例子核心代码:
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方法的具体实现:
public void cancel() {
if (!mCanceled && mCancelMessage != null) {
mCanceled = true;
Message.obtain(mCancelMessage).sendToTarget();
}
dismiss();
}
可以看到方法体中,若当前Dialog没有取消,并且设置了取消message,则调用Message.obtain(mCancel).sendToTarget(),前面已经分析过这里的sendToTarget方法会回调我们注册的异步消息处理逻辑:
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的定义:
private static final class ListenersHandler extends Handler {
private WeakReference mDialog;
public ListenersHandler(Dialog dialog) {
mDialog = new WeakReference(dialog);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case DISMISS:
((OnDismissListener) msg.obj).onDismiss(mDialog.get());
break;
case CANCEL:
((OnCancelListener) msg.obj).onCancel(mDialog.get());
break;
case SHOW:
((OnShowListener) msg.obj).onShow(mDialog.get());
break;
}
}
}
好吧,这里调用的是设置的OnCancelListener的onCancel方法,也就是说我们调用dialog.cancel方法时首先会判断dialog是否调用了setOnCancelListener若设置了,则先调用OnCancelListener的onCancel方法,然后再次执行dismiss方法,若我们没有为Dialog.Builder设置OnCancelListener那么cancel方法和dismiss方法是等效的。
这样,我们来看一下dismiss方法的实现逻辑:
public void dismiss() {
if (Looper.myLooper() == mHandler.getLooper()) {
dismissDialog();
} else {
mHandler.post(mDismissAction);
}
}
可以看到,这里首先判断当前线程的Looper是否是主线程的Looper(由于mHandler是在主线程中创建的,所以mHandler.getLooper返回的是主线程中创建的Looper对象),若是的话,则直接执行dismissDialog()方法,否则的话,通过mHandler发送异步消息至主线程中,简单来说就是判断当前线程是否是主线程,若是主线程则执行dismissDialog方法否则发送异步消息,我们看一下mHandler对异步消息的处理机制,由于这里的mDismissAction是一个Runnable对象,所以这里直接看一下mDismissAction的定义:
private final Runnable mDismissAction = new Runnable() {
public void run() {
dismissDialog();
}
};
好吧,这里的异步消息最终也是调用的dismissDialog方法。。。。
所以无论我们执行的cancel方法还是dismiss方法,无论我们方法是在主线程执行还是子线程中执行,最终调用的都是dismissDialog方法,那么就看一下dismissDialog是怎么个执行逻辑。
void dismissDialog() {
if (mDecor == null || !mShowing) {
return;
}
if (mWindow.isDestroyed()) {
Log.e(TAG, "Tried to dismissDialog() but the Dialog's window was already destroyed!");
return;
}
try {
mWindowManager.removeViewImmediate(mDecor);
} finally {
if (mActionMode != null) {
mActionMode.finish();
}
mDecor = null;
mWindow.closeAllPanels();
onStop();
mShowing = false;
sendDismissMessage();
}
}
好吧,看样子代码还不是特别多,方法体中,首先判断当前的mDector是否为空,或者当前Dialog是否在显示,若为空或者没有在显示,则直接return掉,也就是说当前我们的dialog已经不再显示了,则我们不需要往下在执行。
然后我们调用了mWindow.isDestroyed()方法,判断Window对象是否已经被销毁,若已经被销毁,则直接return,并打印错误日志。
然后我们调用了mWindowManager.removeViewImmediate(mDector),这里的mDector是我们Dialog窗口的根布局,看这个方法的名字应该就是Dialog去除根布局的操作了,可以看一下这个方法的具体实现。前几篇文章中我们已经分析过了这里的mWindowManager其实是WindowManagerImpl的实例,所以这里的removeViewImmediate方法应该是WindowManagerImpl中的方法,我们看一下它的具体实现:
@Override
public void removeViewImmediate(View view) {
mGlobal.removeView(view, true);
}
可以发现,这里它调用了mGlobal.removeView方法,而这里的mGlobal是WindowManagerGlobal的实例,所以我们再看一下WIndowManagerGlobal中removeView的实现逻辑:
public void removeView(View view, boolean immediate) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
synchronized (mLock) {
int index = findViewLocked(view, true);
View curView = mRoots.get(index).getView();
removeViewLocked(index, immediate);
if (curView == view) {
return;
}
throw new IllegalStateException("Calling with view " + view
+ " but the ViewAncestor is attached to " + curView);
}
}
可以发现,这里在获取了保存的mDector组件之后,又调用了removeViewLocked方法,我们在看一下这个方法的具体实现逻辑:
private void removeViewLocked(int index, boolean immediate) {
ViewRootImpl root = mRoots.get(index);
View view = root.getView();
if (view != null) {
InputMethodManager imm = InputMethodManager.getInstance();
if (imm != null) {
imm.windowDismissed(mViews.get(index).getWindowToken());
}
}
boolean deferred = root.die(immediate);
if (view != null) {
view.assignParent(null);
if (deferred) {
mDyingViews.add(view);
}
}
}
看到了么,我们获取了mDector组件的ViewRootImpl,然后调用了其的die方法,通过这个方法实现Window组件的销毁流程。
boolean die(boolean immediate) {
// Make sure we do execute immediately if we are in the middle of a traversal or the damage
// done by dispatchDetachedFromWindow will cause havoc on return.
if (immediate && !mIsInTraversal) {
doDie();
return false;
}
if (!mIsDrawing) {
destroyHardwareRenderer();
} else {
Log.e(TAG, "Attempting to destroy the window while drawing!\n" +
" window=" + this + ", title=" + mWindowAttributes.getTitle());
}
mHandler.sendEmptyMessage(MSG_DIE);
return true;
}
可以看到在方法体中有调用了doDie方法,看名字应该就是真正执行window销毁工作的方法了,我们在看一下doDie方法的具体实现:
void doDie() {
checkThread();
if (LOCAL_LOGV) Log.v(TAG, "DIE in " + this + " of " + mSurface);
synchronized (this) {
if (mRemoved) {
return;
}
mRemoved = true;
if (mAdded) {
dispatchDetachedFromWindow();
}
if (mAdded && !mFirst) {
destroyHardwareRenderer();
if (mView != null) {
int viewVisibility = mView.getVisibility();
boolean viewVisibilityChanged = mViewVisibility != viewVisibility;
if (mWindowAttributesChanged || viewVisibilityChanged) {
try {
if ((relayoutWindow(mWindowAttributes, viewVisibility, false)
& WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
mWindowSession.finishDrawing(mWindow);
}
} catch (RemoteException e) {
}
}
mSurface.release();
}
}
mAdded = false;
}
WindowManagerGlobal.getInstance().doRemoveView(this);
}
可以看到方法体中,首先调用了checkThread方法,介绍Activity的绘制流程的时候有过介绍,判断当前执行代码的线程,若不是主线程,则抛出异常:
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
我们顺着doDie的方法往下看,又调用了dispatchDetachedFromWindow()方法,这个方法主要是销毁Window中的各中成员变量,临时变量等
void dispatchDetachedFromWindow() {
if (mView != null && mView.mAttachInfo != null) {
mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false);
mView.dispatchDetachedFromWindow();
}
mAccessibilityInteractionConnectionManager.ensureNoConnection();
mAccessibilityManager.removeAccessibilityStateChangeListener(
mAccessibilityInteractionConnectionManager);
mAccessibilityManager.removeHighTextContrastStateChangeListener(
mHighContrastTextManager);
removeSendWindowContentChangedCallback();
destroyHardwareRenderer();
setAccessibilityFocus(null, null);
mView.assignParent(null);
mView = null;
mAttachInfo.mRootView = null;
mSurface.release();
if (mInputQueueCallback != null && mInputQueue != null) {
mInputQueueCallback.onInputQueueDestroyed(mInputQueue);
mInputQueue.dispose();
mInputQueueCallback = null;
mInputQueue = null;
}
if (mInputEventReceiver != null) {
mInputEventReceiver.dispose();
mInputEventReceiver = null;
}
try {
mWindowSession.remove(mWindow);
} catch (RemoteException e) {
}
if (mInputChannel != null) {
mInputChannel.dispose();
mInputChannel = null;
}
mDisplayManager.unregisterDisplayListener(mDisplayListener);
unscheduleTraversals();
}
可以看到我们在方法中调用了mView.dispatchDetachedFromWindow方法,这个方法的作用就是将mView从Window中detach出来,我们可以看一下这个方法的具体实现:
void dispatchDetachedFromWindow() {
AttachInfo info = mAttachInfo;
if (info != null) {
int vis = info.mWindowVisibility;
if (vis != GONE) {
onWindowVisibilityChanged(GONE);
}
}
onDetachedFromWindow();
onDetachedFromWindowInternal();
InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null) {
imm.onViewDetachedFromWindow(this);
}
ListenerInfo li = mListenerInfo;
final CopyOnWriteArrayList listeners =
li != null ? li.mOnAttachStateChangeListeners : null;
if (listeners != null && listeners.size() > 0) {
for (OnAttachStateChangeListener listener : listeners) {
listener.onViewDetachedFromWindow(this);
}
}
if ((mPrivateFlags & PFLAG_SCROLL_CONTAINER_ADDED) != 0) {
mAttachInfo.mScrollContainers.remove(this);
mPrivateFlags &= ~PFLAG_SCROLL_CONTAINER_ADDED;
}
mAttachInfo = null;
if (mOverlay != null) {
mOverlay.getOverlayView().dispatchDetachedFromWindow();
}
}
其中onDetachedFromWindow方法是一个空的回调方法,这里我们重点看一下onDetachedFromWindowInternal方法:
protected void onDetachedFromWindowInternal() {
mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT;
mPrivateFlags3 &= ~PFLAG3_IS_LAID_OUT;
removeUnsetPressCallback();
removeLongPressCallback();
removePerformClickCallback();
removeSendViewScrolledAccessibilityEventCallback();
stopNestedScroll();
jumpDrawablesToCurrentState();
destroyDrawingCache();
cleanupDraw();
mCurrentAnimation = null;
}
onDetachedFromWindowInternal方法的方法体也不是特别长,都是一些调用函数,这里看一下destropDrawingCache方法,这个方法主要是销毁View的缓存Drawing,我们来看一下具体实现:
public void destroyDrawingCache() {
if (mDrawingCache != null) {
mDrawingCache.recycle();
mDrawingCache = null;
}
if (mUnscaledDrawingCache != null) {
mUnscaledDrawingCache.recycle();
mUnscaledDrawingCache = null;
}
}
这里的mDrawingCache其实就是一个Bitmap类型的成员变量,而这里调用的recycler和置空操作其实就是把View中执行draw方法之后缓存的bitmap清空。
这里需要说明的是,我们View组件的最终显示落实是通过draw方法实现绘制的,而我们的draw方法的参数是一个Canvas,这是一个画布的对象,通过draw方法就是操作这个对象并显示出来,而Canvas对象之所以能够实现显示的效果是因为其内部保存着一个Bitmap对象,通过操作Canvas对象实质上是操作Canvas对象内部的Bitmap对象,而View组件的显示也就是通过这里的Bitmap来实现的。
而我们上文中置空了bitmap对象就相当于把View组件的显示效果置空了,就是相当于我们取消了View的draw方法的执行效果,继续回到我们的dispatchDetachedFromWindow方法,在执行了mView.dispatchDetachedFromWindow()方法之后,又调用了mView = null;方法,这里设置mView为空,这样我们有取消了View的meature和layouot的执行效果。
这样经过一系列的操作之后我们的Dialog的取消绘制流程就结束了,现在我们来看一下Activity的取消绘制流程。还记得我们“Activity的销毁流程”么?可参考:android源码解析之(十五)–>Activity销毁流程
当我们调用activity的finish方法的时候回调用ActivityThread的handleDestroyActivity方法,我们来看一下这个方法的实现:
private void handleDestroyActivity(IBinder token, boolean finishing,
int configChanges, boolean getNonConfigInstance) {
...
wm.removeViewImmediate(v);
...
}
可以看到这里调用了这里调用了wm.removeViewImmediate方法,这个方法不就是我们刚刚分析Dialog销毁绘制流程的起始方法么?以后的逻辑都是详细的,这样我们就实现了Activity的取消绘制流程。
总结:
窗口的取消绘制流程是相似的,包括Activity和Dialog等;
通过调用WindowManager.removeViewImmediate方法,开始执行Window窗口的取消绘制流程;
Window窗口的取消绘制流程,通过清空bitma撤销draw的执行效果,通过置空View撤销meature和layout的执行效果;
另外对android源码解析方法感兴趣的可参考我的:
android源码解析之(一)–>android项目构建过程
android源码解析之(二)–>异步消息机制
android源码解析之(三)–>异步任务AsyncTask
android源码解析之(四)–>HandlerThread
android源码解析之(五)–>IntentService
android源码解析之(六)–>Log
android源码解析之(七)–>LruCache
android源码解析之(八)–>Zygote进程启动流程
android源码解析之(九)–>SystemServer进程启动流程
android源码解析之(十)–>Launcher启动流程
android源码解析之(十一)–>应用进程启动流程
android源码解析之(十二)–>系统启动并解析Manifest的流程
android源码解析之(十三)–>apk安装流程
android源码解析之(十四)–>Activity启动流程
android源码解析之(十五)–>Activity销毁流程
android源码解析(十六)–>应用进程Context创建流程
android源码解析(十七)–>Activity布局加载流程
android源码解析(十八)–>Activity布局绘制流程
android源码解析(十九)–>Dialog加载绘制流程
本文以同步至github中:github.com/yipianfengy…,欢迎star和follow
转载请标明出处:一片枫叶的专栏
在前面的几篇文章中我们分析了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的简单用法。
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的构造方法的实现:
public PopupWindow(View contentView, int width, int height) {
this(contentView, width, height, false);
}
可以看到这里调用了PopupWindow的重载构造方法,好吧,继续看一下这个重载构造方法的实现逻辑:
public PopupWindow(View contentView, int width, int height, boolean focusable) {
if (contentView != null) {
mContext = contentView.getContext();
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
}
setContentView(contentView);
setWidth(width);
setHeight(height);
setFocusable(focusable);
}
这里首先根据传入的View是否为空做了一下判断,若不为空,则初始化成员变量,Context和mWindowManager,可以发现这里的mContext对象就是传入的View组件中保留的Context对象,这里的mWindowManager是应用进程创建的时候注册的服务本地接口。然后调用了setContentView方法,这里就是为PopupWindow的contentView赋值。然后后面调用的setWidth、setHeight、setFocusable方法都是为PopupWindow的成员变量,width,height,focusable等赋值,这样PopupWindow的构造方法就执行完成了。
我们继续回到我们的例子代码中,在后续的代码中我们调用了:popupWindow.setBackgroundDrawable、popupWindow.setFocusable、PopupWindow.setOutsideTouchable、
PopupWindow.setAnimationStyle等方法,初始化了PopupWindow中的相关成员变量,最后我们调用了popupWindow.showAtLocation方法用于展示PopupWindow,这里我们具体看一下showAtLocation的实现逻辑:
public void showAtLocation(View parent, int gravity, int x, int y) {
showAtLocation(parent.getWindowToken(), gravity, x, y);
}
可以发现,这里调用了showAtLocation的重载函数,这样我们继续看一下这个重载函数的实现方式:
public void showAtLocation(IBinder token, int gravity, int x, int y) {
if (isShowing() || mContentView == null) {
return;
}
TransitionManager.endTransitions(mDecorView);
unregisterForScrollChanged();
mIsShowing = true;
mIsDropdown = false;
final WindowManager.LayoutParams p = createPopupLayoutParams(token);
preparePopup(p);
if (gravity != Gravity.NO_GRAVITY) {
p.gravity = gravity;
}
p.x = x;
p.y = y;
invokePopup(p);
}
可以看到通过调用createPopupLayoutParams方法创造了WindowManager.LayoutParams对象,然后又调用了preparePopup方法,可以看一下preparePopup方法的具体实现:
private void preparePopup(WindowManager.LayoutParams p) {
if (mContentView == null || mContext == null || mWindowManager == null) {
throw new IllegalStateException("You must specify a valid content view by "
+ "calling setContentView() before attempting to show the popup.");
}
if (mDecorView != null) {
mDecorView.cancelTransitions();
}
if (mBackground != null) {
mBackgroundView = createBackgroundView(mContentView);
mBackgroundView.setBackground(mBackground);
} else {
mBackgroundView = mContentView;
}
mDecorView = createDecorView(mBackgroundView);
mBackgroundView.setElevation(mElevation);
final int surfaceInset = (int) Math.ceil(mBackgroundView.getZ() * 2);
p.surfaceInsets.set(surfaceInset, surfaceInset, surfaceInset, surfaceInset);
p.hasManualSurfaceInsets = true;
mPopupViewInitialLayoutDirectionInherited =
(mContentView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);
mPopupWidth = p.width;
mPopupHeight = p.height;
}
preparePopup方法的参数是WindowManager.LayoutParams,然后设置了PopupWindow中的几个比较重要的成员变量,首先看一下mBackgroundView的初始化过程:
if (mBackground != null) {
mBackgroundView = createBackgroundView(mContentView);
mBackgroundView.setBackground(mBackground);
} else {
mBackgroundView = mContentView;
}
可以发现如果我们设置了mBackground变量也就是我们在初始化的时候执行了popupWindow的setBackgound方法,那么我们这里执行的就是if分之,这里看一下createBackgourndView的具体执行逻辑:
private PopupBackgroundView createBackgroundView(View contentView) {
final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
final int height;
if (layoutParams != null && layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
height = ViewGroup.LayoutParams.WRAP_CONTENT;
} else {
height = ViewGroup.LayoutParams.MATCH_PARENT;
}
final PopupBackgroundView backgroundView = new PopupBackgroundView(mContext);
final PopupBackgroundView.LayoutParams listParams = new PopupBackgroundView.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, height);
backgroundView.addView(contentView, listParams);
return backgroundView;
}
可以看到,createBackgroundView的执行逻辑就是在参数contentView的外面一层包裹一层PopupBackgroundView,而这里的PopupBackgroundView值我们自定义的FrameLayout的子类,重写了其onCreateDrawableState方法。
继续回到我们的preparePopup方法,这里我们又调用了createDecorView方法初始化mDectorView变量,我们可以看一下createDecorView的具体实现:
private PopupDecorView createDecorView(View contentView) {
final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
final int height;
if (layoutParams != null && layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
height = ViewGroup.LayoutParams.WRAP_CONTENT;
} else {
height = ViewGroup.LayoutParams.MATCH_PARENT;
}
final PopupDecorView decorView = new PopupDecorView(mContext);
decorView.addView(contentView, ViewGroup.LayoutParams.MATCH_PARENT, height);
decorView.setClipChildren(false);
decorView.setClipToPadding(false);
return decorView;
}
可以发现这里也是给参数contentView外面包裹了一层PopupDecorView,这里的PopupDecorView也是我们自定义的FrameLayout的子类,PopupDecorView的源码比较多,这里就不都贴出来了,这里具体看一下其onTouchEvent方法的实现:
@Override
public boolean onTouchEvent(MotionEvent event) {
final int x = (int) event.getX();
final int y = (int) event.getY();
if ((event.getAction() == MotionEvent.ACTION_DOWN)
&& ((x < 0) || (x >= getWidth()) || (y < 0) || (y >= getHeight()))) {
dismiss();
return true;
} else if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
dismiss();
return true;
} else {
return super.onTouchEvent(event);
}
}
可以发现其重写了onTouchEvent时间,这样我们在点击popupWindow外面的时候就会执行pupopWindow的dismiss方法,取消PopupWindow。
好吧,继续回到我们的showAsDropDown方法,在执行完成preparePopup方法之后又调用了invokePopup方法,这里的方法应该就是具体执行PopupWindow的加载与显示逻辑了。这里我们具体看一下其实现逻辑:
private void invokePopup(WindowManager.LayoutParams p) {
if (mContext != null) {
p.packageName = mContext.getPackageName();
}
final PopupDecorView decorView = mDecorView;
decorView.setFitsSystemWindows(mLayoutInsetDecor);
setLayoutDirectionFromAnchor();
mWindowManager.addView(decorView, p);
if (mEnterTransition != null) {
decorView.requestEnterTransition(mEnterTransition);
}
}
我们看到这里我们调用了mWindowManager.addView方法,看过我们前面几篇关于Dialog和Activity的加载与现实流程的同学应该知道这里的addView其实是我们布局绘制的流程,这里的mWindowManager是我们在调用PopupWIndow的构造函数的时候初始化的,其调用的是:
if (mWindowManager == null && mContentView != null) {
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
}
而这里的mContext.getSystemService是一个接口其具体的实现是在ContextImpl中实现的,所以这里我们看一下ContextImpl的getSystemService的实现:
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
好吧,在ContextImpl中的getSystemService方法又调用了SystemServiceRegister中的静态方法getSystemService,这样我们再看看一下在SystemServiceRegister是如何实现的。
public static Object getSystemService(ContextImpl ctx, String name) {
ServiceFetcher fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
return fetcher != null ? fetcher.getService(ctx) : null;
}
这里发现服务对象的获取就是通过一个SYSTEM_SERVICE_FETCHERS的map数据结构获取的,那么这个map对象的数据是何时填充的呢?通过查看源码我们发下在SystemServiceRegister中有一段静态代码主要用于注册本地服务接口,其中关于windowManagerService本地服务的代码如下:
registerService(Context.WINDOW_SERVICE, WindowManager.class,
new CachedServiceFetcher() {
@Override
public WindowManager createService(ContextImpl ctx) {
return new WindowManagerImpl(ctx.getDisplay());
}});
好吧,原来我们通过mContext.getSystemService获取的WindowManager其实际上是一个WindowManagerImpl对象,而我们调用的addView就是WindowManagerImpl的addView方法。
这样就回到了我们前几篇讲解的内容上了,通过调用WindowManagerImpl实现了布局文件的绘制流程。。。。
好了,经过上面的一系列的操作我们分析完了PopupWindow的加载绘制流程,其和Dialog,Activity的加载绘制流程类似,都是通过Window对象控制布局文件的加载与绘制流程。
总结:
PopupWindow的界面加载绘制流程也是通过Window对象实现的;
PopupWindow内部保存的mWindowManager对象通过ContextImpl中获取,并且取得的是WindowManagerImpl对象;
PopupWindow通过为传入的View添加一层包裹的布局,并重写该布局的点击事件,实现点击PopupWindow之外的区域PopupWindow消失的效果;
另外对android源码解析方法感兴趣的可参考我的:
android源码解析之(一)–>android项目构建过程
android源码解析之(二)–>异步消息机制
android源码解析之(三)–>异步任务AsyncTask
android源码解析之(四)–>HandlerThread
android源码解析之(五)–>IntentService
android源码解析之(六)–>Log
android源码解析之(七)–>LruCache
android源码解析之(八)–>Zygote进程启动流程
android源码解析之(九)–>SystemServer进程启动流程
android源码解析之(十)–>Launcher启动流程
android源码解析之(十一)–>应用进程启动流程
android源码解析之(十二)–>系统启动并解析Manifest的流程
android源码解析之(十三)–>apk安装流程
android源码解析之(十四)–>Activity启动流程
android源码解析之(十五)–>Activity销毁流程
android源码解析(十六)–>应用进程Context创建流程
android源码解析(十七)–>Activity布局加载流程
android源码解析(十八)–>Activity布局绘制流程
android源码解析(十九)–>Dialog加载绘制流程
android源码解析(二十)–>Dialog取消绘制流程
本文以同步至github中:github.com/yipianfengy…,欢迎star和follow
转载请标明出处:一片枫叶的专栏
前面我们分析了Activity、Dialog、PopupWindow的加载绘制流程,相信大家对整个Android系统中的窗口绘制流程已经有了一个比较清晰的认识了,这里最后再给大家介绍一下Toast的加载绘制流程。
其实Toast窗口和Activity、Dialog、PopupWindow有一个不太一样的地方,就是Toast窗口是属于系统级别的窗口,他和输入框等类似的,不属于某一个应用,即不属于某一个进程,所以自然而然的,一旦涉及到Toast的加载绘制流程就会涉及到进程间通讯,看过前面系列文章的同学应该知道,Android间的进程间通讯采用的是Android特有的Binder机制,所以Toast的加载绘制流程也会涉及到Binder进程间通讯。
Toast的显示流程其实内部还是通过Window的窗口机制实现加载绘制的,只不过由于是系统级别的窗口,在显示过程中涉及到了进程间通讯等机制。
下面我们来具体看一下Toast窗口的简单使用。
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show()
上面的代码是Toast的典型使用方式,通过makeText方法创建出一个Toast对象,然后调用show方法将Toast窗口显示出来。
下面我们来看一下makeText方法的具体实现:
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的构造方法做了哪些事。
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是什么东西呢?
private static class TN extends ITransientNotification.Stub
从类的源码定义来看,我们知道TN是一个继承自ITransientNotification.Stub的类,这里我们暂时只用知道他的继承关系就好了,知道其是一个Binder对象,可以用于进程间通讯,然后回到我们的makeText方法,在调用了Toast的构造方法创建了Toast对象之后,我们又通过context.getSystemService方法获取到LayoutInflater,然后通过调用LayoutInflater的inflate方法加载到了Toast的布局文件:
LayoutInflater inflate = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)
View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null)
这里我们可以看一下布局文件的具体代码:
可以发现Toast加载的布局文件只有一个LinearLayout布局,并且只包含一个TextView组件。。。。
然后我们通过调用:
TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message)
tv.setText(text)
result.mNextView = v
result.mDuration = duration
return result
初始化了布局文件,Toast的mNextView和mDuration成员变量并返回Toast类型的result对象。这样我们的Toast对象就构造完成了。
然后我们回到我们的Toast.show方法,调用完这个方法之后就准备开始显示Toast窗口了,我们来具体看一下show方法的具体实现:
public void show() {
if (mNextView == null) {
throw new RuntimeException("setView must have been called");
}
INotificationManager service = getService();
String pkg = mContext.getOpPackageName();
TN tn = mTN;
tn.mNextView = mNextView;
try {
service.enqueueToast(pkg, tn, mDuration);
} catch (RemoteException e) {
}
}
首先判断我们的mNextView是否为空,为空的话,显示逻辑就无法进行了,所以这里判断如果mNextView为空的话,就直接抛出异常,不在往下执行。。。。
然后我们执行了:
INotificationManager service = getService();
这里的INotificationManager是服务器端NotificationManagerService的Binder客户端,我们可以看一下getService方法的实现方式:
static private INotificationManager getService() {
if (sService != null) {
return sService;
}
sService = INotificationManager.Stub.asInterface(ServiceManager.getService("notification"));
return sService;
}
这里获取了INotificationManager对象,然后我们调用了service.enqueueToast方法,并传递了package,TN对象,duration等参数,这里实际执行的是NotificationManagerService的内部类的INotificationManager.Stub的enqueueToast方法,而我们的NoticationManagerService是在SystemServer进程中执行的,这里的底层其实是通过Binder机制传输数据的,具体的Binder机制相关知识可自行学习。。
好吧,我们在看一下INotificationManager.Stub的enqueueToast方法的具体实现:
@Override
public void enqueueToast(String pkg, ITransientNotification callback, int duration)
{
...
synchronized (mToastQueue) {
int callingPid = Binder.getCallingPid();
long callingId = Binder.clearCallingIdentity();
try {
ToastRecord record;
int index = indexOfToastLocked(pkg, callback);
if (index >= 0) {
record = mToastQueue.get(index);
record.update(duration);
} else {
if (!isSystemToast) {
int count = 0;
final int N = mToastQueue.size();
for (int i=0; i= MAX_PACKAGE_NOTIFICATIONS) {
Slog.e(TAG, "Package has already posted " + count
+ " toasts. Not showing more. Package=" + pkg);
return;
}
}
}
}
record = new ToastRecord(callingPid, pkg, callback, duration);
mToastQueue.add(record);
index = mToastQueue.size() - 1;
keepProcessAliveLocked(callingPid);
}
if (index == 0) {
showNextToastLocked();
}
} finally {
Binder.restoreCallingIdentity(callingId);
}
}
}
可以发现我们首先将我们的ToastRecord(Toast对象在server端的对象)保存到一个List列表mToastQueue中,然后调用了showNextToastLocked方法,这样我们在看一下showNextToastLocked方法的具体实现。
void showNextToastLocked() {
ToastRecord record = mToastQueue.get(0);
while (record != null) {
if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
try {
record.callback.show();
scheduleTimeoutLocked(record);
return;
} catch (RemoteException e) {
Slog.w(TAG, "Object died trying to show notification " + record.callback
+ " in package " + record.pkg);
int index = mToastQueue.indexOf(record);
if (index >= 0) {
mToastQueue.remove(index);
}
keepProcessAliveLocked(record.pid);
if (mToastQueue.size() > 0) {
record = mToastQueue.get(0);
} else {
record = null;
}
}
}
}
这里主要执行了record.callback.show方法,而这里的callback对象就是我们创建Toast对象的时候传递的TN对象,显然的,这了的show方法就是我们的Toast内部类TN的show方法,然后我们调用了scheduleTimeoutLocked方法,这里先看一下scheduleTimeoutLocked方法的实现。
private void scheduleTimeoutLocked(ToastRecord r)
{
mHandler.removeCallbacksAndMessages(r);
Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
mHandler.sendMessageDelayed(m, delay);
}
可以发现这里发送了一个异步消息,并且这里的异步消息是在duration时间之后发送的,也就是说我们在Toast端传递的duration参数就是这里的message消息delay发送的时间,而我们发送MESSAGE_TIMEOUT异步消息之后最终会被方法handleTimeout执行。
private void handleTimeout(ToastRecord record)
{
if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
synchronized (mToastQueue) {
int index = indexOfToastLocked(record.pkg, record.callback);
if (index >= 0) {
cancelToastLocked(index);
}
}
}
好吧,方法体里面又调用了cancelToastLocked方法,然后我们看一下cancelToastLocked方法的实现:
void cancelToastLocked(int index) {
ToastRecord record = mToastQueue.get(index);
try {
record.callback.hide();
} catch (RemoteException e) {
Slog.w(TAG, "Object died trying to hide notification " + record.callback
+ " in package " + record.pkg);
}
mToastQueue.remove(index);
keepProcessAliveLocked(record.pid);
if (mToastQueue.size() > 0) {
showNextToastLocked();
}
}
好吧,这里又是调用了record.callback.hide方法,显然的这里的hide方法和刚刚的show方法是相似的,都是调用的Toast内部类TN的hide方法,所以这里可以看出Toast的显示与隐藏操作都是在Toast内部类TN的show和hide方法实现的,然后我们调用了:
mToastQueue.remove(index);
清除这个Toast对象,并继续执行showNextToastLocked方法,直到mToastQueue的大小为0。。。
这样关于Toast窗口的显示与隐藏操作都是在Toast内部类TN的show方法和hide方法中,我们先看一下TN内部类的show方法的具体实现:
@Override
public void show() {
if (localLOGV) Log.v(TAG, "SHOW: " + this);
mHandler.post(mShow);
}
好吧,这里也是发送一个异步消息,我们看一下Runnable类型的mShow的定义。
final Runnable mShow = new Runnable() {
@Override
public void run() {
handleShow();
}
};
可以看到再其run方法中调用了handleShow方法,继续看handleShow方法的实现逻辑。
public void handleShow() {
if (localLOGV) Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView
+ " mNextView=" + mNextView)
if (mView != mNextView) {
// remove the old view if necessary
handleHide()
mView = mNextView
Context context = mView.getContext().getApplicationContext()
String packageName = mView.getContext().getOpPackageName()
if (context == null) {
context = mView.getContext()
}
mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE)
// We can resolve the Gravity here by using the Locale for getting
// the layout direction
final Configuration config = mView.getContext().getResources().getConfiguration()
final int gravity = Gravity.getAbsoluteGravity(mGravity, config.getLayoutDirection())
mParams.gravity = gravity
if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
mParams.horizontalWeight = 1.0f
}
if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
mParams.verticalWeight = 1.0f
}
mParams.x = mX
mParams.y = mY
mParams.verticalMargin = mVerticalMargin
mParams.horizontalMargin = mHorizontalMargin
mParams.packageName = packageName
if (mView.getParent() != null) {
if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this)
mWM.removeView(mView)
}
if (localLOGV) Log.v(TAG, "ADD! " + mView + " in " + this)
mWM.addView(mView, mParams)
trySendAccessibilityEvent()
}
}
好吧,在handleShow方法中经过一系列的初始化操作,初始化mWN对象,初始化mView对象,初始化了mParams对象,然后调用了mWM的addView方法,到了这里大家应该就很熟悉了(不熟悉的同学可以看一下Activity的加载绘制流程等文章
android源码解析(十八)–>Activity布局绘制流程
android源码解析(十七)–>Activity布局加载流程)通过这个方法就实现了Toast窗口的显示逻辑。
继续看一下TN的hide方法:
@Override
public void hide() {
if (localLOGV) Log.v(TAG, "HIDE: " + this);
mHandler.post(mHide);
}
好吧,和show方法类似,也是发送了一个异步消息,这里看一下Runnable类型的mHide对象的定义:
final Runnable mHide = new Runnable() {
@Override
public void run() {
handleHide();
mNextView = null;
}
};
可以发现在其run方法中调用了handleHide方法,显然的,与show方法类似,这里的handleHide方法也是执行Toast窗口销毁的逻辑:
public void handleHide() {
if (localLOGV) Log.v(TAG, "HANDLE HIDE: " + this + " mView=" + mView);
if (mView != null) {
if (mView.getParent() != null) {
if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);
mWM.removeView(mView);
}
mView = null;
}
}
可以发现,在方法体重调用了mWM.removeView(mView),又是熟悉的代码,通过执行这里的removeView方法,我们可以实现Toast窗口的销毁流程,至此我们就分析完了Toast窗口的显示与销毁流程。
总结:
Toast是一个系统窗口,Toast在显示与销毁流程设计到进程间通讯(Binder机制实现)
Toast的show方法首先会初始化一个Toast对象,然后将内部对象TN与duration传递给NotificationManagerService,并在NotificationManagerService端维护一个Toast对象列表。
NotificationManagerService接收到Toast的show请求之后,保存Toast对象并回调Toast.TN的show方法具体实现Toast窗口的显示逻辑。
Toast窗口的显示与销毁机制与Activity、Dialog、PopupWIndow都是类似的,都是通过WIndow对象实现的。
NotificationManagerService端在执行show方法执行会发送一个异步消息用于销毁Toast窗口,这个异步消息会在duration时间段之后发出,这样,在设置Toast显示的时间就会被传递到NotificationManagerService端,并在这段时间之后发送异步消息销毁Toast窗口。
另外对android源码解析方法感兴趣的可参考我的:
android源码解析之(一)–>android项目构建过程
android源码解析之(二)–>异步消息机制
android源码解析之(三)–>异步任务AsyncTask
android源码解析之(四)–>HandlerThread
android源码解析之(五)–>IntentService
android源码解析之(六)–>Log
android源码解析之(七)–>LruCache
android源码解析之(八)–>Zygote进程启动流程
android源码解析之(九)–>SystemServer进程启动流程
android源码解析之(十)–>Launcher启动流程
android源码解析之(十一)–>应用进程启动流程
android源码解析之(十二)–>系统启动并解析Manifest的流程
android源码解析之(十三)–>apk安装流程
android源码解析之(十四)–>Activity启动流程
android源码解析之(十五)–>Activity销毁流程
android源码解析(十六)–>应用进程Context创建流程
android源码解析(十七)–>Activity布局加载流程
android源码解析(十八)–>Activity布局绘制流程
android源码解析(十九)–>Dialog加载绘制流程
android源码解析(二十)–>Dialog取消绘制流程
android源码解析(二十一)–>PopupWindow加载绘制流程
本文以同步至github中:github.com/yipianfengy…,欢迎star和follow
转载请标明出处:一片枫叶的专栏
前面的几篇文章都是讲解的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方法的具体实现:
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方法的实现:
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的具体实现:
private static class UncaughtHandler implements Thread.UncaughtExceptionHandler {
public void uncaughtException(Thread t, Throwable e) {
try {
if (mCrashing) return;
mCrashing = true;
if (mApplicationObject == null) {
Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);
} else {
StringBuilder message = new StringBuilder();
message.append("FATAL EXCEPTION: ").append(t.getName()).append("\n");
final String processName = ActivityThread.currentProcessName();
if (processName != null) {
message.append("Process: ").append(processName).append(", ");
}
message.append("PID: ").append(Process.myPid());
Clog_e(TAG, message.toString(), e);
}
ActivityManagerNative.getDefault().handleApplicationCrash(
mApplicationObject, new ApplicationErrorReport.CrashInfo(e));
} catch (Throwable t2) {
try {
Clog_e(TAG, "Error reporting crash", t2);
} catch (Throwable t3) {
}
} finally {
Process.killProcess(Process.myPid());
System.exit(10);
}
}
}
这里uncaughtException方法最终会被执行异常信息的处理,我们看一下在这里我们调用了ActivityManagerNative.getDefault().handleApplicationCrash方法,看过我们前面Activity启动流程的童鞋应该知道这里的ActivityManagerNative其实是ActivityManagerService的Binder客户端,而这里的handleApplicationCrash方法最终会调用的是ActivityManagerService的handleApplicationCrash方法。最后在finally分之中,我们调用了Process.killProcess(Process.myPid)和System.exit(10),这样我们的应用进程就会退出了。
然后我们在这里先简单的分析一下Binder的数据传输过程,看一下handleApplicationCrash方法具体做了哪些事,首先看一下ActivityManagerNative的getDefault方法是如何实现的?
static public IActivityManager getDefault() {
return gDefault.get();
}
可以发现,其是一个静态方法,并执行了gDefault.get方法,我们在看一下gDefault.get方法的实现逻辑:
private static final Singleton gDefault = new Singleton() {
protected IActivityManager create() {
IBinder b = ServiceManager.getService("activity");
if (false) {
Log.v("ActivityManager", "default service binder = " + b);
}
IActivityManager am = asInterface(b);
if (false) {
Log.v("ActivityManager", "default service = " + am);
}
return am;
}
};
可以发现这里返回一个IActivityManager类型的am对象,而这个am对象是通过调用asInterface方法创建的,我们再来看一下这个asInterface方法的实现逻辑。
static public IActivityManager asInterface(IBinder obj) {
if (obj == null) {
return null;
}
IActivityManager in =
(IActivityManager)obj.queryLocalInterface(descriptor);
if (in != null) {
return in;
}
return new ActivityManagerProxy(obj);
}
可以发现该方法最终返回的是一个ActivityManagerProxy对象,所以ActivityManagerNative.getDefault()方法最终返回的是一个ActivityManagerProxy对象,我们再来看一下ActivityManagerProxy的handleApplicationCrash方法。
public void handleApplicationCrash(IBinder app,
ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(app);
crashInfo.writeToParcel(data, 0);
mRemote.transact(HANDLE_APPLICATION_CRASH_TRANSACTION, data, reply, 0);
reply.readException();
reply.recycle();
data.recycle();
}
这里就是具体的Binder传输数据的逻辑了,这里ActivityManagerNative最为Binder的clent端,而我们的ActivityManagerService同样是继承与ActivityManagerNative,最为Binder的server端,通过传输最终ActivityManagerService的handleApplicationCrash方法会被执行。
public void handleApplicationCrash(IBinder app, ApplicationErrorReport.CrashInfo crashInfo) {
ProcessRecord r = findAppProcess(app, "Crash");
final String processName = app == null ? "system_server"
: (r == null ? "unknown" : r.processName);
handleApplicationCrashInner("crash", r, processName, crashInfo);
}
可以看到在ActivityManagerService的handleApplicationCrash方法中我们调用了handleApplicationCreashInner方法,这样我们继续看一下handleApplicationCrashInner方法的实现。
void handleApplicationCrashInner(String eventType, ProcessRecord r, String processName,
ApplicationErrorReport.CrashInfo crashInfo) {
EventLog.writeEvent(EventLogTags.AM_CRASH, Binder.getCallingPid(),
UserHandle.getUserId(Binder.getCallingUid()), processName,
r == null ? -1 : r.info.flags,
crashInfo.exceptionClassName,
crashInfo.exceptionMessage,
crashInfo.throwFileName,
crashInfo.throwLineNumber)
addErrorToDropBox(eventType, r, processName, null, null, null, null, null, crashInfo)
crashApplication(r, crashInfo)
}
可以发现在handleApplicationCrashInner方法中主要调用了两个方法addErrorToDropBox和crashApplication,我们首先看一下addErrorToDropBox方法的实现逻辑。
public void addErrorToDropBox(String eventType,
ProcessRecord process, String processName, ActivityRecord activity,
ActivityRecord parent, String subject,
final String report, final File logFile,
final ApplicationErrorReport.CrashInfo crashInfo) {
final String dropboxTag = processClass(process) + "_" + eventType;
final DropBoxManager dbox = (DropBoxManager)
mContext.getSystemService(Context.DROPBOX_SERVICE);
if (dbox == null || !dbox.isTagEnabled(dropboxTag)) return;
final StringBuilder sb = new StringBuilder(1024);
appendDropBoxProcessHeaders(process, processName, sb);
if (activity != null) {
sb.append("Activity: ").append(activity.shortComponentName).append("\n");
}
if (parent != null && parent.app != null && parent.app.pid != process.pid) {
sb.append("Parent-Process: ").append(parent.app.processName).append("\n");
}
if (parent != null && parent != activity) {
sb.append("Parent-Activity: ").append(parent.shortComponentName).append("\n");
}
if (subject != null) {
sb.append("Subject: ").append(subject).append("\n");
}
sb.append("Build: ").append(Build.FINGERPRINT).append("\n");
if (Debug.isDebuggerConnected()) {
sb.append("Debugger: Connected\n");
}
sb.append("\n");
Thread worker = new Thread("Error dump: " + dropboxTag) {
@Override
public void run() {
if (report != null) {
sb.append(report);
}
if (logFile != null) {
try {
sb.append(FileUtils.readTextFile(logFile, DROPBOX_MAX_SIZE,
"\n\n[[TRUNCATED]]"));
} catch (IOException e) {
Slog.e(TAG, "Error reading " + logFile, e);
}
}
if (crashInfo != null && crashInfo.stackTrace != null) {
sb.append(crashInfo.stackTrace);
}
String setting = Settings.Global.ERROR_LOGCAT_PREFIX + dropboxTag;
int lines = Settings.Global.getInt(mContext.getContentResolver(), setting, 0);
if (lines > 0) {
sb.append("\n");
InputStreamReader input = null;
try {
java.lang.Process logcat = new ProcessBuilder("/system/bin/logcat",
"-v", "time", "-b", "events", "-b", "system", "-b", "main",
"-b", "crash",
"-t", String.valueOf(lines)).redirectErrorStream(true).start();
try { logcat.getOutputStream().close(); } catch (IOException e) {}
try { logcat.getErrorStream().close(); } catch (IOException e) {}
input = new InputStreamReader(logcat.getInputStream());
int num;
char[] buf = new char[8192];
while ((num = input.read(buf)) > 0) sb.append(buf, 0, num);
} catch (IOException e) {
Slog.e(TAG, "Error running logcat", e);
} finally {
if (input != null) try { input.close(); } catch (IOException e) {}
}
}
dbox.addText(dropboxTag, sb.toString());
}
};
if (process == null) {
worker.run();
} else {
worker.start();
}
}
可以看到方法体很长,但是逻辑比较简单,在方法体最后通过判断应用进程是否为空(是否被销毁)来执行worker.run方法或者是worker.start方法,这里的worker是一个Thread对象,而在我们的worker对象的run方法中主要的执行逻辑就是将崩溃信息写入系统log中,所以addErrorToDropBox方法的主要执行逻辑就是讲App的崩溃信息写入系统log中。。。。
继续回到我们的handleApplicationCrashInner方法中,看一下crashApplication方法是如何实现的。
private void crashApplication(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) {
long timeMillis = System.currentTimeMillis();
String shortMsg = crashInfo.exceptionClassName;
String longMsg = crashInfo.exceptionMessage;
String stackTrace = crashInfo.stackTrace;
if (shortMsg != null && longMsg != null) {
longMsg = shortMsg + ": " + longMsg;
} else if (shortMsg != null) {
longMsg = shortMsg;
}
AppErrorResult result = new AppErrorResult();
synchronized (this) {
if (mController != null) {
try {
String name = r != null ? r.processName : null;
int pid = r != null ? r.pid : Binder.getCallingPid();
int uid = r != null ? r.info.uid : Binder.getCallingUid();
if (!mController.appCrashed(name, pid,
shortMsg, longMsg, timeMillis, crashInfo.stackTrace)) {
if ("1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"))
&& "Native crash".equals(crashInfo.exceptionClassName)) {
Slog.w(TAG, "Skip killing native crashed app " + name
+ "(" + pid + ") during testing");
} else {
Slog.w(TAG, "Force-killing crashed app " + name
+ " at watcher's request");
if (r != null) {
r.kill("crash", true);
} else {
Process.killProcess(pid);
killProcessGroup(uid, pid);
}
}
return;
}
} catch (RemoteException e) {
mController = null;
Watchdog.getInstance().setActivityController(null);
}
}
final long origId = Binder.clearCallingIdentity();
if (r != null && r.instrumentationClass != null) {
Slog.w(TAG, "Error in app " + r.processName
+ " running instrumentation " + r.instrumentationClass + ":");
if (shortMsg != null) Slog.w(TAG, " " + shortMsg);
if (longMsg != null) Slog.w(TAG, " " + longMsg);
Bundle info = new Bundle();
info.putString("shortMsg", shortMsg);
info.putString("longMsg", longMsg);
finishInstrumentationLocked(r, Activity.RESULT_CANCELED, info);
Binder.restoreCallingIdentity(origId);
return;
}
if (r != null) {
mBatteryStatsService.noteProcessCrash(r.processName, r.uid);
}
if (r == null || !makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace)) {
Binder.restoreCallingIdentity(origId);
return;
}
Message msg = Message.obtain();
msg.what = SHOW_ERROR_MSG;
HashMap data = new HashMap();
data.put("result", result);
data.put("app", r);
msg.obj = data;
mUiHandler.sendMessage(msg);
Binder.restoreCallingIdentity(origId);
}
int res = result.get();
Intent appErrorIntent = null;
synchronized (this) {
if (r != null && !r.isolated) {
mProcessCrashTimes.put(r.info.processName, r.uid,
SystemClock.uptimeMillis());
}
if (res == AppErrorDialog.FORCE_QUIT_AND_REPORT) {
appErrorIntent = createAppErrorIntentLocked(r, timeMillis, crashInfo);
}
}
if (appErrorIntent != null) {
try {
mContext.startActivityAsUser(appErrorIntent, new UserHandle(r.userId));
} catch (ActivityNotFoundException e) {
Slog.w(TAG, "bug report receiver dissappeared", e);
}
}
}
可以发现在方法体中我们调用了mUiHandler.sendMessage(msg),其中mUiHandler是一个在主线程中创建的Handler对象,而这里的msg是一个what值为SHOW_ERROR_MSG的消息,这句话的本质就是向Ui线程中发送一个异步消息。我们来看一下mUiHander的处理逻辑。
在mUiHandler的handeMessage方法中,根据what值得不同,执行了如下逻辑:
case SHOW_ERROR_MSG: {
HashMap data = (HashMap) msg.obj;
boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
synchronized (ActivityManagerService.this) {
ProcessRecord proc = (ProcessRecord)data.get("app");
AppErrorResult res = (AppErrorResult) data.get("result");
if (proc != null && proc.crashDialog != null) {
Slog.e(TAG, "App already has crash dialog: " + proc);
if (res != null) {
res.set(0);
}
return;
}
boolean isBackground = (UserHandle.getAppId(proc.uid)
>= Process.FIRST_APPLICATION_UID
&& proc.pid != MY_PID);
for (int userId : mCurrentProfileIds) {
isBackground &= (proc.userId != userId);
}
if (isBackground && !showBackground) {
Slog.w(TAG, "Skipping crash dialog of " + proc + ": background");
if (res != null) {
res.set(0);
}
return;
}
if (mShowDialogs && !mSleeping && !mShuttingDown) {
Dialog d = new AppErrorDialog(mContext,
ActivityManagerService.this, res, proc);
d.show();
proc.crashDialog = d;
} else {
if (res != null) {
res.set(0);
}
}
}
ensureBootCompleted();
}
可以看到在方法体中我们创建了一个AppErrorDialog对象,并执行了show方法,这样该Dialog就会被显示出来。而这里的Dialog的显示内容就是:App already has crash dialog: ….
O(∩_∩)O哈哈~,原来我们App崩溃的时候弹出昂的异常提示框就是在这里弹出来的。这里对AppErrorDialog不做过多的介绍,在其的构造方法中,调用了如下的代码:
// After the timeout, pretend the user clicked the quit button
mHandler.sendMessageDelayed(
mHandler.obtainMessage(FORCE_QUIT),
DISMISS_TIMEOUT)
这里的常量DISMISS_TIME = 5 * 60 * 1000,也就是五分钟,相当于这里发送了一个延时异步消息五分钟之后取消崩溃弹窗的显示。所以我们的App若果崩溃之后不主动取消弹窗,崩溃弹窗也会默认在五分钟之后取消。
好吧,文章开头我们所提到的两个问题我们已经解决掉一个了,force close弹窗是如何弹出来的,相信大家已经有所了解了,其实第二个问题也已经说明了,我们知道系统默认的App异常处理流程就是从Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler());开始的,并创建了自己的UncaughtHandler对象,那么我们接管系统默认的异常处理逻辑其实也就是从Thread.setDefaultUncaughtExceptionHandler开始的,并重写其uncaughtException方法,那么App异常信息就会被我们自定义的UncaughtHandler所捕获,捕获之后奔溃信息的记录与上报就可以做定制了。。。
这样我们就大概分析完成了Android系统的异常处理流程。O(∩_∩)O哈哈~
总结:
App应用进程启动时会经过一系列的调用,执行Thread.setDefaultUncaughtExceptionHandler方法,创建默认的UncaughtHandler异常处理对象。
默认的UncaughtHandler异常处理对象,在其回调方法uncaughtException方法中会执行弹窗异常弹窗的操作,这也就是我们原生的force close弹窗,并且弹窗如果不主动取消的话,会在五分钟内默认取消。
自定义App的全局异常处理逻辑,需要接管UncaughtHandler,也就是创建自身的UncaughtHandler对象,并调用Thread.setDefaultUncaughtExceptionHandler方法,接管默认的异常处理逻辑。
force close弹窗,弹窗的时候App应用可能已经退出,该弹窗的弹窗是SystemServer进程中的ActivityManagerService服务控制的。
另外对android源码解析方法感兴趣的可参考我的:
android源码解析之(一)–>android项目构建过程
android源码解析之(二)–>异步消息机制
android源码解析之(三)–>异步任务AsyncTask
android源码解析之(四)–>HandlerThread
android源码解析之(五)–>IntentService
android源码解析之(六)–>Log
android源码解析之(七)–>LruCache
android源码解析之(八)–>Zygote进程启动流程
android源码解析之(九)–>SystemServer进程启动流程
android源码解析之(十)–>Launcher启动流程
android源码解析之(十一)–>应用进程启动流程
android源码解析之(十二)–>系统启动并解析Manifest的流程
android源码解析之(十三)–>apk安装流程
android源码解析之(十四)–>Activity启动流程
android源码解析之(十五)–>Activity销毁流程
android源码解析(十六)–>应用进程Context创建流程
android源码解析(十七)–>Activity布局加载流程
android源码解析(十八)–>Activity布局绘制流程
android源码解析(十九)–>Dialog加载绘制流程
android源码解析(二十)–>Dialog取消绘制流程
android源码解析(二十一)–>PopupWindow加载绘制流程
android源码解析(二十二)–>Toast加载绘制流程
本文以同步至github中:github.com/yipianfengy…,欢迎star和follow
转载请标明出处:一片枫叶的专栏
我们已经分析过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的源码:
private void handlePauseActivity(IBinder token, boolean finished,
boolean userLeaving, int configChanges, boolean dontReport) {
ActivityClientRecord r = mActivities.get(token);
if (r != null) {
if (userLeaving) {
performUserLeavingActivity(r);
}
r.activity.mConfigChangeFlags |= configChanges;
performPauseActivity(token, finished, r.isPreHoneycomb());
if (r.isPreHoneycomb()) {
QueuedWork.waitToFinish();
}
if (!dontReport) {
try {
ActivityManagerNative.getDefault().activityPaused(token);
} catch (RemoteException ex) {
}
}
mSomeActivitiesChanged = true;
}
}
在方法体中我们除了执行一些其他的操作,然后在handlePauseActivity方法体中调用了performPauseActivity方法,这个方法就是具体执行回调pauseActivity操作的方法,既然这样我们在看一下performPauseActivity方法的实现:
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方法的重载方法:
final Bundle performPauseActivity(ActivityClientRecord r, boolean finished,
boolean saveState) {
...
if (!r.activity.mFinished && saveState) {
callCallActivityOnSaveInstanceState(r);
}
...
}
可以发现,这里调用了callCallActivityOnSaveInstanceState方法,看名称可以发现这里应该回调的是Activity的onSaveInstanceState方法,但是这里执行之前有一个条件判断,首先会判断这里的Activity是否被finish?应为这时候刚刚执行onPause方法所以这里的mFinished变量为false,所以判断执行callCallActivityOnSaveInstanceState方法只要需要通过saveState变量来判断了,而这里的saveState方法是performPauseActivity方法传递过来的。。。。好吧,我们来看一下调用performPauseActivity方法时saveState变量是如何赋值的。回到我们的handlePauseActivity方法,看一下performPauseActivity方法是如何调用的:
performPauseActivity(token, finished, r.isPreHoneycomb());
可以发现saveState boolean变量是通过r.isPreHoneycomb方法赋值的,这里我们看一下IsPreHoneycomb方法是如何实现的:
public boolean isPreHoneycomb() {
if (activity != null) {
return activity.getApplicationInfo().targetSdkVersion
< android.os.Build.VERSION_CODES.HONEYCOMB;
}
return false;
}
可以发现当我们的App设置的targetSdk版本号小于android versionCode 11也就是android3.0的时候返回为true,其他的时候返回为false,也就是说当我们App设置的targetVersion大于android3.0的时候才会执行callCallActivityOnSaveInstanceState方法,好吧,继续看一下callCallActivityOnSaveInstanceState方法是如何实现的:
private void callCallActivityOnSaveInstanceState(ActivityClientRecord r) {
r.state = new Bundle()
r.state.setAllowFds(false)
if (r.isPersistable()) {
r.persistentState = new PersistableBundle()
mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state,
r.persistentState)
} else {
mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state)
}
}
可以发现方法体主要调用了mInstrumentation的callActivityOnSaveInstanceState方法,既然这样,我们再来看一下callActivityOnSaveInstanceState方法:
public void callActivityOnSaveInstanceState(Activity activity, Bundle outState,
PersistableBundle outPersistentState) {
activity.performSaveInstanceState(outState, outPersistentState);
}
这里方法体中又回调了Activity的performSaveInstanceState方法。。。
final void performSaveInstanceState(Bundle outState) {
onSaveInstanceState(outState);
saveManagedDialogs(outState);
mActivityTransitionState.saveState(outState);
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState);
}
可以看到这里回调了Activity的onSaveInstanceState方法,这样经过一系列的方法回调之后我们就执行了onSaveInstanceState方法。
这样我们当只执行onPause方法的时候一般通过设置targetVersion控制是否执行onSaveInstanceState方法,当设置的targetVersionCode大于android3.0的时候默认不会执行onSaveInstanceState方法。
然后我们看一下当Activity执行onStop方法的时候是否会执行onSaveInstanceState方法,通过之前分析的Activity的启动流程,我们知道Actvitiy执行onStop方法会回调ActivityThread的handleStopActivity,这样我们先看一下handleStopActivity方法的实现:
private void handleStopActivity(IBinder token, boolean show, int configChanges) {
ActivityClientRecord r = mActivities.get(token)
r.activity.mConfigChangeFlags |= configChanges
StopInfo info = new StopInfo()
performStopActivityInner(r, info, show, true)
if (localLOGV) Slog.v(
TAG, "Finishing stop of " + r + ": show=" + show
+ " win=" + r.window)
updateVisibility(r, show)
info.activity = r
info.state = r.state
info.persistentState = r.persistentState
mH.post(info)
mSomeActivitiesChanged = true
}
然后我们发现在方法performStopActivity方法中调用了performStopActivityInner方法,我们继续看一下performStopActivityInner方法的实现:
private void performStopActivityInner(ActivityClientRecord r,
StopInfo info, boolean keepShown, boolean saveState) {
...
if (!r.activity.mFinished && saveState) {
if (r.state == null) {
callCallActivityOnSaveInstanceState(r);
}
}
...
}
可以发现还是通过saveState变量来控制是否调用onSaveInstanceState,而这里的saveState变量是在performStopActivityInner方法调用的时候传递的,回到我们的handleStopActivity方法中关于performStopActivityInner调用的代码:
performStopActivityInner(r, info, show, true);
好吧,这里直接传值为true,这样我们执行Activity的stop方法一定执行onSaveInstanceState方法。
onSaveInstanceState方法是Activity的生命周期方法,主要用于在Activity销毁时保存一些信息。
当Activity只执行onPause方法时(Activity a打开一个透明Activity b)这时候如果App设置的targetVersion大于android3.0则不会执行onSaveInstanceState方法。
当Activity执行onStop方法时,通过分析源码我们知道调用onSaveInstanceState的方法直接传值为true,所以都会执行onSaveInstanceState方法。
另外对android源码解析方法感兴趣的可参考我的:
android源码解析之(一)–>android项目构建过程
android源码解析之(二)–>异步消息机制
android源码解析之(三)–>异步任务AsyncTask
android源码解析之(四)–>HandlerThread
android源码解析之(五)–>IntentService
android源码解析之(六)–>Log
android源码解析之(七)–>LruCache
android源码解析之(八)–>Zygote进程启动流程
android源码解析之(九)–>SystemServer进程启动流程
android源码解析之(十)–>Launcher启动流程
android源码解析之(十一)–>应用进程启动流程
android源码解析之(十二)–>系统启动并解析Manifest的流程
android源码解析之(十三)–>apk安装流程
android源码解析之(十四)–>Activity启动流程
android源码解析之(十五)–>Activity销毁流程
android源码解析(十六)–>应用进程Context创建流程
android源码解析(十七)–>Activity布局加载流程
android源码解析(十八)–>Activity布局绘制流程
android源码解析(十九)–>Dialog加载绘制流程
android源码解析(二十)–>Dialog取消绘制流程
android源码解析(二十一)–>PopupWindow加载绘制流程
android源码解析(二十二)–>Toast加载绘制流程
android源码解析(二十三)–>Android异常处理流程
本文以同步至github中:github.com/yipianfengy…,欢迎star和follow
转载请标明出处:一片枫叶的专栏
上篇文章中我们分析了Activity的onSaveInstanceState方法执行时机,知道了Activity在一般情况下,若只是执行onPause方法则不会执行onSaveInstanceState方法,而一旦执行了onStop方法就会执行onSaveInstanceState方法,具体的信息,可以参见onSaveInstanceState方法执行时机:android源码解析(二十四)–>onSaveInstanceState执行时机 这篇文章中同样的我们分析一下Actvity(当然不只是Activity,同样包含Servier,ContentProvider,Application等)的另一个内部方法:onLowMemory。该方法主要用于当前系统可用内存比较低的时候回调使用。
这里简单介绍一下Android系统的内存分配机制。Android系统中一个个的App都是一个个不同的应用进程,拥有各自的JVM与运行时,每个App的进程可使用的内存大小都是固定的,当系统中App打开数量过多时,就会使Android系统的可用内存降低,对于当前正在使用的App而言,可能还需要继续申请系统内存,而我们的剩余系统内存已经不足以被当前App所申请了,这时候系统会自动的清理那些后台进程,进而释放出可用内存用于前台进程的使用,当然这里系统清理后台进程的算法不是我们讨论的重点。这里我们只是大概的分析Android系统回调Activity的onLowMemory方法的流程。
通过前面关于Activity的启动流程分析我们知道ActivityManagerService是整个Android系统的管理中枢,负责Activity,Servier等四大组件的启动与销毁等工作,同样的对于应用进程的管理工作也是在ActivityMaangerServier中完成的,我们知道android系统中有两个比较重要的进程Zygote进程和SystemServer进程,其中Zygote进程是整个Android系统的根进程,其他所有的进程都是通过Zygote进程fork出来的。而SystemServer进程则用于运行各种服务,为其他的应用进程提供各种功能接口等,在前面我们分析过SystemServer进程的启动流程(参考: android源码解析之(九)–>SystemServer进程启动流程)其中在SystemServer的startBootService方法中我们调用了:
// Set up the Application instance for the system process and get started.
mActivityManagerService.setSystemProcess();
方法,看其注释说明,说的是为System进程初始化Application实例,这里我们可以看一下该方法的具体实现:
public void setSystemProcess() {
try {
ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true);
ServiceManager.addService(ProcessStats.SERVICE_NAME, mProcessStats);
ServiceManager.addService("meminfo", new MemBinder(this));
ServiceManager.addService("gfxinfo", new GraphicsBinder(this));
ServiceManager.addService("dbinfo", new DbBinder(this));
if (MONITOR_CPU_USAGE) {
ServiceManager.addService("cpuinfo", new CpuBinder(this));
}
ServiceManager.addService("permission", new PermissionController(this));
ServiceManager.addService("processinfo", new ProcessInfoService(this));
ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(
"android", STOCK_PM_FLAGS);
mSystemThread.installSystemApplicationInfo(info, getClass().getClassLoader());
synchronized (this) {
ProcessRecord app = newProcessRecordLocked(info, info.processName, false, 0);
app.persistent = true;
app.pid = MY_PID;
app.maxAdj = ProcessList.SYSTEM_ADJ;
app.makeActive(mSystemThread.getApplicationThread(), mProcessStats);
synchronized (mPidsSelfLocked) {
mPidsSelfLocked.put(app.pid, app);
}
updateLruProcessLocked(app, false, null);
updateOomAdjLocked();
}
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException(
"Unable to find android system package", e);
}
}
这里简单介绍一下ServierManager是一个管理服务的服务,而其addServier方法就是注册各种服务(服务注册到JNI层,具体的关于是如何注册到JNI层的这里暂不做过多的解释)。可以发现在方法体中我们注册了名称为:memInfo的服务MemBinder,MemBinder是一个Binder类型的服务,主要用于检测系统内存情况,这里可以看一下其具体的实现逻辑:
static class MemBinder extends Binder {
ActivityManagerService mActivityManagerService;
MemBinder(ActivityManagerService activityManagerService) {
mActivityManagerService = activityManagerService;
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mActivityManagerService.checkCallingPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
pw.println("Permission Denial: can't dump meminfo from from pid="
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+ " without permission " + android.Manifest.permission.DUMP);
return;
}
mActivityManagerService.dumpApplicationMemoryUsage(fd, pw, " ", args, false, null);
}
}
查看源码,我们可以发现MemBinder类继承于Binder类也就是说其实一个Binder类型的服务,并且有一个成员方法dump,该方法主要用于执行shell命令,当系统可用内存比较低的时候就会执行了该方法,然后回调到ActivityManagerService中的killAllBackground方法,下面我们重点看一下killAllBackground方法的具体实现:
@Override
public void killAllBackgroundProcesses() {
...
doLowMemReportIfNeededLocked(null);
...
} finally {
Binder.restoreCallingIdentity(callingId);
}
}
可以看到这个方法体中会执行doLowMemReportIfNeededLocked方法,该方法是做什么的呢?我们继续看一下doLowMemReportIfNeededLoced方法的实现:
final void doLowMemReportIfNeededLocked(ProcessRecord dyingProc) {
...
scheduleAppGcsLocked();
...
}
好吧,在这个方法中我们又调用了scheduleAppGcsLocked方法,这样我们就继续看一下scheduleAppGcsLocked方法的实现逻辑:
/**
* Schedule the execution of all pending app GCs.
*/
final void scheduleAppGcsLocked() {
mHandler.removeMessages(GC_BACKGROUND_PROCESSES_MSG);
if (mProcessesToGc.size() > 0) {
ProcessRecord proc = mProcessesToGc.get(0);
Message msg = mHandler.obtainMessage(GC_BACKGROUND_PROCESSES_MSG);
long when = proc.lastRequestedGc + GC_MIN_INTERVAL;
long now = SystemClock.uptimeMillis();
if (when < (now+GC_TIMEOUT)) {
when = now + GC_TIMEOUT;
}
mHandler.sendMessageAtTime(msg, when);
}
}
可以发现这里执行的逻辑就是通过mHandler发送一个msg.what为GC_BACKGROUND_PROCESSES_MSG的异步消息,这样消息体最终会被mHandler的handleMessage方法所执行,继续看一下mHandler的handleMessage方法的执行逻辑:
case GC_BACKGROUND_PROCESSES_MSG: {
synchronized (ActivityManagerService.this) {
performAppGcsIfAppropriateLocked();
}
} break;
在mHandler的handleMessage方法中,首先会判断msg的what是否为GC_BACKGROUND_PROCESSES_MSG,然后会执行performAppGcsIfAppropriateLocked方法,这样我们继续看一下performAppGcsIfAppropriateLocked方法的实现:
/**
* If all looks good, perform GCs on all processes waiting for them.
*/
final void performAppGcsIfAppropriateLocked() {
if (canGcNowLocked()) {
performAppGcsLocked();
return;
}
scheduleAppGcsLocked();
}
可以发现这里首先判断是否能够执行gc操作,若不能继续执行上面的scheduleAppGcsLocked方法,然后继续执行发送异步消息的逻辑,直到变量canGcNowLocked为true,并执行performAppGcsLocked方法,然后return掉,这样我们继续跟踪代码,看一下performAppGcsLocked方法的执行逻辑:
/**
* Perform GCs on all processes that are waiting for it, but only
* if things are idle.
*/
final void performAppGcsLocked() {
final int N = mProcessesToGc.size();
if (N <= 0) {
return;
}
if (canGcNowLocked()) {
while (mProcessesToGc.size() > 0) {
ProcessRecord proc = mProcessesToGc.remove(0);
if (proc.curRawAdj > ProcessList.PERCEPTIBLE_APP_ADJ || proc.reportLowMemory) {
if ((proc.lastRequestedGc+GC_MIN_INTERVAL)
<= SystemClock.uptimeMillis()) {
performAppGcLocked(proc);
scheduleAppGcsLocked();
return;
} else {
addProcessToGcListLocked(proc);
break;
}
}
}
scheduleAppGcsLocked();
}
}
可以发现该方法经过一系列的逻辑判断之后会执行performAppGcLocked方法,我们继续看一下该方法的实现:
/**
* Ask a given process to GC right now.
*/
final void performAppGcLocked(ProcessRecord app) {
try {
app.lastRequestedGc = SystemClock.uptimeMillis();
if (app.thread != null) {
if (app.reportLowMemory) {
app.reportLowMemory = false;
app.thread.scheduleLowMemory();
} else {
app.thread.processInBackground();
}
}
} catch (Exception e) {
}
}
可以发现最终执行的是app.thread.scheduleLowMemory方法,而这里的app.thread是ActivityThread.ApplicationThread对象,所以这里最终是通过Binder进程间通讯,执行的是ActivityThread.ApplicationThread的scheduleLowMemory方法,好吧让我们看一下ActivityThread.ApplicationThread的scheduleLowMemory
方法的实现逻辑…
@Override
public void scheduleLowMemory() {
sendMessage(H.LOW_MEMORY, null);
}
在ActivityThread中的scheduleLowMemory方法中并没有执行额外逻辑,而是直接调用了sendMessage方法,继续跟踪方法的执行:
private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
if (DEBUG_MESSAGES) Slog.v(
TAG, "SCHEDULE " + what + " " + mH.codeToString(what)
+ ": " + arg1 + " / " + obj);
Message msg = Message.obtain();
msg.what = what;
msg.obj = obj;
msg.arg1 = arg1;
msg.arg2 = arg2;
if (async) {
msg.setAsynchronous(true);
}
mH.sendMessage(msg);
}
可以发现在sendMessage方法中最终通过一个Handler类型的mH成员变量发送一个异步消息,这样异步消息最终会被mH的handleMessage方法执行。。。。,经过查看源代码我们知道在mH的handleMessage方法中最终调用的是handleLowMemory方法:
final void handleLowMemory() {
ArrayList callbacks = collectComponentCallbacks(true, null);
final int N = callbacks.size();
for (int i=0; i
可以发现这里通过遍历ComponentCallbacks2并执行了其onLowMemory方法,那么这里的ComponentCallBacks2是什么呢?这里我们查看一下collectComponentCallbacks方法的实现逻辑。
ArrayList collectComponentCallbacks(
boolean allActivities, Configuration newConfig) {
ArrayList callbacks
= new ArrayList();
synchronized (mResourcesManager) {
final int NAPP = mAllApplications.size();
for (int i=0; i android源码解析之(一)–>android项目构建过程
android源码解析之(二)–>异步消息机制
android源码解析之(三)–>异步任务AsyncTask
android源码解析之(四)–>HandlerThread
android源码解析之(五)–>IntentService
android源码解析之(六)–>Log
android源码解析之(七)–>LruCache
android源码解析之(八)–>Zygote进程启动流程
android源码解析之(九)–>SystemServer进程启动流程
android源码解析之(十)–>Launcher启动流程
android源码解析之(十一)–>应用进程启动流程
android源码解析之(十二)–>系统启动并解析Manifest的流程
android源码解析之(十三)–>apk安装流程
android源码解析之(十四)–>Activity启动流程
android源码解析之(十五)–>Activity销毁流程
android源码解析(十六)–>应用进程Context创建流程
android源码解析(十七)–>Activity布局加载流程
android源码解析(十八)–>Activity布局绘制流程
android源码解析(十九)–>Dialog加载绘制流程
android源码解析(二十)–>Dialog取消绘制流程
android源码解析(二十一)–>PopupWindow加载绘制流程
android源码解析(二十二)–>Toast加载绘制流程
android源码解析(二十三)–>Android异常处理流程
android源码解析(二十四)–>onSaveInstanceState执行时机
本文以同步至github中:github.com/yipianfengy…,欢迎star和follow
转载请标明出处:一片枫叶的专栏
今天这篇文章我们主要讲一下Android系统中的截屏事件处理流程。用过android系统手机的同学应该都知道,一般的android手机按下音量减少键和电源按键就会触发截屏事件(国内定制机做个修改的这里就不做考虑了)。那么这里的截屏事件是如何触发的呢?触发之后android系统是如何实现截屏操作的呢?带着这两个问题,开始我们的源码阅读流程。
我们知道这里的截屏事件是通过我们的按键操作触发的,所以这里就需要我们从android系统的按键触发模块开始看起,由于我们在不同的App页面,操作音量减少键和电源键都会触发系统的截屏处理,所以这里的按键触发逻辑应该是Android系统的全局按键处理逻辑。
在android系统中,由于我们的每一个Android界面都是一个Activity,而界面的显示都是通过Window对象实现的,每个Window对象实际上都是PhoneWindow的实例,而每个PhoneWindow对象都一个PhoneWindowManager对象,当我们在Activity界面执行按键操作的时候,在将按键的处理操作分发到App之前,首先会回调PhoneWindowManager中的dispatchUnhandledKey方法,该方法主要用于执行当前App处理按键之前的操作,我们具体看一下该方法的实现。
/** {@inheritDoc} */
@Override
public KeyEvent dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags) {
...
KeyEvent fallbackEvent = null;
if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
final KeyCharacterMap kcm = event.getKeyCharacterMap();
final int keyCode = event.getKeyCode();
final int metaState = event.getMetaState();
final boolean initialDown = event.getAction() == KeyEvent.ACTION_DOWN
&& event.getRepeatCount() == 0;
// Check for fallback actions specified by the key character map.
final FallbackAction fallbackAction;
if (initialDown) {
fallbackAction = kcm.getFallbackAction(keyCode, metaState);
} else {
fallbackAction = mFallbackActions.get(keyCode);
}
if (fallbackAction != null) {
...
final int flags = event.getFlags() | KeyEvent.FLAG_FALLBACK;
fallbackEvent = KeyEvent.obtain(
event.getDownTime(), event.getEventTime(),
event.getAction(), fallbackAction.keyCode,
event.getRepeatCount(), fallbackAction.metaState,
event.getDeviceId(), event.getScanCode(),
flags, event.getSource(), null);
if (!interceptFallback(win, fallbackEvent, policyFlags)) {
fallbackEvent.recycle();
fallbackEvent = null;
}
if (initialDown) {
mFallbackActions.put(keyCode, fallbackAction);
} else if (event.getAction() == KeyEvent.ACTION_UP) {
mFallbackActions.remove(keyCode);
fallbackAction.recycle();
}
}
}
...
return fallbackEvent;
}
这里我们关注一下方法体中调用的:interceptFallback方法,通过调用该方法将处理按键的操作下发到该方法中,我们继续看一下该方法的实现逻辑。
private boolean interceptFallback(WindowState win, KeyEvent fallbackEvent, int policyFlags) {
int actions = interceptKeyBeforeQueueing(fallbackEvent, policyFlags);
if ((actions & ACTION_PASS_TO_USER) != 0) {
long delayMillis = interceptKeyBeforeDispatching(
win, fallbackEvent, policyFlags);
if (delayMillis == 0) {
return true;
}
}
return false;
}
然后我们看到在interceptFallback方法中我们调用了interceptKeyBeforeQueueing方法,通过阅读我们我们知道该方法主要实现了对截屏按键的处理流程,这样我们继续看一下interceptKeyBeforeWueueing方法的处理:
@Override
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
if (!mSystemBooted) {
return 0;
}
...
switch (keyCode) {
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_MUTE: {
if (mUseTvRouting) {
result &= ~ACTION_PASS_TO_USER;
}
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
if (down) {
if (interactive && !mScreenshotChordVolumeDownKeyTriggered
&& (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
mScreenshotChordVolumeDownKeyTriggered = true;
mScreenshotChordVolumeDownKeyTime = event.getDownTime();
mScreenshotChordVolumeDownKeyConsumed = false;
cancelPendingPowerKeyAction();
interceptScreenshotChord();
}
} else {
mScreenshotChordVolumeDownKeyTriggered = false;
cancelPendingScreenshotChordAction();
}
}
...
return result;
}
可以发现这里首先判断当前系统是否已经boot完毕,若尚未启动完毕,则所有的按键操作都将失效,若启动完成,则执行后续的操作,这里我们只是关注音量减少按键和电源按键组合的处理事件。另外这里多说一句想安卓系统的HOME按键事件,MENU按键事件,进程列表按键事件等等都是在这里实现的,后续中我们会陆续介绍这方面的内容。
回到我们的interceptKeyBeforeQueueing方法,当我用按下音量减少按键的时候回进入到:case KeyEvent.KEYCODE_VOLUME_MUTE分支并执行相应的逻辑,然后同时判断用户是否按下了电源键,若同时按下了电源键,则执行:
if (interactive && !mScreenshotChordVolumeDownKeyTriggered
&& (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
mScreenshotChordVolumeDownKeyTriggered = true;
mScreenshotChordVolumeDownKeyTime = event.getDownTime();
mScreenshotChordVolumeDownKeyConsumed = false;
cancelPendingPowerKeyAction();
interceptScreenshotChord();
}
可以发现这里的interceptScreenshotChrod方法就是系统准备开始执行截屏操作的开始,我们继续看一下interceptcreenshotChord方法的实现。
private void interceptScreenshotChord() {
if (mScreenshotChordEnabled
&& mScreenshotChordVolumeDownKeyTriggered && mScreenshotChordPowerKeyTriggered
&& !mScreenshotChordVolumeUpKeyTriggered) {
final long now = SystemClock.uptimeMillis();
if (now <= mScreenshotChordVolumeDownKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS
&& now <= mScreenshotChordPowerKeyTime
+ SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS) {
mScreenshotChordVolumeDownKeyConsumed = true;
cancelPendingPowerKeyAction();
mHandler.postDelayed(mScreenshotRunnable, getScreenshotChordLongPressDelay());
}
}
}
在方法体中我们最终会执行发送一个延迟的异步消息,请求执行截屏的操作而这里的延时时间,若当前输入框是打开状态,则延时时间为输入框关闭时间加上系统配置的按键超时时间,若当前输入框没有打开则直接是系统配置的按键超时处理时间,可看一下getScreenshotChordLongPressDelay方法的具体实现。
private long getScreenshotChordLongPressDelay() {
if (mKeyguardDelegate.isShowing()) {
return (long) (KEYGUARD_SCREENSHOT_CHORD_DELAY_MULTIPLIER *
ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
}
return ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout();
}
回到我们的interceptScreenshotChord方法,发送了异步消息之后系统最终会被我们发送的Runnable对象的run方法执行,这里关于异步消息的逻辑可参考:android源码解析之(二)–>异步消息机制
这样我们看一下Runnable类型的mScreenshotRunnable的run方法的实现:
private final Runnable mScreenshotRunnable = new Runnable() {
@Override
public void run() {
takeScreenshot();
}
};
好吧,方法体中并未执行其他操作,直接就是调用了takeScreenshot方法,这样我们继续看一下takeScreenshot方法的实现。
private void takeScreenshot() {
synchronized (mScreenshotLock) {
if (mScreenshotConnection != null) {
return;
}
ComponentName cn = new ComponentName("com.android.systemui",
"com.android.systemui.screenshot.TakeScreenshotService");
Intent intent = new Intent();
intent.setComponent(cn);
ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized (mScreenshotLock) {
if (mScreenshotConnection != this) {
return;
}
Messenger messenger = new Messenger(service);
Message msg = Message.obtain(null, 1);
final ServiceConnection myConn = this;
Handler h = new Handler(mHandler.getLooper()) {
@Override
public void handleMessage(Message msg) {
synchronized (mScreenshotLock) {
if (mScreenshotConnection == myConn) {
mContext.unbindService(mScreenshotConnection);
mScreenshotConnection = null;
mHandler.removeCallbacks(mScreenshotTimeout);
}
}
}
};
msg.replyTo = new Messenger(h);
msg.arg1 = msg.arg2 = 0;
if (mStatusBar != null && mStatusBar.isVisibleLw())
msg.arg1 = 1;
if (mNavigationBar != null && mNavigationBar.isVisibleLw())
msg.arg2 = 1;
try {
messenger.send(msg);
} catch (RemoteException e) {
}
}
}
@Override
public void onServiceDisconnected(ComponentName name) {}
};
if (mContext.bindServiceAsUser(
intent, conn, Context.BIND_AUTO_CREATE, UserHandle.CURRENT)) {
mScreenshotConnection = conn;
mHandler.postDelayed(mScreenshotTimeout, 10000);
}
}
}
可以发现这里通过反射机制创建了一个TakeScreenshotService对象然后调用了bindServiceAsUser,这样就创建了TakeScreenshotService服务并在服务创建之后发送了一个异步消息。好了,我们看一下TakeScreenshotService的实现逻辑。
public class TakeScreenshotService extends Service {
private static final String TAG = "TakeScreenshotService";
private static GlobalScreenshot mScreenshot;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
final Messenger callback = msg.replyTo;
if (mScreenshot == null) {
mScreenshot = new GlobalScreenshot(TakeScreenshotService.this);
}
mScreenshot.takeScreenshot(new Runnable() {
@Override public void run() {
Message reply = Message.obtain(null, 1);
try {
callback.send(reply);
} catch (RemoteException e) {
}
}
}, msg.arg1 > 0, msg.arg2 > 0);
}
}
};
@Override
public IBinder onBind(Intent intent) {
return new Messenger(mHandler).getBinder();
}
}
可以发现在在TakeScreenshotService类的定义中有一个Handler成员变量,而我们在启动TakeScreentshowService的时候回发送一个异步消息,这样就会执行mHandler的handleMessage方法,然后在handleMessage方法中我们创建了一个GlobalScreenshow对象,然后执行了takeScreenshot方法,好吧,继续看一下takeScreentshot方法的执行逻辑。
/**
* Takes a screenshot of the current display and shows an animation.
*/
void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible) {
mDisplay.getRealMetrics(mDisplayMetrics);
float[] dims = {mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels};
float degrees = getDegreesForRotation(mDisplay.getRotation());
boolean requiresRotation = (degrees > 0);
if (requiresRotation) {
mDisplayMatrix.reset();
mDisplayMatrix.preRotate(-degrees);
mDisplayMatrix.mapPoints(dims);
dims[0] = Math.abs(dims[0]);
dims[1] = Math.abs(dims[1]);
}
mScreenBitmap = SurfaceControl.screenshot((int) dims[0], (int) dims[1]);
if (mScreenBitmap == null) {
notifyScreenshotError(mContext, mNotificationManager);
finisher.run();
return;
}
if (requiresRotation) {
Bitmap ss = Bitmap.createBitmap(mDisplayMetrics.widthPixels,
mDisplayMetrics.heightPixels, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(ss);
c.translate(ss.getWidth() / 2, ss.getHeight() / 2);
c.rotate(degrees);
c.translate(-dims[0] / 2, -dims[1] / 2);
c.drawBitmap(mScreenBitmap, 0, 0, null);
c.setBitmap(null);
mScreenBitmap.recycle();
mScreenBitmap = ss;
}
mScreenBitmap.setHasAlpha(false);
mScreenBitmap.prepareToDraw();
startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
statusBarVisible, navBarVisible);
}
可以看到这里后两个参数:statusBarVisible,navBarVisible是否可见,而这两个参数在我们PhoneWindowManager.takeScreenshot方法传递的:
if (mStatusBar != null && mStatusBar.isVisibleLw())
msg.arg1 = 1;
if (mNavigationBar != null && mNavigationBar.isVisibleLw())
msg.arg2 = 1;
可见若果mStatusBar可见,则传递的statusBarVisible为true,若mNavigationBar可见,则传递的navBarVisible为true。然后我们在截屏的时候判断nStatusBar是否可见,mNavigationBar是否可见,若可见的时候则截屏同样将其截屏出来。继续回到我们的takeScreenshot方法,然后调用了:
mScreenBitmap = SurfaceControl.screenshot((int) dims[0], (int) dims[1]);
方法,看注释,这里就是执行截屏事件的具体操作了,然后我看一下SurfaceControl.screenshot方法的具体实现,另外这里需要注意的是,截屏之后返回的是一个Bitmap对象,其实熟悉android绘制机制的童鞋应该知道android中所有显示能够显示的东西,在内存中表现都是Bitmap对象。
public static Bitmap screenshot(int width, int height) {
IBinder displayToken = SurfaceControl.getBuiltInDisplay(
SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN);
return nativeScreenshot(displayToken, new Rect(), width, height, 0, 0, true,
false, Surface.ROTATION_0);
}
好吧,这里调用的是nativeScreenshot方法,它是一个native方法,具体的实现在JNI层,这里就不做过多的介绍了。继续回到我们的takeScreenshot方法,在调用了截屏方法screentshot之后,判断是否截屏成功:
if (mScreenBitmap == null) {
notifyScreenshotError(mContext, mNotificationManager);
finisher.run();
return;
}
若截屏之后,截屏的bitmap对象为空,这里判断截屏失败,调用了notifyScreenshotError方法,发送截屏失败的notification通知。
static void notifyScreenshotError(Context context, NotificationManager nManager) {
Resources r = context.getResources()
// Clear all existing notification, compose the new notification and show it
Notification.Builder b = new Notification.Builder(context)
.setTicker(r.getString(R.string.screenshot_failed_title))
.setContentTitle(r.getString(R.string.screenshot_failed_title))
.setContentText(r.getString(R.string.screenshot_failed_text))
.setSmallIcon(R.drawable.stat_notify_image_error)
.setWhen(System.currentTimeMillis())
.setVisibility(Notification.VISIBILITY_PUBLIC) // ok to show outside lockscreen
.setCategory(Notification.CATEGORY_ERROR)
.setAutoCancel(true)
.setColor(context.getColor(
com.android.internal.R.color.system_notification_accent_color))
Notification n =
new Notification.BigTextStyle(b)
.bigText(r.getString(R.string.screenshot_failed_text))
.build()
nManager.notify(R.id.notification_screenshot, n)
}
然后继续看takeScreenshot方法,判断截屏的图像是否需要旋转,若需要的话,则旋转图像:
if (requiresRotation) {
// Rotate the screenshot to the current orientation
Bitmap ss = Bitmap.createBitmap(mDisplayMetrics.widthPixels,
mDisplayMetrics.heightPixels, Bitmap.Config.ARGB_8888)
Canvas c = new Canvas(ss)
c.translate(ss.getWidth() / 2, ss.getHeight() / 2)
c.rotate(degrees)
c.translate(-dims[0] / 2, -dims[1] / 2)
c.drawBitmap(mScreenBitmap, 0, 0, null)
c.setBitmap(null)
// Recycle the previous bitmap
mScreenBitmap.recycle()
mScreenBitmap = ss
}
在takeScreenshot方法的最后若截屏成功,我们调用了:
// Start the post-screenshot animation
startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
statusBarVisible, navBarVisible);
开始截屏的动画,好吧,看一下动画效果的实现:
/**
* Starts the animation after taking the screenshot
*/
private void startAnimation(final Runnable finisher, int w, int h, boolean statusBarVisible,
boolean navBarVisible) {
mScreenshotView.setImageBitmap(mScreenBitmap);
mScreenshotLayout.requestFocus();
if (mScreenshotAnimation != null) {
mScreenshotAnimation.end();
mScreenshotAnimation.removeAllListeners();
}
mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
ValueAnimator screenshotDropInAnim = createScreenshotDropInAnimation();
ValueAnimator screenshotFadeOutAnim = createScreenshotDropOutAnimation(w, h,
statusBarVisible, navBarVisible);
mScreenshotAnimation = new AnimatorSet();
mScreenshotAnimation.playSequentially(screenshotDropInAnim, screenshotFadeOutAnim);
mScreenshotAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
saveScreenshotInWorkerThread(finisher);
mWindowManager.removeView(mScreenshotLayout);
mScreenBitmap = null;
mScreenshotView.setImageBitmap(null);
}
});
mScreenshotLayout.post(new Runnable() {
@Override
public void run() {
mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
mScreenshotView.buildLayer();
mScreenshotAnimation.start();
}
});
}
好吧,经过着一些列的操作之后我们实现了截屏之后的动画效果了,这里暂时不分析动画效果,我们看一下动画效果之后做了哪些?还记不记的一般情况下我们截屏之后都会收到一个截屏的notification通知?这里应该也是在其AnimatorListenerAdapter的onAnimationEnd方法中实现的,也就是动画执行完成之后,我们看一下其saveScreenshotInWorkerThread方法的实现:
/**
* Creates a new worker thread and saves the screenshot to the media store.
*/
private void saveScreenshotInWorkerThread(Runnable finisher) {
SaveImageInBackgroundData data = new SaveImageInBackgroundData();
data.context = mContext;
data.image = mScreenBitmap;
data.iconSize = mNotificationIconSize;
data.finisher = finisher;
data.previewWidth = mPreviewWidth;
data.previewheight = mPreviewHeight;
if (mSaveInBgTask != null) {
mSaveInBgTask.cancel(false);
}
mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data, mNotificationManager,
R.id.notification_screenshot).execute(data);
}
好吧,这里主要逻辑就是构造了一个SaveImageInBackgroundTask对象,看样子发送截屏成功的通知应该是在这里实现的,我们看一下SaveImageInBackgroundTask构造方法的实现逻辑:
SaveImageInBackgroundTask(Context context, SaveImageInBackgroundData data,
NotificationManager nManager, int nId) {
...
mTickerAddSpace = !mTickerAddSpace;
mNotificationId = nId;
mNotificationManager = nManager;
final long now = System.currentTimeMillis();
mNotificationBuilder = new Notification.Builder(context)
.setTicker(r.getString(R.string.screenshot_saving_ticker)
+ (mTickerAddSpace ? " " : ""))
.setContentTitle(r.getString(R.string.screenshot_saving_title))
.setContentText(r.getString(R.string.screenshot_saving_text))
.setSmallIcon(R.drawable.stat_notify_image)
.setWhen(now)
.setColor(r.getColor(com.android.internal.R.color.system_notification_accent_color));
mNotificationStyle = new Notification.BigPictureStyle()
.bigPicture(picture.createAshmemBitmap());
mNotificationBuilder.setStyle(mNotificationStyle);
mPublicNotificationBuilder = new Notification.Builder(context)
.setContentTitle(r.getString(R.string.screenshot_saving_title))
.setContentText(r.getString(R.string.screenshot_saving_text))
.setSmallIcon(R.drawable.stat_notify_image)
.setCategory(Notification.CATEGORY_PROGRESS)
.setWhen(now)
.setColor(r.getColor(
com.android.internal.R.color.system_notification_accent_color));
mNotificationBuilder.setPublicVersion(mPublicNotificationBuilder.build());
Notification n = mNotificationBuilder.build();
n.flags |= Notification.FLAG_NO_CLEAR;
mNotificationManager.notify(nId, n);
mNotificationBuilder.setLargeIcon(icon.createAshmemBitmap());
mNotificationStyle.bigLargeIcon((Bitmap) null);
}
可以发现在构造方法的后面狗仔了一个NotificationBuilder对象,然后发送了一个截屏成功的Notification,这样我们在截屏动画之后就收到了Notification的通知了。
总结:
在PhoneWindowManager的dispatchUnhandledKey方法中处理App无法处理的按键事件,当然也包括音量减少键和电源按键的组合按键
通过一系列的调用启动TakeScreenshotService服务,并通过其执行截屏的操作。
具体的截屏代码是在native层实现的。
截屏操作时候,若截屏失败则直接发送截屏失败的notification通知。
截屏之后,若截屏成功,则先执行截屏的动画,并在动画效果执行完毕之后,发送截屏成功的notification的通知。
另外对android源码解析方法感兴趣的可参考我的:
android源码解析之(一)–>android项目构建过程
android源码解析之(二)–>异步消息机制
android源码解析之(三)–>异步任务AsyncTask
android源码解析之(四)–>HandlerThread
android源码解析之(五)–>IntentService
android源码解析之(六)–>Log
android源码解析之(七)–>LruCache
android源码解析之(八)–>Zygote进程启动流程
android源码解析之(九)–>SystemServer进程启动流程
android源码解析之(十)–>Launcher启动流程
android源码解析之(十一)–>应用进程启动流程
android源码解析之(十二)–>系统启动并解析Manifest的流程
android源码解析之(十三)–>apk安装流程
android源码解析之(十四)–>Activity启动流程
android源码解析之(十五)–>Activity销毁流程
android源码解析(十六)–>应用进程Context创建流程
android源码解析(十七)–>Activity布局加载流程
android源码解析(十八)–>Activity布局绘制流程
android源码解析(十九)–>Dialog加载绘制流程
android源码解析(二十)–>Dialog取消绘制流程
android源码解析(二十一)–>PopupWindow加载绘制流程
android源码解析(二十二)–>Toast加载绘制流程
android源码解析(二十三)–>Android异常处理流程
android源码解析(二十四)–>onSaveInstanceState执行时机
android源码解析(二十五)–>onLowMemory执行流程
本文以同步至github中:github.com/yipianfengy…,欢迎star和follow
转载请标明出处:一片枫叶的专栏
上一篇文章中我们介绍了android系统的截屏事件,由于截屏事件是一种系统全局处理事件,所以事件的处理逻辑不是在App中执行,而是在PhoneWindowManager中执行。而本文我们现在主要讲解android系统中HOME按键的事件处理,和截屏事件类似,这里的HOME按键也是系统级别的按键事件监听,所以其处理事件的逻辑也应该和截屏事件处理流程类似,从上一篇文章的分析过冲中我们不难发现,系统级别的按键处理逻辑其实都是在PhoneWindowManager中,所以HOME按键的处理逻辑也是在PhoneWindowManager的dispatchUnhandledKey方法中执行,那么我们就从dispatchUnhandleKey方法开始分析HOME按键的处理流程。
好吧我们看一下PhoneWindowManager的dispatchUnhandleKey方法的实现:
@Override
public KeyEvent dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags) {
...
KeyEvent fallbackEvent = null;
if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
final KeyCharacterMap kcm = event.getKeyCharacterMap();
final int keyCode = event.getKeyCode();
final int metaState = event.getMetaState();
final boolean initialDown = event.getAction() == KeyEvent.ACTION_DOWN
&& event.getRepeatCount() == 0;
final FallbackAction fallbackAction;
if (initialDown) {
fallbackAction = kcm.getFallbackAction(keyCode, metaState);
} else {
fallbackAction = mFallbackActions.get(keyCode);
}
if (fallbackAction != null) {
if (DEBUG_INPUT) {
Slog.d(TAG, "Fallback: keyCode=" + fallbackAction.keyCode
+ " metaState=" + Integer.toHexString(fallbackAction.metaState));
}
final int flags = event.getFlags() | KeyEvent.FLAG_FALLBACK;
fallbackEvent = KeyEvent.obtain(
event.getDownTime(), event.getEventTime(),
event.getAction(), fallbackAction.keyCode,
event.getRepeatCount(), fallbackAction.metaState,
event.getDeviceId(), event.getScanCode(),
flags, event.getSource(), null);
if (!interceptFallback(win, fallbackEvent, policyFlags)) {
fallbackEvent.recycle();
fallbackEvent = null;
}
if (initialDown) {
mFallbackActions.put(keyCode, fallbackAction);
} else if (event.getAction() == KeyEvent.ACTION_UP) {
mFallbackActions.remove(keyCode);
fallbackAction.recycle();
}
}
}
if (DEBUG_INPUT) {
if (fallbackEvent == null) {
Slog.d(TAG, "No fallback.");
} else {
Slog.d(TAG, "Performing fallback: " + fallbackEvent);
}
}
return fallbackEvent;
}
通过查看源码,我们重点看一下dispatchUnhandledKey方法中调用的interceptFallback方法,关于HOME按键的处理逻辑也是在这个方法体中的,所以继续看一下interceptFallback方法的实现:
private boolean interceptFallback(WindowState win, KeyEvent fallbackEvent, int policyFlags) {
int actions = interceptKeyBeforeQueueing(fallbackEvent, policyFlags);
if ((actions & ACTION_PASS_TO_USER) != 0) {
long delayMillis = interceptKeyBeforeDispatching(
win, fallbackEvent, policyFlags);
if (delayMillis == 0) {
return true;
}
}
return false;
}
通过分析源码我们知道关于HOME按键的处理逻辑主要是在interceptKeyBeforeDispatching方法的实现的,既然这样,我们看一下interceptKeyBeforeDispatching方法的实现:
@Override
public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {
...
if (keyCode == KeyEvent.KEYCODE_HOME) {
if (!down) {
cancelPreloadRecentApps();
mHomePressed = false;
if (mHomeConsumed) {
mHomeConsumed = false;
return -1;
}
if (canceled) {
Log.i(TAG, "Ignoring HOME; event canceled.");
return -1;
}
TelecomManager telecomManager = getTelecommService();
if (telecomManager != null && telecomManager.isRinging()) {
Log.i(TAG, "Ignoring HOME; there's a ringing incoming call.");
return -1;
}
if (mDoubleTapOnHomeBehavior != DOUBLE_TAP_HOME_NOTHING) {
mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable);
mHomeDoubleTapPending = true;
mHandler.postDelayed(mHomeDoubleTapTimeoutRunnable,
ViewConfiguration.getDoubleTapTimeout());
return -1;
}
handleShortPressOnHome();
return -1;
}
WindowManager.LayoutParams attrs = win != null ? win.getAttrs() : null;
if (attrs != null) {
final int type = attrs.type;
if (type == WindowManager.LayoutParams.TYPE_KEYGUARD_SCRIM
|| type == WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG
|| (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
return 0;
}
final int typeCount = WINDOW_TYPES_WHERE_HOME_DOESNT_WORK.length;
for (int i=0; i
这里我们主要看一下对android系统HOME按键的处理逻辑,通过分析源码我们知道HOME按键进入launcher界面的主要逻辑是在handleShortPressOnHome();方法中执行的,所以我们继续看一下handleShortPressOnHome方法的实现。
private void handleShortPressOnHome() {
getHdmiControl().turnOnTv();
if (mDreamManagerInternal != null && mDreamManagerInternal.isDreaming()) {
mDreamManagerInternal.stopDream(false );
return;
}
launchHomeFromHotKey();
}
可以看到在handleShortPressOnHome方法中调用了launchHomeFromHotKey方法,该方法的注释用于go home,所以继续看一下该方法的实现:
void launchHomeFromHotKey() {
launchHomeFromHotKey(true , true );
}
可以看到在launchHomeFromHotKey方法中我们又调用了launchHomeFromHotkey的重构方法,这样我们看一下这个重构方法的实现。
void launchHomeFromHotKey(final boolean awakenFromDreams, final boolean respectKeyguard) {
if (respectKeyguard) {
if (isKeyguardShowingAndNotOccluded()) {
return;
}
if (!mHideLockScreen && mKeyguardDelegate.isInputRestricted()) {
mKeyguardDelegate.verifyUnlock(new OnKeyguardExitResult() {
@Override
public void onKeyguardExitResult(boolean success) {
if (success) {
try {
ActivityManagerNative.getDefault().stopAppSwitches();
} catch (RemoteException e) {
}
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
startDockOrHome(true , awakenFromDreams);
}
}
});
return;
}
}
try {
ActivityManagerNative.getDefault().stopAppSwitches();
} catch (RemoteException e) {
}
if (mRecentsVisible) {
if (awakenFromDreams) {
awakenDreams();
}
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
hideRecentApps(false, true);
} else {
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
startDockOrHome(true , awakenFromDreams);
}
}
可以发现在方法中我们首先调用了ActivityManagerNative.getDefault().stopAppSwitches();该方法主要用于暂停后台的打开Activity的操作,避免打扰用户的操作。比如这时候我们在后台打开一个新的App,那么这时候由于要回到home页面,所以需要先延时打开。方法执行这个方法之后然后执行了sendCloseSystemWindows方法,该方法主要实现了对当前系统App页面的关闭操作,下面我们先看一下ActivityManagerNative.getDefault().stopAppSwitches();方法的实现,这里的ActivityManagerNative.getDefault我们在前面已经多次说过了其是一个Binder对象,是应用进程Binder客户端用于与ActivityManagerService之间通讯,所以这里最终调用的是ActivityManagerService的stopAppsSwitches方法,这样我们就继续看一下ActivityManagerService的stopAppsSwitches方法的实现。
@Override
public void stopAppSwitches() {
if (checkCallingPermission(android.Manifest.permission.STOP_APP_SWITCHES)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires permission "
+ android.Manifest.permission.STOP_APP_SWITCHES);
}
synchronized(this) {
mAppSwitchesAllowedTime = SystemClock.uptimeMillis()
+ APP_SWITCH_DELAY_TIME;
mDidAppSwitch = false;
mHandler.removeMessages(DO_PENDING_ACTIVITY_LAUNCHES_MSG);
Message msg = mHandler.obtainMessage(DO_PENDING_ACTIVITY_LAUNCHES_MSG);
mHandler.sendMessageDelayed(msg, APP_SWITCH_DELAY_TIME);
}
}
可以发现这里主要是发送了一个异步消息,并且msg.what为DO_PENDING_ACTIVITY_LAUNCHES_MSG,即跳转Activity,然后我们继续我们看一下mHandler的handleMessage方法当msg.what为DO_PENDING_ACTIVITY_LAUNCHES_MSG时的操作。而且我们可以发现这里的异步消息是一个延时的异步消息,延时的时间为APP_SWITCH_DELAY_TIME,我们可以看一下该变量的定义:
static final long APP_SWITCH_DELAY_TIME = 5*1000;
然后我们可以看一下mHander的handleMessage方法的具体实现:
case DO_PENDING_ACTIVITY_LAUNCHES_MSG: {
synchronized (ActivityManagerService.this) {
mStackSupervisor.doPendingActivityLaunchesLocked(true);
}
} break;
可以发现这里直接调用了mStackSupervisor.doPendingActivityLaunchesLocked方法,好吧,继续看一下doPendingActivityLaunchesLocked方法的实现。
final void doPendingActivityLaunchesLocked(boolean doResume) {
while (!mPendingActivityLaunches.isEmpty()) {
PendingActivityLaunch pal = mPendingActivityLaunches.remove(0);
startActivityUncheckedLocked(pal.r, pal.sourceRecord, null, null, pal.startFlags,
doResume && mPendingActivityLaunches.isEmpty(), null, null);
}
}
可以发现这里就是调用了startActivity的操作了,看过Activity启动流程的同学应该知道:android源码解析之(十四)–>Activity启动流程 这里就是开始启动Activity了,所以当我们按下HOME按键的时候,后台的startActivity都会延时5秒钟执行…
然后回到我们的launchHomeFromHotKey方法,看一下launchHomeFromHotKey方法的实现。
void sendCloseSystemWindows(String reason) {
PhoneWindow.sendCloseSystemWindows(mContext, reason);
}
可以发现这里调用了PhoneWindow的静态方法sendCloseSystemWindow,继续看一下该方法的实现逻辑。
public static void sendCloseSystemWindows(Context context, String reason) {
if (ActivityManagerNative.isSystemReady()) {
try {
ActivityManagerNative.getDefault().closeSystemDialogs(reason);
} catch (RemoteException e) {
}
}
}
看到这里,很明显了又是调用了Binder的进程间通讯,最终ActivityManagerService的closeSystemDialogs方法会被执行,所以我们继续看一下ActivityManagerService的closeSystemDialogs方法的实现。
@Override
public void closeSystemDialogs(String reason) {
enforceNotIsolatedCaller("closeSystemDialogs");
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
try {
synchronized (this) {
if (uid >= Process.FIRST_APPLICATION_UID) {
ProcessRecord proc;
synchronized (mPidsSelfLocked) {
proc = mPidsSelfLocked.get(pid);
}
if (proc.curRawAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
Slog.w(TAG, "Ignoring closeSystemDialogs " + reason
+ " from background process " + proc);
return;
}
}
closeSystemDialogsLocked(reason);
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
可以发现其实在方法体中将关闭窗口的逻辑下发到了closeSystemDialogsLocked中,所以我们继续看一下closeSystemDialogsLocked方法的实现。
void closeSystemDialogsLocked(String reason) {
Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
if (reason != null) {
intent.putExtra("reason", reason);
}
mWindowManager.closeSystemDialogs(reason);
mStackSupervisor.closeSystemDialogsLocked();
broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
AppOpsManager.OP_NONE, null, false, false,
-1, Process.SYSTEM_UID, UserHandle.USER_ALL);
}
可以发现在方法体中首先调用了mWindowManager.closeSystemDialogs方法,该方法就是关闭当前页面中存在的系统窗口,比如输入法,壁纸等:
@Override
public void closeSystemDialogs(String reason) {
synchronized(mWindowMap) {
final int numDisplays = mDisplayContents.size();
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
final WindowList windows = mDisplayContents.valueAt(displayNdx).getWindowList();
final int numWindows = windows.size();
for (int winNdx = 0; winNdx < numWindows; ++winNdx) {
final WindowState w = windows.get(winNdx);
if (w.mHasSurface) {
try {
w.mClient.closeSystemDialogs(reason);
} catch (RemoteException e) {
}
}
}
}
}
}
讲过这样一层操作之后,我们就关闭了当前中存在的系统窗口。然后还是回到我们的launchHomeFromHotKey方法,我们发现在方法体的最后我们调用了startDockOrHome方法,这个方法就是实际的跳转HOME页面的方法了,我们可以具体看一下该方法的实现。
void startDockOrHome(boolean fromHomeKey, boolean awakenFromDreams) {
if (awakenFromDreams) {
awakenDreams();
}
Intent dock = createHomeDockIntent();
if (dock != null) {
try {
if (fromHomeKey) {
dock.putExtra(WindowManagerPolicy.EXTRA_FROM_HOME_KEY, fromHomeKey);
}
startActivityAsUser(dock, UserHandle.CURRENT);
return;
} catch (ActivityNotFoundException e) {
}
}
Intent intent;
if (fromHomeKey) {
intent = new Intent(mHomeIntent);
intent.putExtra(WindowManagerPolicy.EXTRA_FROM_HOME_KEY, fromHomeKey);
} else {
intent = mHomeIntent;
}
startActivityAsUser(intent, UserHandle.CURRENT);
}
可以发现我们在方法体中调用了createHomeDockIntent,这个方法的作用就是创建到达HOME页面的Intent对象,然后我们调用了startActivityAsUser方法,这样经过一系列的调用之后就调起了home页面的Activity,所以这时候系统就返回到了HOME页面。
总结:
系统也是在PhoneWindowManager中监听HOME按键的点击并进行处理;
系统监听到HOME按键之后会首先关闭相应的系统弹窗;
通过创建Intent对象,并调用startActivity方法使系统跳转到HOME页面;
另外对android源码解析方法感兴趣的可参考我的:
android源码解析之(一)–>android项目构建过程
android源码解析之(二)–>异步消息机制
android源码解析之(三)–>异步任务AsyncTask
android源码解析之(四)–>HandlerThread
android源码解析之(五)–>IntentService
android源码解析之(六)–>Log
android源码解析之(七)–>LruCache
android源码解析之(八)–>Zygote进程启动流程
android源码解析之(九)–>SystemServer进程启动流程
android源码解析之(十)–>Launcher启动流程
android源码解析之(十一)–>应用进程启动流程
android源码解析之(十二)–>系统启动并解析Manifest的流程
android源码解析之(十三)–>apk安装流程
android源码解析之(十四)–>Activity启动流程
android源码解析之(十五)–>Activity销毁流程
android源码解析(十六)–>应用进程Context创建流程
android源码解析(十七)–>Activity布局加载流程
android源码解析(十八)–>Activity布局绘制流程
android源码解析(十九)–>Dialog加载绘制流程
android源码解析(二十)–>Dialog取消绘制流程
android源码解析(二十一)–>PopupWindow加载绘制流程
android源码解析(二十二)–>Toast加载绘制流程
android源码解析(二十三)–>Android异常处理流程
android源码解析(二十四)–>onSaveInstanceState执行时机
android源码解析(二十五)–>onLowMemory执行流程
android源码解析(二十六)–>截屏事件流程
本文以同步至github中:github.com/yipianfengy…,欢迎star和follow
转载请标明出处:一片枫叶的专栏
前面我们讲解了系统截屏按键处理流程,HOME按键处理流程,今天再来讲解一下电源开关机按键事件流程,当然这也是系统按键处理流程方面的最后一篇博客了。
和截屏按键、HOME按键的处理流程类似,电源按键由于也是系统级别的按键,所以对其的事件处理逻辑是和截屏按键、HOME按键类似,不在某一个App中,而是在PhoneWindowManager的dispatchUnhandledKey方法中。所以和前面两篇类似,这里我们也是从PhoneWindowManager的dispatchUnhandledKey方法开始我们今天电源开关机按键的事件流程分析。
下面首先看一下dispatchUnhandledKey方法的实现逻辑:
public KeyEvent dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags) {
...
KeyEvent fallbackEvent = null;
if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
final KeyCharacterMap kcm = event.getKeyCharacterMap();
final int keyCode = event.getKeyCode();
final int metaState = event.getMetaState();
final boolean initialDown = event.getAction() == KeyEvent.ACTION_DOWN
&& event.getRepeatCount() == 0;
final FallbackAction fallbackAction;
if (initialDown) {
fallbackAction = kcm.getFallbackAction(keyCode, metaState);
} else {
fallbackAction = mFallbackActions.get(keyCode);
}
if (fallbackAction != null) {
if (DEBUG_INPUT) {
Slog.d(TAG, "Fallback: keyCode=" + fallbackAction.keyCode
+ " metaState=" + Integer.toHexString(fallbackAction.metaState));
}
final int flags = event.getFlags() | KeyEvent.FLAG_FALLBACK;
fallbackEvent = KeyEvent.obtain(
event.getDownTime(), event.getEventTime(),
event.getAction(), fallbackAction.keyCode,
event.getRepeatCount(), fallbackAction.metaState,
event.getDeviceId(), event.getScanCode(),
flags, event.getSource(), null);
if (!interceptFallback(win, fallbackEvent, policyFlags)) {
fallbackEvent.recycle();
fallbackEvent = null;
}
if (initialDown) {
mFallbackActions.put(keyCode, fallbackAction);
} else if (event.getAction() == KeyEvent.ACTION_UP) {
mFallbackActions.remove(keyCode);
fallbackAction.recycle();
}
}
}
...
return fallbackEvent;
}
通过前面两篇文章的分析
( android源码解析(二十六)–>截屏事件流程
android源码解析(二十七)–>HOME事件流程)
我们知道关于系统按键的处理逻辑被下放到了interceptFallback方法中,所以我们继续看一下interceptFallback方法的实现逻辑。
private boolean interceptFallback(WindowState win, KeyEvent fallbackEvent, int policyFlags) {
int actions = interceptKeyBeforeQueueing(fallbackEvent, policyFlags);
if ((actions & ACTION_PASS_TO_USER) != 0) {
long delayMillis = interceptKeyBeforeDispatching(
win, fallbackEvent, policyFlags);
if (delayMillis == 0) {
return true;
}
}
return false;
}
通过分析interceptFallback方法的源码,我们知道关于电源按键的处理逻辑在interceptKeyBeforeQueueing方法中,所以我们需要继续看一下interceptKeyBeforeQueueing方法中关于电源按键的处理逻辑。
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
...
case KeyEvent.KEYCODE_POWER: {
result &= ~ACTION_PASS_TO_USER;
isWakeKey = false; // wake-up will be handled separately
if (down) {
interceptPowerKeyDown(event, interactive);
} else {
interceptPowerKeyUp(event, interactive, canceled);
}
break;
}
...
return result;
}
这里我们重点看一下电源按键的处理事件,可以发现当电源按键按下的时候我们调用了interceptPowerKeyDown方法,可以看出,这个方法就是处理电源事件的了,既然如此,我们继续看一下interceptPowerKeyDown方法的执行逻辑。
private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {
...
if (interactive && !mScreenshotChordPowerKeyTriggered
&& (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
mScreenshotChordPowerKeyTriggered = true;
mScreenshotChordPowerKeyTime = event.getDownTime();
interceptScreenshotChord();
}
TelecomManager telecomManager = getTelecommService();
boolean hungUp = false;
if (telecomManager != null) {
if (telecomManager.isRinging()) {
telecomManager.silenceRinger();
} else if ((mIncallPowerBehavior
& Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
&& telecomManager.isInCall() && interactive) {
hungUp = telecomManager.endCall();
}
}
mPowerKeyHandled = hungUp || mScreenshotChordVolumeDownKeyTriggered
|| mScreenshotChordVolumeUpKeyTriggered;
if (!mPowerKeyHandled) {
if (interactive) {
if (hasLongPressOnPowerBehavior()) {
Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);
msg.setAsynchronous(true);
mHandler.sendMessageDelayed(msg,
ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
}
} else {
wakeUpFromPowerKey(event.getDownTime());
if (mSupportLongPressPowerWhenNonInteractive && hasLongPressOnPowerBehavior()) {
Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);
msg.setAsynchronous(true);
mHandler.sendMessageDelayed(msg,
ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
mBeganFromNonInteractive = true;
} else {
final int maxCount = getMaxMultiPressPowerCount();
if (maxCount <= 1) {
mPowerKeyHandled = true;
} else {
mBeganFromNonInteractive = true;
}
}
}
}
}
这里我们重点看一下if(interactive)分支,在这里我们发送一个一个异步消息,并且msg的what为MSG_POWER_LONG_PRESS,即长按电源事件的异步消息,所以我们看一下mHandler的handleMessage方法对该what消息的处理逻辑。
case MSG_POWER_LONG_PRESS:
powerLongPress();
break;
我们可以发现在mHandler的handleMessage方法中当msg的what为MSG_POWER_LONG_PRESS时我们调用了powerLongPress方法,这个方法应该就是处理电源按键长按的逻辑,下面我们来看一下powerLongPress方法的实现。
private void powerLongPress() {
final int behavior = getResolvedLongPressOnPowerBehavior();
switch (behavior) {
case LONG_PRESS_POWER_NOTHING:
break;
case LONG_PRESS_POWER_GLOBAL_ACTIONS:
mPowerKeyHandled = true;
if (!performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false)) {
performAuditoryFeedbackForAccessibilityIfNeed();
}
showGlobalActionsInternal();
break;
case LONG_PRESS_POWER_SHUT_OFF:
case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM:
mPowerKeyHandled = true;
performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
mWindowManagerFuncs.shutdown(behavior == LONG_PRESS_POWER_SHUT_OFF);
break;
}
}
可以发现这里有四个switch分之,其中第一个什么都不做直接break掉,第二个case则需要弹出选择操作界面,比如:飞行模式,开关机,静音模式,重新启动等,这里可以参看一下小米手机的关机界面:

然后第三第四个case分之则是直接调用关机方法,这里我们先看第二个case,看看系统是如何显示出关机操作界面的。那我们看一下showGlobalActionsInternal方法的实现逻辑。
void showGlobalActionsInternal() {
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
if (mGlobalActions == null) {
mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs);
}
final boolean keyguardShowing = isKeyguardShowingAndNotOccluded();
mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());
if (keyguardShowing) {
mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
}
}
可以发现我们首先调用了sendCloseSystemWindows方法,前面我们分析HOME按键流程的时候(android源码解析(二十七)–>HOME事件流程)知道该方法用于关机系统弹窗,比如输入法,壁纸等。然后我们创建了一个GlobalActions对象,并调用了其showDialog方法,通过分析源码,我们发现该方法就是用于显示长按电源按键弹出操作界面的,我们首先看一下GlobalActions的构造方法:
public GlobalActions(Context context, WindowManagerFuncs windowManagerFuncs) {
mContext = context
mWindowManagerFuncs = windowManagerFuncs
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE)
mDreamManager = IDreamManager.Stub.asInterface(
ServiceManager.getService(DreamService.DREAM_SERVICE))
// receive broadcasts
IntentFilter filter = new IntentFilter()
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
filter.addAction(Intent.ACTION_SCREEN_OFF)
filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)
context.registerReceiver(mBroadcastReceiver, filter)
ConnectivityManager cm = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE)
mHasTelephony = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)
// get notified of phone state changes
TelephonyManager telephonyManager =
(TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE)
telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE)
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true,
mAirplaneModeObserver)
Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE)
mHasVibrator = vibrator != null && vibrator.hasVibrator()
mShowSilentToggle = SHOW_SILENT_TOGGLE && !mContext.getResources().getBoolean(
com.android.internal.R.bool.config_useFixedVolume)
}
可以看到在GlobalActions对象的构造方法中我们主要用于初始化其成员变量,由于我们的电源长按操作界面是一个全局页面,所以这里自定义了一个Window对象,下面我们看一下GlobalActions的showDialog方法。
public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
mKeyguardShowing = keyguardShowing;
mDeviceProvisioned = isDeviceProvisioned;
if (mDialog != null) {
mDialog.dismiss();
mDialog = null;
mHandler.sendEmptyMessage(MESSAGE_SHOW);
} else {
handleShow();
}
}
可以看到在showDialog方法中我们首先判断mDialog是否为空,若为空则发送msg的what为MESSAGE_SHOW的异步消息,否则调用handleShow方法,而这里的mDialog是一个类型为GlobalActionsDialog的变量,由于我们的mDialog为空,所以下面我们看一下handleShow方法。
private void handleShow() {
awakenIfNecessary();
mDialog = createDialog();
prepareDialog();
if (mAdapter.getCount() == 1
&& mAdapter.getItem(0) instanceof SinglePressAction
&& !(mAdapter.getItem(0) instanceof LongPressAction)) {
((SinglePressAction) mAdapter.getItem(0)).onPress();
} else {
WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();
attrs.setTitle("GlobalActions");
mDialog.getWindow().setAttributes(attrs);
mDialog.show();
mDialog.getWindow().getDecorView().setSystemUiVisibility(View.STATUS_BAR_DISABLE_EXPAND);
}
在方法体中我们调用了createDialog方法,创建了GlobalActionsDialog类型的mDialog,这里我们看一下createDialog的实现方法。
private GlobalActionsDialog createDialog() {
...
mAirplaneModeOn = new ToggleAction(
R.drawable.ic_lock_airplane_mode,
R.drawable.ic_lock_airplane_mode_off,
R.string.global_actions_toggle_airplane_mode,
R.string.global_actions_airplane_mode_on_status,
R.string.global_actions_airplane_mode_off_status) {
void onToggle(boolean on) {
if (mHasTelephony && Boolean.parseBoolean(
SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE))) {
mIsWaitingForEcmExit = true;
Intent ecmDialogIntent =
new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null);
ecmDialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(ecmDialogIntent);
} else {
changeAirplaneModeSystemSetting(on);
}
}
@Override
protected void changeStateFromPress(boolean buttonOn) {
if (!mHasTelephony) return;
if (!(Boolean.parseBoolean(
SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE)))) {
mState = buttonOn ? State.TurningOn : State.TurningOff;
mAirplaneState = mState;
}
}
public boolean showDuringKeyguard() {
return true;
}
public boolean showBeforeProvisioning() {
return false;
}
};
onAirplaneModeChanged();
mItems = new ArrayList();
String[] defaultActions = mContext.getResources().getStringArray(
com.android.internal.R.array.config_globalActionsList);
ArraySet addedKeys = new ArraySet();
for (int i = 0; i < defaultActions.length; i++) {
String actionKey = defaultActions[i];
if (addedKeys.contains(actionKey)) {
continue;
}
if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) {
mItems.add(new PowerAction());
} else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) {
mItems.add(mAirplaneModeOn);
} else if (GLOBAL_ACTION_KEY_BUGREPORT.equals(actionKey)) {
if (Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0 && isCurrentUserOwner()) {
mItems.add(getBugReportAction());
}
} else if (GLOBAL_ACTION_KEY_SILENT.equals(actionKey)) {
if (mShowSilentToggle) {
mItems.add(mSilentModeAction);
}
} else if (GLOBAL_ACTION_KEY_USERS.equals(actionKey)) {
if (SystemProperties.getBoolean("fw.power_user_switcher", false)) {
addUsersToMenu(mItems);
}
} else if (GLOBAL_ACTION_KEY_SETTINGS.equals(actionKey)) {
mItems.add(getSettingsAction());
} else if (GLOBAL_ACTION_KEY_LOCKDOWN.equals(actionKey)) {
mItems.add(getLockdownAction());
} else if (GLOBAL_ACTION_KEY_VOICEASSIST.equals(actionKey)) {
mItems.add(getVoiceAssistAction());
} else if (GLOBAL_ACTION_KEY_ASSIST.equals(actionKey)) {
mItems.add(getAssistAction());
} else {
Log.e(TAG, "Invalid global action key " + actionKey);
}
addedKeys.add(actionKey);
}
mAdapter = new MyAdapter();
AlertParams params = new AlertParams(mContext);
params.mAdapter = mAdapter;
params.mOnClickListener = this;
params.mForceInverseBackground = true;
GlobalActionsDialog dialog = new GlobalActionsDialog(mContext, params);
dialog.setCanceledOnTouchOutside(false);
dialog.getListView().setItemsCanFocus(true);
dialog.getListView().setLongClickable(true);
dialog.getListView().setOnItemLongClickListener(
new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView parent, View view, int position,
long id) {
final Action action = mAdapter.getItem(position);
if (action instanceof LongPressAction) {
return ((LongPressAction) action).onLongPress();
}
return false;
}
});
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
dialog.setOnDismissListener(this);
return dialog;
}
方法体的内容比较长,我们看重点的内容,首先我们通过调用mContext.getResources().getStringArray(com.android.internal.R.array.config_globalActionsList)获得操作列表,这里可能包含:飞行模式、开关机、静音模式、重启等等,然后我们轮训操作列表,并添加相应的Action最后我们将这个操作列表保存到Dialog的adapter中并返回该dialog,然后我们回到我们刚刚的handleShow方法,在得到返回的dialog之后我们调用了dialog的show方法,这样我们就显示出了电源长按操作界面,比如小米的界面:

好吧,继续我们的分析,当我们长按电源按键弹出操作弹窗之后,这时候点击关机是怎么样的流程呢?我们发现在createDialog方法中关机操作adapter的item,我们添加了:
mItems.add(new PowerAction());
这样不难发现我们对关机按钮的操作封装在了PowerAction中,所以我们继续看一下PowerAction的实现。
private final class PowerAction extends SinglePressAction implements LongPressAction {
private PowerAction() {
super(com.android.internal.R.drawable.ic_lock_power_off,
R.string.global_action_power_off);
}
@Override
public boolean onLongPress() {
UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
if (!um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
mWindowManagerFuncs.rebootSafeMode(true);
return true;
}
return false;
}
@Override
public boolean showDuringKeyguard() {
return true;
}
@Override
public boolean showBeforeProvisioning() {
return true;
}
@Override
public void onPress() {
mWindowManagerFuncs.shutdown(false );
}
}
可以发现在PowerAction类的成员函数onPress方法中我们调用了mWindowManagerFuncs.showdown方法,而这个方法也就是开始执行我们的关机操作了,那么这里的mWindowManagerFuncs又是什么呢?它是在什么时候赋值的呢?通过分析我们发现这里的mWindowManagerFuncs成员变量是在GlobalActions的构造方法中赋值的。
public GlobalActions(Context context, WindowManagerFuncs windowManagerFuncs) {
...
mWindowManagerFuncs = windowManagerFuncs;
...
}
好吧,回到我们的PhoneWindowManager,早构造GlobalActions时,直接传递的是PhoneWindowManager的成员变量mWindowManagerFuncs,那么PhoneWindowManager的mWindowManagerFuncs成员变量又是何时被赋值的呢?通过分析源码我们能够看到PhoneWindowManager的mWindowManagerFuncs变量是在PhoneWindowManager的init方法中初始化的,好吧,再次查找PhoneWindowManager的init方法是何时被调用的。
经过查找终于在WindowManagerService中我们找到了PhoneWindowManager的init方法的调用。
private void initPolicy() {
UiThread.getHandler().runWithScissors(new Runnable() {
@Override
public void run() {
WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper());
mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this);
}
}, 0);
}
这里的mPolicy就是一个PhoneWindowManager的实力,可以发现这里的init方法中mWindowManagerFuncs传递的就是一个WindowManagerService的实例,O(∩_∩)O哈哈~,让我们好找。
然么在PowerAction的onPress方法中调用的mWindowManagerFuncs.shutdown(false /* confirm */);方法,实际上调用的就是WindowManagerService的shutdown方法,这样我们继续看一下WindowManagerService的shutdown方法的实现。
@Override
public void shutdown(boolean confirm) {
ShutdownThread.shutdown(mContext, confirm);
}
好吧,这里很简单就是直接调用了ShutdownThread的shutdown方法,看样子这里就是执行关机操作的封装了,继续看一下ShutdownThread的shutdown方法。
public static void shutdown(final Context context, boolean confirm) {
mReboot = false;
mRebootSafeMode = false;
shutdownInner(context, confirm);
}
可以看到在ShutdownThread的shutdown方法中代码很简单,具体的操作下发到了shutdownInner方法中,那么我们继续看一下shutdownInner方法的实现。
static void shutdownInner(final Context context, boolean confirm) {
// ensure that only one thread is trying to power down.
// any additional calls are just returned
synchronized (sIsStartedGuard) {
if (sIsStarted) {
Log.d(TAG, "Request to shutdown already running, returning.")
return
}
}
final int longPressBehavior = context.getResources().getInteger(
com.android.internal.R.integer.config_longPressOnPowerBehavior)
final int resourceId = mRebootSafeMode
? com.android.internal.R.string.reboot_safemode_confirm
: (longPressBehavior == 2
? com.android.internal.R.string.shutdown_confirm_question
: com.android.internal.R.string.shutdown_confirm)
Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior)
if (confirm) {
final CloseDialogReceiver closer = new CloseDialogReceiver(context)
if (sConfirmDialog != null) {
sConfirmDialog.dismiss()
}
sConfirmDialog = new AlertDialog.Builder(context)
.setTitle(mRebootSafeMode
? com.android.internal.R.string.reboot_safemode_title
: com.android.internal.R.string.power_off)
.setMessage(resourceId)
.setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
beginShutdownSequence(context)
}
})
.setNegativeButton(com.android.internal.R.string.no, null)
.create()
closer.dialog = sConfirmDialog
sConfirmDialog.setOnDismissListener(closer)
sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG)
sConfirmDialog.show()
} else {
beginShutdownSequence(context)
}
}
可以看到方法体中,首先判断若用户点击了关机按键是否弹出确认框,若弹出则弹出关机确认框,若不需要确认,则直接调用beginShutdownSequence方法,执行关机操作。而在关机确认框中我们的确认按钮也是执行了beginShutdownSequence方法,所以我们继续看一下关机方法beginShutdownSequence。
private static void beginShutdownSequence(Context context) {
synchronized (sIsStartedGuard) {
if (sIsStarted) {
Log.d(TAG, "Shutdown sequence already running, returning.")
return
}
sIsStarted = true
}
...
if (PowerManager.REBOOT_RECOVERY.equals(mRebootReason)) {
mRebootUpdate = new File(UNCRYPT_PACKAGE_FILE).exists()
if (mRebootUpdate) {
pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_update_title))
pd.setMessage(context.getText(
com.android.internal.R.string.reboot_to_update_prepare))
pd.setMax(100)
pd.setProgressNumberFormat(null)
pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL)
pd.setProgress(0)
pd.setIndeterminate(false)
} else {
// Factory reset path. Set the dialog message accordingly.
pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_reset_title))
pd.setMessage(context.getText(
com.android.internal.R.string.reboot_to_reset_message))
pd.setIndeterminate(true)
}
} else {
pd.setTitle(context.getText(com.android.internal.R.string.power_off))
pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress))
pd.setIndeterminate(true)
}
pd.setCancelable(false)
pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG)
pd.show()
sInstance.mProgressDialog = pd
sInstance.mContext = context
sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE)
// make sure we never fall asleep again
sInstance.mCpuWakeLock = null
try {
sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu")
sInstance.mCpuWakeLock.setReferenceCounted(false)
sInstance.mCpuWakeLock.acquire()
} catch (SecurityException e) {
Log.w(TAG, "No permission to acquire wake lock", e)
sInstance.mCpuWakeLock = null
}
// also make sure the screen stays on for better user experience
sInstance.mScreenWakeLock = null
if (sInstance.mPowerManager.isScreenOn()) {
try {
sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(
PowerManager.FULL_WAKE_LOCK, TAG + "-screen")
sInstance.mScreenWakeLock.setReferenceCounted(false)
sInstance.mScreenWakeLock.acquire()
} catch (SecurityException e) {
Log.w(TAG, "No permission to acquire wake lock", e)
sInstance.mScreenWakeLock = null
}
}
// start the thread that initiates shutdown
sInstance.mHandler = new Handler() {
}
sInstance.start()
}
在方法beginShutdownSequence中我们首先初始化了一个Process的dialog,该dialog用于显示关机界面,然后我们调用了sInstance.start方法,再往下的方法中就是真正的shutdown方法的实现,同时也是native方法,我们这里就不做过得解读了。。。
总结:
电源按键是系统按键,所以对电源按键的处理逻辑也是在PhoneWindowManager的dispatchUnhandledKey方法中;
在PhoneWindowManager的dispatchUnhandleKey方法处理Power按键之后会首先显示系统操作弹窗,一般包括但不限于:飞行模式,静音模式,重新启动,关机等;
当用户点击关机按钮是调用的是WindowManagerService.shutdown方法,而内部调用的是ShutdownThread.shutdown方法;
另外对android源码解析方法感兴趣的可参考我的:
android源码解析之(一)–>android项目构建过程
android源码解析之(二)–>异步消息机制
android源码解析之(三)–>异步任务AsyncTask
android源码解析之(四)–>HandlerThread
android源码解析之(五)–>IntentService
android源码解析之(六)–>Log
android源码解析之(七)–>LruCache
android源码解析之(八)–>Zygote进程启动流程
android源码解析之(九)–>SystemServer进程启动流程
android源码解析之(十)–>Launcher启动流程
android源码解析之(十一)–>应用进程启动流程
android源码解析之(十二)–>系统启动并解析Manifest的流程
android源码解析之(十三)–>apk安装流程
android源码解析之(十四)–>Activity启动流程
android源码解析之(十五)–>Activity销毁流程
android源码解析(十六)–>应用进程Context创建流程
android源码解析(十七)–>Activity布局加载流程
android源码解析(十八)–>Activity布局绘制流程
android源码解析(十九)–>Dialog加载绘制流程
android源码解析(二十)–>Dialog取消绘制流程
android源码解析(二十一)–>PopupWindow加载绘制流程
android源码解析(二十二)–>Toast加载绘制流程
android源码解析(二十三)–>Android异常处理流程
android源码解析(二十四)–>onSaveInstanceState执行时机
android源码解析(二十五)–>onLowMemory执行流程
android源码解析(二十六)–>截屏事件流程
android源码解析(二十七)–>HOME事件流程
本文以同步至github中:github.com/yipianfengy…,欢迎star和follow
转载请标明出处:一片枫叶的专栏
从这篇文章中我们开始分析android系统的事件分发流程,其实网上已经有了很多关于android系统的事件分发流程的文章,奈何看了很多但是印象还不是很深,所以这里总结一番。
android系统的事件分发流程分为很多部分:
- Native层 –> ViewRootImpl层 –> DecorView层 –> Activity层 –> ViewGroup层 –> View层
所以android系统的事件分发流程是从Native层开始的,然后分发到ViewRootImpl中,然后分发到DecorView层,然后分发到ViewGroup层,最后分发到View层中。下面我们将从Native层开始分析事件的分发流程。
在Native层android系统的事件流程:
Android系统是从从底层驱动中获取各种原始的用户消息,包括按键、触摸屏、鼠标、滚迹球等用户事件消息。
在获取用户消息之后,android系统会对最原始的消息进行预处理,包括两个方面:一方面,将消息转化成系统可以处理的消息事件;另一方面,处理一些特殊的事件,比如HOME、MENU、POWER键等处理(前面的几篇文章中我们已经分析了系统按键处理逻辑的执行流程)。
将处理后的消息事件分发到各个应用进程,这个需要使用IPC机制,Android系统使用管道来进行消息的传递。
Android系统使用InputManager类来管理消息,而具体的功能则是通过InputReaderThread和InputDispatcherThread两个线程来实现。其中InputReaderThread线程负责消息的读取,而InputDispatcherThread则负责消息的预处理和分发到各个应用进程中。
Acitivty系统在SystemServer进程中启动WindowManagerService服务,然后在WindowManagerService服务中启动InputManagerService服务。
可以看到在Native层,主要创建了两个两个线程,其中一个用于读取消息,另一个用于分发消息,消息经过分发最终会上传至App中。
在ViewRootImpl层android系统的事件流程
在Native层的事件分发线程中,经过事件的分发流程,最终会调用InputEventSender的dispatchInputEventFinished方法,可以看一下具体代码的实现:
private void dispatchInputEventFinished(int seq, boolean handled) {
onInputEventFinished(seq, handled);
}
在dispatchInputEventFinished方法中我们最终调用的是onInputEventFinished方法,然后我们查看onInputEventFinished方法的实现,发现其是一个空方法。。。,好吧,经过分析我们发现,Native层最终调用的并不是InputEventSender,而是调用InputEventSender的子类ImeInputEventSender,即ImeInputEventSender的onInputEventFinished方法,该类定义在源文件InputMethodManager中:
private final class ImeInputEventSender extends InputEventSender {
public ImeInputEventSender(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
}
@Override
public void onInputEventFinished(int seq, boolean handled) {
finishedInputEvent(seq, handled, false);
}
}
可以看到在其onInputEventFinished方法中又调用了finishedInputEvent方法,这样我们在继续看一下finishedInputEvent方法的实现。
void finishedInputEvent(int seq, boolean handled, boolean timeout) {
final PendingEvent p;
synchronized (mH) {
int index = mPendingEvents.indexOfKey(seq);
if (index < 0) {
return;
}
p = mPendingEvents.valueAt(index);
mPendingEvents.removeAt(index);
Trace.traceCounter(Trace.TRACE_TAG_INPUT, PENDING_EVENT_COUNTER, mPendingEvents.size());
if (timeout) {
Log.w(TAG, "Timeout waiting for IME to handle input event after "
+ INPUT_METHOD_NOT_RESPONDING_TIMEOUT + " ms: " + p.mInputMethodId);
} else {
mH.removeMessages(MSG_TIMEOUT_INPUT_EVENT, p);
}
}
invokeFinishedInputEventCallback(p, handled);
}
在方法finishedInputEvent中,经过一系列的处理之后最终调用的是invokeFinishedInputEventCallback方法,所以我们继续看一下invokeFinishedInputEventCallback方法的实现。
void invokeFinishedInputEventCallback(PendingEvent p, boolean handled) {
p.mHandled = handled
if (p.mHandler.getLooper().isCurrentThread()) {
// Already running on the callback handler thread so we can send the
// callback immediately.
p.run()
} else {
// Post the event to the callback handler thread.
// In this case, the callback will be responsible for recycling the event.
Message msg = Message.obtain(p.mHandler, p)
msg.setAsynchronous(true)
msg.sendToTarget()
}
}
可以发现这里我们首先判断PendingEvent的mHandler所在的线程是否是当前线程,若是的话则直接调用p.run方法,若不是的话则发送一个异步消息,而异步消息最终也是执行的p.run方法,所以我们继续看一下PendingEvent的run方法。
@Override
public void run() {
mCallback.onFinishedInputEvent(mToken, mHandled);
synchronized (mH) {
recyclePendingEventLocked(this);
}
}
可以发现在run方法中我们调用了mCallback的onFinishedInputEvent方法,需要说明的是这里的mCallback就是我们ViewRootImpl中的ImeInputStage类对象,而这里的ViewRootImpl对象就是我们的系统当前界面,前面我们分析Activity的加载绘制流程的时候知道Activity中保存了一个Window对象用于表示窗口信息,而Window对象内部就是通过ViewRootImpl对象实现窗口的加载绘制,所以这里的mCallback对象就是我们当前的App获取焦点的窗口的ViewRootImpl中的ImeInputStage对象,然后我们看一下该对象的onFinishedInputEvent方法的实现。
final class ImeInputStage extends AsyncInputStage
implements InputMethodManager.FinishedInputEventCallback {
...
@Override
public void onFinishedInputEvent(Object token, boolean handled) {
QueuedInputEvent q = (QueuedInputEvent)token;
if (handled) {
finish(q, true);
return;
}
forward(q);
}
}
这样经过一系列的调用之后我们消息的处理逻辑上传至了ViewRootImpl中,而在ViewRootImpl中经过一些列的调用之后我们ViewRootImpl$ViewPostImeInputStage.processKeyEvent方法:
at android.view.ViewRootImpl$ViewPostImeInputStage.processKeyEvent(ViewRootImpl.java:4152)
at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4114)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3662)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3715)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3681)
at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3807)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3689)
at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:3864)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3662)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3715)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3681)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3689)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3662)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3715)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3681)
at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3840)
at android.view.ViewRootImpl$ImeInputStage.onFinishedInputEvent(ViewRootImpl.java:4006)
at android.view.inputmethod.InputMethodManager$PendingEvent.run(InputMethodManager.java:2272)
at android.view.inputmethod.InputMethodManager.invokeFinishedInputEventCallback(InputMethodManager.java:1893)
at android.view.inputmethod.InputMethodManager.finishedInputEvent(InputMethodManager.java:1884)
at android.view.inputmethod.InputMethodManager$ImeInputEventSender.onInputEventFinished(InputMethodManager.java:2249)
at android.view.InputEventSender.dispatchInputEventFinished(InputEventSender.java:141)
这是通过异常信息打印的堆栈信息,从中我们可以看到在ViewRootImpl中我们经过一系列的调用之后最终执行的是:ViewRootImpl$ViewPostImeInputStage.processKeyEvent方法,这样我们继续看一下processKeyEvent方法。
private int processKeyEvent(QueuedInputEvent q) {
...
// Deliver the key to the view hierarchy.
if (mView.dispatchKeyEvent(event)) {
return FINISH_HANDLED;
}
...
}
可以看到这里调用了mView的dispatchKeyEvent方法,而我们分析过Activity窗口加载绘制流程,从中我们知道ViewRootImpl中的mView对象就是我们PhoneWindow中的mDecorView对象(DecorView),所以经过层层调用我们最终执行到了DecorView层。
在DecorView层android系统的事件流程
从上面我们知道在ViewRootImpl中我们最终调用了mView.dispatchKeyEvent方法,即执行的是PhoneWindow%DecorView.dispatchKeyEvent方法。
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
final int keyCode = event.getKeyCode();
final int action = event.getAction();
final boolean isDown = action == KeyEvent.ACTION_DOWN;
if (isDown && (event.getRepeatCount() == 0)) {
if ((mPanelChordingKey > 0) && (mPanelChordingKey != keyCode)) {
boolean handled = dispatchKeyShortcutEvent(event);
if (handled) {
return true;
}
}
if ((mPreparedPanel != null) && mPreparedPanel.isOpen) {
if (performPanelShortcut(mPreparedPanel, keyCode, event, 0)) {
return true;
}
}
}
if (!isDestroyed()) {
final Callback cb = getCallback();
final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
: super.dispatchKeyEvent(event);
if (handled) {
return true;
}
}
return isDown ? PhoneWindow.this.onKeyDown(mFeatureId, event.getKeyCode(), event)
: PhoneWindow.this.onKeyUp(mFeatureId, event.getKeyCode(), event);
}
从中我们可以看到如果当前的PhoneWindow不是destroy庄则,则执行cb.dispatchKeyEvent方法,而这里的callback对象就是我们的Activity对象,所以这里最终会执行到Activity的dispatchKeyEvent方法。。。
在Activity层android系统的事件流程
所以我们这里继续看一下Actiivty中的dispatchKeyEvent方法:
public boolean dispatchKeyEvent(KeyEvent event) {
onUserInteraction();
if (event.getKeyCode() == KeyEvent.KEYCODE_MENU &&
mActionBar != null && mActionBar.onMenuKeyEvent(event)) {
return true;
}
Window win = getWindow();
if (win.superDispatchKeyEvent(event)) {
return true;
}
View decor = mDecor;
if (decor == null) decor = win.getDecorView();
return event.dispatch(this, decor != null
? decor.getKeyDispatcherState() : null, this);
}
从中我们可以看到我们首先调用了Activity的window对象的superDispatchKeyEvent方法,而这个方法就是将处理方法下发带Activity中的View,而这里我们分析的是返回按键,显然的View层是无法处理这里的返回按键的,所以win.superDispatchKeyEvent方法返回的是false,所以最终我们执行的是event.dispatch方法。这样我们继续看一下event.dispatch方法的实现。
public final boolean dispatch(Callback receiver, DispatcherState state,
Object target) {
switch (mAction) {
...
case ACTION_UP:
if (DEBUG) Log.v(TAG, "Key up to " + target + " in " + state
+ ": " + this);
if (state != null) {
state.handleUpEvent(this);
}
return receiver.onKeyUp(mKeyCode, this);
...
}
return false;
}
这里我们暂时分析一下ACTION_UP事件,可以发现这里最终调用的是receiver.onKeyUp方法,而这里的receiver就是我们的Actiivty,所以这里又回到了Activity并且执行其onKeyUp方法。
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (getApplicationInfo().targetSdkVersion
>= Build.VERSION_CODES.ECLAIR) {
if (keyCode == KeyEvent.KEYCODE_BACK && event.isTracking()
&& !event.isCanceled()) {
onBackPressed();
return true;
}
}
return false;
}
看onKeyUp方法,我们可以发现当我们按的是返回按键时,其回调了onBackPressed方法,所以我们继续看一下onBackPressed方法。
public void onBackPressed() {
if (mActionBar != null && mActionBar.collapseActionView()) {
return;
}
if (!mFragments.getFragmentManager().popBackStackImmediate()) {
finishAfterTransition();
}
}
可以看到,在onBackPressed方法中,我们最终调用的是finishAfterTransition方法,所以继续看一下这个方法的实现逻辑。
public void finishAfterTransition() {
if (!mActivityTransitionState.startExitBackTransition(this)) {
finish();
}
}
O(∩_∩)O哈哈~,原来finish方法是在这里调用的,这样我们按下返回按键并抬起之后,经过层层的调用之后最终调用了我们的finish方法,而这个方法就是finish掉Activity的方法,也就解释了我们在App中默认按下返回按键之后Acitivty会被销毁了。
总结:
本文中由于是分析的返回按键的处理流程,所以事件的分发流程没有做说明,下面的文章中会着重介绍Android的事件分发流程;
事件分发流程从Native –> ViewRootImpl层 –> DecorView层 –> Activity层都是类似的,无论是按键分发流程还是触摸事件分发流程
另外对android源码解析方法感兴趣的可参考我的:
android源码解析之(一)–>android项目构建过程
android源码解析之(二)–>异步消息机制
android源码解析之(三)–>异步任务AsyncTask
android源码解析之(四)–>HandlerThread
android源码解析之(五)–>IntentService
android源码解析之(六)–>Log
android源码解析之(七)–>LruCache
android源码解析之(八)–>Zygote进程启动流程
android源码解析之(九)–>SystemServer进程启动流程
android源码解析之(十)–>Launcher启动流程
android源码解析之(十一)–>应用进程启动流程
android源码解析之(十二)–>系统启动并解析Manifest的流程
android源码解析之(十三)–>apk安装流程
android源码解析之(十四)–>Activity启动流程
android源码解析之(十五)–>Activity销毁流程
android源码解析(十六)–>应用进程Context创建流程
android源码解析(十七)–>Activity布局加载流程
android源码解析(十八)–>Activity布局绘制流程
android源码解析(十九)–>Dialog加载绘制流程
android源码解析(二十)–>Dialog取消绘制流程
android源码解析(二十一)–>PopupWindow加载绘制流程
android源码解析(二十二)–>Toast加载绘制流程
android源码解析(二十三)–>Android异常处理流程
android源码解析(二十四)–>onSaveInstanceState执行时机
android源码解析(二十五)–>onLowMemory执行流程
android源码解析(二十六)–>截屏事件流程
android源码解析(二十七)–>HOME事件流程
android源码解析(二十八)–>电源开关机按键事件流程
本文以同步至github中:github.com/yipianfengy…,欢迎star和follow
转载请标明出处:一片枫叶的专栏
前面一篇文章中我们分析了App返回按键的分发流程,从Native层到ViewRootImpl层到DocorView层到Activity层,以及在Activity中的dispatchKeyEvent方法中分发事件,最终调用了Activity的finish方法,即销毁Activity,所以一般情况下假如我们不重写Activity的onBackPress方法或者是onKeyDown方法,当我们按下并抬起返回按键的时候默认都是销毁当前Activity。而本文中我们主要介绍触摸事件的分发流程,从Native层到Activity层触摸事件的分发了流程和按键的分发事件都是类似的,这里我们可以根据异常堆栈信息看一下。
at com.example.aaron.helloworld.MainActivity.dispatchTouchEvent(MainActivity.java:103)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:2359)
at android.view.View.dispatchPointerEvent(View.java:8698)
at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:4530)
at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4388)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3924)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3977)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3943)
at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:4053)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3951)
at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4110)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3924)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3977)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3943)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3951)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3924)
at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:6345)
at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:6301)
at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:6254)
at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:6507)
at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:185)
这样经过一系列的方法调用之后最终调用了Activity的dispatchTouchEvent方法,而我们也是从Activiyt的dispatchTouchEvent方法开始对触摸事件的分发进行分析。
在具体查看Activity的dispatchTouchEvent方法之前我们先简单介绍一下触摸事件,触摸事件是由一个触摸按下事件、N个触摸滑动事件和一个触摸抬起事件组成的,通常的一个触摸事件中只能存在一个触摸按下和一个触摸抬起事件,但是触摸滑动事件可以有零个或者多个。好了,知道这个概念以后,下面我们就具体看一下Activity中的dispatchTouchEvent的实现逻辑。
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
在看一下dispatchTouchEvent方法之前我们首先需要解释一下MotionEvent的概念。MotionEvent是一个触摸动作的封装,里面包含了触摸动作的类型,以及操作等属性,我们具体的可以看一下MotionEvent的说明:
Object used to report movement (mouse, pen, finger, trackball) events. Motion events may hold either absolute or relative movements and other data, depending on the type of device.
然后在dispatchTouchEvent方法中,会首先判断MotionEvent的动作类型,也就是我们的触目动作的类型,判断其是否是“按下”操作,若是的湖泽,则执行onUserInteraction方法,这个方法又是实现了什么逻辑呢?
public void onUserInteraction() {
}
可以发现其在Activity中只是一个简单的空实现方法,同样的我们可以看一下该方法的介绍:
Called whenever a key, touch, or trackball event is dispatched to the activity. Implement this method if you wish to know that the user has interacted with the device in some way while your activity is running. This callback and {@link #onUserLeaveHint} are intended to help activities manage status bar notifications intelligently; specifically, for helping activities determine the proper time to cancel a notfication.
理解上就是用户在触屏点击,按home,back,menu键都会触发此方法。
回到Activity的dispatchTouchEvent方法中,我们调用了getWindow().suerDispatchTouchEvent()方法,我们分析过Activity的加载绘制流程,而这里的getWindow()就是返回Activity中的mWindow对象,而我们知道Activity中的mWindow对象就是一个PhoneWindow的实例。并且这里的window.superDispatchTouchEvent若返回值为ture,则直接返回true,否则的话会执行Activity的onTouchEvent方法,继续我们看一下PhoneWindow的superDispatchTouchEvent方法。
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
可以看到在PhoneWindow中的superDispatchTouchEvent方法中调用的是mDecor.superDispatchTouchEvent方法,而这里的mDecor是我们Activity显示的ViewTree的根View,并且mDecor是一个FrameLayout的子类,所以这里我们看一下mDecor的superDispatchTouchEvent方法。
private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
...
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
...
}
在DecorView的superDispatchTouchEvent方法中我们调用了super.dispatchTouchEvent方法,而我们的DecorView继承于FrameLayout,但是经过查看之后我们知道FrameLayout中并没有实现dispatchTouchEvent方法,而由于我们的FrameLayout继承于ViewGroup,所以这里的dispatchTouchEvent方法应该就是ViewGroup的dispatchTouchEvent方法。
好了,这里先暂时说一下Acitivty中的事件分发流程
ViewRootImpl层的事件分发会首先调用Activity的dispatchTouchEvent方法;
Activity的dispatchTouchEvent方法中会通过Window.superDispatchTouchEvent方法将事件传递给DecorView即ViewGroup。
若window的superDispatchTouchEvent方法返回true,则事件分发完成,Activity的dispatchTouchEvent直接返回为true,否则的话调用Activity的onTouchEvent方法,并且Acitivty的dispatchTouchEvent返回值与Activity的onTouchEvent返回值一致。
下面我们在继续看一下ViewGroup的dispatchTouchEvent方法。
public boolean dispatchTouchEvent(MotionEvent ev) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
}
if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
ev.setTargetAccessibilityFocus(false);
}
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
if (actionMasked == MotionEvent.ACTION_DOWN) {
cancelAndClearTouchTargets(ev);
resetTouchState();
}
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action);
} else {
intercepted = false;
}
} else {
intercepted = true;
}
if (intercepted || mFirstTouchTarget != null) {
ev.setTargetAccessibilityFocus(false);
}
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
if (!canceled && !intercepted) {
View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
? findChildWithAccessibilityFocus() : null;
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
final int actionIndex = ev.getActionIndex();
final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
: TouchTarget.ALL_POINTER_IDS;
removePointersFromTouchTargets(idBitsToAssign);
final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
final ArrayList preorderedList = buildOrderedChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = customOrder
? getChildDrawingOrder(childrenCount, i) : i;
final View child = (preorderedList == null)
? children[childIndex] : preorderedList.get(childIndex);
if (childWithAccessibilityFocus != null) {
if (childWithAccessibilityFocus != child) {
continue;
}
childWithAccessibilityFocus = null;
i = childrenCount - 1;
}
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
continue;
}
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}
resetCancelNextUpFlag(child);
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
mLastTouchDownTime = ev.getDownTime();
if (preorderedList != null) {
for (int j = 0; j < childrenCount; j++) {
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
} else {
mLastTouchDownIndex = childIndex;
}
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
ev.setTargetAccessibilityFocus(false);
}
if (preorderedList != null) preorderedList.clear();
}
if (newTouchTarget == null && mFirstTouchTarget != null) {
newTouchTarget = mFirstTouchTarget;
while (newTouchTarget.next != null) {
newTouchTarget = newTouchTarget.next;
}
newTouchTarget.pointerIdBits |= idBitsToAssign;
}
}
}
if (mFirstTouchTarget == null) {
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
while (target != null) {
final TouchTarget next = target.next;
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
} else {
final boolean cancelChild = resetCancelNextUpFlag(target.child)
|| intercepted;
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
}
if (cancelChild) {
if (predecessor == null) {
mFirstTouchTarget = next;
} else {
predecessor.next = next;
}
target.recycle();
target = next;
continue;
}
}
predecessor = target;
target = next;
}
}
if (canceled
|| actionMasked == MotionEvent.ACTION_UP
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
resetTouchState();
} else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
final int actionIndex = ev.getActionIndex();
final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
removePointersFromTouchTargets(idBitsToRemove);
}
}
if (!handled && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
}
return handled;
}
前面我们知道触摸事件是由一个触摸按下事件,一个触摸抬起事件和N个触摸滑动事件组成的,而这里的触摸按下事件就是这里的ACTION_DOWN,同时友谊ACTION_DOWN是一系列事件的开端,所以我们在ACTION_DOWN时进行一些初始化操作,从上面源码中注释也可以看出来,清除以往的Touch状态然后开始新的手势。并在在cancelAndClearTouchTargets(ev)方法中将mFirstTouchTarget设置为了null,接着在resetTouchState()方法中重置Touch状态标识。
然后标记ViewGroup是否拦截Touch事件的传递,if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null)这一条判断语句说明当事件为ACTION_DOWN或者mFirstTouchTarget不为null(即已经找到能够接收touch事件的目标组件)时if成立,否则if不成立,然后将intercepted设置为true,也即拦截事件。这里说明一下ViewGroup中的onInterceptTouchEvent方法是ViewGroup中特有的方法用于表示是否拦截触摸事件,返回为true的话则表示拦截事件,事件不在向子View中分发,若范围为false的话,则表示不拦截事件,继续分发事件。
public boolean onInterceptTouchEvent(MotionEvent ev) {
return false;
}
一般的我们可以在自定义的ViewGroup中重写该方法,用于拦截事件的分发。而当我们在父ViewGroup重写该方法返回为true执行事件拦截的逻辑的时候,可以在子View中通过调用requestDisallowInterceptTouchEvent方法,重新设置父ViewGroup的onInterceptTouchEvent方法为false,不拦截对事件的分发逻辑。
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {
return;
}
if (disallowIntercept) {
mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
} else {
mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
}
if (mParent != null) {
mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
}
}
比如常见的向我们的ViewPager中由于需要处理左右滑动事件从而在其onInterceptTouchEvent方法中重写了返回值,返回为true,拦截对事件的处理逻辑,但是若这时候ViewPager中嵌套了ListView,则listView也需要处理触摸事件的逻辑,但是ViewPager中已经重写了onInterceptTouchEvent方法,这时候怎么办呢?幸运的是ListView也在内部的实现中调用了requestDisallowInterceptTouchEvent方法,保证自身获得对触摸事件的处理。
然后在代码中我们判断childrenCount个数是否不为0,继续我们获取子View的list集合preorderedList;最后通过一个for循环倒序遍历所有的子view,这是因为preorderedList中的顺序是按照addView或者XML布局文件中的顺序来的,后addView添加的子View,会因为Android的UI后刷新机制显示在上层;假如点击的地方有两个子View都包含的点击的坐标,那么后被添加到布局中的那个子view会先响应事件;也就是说后被添加的子view会浮在上层,点击的时候最上层的那个组件先去响应事件。
然后代码通过调用getTouchTarget去查找当前子View是否在mFirstTouchTarget.next这条target链中的某一个targe中,如果在则返回这个target,否则返回null。在这段代码的if判断通过说明找到了接收Touch事件的子View,即newTouchTarget,那么,既然已经找到了,所以执行break跳出for循环。如果没有break则继续向下执行,这里你可以看见一段if判断的代码if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)),那么这个方法又是执行什么逻辑的呢?
在该方法中为一个递归调用,会递归调用dispatchTouchEvent()方法。在dispatchTouchEvent()中如果子View为ViewGroup并且Touch没有被拦截那么递归调用dispatchTouchEvent(),如果子View为View那么就会调用其onTouchEvent()。dispatchTransformedTouchEvent方法如果返回true则表示子View消费掉该事件。
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
final int oldAction = event.getAction();
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
event.setAction(MotionEvent.ACTION_CANCEL);
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
return handled;
}
final int oldPointerIdBits = event.getPointerIdBits();
final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
if (newPointerIdBits == 0) {
return false;
}
final MotionEvent transformedEvent;
if (newPointerIdBits == oldPointerIdBits) {
if (child == null || child.hasIdentityMatrix()) {
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
event.offsetLocation(offsetX, offsetY);
handled = child.dispatchTouchEvent(event);
event.offsetLocation(-offsetX, -offsetY);
}
return handled;
}
transformedEvent = MotionEvent.obtain(event);
} else {
transformedEvent = event.split(newPointerIdBits);
}
if (child == null) {
handled = super.dispatchTouchEvent(transformedEvent);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
transformedEvent.offsetLocation(offsetX, offsetY);
if (! child.hasIdentityMatrix()) {
transformedEvent.transform(child.getInverseMatrix());
}
handled = child.dispatchTouchEvent(transformedEvent);
}
transformedEvent.recycle();
return handled;
}
然后在在ViewGroup的dispatchTransformedTouchEvent方法中,调用了该ViewGroup的child View的dispatchTouchEvent方法,若其子View也是ViewGroup,则重复执行ViewGroup的dispatchTouchEvent方法,若其子View是View,则执行View的dispatchTouchEvent方法。
但这里大概分析了一下ViewGroup的事件分发流程
- 首先在android的事件分发流程中,通过调用Activity的dispatchTouchEvent,事件会首先被派发是先传递到最顶级的DecorView也就是ViewGroup,再由ViewGroup递归传递到View的。
- 在ViewGroup中可以通过设置onInterceptTouchEvent方法对事件传递进行拦截,onInterceptTouchEvent方法返回true代表不允许事件继续向子View传递,返回false代表不对事件进行拦截,默认返回false。
下面我们继续看一下View的dispatchTouchEvent方法。
public boolean dispatchTouchEvent(MotionEvent event) {
...
if (onFilterTouchEventForSecurity(event)) {
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
if (!result && onTouchEvent(event)) {
result = true;
}
}
...
return result;
}
View的dispatchTouchEvent方法的内容比较长,我们重点看一下View对触摸事件的处理逻辑,首先调用了onFilterTouchEventForSecurity(event)方法判断当前的View是否被遮盖,若没有的话,则判断View的mListenerInfo城边变量是否为空,而这里的mListenerInfo又是什么呢?通过分析源码我们知道这里的mListenerInfo是通过setOnClickListener方法设置的。
public void setOnClickListener(@Nullable OnClickListener l) {
if (!isClickable()) {
setClickable(true);
}
getListenerInfo().mOnClickListener = l;
}
可以当前View一旦执行了setOnClickListener方法改View的mListenerInfo就不为空,若后有判断了该View是否可点击,最后是判断View的onTouchListener的onTouch方法的返回值。
所以当我们为当前View设置了OnTouchListener并且返回值为true的话,则直接执行其onTouch方法,若onTouch方法返回为true的话,则直接返回不在执行后续的View的onTouchEvent方法,否则继续执行View的onTouchEvent方法,而我们继续看一下View的onTouchEvent方法的实现逻辑。
public boolean onTouchEvent(MotionEvent event) {
final float x = event.getX();
final float y = event.getY();
final int viewFlags = mViewFlags;
final int action = event.getAction();
if ((viewFlags & ENABLED_MASK) == DISABLED) {
if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
setPressed(false);
}
return (((viewFlags & CLICKABLE) == CLICKABLE
|| (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
|| (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE);
}
if (mTouchDelegate != null) {
if (mTouchDelegate.onTouchEvent(event)) {
return true;
}
}
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
(viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
switch (action) {
case MotionEvent.ACTION_UP:
boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
boolean focusTaken = false;
if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
focusTaken = requestFocus();
}
if (prepressed) {
setPressed(true, x, y);
}
if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
removeLongPressCallback();
if (!focusTaken) {
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClick();
}
}
}
if (mUnsetPressedState == null) {
mUnsetPressedState = new UnsetPressedState();
}
if (prepressed) {
postDelayed(mUnsetPressedState,
ViewConfiguration.getPressedStateDuration());
} else if (!post(mUnsetPressedState)) {
mUnsetPressedState.run();
}
removeTapCallback();
}
mIgnoreNextUpEvent = false;
break;
case MotionEvent.ACTION_DOWN:
mHasPerformedLongPress = false;
if (performButtonActionOnTouchDown(event)) {
break;
}
boolean isInScrollingContainer = isInScrollingContainer();
if (isInScrollingContainer) {
mPrivateFlags |= PFLAG_PREPRESSED;
if (mPendingCheckForTap == null) {
mPendingCheckForTap = new CheckForTap();
}
mPendingCheckForTap.x = event.getX();
mPendingCheckForTap.y = event.getY();
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
} else {
setPressed(true, x, y);
checkForLongClick(0);
}
break;
case MotionEvent.ACTION_CANCEL:
setPressed(false);
removeTapCallback();
removeLongPressCallback();
mInContextButtonPress = false;
mHasPerformedLongPress = false;
mIgnoreNextUpEvent = false;
break;
case MotionEvent.ACTION_MOVE:
drawableHotspotChanged(x, y);
if (!pointInView(x, y, mTouchSlop)) {
removeTapCallback();
if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
removeLongPressCallback();
setPressed(false);
}
}
break;
}
return true;
}
return false;
}
在ACTION为MotionEvent.ACTION_UP时,我们经过层层调用最终执行了performClick,方法而这个方法中我们回调了View的OnClickListener的onClick方法。。。
public boolean performClick() {
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
return result;
}
所以View组件分发触摸事件的时候:
View控件会首先执行dispatchTouchEvent方法。
View控件在dispatchTouchEvent方法中先执行onTouch方法,后执行onClick方法。
View的onTouch返回false或者mOnTouchListener为null(控件没有设置setOnTouchListener方法)或者控件不是enable的情况下会调运onTouchEvent,dispatchTouchEvent返回值与onTouchEvent返回一样。
View控件不是enable的,那么即使设置了onTouch方法也不会执行,只能通过重写控件的onTouchEvent方法处理,dispatchTouchEvent返回值与onTouchEvent返回一样。
如果控件(View)是enable且onTouch返回true情况下,dispatchTouchEvent直接返回true,不会调用onTouchEvent方法。
参考:
blog.csdn.net/xiaanming/a…
blog.csdn.net/guolin_blog…
blog.csdn.net/guolin_blog…
另外对android源码解析方法感兴趣的可参考我的:
android源码解析之(一)–>android项目构建过程
android源码解析之(二)–>异步消息机制
android源码解析之(三)–>异步任务AsyncTask
android源码解析之(四)–>HandlerThread
android源码解析之(五)–>IntentService
android源码解析之(六)–>Log
android源码解析之(七)–>LruCache
android源码解析之(八)–>Zygote进程启动流程
android源码解析之(九)–>SystemServer进程启动流程
android源码解析之(十)–>Launcher启动流程
android源码解析之(十一)–>应用进程启动流程
android源码解析之(十二)–>系统启动并解析Manifest的流程
android源码解析之(十三)–>apk安装流程
android源码解析之(十四)–>Activity启动流程
android源码解析之(十五)–>Activity销毁流程
android源码解析(十六)–>应用进程Context创建流程
android源码解析(十七)–>Activity布局加载流程
android源码解析(十八)–>Activity布局绘制流程
android源码解析(十九)–>Dialog加载绘制流程
android源码解析(二十)–>Dialog取消绘制流程
android源码解析(二十一)–>PopupWindow加载绘制流程
android源码解析(二十二)–>Toast加载绘制流程
android源码解析(二十三)–>Android异常处理流程
android源码解析(二十四)–>onSaveInstanceState执行时机
android源码解析(二十五)–>onLowMemory执行流程
android源码解析(二十六)–>截屏事件流程
android源码解析(二十七)–>HOME事件流程
android源码解析(二十八)–>电源开关机按键事件流程
android源码解析(二十九)–>应用程序返回按键执行流程
本文以同步至github中:github.com/yipianfengy…,欢迎star和follow
转载请标明出处:一片枫叶的专栏
从这篇文章开始我们暂停一下对Android源码的分析,开始讲一下Android产品研发中一些常用的技术,技巧,方法,实践等姿势。这里需要强调的是我们所讲解的这些东西可能对产品开发中比较常用的,因为对于项目开发中,可能更多的强调管理,进度方法的东西,对工程化的东西比较强调,而我们这里更多的是对产品技术方面的归纳总结。
而本文中选择将开发规范作为这个系列的第一篇文章,就是个人感觉产品研发过程中,开发规范真的很重要,很重要,非常重要(重要的事情说三遍),一个好的开发规范可以让团队中的人对他人的代码更熟悉,新人也可以更好的了解产品的业务逻辑。开发规范并不是一个死的一成不变的,每个团队可能都有自己的开发规范,只要是适合团队的开发规范就是最好的开发规范。
所以本文中所讲解的开发规范只能是抛砖引玉,有可取的地方可以借鉴,引用,不能照搬全抄不假思索,毕竟不同的团队有不同的实际情况。最好的方式就是可以根据本文的开发规范总结出自身团队比较适合的规范流程。
好吧,废话不多说了,下面我们就介绍一下我在实践中总结的Android开发规范。
1 前言
1.1 为什么需要开发规范
编码规范对于程序员而言尤为重要,有以下几个原因:
* 一个软件的生命周期中,80%的花费在于维护
* 几乎没有任何一个软件,在其整个生命周期中,均由最初的开发人员来维护
* 编码规范可以改善软件的可读性,可以让程序员尽快而彻底地理解新的代码
* 如果你将源码作为产品发布,就需要确任它是否被很好的打包并且清晰无误,一如你已构建的其它任何产品
1.2 开发规范的作用
* 减少维护花费
* 提高可读性
* 加快工作交接
* 减少名字增生
* 降低缺陷引入的机会
2 命名规范
2.1 常量命名规范
2.1.1 类型
常量命名规范
2.1.2 说明
常量用于保存需要常驻内存中并且经常使用变化不多的数据,定义常量的名称的时候需要遵循望文知意的原则;
2.1.3 规则
-
-
-
2.1.4 备注
代码中涉及到直接使用某个字符串或者其他基本类型的值时,建议定义成常量,避免多处直接使用同样的值作为参数。
2.1.5 举例
- 如:定义一个常量表示最小屏幕宽度的常量,则可以定义一个int类型的常量,该常量可以命名为:“MIN_SCREEN_WIDTH“;
- 其他举例:
- 例如:static final int MIN_SCREEN_WIDTH = 4;( √)
- 例如:static final int min_screen_width = 4;(×)
- 例如:static final int minScreenWidth = 4; (×)
- 例如:static final int WIDTH = 4;(×)
- 例如:static final int width = 4;(×)
- 例如:static final int wd = 4;(×)
2.2 变量命名规范
2.2.1 类型
变量命名规范
2.2.2 说明
变量用于保存系统中的临时数据,变量命名时遵循望文知意,简单明了,驼峰标示等原则。
2.2.3 规则
- 首字母小写;
- java驼峰命名;
- 望文知意原则;
- 推荐引用类型变量添加前缀“m”;
- 如果是View组件变量,则组件名称为xml文件中定义的ID名称去掉下划线,下划线后一位大写;
2.2.4 备注
2.2.5 举例
- 如:定义一个表示最小屏幕宽度的变量,则可以定义一个int型的临时变量为:mMinScreenWidth;
- 例如:static final int mMinScreenWidth = 4; ( √)
- 例如:static final int minWidth = 4;(×)
- 例如:static final int screenWidth = 4;(×)
- 例如:static final int width = 4;(×)
- 例如:static final int min = 4; (×)
- 例如:static final int msw = 4; (×)
2.3 方法命名规范
2.3.1 类型
方法命名规范
2.3.2 说明
方法名的命名应该遵循简单明了的原则;
2.3.3 规则
- 首字母小写;
- java驼峰命名;
- 简单明了原则;
- 初始化View方法init*(每个init做一件事)
2.3.4 备注
- 同时在方法的实现上,尽量不要在一个方法中出现太多实现代码,如一个方法有几百行的实现逻辑,推荐在逻辑复杂时,按功能点拆分出多个方法,便于阅读。
- 另外,出现功能一样的实现逻辑,尽量抽取公用方法,避免将实现逻辑复制到多个用到的地方。
2.3.5 举例
- 如:定义一个获取屏幕宽度的方法,依照上述原则,则可以定义为一个静态方法:public static int getScreenWidth();
- 例如:public static int getScreenWidth();( √)
- 例如:public static int getscreenwidth();(×)
- 例如:public static int getScreenwidth();(×)
- 例如:public static int getWidth();(×)
- 例如:public static int getScreen();(×)
- 例如:public static int getSW();(×)
2.4 类命名规范
2.4.1 类型
类命名规范
2.4.2 说明
类名主要表示一个类的作用,需要简明扼要,望文知意,并且首字母大写。
2.4.3 规则
- 首字母大写;
- java驼峰命名;
- 望文知意原则;
- 能够说明类的功能和主要作用(注释的作用);
- Acitivity类以Acitivity结尾;
- Fragment类以Fragment结尾;
- Service类以Service结尾;
- BroadcastReceiver类以Receiver结尾;
- ContentProvider类以Provider结尾;
- Application类以Application结尾;
- 自定义View类以Custom**View结尾;
- 自定义Adapter类以Adapter结尾;
- adapter中的ViewHolder以Holder结尾;
- 实体Bean以Model结尾;
2.4.4 备注
2.4.5 举例
- 如:定义一个获取屏幕信息的工具类,则可以定义为public class ScreenUtils;
- 例如:public class ScreenUtils; ( √)
- 例如:public class Screenutils; (×)
- 例如:public class Screen; (×)
- 例如:public class screenutils; (×)
- 例如:public class screen; (×)
- 例如:public class su;(x)
2.5 接口命名规范
2.5.1 类型
接口命名规范
2.5.2 说明
接口命名需要简单明了,长度不宜过长;
2.5.3 规则
- 首字母大写(第二个字母也是大写);
- java驼峰命名;
- 望文知意原则;
- 建议在名称前面追加“I”;
2.5.4 备注
- I**Listener
- I**CallBack
- I**;
2.5.5 举例
- 如:定义一个activity的方法接口,实现接口中的某些方法:public
interface IFunctionListener;
- 例如:public interface IFunctionListener;( √)
- 例如:public interface BaseActivity; (×)
- 例如:public interface Baseactivityinter; (×)
- 例如:public interface BaseInter; (×)
- 例如:public interface ActivityInter;(×)
2.6 包名规范
2.6.1 类型
包名规范
2.6.2 说明
用于分类管理类文件;
2.6.3 规则
- 所有字母小写;
- 简单明了,层级很深,没有拼接的包名;
- 望文知意;
- 按功能划分包名,如“我的”
- 工具类可以划分为一个工具类的包名,utils,里面可以添加包名层级;
- 系统类的可以划分为一个系统类的包,system,里面可以添加包名层级;
- 组件类的可以划分为一个组件类的包,*,里面添加adapter的包名,自定义view包名;
- Service类的可以划分为一个服务类的包,service,里面可以添加包名层级;
- 数据库相关类可以划分为一个数据库类,db,里面可以添加数据库相关类,Bean类,数据库服务类等;
- 广播类的可以划分为广播类的包,receiver,可以放一些广播相关的类;
- 网络类相关的可以划分为,network,放一些网络相关的类;
- Fragment类存放在fragment包下;
- Activity类存放在Activity包下;
2.6.4 备注
2.6.5 举例
2.7 目录名称规范
2.7.1 类型
目录名称规范
2.7.2 说明
主要是一些jar包,so文件的配置目录名称;
2.7.3 规则
- 全部为小写字母;
- 简单明了;
- 望文知意;
- 驼峰表示;
2.7.4 备注
2.7.5 举例
- 后期增加目录的可能性不多,现列举出系统中存在的目录结构:
- lib:第三方jar的保存路径;
- jniLibs:jni引用的so文件的目录;
2.8 布局文件名称规范
2.8.1 类型
布局文件名称规范
2.8.2 说明
主要包含资源文件的命名问题;
2.8.3 规则
- 全部为小写字母;
- 中间以”_”连接;
- 望文知意原则;
- 布局文件的开头问类名;
- 列表项的xml布局文件名称:类名_item.xml;
- activity类的xml文件名称:类名_activity.xml;
- fragment类的xml文件名称:类名_fragment.xml;
- 自定义View的xml文件的名称:类名_父类名.xml;
2.8.4 备注
2.8.5 举例
如:如定义H5Activity的xml文件名称,则可以定义为h5.xml;尽量不使用大写字母等。
2.9 drawable文件名称规范
2.9.1 类型
drawable文件名称命名规范
2.9.2 说明
主要包含资源文件的命名问题;
2.9.3 规则
- 全部为小写字母;
- 中间以”_”连接;
- 望文知意原则;
- 布局文件的开头问类名;
- 11_22_33_44,44:selector,shape(大概五六个,暂时不定义其他的); 33:src、bg、color(可扩展,可为空); 22:状态名称或者为空;11:业务名称
2.9.4 备注
2.9.5 举例
2.10 资源ID规范
2.10.1 类型
资源ID命名规范
2.10.2 说明
各种资源ID的定义问题;
2.10.3 规则
- 全部为小写字母;
- 中间以”_”连接;
- 望文知意原则;
2.10.4 备注
可以考虑按照组件的名称的缩写作为前缀,(同一个xml文件中ID名称不能重复)如:组件简写(大写字母缩写)_业务名称
TextView的组件:tv_pay_money
Button的组件:btn_pay_money
EditText的组件:et_user_name
LinerLayout组件:ll_container
2.10.5 举例
如:比如一个textview组件,可点击用于支付的按钮,则可以把ID定义为: tv_pay_money;
3 注释规范
3.1 类注释
在类、接口定义之前当对其进行注释,包括类、接口的目的、作用、功能、继承于何种父类,实现的接口、实现的算法、使用方法、示例程序等。
/**
* author:作者
* time:时间
* desc:描述
*/
3.2 方法注释
方法注释的模板:
/**
* desc:描述
* @param 参数名 参数描述
* @param 参数名2 参数描述
* @return 返回值类型说明
* @throws Exception 异常说明
*/
3.3 类成员变量和常量注释
成员变量和常量需要使用如下注释的形式,注释位于变量的上侧;
/**
*
**/
3.4 内部逻辑注释
内部逻辑注释模板:
//支付成功
if (response.getRet() == 0) {
Toast.makeText(H5Activity.this, "支付成功", Toast.LENGTH_LONG).show();
goToNext(response);
}
//支付失败
else if (response.getRet() == -1) {
Toast.makeText(H5Activity.this, "支付失败", Toast.LENGTH_LONG).show();
//刷新当前页面
reflush(currentUrl);
}
4 代码顺序
4.1 代码顺序
在一个典型的Activity中代码的顺序如下:
/**
* author:sh
* desc:该class的作用
* time:yyyy-MM-dd
**/
public class ClassName {
//(1) 成员变量集合
//(2) 回调方法集合
若该类为activity,则:onCreate、**、onDestory;
若该类为Fragment、则:onCreateView、**、onDestory;
//(3) 其他方法集合
}
5 代码风格
5.1 大括号换行
左大括号不换行,右大括号换行;
class MyClass {
int func() {
if (something) {
// ...
} else if (somethingElse) {
// ...
} else {
// ...
}
}
}
5.2 小括号空格
if (condition) {
body();
} // 推荐
5.3 缩进
- 4 个空格作为缩进排版的一个单位,不使用制表符 tab。
- 8 个空格作为换行后的缩进,包括函数调用和赋值。
- Instrument i =
someLongexpression_r(that, NotFit, on, one, line); // 推荐
5.4 每一行的长度
- 尽量避免一行的长度超过 100 个字符。
- 例外:如果注释行包含了超过 100 个字符的命令示例或者 url 文字,为了便于剪切和复制,其长度可以超过 100 个字符。
- 例外:import 行可以超过限制,因为很少有人会去阅读它。这也简化了编程工具的写入操作。
5.5 每次声明一个变量
- 推荐一行一个声明,因为这样以利于写注释;
- int level; // indentation level
- int size; // size of table
5.6 if-else语句
if-else语句应该具有如下格式:
if (condition) {
statements;
}
if (condition) {
statements;
} else {
statements;
}
if (condition) {
statements;
} else if (condition) {
statements;
} else{
statements;
}
注意:if语句总是用”{“和”}“括起来,避免使用如下容易引起错误的格式:
if (condition) // 避免
statement;
5.7 for语句
一个for语句应该具有如下格式:
for (initialization; condition; update) {
statements;
}
当在for语句的初始化或更新子句中使用逗号时,避免因使用三个以上变量,而导致复杂度提高。
若需要,可以在for循环之前(为初始化子句)或for循环末尾(为更新子句)使用单独的语句。
5.8 while语句
一个while语句应该具有如下格式:
while (condition) {
statements;
}
5.9 do-while语句
do {
statements;
} while (condition);
5.10 switch语句
一个switch语句应该具有如下格式:
switch (condition) {
case ABC:
statements;
/* falls through */
case DEF:
statements;
break;
case XYZ:
statements;
break;
default:
statements;
break;
}
每当一个case顺着往下执行时(因为没有break语句),通常应在break语句的位置添加注释。
6 异常规范
6.1 异常名称
定义异常的时候,异常的后缀名称以Exception结尾,及**Exception;
6.2 异常描述
尽量英文描述,简单明了;
6.3 异常格式
一个try-catch语句应该具有如下格式:
try {
statements;
} catch (ExceptionClass e) {
statements;
}
try {
statements;
} catch (ExceptionClass e) {
statements;
} finally {
statements;
}
7 其他规范
7.1 源文件的函数小于2K
一般来说源文件的行数不能大于2K行,过多的话可以考虑拆分功能,拆分函数等;
7.2 使用TODO注释
- 对那些临时性的、短期的、够棒但不完美的代码,请使用 TODO 注释。
- TODO 注释应该包含全部大写的 TODO,后跟一个冒号:
- // TODO: Remove this code after the UrlTable2 has been checked in.
- // TODO: Change this to use a flag instead of a constant.
如果 TODO 注释是“将来要做某事”的格式)。
7.3 使用自定义LOG
在系统中需要打印LOG的时候,尽量使用自定义的LOG,自定义的LOG在开发环境的时候会打印日志,正式环境的时候不会打印日志。
7.4 使用自定义TAG
在系统打印LOG的时候,使用TAG尽量使用tab,同意的TAG标志。
另外对产品研发技术,技巧,实践方面感兴趣的同学可以参考我的:
Android产品研发–>总结(持续更新)
本文以同步至github中:github.com/yipianfengy…,欢迎star和follow
转载请标明出处:一片枫叶的专栏
上一篇文章中我们介绍加单说明了一下Android的编码规范,这里我是强烈建议大家在团队合作中约定编码规范的,哪怕是一个并不是十分规范的规范总比没有规范好得多,尤其是团队产品的研发,对产品的持续迭代过程中你会越发的意识到编码规范对产品迭代的好处,当然了,这里并不是要求大家一定按照文中给出的编码规范作为团队中使用的编码规范,而是希望大家在团队合作中能够约定出自身的编码规范,哪怕其并不是十分的规范,具体的编码规范可参考:Android产品研发(一)–>实用开发规范。
而这篇文章中我们主要介绍一下启动页的优化,关于启动页的优化是UE方面的内容了,一个很慢的启动页很容易让用户觉得受不了,进而“逃离”App的,所以若想产品有更好的用户体验,做一些启动页的优化是一个不错的选择。这里我们简单介绍一下我在实践中对启动页是如何优化的。
公司产品使用的数据统计产品是友盟,最近产品经理在看友盟数据的时候,发现App在启动页跳出率很高,启动页的平均启动时间为2.8s,如下图:

纳尼,2.8s才启动App,这是杂么回事啊,优化吧…
没办法,2.8s启动启动页时间确实有点长,而且用户在首页的跳出率也确实有点高(这里普及一下跳出率的概念:用户从当前页面离开应用的情况,说白了就是启动页的启动时间过长,不少用户都等不了了,直接从启动页离开App了)。
那么在优化启动也的时候我们应该逐步的分析启动页的逻辑,熟悉了启动页的执行逻辑才能更好的优化启动页的加载速度。
- 启动页面网络请求优化
(1)我们在启动页做了什么?
我们的启动页面主要用于展示启动页面,加载网络配置信息;
具体而言:
加载App中h5页面的url地址
加载请求用户信息
加载首页地图页面需要展示的图标
延时三秒钟跳转首页面,这时候无论是否加载到前面的网络请求信息,都会跳转到首页面
(2)那些可以优化的地方?
这样看来由于我们在启动页面有网络请求,所以主动的延时3秒钟跳转首页,所以造成了我们的启动页面加载过慢,所以我们首要的优化地方就是在网络请求与延时跳转这方面。
(3)如何优化?
现在的逻辑是无论配置信息是否拉取成功都需要在3秒钟的时候跳转主页面,可以这样考虑,若这三个配置信息拉取的时间小于3秒钟其实不必要等到3秒钟在跳转主界面;思路已经出来了,剩下的就是具体实现了。。
(4)启动页面网络请求优化的具体实践
timer = new Timer();
timerTask = new TimerTask() {
@Override
public void run() {
if (isGoToComplete) {
L.i("等待2s完成,取消timer任务...");
cancelTimer();
return;
}
if (isLoadStartQueryComplete && isLoadUserInfoComplete) {
L.i("StartActivity中网络请求完成,执行跳转逻辑.....");
handler.sendEmptyMessage(eventWhat);
L.i("StartActivity中网络请求完成,取消timer任务...");
cancelTimer();
}
}
};
timer.schedule(timerTask, 0, 100);
在这里我们创建一个timer任务,每隔100ms执行一次,判断这两个拉取网络配置信息的异步消息是否已经完成,若已经完成的话则发送handler消息跳转主界面,若2秒钟之内都没有完成拉取任务,则直接跳转同时取消timer任务。
简单来说就是启动页面最长的等待时间为2秒钟,若两秒钟之内启动页面的网络请求信息还没有完成也不在等待直接跳转主页面,而若在2秒钟之内启动页面的网络请求执行完毕,则直接跳转不必等到2秒钟之后再跳转,这样我们启动页面就根据网络请求的情况大大缩短了加载时间。
- 启动页面特效优化
现如今几乎所有的安卓app在启动的时候都会有一个类似于开机画面的东西,往往是一个开机界面,上面写着这个应用程序的提示文字,动画等等,并且这个开解界面往往实现了渐变,loading等效果,这样启动页面的耗时比较长的话,用户也不会觉得时间很长,难以忍受,当然了这种方式只是在视觉效果上造成“启动页面加载速度很快”的效果的。
这里我们简单的实现启动页面的动画效果的实现:
AlphaAnimation alphaAnimation = new AlphaAnimation(0.3f,1.0f);
alphaAnimation.setDuration(2000);
contentView.startAnimation(alphaAnimation);
alphaAnimation.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationEnd(Animation arg0) {
L.i("启动页面动画执行完毕...");
}
@Override
public void onAnimationRepeat(Animation animation) {
L.i("启动页面动画重复执行...");
}
@Override
public void onAnimationStart(Animation animation) {
L.i("启动页面动画开始执行...")
}
});
而这里的contentView就是我们启动页Activity的主View,所以这里的执行效果就是我们的启动页Activity的界面的透明度逐渐增加,这样在用户看来由于有了一层loading效果,加载速度显得很快。
- 启动页面无黑屏
这里同时也介绍一下Android app启动页面黑屏的问题,Android开发app启动时若没有做特殊处理的话,会出现一瞬间的黑屏现象,网上针对这种情况一般有两种解决方式:
(1)App启动页面为图片或者是drawable文件
@drawable/start_background
设置设置整个App的style文件,设置Android:windowBackground属性为图片或者是drawable文件,这样App在启动过程中就是显示的是启动页面的图片或者是是drawable文件;
(2)App启动页面为Layout文件
这时候无法为App的style设置background,退而求其次主流的方式是设置白色背景
@color/c10
而这里的@color/c10就是我们定义的白色色值,这样经过处理之后App启动时就不会出现黑屏的效果了。
- Application启动速度优化
由于我们的应用启动过程首先会执行Application的创建于执行其生命周期方法(onCreate方法),具体可参见我的Android应用进程启动流程: Android源码解析之(十一)–>应用进程启动流程
所以提高app的启动速度,加快Application的执行时间也是一个很重要的方面,这里我暂时总结了几条原则:
尽量不将一些业务逻辑放于Application中;
Application尽量不以静态变量的方式保存应用数据;
若App的大小不是特别大无需使用dex分包方案;
在Application中关于文件,数据库的操作尽量
- 启动页面屏蔽返回按键
一般的我们的App中都会在启动页面执行一些网络操作,初始化配置等,所以这时候我们是不希望用户通过按下返回按键退出App,因而我们需要在启动页中屏蔽返回按键,这里简单的介绍一下具体的实现:
/**
* startActivity屏蔽物理返回按钮
*
* @param keyCode
* @param event
* @return
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
return true;
}
return super.onKeyDown(keyCode, event);
}
这里我们重写启动页面Activity的onKeyDown方法,首先判断用户按下的是否是返回按键,若是的话则直接返回true,屏蔽返回按键的后续执行逻辑。关于返回按键的执行流程,可参考我的Android系统返回按键执行流程:Android源码解析(二十九)–>应用程序返回按键执行流程
以上就是个人总结的启动页中一些要点或者是需要注意的地方,个人能力不足有不对的地方欢迎指正。
另外对产品研发技术,技巧,实践方面感兴趣的同学可以参考我的:
Android产品研发–>总结(持续更新)
Android产品研发(一)–>实用开发规范
本文以同步至github中:github.com/yipianfengy…,欢迎star和follow
转载请标明出处:一片枫叶的专栏
在上一篇文章中我们介绍了在Android产品研发过程中,启动页的优化工作,比如启动页性能优化,启动页渐进动画效果,启动页屏蔽返回按键等等,而在本文中我们将要介绍一下在App产品研发中都会复写的基类Activity,具体可参考:Android产品研发(二)–>启动页优化。
在实际的Android产品研发中,一般的我们在写Activity的时候都会继承于一个基类Activity,该Activity是所有的Activity的基类。在该基类中我们主要用于重写一些共有的逻辑。好处是显而易见的对于一些Activity的共有逻辑我们不必要在每个Activity中都重新写一遍,只需要在基类Activity中写一遍就好了。
下面我就讲解一下在产品研发过程中我对基类Activity的设计。
(一)基类Activity是如何使用的?
定义一个BaseActivity,让App中所有的Activity都继承于BaseActivity;
(二)基本Activity包含的内容
- 在BaseActivity的生命周期中复写友盟数据统计方法。
用过友盟数据统计的同学应该知道,为了统计每个页面的点击事件,页面访问路径,异常信息等我们需要在Activity的生命周期方法中添加友盟的API,这些都是一些相同的逻辑,难道我们需要在每个Activity中都重写这些友盟API么?答案当然是否定的,友盟官方也不建议我们这么做,我们可以在一个基类Activity的生命周期方法中重写这些友盟API,并让我们需要统计的页面继承于该基类Activity。
@Override
protected void onResume() {
MobclickAgent.onResume(this);
}
@Override
protected void onPause() {
MobclickAgent.onPause(this);
}
(其他生命周期方法暂略)
...
由于我们需要统计不同的Activity页面的数据,所以我们需要在各个Activity的生命周期中都要重写友盟的API,那么这里我们就完全可以将这种重复性的动作下发到BaseActivity中,而让所有的Activity都继承于BaseActivity,进而我们的Activity就在生命周期方法中重写了友盟的API。
- 在生命周期方法中保存Context对象,保存Activity栈
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this;
Config.setActivityState(this);
...
}
public static void setActivityState(Activity activity) {
currentContext = activity;
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
UUApp.getInstance().addActivity(activity);
}
@Override
protected void onDestroy() {
super.onDestroy();
UUApp.getInstance().removeActivity(this);
}
因为我们在Activity页面中经常需要使用“this”,但是到处都是用this显得不太好,这时候我们可以在BaseActivity中定义一个Actiivty类型的mContext成员变量并在BaseActivity中的onCreate方法中赋值为this,这样我们在子Activity中需要使用this的地方直接使用mContext成员变量就好了。
并且我们在内存中保存了一个Activity的自定义栈,正常的做法是在Activity的onCreate方法中执行入栈操作,在Activity的onDestory方法中执行出栈操作,由于这些都是共有的逻辑因此我们把这两个操作放到BaseActivity中。
- 在生命周期方法中对事件总线框架EventBus进行注册和反注册
使用过EventBus的同学应该知道其能够实现事件传递的核心机制就是将Activity对象“注册”到EventBus中,但是有注册也必须要有反注册的机制,因为Activity不是常驻内存的,Activity也有销毁的时候,这时候就需要从EventBus中“反注册”,一个比较好的方式就是在Activity
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
EventBus.getDefault().register(this);
...
}
@Override
protected void onDestroy() {
super.onDestroy();
...
EventBus.getDefault().unregister(this);
...
}
/**
* event 更改显示登陆状态
*/
public void onEventMainThread(BaseEvent event) {
...doSomething...
}
这样我们就不必要单独在Activity中重写EventBus的注册和反注册逻辑了。
- 在onResume方法中执行更新登录用户票据更新的操作
在App端保存了用户的token信息,但是这里的token信息不是一直有效的,有效期为30天,所以这里就需要有一个更新token的操作,App现在的更新操作是判断token的有效期是否过了一半,即15天,若过了一半了,则在新打开的Activity时执行更新票据操作,所以这也是共有的逻辑,即在BaseActivity的onResume中执行更新票据的操作。
@Override
protected void onResume() {
super.onResume();
/**
* 验证用户票据是否失效,失效的话则默认执行更换票据操作
* startActivity中不需要更新票据,否则起始页有可能会弹出登陆提示框
*/
if (UserConfig.isNeedUpdateTicket() && (!(this instanceof StartActivity) || !this.getClass().getName().equals(StartActivity.class.getName()))) {
if (!UserConfig.isUpdateTicketing) {
L.d("onResume 中更新用户票据");
UserConfig.requestUpdateTicket();
}
}
...
}
- 在setContentView方法中,重写加载布局文件的逻辑,统一App页面布局
@Override
public void setContentView(int layoutResID) {
View view = getLayoutInflater().inflate(R.layout.activity_base_layout, null);
super.setContentView(view);
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) {
view.setFitsSystemWindows(true);
setTranslucentStatus(true);
SystemBarTintManager tintManager = new SystemBarTintManager(this);
tintManager.setStatusBarTintEnabled(true);
tintManager.setStatusBarTintResource(R.color.colorPrimaryDark);
}
initDefaultView(layoutResID);
initDefaultToolBar();
}
/**
* 初始化默认的布局组件
*
* @param layoutResID
*/
private void initDefaultView(int layoutResID) {
mProgressLayout = (ProgressLayout) findViewById(R.id.progress_layout);
mProgressLayout.setAttachActivity(this);
mProgressLayout.setUseSlideBack(false);
mToolbarContainer = (FrameLayout) findViewById(R.id.toolbar_container);
mDefaultToolBar = (Toolbar) findViewById(R.id.default_toolbar);
mToolbarTitle = (TextView) findViewById(R.id.toolbar_title);
mRvRight = (RippleView) findViewById(R.id.rv_right);
mRightOptButton = (TextView) findViewById(R.id.right_opt_button);
mContentContainer = (FrameLayout) findViewById(R.id.fl_content_container);
View childView = layoutInflater.inflate(layoutResID, null);
mContentContainer.addView(childView, 0);
}
/**
* 初始化默认的ToolBar
*/
private void initDefaultToolBar() {
if (mDefaultToolBar != null) {
String label = getTitle().toString();
setTitle(label);
setSupportActionBar(mDefaultToolBar);
mDefaultToolBar.setNavigationIcon(R.mipmap.toolbar_back_icn_transparent);
}
}
在BaseActivity中重写了setContentView方法,然后统一布局文件,即让我们调用setContentView传递的layoutId加载到我们自定义的ViewContain中,这样我们就统一了Activity的布局文件,当然了这也是共有的逻辑,我们可以写在BaseActivity中。
- 执行共有的UI操作,比如显示Toast,显示SnakeBar,显示Progress,显示Dialog等
public void showToast(String text) {
if (text != null && !text.trim().equals("")) {
Toast.makeText(getApplicationContext(), text, Toast.LENGTH_SHORT).show();
}
}
public void showProgress(boolean canCancled, final Config.ProgressCancelListener listener) {
Config.showProgressDialog(mContext, canCancled, listener);
}
public void dismissProgress() {
Config.dismissProgress();
}
public void showResponseCommonMsg(HeaderCommon.ResponseCommonMsg msg) {
if (msg.getMsg() != null && msg.getMsg().length() > 0) {
if (msg.hasShowType()) {
if (msg.getShowType().equals(HeaderCommon.ResponseCommonMsgShowType.TOAST)) {
showSnackBarMsg(msg.getMsg());
} else if (msg.getShowType().equals(HeaderCommon.ResponseCommonMsgShowType.WINDOW)) {
showResponseCommonMsg(msg, null);
}
} else {
showSnackBarMsg(msg.getMsg());
}
}
}
我们可以在BaseActivity中重写了一些显示Toast,Dialog,SnakeBar的操作,这样我们在Activity中显示这些UI的时候可以统一调用方法与UI风格。
从上面我们分析的基类内容可以知道一般在涉及Activity的时候都需要重写一个基类Activity,用于重写Activity中的共有逻辑,避免我们在每个Activity中都重写相关的重复逻辑。
另外对产品研发技术,技巧,实践方面感兴趣的同学可以参考我的:
Android产品研发–>总结(持续更新)
Android产品研发(一)–>实用开发规范
Android产品研发(二)–>启动页优化
本文以同步至github中:github.com/yipianfengy…,欢迎star和follow
发表于2016/6/2 18:05:32 3652人阅读
分类:
android产品研发
转载请标明出处:一片枫叶的专栏
随着移动技术的深入发展,各种炫酷效果的更新,在我们追求UI与UE的同时一个不如忽视的问题逐渐暴露出来,那就是apk文件越来越大,可能有的童鞋会说现在都是wifi环境,apk文件增大几M不是什么大不了的问题,这其实也是有一定道理的,但是作为开发人员的我们这绝不是我们认为可以忽略这个问题的理由。优化Apk大小也是优化我们App体验的一个重要方面,虽然可能它不是那么的重要。
那么到底是那些原因让我们的Apk文件变得越来越大呢?
多屏幕适配问题,移动设备的多样化导致了一个app需要N种不同尺寸的设置,从而增加了安装文件中资源文件的种类与大小;
各种开发框架,开发工具的改进,虽然方便了我们开发者重复造轮子,但是这也间接的增加了安装文件的大小;
在追求各种动画、UI效果的内在需求,显而易见的更加炫酷的UI、UE效果会客观上增减Apk文件的大小;
开发人员沟通不足,冗余代码过多造成打出来的apk文件增大;
为了满足性能、安全性的需求,增添.so库;
好吧,既然我们知道了为何我们的apk文件越来越大,我们就可以有针对性的提出解决办法,为了更好的说明问题,这里我简单的列一下apk文件的组成:
源码文件即源代码文件等
资源文件包括apk文件中的各种布局文件,资源文件,图片文件,原生文件等等
本地代码文件这里主要指的是.so文件
那么在知道了apk文件的组成之后,如何减少apk文件的大小就很简单了
减小apk文件的大小,已经是一件很重要的事情。上面我们知道了apk文件的组成部分,就可以根据这些组成部分做一些优化安装文件的工作了。。下面是我总结的一些减小apk文件大小的方式:
源码文件方面的优化方式:
掌握良好的编码习惯,复用代码,对于一些重复性的代码逻辑复用;
使用Proguard对代码混淆,优化,压缩(它不仅仅是一个牛擦的混淆工具在代码优化方面也是很棒滴)。
减少一些无用代码库的引用,经常的一个场景就是可能一些代码库已经不需要了,但是还是在代码中引用着,这样做的后果就是无用的代码库也会被编译到apk文件中;
定期进行代码review,这是一个很重要的工作,可以是团队成员之间熟悉彼此代码同时也可以查找出一些自己看不出来的bug以及减小一些无用代码;
做一些代码lint之类的工作,Android studio已经提供了这样的功能,定期执行代码lint操作是一个比较不错的习惯;
资源文件方面的优化方式:
对于一些不必要的设备尺寸,不必要全部设备(主要看产品需求)
对资源文件,主要是图片资源进行压缩;
一些UI效果可以使用代码渲染替代图片资源;
资源文件的复用,比如两个页面的背景图片相同底色不同,就可以复用背景图片,设置不同的底色;
本地代码文件方面的优化方式:
限制app支持的cpu架构的数目,在当前的Android 生态系统中,让你的app支持 armabi 和 x86 架构就够了;
尽可能的重用so库文件;
最后重点讲一下图片的压缩工作,其实上面所讲的所有的减小apk大小的技巧中最重要的就是对图片的压缩工作。一般而言图片压缩对减小Apk大小所产生的效果占到你所有减小Apk努力的效果50%以上,因而要想减小Apk大小就考虑一下怎么减小图片的大小吧。
这里推荐一个比较不错的图片压缩网站:tinypng.com/ 支持对jpg与png图片资源的压缩。
我们可以具体的测试一下不同大小图片资源的压缩情况:

可以看到当图片资源大于100k之后压缩率都是65%左右,在10k+的图片资源上表现同样很牛擦,当图片资源小于10k的随着图片大小的减小,逐渐减少。
后来具体在我们的apk文件中都是用这种方式压缩一遍之后,apk文件的大小从11.86M减少到了10.52M,可见apk文件减小的效果还是很明显的。
PS:
最新的Android Studio2.2提供了一个Apk分析器,能够查看Apk文件的组成部分一起各自大小所占的比重,相当不错哈。有兴趣的同学可以研究一下,多如何减小Apk大小还是很有帮助的。
打开Android Studio –> Build –> Analyze Apk

以上减少apk文件的方式相对来说,体验方面就需要有一定的取舍,设计就是在一个约束集里面找出最好的方案。显然apk文件的大小就是一个约束。不要害怕为了让多个方面变得更好而放松一个方面的约束。你需要将app各方面进行整体考虑,而不是仅仅几个方面的斟酌。
另外对产品研发技术,技巧,实践方面感兴趣的同学可以参考我的:
Android产品研发–>总结(持续更新)
Android产品研发(一)–>实用开发规范
Android产品研发(二)–>启动页优化
Android产品研发(三)–>基类Activity
本文以同步至github中:github.com/yipianfengy…,欢迎star和follow
android,产品,研发,apk
相关博文