Android默认启动器原理

一. 描述:

在Android中默认启动器,即能列出设备上的其他应用。点选任意列表项会启动相应应用。
可启动应用就是指点击主屏幕或启动器界面上的图标就能打开的应用。
大致思路:使用PackageManager获取所有可启动主Activity。可启动主activity都带有包含MAIN操作和LAUNCHER类别的intent过滤器。
其所需的知识点:intent、intent过滤器、以及Android应用间是如何交互的。


二. 实现过程:

  1. 创建一个隐式intent并从PackageManager那里获取匹配他的所有activity,然后向PackageManager查询activity总数:
Intent startupIntent=new Intent(Intent.ACTION_MAIN);
startupIntent.addCategory(Intent.CATEGORY_LAUNCHER);PackageManager pm=getActivity().getPackageManager();
List<ResolveInfo> activities=pm.queryIntentActivities(startupIntent,0);Log.i(TAG,“手机上一共有 ”+activities.size()+"个应用");

  定义了MAIN/LAUNCHER intent过滤器的activity是应用的主要入口点。 它只负责做好作为应用主要入口点要处理的工作,通常不关心自己是否为默认的主要入口点,所以可以不包含CATEGORY_DEFAULT类别。
  因为MAIN/LAUNCHER intent过滤器并不一定包含CATEGORY_DEAFULT类别,因此不能保证可以与startActivity()方法发送的隐式intent匹配。转而使用intent直接向PackageManager查询带有MAIN/LAUNCHER intent过滤器的activity。

  对比使用隐式intent发送文字等,先创建隐士intent,再将其封装在选择器intent中,最后调用startActivity(intent)方法发送给操作系统:

Intent i =new Intent(Intent.ACTION_SEND);
//。。附加extras
i=Intent.createChooser(i,“xxxx”);
startActivity(i);

  这里没有用上述代码,原因很简单:MAIN/LAUNCHER intent过滤器可能无法与通过startActivity()方法发送的MAIN/LAUNCHER隐式intent相匹配。startActivity(Intent)意味着“启动匹配隐式intent的默认activity”,而不是想当然的”启动匹配隐士intent的activity“。
调用startActivity(Intent)/startActivityForResult()方法发送隐式intent时,操作系统会默认为目标intent添加Intent,CATEGORY_DEFAULT类别。所以,如果希望Intent过滤器匹配startActivity()方法发送的隐式intent,就必须在对应的intent过滤器中包含DEFAULT类别。

  1. 在视图中展示查询到的activity标签:
    activity标签是用户可以识别的展示名称。查询到的acitivity都是启动acitivity,那么标签名通常也就是应用名。在PackageManager返回的ResolveInfo对象中,可以获取activity标签和其他的一些元数据。
  2. 对activity标签排序:
    使用ResolveInfo,loadLabel(PackageManager)方法,对ResolveInfo对象中的activity标签按首字母排序。
Collections.sort(activities,new Comparator<ResolveInfo>(){public int compare(ResolveInfo a,ResolveInfo b){return String.CASE_INSENSITIVE_ORDER.compare(a.loadLabel(pm).toString(),b.loadLabel(pm).toString());}
});
  1. 启动目标Activity:
      在运行时创建显式Intent,点击任一应用label,启动对应的activity要创建启动acitivty的显式intent,需要从ResolveInfo对象中获取activity的包名和类名。这些信息从ResolveInfo对象的ActivityInfo中获取。
    (注:了解ResolveInfo类,去 developer.android.com 官网去查阅)
    可以注册列表监听器,并使用ActivityInfo对象中的数据信息,创建一个显式intent并启动目标Activity:
ActivityInfo activityInfo=mResolveInfo.activityInfo;
Intent i=new Intent(Intent.ACTION_MAIN).setClassName(activityInfo.applicationInfo.packageName,activityInfo.name);
startActivity(i);

上述代码中,发送了ACTION_MAIN操作,发送的intent是否包含操作,对于大多数应用来说没什么差别。不过有些应用的启动行为可能会有所不同。取决于不同的启动要求,同样的activity可能会显示不同的用户界面。我们应该最好能明确启动意图,以便让activity完成它该完成的任务。
  上面的代码中还使用了这个方法:

public Intent setClassName(String packageName,String className)

这是使用包名和类名创建显式intent时采用的Intent方法。这个平常创建显式intent的方式有点不同。通常,我们使用的是接受Context和Class对象的Intent构造方法:

public Intent(Context packageContext,Class<?>cls)

该构造方法使用传入的参数来获取Intent需要的ComponentName。ComponentName由包名和类名共同组成。传入Activity和Class创建Intent时,构造方法会通过Activity类自行确定全路径包名。也可自己通过包名和类名创建ComponentName,然后使用下面的 Intent方法创建显式Intent:

public Intent setComponent(ComponentName component)

但是,setClassName()能够自动创建组件名。用它可以简化代码。

经过这几个步骤,就可以使用自定义的APP来启动你本机上的一些应用了。

最终结果:


三. 分离任务:

在app运行时,android使用任务来跟踪用户的状态。通过android默认启动应用打开的应用都有自己的任务。然而,这不适用于上面代码中创建的APP。即上述代码中创建的APP打开手机上的应用,然后按任务列表按钮,会发现只有android默认启动应用app的任务图标,而没有目标app的任务,而目标任务的视图却在android默认启动应用app的任务视图中。要想让目标app独立出自己的任务,需要明白:任务和回退栈

  • 任务和回退栈:
    任务是一个acitivty栈。栈底部的activity通常称为基activity。栈顶的activitiy是用户能够看到的。如果按后退键,栈顶的activiity会弹出栈外。如果用户看到的是基activity,按后退键,系统就会回到主屏幕。
    默认情况下,新的activity都是在当前任务中启动的。在android默认启动应用app中,无论何时启动新activity,它都会被添加到当前任务中。即使要启动的activity不属于本应用(在本app中打开第三方app),它也同样在当前任务中启动。
    在当前任务中启动activity的好处是:用户可以在任务内而不是在应用层级间导航返回。

  • 在任务间切换:
    在不影响各个任务状态的情况下,最近任务列表按钮(home键旁边的那个建)可以让我们在任务间切换。例如在加入联系人信息,然后转到抖音去看视频,这时就启动了2个任务。如果再回到联系人应用,我们在2个任务中所处的操作状态都会被保存下来。

  • 启动新任务:
    有时需要在当前任务中启动activity,有时又需要在新任务中启动activity。
    当前,android默认启动应用app启动的任何activity都会被添加到本应用的任务中。即打开最近任务列表按钮可以看到目标actiivty视图显示在本应用的视图中。
    现在, 我们需要在新任务中启动activity,如下图效果:

这样点击启动器中的应用就可以让应用拥有自己的任务,用户可以在运行的应用间自由切换了。
为了在启动新activity时启动新任务,需要为intent添加一个标志:

Intent i=new Intent(xxx).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

然后运行程序,就能看见如上面效果图的显示效果了。目标应用处于了一个单独的任务中,如果从启动器应用中再次启动目标应用,也不会创建第二个相同的目标任务。FLAG_ACTIVITY_NEW_TASK标志控制每个activity仅仅创建一个任务。


四. 使用自定义的APP应用作为设备主屏幕:

试想你是用户,你会愿意专门下载一个APP来每次启动你手机上的应用吗?肯定不会啊,浪费内存。

因此,以替换Android主界面的方式使用我们自定义的APP应用会更加合适一些。
实现操作只需要修改清单文件,但是不同机型的限制,此方法不一定能成功,和真机设备有关。

  • 步骤:
    打开项目的AndroidManifest,xml,向Intent主过滤器中添加节点定义:
    注:一个intent-filter中只允许存在一个action,但允许存在多个category.
<intent-filter><action android:name="android.intent.action.MAIN"/><category android:name="android.intent.category.LAUNCHER"/><category android:name="android.intent.category.HOME"/><category android.name="android.intent.category.DEFAULT"/>
</intent-filter>

添加完HOME和DEAFULT类别定义后,应用的activity会成为可选的主界面。按主屏幕键会弹出对话框,本应用成了主界面的可选项。

  清除该设置,可以在手机上的”设置-》应用-》本应用名-》清除默认按钮“。


补充:

光有每个应用activity的名字还不够,我们还可以使用ResolveInfo.loadIcon() 来为每个应用加载显示图标


扩展:关于WPS软件中的并发打开(多开效果)

  • 效果图:
  • 原理:并发文档。
    所打开的多个wps中的pdf文档,这些文档不会被添加到WPS应用任务中,而是添加到它自己的独立任务中。对于以android.intent.action.SEND或者action.intent.action.SEND_MULTIPLE启动的actiivty,隐式intent选择器会创建独立的新任务。
    这种现象就是并发文档(concurrent document)。有了并发文档就可以 为运行的应用动态创建任意数目的任务。
    比如WPS可以同时打开并编辑多份文档,这些文档编辑activity都处在独立的任务中。

要i想应用启动多个任务,可采用如下2种方式:

  1. 给Intent加上Intent.FLAG_ACTIVITY_NEW_DOCUMENT标签,再调用startActivity()方法。
  2. 在清单文件中,为activity设置如下documentLaunchMode:
<activityandroid:name="xxxx"android:documentLaunchMode="intoExisting"/>

使用上述的方法后,一份文档只对应一个任务。如果发送带有和已存在任务相同数据的intent,系统就不会再创建新任务。如果无论如何都i想创建新任务,就给intent同时加上Intent.FLAG_ACTIVITY_NEW_DOCUMENT、Intent,FLAG_ACITVITY_MULTIPLE_TASK标签,或者在清单文件中的documentLaunchMode的属性值改为always.

Android默认启动器原理相关推荐

  1. android 默认启动器,安卓启动器_安卓默认启动器如何替换?

    安卓默认启动器如何替换? 默认启动器的更改,可以通过下载桌面启动器来清除原有启动器的默认设置即可. 在Android中默认启动器,即能列出设备上的其他应用.点选任意列表项会启动相应应用.可启动应用就是 ...

  2. Android 插件化原理学习 —— Hook 机制之动态代理

    前言 为了实现 App 的快速迭代更新,基于 H5 Hybrid 的解决方案有很多,由于 webview 本身的性能问题,也随之出现了很多基于 JS 引擎实现的原生渲染的方案,例如 React Nat ...

  3. Android 插件化原理解析——Activity生命周期管理

    之前的 Android插件化原理解析 系列文章揭开了Hook机制的神秘面纱,现在我们手握倚天屠龙,那么如何通过这种技术完成插件化方案呢?具体来说,插件中的Activity,Service等组件如何在A ...

  4. android r.java 原理,深入理解Android消息处理系统原理

    Android应用程序也是消息驱动的,按道理来说也应该提供消息循环机制.实际上谷歌参考了Windows的消息循环机制,也在Android系统中实现了消息循环机制. Android通过Looper.Ha ...

  5. Android 系统(175)---Android硬件加速原理与实现简介

    Android硬件加速原理与实现简介 在手机客户端尤其是Android应用的开发过程中,我们经常会接触到"硬件加速"这个词.由于操作系统对底层软硬件封装非常完善,上层软件开发者往往 ...

  6. java重置输出窗口_java – 如何重置默认启动器/主屏幕替换?

    这不是直接可能的,Android开发人员已声明他们不希望任何应用程序更改用户的首选项.但是,根据Android如何维护这些首选项,有一种解决方法. 让你的清单看起来像这样: android:name= ...

  7. Android插件化原理—ClassLoader加载机制

    前面<Android 插件化原理学习 -- Hook 机制之动态代理>一文中我们探索了一下动态代理 hook 实现了 启动没有在 AndroidManifest.xml 中显式声明的 Ac ...

  8. Android源代码编译原理与前期准备

    本文的主要内容是解决在Android源代码的编译过程中出现的各种问题. 大家都知道,Android是开源的,可以在Android Open Source Project(点击打开链接)下载.下载的流程 ...

  9. android触摸震动原理

    android触摸震动原理 作者: qw15262901392@gmail.com http://blog.csdn.net/a345017062/article/details/6417929 ht ...

最新文章

  1. 为什么你“越努力,越焦虑”?背后原因,99%的人都忽略了……
  2. Struts 2 标签库详解
  3. Docker容器管理总结
  4. 用vmware安装win7虚拟机(windows10系统)
  5. 【算法】螺旋方阵 上交OJ1021
  6. MSSQL返回季度开始月和某月是第几季度
  7. (转)关于eclipse的TestNG的插件安装方法
  8. Wireshark图解教程(简介、抓包、过滤器)
  9. python isalnum函数_探究Python中isalnum()方法的使用
  10. ActiveMQ学习笔记(2)——JMS消息模型
  11. 微服务开发及部署_基于 Kubernetes 的微服务部署即代码
  12. java字符串拼接_Java 8中字符串拼接新姿势:StringJoiner
  13. GitHub高赞,一款足以取代迅雷的开源下载工具
  14. python控制安捷伦频谱仪_安捷伦频谱仪使用说明
  15. 文件系统性能测试工具 IOZONE工具
  16. 受力分析软件_基于非线性分析的建筑结构设计与优化
  17. 创业者如何吸引风险投资商
  18. 硬脆材料划片机的工艺参数研究
  19. matlab ext2int,PF_MATLAB_new 一个非常不错的粒子滤波工具箱,基于面向对象的思 实 实现非线性 ,包 238万源代码下载- www.pudn.com...
  20. 在C#中使用钩子实现Alt+F4健窗口最小化功能

热门文章

  1. Command命令模式
  2. win10家庭版可以用c语言,手把手教你通过bat批处理开启win10家庭版组策略的图文教程-系统操作与应用 -亦是美网络...
  3. 判断当前的日期是否是节假日、调休、补班、工作日、双休日
  4. java localstorage_localStorage使用总结
  5. Android 打开拨号界面
  6. ThreadPool
  7. 用Python代码实现电子时钟
  8. Unix Socket
  9. 八数码问题a*算法c语言,八数码问题的A*算法求解
  10. imx6ull 正点原子设备树适配韦东山的开发板 (一)顺利启动,配置led,button