상세 안 드 로 이 드성능 최적화 ViewPager 에 수백 수천 개의 고 화질 큰 그림 oom 솔 루 션 을 불 러 옵 니 다.

배경
최근 에 프로젝트 를 할 때 선택 한 그림 을 업로드 해 야 합 니 다.위 챗,웨 이 보 와 같은 그림 선택 기 입 니 다.ContentResolver 는 로 컬 그림 자원 을 읽 고 RecyclerView+Glide 로 그림 을 불 러 오 면 목록 의 표 시 를 해결 합 니 다.이것 은 큰 문제 가 없습니다.중요 한 것 은 그림 을 클릭 하여 큰 그림 탐색 에 들 어 가 는 것 입 니 다.예 를 들 어 앨범 에 수백 장의 그림 이 있 습 니 다.즉,ViewPager 에 수백 개의 view 를 불 러 와 야 한 다 는 뜻 이다.게다가 핸드폰 으로 찍 은 사진 은 모두 1-2 천만 화소 의 고 화질 큰 그림(필자 핸드폰 2 천만 화소 즉 사진 을 찍 은 사진 3888*5152)이 고 크기 도 5-7 메 가 이다.ViewPager 는 10 여 장 이 미 끄 러 지지 않 으 면 oom 이다.즉,사진 을 압축 처리 하여 사진 해상 도 를 1366*960 으로 낮 춘 것 이다.크기 를 150 k 이하 로 압축 하고 ViewPager 의 destroy Item 방법 으로 bitmap 자원 을 회수 했다.효과 가 좋 지만 oom 의 강림 을 막 을 수 없다.50-70 장 을 훑 어 보 았 을 때 oom 은 메모리 가 계속 폭등 하여 회수 할 수 없 었 습 니 다.믿 지 않 으 면 압축 과 회수 가 문 제 를 근본적으로 해결 할 수 없 었 습 니 다.그러면 어떻게 해결 합 니까?위 챗 과 위 보 를 연 구 했 는데 그들 은 아무리 해도 oom 을 할 줄 모 르 고 마지막 에 나 는 해결 방안 을 생각 했다.
2.방안 실시
1.예전 의 일반적인 방법
부분 코드:

List<SubsamplingScaleImageView> mViews = new ArrayList<>(); 
     
    int size = mDatas.size(); 
    for (int i = 0; i < size; i++) { 
      SubsamplingScaleImageView view = new SubsamplingScaleImageView(this); 
      mViews.add(view); 
    } 
 
    mBinding.viewpager.setAdapter(new MyAdapter()); 

class MyAdapter extends PagerAdapter { 
 
    @Override 
    public int getCount() { 
      return mDatas.size(); 
    } 
 
    @Override 
    public boolean isViewFromObject(View view, Object object) { 
      return view == object; 
    } 
 
    @Override 
    public Object instantiateItem(ViewGroup container, final int position) { 
 
      ViewGroup.LayoutParams params = new ViewGroup.LayoutParams( 
          ViewPager.LayoutParams.MATCH_PARENT,ViewPager.LayoutParams.MATCH_PARENT); 
      final SubsamplingScaleImageView imageView = mViews.get(position); 
      imageView.setLayoutParams(params); 
 
      final String url = mDatas.get(position); 
      String cacheExists = cacheExists(url); 
      if(TextUtils.isEmpty(cacheExists)) {//        (       ) 
        new AsyncTask<Void, Void, String>() { 
          @Override 
          protected String doInBackground(Void... voids) { 
            String cacheNoExistsPath = getCacheNoExistsPath(url); 
            BitmapCompressUtils.compressBitmap(url, cacheNoExistsPath); 
            File file = new File(cacheNoExistsPath); 
            if (file.exists()) {//       
              return cacheNoExistsPath; 
            } else { 
              return url; 
            } 
          } 
 
          @Override 
          protected void onPostExecute(String s) { 
            imageView.setImage(ImageSource.uri(s)); 
          } 
 
        }.execute(); 
 
 
      } else {//         
        imageView.setImage(ImageSource.uri(cacheExists)); 
      } 
 
      container.addView(imageView); 
      return imageView; 
 
    } 
 
    @Override 
    public void destroyItem(ViewGroup container, int position, Object object) { 
 
      SubsamplingScaleImageView imageView = mViews.get(position); 
      if(imageView != null) { 
        imageView.recycle(); 
      } 
 
      container.removeView(imageView); 
 
    } 
  } 


/** 
   *       url              ""      
   * 
   * @param url      
   * @return 
   */ 
  private String cacheExists(String url) { 
    try { 
      File fileDir = new File(mCacheRootPath); 
      if(!fileDir.exists()) { 
        fileDir.mkdirs(); 
      } 
 
      File file = new File(mCacheRootPath,new StringBuffer().append(MD5EncryptorUtils.md5Encryption(url)).toString()); 
      if(file.exists()) { 
        return file.getAbsolutePath(); 
      } 
    } catch (Exception e) { 
      e.printStackTrace(); 
    } 
 
    return ""; 
  } 
 
  public String getCacheNoExistsPath(String url) { 
    File fileDir = new File(mCacheRootPath); 
    if(!fileDir.exists()) { 
      fileDir.mkdirs(); 
    } 
 
 
    return new StringBuffer().append(mCacheRootPath) 
        .append(MD5EncryptorUtils.md5Encryption(url)).toString(); 
  } 
여기 서 필 자 는 자신의 압축 알고리즘(이전 글 AndroidNDK 이미지 압축 의 Libjpeg 라 이브 러 리 사용)는 이미지 압축 을 하고 캐 시 를 했 습 니 다.세심 한 친구 들 은 mViews 집합 에 추 가 된 view 개 수 는 mDatas 의 size 크기 개수 라 는 것 을 알 아야 합 니 다.그러면 문제 가 발생 할 수 있 습 니 다.ViewPager 가 계속 아래로 미 끄 러 질 때 메모리 가 계속 증가 합 니 다.즉,자원 회수 입 니 다.또한 문 제 를 해결 할 수 없습니다.

2.해결 방안:
그림 압축 도 하고 자원 회수 도 했 지만 ViewPager 가 점점 더 많은 그림 을 불 러 올 때 oom 은 피 할 수 없 을 것 입 니 다.믿 지 않 으 면 해 보 세 요.
여기 서 ViewPager 의 view 재 활용 메커니즘(자신 이 이해 하 는)을 사용 해 야 합 니 다.즉,mViews 는 우리 가 주어진 수량 을 고정 시 켜 야 합 니 다.예 를 들 어 4.이렇게 ViewPager 의 i 가 실제 필요 로 하 는 item 도 4 개 밖 에 없습니다.
수 정 된 부분 코드:

for (int i = 0; i < 4; i++) { 
      SubsamplingScaleImageView view = new SubsamplingScaleImageView(this); 
      mViews.add(view); 
    } 
 
    mBinding.viewpager.setAdapter(new MyAdapter()); 

class MyAdapter extends PagerAdapter { 
 
    @Override 
    public int getCount() { 
      return mDatas.size(); 
    } 
 
    @Override 
    public boolean isViewFromObject(View view, Object object) { 
      return view == object; 
    } 
 
    @Override 
    public Object instantiateItem(ViewGroup container, final int position) { 
 
      ViewGroup.LayoutParams params = new ViewGroup.LayoutParams( 
          ViewPager.LayoutParams.MATCH_PARENT,ViewPager.LayoutParams.MATCH_PARENT); 
 
      int i = position % 4; 
      final SubsamplingScaleImageView imageView = mViews.get(i); 
      imageView.setLayoutParams(params); 
 
      final String url = mDatas.get(position); 
      String cacheExists = cacheExists(url); 
      if(TextUtils.isEmpty(cacheExists)) {//        (       ) 
        new AsyncTask<Void, Void, String>() { 
          @Override 
          protected String doInBackground(Void... voids) { 
            String cacheNoExistsPath = getCacheNoExistsPath(url); 
            BitmapCompressUtils.compressBitmap(url, cacheNoExistsPath); 
            File file = new File(cacheNoExistsPath); 
            if (file.exists()) {//       
              return cacheNoExistsPath; 
            } else { 
              return url; 
            } 
          } 
 
          @Override 
          protected void onPostExecute(String s) { 
            imageView.setImage(ImageSource.uri(s)); 
          } 
 
        }.execute(); 
 
 
      } else {//         
        imageView.setImage(ImageSource.uri(cacheExists)); 
      } 
 
      container.addView(imageView); 
      return imageView; 
 
    } 
 
    @Override 
    public void destroyItem(ViewGroup container, int position, Object object) { 
      int i = position % 4; 
      SubsamplingScaleImageView imageView = mViews.get(i); 
      if(imageView != null) { 
        imageView.recycle(); 
      } 
 
      container.removeView(imageView); 
 
    } 

간단 한 수정 으로 oom 을 효과적으로 방지 할 수 있 습 니 다.  position%4 를 이용 하여 몇 번 째 컨트롤 을 가 져 와 mViews 에서 값 을 추출 하여 viewpager 에 불 러 온 mViews 에 저 장 된 그림 을 4 개 로 보장 합 니 다.
계속 아래로 미 끄 러 지 는 메모리 동향 도 를 보 세 요.

메모리 가 기본적으로 안정 을 유지 하 다.
데모 데모
앨범 을 읽 으 려 면 시 뮬 레이 터 를 실행 하지 않 고 gif 를 직접 캡 처 합 니 다.

 
demo 다운로드:demo
총화
이것 은 간단 한 프 리 젠 테 이 션 일 뿐 실제 프로젝트 의 앨범 은 이것 보다 훨씬 복잡 합 니 다.쉽게 말 하면 압축 하고 회수 하 며 View 를 다시 사용 하 는 것 입 니 다.
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기