程序中我们所遇到的内存问题

Android开发者,我相信绝对会困扰着。它不像IOS那么好用,有方便的LIBS提供你去使用,以及它本系统自动处理,那么作为Android开发者的你们该如何去处理这个问题?
性能优化 
Android应用程序运行的移动设备受限于其运算能力,存储空间,及电池续航。由此,它必须是高效的。电池续航可能是一个促使你优化程序的原因,即使他看起来已经运行的足够快了。由于续航对用户的重要性,当电量耗损陡增时,意味这用户迟早会发现是由于你的程序。 虽然这份文档主要包含着细微的优化,但这些绝不能成为你软件成败的关键。选择合适的算法和数据结构永远是你最先应该考虑的事情,但这超出这份文档之外。



    从程序中我们所遇到的内存问题
       使用涉及到图片控件时,如果载入的图片过多,过大,就很容易出现OutOfMemoryError异常
     在程序中,适时释放资源,是相当关键:
  1. class ImageManager {   
  2.     private WeakHashMap<Integer, WeakReference<Bitmap>> mBitmaps;   
  3.     private WeakHashMap<Integer, WeakReference<Drawable》> mDrawables;   
  4.   
  5.     private boolean mActive = true;   
  6.   
  7.     public ImageManager() {   
  8.         mBitmaps = new WeakHashMap<Integer, WeakReference<Bitmap>>();   
  9.         mDrawables = new WeakHashMap<Integer, WeakReference<Drawable>>();   
  10.     }   
  11.   

  12.     public Bitmap getBitmap(int resource) {   
  13.         if (mActive) {   
  14.             if (!mBitmaps.containsKey(resource)) {   
  15.                 mBitmaps.put(resource,   
  16.                     new WeakReference<Bitmap>(BitmapFactory.decodeResource(MainActivity.getContext().getResources(), resource)));   
  17.             }   
  18.             return ((WeakReference<Bitmap>)mBitmaps.get(resource)).get();   
  19.         }   
  20.         return null;   
  21.     }   
  22.   
  23.     public Drawable getDrawable(int resource) {   
  24.         if (mActive) {   
  25.             if (!mDrawables.containsKey(resource)) {   
  26.                 mDrawables.put(resource, new WeakReference<Drawable>(getApplication().getResources().getDrawable(resource)));   
  27.             }   
  28.             return ((WeakReference<Drawable>)mDrawables.get(resource)).get();   
  29.         }   
  30.         return null;   
  31.     }   
  32.   
  33.     public void recycleBitmaps() {   
  34.         Iterator itr = mBitmaps.entrySet().iterator();   
  35.         while (itr.hasNext()) {   
  36.             Map.Entry e = (Map.Entry)itr.next();   
  37.             ((WeakReference<Bitmap>) e.getValue()).get().recycle();   
  38.         }   
  39.         mBitmaps.clear();   
  40.     }   
  41.   
  42.     public ImageManager setActive(boolean b) {   
  43.         mActive = b;   
  44.         return this;   
  45.     }   
  46.   
  47.     public boolean isActive() {   
  48.         return mActive;   
  49.     }   
  50. }  
复制代码
在处理网络连接的时候,通常程序下开发,都会采用HTTP协议,而对应的在连接的时候,移动端就要做好对3G或者WIFI的检查连接
  1. private boolean isConnected(){
  2.                 ConnectivityManager mConnectivity = (ConnectivityManager) this.getSystemService(CONNECTIVITY_SERVICE);  
  3.                 TelephonyManager mTelephony = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
  4.                 
  5.                 // 检查网络连接,如果无网络可用,就不需要进行连网操作等
  6.                 NetworkInfo info = mConnectivity.getActiveNetworkInfo();
  7.                 if (info == null ||
  8.                         !mConnectivity.getBackgroundDataSetting()) {
  9.                         return false;
  10.                 }
  11.                 //判断网络连接类型,只有在3G或wifi里进行一些数据更新。
  12.                 int netType = info.getType();
  13.                 int netSubtype = info.getSubtype();
  14.                 if (netType == ConnectivityManager.TYPE_WIFI) {
  15.                         return info.isConnected();
  16.                 } else if (netType == ConnectivityManager.TYPE_MOBILE
  17.                         && netSubtype == TelephonyManager.NETWORK_TYPE_UMTS
  18.                         && !mTelephony.isNetworkRoaming()) {
  19.                     return info.isConnected();
  20.                 } else {
  21.                     return false;
  22.                 }
  23.         }
复制代码
对于一些Android项目,影响性能瓶颈的主要是Android自己内存管理机制问题,我们需要做什么?
缓存类
  1. public class FileCache {
  2.         
  3.         private File cacheDir;
  4.         private static final int DEFAULT_DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB
  5.         
  6.         public FileCache(Context context) {
  7.                 // 如果有SD卡则在SD卡中建一个LazyList的目录存放缓存的图片
  8.                 // 没有SD卡就放在系统的缓存目录中
  9.                 //cacheDir = new File(Environment.getExternalStorageDirectory(),"Cache_memory");
  10.                 context.getCacheDir();
  11.                 cacheDir = new File(context.getCacheDir(), "cache");
  12.                 if(!cacheDir.exists())
  13.                 {
  14.                         cacheDir.mkdirs();
  15.                 }
  16.         }

  17.     public static String hashKeyForDisk(String key) {
  18.         String cacheKey;
  19.         try {
  20.             final MessageDigest mDigest = MessageDigest.getInstance("加密算法");
  21.             mDigest.update(key.getBytes());
  22.             cacheKey = bytesToHexString(mDigest.digest());
  23.         } catch (NoSuchAlgorithmException e) {
  24.             cacheKey = String.valueOf(key.hashCode());
  25.         }
  26.         return cacheKey;
  27.     }

  28.     private static String bytesToHexString(byte[] bytes) {
  29.         StringBuilder sb = new StringBuilder();
  30.         for (int i = 0; i < bytes.length; i++) {
  31.             String hex = Integer.toHexString(0xFF & bytes[i]);
  32.             if (hex.length() == 1) {
  33.                 sb.append('0');
  34.             }
  35.             sb.append(hex);
  36.         }
  37.         return sb.toString();
  38.     }
  39.         
  40.         public File getFile(String url) {
  41.                 String filename = hashKeyForDisk(url);
  42.                 File f = new File(cacheDir, filename);
  43.                 return f;
  44.         }

  45.         public void clear() {
  46.                 File[] files = cacheDir.listFiles();
  47.                 if (files == null)
  48.                         return;
  49.                 for (File f : files)
  50.                         f.delete();
  51.         }
  52.         
  53.         public void checkAndClear() {
  54.                 if (cacheDir != null) {
  55.                         if (cacheDir.length() > DEFAULT_DISK_CACHE_SIZE) {
  56.                                 clear();
  57.                         }
  58.                 }
  59.         }
  60.         
  61. }
复制代码
释放内存,释放资源,是关键,图片异步加载更是一个层次,不要不注意警告,有时候警告也是一个关键
  1. public class ImageLoader {
  2.         
  3.         public final static int TYPE_HEAD = 1;

  4.         private Activity activity;
  5.         MemoryCache memoryCache = new MemoryCache();
  6.         FileCache fileCache;

  7.         private Map<ImageView, String> imageViews = Collections
  8.                         .synchronizedMap(new WeakHashMap<ImageView, String>());
  9.         // 线程池
  10.         ExecutorService executorService;

  11.         private static final ThreadFactory sThreadFactory = new ThreadFactory() {
  12.                 private final AtomicInteger mCount = new AtomicInteger(1);

  13.                 public Thread newThread(Runnable r) {
  14.                         return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
  15.                 }
  16.         };

  17.         public ImageLoader(Context context) {
  18.                 this.activity = (Activity) context;
  19.                 fileCache = new FileCache(context);
  20.                 executorService = Executors.newFixedThreadPool(5, sThreadFactory);
  21.         }

  22.         // 最主要的方法
  23.         public void DisplayImage(String url, ImageView imageView, int defaultId) {
  24. /*                if (HQClubApplication.getInstance().getSaveingMode()) {
  25.                         imageView.setImageResource(defaultId);
  26.                         return;
  27.                 }*/
  28.                 imageViews.put(imageView, url);
  29.                 // 先从内存缓存中查找
  30.                 Bitmap bitmap = memoryCache.get(url);
  31.                 if (bitmap != null) {
  32.                         imageView.setImageBitmap(bitmap);
  33.                 } else {
  34.                         // 若没有的话则开启新线程加载图片
  35.                         queuePhoto(url, imageView, defaultId, 0);
  36.                         imageView.setImageResource(defaultId);
  37.                 }
  38.         }
  39.         
  40.         // 最主要的方法
  41.         public void DisplayImage(String url, ImageView imageView, 
  42.                         int defaultId, int type) {
  43. /*                if (HQClubApplication.getInstance().getSaveingMode()) {
  44.                         imageView.setImageResource(defaultId);
  45.                         return;
  46.                 }*/
  47.                 imageViews.put(imageView, url);
  48.                 // 先从内存缓存中查找
  49.                 Bitmap bitmap = memoryCache.get(url);
  50.                 if (bitmap != null) {
  51.                         imageView.setImageBitmap(bitmap);
  52.                 } else {
  53.                         // 若没有的话则开启新线程加载图片
  54.                         queuePhoto(url, imageView, defaultId, type);
  55.                         imageView.setImageResource(defaultId);
  56.                 }
  57.         }
  58.         private void queuePhoto(String url, ImageView imageView, int defaultId, int type) {
  59.                 PhotoToLoad p = new PhotoToLoad(url, imageView, defaultId, type);
  60.                 executorService.submit(new PhotosLoader(p));
  61.         }

  62.         private Bitmap getBitmap(String url) {
  63.                 File f = fileCache.getFile(url);
  64.                 // 先从文件缓存中查找是否有
  65.                 Bitmap b = decodeFile(f);
  66.                 if (b != null)
  67.                         return b;

  68.                 // 最后从指定的url中下载图片
  69.                 try {
  70.                         Bitmap bitmap = null;
  71.                         URL imageUrl = new URL(url);
  72.                         HttpURLConnection conn = (HttpURLConnection) imageUrl
  73.                                         .openConnection();
  74.                         conn.setConnectTimeout(30000);
  75.                         conn.setReadTimeout(30000);
  76.                         conn.setInstanceFollowRedirects(true);
  77.                         InputStream is = conn.getInputStream();
  78.                         OutputStream os = new FileOutputStream(f);
  79.                         CopyStream(is, os);
  80.                         os.close();
  81.                         is.close();
  82.                         bitmap = decodeFile(f);
  83.                         return bitmap;
  84.                 } catch (Exception ex) {
  85.                         ex.printStackTrace();
  86.                         return null;
  87.                 }
  88.         }

  89.         // decode这个图片并且按比例缩放以减少内存消耗,虚拟机对每张图片的缓存大小也是有限制的
  90.         private Bitmap decodeFile(File f) {
  91.                 try {
  92.                         // decode image size
  93.                         BitmapFactory.Options o = new BitmapFactory.Options();
  94.                         o.inJustDecodeBounds = true;
  95.                         BitmapFactory.decodeStream(new FileInputStream(f), null, o);
  96.                         // Find the correct scale value. It should be the power of 2.
  97. //                        final int REQUIRED_SIZE = 70;
  98. //                        int width_tmp = o.outWidth, height_tmp = o.outHeight;
  99. //                        int scale = 1;
  100. //                        while (true) {
  101. //                                if (width_tmp / 2 < REQUIRED_SIZE
  102. //                                                || height_tmp / 2 < REQUIRED_SIZE)
  103. //                                        break;
  104. //                                width_tmp /= 2;
  105. //                                height_tmp /= 2;
  106. //                                scale *= 2;
  107. //                        }
  108.                         // decode with inSampleSize
  109.                         BitmapFactory.Options o2 = new BitmapFactory.Options();
  110.                         //o2.inSampleSize = scale;
  111.                         return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
  112.                 } catch (FileNotFoundException e) {
  113.                 }
  114.                 return null;
  115.         }

  116.         // Task for the queue
  117.         private class PhotoToLoad {
  118.                 public String url;
  119.                 public ImageView imageView;
  120.                 public int defaultId;
  121.                 public int type;

  122.                 public PhotoToLoad(String u, ImageView i, int d, int t) {
  123.                         url = u;
  124.                         imageView = i;
  125.                         defaultId = d;
  126.                         type = t;
  127.                 }
  128.         }

  129.         class PhotosLoader implements Runnable {
  130.                 PhotoToLoad photoToLoad;

  131.                 PhotosLoader(PhotoToLoad photoToLoad) {
  132.                         this.photoToLoad = photoToLoad;
  133.                 }

  134.                 @Override
  135.                 public void run() {
  136.                         if (imageViewReused(photoToLoad))
  137.                                 return;
  138.                         if (TextUtils.isEmpty(photoToLoad.url)) {
  139.                                 return;
  140.                         }
  141.                         Bitmap bmp = getBitmap(photoToLoad.url);
  142.                         if (photoToLoad.type == TYPE_HEAD) {
  143.                                 //bmp = getRoundedCornerBitmap(bmp);
  144.                         }
  145.                         memoryCache.put(photoToLoad.url, bmp);
  146.                         if (imageViewReused(photoToLoad))
  147.                                 return;
  148.                         BitmapDisplayer bd = new BitmapDisplayer(bmp, photoToLoad);
  149.                         // 更新的操作放在UI线程中
  150.                         activity.runOnUiThread(bd);
  151.                 }
  152.         }

  153.         /**
  154.          * 防止图片错位
  155.          * 
  156.          * @param photoToLoad
  157.          * @return
  158.          */
  159.         boolean imageViewReused(PhotoToLoad photoToLoad) {
  160.                 String tag = imageViews.get(photoToLoad.imageView);
  161.                 if (tag == null || !tag.equals(photoToLoad.url))
  162.                         return true;
  163.                 return false;
  164.         }

  165.         // 用于在UI线程中更新界面
  166.         class BitmapDisplayer implements Runnable {
  167.                 Bitmap bitmap;
  168.                 PhotoToLoad photoToLoad;

  169.                 public BitmapDisplayer(Bitmap b, PhotoToLoad p) {
  170.                         bitmap = b;
  171.                         photoToLoad = p;
  172.                 }

  173.                 public void run() {
  174.                         if (imageViewReused(photoToLoad)) {
  175.                                 return;
  176.                         }
  177.                         if (bitmap != null) {
  178.                                 photoToLoad.imageView.setImageBitmap(bitmap);
  179.                         } else {
  180.                                 photoToLoad.imageView.setImageResource(photoToLoad.defaultId);
  181.                         }
  182.                 }
  183.         }

  184.         public void clearCache() {
  185.                 memoryCache.clear();
  186.                 fileCache.clear();
  187.         }

  188.         public static void CopyStream(InputStream is, OutputStream os) {
  189.                 final int buffer_size = 1024;
  190.                 try {
  191.                         byte[] bytes = new byte[buffer_size];
  192.                         for (;;) {
  193.                                 int count = is.read(bytes, 0, buffer_size);
  194.                                 if (count == -1)
  195.                                         break;
  196.                                 os.write(bytes, 0, count);
  197.                         }
  198.                 } catch (Exception ex) {
  199.                 }
  200.         }
  201.         
  202.         //圆角处理
  203. //        public Bitmap getRoundedCornerBitmap(Bitmap bitmap) {
  204. //            Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
  205. //                bitmap.getHeight(), Config.ARGB_8888);
  206. //            Canvas canvas = new Canvas(output);
  207. //            final int color = 0xff424242;
  208. //            final Paint paint = new Paint();
  209. //            final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
  210. //            final RectF rectF = new RectF(rect);
  211. //            float roundPx;
  212. //            if(bitmap.getHeight()>70||bitmap.getWidth()>70){
  213. //                    roundPx = 15;
  214. //            }else{
  215. //                    roundPx = 5;
  216. //            }
  217. //            paint.setAntiAlias(true);
  218. //            canvas.drawARGB(0, 0, 0, 0);
  219. //            paint.setColor(color);
  220. //            canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
  221. //            paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
  222. //            canvas.drawBitmap(bitmap, rect, rect, paint);
  223. //            return output;
  224. //        }
  225. //        
  226.         
  227. }
复制代码
移动终端的资源有限,特别是在Android上,而资源的读取无疑来自两个方面:
  1. <font color="#ff0000">**</font><font color="#000000">电池优化是一门不可忽视的问题!</font>
复制代码
优化不仅源于程序,在虚拟机和原生库层面,Android同样进行了很多的优化!
在这边,我把内存泄漏的情况总结下:
  1.         @Override
  2.         public View getView(int position, View convertView, ViewGroup parent) {
  3.                 ViewHolder vHolder = null;
  4.                 //如果convertView对象为空则创建新对象,不为空则复用
  5.                 if (convertView == null) {
  6.                         convertView = inflater.inflate(..., null);
  7.                         // 创建 ViewHodler 对象
  8.                         vHolder = new ViewHolder();
  9.                         vHolder.img= (ImageView) convertView.findViewById(...);
  10.                         vHolder.tv= (TextView) convertView
  11.                                         .findViewById(...);
  12.                         // 将ViewHodler保存到Tag中
  13.                         convertView.setTag(vHolder);
  14.                 } else {
  15.                         //当convertView不为空时,通过getTag()得到View
  16.                         vHolder = (ViewHolder) convertView.getTag();
  17.                 }
  18.                 // 给对象赋值,修改显示的值
  19.                 vHolder.img.setImageBitmap(...);
  20.                 vHolder.tv.setText(...);
  21.                 return convertView;
  22.         }
  23.         //将显示的View 包装成类
  24.         static class ViewHolder {
  25.                 TextView tv;
  26.                 ImageView img;
  27.         }
复制代码

3.调用registerReceiver()后未调用unregisterReceiver(). (这个可以忽略)
  1. <blockquote> @Override
复制代码
4. 6.Context泄漏
1.使用Application这种Context类型。 
2.注意对Context的引用不要超过它本身的生命周期。 
3.慎重的使用“static”关键字。 
4.Context里如果有线程,一定要在onDestroy()里及时停掉。
  1. private static Drawable sBackground;
  2. @Override
  3. protected void onCreate(Bundle state) {
  4.   super.onCreate(state);

  5.   TextView label = new TextView(this);
  6.   label.setText("Leaks are bad");

  7.   if (sBackground == null) {
  8.     sBackground = getDrawable(R.drawable.large_bitmap);
  9.   }
  10.   label.setBackgroundDrawable(sBackground);

  11.   setContentView(label);
  12. }
复制代码




在涉及到性能优化方面,内存是占据第一位的,而Android的垃圾收集中,内存泄漏和引用,在文档中,我们注意到一个典型的例子就是:屏幕旋转,activiity对象会有泄漏,很严重,却常常被忽略掉,毕竟在应用中占据内存的是activity,在Android中DDMS,可以使用Heap以及Allocation Tracker跟踪内存使用和分配情况去检测内存情况。
    从android 3.0以上的版本中,可以检测到潜在的泄漏:1.activity泄漏;2.其他对象泄漏;3.对象没有关闭造成的泄漏;
    通常下,使用strictMode类来检测内存泄漏
  1. public void onCreate() {
  2.      if (DEVELOPER_MODE) {
  3.          StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
  4.                  .detectDiskReads()
  5.                  .detectDiskWrites()
  6.                  .detectNetwork()   // or .detectAll() for all detectable problems
  7.                  .penaltyLog()
  8.                  .build());
  9.          StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
  10.                  .detectLeakedSqlLiteObjects()
  11.                  .detectLeakedClosableObjects()
  12.                  .penaltyLog()
  13.                  .penaltyDeath()
  14.                  .build());
  15.      }
  16.      super.onCreate();
  17. }
复制代码
  1. public class MyApplication extends Application {
  2.         @Override
  3.         public void onCreate() {
  4.                 super.onCreate();
  5.                 StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
  6.                 builder.detectLeakedSqlLiteObjects();
  7.                 if(VERSION.SDK_INT >=Build.VERSION_CODES.HOMEYCOMB)
  8.                 {
  9.                         builder.detectLeakedSqlLiteObjects().detectLeakedSqlLiteObjects();
  10.                 }
  11.                 //或者可以简单的调用builder.detectAll();
  12.                 //采取措施
  13.                 builder.penaltyLog();//若有其他措施(如penaltyDeath())可以在这合并处理
  14.                 StrictMode.VmPolicy vmp = builder.build();
  15.                 StrictMode.setVmPolicy(vmp);
  16.         }
  17. }
复制代码
转载自http://www.apkbus.com/forum.php?mod=viewthread&tid=157030&page=1&extra=    作者:清雨轩
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值