本文来自http://blog.csdn.net/liuxian13183/ ,引用必须注明出处!
安卓应用的内存往往是有限的,从开始的8M到16M,24M,32M,48M,64M等逐步变大,但内存的变大是由于分辨率的提高导致,并不意味着可以随意声明使用内存,而不及时回收(即使Java有自己的垃圾回收机制,但内存过高会引起应用变卡,体验流畅性下降)。
降低应用内存消耗的办法有以下几种常见办法:
1、图片声明使用的context使用Application,回收时清除ImageView的drawable
但是如果使用context.startActivity会报一个错
android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
因此如果确定用application启动activity,要注意加个FLAG_ACTIVITY_NEW_TASK这个flag
同时ProgressDialog的生成也不能用application,因为它依赖着当前页面
2、使用viewStub占位,避免经常使用gone方法,减少对象的加载和初始化
3、使用merge把能合并的布局统统合并,在hierachyviewer里面可以看到布局的复杂度
如果使用include,给布局加margin的话,需要同时设置include标签的宽高
4、去掉decorView和window的背景,往往由于应用有自身的色调搭配
5、通过canvas的clip方法,避免在看不到的地方画图,通过quickReject方法来在确定的区域比如矩形内绘制,
跳过非既定区域内绘制
6、使用9path文件和自定义图片,以及透明背景,来防止过度绘制
7、列表可以给定一个高度(根据item的高度来动态设置),来防止重复计算高度和执行布局方法
8、合理选择组件,选择简单的而非复杂的组件(原因,如果你自定义过复杂组件自己就会明白)
9、开启新进程作为服务进程和工具进程-最大招,有效降低当前应用的内存消耗
10、避免在onDraw频繁调用。
避免内存泄露的几个办法:
1、及时清除对象或回调引用的context(或使用Application),降低引用链长度
/** * 清除页面的ImageView的引用链 * @param view */ public static void unbindDrawables(View view) { if (view.getBackground() != null) { view.getBackground().setCallback(null); } if (view instanceof ViewGroup) { for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) { unbindDrawables(((ViewGroup) view).getChildAt(i)); } ((ViewGroup) view).removeAllViews(); } }
2、Bitmap用前根据屏幕dpi或自定义要求进行压缩,过后及时回收;使用inBitmap复制前面图片的内存,使整个屏幕
只展示当前页面图片所占的内存。
使用inBitmap需要注意几个限制条件:
另外提一点:在2.x的系统上,尽管bitmap是分配在native层,但是还是无法避免被计算到OOM的引用计数器里面。这里提示一下,不少应用会通过反射BitmapFactory.Options里面的inNativeAlloc来达到扩大使用内存的目的,但是如果大家都这么做,对系统整体会造成一定的负面影响,建议谨慎采纳。
3、Cursor对象及时关闭,避免对数据库对象的长期引用
4、关键地方做空判断,页面关闭时及时回收对象
5、context尽量使用application,避免页面关闭时,由于引用存在而不能及时回收对象,尤其getResource功能,
在fragment中特别容易引起泄露,出现not attach的问题
6、避免在for循环中声明对象(一下子无数个对象产生,内存暴增),引用能写在外面最好,如array.length,直接用
int size获取值,再遍历
7、打开开发者模式中的CPU绘制选项,根据屏幕显示的红黄蓝来辨别页面的绘制情况
8、handler(等内部类)往往引用context,使用弱引用的方式处理
public WeakHandler handler = new WeakHandler(this); public class WeakHandler extends Handler { WeakReference<Context> mContextWeakReference; public WeakHandler(Context context) { mContextWeakReference = new WeakReference<Context>(context); } @Override public void handleMessage(Message msg) { if (mContextWeakReference.get() == null || msg == null) { return; } boolean handled = !handleMessageDelegate(msg.what, msg.obj); if (handled) { if (msg.what < 0) { handleErrorMessage(msg); } else { handlePtrMessage(msg); } } } }
9、一般webView也会有内存泄露的问题出现,往往由于引用未删除,自身的view仍然存在,在进程一系列操作后,仍可以使用开启新进程来降低应用内存(通过Aidl进行通信。)
/** * 优化内存最后一招-开启新进程 */ @Override protected void onDestroy() { if (mWebView != null) {// remove webView, prevent chromium to crash ViewParent parent = mWebView.getParent(); if (parent != null) { ((ViewGroup) parent).removeView(mWebView); } // 退出时调用此方法,移除绑定的服务,否则某些特定系统会报错 mWebView.getSettings().setJavaScriptEnabled(false); // 解决Receiver not registered: // android.widget.ZoomButtonsController mWebView.setVisibility(View.GONE); mWebView.removeAllViews(); mWebView.clearCache(false); mWebView.stopLoading(); mWebView.destroy(); mWebView = null; setConfigCallback(null); } super.onDestroy(); } /** * 删除引用 * @param windowManager */ public void setConfigCallback(WindowManager windowManager) { try { Field field = WebView.class.getDeclaredField("mWebViewCore"); field = field.getType().getDeclaredField("mBrowserFrame"); field = field.getType().getDeclaredField("sConfigCallback"); field.setAccessible(true); Object configCallback = field.get(null); if (null == configCallback) { return; } field = field.getType().getDeclaredField("mWindowManager"); field.setAccessible(true); field.set(configCallback, windowManager); } catch(Exception e) { } }
10、静态匿名内部类相比内部类来说,后者会直接引用父类,而前者不会,因此前者不会引起内存泄露后者则会。
11、注册的组件要在生命周期结束时要注销掉。
检查内存泄露的工具有:Lint(inspect code-performance)、Mat(case gc-分析hprof文件)、LeakMemory(Log日志弹窗)、As自带(Monitor-Dump Java Heap),更多介绍
图片更多:Android ImageView设置图片原理(下)
电量:
1、一般情况下耗电可能是死循环引起的,不断的请求,不断的失败,又不断的重试,总之业务引起的占多数。
2、其次系统加入低电耗的模式,如果你的应用不断唤醒cpu,执行一些耗时操作,那自然电量消耗过多
3、接口和产品设计不合理,一般情况下能一个接口返回的数据不要两个,请求数据包的封装也要占一大部分流量,另外产品的YY需求,而非用户真实需求,往往会造成实现的复杂度。
4、同步数据尽量在用户充电时或电量较多时
流量:
1、同步数据尽量在wifi下
2、接口能合一则合,产品设计简单极致
3、做好本地数据缓存,以及和服务端的同步机制;例如get请求和cache-control
4、webview做好本地缓存,加载时优先加载本地资源,当然也需要服务端支持
修改http服务器中的配置,使其支持text/cache-manifest,我使用的是apache服务器,是windows版本的,在apache的conf文件夹中找到mime.types文件,打开后在文件的最后加上“text/cache-manifest mf manifest”,重启服务器
5、压缩H5的图片大小,以及改变图片格式为webp,或者把背景图改为jpg,将纯色图用xml来写。
关于流量再讲下IOS吧,一般应用安装后都会开启后台刷新功能,导致流量耗用的特别快,开着移动流量三四天一百兆一点也不夸张(应用安装上百个),在设置-通用-后台应用刷新,关掉后台刷新,只把微信打开就够了。
卡顿:
1、ANR的出现在于在UI线程操作过久(Activity5s,BroadReceiver10s),则耗时操作尽量放在子线程处理,完毕后通知主线程
2、页面重绘多次,划定绘画区域如canvas.clipRect;控制的宽高不定多次计算,可以设置定值
3、页面复杂度过高,产品或自己的原因,内存不注意回收
4、点击后的背景尽量用透明,减少图片绘制的复杂度
5、避免onDraw频繁调用
使用leakCanary来监控泄露,并上传
https://www.liaohuqiu.net/cn/posts/leak-canary-read-me/
https://github.com/square/leakcanary
使用ProGuard做apk压缩
https://developer.android.com/studio/build/shrink-code.html
其他工具:MAT、Emmagee
页面渲染时,被绘制的元素最终要转换成矩阵像素点(即多维数组形式,类似安卓中的Bitmap),才能被显示器显示。
硬件加速的主要原理,就是通过底层软件代码,将CPU不擅长的图形计算转换成GPU专用指令,由GPU完成。