android launch icon,Android动态更换启动图标,以及原理

前言

笔者最近看到百度网盘的启动图标可以随着办理SVIP可以动态更换启动器图标。所以自己试着搞了一下。

LaunchUtil

class LaunchUtil(packageManager: PackageManager) {

var mPm: PackageManager = packageManager

// 双重锁单例

companion object {

fun getInstance(packageManager: PackageManager): LaunchUtil {

val instance: LaunchUtil by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {

LaunchUtil(packageManager)

}

return instance

}

}

// 开启四大组件的方法

fun enableComponent(componentName: ComponentName) {

mPm.setComponentEnabledSetting(

componentName,

PackageManager.COMPONENT_ENABLED_STATE_ENABLED,

PackageManager.DONT_KILL_APP

)

}

// 禁用四大组件的方法

fun disableComponent(componentName: ComponentName) {

mPm.setComponentEnabledSetting(

componentName,

PackageManager.COMPONENT_ENABLED_STATE_DISABLED,

PackageManager.DONT_KILL_APP

)

}

}

提到PackageManager,我们就复习下PM类吧。

Context#getPackageManager()

/** Return PackageManager instance to find global package information. */

public abstract PackageManager getPackageManager();

ContextImpl#getPackageManager()

@Override

public PackageManager getPackageManager() {

if (mPackageManager != null) {

return mPackageManager;

}

IPackageManager pm = ActivityThread.getPackageManager();

if (pm != null) {

// Doesn't matter if we make more than one instance.

return (mPackageManager = new ApplicationPackageManager(this, pm));

}

return null;

}

ActivityThread#getPackageManager()

@UnsupportedAppUsage

public static IPackageManager getPackageManager() {

if (sPackageManager != null) {

//Slog.v("PackageManager", "returning cur default = " + sPackageManager);

return sPackageManager;

}

IBinder b = ServiceManager.getService("package");

//Slog.v("PackageManager", "default service binder = " + b);

sPackageManager = IPackageManager.Stub.asInterface(b);

//Slog.v("PackageManager", "default service = " + sPackageManager);

return sPackageManager;

}

PackageManager是抽象类,所以很容易找到ApplicationPackageManager是其实现类, 而后找到APM的setComponentEnabledSetting()

ApplicationPackageManager#setComponentEnabledSetting()

@UnsupportedAppUsage

private final IPackageManager mPM

@Override

public void setComponentEnabledSetting(ComponentName componentName,

int newState, int flags) {

try {

mPM.setComponentEnabledSetting(componentName, newState, flags, getUserId());

} catch (RemoteException e) {

throw e.rethrowFromSystemServer();

}

}

IPackageManager是AIDL类,所以mPM成员变量是通过Binder调用到PackageManagerService。

然而我们知道PM功能有一下几点:

提供一个应用程序的所有信息(ApplicationInfo)。

提供四大组件的信息。

查询permission信息。

安装与卸载apk。

启动一个新的图标

android:name=".App"

android:allowBackup="true"

android:icon="@mipmap/ic_launcher"

android:label="@string/app_name"

android:networkSecurityConfig="@xml/network_security_config"

android:roundIcon="@mipmap/ic_launcher_round"

android:supportsRtl="true"

android:theme="@style/AppTheme">

android:name=".MainActivity"

android:enabled="true">

android:name="Double11Alias"

android:enabled="false"

android:icon="@mipmap/ic_double_11"

android:label="双十一"

android:roundIcon="@mipmap/ic_launcher"

android:targetActivity=".MainActivity">

android:name="Double12Alias"

android:enabled="false"

android:icon="@mipmap/ic_double_12"

android:label="双十二"

android:roundIcon="@mipmap/ic_launcher"

android:targetActivity=".MainActivity">

activity-alias标签就是多入口配置,例如著名的LeakCanary就是通过这种方式开启另一个子app,并放开启了一个新的Activity栈。

cnDefault = ComponentName(baseContext, "$packageName.DefaultAlias")

cnNewActivity = ComponentName(baseContext, "$packageName.NewActivity1")

launchUtil = LaunchUtil.getInstance(applicationContext.packageManager)

fun bt1(view: View) {

launchUtil!!.enableComponent(cnDefault!!)

}

fun bt2(view: View) {

launchUtil!!.disableComponent(cnDefault!!)

launchUtil!!.enableComponent(cnNewActivity!!)

}

通过这种开启与禁用四大服务的方式,通知PMS进行刷新请求,即可完成动态换ICON。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值