- 类似于双11与双12活动,app的启动图标会自动进行更换。原理很简单,通过PackageManager来启动或者禁止组件。
预埋多个app入口,放置不同的icon。通过改变启动的activity来更换图片
常规启动
首先看下在清单文件中常规启动设置如下:
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
action 代表了系统中已经定义了一系列常用的动作, ACTION_MAIN:Android Application的入口,每个Android应用必须且只能包含一个此类型的Action声明。
category 指定当前动作(Action)被执行的环境 ,CATEGORY_LAUNCHER:设置该组件为在当前应用程序启动器中优先级最高的Activity,通常为入口ACTION_MAIN配合使用。
activity-alias介绍
这里首先要介绍下清单文件中一个不常用的标签,activity-alias,是作为一个已存在的activity的别名,则应该可以通过该别名标签声明快速打开目标Activity。因此activity-alias可用来设置某个Activity的快捷入口,可以放在桌面上或者通过该别名被其他组件快速调起。常用属性如下:
- android:enable 该属性用来决定目标Activity可否通过别名被系统实例化,默认为true。需要注意的是application也有enable属性,只用当它们同时为true时,activity-alias的enable才生效。是否为启动页
- android:exported 该属性为true的话,则目标Activity可被其他应用调起,如为false则只能被应用自身调起。其默认值根据activity-alias是否包含intent-filter元素决定,如果有的话,则默认为true;没有的话则为false。其实也很好理解,如果有intent-filter,则目标Activity可以匹配隐式Intent,因此可被外部应用唤起;如果没有intent-filter,则目标Activity要被调起的话必须知道其精确类名,因为只有应用本身才知道精确类名,所以此时默认为false。
- android:icon 该属性允许自定义icon,可以不同于应用本身在桌面的icon。如果需要在桌面上创建快捷入口,也许产品会要求换个不同的icon。设置不同启动图标
- android:label 该属性类似于android:icon,设置不同名称。
- android:name 。activity元素的name属性实质上都会指向一个具体的Activity类,而activity-alias的name属性仅作为一个唯一标识而已。
- android:permission 该属性指明了通过别名声明调起目标Activity所必需的权限。
android:targetActivity 该属性指定了目标Activity,即通过activity-alias调起的Activity是哪个,此属性其实类似于activity标签中的name属性,需要规范的Activity包名类名。更换图标必须指向同一activity
多个启动入口清单文件配置
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity-alias
android:name=".Test11"
android:label="切换标签11"
android:enabled="false"
android:targetActivity=".MainActivity"
android:icon="@mipmap/laugch_11"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
<activity-alias
android:name=".Test12"
android:label="切换标签12"
android:enabled="false"
android:targetActivity=".MainActivity"
android:icon="@mipmap/laugch_12"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
代码中动态设置app入口
要做到动态切换预埋的别名入口,需要用到PackageManager来管理组件,其中有setComponentEnabledSetting来启动或者禁止组件,这里就是来禁止默认,启动双11的别名入口达到更换app图标。浅层次源码:
/**
* Set the enabled setting for a package component (activity, receiver, service, provider).
* This setting will override any enabled state which may have been set by the component in its
* manifest.
*
* @param componentName The component to enable
* @param newState The new enabled state for the component. The legal values for this state
* are:
* {@link #COMPONENT_ENABLED_STATE_ENABLED},
* {@link #COMPONENT_ENABLED_STATE_DISABLED}
* and
* {@link #COMPONENT_ENABLED_STATE_DEFAULT}
* The last one removes the setting, thereby restoring the component's state to
* whatever was set in it's manifest (or enabled, by default).
* @param flags Optional behavior flags: {@link #DONT_KILL_APP} or 0.
*/
public abstract void setComponentEnabledSetting(ComponentName componentName,
int newState, int flags);
setComponentEnabledSetting参数介绍
componentName: 组件名字
newState:组件新的状态,可以设置三个值,分别是如下:
不可用状态:COMPONENT_ENABLED_STATE_DISABLED
可用状态:COMPONENT_ENABLED_STATE_ENABLED
默认状态:COMPONENT_ENABLED_STATE_DEFAULT
flags:行为标签,值可以是DONT_KILL_APP或者0。 0说明杀死包含该组件的app
了解了功能就可以在代码中设置了:
拿到包管理器和三个组件
//代码省略
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//拿到package管理器
mPM = getApplicationContext().getPackageManager();
//默认的启动页组件
mComponent = getComponentName();
//双11组件
mComponent11 = new ComponentName(getBaseContext(), "com.hu.test.Test11");
//双12组件
mComponent12 = new ComponentName(getBaseContext(), "com.hu.test.Test12");
//代码省略
设置两个方法来启动和关闭某个组件
/**
* 开启某个组件
* @param componentName
*/
private void enableComponent(ComponentName componentName){
mPM.setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
}
/**
* 关闭某个组件
* @param componentName
*/
private void disabledComponent(ComponentName componentName){
mPM.setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
}
根据情况来启动某个组件设置3个方法如下:
/**
* 启动为双11
*/
private void changeDouble11(){
disabledComponent(mComponent);
disabledComponent(mComponent12);
enableComponent(mComponent11);
}
/**
* 启动为双12
*/
private void changeDouble12(){
disabledComponent(mComponent);
disabledComponent(mComponent11);
enableComponent(mComponent12);
}
/**
* 活动结束或者默认
*/
private void restoreComponent(){
disabledComponent(mComponent11);
disabledComponent(mComponent12);
enableComponent(mComponent);
}
随便找个监听去切换:
//代码省略
case R.id.ll_question://双11活动
changeDouble11();
break;
case R.id.ll_skin://双12活动
changeDouble12();
break;
//代码省略
看下实际效果图:
会有一个短暂的过程,所以一般更换图标要在夜深人静的时候更换。
(备注:调试时候,如果已经更换了图标,在重新run app的时候,会报以下错误:
Error: Activity class {com.hu.test/com.hu.test.MainActivity} does not exist.
找不到启动activity,因为更换图标后,默认的activity被PM关闭了,不在是清单文件中的默认启动activity,所以会报这个错误,解决方法:1>,卸载app,重新run app;2>,app中通过PM将默认activity重新开启,预埋的关闭)。
如有不足,请大佬们指出。