📱 Unity Android 性能优化实战:Android Studio 工具完全指南

💡 Android 优化的价值:

  • Android 平台性能问题难定位?
  • Android Studio Profiler 怎么用?
  • GPU、内存、耗电量,如何全面分析?
  • ADB 命令行工具有哪些神技?

这篇文章! 将深入讲解 Unity Android 优化,从 Android Studio Profiler 到 ADB 命令,让 Android 性能更优!


一、工具概览

1.1 性能分析工具对比

工具 用途 优势
Android Profiler CPU/内存/网络/能耗分析 实时监控,图形化展示
Memory Profiler 内存泄漏分析 堆转储,对象引用追踪
GPU Profiler 渲染性能分析 GPU 活动监控
Layout Inspector UI 布局分析 可视化层级结构
ADB Shell 命令行调试 灵活,可脚本化
Unity Profiler Unity 专用分析 深入 Unity 内部

1.2 工具链示意

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
┌─────────────────────────────────────────────────────────────────────────┐
│ Unity Android 性能分析工具链 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 开发流程: │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌──────┐│
│ │ Unity │───>│ 打包APK │───>│ 安装到 │───>│ 运行App │───>│ 分析 ││
│ │ Editor │ │ │ │ 设备 │ │ │ │ 优化 ││
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └──────┘│
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 性能分析工具 │ │
│ ├─────────────┬─────────────┬─────────────┬─────────────┬─────────┤ │
│ │Android │Memory │GPU │Layout │ADB │ │
│ │Profiler │Profiler │Profiler │Inspector │Shell │ │
│ └─────────────┴─────────────┴─────────────┴─────────────┴─────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘

二、Android Profiler 使用

2.1 启动 Android Profiler

方式一:从 Android Studio 启动

1
2
3
4
1. 连接 Android 设备或启动模拟器
2. 打开 Android Studio
3. View → Tool Windows → Profiler
4. 选择目标设备和进程

方式二:命令行启动

1
2
3
4
5
# 查看连接的设备
adb devices

# 启动 Profiler (Android Studio 3.0+)
# 自动检测可分析的进程

2.2 CPU Profiler

用途:分析 CPU 使用情况和函数调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
┌─────────────────────────────────────────────────────────────────────────┐
│ CPU Profiler 功能 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 采样模式 (Sample): │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ • 低开销 │ │
│ │ • 采样间隔可调 (ms 级) │ │
│ │ • 适合长时间监控 │ │
│ │ • 显示函数调用占比 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ 嵌入模式 (Instrumented): │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ • 高开销 │ │
│ │ • 精确记录每个函数调用 │ │
│ │ • 显示调用时序 │ │
│ │ • 适合短期详细分析 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘

CPU 分析常见问题:

现象 可能原因 解决方案
CPU 占用过高 过多 Update 调用 减少 FixedUpdate 频率
卡顿 主线程阻塞 异步处理,线程池
发热 持续高负载 降低帧率,减少计算

2.3 Memory Profiler

用途:分析内存分配和泄漏

1
2
3
4
5
// 触发 GC 并捕获堆转储
// 在 Memory Profiler 中:
// 1. 点击 "Capture heap dump"
// 2. 分析对象引用链
// 3. 查找泄漏对象

内存分析要点:

指标 说明 警告阈值
Heap Size 堆内存总量 > 80% 设备内存
Allocated 已分配内存 持续增长
Native Heap 原生内存 异常增长
Object Count 对象数量 短时间暴涨

2.4 Network Profiler

用途:分析网络请求性能

监控内容 说明
请求时序 请求时间线
数据大小 请求/响应字节数
响应时间 端到端延迟
线程分析 网络请求所在线程

三、ADB 命令行工具

3.1 ADB 基础命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 设备管理
adb devices # 列出连接的设备
adb devices -l # 详细信息
adb connect <ip>:5555 # 无线连接
adb disconnect <ip> # 断开连接

# 应用管理
adb install app.apk # 安装应用
adb install -r app.apk # 覆盖安装
adb uninstall com.company.app # 卸载应用
adb shell pm list packages # 列出所有应用
adb shell pm list packages -3 # 列出第三方应用

# 文件操作
adb push local_file /sdcard/ # 上传文件到设备
adb pull /sdcard/file local_path # 从设备下载文件
adb shell ls /sdcard/ # 列出目录
adb shell rm /sdcard/file # 删除文件

# 日志查看
adb logcat # 查看全部日志
adb logcat -s Unity # 过滤 Unity 标签
adb logcat -s Unity ActivityManager # 多标签过滤
adb logcat -c # 清除日志
adb logcat -d > log.txt # 保存日志到文件

3.2 性能监控命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# CPU 使用率
adb shell top -n 1 # 实时进程信息
adb shell dumpsys cpuinfo # 详细 CPU 信息

# 内存使用
adb shell dumpsys meminfo com.company.app # 应用内存详情
adb shell dumpsys meminfo com.company.app -d # 持续监控

# GPU 使用
adb shell dumpsys gfxinfo com.company.app # GPU 渲染信息
adb shell dumpsys gfxinfo com.company.app reset # 重置统计

# 网络状态
adb shell netstat # 网络连接
adb shell cat /proc/net/tcp # TCP 连接详情

3.3 Frame Stats 帧统计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 获取帧率信息
adb shell dumpsys gfxinfo com.company.app

# 关键指标解读:
# ┌─────────────────────────────────────────────────────────────────┐
# │ Metrics 说明 │
# ├─────────────────────────────────────────────────────────────────┤
# │ Frames rendered 渲染帧数 │
# │ Janky frames 卡顿帧 (>16.6ms) │
# │ 90th percentile 90%帧的耗时 │
# │ 95th percentile 95%帧的耗时 │
# │ 99th percentile 99%帧的耗时 │
# │ Number of missed Vsync 错过的垂直同步次数 │
# └─────────────────────────────────────────────────────────────────┘

理想指标:

指标 目标值
90th percentile < 16.6ms (60fps)
95th percentile < 33ms (30fps)
Janky frames < 5%

3.4 帧时间详细分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 详细帧分析 (逐帧数据)
adb shell dumpsys gfxinfo com.company.app framestats

# 输出示例:
# Flags : 0
# IntendedVsync: 1234567890123
# Vsync : 1234567890125
# OldestInput : 1234567890124
# NewestInput : 1234567890124
# Issue : 1234567890126
# SyncStart : 1234567890127
# SyncEnd : 1234567890130
# Present : 1234567890131
# ...

四、Unity Android 优化技巧

4.1 图形优化

优化项 设置 效果
目标帧率 Application.targetFrameRate = 30 降低 CPU/GPU 负载
垂直同步 QualitySettings.vSyncCount 避免撕裂
MSAA 禁用或 2x 平衡质量与性能
阴影 降低阴影距离/分辨率 减少 GPU 负载
像素光 限制数量 减少计算
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
using UnityEngine;

/// <summary>
/// Android 性能设置
/// </summary>
public class AndroidPerformanceSettings : MonoBehaviour
{
[System.Serializable]
public class QualityPreset
{
public string presetName;
public int targetFrameRate = 30;
public int vSyncCount = 1;
public int textureQuality = 0;
public int shadowResolution = 1;
public int shadowDistance = 50;
public int pixelLightCount = 2;
}

public QualityPreset low;
public QualityPreset medium;
public QualityPreset high;

void Start()
{
ApplyQualityPreset(medium);
}

public void ApplyQualityPreset(QualityPreset preset)
{
// 帧率设置
Application.targetFrameRate = preset.targetFrameRate;

// 垂直同步
QualitySettings.vSyncCount = preset.vSyncCount;

// 纹理质量
QualitySettings.masterTextureLimit = preset.textureQuality;

// 阴影设置
QualitySettings.shadowResolution = (ShadowResolution)preset.shadowResolution;
QualitySettings.shadowDistance = preset.shadowDistance;

// 像素光
QualitySettings.pixelLightCount = preset.pixelLightCount;
}
}

4.2 内存优化

纹理优化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 纹理压缩设置
// Edit → Project Settings → Player → Android → Texture Compression
// 推荐: ASTC (Adaptive Scalable Texture Compression)

// 运行时纹理加载优化
using UnityEngine;

public class TextureOptimization : MonoBehaviour
{
[Header("纹理设置")]
public int maxTextureSize = 1024;
public TextureCompression compressionFormat = TextureCompression.ASTC;

void Start()
{
OptimizeTextures();
}

void OptimizeTextures()
{
// 获取所有纹理
Texture2D[] textures = Resources.FindObjectsOfTypeAll<Texture2D>();

foreach (var tex in textures)
{
// 调整导入设置
// (需要在 Editor 中设置,运行时无法修改压缩格式)
}
}
}

对象池:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
using UnityEngine;
using System.Collections.Generic;

/// <summary>
/// Android 对象池
/// </summary>
public class AndroidObjectPool : MonoBehaviour
{
private Dictionary<string, Queue<GameObject>> pools = new Dictionary<string, Queue<GameObject>>();

/// <summary>
/// 获取对象
/// </summary>
public GameObject Get(GameObject prefab)
{
string key = prefab.name;

if (pools.ContainsKey(key) && pools[key].Count > 0)
{
GameObject obj = pools[key].Dequeue();
obj.SetActive(true);
return obj;
}

return Instantiate(prefab);
}

/// <summary>
/// 回收对象
/// </summary>
public void Return(GameObject obj, float delay = 0f)
{
if (delay > 0)
{
StartCoroutine(ReturnDelayed(obj, delay));
}
else
{
obj.SetActive(false);
ReturnToPool(obj);
}
}

private IEnumerator ReturnDelayed(GameObject obj, float delay)
{
yield return new WaitForSeconds(delay);
obj.SetActive(false);
ReturnToPool(obj);
}

private void ReturnToPool(GameObject obj)
{
string key = obj.name.Replace("(Clone)", "");

if (!pools.ContainsKey(key))
pools[key] = new Queue<GameObject>();

pools[key].Enqueue(obj);
}
}

4.3 代码优化

协程 vs Update:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
using UnityEngine;
using System.Collections;

/// <summary>
/// 使用协程替代 Update 以降低调用频率
/// </summary>
public class CoroutineOptimization : MonoBehaviour
{
// 低频检查 (每秒一次)
IEnumerator LowFrequencyCheck()
{
while (true)
{
// 检查逻辑
CheckGameState();
yield return new WaitForSeconds(1f);
}
}

// 中频检查 (每 0.1 秒一次)
IEnumerator MediumFrequencyCheck()
{
while (true)
{
// AI 决策等
UpdateAI();
yield return new WaitForSeconds(0.1f);
}
}

void Start()
{
StartCoroutine(LowFrequencyCheck());
StartCoroutine(MediumFrequencyCheck());
}

void CheckGameState() { }
void UpdateAI() { }
}

避免装箱:

1
2
3
4
5
6
7
8
9
10
11
12
// ❌ 错误:产生装箱
int value = 42;
string text = "Value: " + value; // value 被装箱

// ✅ 正确:避免装箱
int value = 42;
string text = $"Value: {value}"; // 使用字符串插值

// ✅ 正确:使用 StringBuilder
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.Append("Value: ");
sb.Append(value);

五、常见问题诊断

5.1 过热问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
┌─────────────────────────────────────────────────────────────────────────┐
│ Android 设备过热诊断 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 可能原因: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 1. CPU 持续高负载 │ │
│ │ 2. GPU 高负载 (高分辨率/高帧率) │ │
│ │ 3. 网络频繁请求 │ │
│ │ 4. 后台服务运行 │ │
│ │ 5. 电池管理冲突 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ 解决方案: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 1. 降低目标帧率 (30fps) │ │
│ │ 2. 降低屏幕分辨率 │ │
│ │ 3. 启用批处理 (Batching) │ │
│ │ 4. 减少实时阴影 │ │
│ │ 5. 优化物理计算频率 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘

5.2 内存增长

1
2
3
4
5
6
7
8
# 监控内存增长
adb shell dumpsys meminfo com.company.app -d | grep "TOTAL"

# 输出示例:
# TOTAL: 123456 (TOTAL): 456789 (TOTAL): 567890
# ^ 进程内存 ^ Native堆 ^ 总内存
#
# 如果持续增长,可能存在内存泄漏

常见内存泄漏源:

来源 说明 检测方法
事件监听 未取消订阅 检查 OnDestroy
静态引用 持有对象引用 Memory Profiler
协程 未正确停止 检查 StopCoroutine
纹理 未释放资源 Resources.UnloadUnusedAssets
WebView 未销毁 检查生命周期

5.3 闪退分析

1
2
3
4
5
6
7
8
# 获取闪退日志
adb logcat -b crash -b system -b main | grep -E "FATAL|AndroidRuntime"

# 查看闪退详情
adb logcat -c # 清除日志
adb logcat -s Unity:V AndroidRuntime:E # 过滤关键日志
# 重现闪退
adb logcat -d > crash_log.txt # 保存日志

六、高级技巧

6.1 自定义性能监控

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
using UnityEngine;
using System.Diagnostics;

/// <summary>
/// 移动端性能监控器
/// </summary>
public class MobilePerformanceMonitor : MonoBehaviour
{
[Header("显示设置")]
public bool showOnScreen = true;
public Vector2 position = new Vector2(10, 10);
public int fontSize = 20;

[Header("警告阈值")]
public float warningFrameTime = 20f; // ms
public Color warningColor = Color.red;

// 性能数据
private float fps;
private float frameTime;
private float memoryUsage;
private int drawCalls;
private int triangles;

private float deltaTime;
private int frameCount;
private float fpsUpdateInterval = 0.5f;
private float fpsTimer;

void Update()
{
deltaTime += Time.unscaledDeltaTime;
frameCount++;

if (deltaTime >= fpsUpdateInterval)
{
fps = frameCount / deltaTime;
frameTime = (deltaTime / frameCount) * 1000f; // ms
frameCount = 0;
deltaTime = 0f;

// 获取内存使用
memoryUsage = System.GC.GetTotalMemory(false) / (1024f * 1024f); // MB

// 获取渲染数据
drawCalls = UnityEngine.Statistics.DrawCalls;
triangles = UnityEngine.Statistics.Triangles;
}
}

void OnGUI()
{
if (!showOnScreen) return;

GUIStyle style = new GUIStyle(GUI.skin.label);
style.fontSize = fontSize;
style.normal.textColor = frameTime > warningFrameTime ? warningColor : Color.white;

GUILayout.BeginArea(new Rect(position.x, position.y, 400, 300));
GUILayout.Label($"FPS: {fps:F1}", style);
GUILayout.Label($"Frame Time: {frameTime:F2} ms", style);
GUILayout.Label($"Memory: {memoryUsage:F1} MB", style);
GUILayout.Label($"Draw Calls: {drawCalls}", style);
GUILayout.Label($"Triangles: {triangles}", style);
GUILayout.EndArea();
}
}

6.2 远程性能数据收集

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
using UnityEngine;
using System;
using System.Collections.Generic;

/// <summary>
/// 性能数据收集与上报
/// </summary>
public class PerformanceDataCollector : MonoBehaviour
{
[System.Serializable]
public class PerformanceReport
{
public string deviceModel;
public string osVersion;
public float averageFps;
public float maxFrameTime;
public float averageMemory;
public long timestamp;
}

private List<float> frameTimes = new List<float>();
private List<float> memorySamples = new List<float>();
private int maxSamples = 1000;

void Update()
{
frameTimes.Add(Time.unscaledDeltaTime * 1000f);

if (frameTimes.Count > maxSamples)
frameTimes.RemoveAt(0);
}

void LateUpdate()
{
memorySamples.Add(System.GC.GetTotalMemory(false) / (1024f * 1024f));

if (memorySamples.Count > maxSamples)
memorySamples.RemoveAt(0);
}

/// <summary>
/// 生成性能报告
/// </summary>
public PerformanceReport GenerateReport()
{
float avgFps = 0f;
float maxFrameTime = 0f;
float avgMemory = 0f;

if (frameTimes.Count > 0)
{
float totalFrameTime = 0f;
foreach (float ft in frameTimes)
{
totalFrameTime += ft;
if (ft > maxFrameTime)
maxFrameTime = ft;
}
avgFps = 1000f / (totalFrameTime / frameTimes.Count);
}

if (memorySamples.Count > 0)
{
float totalMemory = 0f;
foreach (float mem in memorySamples)
{
totalMemory += mem;
}
avgMemory = totalMemory / memorySamples.Count;
}

return new PerformanceReport
{
deviceModel = SystemInfo.deviceModel,
osVersion = SystemInfo.operatingSystem,
averageFps = avgFps,
maxFrameTime = maxFrameTime,
averageMemory = avgMemory,
timestamp = DateTime.UtcNow.Ticks
};
}
}

七、优化检查清单

7.1 发布前检查

类别 检查项 状态
图形 目标帧率设置为 30 ⬜
图形 启用纹理压缩 (ASTC) ⬜
图形 降低阴影距离/质量 ⬜
图形 限制像素光数量 ⬜
内存 释放未使用资源 ⬜
内存 使用对象池 ⬜
内存 避免频繁 GC ⬜
代码 优化 Update 调用 ⬜
代码 使用对象池 ⬜
代码 避免装箱拆箱 ⬜
构建 使用 IL2CPP ⬜
构建 启用 Strip Engine Code ⬜
构建 设置 API Level ⬜

7.2 Player 设置推荐

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
┌─────────────────────────────────────────────────────────────────────────┐
│ Unity Android Player 设置 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Graphics API: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ • Auto Graphics API (自动选择) │ │
│ │ • 或者强制使用 Vulkan (高端设备) / GLES 3.0 (中低端) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Scripting Backend: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ • IL2CPP (性能更好,包体更小) │ │
│ │ • Mono (开发阶段使用) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Target Architecture: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ • ARM64 (推荐,主流设备) │ │
│ │ • ARMv7 (兼容老设备) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Optimization: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ • Prebake Collision Meshes │ │
│ │ • Keep Loaded Shaders │ │
│ │ • Strip Engine Code │ │
│ │ • Use Float Position For Temporal AA (如需要) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘

八、总结

主题 要点
工具 Android Profiler + ADB + Unity Profiler
CPU 降低帧率,减少 Update 调用,使用协程
内存 对象池,纹理压缩,及时释放资源
GPU 降低分辨率,减少阴影/光效
网络 合并请求,减少连接数
诊断 dumpsys + logcat 定位问题

💡 核心建议:

  • 首先使用 Android Profiler 定位瓶颈
  • 优先解决最严重的性能问题
  • 在真机上测试,不要依赖模拟器
  • 建立性能基准,持续监控
  • 针对不同设备档次设置不同质量档位

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1487842110@qq.com