ThumbnailUtils Android2.2新增类

从Android 2.2开始系统新增了一个缩略图ThumbnailUtils类,位于framework的android.media.ThumbnailUtils位置,可以帮助我们从mediaprovider中获取系统中的视频或图片文件的缩略图,该类提供了三种静态方法可以直接调用获取。

   static Bitmap  createVideoThumbnail(String filePath, int kind)  //获取视频文件的缩略图,第一个参数为视频文件的位置,比如/sdcard/android123.3gp,而第二个参数可以为MINI_KIND或 MICRO_KIND最终和分辨率有关 
   static Bitmap  extractThumbnail(Bitmap source, int width, int height, int options)  //直接对Bitmap进行缩略操作,最后一个参数定义为OPTIONS_RECYCLE_INPUT ,来回收资源
   static Bitmap  extractThumbnail(Bitmap source, int width, int height) // 这个和上面的方法一样,无options选项

  最后Android开发网再次提醒大家,ThumbnailUtils类是API Level从8或更高才开始支持的。

Android缩略图类源代码

Android 2.2开始新增的缩略图类ThumbnailUtils的主要方法是静态的,对于Android 2.2或API Level8以下的工程可以直接使用,本类相对于我们常规的缩略图类考虑更周全,除了尺寸比例优化外,针对OOM的内存管理方面有更周全的处理方式,完整代码如下:

   1:  public class ThumbnailUtils {
   2:      private static final String TAG = "ThumbnailUtils";
   3:   
   4:      /* Maximum pixels size for created bitmap. */
   5:      private static final int MAX_NUM_PIXELS_THUMBNAIL = 512 * 384;
   6:      private static final int MAX_NUM_PIXELS_MICRO_THUMBNAIL = 128 * 128;
   7:      private static final int UNCONSTRAINED = -1;
   8:   
   9:      /* Options used internally. */
  10:      private static final int OPTIONS_NONE = 0x0;
  11:      private static final int OPTIONS_SCALE_UP = 0x1;
  12:   
  13:      /**
  14:       * Constant used to indicate we should recycle the input in
  15:       * {@link #extractThumbnail(Bitmap, int, int, int)} unless the output is the input.
  16:       */
  17:      public static final int OPTIONS_RECYCLE_INPUT = 0x2;
  18:   
  19:      /**
  20:       * Constant used to indicate the dimension of mini thumbnail.
  21:       * @hide Only used by media framework and media provider internally.
  22:       */
  23:      public static final int TARGET_SIZE_MINI_THUMBNAIL = 320;
  24:   
  25:      /**
  26:       * Constant used to indicate the dimension of micro thumbnail.
  27:       * @hide Only used by media framework and media provider internally.
  28:       */
  29:      public static final int TARGET_SIZE_MICRO_THUMBNAIL = 96;
  30:   
  31:      /**
  32:       * This method first examines if the thumbnail embedded in EXIF is bigger than our target
  33:       * size. If not, then it'll create a thumbnail from original image. Due to efficiency
  34:       * consideration, we want to let MediaThumbRequest avoid calling this method twice for
  35:       * both kinds, so it only requests for MICRO_KIND and set saveImage to true.
  36:       *
  37:       * This method always returns a "square thumbnail" for MICRO_KIND thumbnail.
  38:       *
  39:       * @param filePath the path of image file
  40:       * @param kind could be MINI_KIND or MICRO_KIND
  41:       * @return Bitmap
  42:       *
  43:       * @hide This method is only used by media framework and media provider internally.
  44:       */
  45:      public static Bitmap createImageThumbnail(String filePath, int kind) {
  46:          boolean wantMini = (kind == Images.Thumbnails.MINI_KIND);
  47:          int targetSize = wantMini
  48:                  ? TARGET_SIZE_MINI_THUMBNAIL
  49:                  : TARGET_SIZE_MICRO_THUMBNAIL;
  50:          int maxPixels = wantMini
  51:                  ? MAX_NUM_PIXELS_THUMBNAIL
  52:                  : MAX_NUM_PIXELS_MICRO_THUMBNAIL;
  53:          SizedThumbnailBitmap sizedThumbnailBitmap = new SizedThumbnailBitmap();
  54:          Bitmap bitmap = null;
  55:          MediaFileType fileType = MediaFile.getFileType(filePath);
  56:          if (fileType != null && fileType.fileType == MediaFile.FILE_TYPE_JPEG) {
  57:              createThumbnailFromEXIF(filePath, targetSize, maxPixels, sizedThumbnailBitmap);
  58:              bitmap = sizedThumbnailBitmap.mBitmap;
  59:          }
  60:   
  61:          if (bitmap == null) {
  62:              try {
  63:                  FileDescriptor fd = new FileInputStream(filePath).getFD();
  64:                  BitmapFactory.Options options = new BitmapFactory.Options();
  65:                  options.inSampleSize = 1;
  66:                  options.inJustDecodeBounds = true;
  67:                  BitmapFactory.decodeFileDescriptor(fd, null, options);
  68:                  if (options.mCancel || options.outWidth == -1
  69:                          || options.outHeight == -1) {
  70:                      return null;
  71:                  }
  72:                  options.inSampleSize = computeSampleSize(
  73:                          options, targetSize, maxPixels);
  74:                  options.inJustDecodeBounds = false;
  75:   
  76:                  options.inDither = false;
  77:                  options.inPreferredConfig = Bitmap.Config.ARGB_8888;
  78:                  bitmap = BitmapFactory.decodeFileDescriptor(fd, null, options);
  79:              } catch (IOException ex) {
  80:                  Log.e(TAG, "", ex);
  81:              }
  82:          }
  83:   
  84:          if (kind == Images.Thumbnails.MICRO_KIND) {
  85:              // now we make it a "square thumbnail" for MICRO_KIND thumbnail
  86:              bitmap = extractThumbnail(bitmap,
  87:                      TARGET_SIZE_MICRO_THUMBNAIL,
  88:                      TARGET_SIZE_MICRO_THUMBNAIL, OPTIONS_RECYCLE_INPUT);
  89:          }
  90:          return bitmap;
  91:      }
  92:   
  93:      /**
  94:       * Create a video thumbnail for a video. May return null if the video is
  95:       * corrupt or the format is not supported.
  96:       *
  97:       * @param filePath the path of video file
  98:       * @param kind could be MINI_KIND or MICRO_KIND
  99:       */
 100:      public static Bitmap createVideoThumbnail(String filePath, int kind) {
 101:          Bitmap bitmap = null;
 102:          MediaMetadataRetriever retriever = new MediaMetadataRetriever();
 103:          try {
 104:              retriever.setMode(MediaMetadataRetriever.MODE_CAPTURE_FRAME_ONLY);
 105:              retriever.setDataSource(filePath);
 106:              bitmap = retriever.captureFrame();
 107:          } catch (IllegalArgumentException ex) {
 108:              // Assume this is a corrupt video file
 109:          } catch (RuntimeException ex) {
 110:              // Assume this is a corrupt video file.
 111:          } finally {
 112:              try {
 113:                  retriever.release();
 114:              } catch (RuntimeException ex) {
 115:                  // Ignore failures while cleaning up.
 116:              }
 117:          }
 118:          if (kind == Images.Thumbnails.MICRO_KIND && bitmap != null) {
 119:              bitmap = extractThumbnail(bitmap,
 120:                      TARGET_SIZE_MICRO_THUMBNAIL,
 121:                      TARGET_SIZE_MICRO_THUMBNAIL,
 122:                      OPTIONS_RECYCLE_INPUT);
 123:          }
 124:          return bitmap;
 125:      }
 126:   
 127:      /**
 128:       * Creates a centered bitmap of the desired size.
 129:       *
 130:       * @param source original bitmap source
 131:       * @param width targeted width
 132:       * @param height targeted height
 133:       */
 134:      public static Bitmap extractThumbnail(
 135:              Bitmap source, int width, int height) {
 136:          return extractThumbnail(source, width, height, OPTIONS_NONE);
 137:      }
 138:   
 139:      /**
 140:       * Creates a centered bitmap of the desired size.
 141:       *
 142:       * @param source original bitmap source
 143:       * @param width targeted width
 144:       * @param height targeted height
 145:       * @param options options used during thumbnail extraction
 146:       */
 147:      public static Bitmap extractThumbnail(
 148:              Bitmap source, int width, int height, int options) {
 149:          if (source == null) {
 150:              return null;
 151:          }
 152:   
 153:          float scale;
 154:          if (source.getWidth() < source.getHeight()) {
 155:              scale = width / (float) source.getWidth();
 156:          } else {
 157:              scale = height / (float) source.getHeight();
 158:          }
 159:          Matrix matrix = new Matrix();
 160:          matrix.setScale(scale, scale);
 161:          Bitmap thumbnail = transform(matrix, source, width, height,
 162:                  OPTIONS_SCALE_UP | options);
 163:          return thumbnail;
 164:      }
 165:   
 166:      /*
 167:       * Compute the sample size as a function of minSideLength
 168:       * and maxNumOfPixels.
 169:       * minSideLength is used to specify that minimal width or height of a
 170:       * bitmap.
 171:       * maxNumOfPixels is used to specify the maximal size in pixels that is
 172:       * tolerable in terms of memory usage.
 173:       *
 174:       * The function returns a sample size based on the constraints.
 175:       * Both size and minSideLength can be passed in as IImage.UNCONSTRAINED,
 176:       * which indicates no care of the corresponding constraint.
 177:       * The functions prefers returning a sample size that
 178:       * generates a smaller bitmap, unless minSideLength = IImage.UNCONSTRAINED.
 179:       *
 180:       * Also, the function rounds up the sample size to a power of 2 or multiple
 181:       * of 8 because BitmapFactory only honors sample size this way.
 182:       * For example, BitmapFactory downsamples an image by 2 even though the
 183:       * request is 3. So we round up the sample size to avoid OOM.
 184:       */
 185:      private static int computeSampleSize(BitmapFactory.Options options,
 186:              int minSideLength, int maxNumOfPixels) {
 187:          int initialSize = computeInitialSampleSize(options, minSideLength,
 188:                  maxNumOfPixels);
 189:   
 190:          int roundedSize;
 191:          if (initialSize <= 8 ) {
 192:              roundedSize = 1;
 193:              while (roundedSize < initialSize) {
 194:                  roundedSize <<= 1;
 195:              }
 196:          } else {
 197:              roundedSize = (initialSize + 7) / 8 * 8;
 198:          }
 199:   
 200:          return roundedSize;
 201:      }
 202:   
 203:      private static int computeInitialSampleSize(BitmapFactory.Options options,
 204:              int minSideLength, int maxNumOfPixels) {
 205:          double w = options.outWidth;
 206:          double h = options.outHeight;
 207:   
 208:          int lowerBound = (maxNumOfPixels == UNCONSTRAINED) ? 1 :
 209:                  (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
 210:          int upperBound = (minSideLength == UNCONSTRAINED) ? 128 :
 211:                  (int) Math.min(Math.floor(w / minSideLength),
 212:                  Math.floor(h / minSideLength));
 213:   
 214:          if (upperBound < lowerBound) {
 215:              // return the larger one when there is no overlapping zone.
 216:              return lowerBound;
 217:          }
 218:   
 219:          if ((maxNumOfPixels == UNCONSTRAINED) &&
 220:                  (minSideLength == UNCONSTRAINED)) {
 221:              return 1;
 222:          } else if (minSideLength == UNCONSTRAINED) {
 223:              return lowerBound;
 224:          } else {
 225:              return upperBound;
 226:          }
 227:      }
 228:   
 229:      /**
 230:       * Make a bitmap from a given Uri, minimal side length, and maximum number of pixels.
 231:       * The image data will be read from specified pfd if it's not null, otherwise
 232:       * a new input stream will be created using specified ContentResolver.
 233:       *
 234:       * Clients are allowed to pass their own BitmapFactory.Options used for bitmap decoding. A
 235:       * new BitmapFactory.Options will be created if options is null.
 236:       */
 237:      private static Bitmap makeBitmap(int minSideLength, int maxNumOfPixels,
 238:              Uri uri, ContentResolver cr, ParcelFileDescriptor pfd,
 239:              BitmapFactory.Options options) {
 240:              Bitmap b = null;
 241:          try {
 242:              if (pfd == null) pfd = makeInputStream(uri, cr);
 243:              if (pfd == null) return null;
 244:              if (options == null) options = new BitmapFactory.Options();
 245:   
 246:              FileDescriptor fd = pfd.getFileDescriptor();
 247:              options.inSampleSize = 1;
 248:              options.inJustDecodeBounds = true;
 249:              BitmapFactory.decodeFileDescriptor(fd, null, options);
 250:              if (options.mCancel || options.outWidth == -1
 251:                      || options.outHeight == -1) {
 252:                  return null;
 253:              }
 254:              options.inSampleSize = computeSampleSize(
 255:                      options, minSideLength, maxNumOfPixels);
 256:              options.inJustDecodeBounds = false;
 257:   
 258:              options.inDither = false;
 259:              options.inPreferredConfig = Bitmap.Config.ARGB_8888;
 260:              b = BitmapFactory.decodeFileDescriptor(fd, null, options);
 261:          } catch (OutOfMemoryError ex) {
 262:              Log.e(TAG, "Got oom exception ", ex);
 263:              return null;
 264:          } finally {
 265:              closeSilently(pfd);
 266:          }
 267:          return b;
 268:      }
 269:   
 270:      private static void closeSilently(ParcelFileDescriptor c) {
 271:        if (c == null) return;
 272:        try {
 273:            c.close();
 274:        } catch (Throwable t) {
 275:            // do nothing
 276:        }
 277:      }
 278:   
 279:      private static ParcelFileDescriptor makeInputStream(
 280:              Uri uri, ContentResolver cr) {
 281:          try {
 282:              return cr.openFileDescriptor(uri, "r");
 283:          } catch (IOException ex) {
 284:              return null;
 285:          }
 286:      }
 287:   
 288:      /**
 289:       * Transform source Bitmap to targeted width and height.
 290:       */
 291:      private static Bitmap transform(Matrix scaler,
 292:              Bitmap source,
 293:              int targetWidth,
 294:              int targetHeight,
 295:              int options) {
 296:          boolean scaleUp = (options & OPTIONS_SCALE_UP) != 0;
 297:          boolean recycle = (options & OPTIONS_RECYCLE_INPUT) != 0;
 298:   
 299:          int deltaX = source.getWidth() - targetWidth;
 300:          int deltaY = source.getHeight() - targetHeight;
 301:          if (!scaleUp && (deltaX < 0 || deltaY < 0)) {
 302:              /*
 303:              * In this case the bitmap is smaller, at least in one dimension,
 304:              * than the target.  Transform it by placing as much of the image
 305:              * as possible into the target and leaving the top/bottom or
 306:              * left/right (or both) black.
 307:              */
 308:              Bitmap b2 = Bitmap.createBitmap(targetWidth, targetHeight,
 309:              Bitmap.Config.ARGB_8888);
 310:              Canvas c = new Canvas(b2);
 311:   
 312:              int deltaXHalf = Math.max(0, deltaX / 2);
 313:              int deltaYHalf = Math.max(0, deltaY / 2);
 314:              Rect src = new Rect(
 315:              deltaXHalf,
 316:              deltaYHalf,
 317:              deltaXHalf + Math.min(targetWidth, source.getWidth()),
 318:              deltaYHalf + Math.min(targetHeight, source.getHeight()));
 319:              int dstX = (targetWidth  - src.width())  / 2;
 320:              int dstY = (targetHeight - src.height()) / 2;
 321:              Rect dst = new Rect(
 322:                      dstX,
 323:                      dstY,
 324:                      targetWidth - dstX,
 325:                      targetHeight - dstY);
 326:              c.drawBitmap(source, src, dst, null);
 327:              if (recycle) {
 328:                  source.recycle();
 329:              }
 330:              return b2;
 331:          }
 332:          float bitmapWidthF = source.getWidth();
 333:          float bitmapHeightF = source.getHeight();
 334:   
 335:          float bitmapAspect = bitmapWidthF / bitmapHeightF;
 336:          float viewAspect   = (float) targetWidth / targetHeight;
 337:   
 338:          if (bitmapAspect > viewAspect) {
 339:              float scale = targetHeight / bitmapHeightF;
 340:              if (scale < .9F || scale > 1F) {
 341:                  scaler.setScale(scale, scale);
 342:              } else {
 343:                  scaler = null;
 344:              }
 345:          } else {
 346:              float scale = targetWidth / bitmapWidthF;
 347:              if (scale < .9F || scale > 1F) {
 348:                  scaler.setScale(scale, scale);
 349:              } else {
 350:                  scaler = null;
 351:              }
 352:          }
 353:   
 354:          Bitmap b1;
 355:          if (scaler != null) {
 356:              // this is used for minithumb and crop, so we want to filter here.
 357:              b1 = Bitmap.createBitmap(source, 0, 0,
 358:              source.getWidth(), source.getHeight(), scaler, true);
 359:          } else {
 360:              b1 = source;
 361:          }
 362:   
 363:          if (recycle && b1 != source) {
 364:              source.recycle();
 365:          }
 366:   
 367:          int dx1 = Math.max(0, b1.getWidth() - targetWidth);
 368:          int dy1 = Math.max(0, b1.getHeight() - targetHeight);
 369:   
 370:          Bitmap b2 = Bitmap.createBitmap(
 371:                  b1,
 372:                  dx1 / 2,
 373:                  dy1 / 2,
 374:                  targetWidth,
 375:                  targetHeight);
 376:   
 377:          if (b2 != b1) {
 378:              if (recycle || b1 != source) {
 379:                  b1.recycle();
 380:              }
 381:          }
 382:   
 383:          return b2;
 384:      }
 385:   
 386:      /**
 387:       * SizedThumbnailBitmap contains the bitmap, which is downsampled either from
 388:       * the thumbnail in exif or the full image.
 389:       * mThumbnailData, mThumbnailWidth and mThumbnailHeight are set together only if mThumbnail
 390:       * is not null.
 391:       *
 392:       * The width/height of the sized bitmap may be different from mThumbnailWidth/mThumbnailHeight.
 393:       */
 394:      private static class SizedThumbnailBitmap {
 395:          public byte[] mThumbnailData;
 396:          public Bitmap mBitmap;
 397:          public int mThumbnailWidth;
 398:          public int mThumbnailHeight;
 399:      }
 400:   
 401:      /**
 402:       * Creates a bitmap by either downsampling from the thumbnail in EXIF or the full image.
 403:       * The functions returns a SizedThumbnailBitmap,
 404:       * which contains a downsampled bitmap and the thumbnail data in EXIF if exists.
 405:       */
 406:      private static void createThumbnailFromEXIF(String filePath, int targetSize,
 407:              int maxPixels, SizedThumbnailBitmap sizedThumbBitmap) {
 408:          if (filePath == null) return;
 409:   
 410:          ExifInterface exif = null;
 411:          byte [] thumbData = null;
 412:          try {
 413:              exif = new ExifInterface(filePath);
 414:              if (exif != null) {
 415:                  thumbData = exif.getThumbnail();
 416:              }
 417:          } catch (IOException ex) {
 418:              Log.w(TAG, ex);
 419:          }
 420:   
 421:          BitmapFactory.Options fullOptions = new BitmapFactory.Options();
 422:          BitmapFactory.Options exifOptions = new BitmapFactory.Options();
 423:          int exifThumbWidth = 0;
 424:          int fullThumbWidth = 0;
 425:   
 426:          // Compute exifThumbWidth.
 427:          if (thumbData != null) {
 428:              exifOptions.inJustDecodeBounds = true;
 429:              BitmapFactory.decodeByteArray(thumbData, 0, thumbData.length, exifOptions);
 430:              exifOptions.inSampleSize = computeSampleSize(exifOptions, targetSize, maxPixels);
 431:              exifThumbWidth = exifOptions.outWidth / exifOptions.inSampleSize;
 432:          }
 433:   
 434:          // Compute fullThumbWidth.
 435:          fullOptions.inJustDecodeBounds = true;
 436:          BitmapFactory.decodeFile(filePath, fullOptions);
 437:          fullOptions.inSampleSize = computeSampleSize(fullOptions, targetSize, maxPixels);
 438:          fullThumbWidth = fullOptions.outWidth / fullOptions.inSampleSize;
 439:   
 440:          // Choose the larger thumbnail as the returning sizedThumbBitmap.
 441:          if (thumbData != null && exifThumbWidth >= fullThumbWidth) {
 442:              int width = exifOptions.outWidth;
 443:              int height = exifOptions.outHeight;
 444:              exifOptions.inJustDecodeBounds = false;
 445:              sizedThumbBitmap.mBitmap = BitmapFactory.decodeByteArray(thumbData, 0,
 446:                      thumbData.length, exifOptions);
 447:              if (sizedThumbBitmap.mBitmap != null) {
 448:                  sizedThumbBitmap.mThumbnailData = thumbData;
 449:                  sizedThumbBitmap.mThumbnailWidth = width;
 450:                  sizedThumbBitmap.mThumbnailHeight = height;
 451:              }
 452:          } else {
 453:              fullOptions.inJustDecodeBounds = false;
 454:              sizedThumbBitmap.mBitmap = BitmapFactory.decodeFile(filePath, fullOptions);
 455:          }
 456:      }
 457:  }
原文地址:https://www.cnblogs.com/GnagWang/p/1917658.html