美文网首页
Android lint 代码优化

Android lint 代码优化

作者: ae12 | 来源:发表于2021-09-28 17:10 被阅读0次

1.string concatenation in loop
这个老生常谈,一个"+"号就会new 一个StringBuilder实例,但是平时code时总会发现项目里有人一个for()循环里开始用“+”去拼接字符串。。。”
用StringBuilder.append()代替;


1631948445(1).png

2.new Integer ()to String can be simplified to Integer.toString
stackoverflow上解释了关于两个的区别和什么时候用哪个写的很详细,高赞截图如下:

Java int to String - Integer.toString(i) vs new Integer(i).toString()

企业微信截图_16318688311604.png

3.suspicious list.remove in the loop
Reports when list.remove(index) is called inside the ascending counted loop. This is suspicious as list becomes shorter after that and the element next to removed will not be processed. Simple fix is to decrease the index variable after removal, but probably removing via iterator or using removeIf method (since Java 8) is a more robust alternative. If you don't expect that remove will be called more than once in a loop, consider adding a break command after it.
list.remove()删除完后,内部数据会补上空缺,从而导致index下获取的值不再是原来的值
建议由

     for (int i = 0; i < list.size(); i++) {
            if (evidenceId == list.get(i).getId()) {
                evidenceRvAdapter.getData().remove(i);
            }
        }

优化为如下:

  for(Iterator<EvidenceListModel.Evidence> iterator =list.iterator();iterator.hasNext();){
            EvidenceListModel.Evidence evidence = iterator.next();
            if (evidenceId ==evidence.getId()) {
                iterator.remove();
            }
        }
  1. Handler reference leaks
    Android 中 Handler 导致的内存泄漏
    上面代码很常见,但是却是能引起内存泄露的,我们用Android Lint工具检查一下代码,会发现如下警告:
Handler reference leaks
  MainActivity.java
    This Handler class should be static or leaks might occur

然后具体解释如下:

Since this Handler is declared as an inner class, it may prevent the outer class from 
being garbage collected. 
If the Handler is using a Looper or MessageQueue for a thread other than the main thread, 
then there is no issue. 
If the Handler is using the Looper or MessageQueue of the main thread, 
you need to fix your Handler declaration, as follows: 
Declare the Handler as a static class; In the outer class, instantiate a WeakReference 
to the outer class and pass this object to your Handler when you instantiate the Handler; 
Make all references to members of the outer class using the WeakReference object

问题分析

Android 方面
当Android应用启动的时候,系统会自动创建一个供主线程使用的Looper实例。

/// file: frameworks/base/core/java/android/app/ActivityThread.java

public static void main(String[] args) {

    // 主线程中调用Looper.prepareMainLooper()方法创建Looper
    Looper.prepareMainLooper();

frameworks/base/core/java/android/app/ActivityThread.java

public static void main(String[] args) {
 ...
 // 主线程中调用Looper.prepareMainLooper()方法创建Looper
 Looper.prepareMainLooper();
 ...

Looper的主要工作就是一个一个处理消息队列(MessageQueue, Looper构造方法中创建)中的消息对象。在Android中,所有Android框架的事件(比如Activity的生命周期方法调用和按钮点击等)都是放入到消息中,然后加入到Looper要处理的消息队列中,由Looper负责一条一条地进行处理。

我们在主线程中实例化一个Handler时,如果没有指定其它的Looper,那么它就会自动使用主线程的Looper,如下图所示log:

image

所以我们发送一条消息到此Handler时,实际上消息是进入了主线程的消息处理队列,而此消息已经包含了一个Handler实例的引用:

frameworks/base/core/java/android/os/Handler.java

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
 // 消息获得当前Handler的引用
 msg.target = this;
 if (mAsynchronous) {
 msg.setAsynchronous(true);
 }
 return queue.enqueueMessage(msg, uptimeMillis);
}

当Looper来处理消息时,会据此引用来回调[Handler#handleMessage(Message)]:

public static void loop() {
 final Looper me = myLooper();

 final MessageQueue queue = me.mQueue;
 ...
 for (;;) {
 Message msg = queue.next(); // might block
 if (msg == null) {
 // No message indicates that the message queue is quitting.
 return;
 }
 ...
 try {
 // 取到消息之后,交给发送该消息的Handler来处理消息
 msg.target.dispatchMessage(msg);
 } finally {
 ...
 }
 ...
 msg.recycleUnchecked();
 }
}

2. Java方面

Java中的非静态内部类以及匿名内部类会持有外部类的引用。静态内部类不会持有外部类的引用。

3. 内存泄漏所在

结合Android和Java方面的分析,我们应该很容易就知道了为什么会产生内存泄漏:

  1. 发消息到Handler,消息进入到主线程的消息队列后持有对Handler的引用
  2. 做为非静态内部类,Handler又持有外部类(在这里是MainActivity)的引用
  3. 只要消息没被处理,那么MainActivity对象就无法被垃圾回收器回收,进而导致MainActivity持有的很多资源都无法回收,这就是我们常说的内存泄漏。

泄漏解决方案

  1. 避免使用非静态内部类,前面有提到过静态内部类不会持有外部类的引用;
  2. 将Handler放在单独的文件中,而不是以内部类方式存在;
  3. 当需要在静态内部类中调用外部的Activity时,我们可以使用弱引用来处理:
public class MainActivity extends AppCompatActivity {
     // 静态内部类,不会持有外部类的引用
     private static class MyHandler extends Handler {
     // 外部类的弱引用
     private WeakReference<MainActivity> mActivity;

     public MyHandler(MainActivity activity) {
     mActivity = new WeakReference<MainActivity>(activity);
     }

     @Override
     public void handleMessage(Message msg) {
     MainActivity activity = mActivity.get();
     // 确保外部类没有被回收
     if (activity != null) {
     // Do something ...
     }
     }
     }
    }

相关文章

网友评论

      本文标题:Android lint 代码优化

      本文链接:https://www.haomeiwen.com/subject/ndcsgltx.html