内存优化、UI优化(布局优化、会只优化)、速度优化(线程优化、网络优化)、启动优化、电量优化
内存优化
内存抖动:内存抖动是因为在短时间内大量的对象被创建又马上被释放
1、内存泄露
出自:https://www.jianshu.com/p/97fb764f2669
当一个对象已经不再被使用需要被GC回收时,另外一个正在使用的对象持有它的引用以至于它不能被GC回收,从而驻留在堆内存中。一般就是被生命周期长的对象引用。
常见的内存泄露情形:
- 集合
// 通过 循环申请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)
// 但集合仍然引用该对象,故垃圾回收器GC 依然不可回收该对象
解决办法:
在使用完毕时,及时清理
// 释放objectList
objectList.clear();
objectList=null;
- static
类的静态变量的声明周期是从这个类加载进内存开始到卸载出内存为止,而对于方法区类卸载的条件时很苛刻的:
a.该类所有的实例都已经被回收,也就是java堆中不存在该类的任何实例。
b.加载该类的ClassLoader已经被回收。
c.该类对应的java.lang.Class对象没有任何地方被引用,无法在任何地方通过反射访问该类的方法。
如果被static修饰的成员变量引用了耗费资源过多的实例,也会导致无法及时回收
例如下面的代码:
// 创建单例时,需传入一个Context
// 若传入的是Activity的Context,此时单例 则持有该Activity的引用
// 由于单例一直持有该Activity的引用(直到整个应用生命周期结束),即使该Activity退出,该Activity的内存也不会被回收
// 特别是一些庞大的Activity,此处非常容易导致OOM
public class SingleInstanceClass {
private static SingleInstanceClass instance;
private Context mContext;
private SingleInstanceClass(Context context) {
this.mContext = context; // 传递的是Activity的context
}
public SingleInstanceClass getInstance(Context context) {
if (instance == null) {
instance = new SingleInstanceClass(context);
}
return instance;
}
}
所以如果需引用 Context,则尽量使用Applicaiton的Context
- 非静态内部类、匿名内部类
非静态内部类 / 匿名类 默认持有 外部类的引用;而静态内部类则不会
下面的例子中定义了一个非静态内部类,并且定义了一个static的引用
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 {
//...
}
}
线程内部类,线程中做了太多耗时操作,导致MainActivity无法被及时回收
/**
* 方式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();
}
}
解决办法:
/**
* 解决方式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();
}
}
}
}
- 及时释放使用后的资源
对于资源的使用(如 广播BraodcastReceiver、文件流File、数据库游标Cursor、图片资源Bitmap等),若在Activity销毁时无及时关闭 / 注销这些资源,则这些资源将不会被回收,从而造成内存泄漏
// 对于 广播BraodcastReceiver:注销注册
unregisterReceiver()
// 对于 文件流File:关闭流
InputStream / OutputStream.close()
// 对于数据库游标cursor:使用后关闭游标
cursor.close()
// 对于 图片资源Bitmap:Android分配给图片的内存只有8M,若1个Bitmap对象占内存较多,当它不再被使用时,应调用recycle()回收此对象的像素所占用的内存;最后再赋为null
Bitmap.recycle();
Bitmap = null;
- Handler
注意Handler的正确使用
// 分析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;
}
}
}
网友评论