Android代码优化十九条

1、避免使用静态的变量尤其是静态的Context、View、Drawable等消耗内存的对象,如果一定要使用可以使用弱引用,即WeakReference这个类,如下:

    private static WeakReference<Context> context;

    @Override

    public void onCreate() {

        super.onCreate();

        context = new WeakReference<Context>(this);

    }

 

    public static Context getContext() {

        return context.get();

    }

2、避免非静态内部类引用外部类,因为静态内部类会引用外部类的对象或View对象,造成内存泄露,最典型的是handler使用,如下:

private Handler handler = new Handler() {

    @Override

    public void handleMessage(Message msg) {

        Toast.makeText(Act.this, "I am handler", Toast.LENGTH_SHORT).show();

    }

};

private TextView textView;

private Handler handler = new Handler() {

    @Override

    public void handleMessage(Message msg) {

        textView.setText("text from handler");

    }

};

替代为:

private Handler handler = new MyHandler(this);

private static class MyHandler extends Handler {

    private WeakReference<Act> activity;

    public MyHandler(Act activity) {

        this.activity = new WeakReference<>(activity);

    }

    @Override

    public void handleMessage(Message msg) {

        Toast.makeText(activity.get(), "show toast from handler", Toast.LENGTH_SHORT).show();

    }

}

3、避免使用枚举,在Android中官方已经不推荐使用枚举,可以使用android.support包提供的annotation编译期注解配合常量来替代,可以参考系统的Toast类源码如下:

    public static final int LENGTH_SHORT = 0;

    public static final int LENGTH_LONG = 1;

 

    @IntDef({LENGTH_SHORT, LENGTH_LONG})

    @Retention(RetentionPolicy.SOURCE)

    public @interface Duration {

 

    }

4、在大部分情况下,使用增强的foreach替代for循环,如下:

  int[] num = new int[]{1, 3, 5, 6, 9};

  int sum = 0;

  for (int j = 0; j < num.length; j++) {

    sum += num[j];

  }

可以替代为:

 int[] num = new int[]{1, 3, 5, 6, 9};

 int sum = 0;

 for (int j : num) {

     sum += j;

 }

5、避免在循环语句内部反复创建和销毁对象,避免内存抖动,影响性能,如下:

 int[] numArray1 = new int[]{1, 3, 5, 6, 9};

 int[] numArray2 = new int[]{1, 3, 5, 6, 9};

 int sum = 0;

 for (int j = 0; j < numArray1.length; j++) {

     int num1 = numArray1[j];

     int num2 = numArray2[j];

     sum += num1 + num2;

 }

可以替代为:

int[] numArray1 = new int[]{1, 3, 5, 6, 9};

int[] numArray2 = new int[]{1, 3, 5, 6, 9};

int sum = 0;

int num1;

int num2;for (int j = 0; j < numArray1.length; j++) {

    num1 = numArray1[j];

    num2 = numArray2[j];

    sum += num1 + num2;

}

6、使用更加高效的数据结构,在Java中如Map、HashMap等,在Android中有专门的设计类都是以Sparsexx开头,如下:

 Map<Integer, String> map1 = new HashMap<>();

 Map<Integer, File> map2 = new HashMap<>();

可以替代为:

SparseArray<String> s1 = new SparseArray<>();

SparseArray<File> s2 = new SparseArray<>();

SparseArray内部定义了一个int类型的数组用于存储key,所以如果map的key是int类型,就可以使用SparseArray替代,下面是类定义:

当然除了这个类还有其他的类,如:SparseBooleanArray、SparseIntArray、SparseLongArray,这三个类内部都定义了int类型的数组来存储key。

7、字符串大量拼接时,使用StringBuilder替代+,且StringBuilder效率比StringBuffer高,如下:

String str = "我";

List<String> names = new ArrayList<>();

names.add("小明");

names.add("小王");

names.add("小刘");

names.add("小赵");for (String s : names) {

    str += s;

}

可以替代为:

StringBuilder str = new StringBuilder("我");

List<String> names = new ArrayList<>();

names.add("小明");

names.add("小王");

names.add("小刘");

names.add("小赵");for (String s : names) {

    str.append(s);

}

注意:不要在使用了StringBuilder中又同时使用字符串+,如这样:str.append(s+"\n"),可以这样:str.append(s).append("\n")。

8、避免在自定义View的onDraw方法中重复申请和释放内存,如下:

@Override

protected void onDraw(Canvas canvas) {

    super.onDraw(canvas);

    Paint paint = new Paint();

    paint.setColor(Color.RED);

    paint.setTypeface(Typeface.DEFAULT_BOLD);

    paint.setAntiAlias(true);

    paint.setTextSize(28);

}

改为在onDraw方法之前申明:

Paint paint = new Paint();

@Override

protected void onDraw(Canvas canvas) {

    super.onDraw(canvas);

    paint.setColor(Color.RED);

    paint.setTypeface(Typeface.DEFAULT_BOLD);

    paint.setAntiAlias(true);

    paint.setTextSize(28);

}

9、避免使用系统已经过时的方法,如果要使用要对系统API版本进行判断处理,如下:

getResources().getColor(R.color.color_0d0d0d);

getColor带一个参数的这个方法已经标注为过时,替代为:

if (Build.VERSION.SDK_INT >= 23) {

    getResources().getColor(R.color.color_0d0d0d, getTheme());

} else {

    getResources().getColor(R.color.color_0d0d0d);

}

推荐写法,使用ContextCompat这个向后兼容的帮助类:

ContextCompat.getColor(this,R.color.color_0d0d0d);

这样就不用我们自己做api版本判断了,其实它内部的逻辑跟第一种写法是一样的,下面是它的源码:

public static final int getColor(Context context, @ColorRes int id) {

    final int version = Build.VERSION.SDK_INT;

    if (version >= 23) {

        return ContextCompatApi23.getColor(context, id);

    } else {

        return context.getResources().getColor(id);

    }

}

ContextCompatApi23.getColor方法内部又调用了context的getColor方法:

public static int getColor(Context context, int id) {

    return context.getColor(id);

}

最终还是调用Resources的getColor(@ColorRes int id, @Nullable Theme theme)方法:

public final int getColor(@ColorRes int id) {

    return getResources().getColor(id, getTheme());

}

10、使用泛型的时候如果明确知道类型最好限制泛型类型,如下:

public <VIEW> VIEW findViewById(int resId) {

    return (VIEW) contentView.findViewById(resId);

}

替代为:

public <VIEW extends View> VIEW findViewById(int resId) {

    return (VIEW) contentView.findViewById(resId);

}

11、使用android.support.annotation包改进代码,增强代码健壮性,如下:

import android.support.annotation.IdRes;

public View findViewById(@IdRes int resId) {

    return contentView.findViewById(resId);

}

@IdRes是一个编译期注解,代码会在编译阶段进行检查,这样做的好处是外部要调用findViewById方法时不可以随便传入数值,必须是R.id.xx中的某一个,当然传入0和-1也是没问题,一般会用来做默认判断处理,如:

public View findViewById(@IdRes int resId) {

    if (resId <= 0) {

        throw new IllegalArgumentException("传入的参数不对,只能是资源ID.");

    }

    return contentView.findViewById(resId);

}

资源型注解:

  • @LayoutRes:限制类型为R.layout中的资源
  • @StringRes:限制类型为R.string中的资源
  • @ColorRes:限制类型为R.color中的资源
  • @StyleRes:限制类型为R.style中的资源
  • @DrawableRes:限制类型为R.drawable或R.mipmap中的资源
  • @MenuRes:限制类型为R.menu中的资源
  • @DimenRes:限制类型为R.dimen中的资源
  • @AnimRes:限制类型为R.anim中的资源

数值型注解:

  • @IntDef:定义一组取值常量,限制取值范围,可以用来替代枚举
  • @IntRange:限制int取值范围,用法:

void add(@IntRange(from = 0, to = 20) int num) {

 

}

add方法只能传入0-20之间的整数。

  1. 使用ListView时Adapter一定要复用convertView并使用ViewHolder
  2. 使用RecyclerView替代ListView,Android 5.0推出了RecyclerView,RecyclerView更加强大、灵活、可扩展性强
  3. 如果成员变量的值是固定不变的,改用final static修饰,因为常量会经过编译器优化

15、使用Timer或TimerTask时要记得取消,在Activity的onDestroy或Fragment的onDetach方法中调用cancel方法取消。

16、使用TypedArray完毕后及时调用typedArray.recycle()方法释放资源。

17、文件或流操作时要在finally语句块中关闭而不要在catch语句块中关闭,因为如果发生异常程序就不会往下执行,如下:

FileInputStream fis;

try {

    fis = new FileInputStream(Environment.getExternalStorageDirectory() + "aa.txt");

    fis.close();

} catch (FileNotFoundException e) {

    e.printStackTrace();

} catch (IOException e) {

    e.printStackTrace();

}

改为:

FileInputStream fis = null;

try {

    fis = new FileInputStream(Environment.getExternalStorageDirectory() + "aa.txt");

} catch (FileNotFoundException e) {

    e.printStackTrace();

} finally {

    if (fis != null) {

        try {

            fis.close();

        } catch (IOException e) {

            e.printStackTrace();

        }

    }

}

18、ArrayList清空数据使用clear()方法,不要用list.removeAll(list),我见过有人这样写!!!会引起内存溢出!

19、集合添加所有数据到另外一个集合中,用addAll而不要用for循环

addAll方法是Collection接口提供的,具体实现是在ArrayList中:

public boolean addAll(Collection<? extends E> c) {

    Object[] a = c.toArray();

    int numNew = a.length;

    ensureCapacityInternal(size + numNew);  // Increments modCount

    System.arraycopy(a, 0, elementData, size, numNew);

    size += numNew;

    return numNew != 0;

}

数组也可以,用Arrays这个工具类将数组转成List:

String[] arrays = new String[]{"C", "C++", "Java", "Kotlin"};

ArrayList<String> list2 = new ArrayList<>();

list2.addAll(Arrays.asList(arrays));

addAll方法核心用到的是数组拷贝。System.arraycopy是native方法,具体由C实现。

链接:https://juejin.im/post/5a24ae816fb9a044fb07960e
布局相关优化:https://juejin.im/post/5a24e9bb6fb9a04527256c14

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值