最近想系统的学习android,无奈之前的基础太差(linux),所以算是一切从头开始学习。
从Android的启动流程开始,到Launcher这边的时候,看了Launcher3的一点源码分析,因为不懂android框架API和java的一些语法,大多看的云里雾里的。所以想自己做一个简单的Launcher练练手。因为Launcher说到底还是个APP,也顺遍学习学习android应用的开发。
趁着元旦假期和后面的两三个周末时间,自娱自乐勉勉强强做了一个能看的Launcher。怕脑子有点不好使,看过的东西回忘,为了方便还是把整个过程和思路及要点都理一下,很多东西都是东拼西凑边写边做的。
由于不太懂JAVA的语法加上第一次接触android应用,Launcher3的我也没借鉴太多,很多东西都是我按自己的套路来套的(甚至一些语言上命名习惯上也一时难以纠正),所以一些错误的思路和方案选择在所难免,主要目的还是在于可以入门了解Android应用开发为主。。
下面的讲解,不会去分析自己的代码,因为没有意义,着重还是在于理思路,以及总结过程需要用到的知识点。其实中间很多地方,很多原理摊开来讲都很多,这里为了方便就不细讲了,其实很多我也没有去细究过甚至有点自己被绕进去了的感觉,不过方向就在那里,查一下也很方便。
内容实现较多,所以分了几篇,先搭上个电梯:
代码上传GITHUB:
写Launcer之前需要了解什么是Launcher。
Google 即时桌面与其他的桌面/启动器应用基本功能无异,可以用来启动其他应用,用户也可以在 Google 即时桌面中随意设置 Widget 以及其他样式。同时拥有一个应用抽屉,用以显示用户安装的所有应用。
Launcher的介绍:
https://juejin.im/entry/580088a8bf22ec0064c1884e
https://blog.csdn.net/dddxxxx/article/details/78708971
因为这里只是单纯写一个APP,还没有讨论到定制系统的Launcher的问题,所以这个暂时放到一边。(本来还误认为把setting也以为是Launcher的一部分,把虚拟键也认为是Launcher的一部分,现在看来感觉有点蠢了)
从大致的介绍上看,最主要的功能还是显示APP,打开卸载操作APP等。因为Launcher本身就是可以定制的,然后很多功能效果都是可以自己加的。为了简化程序,就照着旁边一个会议电视机的Launcher的效果(去了一些麻烦的和没看出逻辑的效果),做了一个简易版本的。
首先Launcher整体的结构分为两层,第一层放置着一些常用的APP图标和日期时间的显示。
第二层类似于Launcher中的抽屉,放置APP。
支持Android 5.1以上的。
开发工具:Android Studio
屏幕大小应该没什么关系,我的想法基本是按分辨率自适应的。
新建一个环境没什么特别的,按一般的android工程建就好了。
需要注意两点。
因为我建了一个新项目是空项目,头顶上的那个标题栏看着很难受,而且Launcher不需要,所以需要把他删了。
网上的大多方法是修改Android——res——styles.xml的样式。在sytle的标签下新加了一个item的标签,如下:
- true
整体来说看到这里,大概可以理解成android的样式和代码是分开的,而我们开发通过XML来方便的设置布局,那么以一定也有个解析其解析出参数传入框架。
注:这里可以也可以设置背景为透明,但本身我就想固定整个背景了,所以就不修改了
这个分类是一定要设置的,后面替换Launcher会用到。
从AOSP的源码中可知道,读取 在桌面应用的时候会使用intent_filter找出类型是HOME的APP。所以把这个改了,然后重新启动板卡就会出现2个桌面应用,一个是系统自带的,还有就是这个launcher.
系统启动这里Launcher有个过程,大概是从init到启动Zygote,Zygote调AndroidRuntimeStart这个方法,里面会fork一个system_server的紫禁城,在通过反射调用启动SystemSercer。这里面会做很多很多工作,像加载JNI,启动Android服务等,然后里面有个ActivityMangerService它会启动调用第一个App:Launcher。
boolean startHomeActivityLocked(int userId) {
if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
&& mTopAction == null) {
// We are running in factory test mode, but unable to find
// the factory test app, so just sit around displaying the
// error message and don't try to start anything.
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));
// 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.instrumentationClass == null) {
intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
mStackSupervisor.startHomeActivity(intent, aInfo);
}
}
return true;
}
修改使之成为默认启动APP的地方在AndroidManifest.xml文件里,增加:
参考:
CATEGORY_HOME
added in API level 1
public static final String CATEGORY_HOME
This is the home activity, that is the first activity that is displayed when the device boots.
Constant Value: “android.intent.category.HOME”
https://developer.android.com/reference/android/content/Intent#CATEGORY_DEFAULT
官方给的解释是CATEGORY_HOME是设备启动的第一个activity。
这里特地研究一下这个intent的东西,总体概括为:多个activity间的通信方式。后面还会说到包管理器和intent过滤器的联合使用。
可以参考:
https://developer.android.com/reference/android/content/Intent
https://developer.android.com/guide/components/intents-filters?hl=zh-cn
这里关于什么是布局和设置布局就不细说了,我直接在android下的app–res–layout里面的XML文件直接设置了,语法就是XML的语法,就是标签和属性是特定的一些,查文档就可以了,算事比较好理解。此外,底下的TEXT是对应的XML图,DESIGN对应的是预览,也可以直接在上面拖控件和布局。我这里之前会出现XML的DESIGN无法预览的情况。
解决方法是将Style中的style标签下的parent属性前部分加一个“Base.”