1:布局优化
减少布局文件的层级,布局中的层级减少了,意味着Android绘制时的工作量少了,程序的性能自然就高了。
1.1 如何进行布局优化呢?
- 删除布局无用的层级
- 有选择行使用性能较低的ViewGroup.比如RelativeLayout。如果布局中能使用LinearLaout也可以使用Relativelaout,那么使用LinearLayout,因为RelativeLayotd 功能复杂些,布局过程使用的cup需要花费更多的时间。
- 采用<include>标签,<merge>标签,Viewstub标签。<include>标签主要用于布局的重用,<merge>标签一般和<include>配合使用,减少布局的层级,ViewStub则提供了按需加载的功能,当需要是才会将ViewStub中的布局加载到内存,这提高了程序的初始化效率。
2 绘制优化
绘制优化是指View的onDraw方法要避免执行大量的操作,这主要体现在两个方面。
- onDraw方法中不要创建新的局部对象,这是因为onDraw方法可能会被频繁调用,这样会在一瞬间产生大量的临时对象,这不仅占用了过多的内存而且还会导致系统更加频繁gc,降低了程序的执行效率。
- onDraw方法中不要做好事的任务
内存泄漏优化
3.1 内存泄漏的原因
- 当一个对象已经不需要在被使用,本该被GC回收时,而灵位一个正在使用的对象持有它的引用,从而导致它不能被程序回收,而停留在堆内存中。
- 本质原因:持有引用者的生命周期>被引用者的生命周期
3.2 内存泄漏的场景
3.2.1 静态变量导致的内存泄漏
public class MainActivity extends AppCompatActivity {
private static Context sContext;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
sContext=this;
}
}
public class MainActivity extends AppCompatActivity {
private static View mView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
sContext=this;
mView=new View(this);
}
}
public class ClassName {
// 定义1个静态变量
private static Context mContext;
//...
// 引用的是Activity的context
mContext = context;
// 当Activity需销毁时,由于mContext = 静态 & 生命周期 = 应用程序的生命周期,故 Activity无法被回收,从而出现内存泄露
}
以上三个都是Static成员变量持有了当前activity的引用,被 Static 关键字修饰的成员变量的生命周期 = 应用程序的生命周期
静态变量导致的内存泄漏解决方案
- 尽量避免 Static 成员变量引用资源耗费过多的实例(如 Context)
- 如需引用Context,则尽量使用Applicatong的Context
- 使用 弱引用(WeakReference) 代替 强引用 持有实例
3.2.2 单利模式导致的内存泄漏
public class SingleInstanceClass {
private static SingleInstanceClass instance;
private Context mContext;
private SingleInstanceClass(Context context) {
this.mContext = context.getApplicationContext(); // 传递的是Application 的context
}
public SingleInstanceClass getInstance(Context context) {
if (instance == null) {
instance = new SingleInstanceClass(context);
}
return instance;
}
}
解决方式;
当单利模式要访问资源文件的时候,这个时候要获取Context对象,此时传入Application的Context对象即可
3.2.3资源对象为能即使释放导致的内存泄漏
- BroadcastReceiver 注销
- 文件流File的close
- 数据库游标Cursor(cursor.close())
- 图片资源Bitmap未释放
- 动画未被取消(因为动画持有了view对象,而View又持有了Activity,最终Activity无法释放)
解决方案
在Activity销毁时 及时关闭 / 注销资源
3.2.4非静态内部类|匿名类
非静态内部类 / 匿名类 默认持有 外部类的引用;而静态内部类则不会
非静态内部类的实例 = 静态
若 非静态内部类所创建的实例 = 静态(其生命周期 = 应用的生命周期),会因 非静态内部类默认持有外部类的引用 而导致外部类无法释放,最终 造成内存泄露
即 外部类中 持有 非静态内部类的静态对象
a. 在启动频繁的Activity中,为了避免重复创建相同的数据资源,会在Activity内部创建一个非静态内部类的单例
b. 每次启动Activity时都会使用该单例的数据
public class TestActivity extends AppCompatActivity {
// 非静态内部类的实例的引用
// 注:设置为静态
public static InnerClass innerClass = null;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 保证非静态内部类的实例只有1个
if (innerClass == null)
innerClass = new InnerClass();
}
// 非静态内部类的定义
private class InnerClass {
//...
}
}
// 造成内存泄露的原因:
// a. 当TestActivity销毁时,因非静态内部类单例的引用(innerClass)的生命周期 = 应用App的生命周期、持有外部类TestActivity的引用
// b. 故 TestActivity无法被GC回收,从而导致内存泄漏
解决方案:
- 将非静态内部类设置为:静态内部类(静态内部类默认不持有外部类的引用)
- 该内部类抽取出来封装成一个单例
- 尽量 避免 非静态内部类所创建的实例 = 静态
多线程:AsyncTask、实现Runnable接口、继承Thread类
原因:
多线程的使用方法 = 非静态内部类 / 匿名类;即 线程类 属于 非静态内部类 / 匿名类
/**
* 方式1:新建Thread子类(内部类)
*/
public class MainActivity extends AppCompatActivity {
public static final String TAG = "carson:";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 通过创建的内部类 实现多线程
new MyThread().start();
}
// 自定义的Thread子类
private class MyThread extends Thread{
@Override
public void run() {
try {
Thread.sleep(5000);
Log.d(TAG, "执行了多线程");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 方式2:匿名Thread内部类
*/
public class MainActivity extends AppCompatActivity {
public static final String TAG = "carson:";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 通过匿名内部类 实现多线程
new Thread() {
@Override
public void run() {
try {
Thread.sleep(5000);
Log.d(TAG, "执行了多线程");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
}
/**
* 分析:内存泄露原因
*/
// 工作线程Thread类属于非静态内部类 / 匿名内部类,运行时默认持有外部类的引用
// 当工作线程运行时,若外部类MainActivity需销毁
// 由于此时工作线程类实例持有外部类的引用,将使得外部类无法被垃圾回收器(GC)回收,从而造成 内存泄露
产生内存泄漏的条件:
- 存在 ”工作线程实例 持有外部类引用“ 的引用关系
- 工作线程实例的生命周期 > 外部类的生命周期,即工作线程仍在运行 而 外部类需销毁
解决方案的思路 = 使得上述任1条件不成立 即可。
// 共有2个解决方案:静态内部类 & 当外部类结束生命周期时,强制结束线程
// 具体描述如下
/**
* 解决方式1:静态内部类
* 原理:静态内部类 不默认持有外部类的引用,从而使得 “工作线程实例 持有 外部类引用” 的引用关系 不复存在
* 具体实现:将Thread的子类设置成 静态内部类
*/
public class MainActivity extends AppCompatActivity {
public static final String TAG = "carson:";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 通过创建的内部类 实现多线程
new MyThread().start();
}
// 分析1:自定义Thread子类
// 设置为:静态内部类
private static class MyThread extends Thread{
@Override
public void run() {
try {
Thread.sleep(5000);
Log.d(TAG, "执行了多线程");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 解决方案2:当外部类结束生命周期时,强制结束线程
* 原理:使得 工作线程实例的生命周期 与 外部类的生命周期 同步
* 具体实现:当 外部类(此处以Activity为例) 结束生命周期时(此时系统会调用onDestroy()),强制结束线程(调用stop())
*/
@Override
protected void onDestroy() {
super.onDestroy();
Thread.stop();
// 外部类Activity生命周期结束时,强制结束线程
}
消息传递机制:Handler
非静态内部类 & 匿名内部类都默认持有 外部类的引用
解决方案:
- 解决方案1:静态内部类+弱引用
- 同时,还可加上 使用WeakReference弱引用持有Activity实例
- 原因:弱引用的对象拥有短暂的生命周期。在垃圾回收器线程扫描时,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存
public class MainActivity extends AppCompatActivity {
public static final String TAG = "carson:";
private Handler showhandler;
// 主线程创建时便自动创建Looper & 对应的MessageQueue
// 之后执行Loop()进入消息循环
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//1. 实例化自定义的Handler类对象->>分析1
//注:
// a. 此处并无指定Looper,故自动绑定当前线程(主线程)的Looper、MessageQueue;
// b. 定义时需传入持有的Activity实例(弱引用)
showhandler = new FHandler(this);
// 2. 启动子线程1
new Thread() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// a. 定义要发送的消息
Message msg = Message.obtain();
msg.what = 1;// 消息标识
msg.obj = "AA";// 消息存放
// b. 传入主线程的Handler & 向其MessageQueue发送消息
showhandler.sendMessage(msg);
}
}.start();
// 3. 启动子线程2
new Thread() {
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// a. 定义要发送的消息
Message msg = Message.obtain();
msg.what = 2;// 消息标识
msg.obj = "BB";// 消息存放
// b. 传入主线程的Handler & 向其MessageQueue发送消息
showhandler.sendMessage(msg);
}
}.start();
}
// 分析1:自定义Handler子类
// 设置为:静态内部类
private static class FHandler extends Handler{
// 定义 弱引用实例
private WeakReference<Activity> reference;
// 在构造方法中传入需持有的Activity实例
public FHandler(Activity activity) {
// 使用WeakReference弱引用持有Activity实例
reference = new WeakReference<Activity>(activity); }
// 通过复写handlerMessage() 从而确定更新UI的操作
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
Log.d(TAG, "收到线程1的消息");
break;
case 2:
Log.d(TAG, " 收到线程2的消息");
break;
}
}
}
}
解决方案2:当外部类结束生命周期时,清空Handler内消息队列
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
// 外部类Activity生命周期结束时,同时清空消息队列 & 结束Handler生命周期
}
集合类
原因:
集合类 添加元素后,仍引用着 集合元素对象,导致该集合元素对象不可被回收,从而 导致内存泄漏
// 通过 循环申请Object 对象 & 将申请的对象逐个放入到集合List
List<Object> objectList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
Object o = new Object();
objectList.add(o);
o = null;
}
// 虽释放了集合元素引用的本身:o=null)
// 但集合List 仍然引用该对象,故垃圾回收器GC 依然不可回收该对象
解决方案:
集合类 添加集合元素对象 后,在使用后必须从集合中删除
// 释放objectList
objectList.clear();
objectList=null;
4电量优化
在 Android5.0 以前,在应用中测试电量消耗比较麻烦,也不准确,5.0 之后专门引入了一个获取设备上电量消耗信息的 API:Battery Historian。Battery Historian 是一款由 Google 提供的 Android 系统电量分析工具,和Systrace 一样,是一款图形化数据分析工具,直观地展示出手机的电量消耗过程,通过输入电量分析文件,显示消耗情况,最后提供一些可供参考电量优化的方法。
除此之外,还有一些常用方案可提供
- 计算优化,避开浮点运算等。
- 避免 WaleLock 使用不当
- 使用 Job Scheduler。
5 网络优化
6 APK体积的优化
https://blog.csdn.net/huangxiaoguo1/article/details/80434456
https://www.jianshu.com/p/9745a9375191
网友评论