当咱们开发安卓运用时,功能优化是非常重要的一个方面。一方面,优化能够进步运用的响应速度、下降卡顿率,然后进步用户体验;另一方面,优化也能够减少运用的资源占用,进步运用的稳定性和安全性,下降运用被杀死的概率,然后进步用户的满意度和留存率。

但是,关于许多开发者来说,安卓功能优化往往是一个比较扎手的问题。由于安卓设备的品种繁复,硬件装备各不相同,因而优化的办法和战略也各不相同。一起,安卓运用的开发周期较长,往往需求不断地迭代和更新,因而优化也需求不断地继续和优化。

因而,学习安卓功能优化的知识和技巧,是每个安卓开发者必备的技能之一。经过把握安卓功能优化的根本原理和办法,咱们能够愈加深入地了解安卓设备的作业机制,理解运用的功能瓶颈,然后采纳有用的优化战略和办法,进步运用的功能和稳定性,进步用户的满意度和留存率。

因而,本系列博客旨在介绍安卓功能优化的根本原理、优化战略和实践技巧,协助开发者更好地了解安卓设备的作业原理,把握安卓功能优化的根本办法和技巧,然后进步运用的功能和稳定性,为用户提供愈加优质的运用体验

安卓开发的功能优化问题非常广泛,以下是其间一些常见的问题:

  1. 内存走漏:当运用程序不正确地办理内存时,会发生内存走漏,导致内存占用过高,甚至导致运用程序崩溃。
  2. 布局优化:布局是运用程序中最常见的功能瓶颈之一,由于过于复杂的布局会导致运用程序响应缓慢或卡顿。
  3. 图片优化:图片是运用程序中占用内存最多的资源之一,因而有必要慎重运用,并对其进行恰当的压缩和缓存,以保证运用程序的功能。
  4. 网络恳求优化:网络恳求能够在运用程序中占用很多的时间和资源,因而有必要对其进行优化,以减少恳求次数和进步响应速度。
  5. 数据库优化:当运用程序需求很多访问数据库时,可能会导致功能问题。经过优化数据库规划和运用恰当的数据库缓存,能够进步运用程序的功能。
  6. 多线程优化:多线程能够进步运用程序的功能,但假如不正确地运用它们,则可能导致死锁、线程竞争和其他问题。
  7. 内存优化:内存是运用程序功能的重要因素之一。经过及时开释不再需求的内存和防止不必要的内存分配,能够进步运用程序的功能。
  8. 代码优化:优化代码结构和算法能够进步运用程序的功能。例如,运用更快速和有用的数据结构和算法来进步运用程序的响应速度。
  9. 安全性优化:安全问题也可能对运用程序的功能发生负面影响。经过防止不安全的代码实践和运用加密技术来维护数据,能够进步运用程序的安全性和功能。

我在功能优化 中记录过几篇文章,也不多,都是从遇到问题的视点出发写的,今日就从上述的惯例优化开端,一个一个剖析记录一下。

为什么说是惯例优化呢?由于Android的功能优化归结到底便是内存问题,而内存层次的优化,不仅是描述中的这些惯例优化项,还能够进行磁盘读写次数、磁盘页数据同步等进一步的优化,而这些优化在这里是不评论的。

内存走漏

内存走漏是指运用程序在运转过程中,无法正确地开释现已不再运用的内存资源,导致内存占用不断添加,终究导致运用程序崩溃或运转缓慢。

内存走漏的原理

安卓内存走漏的原理是指运用程序在运用内存时,由于程序规划问题或许过错,导致无法开释不再运用的内存,终究导致体系中的内存不足,影响体系的稳定性和功能。

以下是一些可能导致安卓内存走漏的常见原因:

  1. 目标引证未开释:当目标被创立时,假如没有被正确开释,那么这些目标就会一向占用内存,直到运用程序退出。例如,当一个Activity被毁掉时,假如它还持有其他目标的引证,那么这些目标就无法被废物收回器收回,然后导致内存走漏。

假如存在内存走漏,那么这些内存中的目标就会被引证,无法被废物收回机制收回,这时咱们需求经过GCRoot来识别内存走漏的目标和引证。

GCRoot是废物收回机制中的根节点,根节点包含虚拟机栈、本地办法栈、办法区中的类静态属性引证、活动线程等,这些目标被废物收回机制视为“活着的目标”,不会被收回。

当废物收回机制执行时,它会从GCRoot出发,遍历一切的目标引证,并符号一切活着的目标,未被符号的目标即为废物目标,将会被收回。

当存在内存走漏时,废物收回机制无法收回一些现已不再运用的目标,这些目标依然被引证,形成了一些GCRoot到内存走漏目标的引证链,这些目标将无法被收回,导致内存走漏。

经过查找内存走漏目标和GCRoot之间的引证链,能够定位到内存走漏的本源,然后处理内存走漏问题,LeakCancry便是经过这个机制实现的。
一些常见的GCRoot包含:

  • 虚拟机栈(Local Variable)中引证的目标。
  • 办法区中静态属性(Static Variable)引证的目标。
  • JNI 引证的目标。
  • Java 线程(Thread)引证的目标。
  • Java 中的 synchronized 锁持有的目标。
  1. 匿名内部类形成的内存走漏:匿名内部类通常会持有外部类的引证,假如外部类的生命周期比匿名内部类长,(更正一下,这里用生命周期不太恰当,当外部类被毁掉时,内部类并不会主动毁掉,由于内部类并不是外部类的成员变量,它们只是在外部类的效果域内创立的目标,所以内部类的毁掉机遇和外部类的毁掉机遇是不同的,所以会不会取决与对应目标是否存在被持有的引证)那么就会导致外部类无法被收回,然后导致内存走漏。
  2. 静态变量持有Activity或Context的引证:假如一个静态变量持有Activity或Context的引证,那么这些Activity或Context就无法被废物收回器收回,然后导致内存走漏。
  3. 未封闭的Cursor、Stream或许Bitmap目标:假如程序在运用Cursor、Stream或许Bitmap目标时没有正确封闭这些目标,那么这些目标就会一向占用内存,然后导致内存走漏。
  4. 资源未开释:假如程序在运用体系资源时没有正确开释这些资源,例如未封闭数据库衔接、未开释音频资源等,那么这些资源就会一向占用内存,然后导致内存走漏。

常见的内存走漏

静态引证导致的内存走漏

当一个目标被一个静态变量持有时,即使这个目标现已不再运用,也不会被废物收回器收回,这就会导致内存走漏

public class MySingleton {
    private static MySingleton instance;
    private Context context;
    private MySingleton(Context context) {
        this.context = context;
    }
    public static MySingleton getInstance(Context context) {
        if (instance == null) {
            instance = new MySingleton(context);
        }
        return instance;
    }
}

上面的代码中,MySingleton持有了一个Context目标的引证,而MySingleton是一个静态变量,导致即使这个目标现已不再运用,也不会被废物收回器收回。

注意事项:假如需求运用静态变量,请注意在不需求时将其设置为null,以便及时开释内存。

匿名内部类导致的内存走漏

匿名内部类会隐式地持有外部类的引证,假如这个匿名内部类被持有了,就会导致外部类无法被废物收回。

public class MyActivity extends Activity {
    private Button button;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        button = new Button(this);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // do something
            }
        });
        setContentView(button);
    }
}

匿名内部类OnClickListener持有了外部类MyActivity的引证,假如MyActivity被毁掉之前,button没有被铲除,就会导致MyActivity无法被废物收回。(此处能够将Button 看作是自己界说的一个目标,一般解法是将button目标置为空)

注意事项:在Activity毁掉时,应该将一切持有Activity引证的目标设置为null。

Handler引起的内存走漏

Handler是在Android运用程序中常用的一种线程通讯机制,假如Handler被过错地运用,就会导致内存走漏。

public class MyActivity extends Activity {
    private static final int MSG_WHAT = 1;
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_WHAT:
                    // do something
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mHandler.sendEmptyMessageDelayed(MSG_WHAT, 1000 * 60 * 5);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 在Activity毁掉时,应该将Handler的音讯行列清空,以防止内存走漏。
        mHandler.removeCallbacksAndMessages(null);
        }
}

Handler持有了Activity的引证,假如Activity被毁掉之前,Handler的音讯行列中还有未处理的音讯,就会导致Activity无法被废物收回。

注意事项:在Activity毁掉时,应该将Handler的音讯行列清空,以防止内存走漏。

Bitmap目标导致的内存走漏

当一个Bitmap目标被创立时,它会占用很多内存,假如不及时开释,就会导致内存走漏。

public class MyActivity extends Activity {
private Bitmap mBitmap;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    // 加载一张大图
    mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.big_image);
}
@Override
protected void onDestroy() {
    super.onDestroy();
    // 开释Bitmap目标
    mBitmap.recycle();
    mBitmap = null;
}
}

当Activity被毁掉时,Bitmap目标mBitmap应该被及时开释,不然就会导致内存走漏。

注意事项:当运用很多Bitmap目标时,应该及时收回不再运用的目标,防止内存走漏。别的,能够考虑运用图片加载库来办理Bitmap目标,例如Glide、Picasso等。

资源未封闭导致的内存走漏

当运用一些体系资源时,例如文件、数据库等,假如不及时封闭,就可能导致内存走漏。例如:

public void readFile(String filePath) throws IOException {
    FileInputStream fis = null;
    try {
        fis = new FileInputStream(filePath);
        // 读取文件...
    } finally {
        if (fis != null) {
            try {
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

上面的代码中,假如在读取文件之后没有及时封闭FileInputStream目标,就可能导致内存走漏。

注意事项:在运用一些体系资源时,例如文件、数据库等,要及时封闭相关目标,防止内存走漏。

防止内存走漏需求在编写代码时时刻注意,及时整理不再运用的目标,保证内存资源得到及时开释。
,一起,能够运用一些东西来检测内存走漏问题,例如Android Profiler、LeakCanary等。

WebView 内存走漏

当运用WebView时,假如不及时开释,就可能导致内存走漏

public class MyActivity extends Activity {
    private WebView mWebView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mWebView = findViewById(R.id.webview);
        mWebView.loadUrl("https://www.example.com");
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 开释WebView目标
        if (mWebView != null) {
            mWebView.stopLoading();
            mWebView.clearHistory();
            mWebView.clearCache(true);
            mWebView.loadUrl("about:blank");
            mWebView.onPause();
            mWebView.removeAllViews();
            mWebView.destroy();
            mWebView = null;
        }
    }
}

上面的代码中,当Activity毁掉时,WebView目标应该被及时开释,不然就可能导致内存走漏。

注意事项:在运用WebView时,要及时开释WebView目标,能够在Activity毁掉时调用WebView的destroy办法,一起也要铲除WebView的历史记录、缓存等内容,以保证开释一切资源。

监测东西

  1. 内存监督东西:Android Studio提供了内存监督东西,能够在开发过程中实时监督运用程序的内存运用情况,协助开发者及时发现内存走漏问题。
  2. DDMS:Android SDK中的DDMS东西能够监督Android设备或模拟器的进程和线程,包含内存运用情况、仓库跟踪等信息,能够用来诊断内存走漏问题。
  3. MAT:MAT(Memory Analyzer Tool)是一款基于Eclipse的内存剖析东西,能够剖析运用程序的堆内存运用情况,识别和定位内存走漏问题。
  4. 腾讯的Matrix,也是非常好的一个开源项目,推荐咱们运用

总结

内存走漏是指程序中的某些目标或资源没有被妥善地开释,然后导致内存占用不断添加,终究可能导致运用程序崩溃或体系运转缓慢等问题。

常见的内存走漏问题包含:

  1. 长时间持有Activity或Fragment目标导致的内存走漏;
  2. 匿名内部类和非静态内部类导致的内存走漏;
  3. WebView持有Activity目标导致的内存走漏;
  4. 单例形式持有资源目标导致的内存走漏;
  5. 资源未封闭导致的内存走漏;
  6. 静态变量持有Context目标导致的内存走漏;
  7. Handler持有外部类引证导致的内存走漏;
  8. Bitmap占用很多内存导致的内存走漏;
  9. 单例持有很多数据导致的内存走漏。

为防止内存走漏问题,咱们能够采纳以下办法:

  1. 及时开释Activity或Fragment目标;
  2. 防止匿名内部类和非静态内部类;
  3. 在运用WebView时,及时调用destroy办法;
  4. 在单例形式中防止长时间持有资源目标;
  5. 及时封闭资源目标;
  6. 防止静态变量持有Context目标;
  7. 防止Handler持有外部类引证;
  8. 在运用Bitmap时,及时开释内存;
  9. 防止单例持有很多数据。

欢迎弥补评论