实现唯一Launcher
来源:互联网 发布:淘宝 非典 编辑:程序博客网 时间:2024/06/06 08:31
在定制的系统中,Launcher通常自己来写,并需要把自己写的Launcher替换成系统唯一的Launcher。
原理分析
如果了解Android的启动流程的同学都知道,Zygote启动SystemServer,SystemServer的main函数开始启动各种服务。
首先启动init1,然后启动init2. init1这个方法是被Zygote调用来初始化系统的,init1会启动native的服务如SurfaceFlinger,AudioFlinger等等,
这些工作做完以后会回调init2来启动Android的service。
public static final void init2() { Log.i(TAG, "Entered the Android system server!"); Thread thr = new ServerThread(); thr.setName("android.server.ServerThread"); thr.start(); }
init2中启动ServerThread线程,ServerThread中启动了一系列的服务,比如ActivityManagerService,EntropyService等等。
当这些服务起来以后,((ActivityManagerService)
ActivityManagerNative.getDefault()).
systemReady() 在systemReady后开始开始启动Launcher。
frameworks\base\services\Java\com\android\server\am\ActivityManagerService.java line 8422
public void systemReady(final Runnable goingCallback) { // In the simulator, startRunning will never have been called, which // normally sets a few crucial variables. Do it here instead. ......................... resumeTopActivityLocked(null); }
frameworks\base\services\java\com\android\server\am\ActivityManagerService.java (line 2576)
private final boolean resumeTopActivityLocked(HistoryRecord prev) { // Find the first activity that is not finishing. HistoryRecord next = topRunningActivityLocked(null); // Remember how we'll process this pause/resume situation, and ensure // that the state is reset however we wind up proceeding. final boolean userLeaving = mUserLeaving; mUserLeaving = false; if (next == null) { // There are no more activities! Let's just start up the // Launcher... return startHomeActivityLocked(); } .... }
frameworks\base\services\java\com\android\server\am\ActivityManagerService.java (line 2457)
private boolean startHomeActivityLocked() { if (mFactoryTest == SystemServer.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 = new Intent( mTopAction, mTopData != null ? Uri.parse(mTopData) : null); intent.setComponent(mTopComponent); if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) { intent.addCategory(Intent.CATEGORY_HOME); //这里就是了 } .... }
然后我们发现在寻找Launcher的时候是根据HOME的filter(在Manifest中定义的)来过滤。
public static final String CATEGORY_HOME = "android.intent.category.HOME";
方法分析
既然如此,我们现在就可以更改我们的AndroidManifest.xml来安装自己的HOME。所以我们只需在AndroidManifest.xml添加两行代码:
<category android:name="android.intent.category.HOME" /> <category android:name="android.intent.category.DEFAULT" />
现在重新编译我们的应用程序,把编译生成的APK放到相应的目录下,一般是/system/app,启动开发板,
我们可以看到在我们的LCD屏上面,要求用户选择launcher。
既然是这样那我们可以通过暴力修改这个值来达到唯一的launcher的目的,这就好比原来一把锁配了好多个钥匙,结果你看不下去了,想一个人独占这个这个房间,
最简单粗暴的方法就是把锁换掉(方法二),不过我觉得这样要改动的地方太多了,linux下面可以使用grep命令,本人推荐使用方法1。
第一种方式
原理:通过修改PackageParser文件
位置:frameworks\base\core\Java\Android\content\pm\PackageParser.java
在parseIntent这个私有方法,因为APK在安装的时候会通过这个方法解析Manifest清单文件,然后将每个Activity的intent-filter保存在内存中,所以修改这个方法就相当于修改了内存的应用注册表的信息,也就是偷换概念的做法,等于是在安装的时候就把问题解决了,扼杀在萌芽状态:
1.在你自己的launcher的Manifest的Activity里Intent-filter里加入:
2.那么在代码里修改的地方的xxx就替换成你自己起的名字
原理就是安装每个APK的时候 只要是launcher ,就把category是HOME的值改成x,只要是你的launcher就把category的值改成HOME,那么全局就只有你一个launcher了
/*************************************************************************************/ if(Intent.CATEGORY_HOME.equals(value)) { value = "x"; } else if((Intent.CATEGORY_HOME + ".xxx").equals(value)) { value = Intent.CATEGORY_HOME; } /*************************************************************************************/
使用这种方法修改时必须确保你的launcher被安装了或者放在了syste/app下了且Manifest的Activity里的intent-filter为android.category.HOME.xxx
具体做法:
修改frameworks\base\core\java\android\content\pm\PackageParser.java里的parseIntent方法
vim frameworks/base/core/java/android/content/pm/PackageParser.java
源代码为:
private boolean parseIntent(Resources res, XmlPullParser parser, AttributeSet attrs, int flags, IntentInfo outInfo, String[] outError, boolean isActivity) throws XmlPullParserException, IOException { TypedArray sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestIntentFilter); int priority = sa.getInt( com.android.internal.R.styleable.AndroidManifestIntentFilter_priority, 0); if (priority > 0 && isActivity && (flags&PARSE_IS_SYSTEM) == 0) { Log.w(TAG, "Activity with priority > 0, forcing to 0 at " + mArchiveSourcePath + " " + parser.getPositionDescription()); priority = 0; } outInfo.setPriority(priority); TypedValue v = sa.peekValue( com.android.internal.R.styleable.AndroidManifestIntentFilter_label); if (v != null && (outInfo.labelRes=v.resourceId) == 0) { outInfo.nonLocalizedLabel = v.coerceToString(); } outInfo.icon = sa.getResourceId( com.android.internal.R.styleable.AndroidManifestIntentFilter_icon, 0); sa.recycle(); int outerDepth = parser.getDepth(); int type; while ((type=parser.next()) != parser.END_DOCUMENT && (type != parser.END_TAG || parser.getDepth() > outerDepth)) { if (type == parser.END_TAG || type == parser.TEXT) { continue; } String nodeName = parser.getName(); if (nodeName.equals("action")) { String value = attrs.getAttributeValue( ANDROID_RESOURCES, "name"); if (value == null || value == "") { outError[0] = "No value supplied for <android:name>"; return false; } XmlUtils.skipCurrentTag(parser); outInfo.addAction(value); } else if (nodeName.equals("category")) { String value = attrs.getAttributeValue( ANDROID_RESOURCES, "name"); if (value == null || value == "") { outError[0] = "No value supplied for <android:name>"; return false; } XmlUtils.skipCurrentTag(parser); /*************************************************************************************/ if(Intent.CATEGORY_HOME.equals(value)) { value = "x"; } else if((Intent.CATEGORY_HOME + ".xxx").equals(value)) { value = Intent.CATEGORY_HOME; } /*************************************************************************************/ outInfo.addCategory(value); } else if (nodeName.equals("data")) { sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestData); String str = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestData_mimeType, 0); if (str != null) { try { outInfo.addDataType(str); } catch (IntentFilter.MalformedMimeTypeException e) { outError[0] = e.toString(); sa.recycle(); return false; } } str = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestData_scheme, 0); if (str != null) { outInfo.addDataScheme(str); } String host = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestData_host, 0); String port = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestData_port, 0); if (host != null) { outInfo.addDataAuthority(host, port); } str = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestData_path, 0); if (str != null) { outInfo.addDataPath(str, PatternMatcher.PATTERN_LITERAL); } str = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestData_pathPrefix, 0); if (str != null) { outInfo.addDataPath(str, PatternMatcher.PATTERN_PREFIX); } str = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestData_pathPattern, 0); if (str != null) { outInfo.addDataPath(str, PatternMatcher.PATTERN_SIMPLE_GLOB); } sa.recycle(); XmlUtils.skipCurrentTag(parser); } else if (!RIGID_PARSER) { Log.w(TAG, "Unknown element under <intent-filter>: " + parser.getName() + " at " + mArchiveSourcePath + " " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); } else { outError[0] = "Bad element under <intent-filter>: " + parser.getName(); return false; } } outInfo.hasDefault = outInfo.hasCategory(Intent.CATEGORY_DEFAULT); if (false) { String cats = ""; Iterator<String> it = outInfo.categoriesIterator(); while (it != null && it.hasNext()) { cats += " " + it.next(); } System.out.println("Intent d=" + outInfo.hasDefault + ", cat=" + cats); } return true; }
第二种方式
定义一个私有的filter选项,然后用这个选项来过滤HOME.一般情况下我们使用Manifest中定义的
category android:name=”android.intent.category.HOME”
来过滤的,我们现在增加一个私有的HOME_FIRST过滤。(HOME_FIRST随意)
在Intent.java(frameworks/base/core/java/android/content/Intent.java)中添加两行代码//larosn:添加CATEGORY_HOME_FIRST@SdkConstant(SdkConstantType.INTENT_CATEGORY)public static final String CATEGORY_HOME_FIRST = "android.intent.category.HOME_FIRST";
修改和CATEGORY_HOME相关的所有的地方,都改成HOME_FIRST,主要是framework中的这几个地方:
frameworks/base/services/java/com/android/server/am/ActivityManagerService.java中
//intent.addCategory(Intent.CATEGORY_HOME);改成intent.addCategory(Intent.CATEGORY_HOME_FIRST); //larson: //if (r.intent.hasCategory(Intent.CATEGORY_HOME)) {改成if (r.intent.hasCategory(Intent.CATEGORY_HOME_FIRST)) { //larson: Intent.CATEGORY_HOME -> Intent.CATEGORY_HOME_FIRST
- frameworks/base/services/java/com/android/server/am/HistoryRecorder.java中
// _intent.hasCategory(Intent.CATEGORY_HOME) &&改成 _intent.hasCategory(Intent.CATEGORY_HOME_FIRST) && //larson: Intent.CATEGORY_HOME->Intent.CATEGORY_HOME_FIRST
- frameworks/policies/base/mid/com/android/internal/policy/impl/MidWindowManager.java中
// mHomeIntent.addCategory(Intent.CATEGORY_HOME);改成 mHomeIntent.addCategory(Intent.CATEGORY_HOME_FIRST); //larson
- frameworks/policies/base/mid/com/android/internal/policy/impl/RecentApplicationsDialog.java中
// new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME),0);改成 new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME_FIRST),0);
- frameworks/policies/base/phone/com/android/internal/policy/impl/PhoneWindowManager.java中
// mHomeIntent.addCategory(Intent.CATEGORY_HOME);改成 mHomeIntent.addCategory(Intent.CATEGORY_HOME_FIRST); //larson
- frameworks/policies/base/phone/com/android/internal/policy/impl/RecentApplicationsDialog.java中
// ResolveInfo homeInfo = pm.resolveActivity(new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME),0);改成 ResolveInfo homeInfo = pm.resolveActivity(new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME_FIRST),0); //larson
这里我们有一种比较暴力的更改方法,其实原理和第二种方式差不多,就是把系统中原有的public static final String CATEGORY_HOME = “android.intent.category.HOME”;
更改成public static final String CATEGORY_FS_HOME = “android.intent.category.LS_HOME”;
然后修改和CATEGORY_HOME相关的所有的地方,都改成CATEGORY_FS_HOME.如果不知道修改哪些地方,可以使用如下命令去查找:
grep CATEGORY_HOME -l * -R
将上述结果文件中和CATEGORY_HOME相关的所有的地方,都改成CATEGORY_LS_HOME即可。(别家的launcher里面的CATEGORY_HOME就不要改了)。
然后把自己app的AndroidManifest.xml更改为CATEGORY_FS_HOME,重新编译到系统,刷机后看到的就是我们的launcher。
第三种方式
在Android5.1的源码里面有两个launcher,一个叫做Launcher2,一个叫Launcher3,分别位于源码目录的/packages/apps/Launcher2,以及/packages/apps/Launcher3 。
直接删除上述两个应用的Android.mk(建议将Android.mk改成Android.mk.bak,以免以后再次启用)
假设我的应用为XLauncher;
自己的XLauncher程序拷贝到packages/apps目录下,要在目录里添加Android.mk,具体参考packages/apps下面应用自带的Android.mk,必须保证AndroidManifest.xml下里的值为:
<action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.HOME" /><category android:name="android.intent.category.DEFAULT" /><category android:name="android.intent.category.MONKEY"/>
然后修改Build目录下的core文件:
vim build/target/product/core.mk
我把里面的Launcher2替换成了自己的XLauncher,保存退出,然后重新make一遍。
刷机,运行,看效果。
注意事项
在实际开发过程中,我三种方式都使用了,但是没有效果,后来才发现,修改了build下的mk文件是需要整编的。
也就是说需要先make clean一下。我才用的第三种方法,亲测成功,有问题的同学可以在下面留言。
- 实现唯一Launcher
- Android 修改framework实现 全局唯一launcher
- Launcher简要分析:Launcher的功能实现
- Launcher简要分析:Launcher的功能实现
- launcher 实现过程
- Android 实现 Launcher
- Launcher的简单实现,
- 让自己的Launcher成为系统中的唯一
- 让你定制的Launcher成为系统中唯一的Launcher
- Android 如何将定制的Launcher成为系统中唯一的Launcher
- Android 如何将定制的Launcher成为系统中唯一的Launcher
- Android的Launcher成为系统中第一个启动的,也是唯一的Launcher
- Android 如何将定制的Launcher成为系统中唯一的Launcher
- 如何将定制的Launcher成为系统中唯一的Launcher
- MFC实现游戏Launcher效果
- Launcher实现左右循环滑动
- Android Launcher workspace缩略图实现
- android APP如何实现launcher
- Android性能优化-内存泄漏(下)
- Linux基础学习——unit14
- windows系统下Python环境的搭建
- android系统源码中添加app源码(源码部署移植)
- 安卓帧动画的实现
- 实现唯一Launcher
- 每天一道算法题(一) (动态规划算法)背包问题Java实现
- 剑指offer-从尾到头打印链表
- Access denied for user 'Administrator'@'172.16.12.34' (using password: YES)
- 修改System.UI并编译
- 数据库三大范式
- Manacher算法总结
- Multiple Inheritance in C++
- MySQL锁类型以及子查询锁表问题、解锁