Android编程规范摘要6 (Bitmap、Drawable 与动画)

Bitmap、Drawable 与动画


其它相关文章:
Android编程规范摘要1 (资源文件命名与使用)
Android编程规范摘要2 (基本组件)
Android编程规范摘要3 (UI与布局)
Android编程规范摘要4 (进程、线程与消息通信)
Android编程规范摘要5 (文件与数据库)
Android编程规范摘要6 (Bitmap、Drawable 与动画)
Android编程规范摘要7 (安全)


  1. [强制] 加载大图片或者一次性加载多张图片,应该在异步线程中进行。图片的加载,涉及到IO 操作,以及CPU 密集操作,很可能引起卡顿。

    • 正例:

      class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
          ...
          // 在后台进行图片解码
          @Override
          protected Bitmap doInBackground(Integer... params) {
              final Bitmap bitmap = BitmapFactory.decodeFile("some path");
              return bitmap;
          }
          ...
      }
    • 反例:

      Button btnLoadImage = (Button) findViewById(R.id.btn);
      btnLoadImage.setOnClickListener(new OnClickListener(){
          public void onClick(View v) {
              Bitmap bitmap = BitmapFactory.decodeFile("some path");
          }
      });
  2. [强制] 在ListView,ViewPager,RecyclerView,GirdView等组件中使用图片时,应做好图片的缓存,避免始终持有图片导致内存溢出,也避免重复创建图片,引起性能问题。

    • 建议使用fresco、glide等图片库。
    • 正例: 使用系统LruCache 缓存

      public class Test1Activity extends Activity {
      
          private LruCache<String, Bitmap> mMemoryCache;
          private ImageView mImageView;
      
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_test1);
      
              final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
              // 把最大可用内存的1/8 作为缓存空间
              final int cacheSize = maxMemory / 8;
              mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
                  @Override
                  protected int sizeOf(String key, Bitmap bitmap) {
                      return bitmap.getByteCount() / 1024;
                  }
              };
          }
          public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
              if (getBitmapFromMemCache(key) == null) {
                  mMemoryCache.put(key, bitmap);
              }
          }
          public Bitmap getBitmapFromMemCache(String key) {
              return mMemoryCache.get(key);
          }
      
          public void loadBitmap(int resId, ImageView imageView) {
              final String imageKey = String.valueOf(resId);
              final Bitmap bitmap = getBitmapFromMemCache(imageKey);
              if (bitmap != null) {
                  mImageView.setImageBitmap(bitmap);
              } else {
                  mImageView.setImageResource(R.drawable.ic_launcher_background);
                  BitmapWorkerTask task = new BitmapWorkerTask(mImageView);
                  task.execute(resId);
              }
          }
          class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
              ...
              // 在后台进行图片解码
              @Override
              protected Bitmap doInBackground(Integer... params) {
                  final Bitmap bitmap = decodeSampledBitmapFromResource(getResources(),
                          params[0], 100, 100);
                  addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);
                  return bitmap;
              }
              ...
          }
      }
    • 反例: 没有存储,每次都需要解码,或者有缓存但是没有合适的淘汰机制,导致缓存效果很差,依然经常需要重新解码。
  3. [强制] png 图片使用TinyPNG 或者类似工具压缩处理,减少包体积。

  4. 应根据实际展示需要,压缩图片,而不是直接显示原图。手机屏幕比较小,直接显示原图,并不会增加视觉上的收益,但是却会耗费大量宝贵的内存。

    • 正例:

      public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) {
          // 首先通过inJustDecodeBounds=true 获得图片的尺寸
          final BitmapFactory.Options options = new BitmapFactory.Options();
          options.inJustDecodeBounds = true;
          BitmapFactory.decodeResource(res, resId, options);
          // 然后根据图片分辨率以及我们实际需要展示的大小,计算压缩率
          options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
          // 设置压缩率,并解码
          options.inJustDecodeBounds = false;
          return BitmapFactory.decodeResource(res, resId, options);
      }
    • 反例: 不经压缩显示原图。
  5. [强制] 使用完毕的图片,应该及时回收,释放宝贵的内存。

    • 正例:

      Bitmap bitmap = null;
      loadBitmapAsync(new OnResult(result){
      bitmap = result;
      });
      //...使用该bitmap...
      // 使用结束,在2.3.3 及以下需要调用recycle()函数,在2.3.3 以上GC 会自动管理,除非你明确不需要再用。
      if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) {
          bitmap.recycle();
      }
      bitmap = null;
    • 反例: 使用完成图片,始终不释放资源。
  6. [强制] 在Activity#onPause()或Activity#onStop()回调中,关闭当前activity 正在执行的的动画。

    • 正例:

      public class MyActivity extends Activity {
          ImageView mImageView;
          Animation mAnimation;
          Button mBtn;
          @Override
          public void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.main);
              mImageView = (ImageView)findViewById(R.id.ImageView01);
              mAnimation = AnimationUtils.loadAnimation(this, R.anim.anim);
              mBtn= (Button)findViewById(R.id.Button01);
              mBtn.setOnClickListener(new View.OnClickListener() {
                  @Override
                  public void onClick(View v) {
                  mImageView.startAnimation(mAnimation);
              }
          });
      }
      
          @Override
          public void onPause() {
              //页面退出,及时清理动画资源
              mImageView.clearAnimation()
          }
      }
    • 页面退出时,不关闭该页面相关的动画。
  7. 在动画或者其他异步任务结束时,应该考虑回调时刻的环境是否还支持业务处理。例如Activity 的onStop()函数已经执行,且在该函数中主动释放了资源,此时回调中如果不做判断就会空指针崩溃。

    • 正例:

      public class MyActivity extends Activity {
          private ImageView mImageView;
          private Animation mAnimation;
          @Override
          public void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.main);
              mImageView = (ImageView)findViewById(R.id.ImageView01);
              mAnimation = AnimationUtils.loadAnimation(this, R.anim.anim);
              mAnimation.setAnimationListener(new AnimationListener() {
                  @Override
                  public void onAnimationEnd(Animation arg0) {
                      //判断一下资源是否被释放了
                      if (mImageView != null) {
                          mImageView.clearAnimation();
                      }
                  }
              });
              mImageView.startAnimation(mAnimation);
          }
      }
    • 反例: 动画结束回调中,直接使用资源不加判断,导致异常。
  8. 使用 inBitmap 重复利用内存空间,避免重复开辟新内存。

    • 正例:

      public static Bitmap decodeSampledBitmapFromFile(String filename, int reqWidth, int reqHeight, ImageCache cache) {
          final BitmapFactory.Options options = new BitmapFactory.Options();
          ...
          BitmapFactory.decodeFile(filename, options);
          ...
          // 如果在Honeycomb 或更新版本系统中运行,尝试使用inBitmap
          if (Utils.hasHoneycomb()) {
              addInBitmapOptions(options, cache);
          }
          ...
          return BitmapFactory.decodeFile(filename, options);
      }
      
      private static void addInBitmapOptions(BitmapFactory.Options options, ImageCache cache) {
          // inBitmap 只处理可变的位图,所以强制返回可变的位图
          options.inMutable = true;
          if (cache != null) {
              Bitmap inBitmap = cache.getBitmapFromReusableSet(options);
              if (inBitmap != null) {
                  options.inBitmap = inBitmap;
              }
          }
      }
  9. 使用RGB_565 代替RGB_8888,在不怎么降低视觉效果的前提下,减少内存占用。

    • android.graphics.Bitmap.Config 类中关于图片颜色的存储方式定义:

      Config规格
      ALPHA_88 位 Alpha 位图
      ARGB_444416 位ARGB 位图
      ARGB_888832 位ARGB 位图
      RGB_5658 位RGB 位图
    • 注意: RGB_565 是没有透明度的,如果图片本身需要保留透明度,那么就不能使用RGB_565。

    • 正例:

      Config config = drawableSave.getOpacity() != PixelFormat.OPAQUE ? Config.ARGB_8565 :
      Config.RGB_565;
      Bitmap bitmap = Bitmap.createBitmap(w, h, config);
    • 反例:

      Bitmap newb = Bitmap.createBitmap(width, height, Config.ARGB_8888);
  10. 尽量减少 Bitmap(BitmapDrawable)的使用,尽量使用纯色(ColorDrawable)、渐变色(GradientDrawable)、StateSelector(StateListDrawable)等与Shape 结合的形式构建绘图。

  11. 谨慎使用gif 图片,注意限制每个页面允许同时播放的gif图片,以及单个gif图片的大小。

  12. [参考] 大图片资源不要直接打包到apk,可以考虑通过文件仓库远程下载,减小包体积。

  13. 根据设备性能,选择性开启复杂动画,以实现一个整体较优的性能和体验;

  14. 在有强依赖 onAnimationEnd 回调的交互时,如动画播放完毕才能操作页面,onAnimationEnd 可能会因各种异常没被回调(参考:https://stackoverflow.com/questions/5474923/onanimationend-is-not-getting-called-onanimationstart-works-fine), 建议加上超时保护或通过 postDelay 替代onAnimationEnd。

    • 正例:

      View v = findViewById(R.id.xxxViewID);
      final FadeUpAnimation anim = new FadeUpAnimation(v);
      anim.setInterpolator(new AccelerateInterpolator());
      anim.setDuration(1000);
      anim.setFillAfter(true);
      new Handler().postDelayed(new Runnable() {
          public void run() {
              if (v != null) {
                  v.clearAnimation();
              }
          }
      }, anim.getDuration());
      v.startAnimation(anim);
  15. 当View Animation 执行结束时,调用View.clearAnimation()释放相关资源。

    • 正例:

      View v = findViewById(R.id.xxxViewID);
      final FadeUpAnimation anim = new FadeUpAnimation(v);
      anim.setInterpolator(new AccelerateInterpolator());
      anim.setDuration(1000);
      anim.setFillAfter(true);
      anim.setAnimationListener(new AnimationListener() {
          @Override
          public void onAnimationEnd(Animation arg0) {
              //判断一下资源是否被释放了
              if (v != null) {
                  v.clearAnimation();
              }
          }
      });
      v.startAnimation(anim);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值