当咱们开发安卓运用时,功能优化是非常重要的一个方面。一方面,优化能够进步运用的响应速度、下降卡顿率,然后进步用户体验;另一方面,优化也能够减少运用的资源占用,进步运用的稳定性和安全性,下降运用被杀死的概率,然后进步用户的满意度和留存率。
但是,关于许多开发者来说,安卓功能优化往往是一个比较扎手的问题。由于安卓设备的品种繁复,硬件装备各不相同,因而优化的办法和战略也各不相同。一起,安卓运用的开发周期较长,往往需求不断地迭代和更新,因而优化也需求不断地继续和优化。
因而,学习安卓功能优化的知识和技巧,是每个安卓开发者必备的技能之一。经过把握安卓功能优化的根本原理和办法,咱们能够愈加深入地了解安卓设备的作业机制,理解运用的功能瓶颈,然后采纳有用的优化战略和办法,进步运用的功能和稳定性,进步用户的满意度和留存率。
因而,本系列博客旨在介绍安卓功能优化的根本原理、优化战略和实践技巧,协助开发者更好地了解安卓设备的作业原理,把握安卓功能优化的根本办法和技巧,然后进步运用的功能和稳定性,为用户提供愈加优质的运用体验
安卓开发的功能优化问题非常广泛,以下是其间一些常见的问题:
- 内存走漏:当运用程序不正确地办理内存时,会发生内存走漏,导致内存占用过高,甚至导致运用程序崩溃。
- 布局优化:布局是运用程序中最常见的功能瓶颈之一,由于过于复杂的布局会导致运用程序响应缓慢或卡顿。
- 图片优化:图片是运用程序中占用内存最多的资源之一,因而有必要慎重运用,并对其进行恰当的压缩和缓存,以保证运用程序的功能。
- 网络恳求优化:网络恳求能够在运用程序中占用很多的时间和资源,因而有必要对其进行优化,以减少恳求次数和进步响应速度。
- 数据库优化:当运用程序需求很多访问数据库时,可能会导致功能问题。经过优化数据库规划和运用恰当的数据库缓存,能够进步运用程序的功能。
- 多线程优化:多线程能够进步运用程序的功能,但假如不正确地运用它们,则可能导致死锁、线程竞争和其他问题。
- 内存优化:内存是运用程序功能的重要因素之一。经过及时开释不再需求的内存和防止不必要的内存分配,能够进步运用程序的功能。
- 代码优化:优化代码结构和算法能够进步运用程序的功能。例如,运用更快速和有用的数据结构和算法来进步运用程序的响应速度。
- 安全性优化:安全问题也可能对运用程序的功能发生负面影响。经过防止不安全的代码实践和运用加密技术来维护数据,能够进步运用程序的安全性和功能。
我在功能优化 中记录过几篇文章,也不多,都是从遇到问题的视点出发写的,今日就从上述的惯例优化开端,一个一个剖析记录一下。
为什么说是惯例优化呢?由于Android的功能优化归结到底便是内存问题,而内存层次的优化,不仅是描述中的这些惯例优化项,还能够进行磁盘读写次数、磁盘页数据同步等进一步的优化,而这些优化在这里是不评论的。
内存走漏
内存走漏是指运用程序在运转过程中,无法正确地开释现已不再运用的内存资源,导致内存占用不断添加,终究导致运用程序崩溃或运转缓慢。
内存走漏的原理
安卓内存走漏的原理是指运用程序在运用内存时,由于程序规划问题或许过错,导致无法开释不再运用的内存,终究导致体系中的内存不足,影响体系的稳定性和功能。
以下是一些可能导致安卓内存走漏的常见原因:
- 目标引证未开释:当目标被创立时,假如没有被正确开释,那么这些目标就会一向占用内存,直到运用程序退出。例如,当一个Activity被毁掉时,假如它还持有其他目标的引证,那么这些目标就无法被废物收回器收回,然后导致内存走漏。
假如存在内存走漏,那么这些内存中的目标就会被引证,无法被废物收回机制收回,这时咱们需求经过GCRoot来识别内存走漏的目标和引证。
GCRoot是废物收回机制中的根节点,根节点包含虚拟机栈、本地办法栈、办法区中的类静态属性引证、活动线程等,这些目标被废物收回机制视为“活着的目标”,不会被收回。
当废物收回机制执行时,它会从GCRoot出发,遍历一切的目标引证,并符号一切活着的目标,未被符号的目标即为废物目标,将会被收回。
当存在内存走漏时,废物收回机制无法收回一些现已不再运用的目标,这些目标依然被引证,形成了一些GCRoot到内存走漏目标的引证链,这些目标将无法被收回,导致内存走漏。
经过查找内存走漏目标和GCRoot之间的引证链,能够定位到内存走漏的本源,然后处理内存走漏问题,LeakCancry便是经过这个机制实现的。
一些常见的GCRoot包含:
- 虚拟机栈(Local Variable)中引证的目标。
- 办法区中静态属性(Static Variable)引证的目标。
- JNI 引证的目标。
- Java 线程(Thread)引证的目标。
- Java 中的 synchronized 锁持有的目标。
- 匿名内部类形成的内存走漏:匿名内部类通常会持有外部类的引证,
假如外部类的生命周期比匿名内部类长,(更正一下,这里用生命周期不太恰当,当外部类被毁掉时,内部类并不会主动毁掉,由于内部类并不是外部类的成员变量,它们只是在外部类的效果域内创立的目标,所以内部类的毁掉机遇和外部类的毁掉机遇是不同的,所以会不会取决与对应目标是否存在被持有的引证)那么就会导致外部类无法被收回,然后导致内存走漏。 - 静态变量持有Activity或Context的引证:假如一个静态变量持有Activity或Context的引证,那么这些Activity或Context就无法被废物收回器收回,然后导致内存走漏。
- 未封闭的Cursor、Stream或许Bitmap目标:假如程序在运用Cursor、Stream或许Bitmap目标时没有正确封闭这些目标,那么这些目标就会一向占用内存,然后导致内存走漏。
- 资源未开释:假如程序在运用体系资源时没有正确开释这些资源,例如未封闭数据库衔接、未开释音频资源等,那么这些资源就会一向占用内存,然后导致内存走漏。
常见的内存走漏
静态引证导致的内存走漏
当一个目标被一个静态变量持有时,即使这个目标现已不再运用,也不会被废物收回器收回,这就会导致内存走漏
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的历史记录、缓存等内容,以保证开释一切资源。
监测东西
- 内存监督东西:Android Studio提供了内存监督东西,能够在开发过程中实时监督运用程序的内存运用情况,协助开发者及时发现内存走漏问题。
- DDMS:Android SDK中的DDMS东西能够监督Android设备或模拟器的进程和线程,包含内存运用情况、仓库跟踪等信息,能够用来诊断内存走漏问题。
- MAT:MAT(Memory Analyzer Tool)是一款基于Eclipse的内存剖析东西,能够剖析运用程序的堆内存运用情况,识别和定位内存走漏问题。
- 腾讯的Matrix,也是非常好的一个开源项目,推荐咱们运用
总结
内存走漏是指程序中的某些目标或资源没有被妥善地开释,然后导致内存占用不断添加,终究可能导致运用程序崩溃或体系运转缓慢等问题。
常见的内存走漏问题包含:
- 长时间持有Activity或Fragment目标导致的内存走漏;
- 匿名内部类和非静态内部类导致的内存走漏;
- WebView持有Activity目标导致的内存走漏;
- 单例形式持有资源目标导致的内存走漏;
- 资源未封闭导致的内存走漏;
- 静态变量持有Context目标导致的内存走漏;
- Handler持有外部类引证导致的内存走漏;
- Bitmap占用很多内存导致的内存走漏;
- 单例持有很多数据导致的内存走漏。
为防止内存走漏问题,咱们能够采纳以下办法:
- 及时开释Activity或Fragment目标;
- 防止匿名内部类和非静态内部类;
- 在运用WebView时,及时调用destroy办法;
- 在单例形式中防止长时间持有资源目标;
- 及时封闭资源目标;
- 防止静态变量持有Context目标;
- 防止Handler持有外部类引证;
- 在运用Bitmap时,及时开释内存;
- 防止单例持有很多数据。
欢迎弥补评论