关于Launcher加载应用图标,导致图标显示异常的问题

问题产生的背景:Launcher多应用页面,APP可以拖拽改变位置。在安装三方应用后,三方应用会在拖拽后,图标缩小,显示不正常。

分析过程

1、分析XML文件,复现是谁变小了。到最后发现是图片变小了。ImageView大小没变。至此分析出是图片加载了问题。

2、分析Adapter中的图片加载方式,展示方式都是一样的,也未在拖拽时去修改图片。所以图片加载方式也是没有问题的。

3、发现图片数据的来源,分析来源。

分析log发现,显示异常的app,icon是 AdaptiveIconDrawable 类型的Drawable,其他正常的显示的app都是 BitmapDrawable 类型的Drawable。所以就可以知道问题出在这里。

那是AdaptiveIconDrawable 什么呢?android官网描述如下:AdaptiveIconDrawable  |  Android Developers

8.0以后Google添加了自适应图标AdaptiveIconDrawable,它是用来做自适应的。至于为什么会导致拖拽后,图标缩小这个问题,还不得而知。但是解决这个问题的思路就是,所有的都用 BitmapDrawable 渲染显示。

本地代码中获取APP信息的方法:

List<AppInfo> customList = new ArrayList<>();
//获取全部应用
PackageManager packageManager = context.getPackageManager();
Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
List<ResolveInfo> resolveInfos = packageManager.queryIntentActivities(mainIntent, 0);
//循环得到APP数据
for (ResolveInfo resolveInfo : resolveInfos) {
    AppInfo bean = new AppInfo();
    String appPackageName = resolveInfo.activityInfo.packageName;
    if (!TextUtils.isEmpty(appPackageName)) {
        String appName = resolveInfo.loadLabel(packageManager).toString();
        String className = resolveInfo.activityInfo.name;
        Drawable appIcon = resolveInfo.loadIcon(packageManager);
        bean.setTitle(appName);// 应用名称
        bean.setPackageName(appPackageName);// 包名
        bean.setClassName(className);// 入口类名
        bean.setIcon(appIcon);// 应用图标
    }
    customList.add(bean);
}

解决思路: 将 resolveInfo.loadIcon(packageManager);得到的Drable,转为BitmapDrawable。代码如下:

public static BitmapDrawable drawableToBitmap(Drawable drawable, Context context) {
    if (drawable instanceof BitmapDrawable) {
        return (BitmapDrawable) drawable;
    }
    Bitmap bitmap;
    if ((drawable.getIntrinsicWidth() <= 0) || (drawable.getIntrinsicHeight() <= 0)) {
        bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
    } else {
        bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
                drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
    }
    return new BitmapDrawable(context.getResources(), bitmap);
}

好了,代码跑进去,之前错误的APP不见了!为什么呢,哦,忘了使用画布去画出图片了,修改一下,再跑。

public static BitmapDrawable drawableToBitmap(Drawable drawable, Context context) {
    if (drawable instanceof BitmapDrawable) {
        return (BitmapDrawable) drawable;
    }
    Bitmap bitmap;
    if ((drawable.getIntrinsicWidth() <= 0) || (drawable.getIntrinsicHeight() <= 0)) {
        bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
    } else {
        bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
                drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
    }
    Canvas canvas = new Canvas(bitmap);
    drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
    drawable.draw(canvas);
    return new BitmapDrawable(context.getResources(), bitmap);
}

好喔,测试没有问题!但是有大佬指出,在Bitmap使用的时候会导致内存泄露。!!!内存泄漏,赶紧解决,不可以存在!!!

根据大佬的,赶紧修改代码,再测!!!

public static BitmapDrawable drawableToBitmap(Drawable drawable, Context context) {
    if (drawable instanceof BitmapDrawable) {
        return (BitmapDrawable) drawable;
    }
    Bitmap bitmap;
    try {
        if ((drawable.getIntrinsicWidth() <= 0) || (drawable.getIntrinsicHeight() <= 0)) {
            bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
        } else {
            bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
                    drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
        }
    } catch (OutOfMemoryError error) {
        LogUtil.error(TAG, "BitmapDrawable OutOfMemoryError");
        bitmap = BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_launcher);
        error.printStackTrace();
    }
    Canvas canvas = new Canvas(bitmap);
    drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
    drawable.draw(canvas);
    BitmapDrawable bitmapDrawable = new BitmapDrawable(context.getResources(), bitmap);
    if (bitmap != null && !bitmap.isRecycled()){
        bitmap.recycle();
    }
    return bitmapDrawable;
}

咦,怎么滑一下页面就有问题了呢,看看是啥问题?Crash了!!!报错:

trying to use a recycled bitmap android.graphics.Bitmap@41d

原因分析如下:如果缓存Drawable不为空,那么就返回原先使用的Drawable,在这里,这个Drawable引用的Bitmap已经被recycle了,但是Drawable这个缓存对象是一直并引用那个失效的Bitmap。

可以看一下这篇文章:完美解决 trying to use a recycled bitmap android.graphics.Bitmap@41d的异常_VIPqiangqiang的博客-CSDN博客

所以,在页面退出的时候去recycle bitmap,最总版代码如下:

List<AppInfo> customList = new ArrayList<>();
//获取全部应用
PackageManager packageManager = context.getPackageManager();
Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
List<ResolveInfo> resolveInfos = packageManager.queryIntentActivities(mainIntent, 0);
//循环得到APP数据
for (ResolveInfo resolveInfo : resolveInfos) {
    AppInfo bean = new AppInfo();
    String appPackageName = resolveInfo.activityInfo.packageName;
    if (!TextUtils.isEmpty(appPackageName)) {
        String appName = resolveInfo.loadLabel(packageManager).toString();
        String className = resolveInfo.activityInfo.name;
        //使用下面的方法读取icon,读取到应用的默认的ICON图标可能会是一个adaptive-icon类型
        //AdaptiveIconDrawable需转为BitmapDrawable使用
        Drawable icon = resolveInfo.loadIcon(packageManager);
        BitmapDrawable appIcon = drawableToBitmap(icon, context);
        bean.setTitle(appName);// 应用名称
        bean.setPackageName(appPackageName);// 包名
        bean.setClassName(className);// 入口类名
        bean.setIcon(appIcon);// 应用图标
    }
    customList.add(bean);
}

//得到一个BitmapDrawable
public static BitmapDrawable drawableToBitmap(Drawable drawable, Context context) {
    if (drawable instanceof BitmapDrawable) {
        return (BitmapDrawable) drawable;
    }
    Bitmap bitmap;
    try {
        if ((drawable.getIntrinsicWidth() <= 0) || (drawable.getIntrinsicHeight() <= 0)) {
            bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
        } else {
            bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
                    drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
        }
    } catch (OutOfMemoryError error) {
        LogUtil.error(TAG, "BitmapDrawable OutOfMemoryError");
        bitmap = BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_launcher);
        error.printStackTrace();
    }
    Canvas canvas = new Canvas(bitmap);
    drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
    drawable.draw(canvas);
    //不可以在此处调用 recycle() 方法。缓存Drawable不为空,那么就返回原先使用的Drawable。
    //如果在这里recycle,这个Drawable引用的Bitmap已经被recycle了,但是Drawable这个缓存对象是一直并引用那个失效的Bitmap。
    return new BitmapDrawable(context.getResources(), bitmap);
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值