在 Android 开发领域,打造高性能应用至关重要。随着移动设备和用户需求的不断演进,应用性能成为决定用户体验的关键因素。性能卓越的应用不仅能提升用户满意度,还能在竞争激烈的应用市场中脱颖而出。今天,我们就来深入探讨 Android 开发中的性能优化相关问题。
一、性能优化的重要性
在当今快节奏的数字时代,用户对应用的响应速度和流畅度要求极高。调查显示,用户卸载软件的主要原因之一就是性能差。例如,一款启动缓慢、界面切换卡顿的购物应用,会让用户在购物过程中失去耐心,转而选择其他竞品应用。因此,性能优化在 Android 开发中占据着举足轻重的地位。
二、性能相关的参考指标
(一)启动速度
启动速度分为冷启动和热启动。冷启动指杀掉应用后重新启动的速度,热启动则是应用在后台运行一段时间后再次启动的速度。快速的启动速度能让用户迅速进入应用,提升用户体验。例如,一款新闻资讯应用如果能在 1 秒内完成冷启动,就能让用户更快地获取最新资讯,增强用户粘性。
(二)界面切换
界面切换应确保无卡顿现象。在实际使用中,当用户在社交应用中快速切换不同的聊天窗口、相册等界面时,如果出现卡顿,会严重影响用户的使用感受。
(三)内存管理
内存管理主要关注内存泄漏问题。内存泄漏会导致应用不断消耗内存,最终可能引发应用崩溃。例如,一个图片编辑应用,如果在处理大量图片时存在内存泄漏,随着使用时间的增加,应用会变得越来越卡顿,甚至出现闪退现象。
(四)UI 过度绘制
UI 过度绘制通常由 xml 布局、自定义控件多次调用 onlayout 等原因导致。这会增加 CPU 和 GPU 的负担,降低应用的性能。比如一个界面复杂的游戏应用,如果存在大量的 UI 过度绘制,会导致游戏运行时发热严重,帧率下降。
三、常见性能问题及解决方案
(一)界面切换卡顿
1.人为在 UI 线程中做轻微耗时操作,导致 UI 线程卡顿:
class DownloadTask extends AsyncTask {
@Override
protected String doInBackground(String... urls) {
// 执行网络请求等耗时操作
return result;
}
@Override
protected void onPostExecute(String result) {
// 更新UI
}
}
2.布局 Layout 过于复杂,无法在 16ms 内完成渲染:
在代码中通过ViewStub viewStub = findViewById(R.id.view_stub); viewStub.inflate();来加载布局。
3. 同一时间动画执行的次数过多,导致 CPU 或 GPU 负载过重:
AnimatorSet animatorSet = new AnimatorSet();
ObjectAnimator animator1 = ObjectAnimator.ofFloat(view, "translationX", 0f, 100f);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(view, "alpha", 1f, 0f);
animatorSet.playSequentially(animator1, animator2);
animatorSet.start();
4.View 过度绘制,导致某些像素在同一帧时间内被绘制多次,从而使 CPU 或 GPU 负载过重:
如果不需要背景,可设置为null:
- null
Glide.with(context)
.load(imageUrl)
.placeholder(R.drawable.placeholder_image)
.into(imageView);
5.View 频繁的触发 measure、layout,导致 measure、layout 累计耗时过多及整个 View 频繁的重新渲染:
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
params.setMargins(leftMargin, topMargin, rightMargin, bottomMargin);
textView.setLayoutParams(params);
6.内存频繁触发 GC 过多(同一帧中频繁创建内存),导致暂时阻塞渲染操作:
public PointPool() {
for (int i = 0; i < POOL_SIZE; i++) {
pointStack.push(new Point());
}
}
public Point obtain() {
if (pointStack.isEmpty()) {
return new Point();
}
return pointStack.pop();
}
public void recycle(Point point) {
pointStack.push(point);
}
}
7.冗余资源及逻辑等导致加载和执行缓慢:
Thread backgroundThread = new Thread(new Runnable() {
@Override
public void run() {
// 后台任务
}
});
backgroundThread.setPriority(Process.THREAD_PRIORITY_BACKGROUND);
backgroundThread.start();
8.ANR(Application Not Responding):
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
// 更新UI
}
});
public class MyIntentService extends IntentService {
public MyIntentService() {
super("MyIntentService");
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
// 处理耗时任务
}
}
在 BroadcastReceiver 中启动 IntentService:
Intent intent = new Intent(context, MyIntentService.class);
context.startService(intent);
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
executor.submit(new Runnable() {
@Override
public void run() {
// Service中的耗时任务
}
});
(二)内存问题
内存问题主要表现为内存泄露,或者内存使用不当导致的内存抖动。如果存在内存泄露,应用会不断消耗内存,易导致频繁 gc 使系统出现卡顿,或者出现 OOM(Out Of Memory)报错;内存抖动也会导致 UI 卡顿。
1.单例造成的内存泄漏:
public class AManager {
private static AManager instance;
private Context context;
private AManager(Context context) {
this.context = context;
}
public static AManager getInstance(Context context) {
if (instance == null) {
instance = new AManager(context);
}
return instance;
}
}
当传入的是 Activity 的 Context 时,若该 Activity 退出,由于单例持有该 Activity 的 Context 引用,导致 Activity 的内存无法回收。
public class AManager {
private static AManager instance;
private Context context;
private AManager(Context context) {
this.context = context.getApplicationContext();
}
public static AManager getInstance(Context context) {
if (instance == null) {
instance = new AManager(context);
}
return instance;
}
}
2.非静态内部类创建静态实例造成的内存泄漏:
public class MainActivity extends AppCompatActivity {
private static InnerClass innerClass;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (innerClass == null) {
innerClass = new InnerClass();
}
}
class InnerClass {
// 内部类代码
}
}
解决方案:将该内部类设为静态内部类或将该内部类抽取出来封装成一个单例,如果需要使用 Context,请使用 ApplicationContext。修改后的代码如下:
public class MainActivity extends AppCompatActivity {
private static InnerClass innerClass;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (innerClass == null) {
innerClass = InnerClass.getInstance(this);
}
}
static class InnerClass {
private static InnerClass instance;
private Context context;
private InnerClass(Context context) {
this.context = context.getApplicationContext();
}
public static InnerClass getInstance(Context context) {
if (instance == null) {
instance = new InnerClass(context);
}
return instance;
}
}
}
Handler 造成的内存泄漏:
public class MainActivity extends AppCompatActivity {
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// 处理消息
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler.sendEmptyMessageDelayed(0, 10000);
}
}
由于 mHandler 是 Handler 的非静态匿名内部类的实例,它持有外部类 Activity 的引用。当 Activity 退出时,如果消息队列中还有未处理的消息或正在处理消息,而 Message 持有 mHandler 实例的引用,mHandler 又持有 Activity 的引用,导致 Activity 的内存资源无法及时回收。