The interviewer asked me: how to load the picture does not explode 100M memory

Remember the interview the interviewer asked me how to load a giant map memory in order not to explode, I did not answer it, he said, slicing show, I find it Fest fragmentation can reduce memory usage? ? He may now face a fight!

Extend

1. The three-level cache image, the image is loaded into memory, if the memory is fast burst, what will happen? How to deal with?
2. If memory is loaded png HD picture a 500 * 500. Should be the number of memory footprint?
How 3.Bitmap handle big picture, the big picture as a 30M, how to prevent OOM?

Android development, and sometimes there will be huge demand load diagram of how to load a large image without creating OOMit, use the system provided by BitmapRegionDecoderthis class can easily complete.

FIG Effect:

BitmapRegionDecoder: decoder area, a rectangular area may be used to decode the image with this area we can customize a rectangle, and then to move the position of the rectangular area according to the gesture of the whole picture can be seen slowly .

OK core principle is so simple, but doing it there are some details of the deal, step by step following the completion of a large image loading, support drag view, double-click zoom in, zoom gestures Custom View.

The first step, variable initialization

  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.OptionsWe are very familiar with, to configure Bitmap-related parameters, such as obtaining Bitmap width and height memory multiplexing parameters.

GestureDetectorDouble-click to identify events ScaleGestureDetectorthat listens finger zoom events, classes are provided by the system, more convenient to use.

The second step, set to load images

  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();
  }

Settings need to load a picture, regardless of where the picture can get into an input stream of the picture, so the parameter input stream through BitmapFactory.Optionsto get the real picture width and height.

inPreferredConfigThis parameter defaults Bitmap.Config.ARGB_8888, where it is changed Bitmap.Config.RGB_565, remove the transparent channel, memory usage can be reduced by half. Finally, the decoder initialization area BitmapRegionDecoder.

ARGB_8888Is composed of four 8-bit 32-bit i.e., RGB_565 is R is 5, G is 6, B is a total of five 16-bit

A third step of obtaining View, width and height, calculated scaling value

  @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;
  }

onSizeChangedDuring the layout method, when the size is changed this view, this method is called, the first time onMeasureafter the call, can easily get View, width and height.

Then give us a custom rectangle mRectaround the upper and lower boundaries of the assignment. Normally we use this custom View larger image display, the View are filled, so here it is the initial size of the rectangle as big as it View.

mScaleFor recording the original side than mCurrentScaleto the side than the current record, and because of the double-click gestures enlarged scale, mCurrentScalewith a gesture change.

The fourth step, draw

  @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);
  }

Drawing is also very simple, a decoder decodes the region by the rectangular area, returns a Bitmap object, and then drawn through Bitmap canvas. Note mOptions.inBitmap = mBitmap; this configuration can be reused memory, use memory to ensure that this area has been the only rectangle.

Here you can run to map out part of the picture, and you want to see the whole picture, you need to drag your finger to see, which need to handle a variety of events.

The fifth step, the distribution event

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

      mScaleGestureDetector.onTouchEvent(event);
      return true;
  }

onTouchEventA very simple gesture events to two detectors own to deal with.

The sixth step, handling GestureDetectorevents

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

When a finger pressed, if the picture is the rapid slide, stop

  @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;
  }

onScrollSlide processing, according to the parameters of the finger movement, to move the rectangular drawing area, where each boundary point to deal with, such as the minimum on the left is 0, the maximum width of the picture to the right, beyond the boundary or not on the error.

  @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();
      }
  }

In the onFlingcalling method slider Scrollerof fling method inertial sliding finger off after treatment. View inertia moving distance of computeScroll()methods to calculate, but also need to pay attention to the border issue, do not slide out of bounds.

The seventh step, double-click the event processing

  @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;
  }

mMultipleDouble-click is enlarged several times, 3 times provided herein. Double-first amplifying 3 times, the second double-click to return undisturbed. After scaling, we need to re-set the boundaries of the drawing area according to the current zoom ratio. Finally, it also needs to be redefined boundaries, because after you use two fingers to enlarge, this time double-click return to the status quo, if not addressed border, position error. Processing the code boundary can be extracted.

The eighth step, processing finger zoom events

  @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;
  }

onScaleBeginThe method needs to return true, otherwise it is impossible to detect the zoom gesture. onScaleThe method of obtaining the scaling factor, the scaling factor is out of the event with the last match. * = So used herein, also need to be reset after completion of the drawing area mRectboundary.

Here the various functions to complete friends ~
Source

More content of the interview, the interview topic, flutter full range of video, audio and video experts from 0 to development.
Focus on GitHub: https://github.com/xiangjiana/Android-MS
free access

More complete project download. To be continued. Source. Graphic knowledge subsequent upload github.
You can click on my link I get

Published 257 original articles · won praise 37 · views 60000 +

Guess you like

Origin blog.csdn.net/Android_SE/article/details/105077379