WEEX-EEUI 卡顿优化(图片加载)

本文介绍了在Android平台上,使用WEEX-EEUI框架时遇到的图片加载导致的卡顿问题及其解决过程。通过Android Profile工具分析,发现线程数量异常增加,与Picasso和Glide的ImageAdapter有关。最终定位问题在于非法的placeHolder和WEEX的autobitmaprecycle参数。移除无效placeHolder和调整autobitmaprecycle设置解决了卡顿问题,强调了Android Profile工具在性能优化中的重要性。

前言

前两天前端的小伙伴告诉我,他写的页面再android手机上特别的卡,崩溃了很多次。这让我紧张了起来,已经用weex-eeui做过一个项目了,之前还没发现这个问题。因为问题不好定位,让我想起了之前一直想用却没有怎么使用过的android profile工具。现在它可以排上用场了。

Android Profile 工具使用

这里先提供几个我觉得写的比较好的文章给大家:
1.Android studio中android profile(性能分析器)的使用
2.Android性能分析工具 — CPU Profile

如果第一个看不太懂,就看第二个。第二篇文章主要讲了CPU分析,因为通过观察发现,内存波动和cpu波动都有些异常,不过cpu的问题更突出一些。
在这里插入图片描述
在这里插入图片描述
通过观察cpu的数据发现,每次打开图片比较多的页面和关闭图片比较多的页面的时候都会出现卡顿,而且线程数量从一开始的七十多不断的增加到两千多个线程,这不卡才怪!
顺着这个思路,我使用了Trace文件分析这个功能,找到了这些线程的真正来源是Picasso 的dispatcher方法,这让我很懵逼。之前用picasso的时候几乎没有遇到过这个问题。还好ImageAdapter又支持picasso和glide,于是乎,我就把框架切换到glide上面,结果还是回出现卡顿,线程数量一直飙升的问题。

问题解决历程

这里我给大家看一下ImageAdapter的代码

//placeHolder是默认占位图,问题就出在这里
if (!TextUtils.isEmpty(strategy.placeHolder)) {
            Log.d(TAG, "placeHolder: " + strategy.placeHolder);
            String placeHolder = eeuiBase.config.verifyFile(eeuiPage.rewriteUrl(view, handCachePageUrl(view.getContext(), strategy.placeHolder)));
            File file=new File(placeHolder);
            if(file.exists()){
                Picasso.Builder builder = new Picasso.Builder(WXEnvironment.getApplication());
                Picasso picasso = builder.build();
                picasso.load(Uri.parse(placeHolder)).into(view);
                view.setTag(strategy.placeHolder.hashCode(), picasso);
                valid=true;
            }else{
                Log.d(TAG, "非法 placeHolder");
                valid=false;
            }

        }
        //
        try {
        //判断使用picasso还是使用glide
            if (imageEngine.equals("picasso") && !tempUrl.startsWith("data:image/")) {
                if (tempUrl.startsWith("file://assets/")) {
                    tempUrl = "file:///android_asset/" + tempUrl.substring(14);
                }

                Picasso.with(view.getContext()).load(tempUrl).into(view, new Callback() {
                    @Override
                    public void onSuccess() {
                        Log.d(TAG, "onSuccess: " );
                        if (strategy.getImageListener() != null) {
                            strategy.getImageListener().onImageFinish(url, view, true, null);
                        }
                        recordImgLoadResult(strategy.instanceId, true, null);
//
                        if (!TextUtils.isEmpty(strategy.placeHolder)&&valid) {
                            ((Picasso) view.getTag(strategy.placeHolder.hashCode())).cancelRequest(view);
                        }
                    }

                    @Override
                    public void onError() {
                        if (strategy.getImageListener() != null) {
                            strategy.getImageListener().onImageFinish(url, view, false, null);
                        }
                        recordImgLoadResult(strategy.instanceId, false, null);
                    }
                });
            } else {
                RequestBuilder<Drawable> myLoad;
                if (tempUrl.startsWith("file://assets/")) {
                    Bitmap myBitmap = getImageFromAssetsFile(view.getContext(), tempUrl.substring(14));
                    myLoad = Glide.with(view.getContext()).load(myBitmap);
                } else {
                    myLoad = Glide.with(view.getContext()).load(tempUrl);
                }
                //
                RequestOptions myOptions = new RequestOptions().diskCacheStrategy(DiskCacheStrategy.ALL);
                myLoad.apply(myOptions).listener(new RequestListener<Drawable>() {
                    @Override
                    public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
                        if (strategy.getImageListener() != null) {
                            strategy.getImageListener().onImageFinish(url, view, false, null);
                        }
                        recordImgLoadResult(strategy.instanceId, false, null);
                        return false;
                    }

                    @Override
                    public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
                        if (strategy.getImageListener() != null) {
                            strategy.getImageListener().onImageFinish(url, view, true, null);
                        }
                        recordImgLoadResult(strategy.instanceId, true, null);

                        if (!TextUtils.isEmpty(strategy.placeHolder)) {
                            ((Picasso) view.getTag(strategy.placeHolder.hashCode())).cancelRequest(view);
                        }
                        return false;
                    }
                }).into(view);
            }
        } catch (IllegalArgumentException ignored) {

        } catch (Exception ignored) {

        }

虽然使用了glide框架,但是依然会出现线程飙升的情况,我一开始以为是AssetManager的问题,毕竟主线程里面进行文件IO的读操作会影响到主线程了。所以就让前端把图片放到OSS服务器上面去了,结果还是不行。

 RequestBuilder<Drawable> myLoad;
                if (tempUrl.startsWith("file://assets/")) {
                //通过AssetManager访问本地文件,
                    Bitmap myBitmap = getImageFromAssetsFile(view.getContext(), tempUrl.substring(14));
                    myLoad = Glide.with(view.getContext()).load(myBitmap);
                } else {
                    myLoad = Glide.with(view.getContext()).load(tempUrl);
                }

于是我只能继续查找别的原因了,我在eeui的imageAdapter的原生实现上,加入了几行日志代码。我发现我的前端小伙伴居然在placeHolder里面放了一句话“默认图”,这显然不是默认图的名字,是一个非法的文件地址。于是我把注意力抓到了placeHolder上面,把他写的weex-vue代码里面的placeHolder都给干掉了。果然,问题就出在这里了。线程数量的问题得到了解决,这里引一下image组件的文档简单说明一下placeHolder的用法:
在这里插入图片描述

但是我在日志中又发现了另外一个问题,当页面从后台切到前台的时候,页面上的图片又重新走了imageAdapter的loadImage方法。导致页面返回的时候,有一些卡顿。上面那个问题没有在eeui的论坛上找到答案,我决定先去eeui上面试一下:

在这里插入图片描述
我发现有人跟我一样的问题,我看了eeui作者的回答:
链接如下:
请参考image标签相关参数,特别是autobitmaprecycle参数: https://eeui.app/weex/components/image.html#autobitmaprecycle

问题就在这里:
在这里插入图片描述
原来weex为我们的image组件提供了一个节省内存的做法,默认节省内存。但是这个体验当,页面图片比较多的时候,就会显得有些卡顿了,确实不该啊。这里weex更倾向于时间换空间,估计是为了适配更多低版本设备吧!但是主流手机这个情况会比较少。及时时间换空间也不该阻塞主线程嘛。我建议吧图片加载写在IdleHandler是不是更合理一些呢?

总结

android profile是一个不错的工具,当你对一个系统不了解的时候,遇到性能问题,没有明确思路的时候。他可以帮你快速找到问题发生的位置,不然在庞大的工程里面找bug真的很急人。越着急解决问题的效率就越低。所以,还没有掌握这一项技能的小伙伴们抓紧学习哦。

喜欢我的作品麻烦点个赞或加个关注

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值