安卓APP开发入门:如何在Android App上高效显示位图
安安 2017-10-05 来源 :网络 阅读 608 评论 0

摘要:本文为大家讲解安卓APP开发入门。为了创建具有视觉魅力的app,显示图像是必须的。学会在你的Android app上高效地显示位图,而不是放弃性能。

本文为大家讲解安卓APP开发入门。为了创建具有视觉魅力的app,显示图像是必须的。学会在你的Android app上高效地显示位图,而不是放弃性能。

在Android上显示图像的痛苦

当工作于开发视觉魅力的app时,显示图像是必须的。问题是,Android操作系统不能很好地处理图像解码,从而迫使开发者要小心某些任务以避免搞乱性能。

Google写了一个有关于高效显示位图的完整指南,我们可以按照这个指南来理解和解决在显示位图时Android操作系统的主要缺陷。

Android app性能杀手

按照Google的指南,我们可以列出一些我们在Android apps上显示图像时遇到的主要问题。

降低图像采样率

无论视图大小,Android总是解码并全尺寸/大小显示图像。因为这个原因,所以如果你试图加载一个大图像,那就很容易使你的设备出现outOfMemoryError。

为了避免这种情况,正如Google所说的那样,我们应该使用BitmapFactory 解码图像,为inSampleSize 参数设置一个值。图象尺寸由inSampleSize划分,减少存储器的使用量。

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,

        int reqWidth, int reqHeight) {

 

    // First decode with inJustDecodeBounds=true to check dimensions

    final BitmapFactory.Options options = new BitmapFactory.Options();

    options.inJustDecodeBounds = true;

    BitmapFactory.decodeResource(res, resId, options);

 

    // Calculate inSampleSize

    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

 

    // Decode bitmap with inSampleSize set

    options.inJustDecodeBounds = false;

    return BitmapFactory.decodeResource(res, resId, options);

}

你可以手动设置inSampleSize,或使用显示器的尺寸计算。

public static int calculateInSampleSize(

            BitmapFactory.Options options, int reqWidth, int reqHeight) {

    // Raw height and width of image

    final int height = options.outHeight;

    final int width = options.outWidth;

    int inSampleSize = 1;

 

    if (height > reqHeight || width > reqWidth) {

 

        final int halfHeight = height / 2;

        final int halfWidth = width / 2;

 

        // Calculate the largest inSampleSize value that is a power of 2 and keeps both

        // height and width larger than the requested height and width.

        while ((halfHeight / inSampleSize) >= reqHeight

                && (halfWidth / inSampleSize) >= reqWidth) {

            inSampleSize *= 2;

        }

    }

 

    return inSampleSize;

}

异步解码

即使在使用BitmapFactory时,图像解码在UI线程上完成。这可以冻结app,并导致ANR(“Application Not Responding应用程序没有响应”)警报。

这个容易解决,你只需要将解码过程放到工作线程上。一种方法是使用异步任务,正如Google指导中解释的那样:

class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {

    private final WeakReference<ImageView> imageViewReference;

    private int data = 0;

 

    public BitmapWorkerTask(ImageView imageView) {

        // Use a WeakReference to ensure the ImageView can be garbage collected

        imageViewReference = new WeakReference<ImageView>(imageView);

    }

 

    // Decode image in background.

    @Override

    protected Bitmap doInBackground(Integer... params) {

        data = params[0];

        return decodeSampledBitmapFromResource(getResources(), data, 100, 100));

    }

 

    // Once complete, see if ImageView is still around and set bitmap.

    @Override

    protected void onPostExecute(Bitmap bitmap) {

        if (imageViewReference != null && bitmap != null) {

            final ImageView imageView = imageViewReference.get();

            if (imageView != null) {

                imageView.setImageBitmap(bitmap);

            }

        }

    }

}

图像缓存

每当对图像进行解码并放置在一个视图中的时候,Android操作系统默认重复整个渲染过程,浪费了宝贵的设备存储器。如果你打算在不同的地方展示相同的图像,或因为app生命周期或行为要多次重新加载,那么这可能会特别烦人。

为了避免占用过多的内存,推荐使用内存和磁盘缓存。接下来,我们将看到这些缓存之间的主要区别,以及为什么同时使用两者有用的原因。代码在这里显示的话太复杂了,所以请自行参阅Google指南的位图缓存部分以了解如何实现内存和磁盘的缓存。

· 内存缓存:图像存储在设备内存中。内存访问快速。事实上,比图像解码过程要快得多,所以将图像存储在这里是让app更快更稳定的一个好主意。内存缓存的唯一缺点是,它只存活于app的生命周期,这意味着一旦app被Android操作系统内存管理器关闭或杀死(全部或部分),那么储存在那里的所有图像都将丢失。请记住,内存缓存必须设置一个最大可用的内存量。否则可能会导致臭名昭著的outOfMemoryError。

· 磁盘缓存:图像存储在设备的物理存储器上(磁盘)。磁盘缓存可以一直存活于app启动期间,安全地存储图片,只要有足够的空间。缺点是,磁盘读取和写入操作可能会很慢,而且总是比访问内存缓存慢。由于这个原因,因此所有的磁盘操作必须在工作线程执行,UI线程之外。否则,app会冻结,并导致ANR警报。

每个缓存都有其优点和缺点,因此最好的做法是两者皆用,并从首先可用的地方读取,通过内存缓存开始。

最后的思考以及EpicBitmapRenderer

不知道你有没有注意到,正如我在本文开头所述,在Android app上显示图片真的很让人头疼。绝非看上去那么简单。

为了避免在每个项目中重复这些任务,我开发了一个100%免费又开源的Android库,EpicBitmapRenderer 。你可以在EpicBitmapRenderer GitHub repo选择它,或在EpicBitmapRenderer网站了解更多。

EpicBitmapRenderer 易于使用,并在每个图像解码操作中自动化了所有这些恼人的任务,这样你就可以专注于app开发。

你只需要添加增加EpicBitmapRenderer 依赖在你的Gradle上(查看其他构建工具的替代品,看看EpicBitmapRenderer文档的导入库部分)。

compile 'com.isaacrf.epicbitmaprenderer:epicbitmaprenderer:1.0'

在EpicBitmapRenderer 中解码图像是很容易的:只需要调用所需的解码方法并管理结果。看看下面这个例子,我们从URL获取图片并显示于ImageVIew上。

//Sample 3: Decode Bitmap from URL (Async)

EpicBitmapRenderer.decodeBitmapFromUrl(

        "//isaacrf.com/wp-content/themes/Workality-Lite-child/images/IsaacRF.png",

        200, 200,

        new OnBitmapRendered() {

            @Override

            public void onBitmapRendered(Bitmap bitmap) {

                //Display rendered Bitmap when ready

                ImageView imgView = findViewById(R.id.imgSampleDecodeUrl);

                imgView.setImageBitmap(bitmap);

            }

        },

        new OnBitmapRenderFailed() {

            @Override

            public void onBitmapRenderFailed(Exception e) {

                //Take actions if Bitmap fails to render

                Toast.makeText(MainActivity.this,

                          "Failed to load Bitmap from URL",

                          Toast.LENGTH_SHORT).show();

            }

        });

许可证

这篇文章以及任何相关的源代码和文件,遵循 The Creative Commons Attribution-Share Alike 3.0 Unported License的授权许可。


希望这篇文章可以帮助到你。总之,同学们,你想要的职坐标Android频道都能找到!


本文由 @安安 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程