http://blog.csdn.net/mdx20072419/article/details/9632779/
launcher,也就是android的桌面应用程序。下图是我正在使用的魅族手机的launcher应用程序:
接下来我们要开发一个自己的launcher,使其替代系统的默认launcher。
怎样使我们的应用程序成为一个launcher?
首先我们要有一个自己的Android应用,在这里,我使用最简单的应用程序Hello,
使用eclipse创建Android项目我这里就省略了,直接上图
来看看我的AndroidManifest.xml
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.example.hello"
- android:versionCode="1"
- android:versionName="1.0" >
- <uses-sdk
- android:minSdkVersion="7"
- android:targetSdkVersion="7" />
- <application
- android:allowBackup="true"
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name"
- android:theme="@style/AppTheme" >
- <activity
- android:name="com.example.hello.MainActivity"
- android:label="@string/app_name" >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
- </manifest>
我们知道,一个应用程序可以有多个Activity,每个Activity是同级别的。那么在启动程序时,最先启动哪个Activity呢?有些程序可能需要显示在程 序列表里,有些不需要。怎么定义呢?android.intent.action.MAIN决定应用程序最先启动的Activity ,android.intent.category.LAUNCHER决定应用程序是否显示在程序列表里。Main和LAUNCHER同时设定才有意义,如果有多个同级的Activity都有过滤器
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
则只有最前面的Activity的 <action android:name="android.intent.action.MAIN" /> 有 效,启动该程序时,执行的是该Activity。且在程序列表中有多个图标,这些Activity都在程序列表中显示,该Application有多个入 口,执行不同的Activity,但是整个程序的主入口(整个程序最先运行的那个activity)只有最先定义的那个Activity。
如 果一个应用没有LAUNCHER则该apk仍能安装到设备上,但是在主程序图中看不到。如果给那个Activity 设定了LAUNCHER,且同时设定了Main,则这个Activity就可出现在程序图中;如果没有Main,则不知启动哪个Activity,故也不 会有图标出现。
那如果我们要把一个应用程序做为桌面应用程序,该怎么办呢?
如果了解Android的启动流程的同学都知道,Zygote启动SystemServer,SystemServer的main函数开始启动各种服务。 首先启动init1,然后启动init2. init1这个方法是被Zygote调用来初始化系统的,init1会启动native的服务如SurfaceFlinger,AudioFlinger等等,这些工作做完以后会回调init2来启动Android的service。
- public static final void init2() {
- 501 Log.i(TAG, "Entered the Android system server!");
- 502 Thread thr = new ServerThread();
- 503 thr.setName("android.server.ServerThread");
- 504 thr.start();
- 505 }
init2中启动ServerThread线程,ServerThread中启动了一系列的服务,比如ActivityManagerService,EntropyService等等。
当这些服务起来以后,开始 ((ActivityManagerService)ActivityManagerNative.getDefault()).systemReady() 在systemReady后开始开始启动Launcher。
frameworks\base\services\java\com\android\server\am\ActivityManagerService.java
- 8422 public void systemReady(final Runnable goingCallback) {
- 8423 // In the simulator, startRunning will never have been called, which
- 8424 // normally sets a few crucial variables. Do it here instead.
- .........................
- 8594 resumeTopActivityLocked(null);
- }
frameworks\base\services\java\com\android\server\am\ActivityManagerService.java
- 2576 private final boolean resumeTopActivityLocked(HistoryRecord prev) {
- 2577 // Find the first activity that is not finishing.
- 2578 HistoryRecord next = topRunningActivityLocked(null);
- 2579
- 2580 // Remember how we'll process this pause/resume situation, and ensure
- 2581 // that the state is reset however we wind up proceeding.
- 2582 final boolean userLeaving = mUserLeaving;
- 2583 mUserLeaving = false;
- 2584
- 2585 if (next == null) {
- 2586 // There are no more activities! Let's just start up the
- 2587 // Launcher...
- 2588 return startHomeActivityLocked();
- 2589 }
frameworks\base\services\java\com\android\server\am\ActivityManagerService.java
- 2457 private boolean startHomeActivityLocked() {
- 2458 if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL
- 2459 && mTopAction == null) {
- 2460 // We are running in factory test mode, but unable to find
- 2461 // the factory test app, so just sit around displaying the
- 2462 // error message and don't try to start anything.
- 2463 return false;
- 2464 }
- 2465 Intent intent = new Intent(
- 2466 mTopAction,
- 2467 mTopData != null ? Uri.parse(mTopData) : null);
- 2468 intent.setComponent(mTopComponent);
- 2469 if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
- 2470 intent.addCategory(Intent.CATEGORY_HOME);
- 2471 }
frameworks/base/core/java/android/content/Intent.java
- 1881 public static final String CATEGORY_HOME = "android.intent.category.HOME";
根据上面代码可知,在寻找Launcher的时候是根据HOME的filter(在Manifest中定义的<category android:name=”android.intent.category.HOME” />)来过滤。 然后根据filter出来的HOME来启动,如果只有一个HOME,则启动这个HOME,如果用户自己装了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,但同时不弹出选择HOME的界面,我们也不希望用户修改我们的home,比如我们的home上放了好多广告,以及强制安装的程序,不希望用户把它干掉。
在这里,我们就可以写一个自己私有的filter选项,然后用这个选项来过滤HOME. 一般情况下我们使用Manifest中定义的<category android:name="android.intent.category.HOME"来过滤的,我们现在增加一个私有的FS_HOME过滤。
这里我们有一种比较暴力的更改方法,就是把系统中原有的public static final String CATEGORY_HOME = "android.intent.category.HOME";
更改成public static final String CATEGORY_FS_HOME = "android.intent.category.FS_HOME";
然后修改和CATEGORY_HOME相关的所有的地方,都改成CATEGORY_FS_HOME.如果不知道修改哪些地方,可以使用如下命令去查找:
- grep CATEGORY_HOME -l * -R
查找到的文件大概有这些:
将上述文件中和CATEGORY_HOME相关的所有的地方,都改成CATEGORY_FS_HOME即可。
然后,我们可以把之前的应用程序hello的AndroidManifest.xml更改如下:
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.example.hello"
- android:versionCode="1"
- android:versionName="1.0" >
- <uses-sdk
- android:minSdkVersion="7"
- android:targetSdkVersion="7" />
- <application
- android:allowBackup="true"
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name"
- android:theme="@style/AppTheme" >
- <activity
- android:name="com.example.hello.MainActivity"
- android:label="@string/app_name" >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.FS_HOME" />
- <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.MONKEY" />
- </intent-filter>
- </activity>
- </application>
- </manifest>
重新编译我们的应用程序,放到我们开发板相应目录下,就可以看到我们自己的Launcher了!
Android设置默认Launcher
http://huamm.blog.51cto.com/5646020/1550305
当系统存在多个launcher时,若没有设置默认launcher,开机启动后会弹出提示框,罗列所有launcher,用户选择并设置了默认launcher后,按home键以及以后重启都会进入默认的launcher。
现在,我希望系统能直接就进入我设定的launcher而不是弹出框后选择然后设置
网上大部分的做法就是修改
1
|
packages/apps/Provision/src/com/android/DefaultActivity.java |
和
1
|
frameworks/base/services/java/com/android/server/pm/PackageManagerService.java |
两个文件,给个相对比较好看一点的链接http://blog.csdn.net/z_guijin/article/details/8964890
我按照这个做不能达到预期的效果,
/////////////////////////////////////////添加内容////////////////////////////////////////
后期修改包名,发现开机自启动出现问题,然后弄了两天,终于差不多弄清楚了。果然,欠下的债迟早得还!
其实修改DefaultActivity.java就能够完成开机自启动,只是当时我的程序有问题,所以没有达到预期效果
当然下面修改ActivityManagerService.java也能完成开机自启动
这两者的区别在于
DefaultActivity.java只是在第一次启动时执行,如果修改了默认launcher后不可恢复
ActivityManagerService.java在每次启动时执行,每次都默认启动设定的launcher,当然,如果设定的launcher存在的话,设置其他的launcher为默认会无效,因为重新启动时setDefaultLauncher()会将当前默认launcher清除。只有在卸载了设定默认启动的launcher后才能设置其他launcher为默认启动.
//////////////////////////////////////////////////////////////////////////////////////////////////////////
修改多次搜索关键字,得到另一篇文章,大致看了下,感觉不会有效,但是已经绝望了就试了试,竟然解决了问题http://blog.csdn.net/jia4525036/article/details/18036765
这篇文章有借鉴之处,直接使用时会发现有些字段是上下文中没有的,故写下此文记录一下。
修改文件
1
|
frameworks\base\services\java\com\android\server\am\ActivityManagerService.java |
添加一个方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
|
private void setDefaultLauncher() {
// get default component
String packageName = "com.coship.factorytest" ; //默认launcher包名
String className = "com.coship.factorytest.MainActivity" ; ////默认launcher入口
IPackageManager pm = ActivityThread.getPackageManager();
//判断指定的launcher是否存在
if (hasApkInstalled(packageName)) {
Slog.i(TAG, "defautl packageName = " + packageName + ", default className = " + className);
//清除当前默认launcher
ArrayList<IntentFilter> intentList = new ArrayList<IntentFilter>();
ArrayList<ComponentName> cnList = new ArrayList<ComponentName>();
mContext.getPackageManager().getPreferredActivities(intentList, cnList, null );
IntentFilter dhIF = null ;
for ( int i = 0 ; i < cnList.size(); i++) {
dhIF = intentList.get(i);
if (dhIF.hasAction(Intent.ACTION_MAIN) && dhIF.hasCategory(Intent.CATEGORY_HOME)) {
mContext.getPackageManager().clearPackagePreferredActivities(cnList.get(i).getPackageName());
}
}
//获取所有launcher activity
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
List<ResolveInfo> list = new ArrayList<ResolveInfo>();
try {
list = pm.queryIntentActivities(intent, intent.resolveTypeIfNeeded(mContext.getContentResolver()), PackageManager.MATCH_DEFAULT_ONLY);
} catch (RemoteException e) {
throw new RuntimeException( "Package manager has died" , e);
}
// get all components and the best match
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_MAIN);
filter.addCategory(Intent.CATEGORY_HOME);
filter.addCategory(Intent.CATEGORY_DEFAULT);
final int N = list.size();
Slog.d(TAG, "N:::::hyhyhyhy:::: = " + N);
//设置默认launcher
ComponentName launcher = new ComponentName(packageName, className);
ComponentName[] set = new ComponentName[N];
int defaultMatch = 0 ;
for ( int i = 0 ; i < N; i++) {
ResolveInfo r = list.get(i);
set[i] = new ComponentName(r.activityInfo.packageName, r.activityInfo.name);
Slog.d(TAG, "r.activityInfo.packageName:::::hyhyhyhy:::: = " + r.activityInfo.packageName);
Slog.d(TAG, "r.activityInfo.name:::::hyhyhyhy:::: = " + r.activityInfo.name);
if (launcher.getClassName().equals(r.activityInfo.name)) {
defaultMatch = r.match;
}
}
try {
pm.addPreferredActivity(filter, defaultMatch, set, launcher);
} catch (RemoteException e) {
throw new RuntimeException( "com.coship.factorytest.MainActivity : Package manager has died" , e);
}
} //end if
}
private static boolean hasApkInstalled(String pkgname) {
try {
mSelf.mContext.getPackageManager().getPackageInfo(pkgname, 0 );
} catch (Exception e) {
Slog.d(TAG, "PackageManager.NameNotFoundException: = " + e.getMessage());
return false ;
}
return true ;
}
|
然后在ActivityManagerService类中的
1
|
boolean startHomeActivityLocked()
|
方法第一行调用上面添加的
1
|
setDefaultLauncher() |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
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 ;
}
/////////////////////////////////////////// setDefaultLauncher();
/////////////////////////////////////////// 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);
}
ActivityInfo aInfo =
intent.resolveActivityInfo(mContext.getPackageManager(),
STOCK_PM_FLAGS);
if (aInfo != null ) {
intent.setComponent( new ComponentName(
aInfo.applicationInfo.packageName, aInfo.name));
// Don't do this if the home app is currently being
// instrumented.
ProcessRecord app = getProcessRecordLocked(aInfo.processName,
aInfo.applicationInfo.uid);
if (app == null || app.instrumentationClass == null ) {
intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
mMainStack.startActivityLocked( null , intent, null , null , 0 , aInfo,
null , null , 0 , 0 , 0 , false , false , null );
}
}
return true ;
}
|
添加后的方法全部内容如上,重新编译android,烧录,开机就能够自动进入自定义的launcher
可以通过系统设置取消该launcher的默认设置,取消之后按home键会弹出launcher选择提示框
1
|
frameworks\base\core\java\com\android\internal\app\ResolverActivity.java |
ResolverActivity类就是选择打开方式的弹出框