从SystemServer处理过程到Launcher启动过程
我们先回顾一下系统启动,我们从init启动到zygote的启动,然后才到SystemServer
在init过程中,我们主要是给初始化和启动属性与启动Zygote
在Zygote中我们主要是创建了Java虚拟机并从中注册了JNI,成功的从Native跳转到了Java虚拟机层,跳转之后无限循环监听socket套接字,并对套接字做出回应,最后启动SystemServer
现在我们来看看如何启动SystemServer并且SystemServer的作用是什么
1.启动SystemServer
说到SystemServer的启动,我们就得先说Zygote了,我们再回忆一下Zygote是如何启动SystemServer
在Zygote的startSystemServer中我们会先创建一个数组用来存放SystemServer启动的参数,然后把这个数组封装成forkSystemServer函数所需要的参数,之后创建一个子进程,找到子进程中pid==0的执行handlerSystemServerProcess
就能成功启动SystemServer了,
这个是之前zygote讲的如何启动SystemServer,但是现在肯定不可能笼统的就讲一个这个,在handlerSystemServerProcess
中我们会看到一串代码
private static Runnable handleSystemServerProcess(ZygoteArguments parsedArgs) {
// set umask to 0077 so new files and directories will default to owner-only permissions.
Os.umask(S_IRWXG | S_IRWXO);
if (parsedArgs.mNiceName != null) {
Process.setArgV0(parsedArgs.mNiceName);
}
final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
if (systemServerClasspath != null) {
// Capturing profiles is only supported for debug or eng builds since selinux normally
// prevents it.
if (shouldProfileSystemServer() && (Build.IS_USERDEBUG || Build.IS_ENG)) {
try {
Log.d(TAG, "Preparing system server profile");
prepareSystemServerProfile(systemServerClasspath);
} catch (Exception e) {
Log.wtf(TAG, "Failed to set up system server profile", e);
}
}
}
if (parsedArgs.mInvokeWith != null) {
String[] args = parsedArgs.mRemainingArgs;
// If we have a non-null system server class path, we'll have to duplicate the
// existing arguments and append the classpath to it. ART will handle the classpath
// correctly when we exec a new process.
if (systemServerClasspath != null) {
String[] amendedArgs = new String[args.length + 2];
amendedArgs[0] = "-cp";
amendedArgs[1] = systemServerClasspath;
System.arraycopy(args, 0, amendedArgs, 2, args.length);
args = amendedArgs;
}
WrapperInit.execApplication(parsedArgs.mInvokeWith,
parsedArgs.mNiceName, parsedArgs.mTargetSdkVersion,
VMRuntime.getCurrentInstructionSet(), null, args);
throw new IllegalStateException("Unexpected return from WrapperInit.execApplication");
} else {
ClassLoader cl = getOrCreateSystemServerClassLoader();
if (cl != null) {
Thread.currentThread().setContextClassLoader(cl);
}
/*
* Pass the remaining arguments to SystemServer.
*/
return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
parsedArgs.mDisabledCompatChanges,
parsedArgs.mRemainingArgs, cl);
}
/* should never reach here */
}
这里的代码有点长,我们看1段比较重要的
ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
parsedArgs.mDisabledCompatChanges,
parsedArgs.mRemainingArgs, cl);
首先我们通过方法名大概可以推断出这个应该是对zygote进行初始化
我们点击zygoteInit里面的源码
public static Runnable zygoteInit(int targetSdkVersion, long[] disabledCompatChanges,
String[] argv, ClassLoader classLoader) {
if (RuntimeInit.DEBUG) {
Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote");
}
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit");
RuntimeInit.redirectLogStreams();
RuntimeInit.commonInit();
ZygoteInit.nativeZygoteInit();//1
return RuntimeInit.applicationInit(targetSdkVersion, disabledCompatChanges, argv,
classLoader);//2
}
其中注释的1与2
ZygoteInit.nativeZygoteInit();
这段代码的主要作用是从调用native层的代码来启动Binder线程池,这样systemserver进程就可以使用Binder与其他进程进行通信了
然后
RuntimeInit.applicationInit(targetSdkVersion, disabledCompatChanges, argv,
classLoader);
这段代码是进入systemServer的main方法
刚才我们说的通过
ZygoteInit.nativeZygoteInit();
实现了调用native层的代码启动Binder线程池与其他进程进行通信,这里就浅浅再说一下这个通信
重所周知在Android中进程之间的通信我们称之为IPC
1.1由zygoteInit.nativeZygoteInit()浅浅聊一下IPC
IPC在Android中主要有以下几个方式
1.1.1Bundle
其中我们常见的Intent.putStringExtra()里面传递的数据就是Bundle类型的
如果我们写全一点的话应该是这样的
Bundle mBundle = new Bundle();
mBundle.putString("Data", "data from TestBundle");
intent.putExtra(mBundle);
1.1.2文件共享
这里一般都是用的sharedprefence,因为用了这个方便后面问一些问题
我们常见的用法就是
SharedPreferences.Editor editor = getSharedPreferences("name",MODE_PRIVATE).edit();
editor.putString("nihao","22");
editor.apply();
这是写操作,我们将键值为nihao实际值为22的存放在name这里面
SharedPreferences sharedPreferences = getSharedPreferences("name",MODE_PRIVATE);
String ss = sharedPreferences.getString("nihao","");
然后从name里面找到文件然后找到键值为nihao的String类型把它给ss
这里一般有3个考点会考察你
1.1.2.1考点一:MODE_PRIVATE是什么
当我们写
getSharedPreferences()
中,第一个参数放的是存放在哪个文件里面,第二个就是那个了
出去几个不常用的参数,还剩下
MODE_PRIVATE
MODE_APPEND
MODE_ENABLE_WRITE_AHEAD_LOGGING
MODE_NO_LOCALIZED_COLLATORS
那么它们有什么作用呢?
我们点开源码进行查看
首先我们知道它们表示的是一种模式
MODE_PRIVATE | 这是默认的模式,它使得SharedPreferences只能被创建它的应用程序访问。其他应用程序无法访问这些SharedPreferences。这种模式非常适合存储私有数据。 |
---|---|
MODE_APPEND | 如果您想将新数据添加到SharedPreferences中而不是覆盖它们,那么可以使用此模式。使用此模式时,新数据将追加到SharedPreferences的末尾 |
MODE_ENABLE_WRITE_AHEAD_LOGGING | 此模式启用了写前日志记录(Write-Ahead Logging,WAL)模式。这个模式可以提高多线程并发读写的性能,但是会增加一些开销。如果您的应用需要频繁地读写SharedPreferences,那么可以考虑使用此模式。 |
MODE_NO_LOCALIZED_COLLATORS | 此模式禁用了排序。如果您不需要在SharedPreferences中进行排序,那么可以使用此模式来提高性能。 |
其中其他应用程序无法访问这些SharedPreferences的意思大概就是你如果不用
SharedPreferences sharedPreferences = getSharedPreferences("name",MODE_PRIVATE);
String ss = sharedPreferences.getString("nihao","");
的话就无法访问到该数据
1.1.2.2考点二:apply与commit
我们在使用Editor的时候
SharedPreferences.Editor editor = getSharedPreferences("name",MODE_PRIVATE).edit();
editor.putString("nihao","22");
editor.apply();
这里editor用的是apply其实我们还可以用commit,那么commit与apply有什么区别呢?
- apply()方法
apply()方法是异步提交数据的方式,它将数据写入内存中的副本,并在后台线程中异步写入磁盘。apply()方法不会返回提交操作的结果,因此无法知道提交是否成功。由于apply()方法是异步的,因此在提交大量数据时,它比commit()方法更快。 - commit()方法
commit()方法是同步提交数据的方式,它将数据写入内存中的副本,并同步写入磁盘。在提交大量数据时,commit()方法可能会阻塞UI线程,导致应用程序响应缓慢。因此,建议只在提交少量数据时使用commit()方法。
这里我们就可以说一说异步和同步了
1.1.2.2.1异步和同步
假如有一个A B C那么同步就是A执行完后执行B,B执行完后执行C
但是如果时异步,如果A花费的时间很长,那么我们可以先执行其他花费时间短的,之后再执行花费时间长的
我们常见的异步和同步就是okhttp3中
下面是okhttp3的异步操作
**// 异步操作
call.enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
//
// Toast.makeText(MainActivity.this, "请求失败", Toast.LENGTH_LONG).show();
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
// Toast.makeText(MainActivity.this, "请求成功", Toast.LENGTH_LONG).show();
final String shuju = response.body().string();
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "请求成功", Toast.LENGTH_LONG).show();
textView.setText(shuju);
}
});
}
});**
同步操作是
textView = findViewById(R.id.t1);
OkHttpClient okHttpClient = new OkHttpClient();
Request request = new Request.Builder().get().
url("https://www.baidu.com").build();
final Call call = okHttpClient.newCall(request);
new Thread(new Runnable() {
@Override
public void run() {
try {
Response response = call.execute();
String sb = response.body().string();
replace(sb);
} catch (IOException e) {
e.printStackTrace();
}
}
public void replace(String sb) {
textView.setText(sb);
}
}).start();
1.1.2.3考点三:为什么进程间通信不推荐用sharedpreferences
系统对sharedpreferences的读与写有一定的缓存策略,即在内存中会有一份sharedprefences文件的缓存,在多进程中,系统对它的读与写不特别安全。虽然apply方法可以异步进行处理,但是但是Service.onStartCommand、Service.onDestroy、Activity.onPause、Activity.onStop等生命周期回调时,会调用QueuedWork.waitToFinish()去等待所有写入任务的执行完成。如果异步提交的任务过多,会阻塞主线程造成 ANR。
后面的IPC主要就是对Binder进行封装与AIDL的使用就不多说了,继续进行我们刚才SystemServer的启动
我们上面提到了
我们通过handlerSystemServerProcess里面的ZygoteInit.zygoteInit发现里面的两个方法
一个是
zygoteInit.nativeZygoteInit()
这个是通过Binder实现多进程之间通信,
还有一个是
RuntimeInit.applicationInit(targetSdkVersion, disabledCompatChanges, argv,
classLoader);
这个是进入SystemServer的main方法
我们进入applicationInit的源码里面看看
1.2如何进入SystemServer的Main方法
我们上面说了,进入的过程主要是在RuntimeInit.applicationInit()里面实现的
里面调用了invokeStaticMain方法
其中它先通过
cl = Class.forName(className,true,classLoader);
来获得一个SystemServer类
然后
m = cl.getMethod("main",new Class[]{
String[].class});
我们通过刚才反射获得的SystemServer来获得那个名为main的方法
但是这样我们并没有直接进入main方法
这段代码执行完之后会抛出一段异常,在异常中我们会发现它调用了一个方法
mMethod.invoke(null,new Object[]{
mArgs});
至此,就进入了SystemServer的Main方法
2.SystemServer都做了些什么
我们想要知道SystemServer做了些什么我们看SystemServer的main方法干了些什么就可以了
public static void main(String[] args) {
new SystemServer().run();
}
它首先创建了一个多线程
然后看一下SystemServer()中的源码
private void run() {
.....
android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_FOREGROUND);
android.os.Process.setCanSelfBackground(false);
Looper.prepareMainLooper();//1------------1
Looper.getMainLooper().setSlowLogThresholdMs(
SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);
SystemServiceRegistry.sEnableServiceNotFoundWtf = true;
System.loadLibrary("android_servers");//2----------------2
initZygoteChildHeapProfiling();
if (Build.IS_DEBUGGABLE) {
spawnFdLeakCheckThread();
}
performPendingShutdown();
createSystemContext();//3------------------3
ActivityThread.initializeMainlineModules();
ServiceManager.addService("system_server_dumper", mDumper);
mDumper.addDumpable(this);
mSystemServiceManager = new SystemServiceManager(mSystemContext);//4-----------------4
mSystemServiceManager.setStartInfo(mRuntimeRestart,
mRuntimeStartElapsedTime, mRuntimeStartUptime);
mDumper.addDumpable(mSystemServiceManager);
LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
SystemServerInitThreadPool tp = SystemServerInitThreadPool.start();
mDumper.addDumpable(tp);
if (Typeface.ENABLE_LAZY_TYPEFACE_INITIALIZATION) {
Typeface.loadPreinstalledSystemFontMap();
}
if (Build.IS_DEBUGGABLE) {
String jvmtiAgent = SystemProperties.get("persist.sys.dalvik.jvmtiagent");
if (!jvmtiAgent.isEmpty()) {
int equalIndex = jvmtiAgent.indexOf('=');
String libraryPath = jvmtiAgent.substring(0, equalIndex);
String parameterList =
jvmtiAgent.substring(equalIndex + 1, jvmtiAgent.length());
try {
Debug.attachJvmtiAgent(libraryPath, parameterList, null);
} catch (Exception e) {
Slog.e("System", "*************************************************");
Slog.e("System", "********** Failed to load jvmti plugin: " + jvmtiAgent);
}
}
}
} finally {
t.traceEnd();
}
RuntimeInit.setDefaultApplicationWtfHandler(SystemServer::handleEarlySystemWtf);
try {
t.traceBegin("StartServices");
startBootstrapServices(t);//5---------------5
startCoreServices(t);//6------------------6
startOtherServices(t);//7----------------7
startApexServices(t);//8----------------8
} catch (Throwable ex) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting system services", ex);
throw ex;
} finally {
t.traceEnd();
}
.....
}
首先
Looper.prepareMainLooper();
是这段代码,Looper先进行一个prepare()方法来创建一个looper,因为这是main ui所以我们是
Looper.prepareMainLooper(),会在内部调用ThreadLocal.set()方法这里就不细讲了
你看下面的那段代码
Looper.getMainLooper().setSlowLogThresholdMs(
SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);
他就是创建完后Looper然后进行循环操作
刚才的Looper的创建于Looper的循环仅仅是第一步
第二步是这个、
System.loadLibrary("android_servers");
我们把android_servers这个动态库加载到System中
接下来就是第三步
createSystemContext();
ServiceManager.addService("system_server_dumper", mDumper);
mDumper.addDumpable(this);
这是对系统服务进行了一个创建,启动,生命周期管理
然后就是这段代码
startBootstrapServices(t);//5---------------5
startCoreServices(t);//6------------------6
startOtherServices(t);//7----------------7
startApexServices(t);//8----------------8
按照字面意思我们就可以理解
启动引导服务->启动核心服务->启动其他服务->启动Apex服务
我查了一下Apex服务在Android9以下是没有的。它可以用于更新系统应用和库,以及提供系统级别的模块化功能。
- startBootstrapServices(t) - 这个方法会启动一些最基础的系统服务,例如 Binder、PackageManagerService, ActivityManagerService等,这些服务是其他服务运行的基础。
- startCoreServices(t) - 这个方法会启动一些核心的系统服,这些服务是 Android 系统的核心功能。
- startOtherServices(t) - 这个方法会启动一些其他的系统服务,例如 InputMethodManager、PowerManager 等,这些服务是 Android 系统的其他功能。
- startApexServices(t) - 这个方法会启动 Apex 服务,这些服务用于管理和加载 Apex 软件包。
但是启动完这些系统服务后,还有应用程序和系统组件的通用方法这些并不是在这几个方法中启动的,而是通过SystemServiceManager 的 startService 来实现的
我们来看看startService的源码
public void startService(@NonNull final SystemService service) {
// Check if already started
String className = service.getClass().getName();
if (mServiceClassnames.contains(className)) {
Slog.i(TAG, "Not starting an already started service " + className);
return;
}
mServiceClassnames.add(className);
// Register it.
mServices.add(service);
// Start it.
long time = SystemClock.elapsedRealtime();
try {
service.onStart();
} catch (RuntimeException ex) {
throw new RuntimeException("Failed to start service " + service.getClass().getName()
+ ": onStart threw an exception", ex);
}
warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onStart");
}
它主要是先将service添加到这个mServices的队列中去去进行注册,然后调用**service的onStart()**来进行启动。
;除此之外,还可以通过各种服务的main方法启动,以PackageManagerService为例,它的main方法就会先创建一个PackageManagerService,然后将其添加进入ServiceManager中,这样也能实现服务的启动。
至此SystemServer就讲完了,那么我们做一个总结
3.SystemServer总结
我们回顾一下SystemServer的过程,首先我们是通过Zygote,当它调用startSystemServer方法的时候会遍历找到pid==0的函数然后执行handlerSystemServerProcess。
在handlerSystemServerProcess里面主要进行了两个操作
1.ZygoteInit.nativeZygoteInit()这个函数成功实现了调用native层的代码启动Binder线程池与其他进程进行通信,
实现了SystemServer与其他进程通过Binder连接池进行进程间的通信
2.RuntimeInit.applicationInit()通过这个函数,我们在它的内部通过反射找到那个名为main的函数并通过动态加载成功进入了SystemServer的main方法
之后我们了解了SystemServer主要做了些什么就是SystemServer的main方法都干了些什么,主要就是先
System.loadLibrary("android_servers");
把这个动态库加载到系统里面,然后
createSystemContext();
ServiceManager.addService("system_server_dumper", mDumper);
mDumper.addDumpable(this);
创建启动生命周期管理
后面我们启动一堆服务比如:startBootstrapServices(t), startCoreServices(t), startOtherServices(t), startApexServices(t)
然后就结束了
所以SystemServer这个进程主要干了这么几件事情
- 启动Binder线程池,这样就可以与其他进程进行通信
- 创建SystemServiceManager,其用于对系统的服务进行创建,启动和生命周期的管理
- 启动各种系统服务
4.Launcher启动过程
Launcher我们可以将它理解为Android系统的桌面。
这个是Android系统启动的最后一步,我们先了解一下Launcher的作用
主要有2点
1.作为Android系统的启动器,用来启动应用程序
2.作为Android系统的桌面,用于显示和管理应用程序的快捷图标或者其他桌面组件
简单来说就是启动其他应用程序与管理图标与组件的
4.1具体流程
刚才SystemServer会启动BootstrapServices(),这里面有一个叫做PackageManagerService,它的作用是:
用来对APK进行安装,解析,删除,卸载等操作
在此前已经启动的AMS(这个也是在BootstrapServices里面启动的)会将Launcer启动起来
Launcher的启动过程的时序图如下所示
我们可以看出来Launcher的入口是AMS的systemReady方法,AMS是SystemServer的startBootstrapServices,但是这个systemReady却是startOtherService就很奇怪
这里面大部分就是跳转从某个地方跳转到某个地方,唯一一个需要特别关注的是ActivityStackSupervisor调用resumeHomeStackTask中内部会调用AMS的startHomeActivityLocked方法
4.1.1startHomeActivityLocked
boolean startHomeActivityLocked(int userId, String reason) {
if(mFactoryTest==FactoryTest.FACTORY_TEST_LOW_LEVEL&&mTopAction==nuull){
return fasle;
} //注释1
//......
Intent intent = getHomeIntent(); //注释2
ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
if (aInfo != null) {
intent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
// Don't do this if the home app is currently being
// instrumented.
aInfo = new ActivityInfo(aInfo);
aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
ProcessRecord app = getProcessRecordLocked(aInfo.processName,
aInfo.applicationInfo.uid, true);
if (app == null || app.instr == null) {
//注释3
intent.setFlags(intent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
final int resolvedUserId = UserHandle.getUserId(aInfo.applicationInfo.uid);
// For ANR debugging to verify if the user activity is the one that actually
// launched.
final String myReason = reason + ":" + userId + ":" + resolvedUserId;
mActivityStarter.startHomeActivityLocked(intent, aInfo, myReason); //注释4
}
} else {
Slog.wtf(TAG, "No home screen found for " + intent, new Throwable());
}
return true;
}
我一共在上面标注了4个注释,注释一的作用是判断现在的系统运行模式,
系统运行模式分为三种:1.非工厂模式
2.低级工厂模式
3.高级工厂模式
mTopAction表示的是第一个被启动该的Activity组件的Action,默认值为Intent.ACTION_MAIN
当我们的运行模式是低级工厂模式且mTopAction为null的时候直接返回一个fasle否则继续进行
之后第二段注释的代码**getHomeIntent()**的源码如下
Intent getHomeIntent() {
Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
intent.setComponent(mTopComponent);
intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
intent.addCategory(Intent.CATEGORY_HOME);
}
return intent;
}
我们仔细一看,这不是Intent的隐式调用嘛,它先创建一个新的Intent并且把刚才上面我们判断的mTopAction与新的一个mTopData
(注意这里面的mTopAction的值就是默认值Intent.ACTION_MAIN)
然后进行判断
如果mFactoryTest即运行的模式不是低级工厂模式,我们则把这个Intent的Category设置为Intent.CATEGORY_HOME
最后返回该Intent。
这时候我们再返回上面的那段源码,注释2已经讲完,现在来看看注释3的
它会判断Action为Intent.ACTION_MAIN,Category为Intent.CATEGORY_HOME的程序是否启动,如果没有的话则调用注释4
mActivityStarter.startHomeActivityLocked(intent, aInfo, myReason);
启动该程序,该程序就是Launcher,
Launcer的AndroidManifest文件的intent_filter标签中Action就是Intent.ACTION_MAIN,Category就是Intent.CATEGORY_HOME
我用了一下chatgpt它是这么解释它的Action为Intent.ACTION_MAIN的
在AndroidManifest.xml文件中,通过为Activity指定相应的
intent-filter
来定义应用程序的入口点。当intent-filter
的Action
为Intent.ACTION_MAIN
时,该Activity就成为了应用程序的入口点之一。在大多数情况下,我们会将主界面Activity指定为应用程序的入口点,因此,Launcher所在的Activity通常是MainActivity。但是,如果应用程序有多个入口点,那么就需要为每个入口点指定不同的
intent-filter
,以便用户可以从不同的途径进入应用程序。在这种情况下,Launcher所在的Activity可能不是MainActivity,而是其他Activity。因此,虽然Launcher所在的Activity通常是MainActivity,但在某些情况下,它可能不是MainActivity,所以我们不能简单地将Launcher和MainActivity视为相同的Activity。
不是特别理解
而刚才我们说的如果它的Action与Category不符合我们的要求的话
则会调用
mActivityStarter.startHomeActivityLocked(intent, aInfo, myReason);
代码我就不详细写了,我们这要知道它其中有段代码
mSupervisor.moveHomeStackTaskToTop(reason);
它把Launcher放在HomeStack中.HomeStack是再ActivityStackSupervisor中定义的用于存储Launcer的变量,接着调用startActivityLocked来启动Launcer
在Launcher的onCreate()中完成启动
4.2Launcher的作用
上面我们已经讲过了,Launcher相当于Android系统的桌面,它可以用来启动应用程序以及管理其他应用APP的图标
我们主要讲的就是Launcher怎么显示图标的
我们刚才讲了Launcer在它调用自己的**onCreate()**后就完成了Launcher的启动
protected void onCreate(Bundle savedInstanceState) {
.......
super.onCreate(savedInstanceState);
LauncherAppState app = LauncherAppState.getInstance(this);//1-----1
mModel = app.setLauncher(this);//2----2
......
if(!mRestoring){
mModel.startLoader();//3---3
}
......
}
这段代码中我们主要看标注释的地方
第一个标注释的是我们获取了 LauncherAppState的实例
第二个注释就是调用它的setLauncher方法将Launcher对象传入
这里我们先看第二个注释,第三个注释稍后看
4.2.1setLauncher
LauncherModel setLauncher(Launcher launcher){
getLocalProvider(mContext).setLauncherProviderChangeListener(launcher);
mModel.initialize(launcher);//1
return mModel;
}
它这里主要调用的是LauncherModel的initalize方法
我们看看它的源码
public void initialize(Callbacks callbacks){
synchronized(mLock){
unbindItemInfosAndClearQueueBindRunnables();
mCallbacks = new WeakReference<Callbacks>(caallbacks);
}
}
这里我们Callbacks对象就是我们传入的Launcher,我们主要就是把这个Launcher封装成弱引用对象
所以setLauncher的主要作用就是把Launcher封装成弱引用对象
我们之前学过强引用与弱引用中,强引用容易导致内存泄漏,可能就是因为这个所以我们选择把Launcher封装成弱引用对象
然后我们现在说会注释3
mModel.startLoader();
4.2.2startLoader
创建具有消息循环的HandlerThread对象
@Thunk static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");//1
static{
sWirkerThread.start();
}
@Thunk static final HandlerThread sWorker = new HandlerThread(sWorkerThread.getLooper());//2
...
public void startLoader(....){
synchronized(mLock){
.......
if(mCallbacks!=null&&mCallbacks.get()!=null){
isLaunching = isLaunching||stopLoaderLocked();
mLoaderTask = new LoaderTask(mApp,isLaunching);//3
if(synchronousBindPage>-1&&mAllAppsLoaded&&mWorkspaceLoaded){
...
}
else{
sWorkerThread.setPriority(Thread.NORM_PRIORITY);
sWorker.post(mLoaderTask);//4
}
}
}
}
这里很好理解,这里第一段代码是创建了具有消息循环的HandlerThread对象,然后用注释2创建了Handler,并且传入HandlerThread的Looper,这里Handler的作用就是向HandlerThread传入消息
注释3创建了LoaderTask对象,并且在注释4将LoaderTask作为消息发送给HandlerThread
这里我们先看一看LoaderTask类,LoaderTask是LauncherModel的内部类,代码如下
private class LoaderTask implements Runnable{
....
public void run(){
synchronized(mLock){
...
}
try{
...
loadWorkspace();//1
...
bindWorkspace(mPageToBindFirst);//2
...
loadAllApps();//3
...
}
catch(xxxx){
}
finally{
}
}
}
Launcher是用工作区的形式来显示系统安装的应用程序的快捷图标,每一个工作区描述一个抽象桌面,由n个屏幕组成,每个屏幕又分为n个单元格,每个单元格显示一个应用程序的快捷图标
注释1和2分别是加载工作区与绑定工作区
loadAllApps用来加载系统已经安装的应用程序信息
源码中loadAllApps主要是先判断callbacks为不为null,不为null的话我们调用callbacks,bindAllApplications(added);
在setLauncher那块我们讲了这里我们Callbacks对象就是封装成弱引用对象的launcher
bindAllApplications(added)里面主要是判断为AllAppsContainerView类型的mAppsView对象为不为null,不为null的话则调用它的**setApps(apps)**方法将包含应用信息的列表apps传进去
4.2.3setApps
public void setApps(List<AppInfo>apps){
mApps.setApps(apps);
}
之后查看AllAppsContainerView的onFinishInflate方法
4.2.4onFinishInflate
protected void onFinishInflate() {
super.onFinishInflate();
...
mAppsRecyclerView = (AllAppsRecyclerView)findViewById(R.id.apps_list_view);
mAppsRecyclerView.setAdapter(mApps);//2
mAppsRecyclerView.setLayoutManager(mLayoutManager);
mAppsRecyclerView.setAdapter(mAdapter);//3
}
onFinishInflate会在AllAppsContainerView加载完XML布局的时候调用,用一个RecyclerView显示APP列表
在2这块把之前的mApps设置进去,3这块设置mAdapter
这样图标列表就通过这种方式显示在屏幕上了
5.Launcher总结
关于Launcher我们主要讲了两点
第一点是Launcher的启动
第二点是Launcher的作用
关于启动我们主要是结合上面讲的SystemServer一起,当SystemServer启动startOtherServices的时候,AMS会调用
systemReady,从一堆方法跳转到令一堆方法最后又回到AMS调用startHomeActivityLocked,让Launcher的Action为Intent.ACTION_MAIN,让Launcher的Category为Intent.CATEGORY_HOME
最后在Launcher的onCreate()成功完成Launcher的启动
而Launcher的作用我们讲的是怎么显示图标的,
我们主要可以分以下几点
1.将Launcher添加到Callback中并将它封装成弱引用对象
2.创建一个HandlerThread
对象,然后创建一个Handler
对象,并将其关联到HandlerThread
上,以便在后台线程中执行操作。接着创建一个LoaderTask
对象,它负责异步加载应用程序列表,并将其作为消息发送给HandlerThread
的Handler
处理。
3.加载工作区与绑定工作区,并且在工作区中用RecyclerView的形式展示图标
6.总结系统加载的全流程
1.启动电源与系统启动
当按下电源的时候引导芯片代码从固化在ROM开始执行,加载引导程序从BootLoader到RAM,然后执行
2.引导程序BootLoader
把系统OS拉起来并运行
3.Linux内核启动
设置缓存,被保护存储器,计划列表,加载驱动。当内核完成系统设置的时候,在系统文件中寻找init.rc文件,启动Init进程
4.init进程启动
初始化与启动属性服务,启动zygote进程
5.zygote进程启动
创建java虚拟机并为java虚拟机注册JNI方法,让系统从native层进入java虚拟机层,创建服务端socket启动systemServer进程
6.systemServer进程启动
调用zygote中的Binder连接池,让systemServer可以通过binder连接池与其他进程通信。且启动了其他的系统服务
7.launcher启动
SystemServer中的AMS启动Launcher,Launcher启动后会把已安装的应用的图标展示在界面上