zoukankan      html  css  js  c++  java
  • Android更改桌面应用程序launcher的两种方式

    http://blog.csdn.net/mdx20072419/article/details/9632779/

    launcher,也就是android的桌面应用程序。下图是我正在使用的魅族手机的launcher应用程序:

     

       接下来我们要开发一个自己的launcher,使其替代系统的默认launcher。
    怎样使我们的应用程序成为一个launcher?

       首先我们要有一个自己的Android应用,在这里,我使用最简单的应用程序Hello,

    使用eclipse创建Android项目我这里就省略了,直接上图

     

       来看看我的AndroidManifest.xml

     

     

    [cpp] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. <?xml version="1.0" encoding="utf-8"?>  
    2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
    3.     package="com.example.hello"  
    4.     android:versionCode="1"  
    5.     android:versionName="1.0" >  
    6.   
    7.     <uses-sdk  
    8.         android:minSdkVersion="7"  
    9.         android:targetSdkVersion="7" />  
    10.   
    11.     <application  
    12.         android:allowBackup="true"  
    13.         android:icon="@drawable/ic_launcher"  
    14.         android:label="@string/app_name"  
    15.         android:theme="@style/AppTheme" >  
    16.         <activity  
    17.             android:name="com.example.hello.MainActivity"  
    18.             android:label="@string/app_name" >  
    19.             <intent-filter>  
    20.                 <action android:name="android.intent.action.MAIN" />  
    21.   
    22.                 <category android:name="android.intent.category.LAUNCHER" />  
    23.             </intent-filter>  
    24.         </activity>  
    25.     </application>  
    26.   
    27. </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。

     

     

    [cpp] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. public static final void init2() {  
    2. 501         Log.i(TAG, "Entered the Android system server!");  
    3. 502         Thread thr = new ServerThread();  
    4. 503         thr.setName("android.server.ServerThread");  
    5. 504         thr.start();                                                                              
    6. 505     }  

       init2中启动ServerThread线程,ServerThread中启动了一系列的服务,比如ActivityManagerService,EntropyService等等。

       当这些服务起来以后,开始  ((ActivityManagerService)ActivityManagerNative.getDefault()).systemReady() 在systemReady后开始开始启动Launcher。

    frameworksaseservicesjavacomandroidserveramActivityManagerService.java

     

    [cpp] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. 8422     public void systemReady(final Runnable goingCallback) {  
    2. 8423         // In the simulator, startRunning will never have been called, which  
    3. 8424         // normally sets a few crucial variables. Do it here instead.   
    4.         .........................  
    5. 8594           resumeTopActivityLocked(null);  
    6.      }  

     

    frameworksaseservicesjavacomandroidserveramActivityManagerService.java

    [cpp] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. 2576     private final boolean resumeTopActivityLocked(HistoryRecord prev) {                         
    2. 2577         // Find the first activity that is not finishing.  
    3. 2578         HistoryRecord next = topRunningActivityLocked(null);  
    4. 2579   
    5. 2580         // Remember how we'll process this pause/resume situation, and ensure  
    6. 2581         // that the state is reset however we wind up proceeding.  
    7. 2582         final boolean userLeaving = mUserLeaving;  
    8. 2583         mUserLeaving = false;  
    9. 2584   
    10. 2585         if (next == null) {  
    11. 2586             // There are no more activities!  Let's just start up the  
    12. 2587             // Launcher...  
    13. 2588             return startHomeActivityLocked();  
    14. 2589         }  

    frameworksaseservicesjavacomandroidserveramActivityManagerService.java

    [cpp] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. 2457     private boolean startHomeActivityLocked() {                                                 
    2. 2458         if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL  
    3. 2459                 && mTopAction == null) {  
    4. 2460             // We are running in factory test mode, but unable to find  
    5. 2461             // the factory test app, so just sit around displaying the  
    6. 2462             // error message and don't try to start anything.  
    7. 2463             return false;  
    8. 2464         }  
    9. 2465         Intent intent = new Intent(  
    10. 2466             mTopAction,  
    11. 2467             mTopData != null ? Uri.parse(mTopData) : null);  
    12. 2468         intent.setComponent(mTopComponent);  
    13. 2469         if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {  
    14. 2470             intent.addCategory(Intent.CATEGORY_HOME);  
    15. 2471         }  


    frameworks/base/core/java/android/content/Intent.java

    [cpp] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. 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添加两行代码:

       

    [cpp] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. <category android:name="android.intent.category.HOME" />    
    2. <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.如果不知道修改哪些地方,可以使用如下命令去查找:

     

    [cpp] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. grep  CATEGORY_HOME  -l  *  -R  

    查找到的文件大概有这些:


    将上述文件中和CATEGORY_HOME相关的所有的地方,都改成CATEGORY_FS_HOME即可。

     

       然后,我们可以把之前的应用程序hello的AndroidManifest.xml更改如下:

     

     

    [cpp] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. <?xml version="1.0" encoding="utf-8"?>  
    2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
    3.     package="com.example.hello"  
    4.     android:versionCode="1"  
    5.     android:versionName="1.0" >  
    6.   
    7.     <uses-sdk  
    8.         android:minSdkVersion="7"  
    9.         android:targetSdkVersion="7" />  
    10.   
    11.     <application  
    12.         android:allowBackup="true"  
    13.         android:icon="@drawable/ic_launcher"  
    14.         android:label="@string/app_name"  
    15.         android:theme="@style/AppTheme" >  
    16.         <activity  
    17.             android:name="com.example.hello.MainActivity"  
    18.             android:label="@string/app_name" >  
    19.             <intent-filter>  
    20.                 <action android:name="android.intent.action.MAIN" />  
    21.                  <category android:name="android.intent.category.FS_HOME" />   
    22.                 <category android:name="android.intent.category.DEFAULT" />                                                                        <category android:name="android.intent.category.MONKEY" />   
    23.             </intent-filter>  
    24.         </activity>  
    25.     </application>  
    26.   
    27. </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
    frameworksaseservicesjavacomandroidserveramActivityManagerService.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, nullnull0, aInfo,
                            nullnull000falsefalsenull);
                }
            }
             
             
            return true;
        }

    添加后的方法全部内容如上,重新编译android,烧录,开机就能够自动进入自定义的launcher

    可以通过系统设置取消该launcher的默认设置,取消之后按home键会弹出launcher选择提示框

    1
    frameworksasecorejavacomandroidinternalappResolverActivity.java

    ResolverActivity类就是选择打开方式的弹出框

  • 相关阅读:
    Java中的数据类型
    SSE2 Intrinsics各函数介绍[转]
    mac terminal的使用技巧
    vi打开二进制文件
    svn
    fitness
    <正见>摘抄
    管理自己
    [C++程序设计]基于对象的程序设计 基于对象的程序设计
    [C++程序设计]引用
  • 原文地址:https://www.cnblogs.com/jukan/p/5756570.html
Copyright © 2011-2022 走看看