Android优化

内存泄露

内存泄露是Android开发过程中最常见的问题之一。Java是垃圾回收语言之一,使用者无须手动释放内存,交由垃圾回收器自动管理即可。但是如果使用不当,很容易造成该释放掉的内存未能及时释放,从而越积越多,最终导致内存耗尽,系统抛出OOM错误。
内存泄露的原因简单来说就是存放在堆中的对象仍存在强引用,GC无法在内存中回收这个对象。形象来说就是生命周期长的对象持有生命周期短的对象的引用。
Android中常见的场景是Activity回收。因为activity是有生命周期的,当其关闭后需要交由GC回收。但是如果此时仍有指向Activity的强引用存在,那么GC是不会回收掉这个Activity的,从而导致该Activity一直占用着系统内存。

Static静态变量

静态变量的生存周期是存在于整个App的生命周期中,所以如果有静态变量持有Activity的强引用,那么这个Activity是回收不了的。

package com.brett.aptproject;

import android.content.Context;

/**
 * @author dell
 */
public class Singleton {
    private static Singleton sSingleton = null;
    private Context mContext;
    
    public Singleton(Context mContext){
        this.mContext = mContext;
    }
    
    public static Singleton getInstance(Context context){
        if(null== sSingleton){
            sSingleton = new Singleton(context);
        }
        return sSingleton;
    }
}

如果Singleton传入的Context是Activity,那么就会导致内存泄露。因为sSingleton是一个静态变量,持有Singleton实例对象的引用,而mContext是Singleton的成员变量,在Singleton实例化的时候指向了传入的Activity。由于Singleton的实例对象被sSingleton静态变量强引用,所以会在内存中一直存在,因此其成员变量mContext同样不会被回收;而mContext又对Activity有强引用,导致Activity不能被回收,这样就发生了内存泄露。
接下来我们就来看下如何解决内存泄漏的问题。
内存泄漏的原因是生命周期长的对象持有生命周期短的对象的引用,那么我们可以考虑将两个对象的生命周期设置成一样长,例如传入getApplicationContext(),它的生命周期就是App的生命周期,跟静态变量一样,所以就不会造成内存泄漏。
关于内存泄漏的检测,还可以通过Profile来发现泄漏问题。
例如,我们创建一个泄漏的Activity:

package com.brett.aptproject.activity;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

import com.alibaba.android.arouter.facade.annotation.Route;
import com.brett.aptproject.R;
import com.brett.aptproject.Singleton;

/**
 * @author dell
 */
@Route(path = "/activity/ProfileActivity",group = "app")
public class ProfileActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_profile);
        Singleton.getInstance(this);
    }
}

然后在MainActivity中打开ProfileActivity,在关闭ProfileActivity,接下来单击 Force garbage collection按钮,强制垃圾回收,理论上ProfileActivity应该被回收,但是从profile文件中可以看到还有ProfileActivity的引用。
在这里插入图片描述

InnerClass内部类

非静态内部类,包括匿名内部类、成员内部类、局部内部类,都会导致内存泄漏。因为内部类会默认持有外部类的引用,不然内部类怎么能够直接访问到外部类的数据呢?
内部类默认持有外部类的引用,意思是创建内部类对象的时候,必须传入外部类对象,作为内部类中outerClass成员变量的引用。

public class InnerClass{
    private OuterClass outerClass;
    
    public InnerClass(OuterClass outerClass){
        this.outerClass = outerClass;
    }
}

那这样问题就来了,如果外部类是一个Activity,当这个Activity需要被回收的时候,此时内部类仍然还在工作,仍然持有Activity的引用,那么这个Activity就不能被及时回收,从而导致内存泄漏。
以匿名内部类为例,匿名内部类没有名字,所以每次只能使用一次,而且匿名内部类必须实现一个接口或者继承父类。

1.new一个抽象类
abstract class Car{
    public abstract void move();
}

public class Demo{
    public static void main(String[] args) {
        Car c = new Car() {
            @Override
            public void move() {
                System.out.println("I moved");
            }
        };
        c.move();
    }
}
2.new一个接口
interface Car{
    void move();
}

public class Demo{
    public static void main(String[] args) {
        Car c = new Car() {
            @Override
            public void move() {
                System.out.println("I moved");
            }
        };
        c.move();
    }
}

这里并不是实例化一个抽象类或接口,而是实现了一个匿名类。
另外,常见的Handler也容易造成内存泄漏:

package com.brett.aptproject.activity;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;

import com.alibaba.android.arouter.facade.annotation.Route;
import com.brett.aptproject.R;
import com.brett.aptproject.Singleton;

/**
 * @author dell
 */
@Route(path = "/activity/ProfileActivity",group = "app")
public class ProfileActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_profile);
//        Singleton.getInstance(this);
        mHandler.sendEmptyMessageDelayed(1,1*60*1000);
    }
    
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };
}

可以看到,mHandler作为成员变量指向匿名内部类Handler,Handler默认持有对Activity的引用,如果在1分钟内Activity有退出,那么由于匿名内部类Handler对外部Activity有强引用,所以外部Activity并不能被回收。
因此我们可以采用静态内部类的方法来实现,考虑到Handler一般是用来更新UI的,所以肯定会对UI进行操作,如果我们采用了静态非匿名内部类的方法,那么Handler就不能直接操作外部的Activity,因此需要在初始化Handler的时候将外部Activity传入,但是这样以来又造成了Handler对Activity持有强引用的问题。因此这种情况下,可以考虑采用弱引用的方式,在系统进行垃圾回收的时候,自动解除引用关系,从而能够顺利回收掉对象。

    private static class MyHandler extends Handler{
        WeakReference<Activity>weakReference;
        public MyHandler(Activity activity){
            weakReference = new WeakReference<>(activity);
        }
        
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if(weakReference.get()!=null){
                //...
            }
        }
    }

另外,在onDestory的时候还需要进行remove操作

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(mHandler!=null){
            mHandler.removeCallbacksAndMessages(null);
            mHandler = null;
        }
    }

内存泄漏也有工具可以检测。LeakCanary是Square公司基于MAT开源的一个工具,专门用来检测App的内存泄漏问题。具体用法这里就不涉及了。

编译速度

配置文件优化

在gradle.properties文件中进行参数配置,具体如下:

#jvm运行时允许分配最大堆内存空间
org.gradle.jvmargs=-Xmx4608m
#开启并行编译
org.gradle.parallel=true
#开启守护进程 通过开启守护进程,下一次构建的时候,将会连接这个守护进程进行构建,而不是重新fork一个gradle构建进程
org.gradle.daemon=true
#启用新的孵化模式
org.gradle.configureondemand=true

Gradle脚本优化

直接在命令行窗口输入:.\gradlew build -profile,完成后可以看到一份html文件,里面可以查看到gradle构建的各个task运行的时间。通过查看生成的html可以屏蔽掉一些task。

//在项目中屏蔽掉lint任务
allprojects {
    gradle.taskGraph.whenReady {
        tasks.each { task ->
            if (task.name.contains("lint")) {
                task.enabled = false
            }
        }
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值