Things have done 这两天比较有意思,对于Android的内存回收和管理方面有了一些理解,主要是通过动态改变主界面背景这一改动引发的。
实现方法 1.动态改变背景 首先,根据天气API返回的JSON数据中的condition字段判断当前天气来设置相应背景,背景图来自索尼自带天气APP,相当不错的素材,天气代码从官方给的说明来看相当多,但是实用的只有几个,所以直接用switch方便的判断
在工具包下新建了ChangeBackground类,Activity中传入主界面layout和天气代码condition_code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 public class ChangeBackground { public ChangeBackground (LinearLayout layout,int condition_code) { Time t=new Time(); t.setToNow(); int hour = t.hour; if (hour>=18 ||hour<=6 ){ switch (condition_code) { case 100 : layout.setBackgroundResource(R.drawable.night_clearsky); break ; case 101 : layout.setBackgroundResource(R.drawable.night_cloudy); break ; case 102 : case 103 : case 104 : layout.setBackgroundResource(R.drawable.night_partlycloudy); break ; case 300 : case 301 : case 302 : case 303 : case 304 : case 305 : case 306 : case 307 : case 308 : case 309 : case 310 : case 311 : case 312 : case 313 : layout.setBackgroundResource(R.drawable.night_rain); break ; case 400 : case 401 : case 402 : case 403 : case 404 : case 405 : case 406 : case 407 : layout.setBackgroundResource(R.drawable.night_snow); break ; case 500 : case 501 : case 502 : case 503 : case 504 : case 507 : case 508 : layout.setBackgroundResource(R.drawable.day_fog); break ; default : layout.setBackgroundResource(R.drawable.night_clearsky); break ; } }else { switch (condition_code) { case 100 : layout.setBackgroundResource(R.drawable.day_clearsky); break ; case 101 : layout.setBackgroundResource(R.drawable.day_cloudy); break ; case 102 : case 103 : case 104 : layout.setBackgroundResource(R.drawable.day_partlycloudy); break ; case 300 : case 301 : case 302 : case 303 : case 304 : case 305 : case 306 : case 307 : case 308 : case 309 : case 310 : case 311 : case 312 : case 313 : layout.setBackgroundResource(R.drawable.day_rain); break ; case 400 : case 401 : case 402 : case 403 : case 404 : case 405 : case 406 : case 407 : layout.setBackgroundResource(R.drawable.day_snow); break ; case 500 : case 501 : case 502 : case 503 : case 504 : case 507 : case 508 : layout.setBackgroundResource(R.drawable.day_fog); break ; default : layout.setBackgroundResource(R.drawable.day_clearsky); break ; } } } }
这里我遇到了一个问题,在模拟器里完美运行,但是真机却无法显示更新的背景,甚至有的机器在进入主界面直接崩溃,一开始我怀疑自己方法写的有问题,换了很多写法,用runOnUiThread之类的方法封装等等但是还是不行,最后怀疑是图片素材的问题,果然在裁剪图片素材分辨率到720*1080后终于可以显示出来了,原来Android对于图片分辨率有着很严的要求~
但是,新的问题又来了
2.万恶的OOM 真机跑通了,却发现还有着很严重的问题,在多次切换城市后出现了OOM,进一步查看程序运行时占用的内存,SONY Z2和SAMSUNG S6 EDGE下一致的达到了120M的可怕数字,后来我又在在线测试平台上发起了测试,还好并不是所有的机型都会消耗如此多的内存
但是问题还是需要解决的,在参考了别的博客后发现大致分为两种方法,一个是在合适时候回收图片占用内存,也就是重写Activity的onStop和onDestroy方法,添加以下代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 if (layout.getBackground() != null ){ Bitmap oldBitmap = ((BitmapDrawable) layout.getBackground()).getBitmap(); layout.setBackground(null ); if (oldBitmap != null ){ oldBitmap.recycle(); oldBitmap = null ; } } System.gc();
这里的System.gc()在查阅后发现对于Java和Android调用这句话的结局并不一定相同,Java中只是通知JVM程序员希望进行一次GC,但是并不一定会进行,而Android的虚拟机dalvik则会执行回收
在重写onDestroy后真机运行后内存从120M降到了80M,如果再重写onStop方法后内存再次降到40M,果然是图片缓存的锅,当然,onStop重写后会导致失去焦点后再次调用时背景消失,所以只能留onDestroy
另一种方法是通过BitmapFactory.decodeStream方法,创建出一个bitmap,再将其设为ImageView的 source,实际在使用这种方法时候,为了对比效果,我在DDMS中分别测试了这两种方法以及不使用内存优化方法的内存占用,发现比较奇怪的结果
图一:不使用优化
图二:重写onDestroy
图三:BitmapFactory
这个结果有点意思啊,说好的decodeStream减少内存占用呢
最后,附上优化过后的在线测试结果
效果还不错,真机多次切换城市也没有出现OOM情况了
Things to do? 有点迷,提不起兴趣