大厂面试送分题:如何加载100M的图片却不撑爆内存? 你会怎么答?

我总结了24家大厂100面试,字节跳动Android岗经典面试,算法太TM重要了 其实如果你技术深度足够,大必用为就业而忧愁。每个行业何尝是这样,最开始的风口,到慢慢的成熟。Android初级在2020年的日子里风光再, 靠会四大组件就能够获取到满意薪资的时代一去复返。**经过一波一波的淘汰与洗牌,剩下的都是技术的金子。就像大浪褪去,裸泳的会慢慢上岸。**而真正坚持下来的一定会取得错成绩。毕竟Android市场是如此之大。从Android高级的蓬勃的就业岗位需求来看,能坚信我们每一位Android开发者的梦想 。 本专栏专注享大型Bat面试知识,后续会持续更新,喜欢的话麻. 阅读详情

还记得当年面试一个面试官问我怎么加载巨图才能不撑爆内存,我没回答上来,他说分片显示,我寻思特么分片能减少内存使用??现在可以打他脸了!

内容扩展

1.图片的三级缓存中,图片加载到内存中,如果内存快爆了,会发生什么?怎么处理?
2.内存中如果加载一张 500*500 的 png 高清图片.应该是占用多少的内存?
3.Bitmap 如何处理大图,如一张 30M 的大图,如何预防 OOM?

938页面试宝典PDF。更多面试内容,面试专题,flutter视频 全套,音视频从0到高手开发。
免费获取面试PDF合集

Android开发中,有时候会有加载巨图的需求,如何加载一个大图而不产生OOM呢,使用系统提供的BitmapRegionDecoder这个类可以很轻松的完成。

效果图:

BitmapRegionDecoder:区域解码器,可以用来解码一个矩形区域的图像,有了这个我们就可以自定义一块矩形的区域,然后根据手势来移动矩形区域的位置就能慢慢看到整张图片了。

OK 核心原理就是这么简单,不过做起来还是有一些细节处理,下面就一步一步的完成一个加载大图,支持拖动查看,双击放大,手势缩放的的自定义View。

第一步,初始化变量

  private void init(){
    mOptions = new BitmapFactory.Options();
    //滑动器
    mScroller = new Scroller(getContext());
    //所放器
    mMatrix = new Matrix();
    //手势识别
    mGestureDetector = new GestureDetector(getContext(),this);
    mScaleGestureDetector = new ScaleGestureDetector(getContext(),this);
}

BitmapFactory.Options我们很熟悉,用来配置Bitmap相关的参数,比如获取Bitmap的宽高,内存复用等参数。

GestureDetector用来识别双击事件,ScaleGestureDetector用来监听手指的缩放事件,都是系统提供的类,比较方便使用。

第二步,设置需要加载的图片

  public void setImage(InputStream is){
      mOptions.inJustDecodeBounds = true;
      BitmapFactory.decodeStream(is,null,mOptions);
      mImageWidth = mOptions.outWidth;
      mImageHeight = mOptions.outHeight;
      mOptions.inPreferredConfig = Bitmap.Config.RGB_565;
      mOptions.inJustDecodeBounds = false;
      try {
          //区域解码器
          mRegionDecoder = BitmapRegionDecoder.newInstance(is,false);
      } catch (IOException e) {
          e.printStackTrace();
      }
      requestLayout();
  }

设置需要要加载的图片,无论图片放到哪里都可以拿到图片的一个输入流,所以参数使用输入流,通过BitmapFactory.Options拿到图片的真实宽高。

inPreferredConfig这个参数默认是Bitmap.Config.ARGB_8888,这里将它改成Bitmap.Config.RGB_565,去掉透明通道,可以减少一半的内存使用。最后初始化区域解码器BitmapRegionDecoder

ARGB_8888就是由4个8位组成即32位, RGB_565就是R为5位,G为6位,B为5位共16位

第三步,获取View的宽高,计算缩放值

  @Override
  protected void onSizeChanged(int w, int h, int oldw, int oldh) {
     super.onSizeChanged(w, h, oldw, oldh);
     mViewWidth = w;
     mViewHeight = h;
     mRect.top = 0;
     mRect.left = 0;
     mRect.right = (int) mViewWidth;
     mRect.bottom = (int) mViewHeight;
     mScale = mViewWidth/mImageWidth;
     mCurrentScale = mScale;
  }

onSizeChanged方法在布局期间,当此视图的大小发生更改时,将调用此方法,第一次在onMeasure之后调用,可以方便的拿到View的宽高。

然后给我们自定义的矩形mRect的上下左右的边界赋值。一般情况下我们使用这个自定义的View显示大图,都是占满这个View,所以这里矩形初始大小就让它跟View一样大。

mScale用来记录原始的所方比,mCurrentScale用来记录当前的所方比,因为有双击放大和手势缩放,mCurrentScale随着手势变化。

第四步,绘制

  @Override
  protected void onDraw(Canvas canvas) {
      super.onDraw(canvas);
      if(mRegionDecoder == null){
          return;
      }
      //复用内存
      mOptions.inBitmap = mBitmap;
      mBitmap = mRegionDecoder.decodeRegion(mRect,mOptions);
      mMatrix.setScale(mCurrentScale,mCurrentScale);
      canvas.drawBitmap(mBitmap,mMatrix,null);
  }

绘制也很简单,通过区域解码器解码一个矩形的区域,返回一个Bitmap对象,然后通过canvas绘制Bitmap。需要注意mOptions.inBitmap = mBitmap;这个配置可以复用内存,保证内存的使用一直只是矩形的这块区域。

到这里运行就能绘制出一部分图片了,想要看全部的图片,需要手指拖动来看,这就需要处理各种事件了。

第五步,分发事件

  @Override
  public boolean onTouchEvent(MotionEvent event) {
      mGestureDetector.onTouchEvent(event);

      mScaleGestureDetector.onTouchEvent(event);
      return true;
  }

onTouchEvent中很简单,事件都交给两个手势检测器自己去处理。

第六步,处理GestureDetector中的事件

  @Override
  public boolean onDown(MotionEvent e) {
      //如果正在滑动,先停止
      if(!mScroller.isFinished()){
          mScroller.forceFinished(true);
      }
      return true;
  }

当手指按下的时候,如果图片正在飞速滑动,那么停止

  @Override
  public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
      //滑动的时候,改变mRect显示区域的位置
      mRect.offset((int)distanceX,(int)distanceY);
      //处理上下左右的边界
      if(mRect.left<0){
          mRect.left = 0;
          mRect.right = (int) (mViewWidth/mCurrentScale);
      }
      if(mRect.right>mImageWidth){
          mRect.right = (int) mImageWidth;
          mRect.left = (int) (mImageWidth-mViewWidth/mCurrentScale);
      }
      if(mRect.top<0){
          mRect.top = 0;
          mRect.bottom = (int) (mViewHeight/mCurrentScale);
      }
      if(mRect.bottom>mImageHeight){
          mRect.bottom = (int) mImageHeight;
          mRect.top = (int) (mImageHeight-mViewHeight/mCurrentScale);
      }
      invalidate();
      return false;
  }

onScroll中处理滑,根据手指移动的参数,来移动矩形绘制区域,这里需要处理各个边界点,比如左边最小就为0,右边最大为图片的宽度,不能超出边界否则就报错了。

  @Override
  public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
      mScroller.fling(mRect.left,mRect.top,-(int)velocityX,-(int)velocityY,0,(int)mImageWidth
             ,0,(int)mImageHeight);
      return false;
  }

  @Override
  public void computeScroll() {
      super.computeScroll();
      if(!mScroller.isFinished()&&mScroller.computeScrollOffset()){
          if(mRect.top+mViewHeight/mCurrentScale<mImageHeight){
              mRect.top = mScroller.getCurrY();
              mRect.bottom = (int) (mRect.top + mViewHeight/mCurrentScale);
          }
          if(mRect.bottom>mImageHeight) {
              mRect.top = (int) (mImageHeight - mViewHeight/mCurrentScale);
              mRect.bottom = (int) mImageHeight;
          }
          invalidate();
      }
  }

onFling方法中调用滑动器Scroller的fling方法来处理手指离开之后惯性滑动。惯性移动的距离在View的computeScroll()方法中计算,也需要注意边界问题,不要滑出边界。

第七步,处理双击事件

  @Override
  public boolean onDoubleTap(MotionEvent e) {
      //处理双击事件
      if (mCurrentScale>mScale){
          mCurrentScale = mScale;
      } else {
          mCurrentScale = mScale*mMultiple;
      }
      mRect.right = mRect.left+(int)(mViewWidth/mCurrentScale);
      mRect.bottom = mRect.top+(int)(mViewHeight/mCurrentScale);
      //处理边界
      if(mRect.left<0){
          mRect.left = 0;
          mRect.right = (int) (mViewWidth/mCurrentScale);
      }
      if(mRect.right>mImageWidth){
          mRect.right = (int) mImageWidth;
          mRect.left = (int) (mImageWidth-mViewWidth/mCurrentScale);
      }
      if(mRect.top<0){
          mRect.top = 0;
          mRect.bottom = (int) (mViewHeight/mCurrentScale);
      }
      if(mRect.bottom>mImageHeight){
          mRect.bottom = (int) mImageHeight;
          mRect.top = (int) (mImageHeight-mViewHeight/mCurrentScale);
      }

      invalidate();
      return true;
  }

mMultiple为双击之后放大几倍,这里设置3倍。第一次双击放大3倍,第二次双击返回原状。缩放完成之后,需要根据当前的缩放比重新设置绘制区域的边界。最后也需要重新定位一下边界,因为如果使用两个手指放大之后,这时候双击返回原状,如果不处理边界,位置会出错。处理边界的代码可以抽取出来。

第八步,处理手指缩放事件

  @Override
  public boolean onScale(ScaleGestureDetector detector) {
      //处理手指缩放事件
      //获取与上次事件相比,得到的比例因子
      float scaleFactor = detector.getScaleFactor();
  //        mCurrentScale+=scaleFactor-1;
      mCurrentScale*=scaleFactor;
      if(mCurrentScale>mScale*mMultiple){
          mCurrentScale = mScale*mMultiple;
      }else if(mCurrentScale<=mScale){
          mCurrentScale = mScale;
      }
      mRect.right = mRect.left+(int)(mViewWidth/mCurrentScale);
      mRect.bottom = mRect.top+(int)(mViewHeight/mCurrentScale);
      invalidate();
      return true;
  }

  @Override
  public boolean onScaleBegin(ScaleGestureDetector detector) {
      //当 >= 2 个手指碰触屏幕时调用,若返回 false 则忽略改事件调用
      return true;
  }

onScaleBegin方法需要返回true,否则无法检测到手势缩放。onScale方法中获取缩放因子,这个缩放因子是跟上次事件相比的出来的。所以这里使用*=,完成之后也需要重新设置绘制区域mRect的边界。

到这里各种功能就完成啦~

最后

最后我想说:对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!

Android源码的Binder权限是如何控制?先睹为 正文 1.图片三级缓存中,图片加载内存中,如果内存了,会发生什么?怎么处理? 参考回: 首先我们要清楚图片三级缓存是如何的 如果内存足够时回收。内存够时就回收软引用对象 2.内存中如果加载一张500X500的png高清图片.应该是占用多少的内存? 参考回考虑屏幕比的话:占用内存=500 * 500 * 4 = 1000000B ≈ 0.95MB 考虑屏幕比的的话:占用内存= 宽度像素 x (inTargetDensity / inDensity) x 高度像素 x (inTarg 阅读详情

相关推荐

android 机试,11道Android机能优化面试【附案】_WEB前端开发

由于现实开发与参考案会有所差别,再者怕误导人人,所以这些面试案照样自身去明白!面试官会针对简历中提到的知识点由浅入深发问,所以要背案,多明白。1、图片三级缓存中,图片加载内存中,假如内存了,会发作什么?怎样处置惩罚?参考回覆:起首我们要清晰图片三级缓存是怎样的 假如内存充足时接纳。内存够时就接纳软援用对象2、内存中假如加载一张500*500的png高清图片.应当是占用若干的...

weixin_42395135的博客 618

2019初中级Android开发社招面试之性能优化

注:因为实际开发与参考案会有所同,再者怕误导大家,所以这些面试案还是自己去理解!面试官会针对简历中提到的知识点由浅入深提问,所以要背案,多理解。 1.图片三级缓存中,图片加载内存中,如果内存了,会发生什么?怎么处理? 参考回: 首先我们要清楚图片三级缓存是如何的 如果内存足够时回收。内存够时就回收软引用对象 2.内存中如果加载一张500X500的png高清图片.应...

beibei_programme的博客 701

Android面试者注意:虚惊一场,差点痛失字节跳动offer,而罪魁祸首竟是(3)

最后这里放上我这段时间复习的资料,这个资料也是偶然一位朋友享给我的,里面包含了腾讯、字节跳动、阿里、百度2019-2021面试解析,并且把每个技术点整理成了视频和PDF(知识脉络 + 诸多细节)。还有高级架构技术进阶脑图、高级进阶架构资料帮助大家学习提升进阶,也可以享给身边好友一起学习。一起互勉~网上学习资料一大堆,但如果学到的知识成体系,遇到问时只是浅尝辄止,再深入研究,那么很难做到真正的技术提升。需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android

pckzzy119的博客 385

2020年最新字节、阿里、腾讯,Android-Camera内存剖析

3.Fragment中add与replace的区别(Fragment重叠) 4.getFragmentManager、getSupportFragmentManager 、getChildFragmentManager之间的区别? 5.FragmentPagerAdapter与FragmentStatePagerAdapter的区别与使用场景 三、Service基础相关 1.Service的生命周期 2.Service的两种启动方式?区别在哪? 3.如何保证Service被杀死 ? 4.能否在Servic

m0_66144992的博客 821

面试被问:如何加载100M的图片内存,我人傻了(1)

总而言之,成功是留给准备好的人的。无论是参加什么面试,都要做好充足的准备,注意好面试的礼仪和穿着,向面试官表现出自己的热忱与真诚就好。即使最后没有过关,也要做好经验的总结,为下一次面试做好充足准备。这里我为大家准备了一些我在面试后整理的面试资料,除了面试,还总结出了互联网公司Android程序员面试涉及到的绝大部面试案,并整理做成了文档,以及系统的进阶学习视频资料享给大家,希望能帮助到你面试前的复习,且找到一个好的工作,也节省大家在网上搜索资料的时间来学习。

2401_84023262的博客 1107

如何加载100M的图片内存,一张 100M 的大图,如何预防 OOM?

Android开发中,有时候会有加载巨图的需求,如何加载一个大图而产生OOM呢,使用系统提供的BitmapRegionDecoder这个类可以很轻松的完成。 效果图: BitmapRegionDecoder:区域解码器,可以用来解码一个矩形区域的图像,有了这个我们就可以自定义一块矩形的区域,然后根据手势来移动矩形区域的位置就能慢慢看到整张图片了。 OK 核心原理就是这么简单,过做起来还是有一些细节处理,下面就一步一步的完成一个加载大图,支持拖动查看,双击放大,手势缩放的的自定义View。 第一步,初始

m0_70083119的博客 1414

面试被问:如何加载100M的图片内存,我人傻了(3)

中处理滑,根据手指移动的参数,来移动矩形绘制区域,这里需要处理各个边界点,比如左边最小就为0,右边最大为图片的宽度,能超出边界否则就报错了。绘制也很简单,通过区域解码器解码一个矩形的区域,返回一个Bitmap对象,然后通过canvas绘制Bitmap。到这里运行就能绘制出一部图片了,想要看全部的图片,需要手指拖动来看,这就需要处理各种事件了。设置需要要加载图片,无论图片放到哪里都可以拿到图片的一个输入流,所以参数使用输入流,通过。方法在布局期间,当此视图的大小发生更改时,将调用此方法,第一次在。

2401_84023262的博客 1073

面试被问:如何加载100M的图片内存,我人傻了(2)

最后也需要重新定位一下边界,因为如果使用两个手指放大之后,这时候双击返回原状,如果处理边界,位置会出错。很多人在刚接触这个行业的时候或者是在遇到瓶颈期的时候,总会遇到一些问,比如学了一段时间感觉没有方向感,知道该从那里入手去学习,对此我针对Android程序员,我这边给大家整理了一套。中处理滑,根据手指移动的参数,来移动矩形绘制区域,这里需要处理各个边界点,比如左边最小就为0,右边最大为图片的宽度,能超出边界否则就报错了。方法在布局期间,当此视图的大小发生更改时,将调用此方法,第一次在。

2401_84023262的博客 1057

2024年最全面试官:怎么加载巨图内存?打脸现场!(1),2024年最新android面试内存优化

下面是有几位Android行业大佬对应上方技术点整理的一些进阶资料。希望能够帮助到大家提升技术高级UI,自定义ViewUI这块知识是现今使用者最多的。当年火一时的Android入门培训,学会这小块知识就能随便找到错的工作了。过很显然现在远远够了,拒绝无休止的CV,亲自去项目实战,读源码,研究原理吧!网上学习资料一大堆,但如果学到的知识成体系,遇到问时只是浅尝辄止,再深入研究,那么很难做到真正的技术提升。需要这份系统化学习资料的朋友,可以戳这里获取一个人可以走的很,但一群人才能走的更远!

2401_84557521的博客 1092

百度Android高工面试:如何加载100M的图片内存,一张-100M-的大图,如何预防-OOM?

为双击之后放大几倍,这里设置3倍。最后也需要重新定位一下边界,因为如果使用两个手指放大之后,这时候双击返回原状,如果处理边界,位置会出错。中处理滑,根据手指移动的参数,来移动矩形绘制区域,这里需要处理各个边界点,比如左边最小就为0,右边最大为图片的宽度,能超出边界否则就报错了。,到了合适的年纪,后续知道该如何发展,转型管理,还是加强技术研究。到这里运行就能绘制出一部图片了,想要看全部的图片,需要手指拖动来看,这就需要处理各种事件了。**第二,**开发几年,知道如何进阶更进一步,比较迷茫。

2401_84102759的博客 652

阿里面试官:Android面试这些原理都给我讲明白了,最低都是20k起步!

前言 大厂面试一直都是程序员圈内摸鱼时间津津乐道的话,进大厂想必也是无数程序员的梦想。 关于“原理”的问,几乎是现如今Android开发岗必问的问,尤其在大厂面试中更为突出。有过大厂面试经验的小伙伴应该知道:大厂面试官一般会基于你的简历,从一个非常基础的问开始问起,然后一步一步给你挖坑,最终必定会问到原理的问上去。 所以,想进大厂,原理这关,你必须得过! 前几天和我一个在阿里的朋友闲谈的时候,特意问了这位10年Android开发的“骨灰级”面试官,阿里巴巴面试都问哪些原理问。特此整理出这篇文.

chuhe1989的博客 1115

2020阿里大佬总结面试系列!大厂面试图片(含案+学习笔记)

作者:爱雨浮龙 一、图片 1、图片库对比 2、LRUCache原理 3、图片加载原理 4、自己去实现图片库,怎么做? 5、Glide源码解析 6、Glide使用什么缓存? 7、Glide内存缓存如何控制大小? 参考案: 1、图片库对比 Picasso Glide Fresco Picasso 毕加索 Square Picasso 没有实现本地缓存功能,...

Aerfa789的博客 798

肝整理!Android最常见大厂面试+解析(二)(1)

: 最常用的布局方式为Absolute Layout、Relative Layout、Linear Layout、FrameLayout、TableLayout。其中Linear Layout和Relative Layout是最常用的方式,他们可以通过在xml配置文件或者代码中进行布局。1、Frame Layout是最简单的布局方式,放置的控件都只能罗列到左上角,控件会有重叠,能进行复杂的布局。

2401_84149785的博客 760

2020BATJ面试系列:145个技术高端技术,助你拿offer!

前言 大家都想进大厂,互联网大公司的优点也很明显,工资高、福利待遇好、工作稳定、还能镀金。 但是大公司的缺点也很明显,难进! 三月,仅是进击阿里的好机会,也是其他进其他大厂的良机,比如百度、腾讯、字节跳动、京东、美团、滴滴等等。 大厂面试流程通常都是很复杂而且很严格的,基本都是三轮起步,五轮什么的也很常见,因此你一定要有很强的抗压能力,有些朋友,一遇到高端Android技术就比较慌,在这里...

Aerfa789的博客 753

2024程序员必看面试指南-进大厂年薪百万需要付出多少努力?你看看你们配吗

有任何问,欢迎广大网友一起来交流,享高阶Android学习视频资料和面试资料包~偷偷说一句:群里高手如云,欢迎大家加群和大佬们一起交流讨论啊!由于文件比较大,这里只是将部目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)[外链图片转存中…(img-doyluzeC-1711895078587)]

2401_83974064的博客 644

2024最新BAT Android高端技术面试145详解,android高级面试pdf

现在随着短视频,抖音,手的流行NDK模块开发也显得越发重要,需要这块人才的企业也越来越多,随之学习这块的人也变多了,音视频的开发,往往是比较难的,而这个比较难的技术就是NDK里面的技术。音视频/高清大图片/人工智能/直播/抖音等等这年与用户最紧密,与我们生活最相关的技术一直都在寻找最终的技术落地平台,以前是windows系统,而现在则是移动系统了,移动系统中又是以Android占比绝大部为前提,所以AndroidNDK技术已经是我们必备技能了。

2401_87361676的博客 2738

饮一杯茶,刷刷听听别人的建议,面试足矣

前言 其实对于很多人来说,都和我原来有同样一个疑虑:到底去大厂还是创业型公司?去大公司一个萝卜一个坑,要往上走需要运气+实力足够好才能脱颖而出,创业型公司,可能更容易实现自己的想法和舞台,能把能力表现的淋漓尽致,可是会碰到更多的问,公司经营下去了怎么办?薪资待遇大厂? 一丶大厂offer? 我相信每个程序员的梦想都是得到大厂的offer,我觉得这很正常,这并是我们的饭后谈资而是每个...

初一十五的博客 278

最新BAT大厂面试者整理的Android面试目模板,面试必会

前言 本人今年25岁,毕业之后进入一家小型的互联网公司工作,在这原公司呆了3年,直至今年才有了跳槽的想法。 每个程序员 都拥有大厂梦,我也例外,在小公司待久了,感觉人会荒废掉,太轻松,没有压迫感。因为有一位前辈的华为内推,所以我直接去了华为面试。 以下享这次面试华为的面试,由于文章篇幅原因,只是把面试列了出来,详细的案我整理成了一份PDF文档,文末有详细的免费领取方式,有需要的朋友可以到文末查看领取方式。 前几天和我一个在阿里的朋友闲谈的时候,特意问了这位10年Android开发的“骨灰级”面试

m0_57064830的博客 133
上一篇: 震惊!!!腾讯竟然又开源一套Android原生UI框架!
下一篇: 保证你所知道不全面的Android 11 新特性!!!
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值