安卓默认启动器

Android默认启动器原理 一. 描述: 在Android中默认启动器,即能列出设备上的其他应用。点选任意列表项会启动相应应用。 可启动应用就是指点击主屏幕或启动器界面上的图标就能打开的应用。 大致思路 :使用PackageManager获取所有可启动主Activity

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:

<activity
	android:name="xxxx"
	android:documentLaunchMode="intoExisting"/>

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

知秋君
上一篇 2024-09-10 15:36
下一篇 2024-09-10 15:02

相关推荐