鏇存柊鏂囨。
This commit is contained in:
49
.obsidian/workspace.json
vendored
49
.obsidian/workspace.json
vendored
@@ -13,15 +13,30 @@
|
||||
"state": {
|
||||
"type": "markdown",
|
||||
"state": {
|
||||
"file": "docs/Obsidian笔记体系/Areas/09-调试与工具链/Systrace_Perfetto全解读.md",
|
||||
"file": "docs/dify/用户注册及添加成员.md",
|
||||
"mode": "source",
|
||||
"source": false
|
||||
},
|
||||
"icon": "lucide-file",
|
||||
"title": "Systrace_Perfetto全解读"
|
||||
"title": "用户注册及添加成员"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "25c9f7051aac05b3",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "markdown",
|
||||
"state": {
|
||||
"file": "docs/android面试/系统原理/WMS面试.md",
|
||||
"mode": "source",
|
||||
"source": false
|
||||
},
|
||||
"icon": "lucide-file",
|
||||
"title": "WMS面试"
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
"currentTab": 1
|
||||
}
|
||||
],
|
||||
"direction": "vertical"
|
||||
@@ -174,17 +189,27 @@
|
||||
},
|
||||
"left-ribbon": {
|
||||
"hiddenItems": {
|
||||
"bases:新建数据库": false,
|
||||
"switcher:打开快速切换": false,
|
||||
"graph:查看关系图谱": false,
|
||||
"canvas:新建白板": false,
|
||||
"daily-notes:打开/创建今天的日记": false,
|
||||
"templates:插入模板": false,
|
||||
"command-palette:打开命令面板": false,
|
||||
"bases:创建新数据库": false
|
||||
"command-palette:打开命令面板": false
|
||||
}
|
||||
},
|
||||
"active": "5b497a77c6d68c73",
|
||||
"active": "25c9f7051aac05b3",
|
||||
"lastOpenFiles": [
|
||||
"docs/Obsidian笔记体系/Projects/saars开发/aiapply/未命名 1.md",
|
||||
"docs/android面试/系统原理/WMS面试.md",
|
||||
"docs/Obsidian笔记体系/Projects/saars开发/aiapply/未命名.md",
|
||||
"docs/Obsidian笔记体系/Projects/saars开发/aiapply",
|
||||
"docs/dify/管理员账户.md",
|
||||
"docs/dify/用户注册及添加成员.md",
|
||||
"docs/dify/密码输入错误次数超过限制时的机制.md",
|
||||
"docs/dify/使用dify,可以生成一个专项事务助手吗,比如公司正规化事务助手.md",
|
||||
"docs/dify/作为安卓高级开发工程师,除了项目管理,你完全可以在技术专项、团队效能和个人成长三大领域构建更懂你的专属助手.md",
|
||||
"docs/Obsidian笔记体系/Areas/09-调试与工具链/Systrace_Perfetto全解读.md",
|
||||
"docs/技术面试问题回答.md",
|
||||
"docs/Google开发文档体系/视频和教程/技术会议.md",
|
||||
"docs/Google开发文档体系/视频和教程/官方视频教程.md",
|
||||
@@ -202,15 +227,6 @@
|
||||
"docs/Google开发文档体系/示例代码/架构示例.md",
|
||||
"docs/Google开发文档体系/示例代码/官方示例项目.md",
|
||||
"docs/Google开发文档体系/示例代码/代码片段.md",
|
||||
"docs/Google开发文档体系/最佳实践/用户体验最佳实践.md",
|
||||
"docs/Google开发文档体系/最佳实践/应用架构最佳实践.md",
|
||||
"docs/Google开发文档体系/最佳实践/性能优化最佳实践.md",
|
||||
"docs/Google开发文档体系/最佳实践/安全最佳实践.md",
|
||||
"docs/Google开发文档体系/最佳实践/代码质量最佳实践.md",
|
||||
"docs/Google开发文档体系/工具和资源/调试工具.md",
|
||||
"docs/Google开发文档体系/工具和资源/构建系统.md",
|
||||
"docs/Google开发文档体系/工具和资源/Android_Studio.md",
|
||||
"docs/Google开发文档体系/工具和资源/性能分析工具.md",
|
||||
"docs/Google开发文档体系/视频和教程",
|
||||
"docs/Google开发文档体系/核心主题",
|
||||
"docs/Google开发文档体系/示例代码",
|
||||
@@ -219,7 +235,6 @@
|
||||
"docs/Google开发文档体系/入门指南",
|
||||
"docs/Google开发文档体系/API参考",
|
||||
"docs/Google开发文档体系",
|
||||
"docs/android面试/技术面试问题回答.txt",
|
||||
"技术面试问题回答.txt"
|
||||
"docs/android面试/技术面试问题回答.txt"
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,786 @@
|
||||
# 应用架构最佳实践
|
||||
|
||||
## 待补充内容
|
||||
良好的应用架构是Android应用开发的基础,它直接影响应用的可维护性、可测试性和可扩展性。本文档介绍Android应用架构设计的最佳实践。
|
||||
|
||||
- 架构设计原则
|
||||
- 分层架构
|
||||
- 模块化设计
|
||||
- 依赖管理
|
||||
- 架构模式选择
|
||||
## 目录
|
||||
|
||||
- [架构设计原则](#架构设计原则)
|
||||
- [分层架构](#分层架构)
|
||||
- [模块化设计](#模块化设计)
|
||||
- [依赖管理](#依赖管理)
|
||||
- [架构模式选择](#架构模式选择)
|
||||
- [Jetpack架构组件](#jetpack架构组件)
|
||||
|
||||
---
|
||||
|
||||
## 架构设计原则
|
||||
|
||||
### 1. 关注点分离(Separation of Concerns)
|
||||
|
||||
#### 单一职责原则
|
||||
|
||||
```kotlin
|
||||
// ✅ 好的设计:每个类只有一个职责
|
||||
class UserRepository {
|
||||
fun getUser(userId: String): User {
|
||||
// 只负责数据获取
|
||||
}
|
||||
}
|
||||
|
||||
class UserViewModel : ViewModel() {
|
||||
private val repository: UserRepository
|
||||
|
||||
fun loadUser(userId: String) {
|
||||
// 只负责UI逻辑
|
||||
viewModelScope.launch {
|
||||
val user = repository.getUser(userId)
|
||||
_user.value = user
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ❌ 不好的设计:一个类承担多个职责
|
||||
class UserManager {
|
||||
fun getUser(userId: String): User { }
|
||||
fun saveUser(user: User) { }
|
||||
fun displayUser(user: User) { } // UI逻辑不应该在这里
|
||||
fun validateUser(user: User) { } // 业务逻辑不应该在这里
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 依赖倒置原则(Dependency Inversion)
|
||||
|
||||
#### 依赖接口而非实现
|
||||
|
||||
```kotlin
|
||||
// ✅ 好的设计:依赖接口
|
||||
interface UserRepository {
|
||||
suspend fun getUser(userId: String): User
|
||||
}
|
||||
|
||||
class UserRepositoryImpl(
|
||||
private val apiService: ApiService,
|
||||
private val localDataSource: UserLocalDataSource
|
||||
) : UserRepository {
|
||||
override suspend fun getUser(userId: String): User {
|
||||
// 实现细节
|
||||
}
|
||||
}
|
||||
|
||||
class UserViewModel(
|
||||
private val repository: UserRepository // 依赖接口
|
||||
) : ViewModel() {
|
||||
// ...
|
||||
}
|
||||
|
||||
// ❌ 不好的设计:依赖具体实现
|
||||
class UserViewModel(
|
||||
private val repository: UserRepositoryImpl // 依赖具体实现
|
||||
) : ViewModel()
|
||||
```
|
||||
|
||||
### 3. 开闭原则(Open/Closed Principle)
|
||||
|
||||
#### 对扩展开放,对修改关闭
|
||||
|
||||
```kotlin
|
||||
// ✅ 好的设计:通过扩展添加功能
|
||||
abstract class DataSource {
|
||||
abstract suspend fun getData(): Data
|
||||
}
|
||||
|
||||
class RemoteDataSource : DataSource() {
|
||||
override suspend fun getData(): Data {
|
||||
// 从远程获取数据
|
||||
}
|
||||
}
|
||||
|
||||
class LocalDataSource : DataSource() {
|
||||
override suspend fun getData(): Data {
|
||||
// 从本地获取数据
|
||||
}
|
||||
}
|
||||
|
||||
// 可以添加新的数据源而不修改现有代码
|
||||
class CacheDataSource : DataSource() {
|
||||
override suspend fun getData(): Data {
|
||||
// 从缓存获取数据
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 接口隔离原则(Interface Segregation)
|
||||
|
||||
#### 使用小而专的接口
|
||||
|
||||
```kotlin
|
||||
// ✅ 好的设计:小而专的接口
|
||||
interface Readable {
|
||||
fun read(): String
|
||||
}
|
||||
|
||||
interface Writable {
|
||||
fun write(data: String)
|
||||
}
|
||||
|
||||
class FileManager : Readable, Writable {
|
||||
override fun read(): String { }
|
||||
override fun write(data: String) { }
|
||||
}
|
||||
|
||||
// ❌ 不好的设计:大而全的接口
|
||||
interface DataManager {
|
||||
fun read(): String
|
||||
fun write(data: String)
|
||||
fun delete()
|
||||
fun update()
|
||||
fun search()
|
||||
// 太多方法,违反接口隔离原则
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 分层架构
|
||||
|
||||
### 1. 三层架构
|
||||
|
||||
#### 表现层(Presentation Layer)
|
||||
|
||||
```kotlin
|
||||
// UI层:Activity/Fragment
|
||||
class UserActivity : AppCompatActivity() {
|
||||
private lateinit var viewModel: UserViewModel
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_user)
|
||||
|
||||
viewModel = ViewModelProvider(this)[UserViewModel::class.java]
|
||||
|
||||
viewModel.user.observe(this) { user ->
|
||||
// 更新UI
|
||||
updateUI(user)
|
||||
}
|
||||
|
||||
viewModel.loadUser(userId)
|
||||
}
|
||||
}
|
||||
|
||||
// ViewModel:处理UI逻辑
|
||||
class UserViewModel(
|
||||
private val getUserUseCase: GetUserUseCase
|
||||
) : ViewModel() {
|
||||
private val _user = MutableLiveData<User>()
|
||||
val user: LiveData<User> = _user
|
||||
|
||||
fun loadUser(userId: String) {
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
val user = getUserUseCase(userId)
|
||||
_user.value = user
|
||||
} catch (e: Exception) {
|
||||
// 处理错误
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 领域层(Domain Layer)
|
||||
|
||||
```kotlin
|
||||
// UseCase:业务逻辑
|
||||
class GetUserUseCase(
|
||||
private val repository: UserRepository
|
||||
) {
|
||||
suspend operator fun invoke(userId: String): Result<User> {
|
||||
return try {
|
||||
val user = repository.getUser(userId)
|
||||
Result.success(user)
|
||||
} catch (e: Exception) {
|
||||
Result.failure(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Entity:领域实体
|
||||
data class User(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val email: String
|
||||
)
|
||||
|
||||
// Repository接口:定义数据访问接口
|
||||
interface UserRepository {
|
||||
suspend fun getUser(userId: String): User
|
||||
}
|
||||
```
|
||||
|
||||
#### 数据层(Data Layer)
|
||||
|
||||
```kotlin
|
||||
// Repository实现
|
||||
class UserRepositoryImpl(
|
||||
private val remoteDataSource: UserRemoteDataSource,
|
||||
private val localDataSource: UserLocalDataSource
|
||||
) : UserRepository {
|
||||
override suspend fun getUser(userId: String): User {
|
||||
return try {
|
||||
// 先尝试从远程获取
|
||||
val user = remoteDataSource.getUser(userId)
|
||||
// 保存到本地
|
||||
localDataSource.saveUser(user)
|
||||
user
|
||||
} catch (e: Exception) {
|
||||
// 如果远程失败,从本地获取
|
||||
localDataSource.getUser(userId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 远程数据源
|
||||
class UserRemoteDataSource(
|
||||
private val apiService: ApiService
|
||||
) {
|
||||
suspend fun getUser(userId: String): User {
|
||||
return apiService.getUser(userId).toDomain()
|
||||
}
|
||||
}
|
||||
|
||||
// 本地数据源
|
||||
class UserLocalDataSource(
|
||||
private val userDao: UserDao
|
||||
) {
|
||||
suspend fun getUser(userId: String): User {
|
||||
return userDao.getUser(userId).toDomain()
|
||||
}
|
||||
|
||||
suspend fun saveUser(user: User) {
|
||||
userDao.insertUser(user.toEntity())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Clean Architecture
|
||||
|
||||
#### 依赖规则
|
||||
|
||||
```
|
||||
表现层 → 领域层 ← 数据层
|
||||
```
|
||||
|
||||
```kotlin
|
||||
// 领域层不依赖任何其他层
|
||||
// Domain Layer
|
||||
interface UserRepository {
|
||||
suspend fun getUser(userId: String): User
|
||||
}
|
||||
|
||||
class GetUserUseCase(
|
||||
private val repository: UserRepository // 依赖接口
|
||||
) {
|
||||
suspend operator fun invoke(userId: String): User {
|
||||
return repository.getUser(userId)
|
||||
}
|
||||
}
|
||||
|
||||
// 数据层实现领域层的接口
|
||||
// Data Layer
|
||||
class UserRepositoryImpl : UserRepository {
|
||||
override suspend fun getUser(userId: String): User {
|
||||
// 实现
|
||||
}
|
||||
}
|
||||
|
||||
// 表现层依赖领域层
|
||||
// Presentation Layer
|
||||
class UserViewModel(
|
||||
private val getUserUseCase: GetUserUseCase // 依赖UseCase
|
||||
) : ViewModel()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 模块化设计
|
||||
|
||||
### 1. 功能模块化
|
||||
|
||||
#### 模块结构
|
||||
|
||||
```
|
||||
app/
|
||||
├── app/ # 主模块
|
||||
├── core/ # 核心模块(通用功能)
|
||||
│ ├── core-common/
|
||||
│ ├── core-network/
|
||||
│ └── core-database/
|
||||
├── feature/ # 功能模块
|
||||
│ ├── feature-home/
|
||||
│ ├── feature-user/
|
||||
│ └── feature-settings/
|
||||
└── buildSrc/ # 构建脚本
|
||||
```
|
||||
|
||||
#### 模块依赖关系
|
||||
|
||||
```gradle
|
||||
// app/build.gradle
|
||||
dependencies {
|
||||
implementation project(':core:core-common')
|
||||
implementation project(':core:core-network')
|
||||
implementation project(':feature:feature-home')
|
||||
implementation project(':feature:feature-user')
|
||||
}
|
||||
|
||||
// feature-home/build.gradle
|
||||
dependencies {
|
||||
implementation project(':core:core-common')
|
||||
// feature模块不依赖其他feature模块
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 组件化架构
|
||||
|
||||
#### 组件通信
|
||||
|
||||
```kotlin
|
||||
// 使用接口定义组件通信
|
||||
interface HomeNavigation {
|
||||
fun navigateToUserDetail(userId: String)
|
||||
fun navigateToSettings()
|
||||
}
|
||||
|
||||
// Home模块实现
|
||||
class HomeFragment : Fragment(), HomeNavigation {
|
||||
override fun navigateToUserDetail(userId: String) {
|
||||
findNavController().navigate(
|
||||
HomeFragmentDirections.actionToUserDetail(userId)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 使用Router进行模块间通信
|
||||
class AppRouter {
|
||||
fun navigateToUserDetail(context: Context, userId: String) {
|
||||
val intent = Intent(context, UserDetailActivity::class.java)
|
||||
intent.putExtra("user_id", userId)
|
||||
context.startActivity(intent)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 依赖管理
|
||||
|
||||
### 1. 依赖注入
|
||||
|
||||
#### 使用Hilt/Dagger
|
||||
|
||||
```kotlin
|
||||
// 定义Module
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
object AppModule {
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideApiService(): ApiService {
|
||||
return Retrofit.Builder()
|
||||
.baseUrl("https://api.example.com/")
|
||||
.build()
|
||||
.create(ApiService::class.java)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideUserRepository(
|
||||
apiService: ApiService
|
||||
): UserRepository {
|
||||
return UserRepositoryImpl(apiService)
|
||||
}
|
||||
}
|
||||
|
||||
// 注入依赖
|
||||
@AndroidEntryPoint
|
||||
class UserActivity : AppCompatActivity() {
|
||||
@Inject
|
||||
lateinit var userRepository: UserRepository
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
#### 手动依赖注入
|
||||
|
||||
```kotlin
|
||||
// 依赖容器
|
||||
class AppContainer {
|
||||
private val apiService: ApiService by lazy {
|
||||
Retrofit.Builder()
|
||||
.baseUrl("https://api.example.com/")
|
||||
.build()
|
||||
.create(ApiService::class.java)
|
||||
}
|
||||
|
||||
val userRepository: UserRepository by lazy {
|
||||
UserRepositoryImpl(apiService)
|
||||
}
|
||||
}
|
||||
|
||||
// 在Application中初始化
|
||||
class MyApplication : Application() {
|
||||
val appContainer = AppContainer()
|
||||
}
|
||||
|
||||
// 在Activity中使用
|
||||
class UserActivity : AppCompatActivity() {
|
||||
private lateinit var userRepository: UserRepository
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
userRepository = (application as MyApplication)
|
||||
.appContainer.userRepository
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 依赖版本管理
|
||||
|
||||
#### 统一版本管理
|
||||
|
||||
```gradle
|
||||
// buildSrc/src/main/kotlin/Dependencies.kt
|
||||
object Versions {
|
||||
const val kotlin = "1.9.0"
|
||||
const val androidxCore = "1.9.0"
|
||||
const val androidxAppCompat = "1.6.1"
|
||||
const val material = "1.8.0"
|
||||
const val hilt = "2.44"
|
||||
}
|
||||
|
||||
object Dependencies {
|
||||
const val kotlinStdlib = "org.jetbrains.kotlin:kotlin-stdlib:${Versions.kotlin}"
|
||||
const val androidxCoreKtx = "androidx.core:core-ktx:${Versions.androidxCore}"
|
||||
const val androidxAppCompat = "androidx.appcompat:appcompat:${Versions.androidxAppCompat}"
|
||||
const val material = "com.google.android.material:material:${Versions.material}"
|
||||
const val hiltAndroid = "com.google.dagger:hilt-android:${Versions.hilt}"
|
||||
}
|
||||
|
||||
// 在模块中使用
|
||||
dependencies {
|
||||
implementation(Dependencies.kotlinStdlib)
|
||||
implementation(Dependencies.androidxCoreKtx)
|
||||
implementation(Dependencies.androidxAppCompat)
|
||||
implementation(Dependencies.material)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 架构模式选择
|
||||
|
||||
### 1. MVVM模式
|
||||
|
||||
#### MVVM实现
|
||||
|
||||
```kotlin
|
||||
// Model:数据模型
|
||||
data class User(
|
||||
val id: String,
|
||||
val name: String
|
||||
)
|
||||
|
||||
// ViewModel:UI逻辑
|
||||
class UserViewModel(
|
||||
private val repository: UserRepository
|
||||
) : ViewModel() {
|
||||
private val _user = MutableLiveData<User>()
|
||||
val user: LiveData<User> = _user
|
||||
|
||||
private val _loading = MutableLiveData<Boolean>()
|
||||
val loading: LiveData<Boolean> = _loading
|
||||
|
||||
fun loadUser(userId: String) {
|
||||
viewModelScope.launch {
|
||||
_loading.value = true
|
||||
try {
|
||||
val user = repository.getUser(userId)
|
||||
_user.value = user
|
||||
} catch (e: Exception) {
|
||||
// 处理错误
|
||||
} finally {
|
||||
_loading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// View:UI
|
||||
class UserActivity : AppCompatActivity() {
|
||||
private lateinit var viewModel: UserViewModel
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_user)
|
||||
|
||||
viewModel = ViewModelProvider(this)[UserViewModel::class.java]
|
||||
|
||||
viewModel.user.observe(this) { user ->
|
||||
// 更新UI
|
||||
nameTextView.text = user.name
|
||||
}
|
||||
|
||||
viewModel.loading.observe(this) { isLoading ->
|
||||
progressBar.visibility = if (isLoading) View.VISIBLE else View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. MVI模式
|
||||
|
||||
#### MVI实现
|
||||
|
||||
```kotlin
|
||||
// State:UI状态
|
||||
data class UserState(
|
||||
val user: User? = null,
|
||||
val isLoading: Boolean = false,
|
||||
val error: String? = null
|
||||
)
|
||||
|
||||
// Intent:用户意图
|
||||
sealed class UserIntent {
|
||||
object LoadUser : UserIntent()
|
||||
data class RefreshUser(val userId: String) : UserIntent()
|
||||
}
|
||||
|
||||
// ViewModel
|
||||
class UserViewModel(
|
||||
private val repository: UserRepository
|
||||
) : ViewModel() {
|
||||
private val _state = MutableStateFlow(UserState())
|
||||
val state: StateFlow<UserState> = _state.asStateFlow()
|
||||
|
||||
fun handleIntent(intent: UserIntent) {
|
||||
when (intent) {
|
||||
is UserIntent.LoadUser -> loadUser()
|
||||
is UserIntent.RefreshUser -> refreshUser(intent.userId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadUser() {
|
||||
viewModelScope.launch {
|
||||
_state.value = _state.value.copy(isLoading = true)
|
||||
try {
|
||||
val user = repository.getUser()
|
||||
_state.value = _state.value.copy(
|
||||
user = user,
|
||||
isLoading = false
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
_state.value = _state.value.copy(
|
||||
error = e.message,
|
||||
isLoading = false
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Clean Architecture + MVVM
|
||||
|
||||
#### 组合使用
|
||||
|
||||
```kotlin
|
||||
// Domain Layer
|
||||
interface UserRepository {
|
||||
suspend fun getUser(userId: String): User
|
||||
}
|
||||
|
||||
class GetUserUseCase(
|
||||
private val repository: UserRepository
|
||||
) {
|
||||
suspend operator fun invoke(userId: String): Result<User> {
|
||||
return try {
|
||||
Result.success(repository.getUser(userId))
|
||||
} catch (e: Exception) {
|
||||
Result.failure(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Presentation Layer (MVVM)
|
||||
class UserViewModel(
|
||||
private val getUserUseCase: GetUserUseCase
|
||||
) : ViewModel() {
|
||||
private val _user = MutableLiveData<User>()
|
||||
val user: LiveData<User> = _user
|
||||
|
||||
fun loadUser(userId: String) {
|
||||
viewModelScope.launch {
|
||||
when (val result = getUserUseCase(userId)) {
|
||||
is Result.Success -> _user.value = result.data
|
||||
is Result.Failure -> {
|
||||
// 处理错误
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Jetpack架构组件
|
||||
|
||||
### 1. ViewModel
|
||||
|
||||
#### 使用ViewModel
|
||||
|
||||
```kotlin
|
||||
class UserViewModel(
|
||||
private val repository: UserRepository
|
||||
) : ViewModel() {
|
||||
private val _user = MutableLiveData<User>()
|
||||
val user: LiveData<User> = _user
|
||||
|
||||
init {
|
||||
loadUser()
|
||||
}
|
||||
|
||||
private fun loadUser() {
|
||||
viewModelScope.launch {
|
||||
val user = repository.getUser()
|
||||
_user.value = user
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
// 清理资源
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. LiveData
|
||||
|
||||
#### 使用LiveData
|
||||
|
||||
```kotlin
|
||||
class UserViewModel : ViewModel() {
|
||||
private val _user = MutableLiveData<User>()
|
||||
val user: LiveData<User> = _user
|
||||
|
||||
private val _error = MutableLiveData<String?>()
|
||||
val error: LiveData<String?> = _error
|
||||
|
||||
fun loadUser() {
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
val user = repository.getUser()
|
||||
_user.value = user
|
||||
_error.value = null
|
||||
} catch (e: Exception) {
|
||||
_error.value = e.message
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 在Activity中观察
|
||||
viewModel.user.observe(this) { user ->
|
||||
// 更新UI
|
||||
}
|
||||
|
||||
viewModel.error.observe(this) { error ->
|
||||
error?.let {
|
||||
// 显示错误
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Room数据库
|
||||
|
||||
#### 使用Room
|
||||
|
||||
```kotlin
|
||||
// Entity
|
||||
@Entity(tableName = "users")
|
||||
data class UserEntity(
|
||||
@PrimaryKey val id: String,
|
||||
val name: String,
|
||||
val email: String
|
||||
)
|
||||
|
||||
// DAO
|
||||
@Dao
|
||||
interface UserDao {
|
||||
@Query("SELECT * FROM users WHERE id = :userId")
|
||||
suspend fun getUser(userId: String): UserEntity?
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insertUser(user: UserEntity)
|
||||
|
||||
@Delete
|
||||
suspend fun deleteUser(user: UserEntity)
|
||||
}
|
||||
|
||||
// Database
|
||||
@Database(entities = [UserEntity::class], version = 1)
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
abstract fun userDao(): UserDao
|
||||
}
|
||||
```
|
||||
|
||||
### 4. WorkManager
|
||||
|
||||
#### 使用WorkManager
|
||||
|
||||
```kotlin
|
||||
// 定义Worker
|
||||
class SyncWorker(
|
||||
context: Context,
|
||||
params: WorkerParameters
|
||||
) : CoroutineWorker(context, params) {
|
||||
override suspend fun doWork(): Result {
|
||||
return try {
|
||||
// 执行后台任务
|
||||
syncData()
|
||||
Result.success()
|
||||
} catch (e: Exception) {
|
||||
Result.retry()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 调度任务
|
||||
val syncRequest = OneTimeWorkRequestBuilder<SyncWorker>()
|
||||
.setConstraints(
|
||||
Constraints.Builder()
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
|
||||
WorkManager.getInstance(context).enqueue(syncRequest)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
良好的应用架构应该遵循以下原则:
|
||||
|
||||
1. **关注点分离**:每个组件只负责一个职责
|
||||
2. **依赖倒置**:依赖接口而非实现
|
||||
3. **分层架构**:清晰的分层结构,便于维护和测试
|
||||
4. **模块化设计**:功能模块化,提高可复用性
|
||||
5. **依赖管理**:使用依赖注入,管理依赖关系
|
||||
6. **架构模式**:根据项目需求选择合适的架构模式
|
||||
7. **Jetpack组件**:充分利用Jetpack架构组件
|
||||
|
||||
通过遵循这些最佳实践,可以构建出高质量、可维护、可扩展的Android应用。
|
||||
|
||||
@@ -1,10 +1,716 @@
|
||||
# 性能优化最佳实践
|
||||
|
||||
## 待补充内容
|
||||
性能优化是Android应用开发的重要环节,直接影响用户体验和应用质量。本文档介绍Android应用性能优化的最佳实践。
|
||||
|
||||
- 性能优化策略
|
||||
- 启动优化实践
|
||||
- 内存优化实践
|
||||
- 布局优化实践
|
||||
- 网络优化实践
|
||||
- 性能监控
|
||||
## 目录
|
||||
|
||||
- [性能优化策略](#性能优化策略)
|
||||
- [启动优化实践](#启动优化实践)
|
||||
- [内存优化实践](#内存优化实践)
|
||||
- [布局优化实践](#布局优化实践)
|
||||
- [网络优化实践](#网络优化实践)
|
||||
- [性能监控](#性能监控)
|
||||
|
||||
---
|
||||
|
||||
## 性能优化策略
|
||||
|
||||
### 1. 性能优化原则
|
||||
|
||||
#### 测量优先
|
||||
|
||||
```kotlin
|
||||
// ✅ 好的做法:先测量,再优化
|
||||
class PerformanceMonitor {
|
||||
fun measureTime(block: () -> Unit): Long {
|
||||
val startTime = System.currentTimeMillis()
|
||||
block()
|
||||
return System.currentTimeMillis() - startTime
|
||||
}
|
||||
|
||||
fun measureMemory(block: () -> Unit): Long {
|
||||
val runtime = Runtime.getRuntime()
|
||||
val beforeMemory = runtime.totalMemory() - runtime.freeMemory()
|
||||
block()
|
||||
val afterMemory = runtime.totalMemory() - runtime.freeMemory()
|
||||
return afterMemory - beforeMemory
|
||||
}
|
||||
}
|
||||
|
||||
// 使用
|
||||
val monitor = PerformanceMonitor()
|
||||
val time = monitor.measureTime {
|
||||
// 执行代码
|
||||
}
|
||||
Log.d("Performance", "执行时间: ${time}ms")
|
||||
```
|
||||
|
||||
#### 优化关键路径
|
||||
|
||||
```kotlin
|
||||
// 识别关键路径并优化
|
||||
class AppInitializer {
|
||||
fun initialize() {
|
||||
// 关键路径:必须立即初始化
|
||||
initCriticalComponents()
|
||||
|
||||
// 非关键路径:可以延迟初始化
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
initNonCriticalComponents()
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 性能指标
|
||||
|
||||
#### 关键指标
|
||||
|
||||
```kotlin
|
||||
// 启动时间
|
||||
class StartupTimeTracker {
|
||||
private var appStartTime: Long = 0
|
||||
|
||||
fun onAppStart() {
|
||||
appStartTime = System.currentTimeMillis()
|
||||
}
|
||||
|
||||
fun onFirstFrame() {
|
||||
val startupTime = System.currentTimeMillis() - appStartTime
|
||||
Log.d("Performance", "启动时间: ${startupTime}ms")
|
||||
}
|
||||
}
|
||||
|
||||
// 内存使用
|
||||
class MemoryTracker {
|
||||
fun getMemoryUsage(): MemoryInfo {
|
||||
val runtime = Runtime.getRuntime()
|
||||
val totalMemory = runtime.totalMemory()
|
||||
val freeMemory = runtime.freeMemory()
|
||||
val usedMemory = totalMemory - freeMemory
|
||||
|
||||
return MemoryInfo(
|
||||
total = totalMemory,
|
||||
used = usedMemory,
|
||||
free = freeMemory
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 启动优化实践
|
||||
|
||||
### 1. Application优化
|
||||
|
||||
#### 延迟初始化
|
||||
|
||||
```kotlin
|
||||
// ✅ 好的做法:延迟初始化非关键组件
|
||||
class MyApplication : Application() {
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
// 关键初始化:必须立即执行
|
||||
initCriticalComponents()
|
||||
|
||||
// 非关键初始化:延迟执行
|
||||
initNonCriticalComponentsAsync()
|
||||
}
|
||||
|
||||
private fun initCriticalComponents() {
|
||||
// 初始化关键组件
|
||||
CrashReporting.init(this)
|
||||
}
|
||||
|
||||
private fun initNonCriticalComponentsAsync() {
|
||||
// 使用后台线程初始化
|
||||
Thread {
|
||||
// 初始化非关键组件
|
||||
Analytics.init(this)
|
||||
ImageLoader.init(this)
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 使用App Startup
|
||||
|
||||
```kotlin
|
||||
// 使用App Startup统一管理初始化
|
||||
class MyInitializer : Initializer<Unit> {
|
||||
override fun create(context: Context) {
|
||||
// 初始化组件
|
||||
Analytics.init(context)
|
||||
}
|
||||
|
||||
override fun dependencies(): List<Class<out Initializer<*>>> {
|
||||
// 定义依赖关系
|
||||
return listOf(OtherInitializer::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
// 在AndroidManifest.xml中注册
|
||||
// <provider
|
||||
// android:name="androidx.startup.InitializationProvider"
|
||||
// android:authorities="${applicationId}.androidx-startup"
|
||||
// android:exported="false"
|
||||
// tools:node="merge">
|
||||
// <meta-data
|
||||
// android:name="com.example.MyInitializer"
|
||||
// android:value="androidx.startup" />
|
||||
// </provider>
|
||||
```
|
||||
|
||||
### 2. 首屏优化
|
||||
|
||||
#### 减少首屏布局复杂度
|
||||
|
||||
```xml
|
||||
<!-- ✅ 好的做法:简化首屏布局 -->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<!-- 只显示关键内容 -->
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="欢迎"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<!-- ❌ 不好的做法:首屏包含过多内容 -->
|
||||
```
|
||||
|
||||
#### 使用ViewStub延迟加载
|
||||
|
||||
```xml
|
||||
<!-- 使用ViewStub延迟加载非关键视图 -->
|
||||
<ViewStub
|
||||
android:id="@+id/viewStub"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout="@layout/expensive_view"/>
|
||||
```
|
||||
|
||||
```kotlin
|
||||
// 在需要时加载
|
||||
class MainActivity : AppCompatActivity() {
|
||||
private lateinit var viewStub: ViewStub
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
viewStub = findViewById(R.id.viewStub)
|
||||
|
||||
// 延迟加载
|
||||
button.setOnClickListener {
|
||||
if (viewStub.parent != null) {
|
||||
viewStub.inflate()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 启动时间测量
|
||||
|
||||
#### 使用Trace API
|
||||
|
||||
```kotlin
|
||||
// 使用Trace API标记启动流程
|
||||
class MainActivity : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
Trace.beginSection("MainActivity.onCreate")
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
Trace.beginSection("initViews")
|
||||
initViews()
|
||||
Trace.endSection()
|
||||
|
||||
Trace.beginSection("loadData")
|
||||
loadData()
|
||||
Trace.endSection()
|
||||
|
||||
Trace.endSection()
|
||||
}
|
||||
}
|
||||
|
||||
// 使用Systrace分析
|
||||
// python systrace.py -t 10 -o trace.html sched freq idle am wm gfx view binder_driver hal dalvik camera input res
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 内存优化实践
|
||||
|
||||
### 1. 内存泄漏预防
|
||||
|
||||
#### 避免静态引用
|
||||
|
||||
```kotlin
|
||||
// ❌ 不好的做法:静态引用Context
|
||||
object AppContext {
|
||||
var context: Context? = null // 可能导致内存泄漏
|
||||
}
|
||||
|
||||
// ✅ 好的做法:使用Application Context
|
||||
object AppContext {
|
||||
fun getContext(): Context {
|
||||
return MyApplication.instance
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 正确处理生命周期
|
||||
|
||||
```kotlin
|
||||
// ✅ 好的做法:正确处理生命周期
|
||||
class MainActivity : AppCompatActivity() {
|
||||
private var handler: Handler? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
// 使用静态内部类+WeakReference
|
||||
handler = MyHandler(this)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
handler?.removeCallbacksAndMessages(null)
|
||||
handler = null
|
||||
}
|
||||
|
||||
private class MyHandler(activity: MainActivity) : Handler(Looper.getMainLooper()) {
|
||||
private val activityRef = WeakReference(activity)
|
||||
|
||||
override fun handleMessage(msg: Message) {
|
||||
activityRef.get()?.let {
|
||||
// 处理消息
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 内存使用优化
|
||||
|
||||
#### 使用对象池
|
||||
|
||||
```kotlin
|
||||
// 对象池:复用对象,减少GC
|
||||
class ObjectPool<T>(private val factory: () -> T, private val maxSize: Int = 10) {
|
||||
private val pool = mutableListOf<T>()
|
||||
|
||||
fun acquire(): T {
|
||||
return if (pool.isNotEmpty()) {
|
||||
pool.removeAt(pool.size - 1)
|
||||
} else {
|
||||
factory()
|
||||
}
|
||||
}
|
||||
|
||||
fun release(obj: T) {
|
||||
if (pool.size < maxSize) {
|
||||
pool.add(obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 使用
|
||||
val viewPool = ObjectPool({ View(context) })
|
||||
val view = viewPool.acquire()
|
||||
// 使用view
|
||||
viewPool.release(view)
|
||||
```
|
||||
|
||||
#### 优化图片加载
|
||||
|
||||
```kotlin
|
||||
// 使用Glide等图片加载库
|
||||
Glide.with(context)
|
||||
.load(imageUrl)
|
||||
.placeholder(R.drawable.placeholder)
|
||||
.error(R.drawable.error)
|
||||
.override(800, 600) // 指定尺寸
|
||||
.into(imageView)
|
||||
|
||||
// 使用WebP格式
|
||||
// WebP格式比PNG/JPG更小,加载更快
|
||||
|
||||
// 使用图片压缩
|
||||
fun compressBitmap(bitmap: Bitmap, quality: Int = 80): ByteArray {
|
||||
val stream = ByteArrayOutputStream()
|
||||
bitmap.compress(Bitmap.CompressFormat.WEBP, quality, stream)
|
||||
return stream.toByteArray()
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 内存监控
|
||||
|
||||
#### 使用LeakCanary
|
||||
|
||||
```gradle
|
||||
// build.gradle
|
||||
dependencies {
|
||||
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12'
|
||||
}
|
||||
```
|
||||
|
||||
```kotlin
|
||||
// 自动检测内存泄漏
|
||||
// LeakCanary会自动检测并报告内存泄漏
|
||||
```
|
||||
|
||||
#### 手动内存分析
|
||||
|
||||
```kotlin
|
||||
// 获取内存信息
|
||||
class MemoryAnalyzer {
|
||||
fun getMemoryInfo(): String {
|
||||
val runtime = Runtime.getRuntime()
|
||||
val maxMemory = runtime.maxMemory()
|
||||
val totalMemory = runtime.totalMemory()
|
||||
val freeMemory = runtime.freeMemory()
|
||||
val usedMemory = totalMemory - freeMemory
|
||||
|
||||
return """
|
||||
Max Memory: ${maxMemory / 1024 / 1024}MB
|
||||
Total Memory: ${totalMemory / 1024 / 1024}MB
|
||||
Used Memory: ${usedMemory / 1024 / 1024}MB
|
||||
Free Memory: ${freeMemory / 1024 / 1024}MB
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
fun dumpHeap(): File {
|
||||
val heapDumpFile = File(cacheDir, "heap_dump.hprof")
|
||||
Debug.dumpHprofData(heapDumpFile.absolutePath)
|
||||
return heapDumpFile
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 布局优化实践
|
||||
|
||||
### 1. 减少布局层级
|
||||
|
||||
#### 使用ConstraintLayout
|
||||
|
||||
```xml
|
||||
<!-- ✅ 好的做法:使用ConstraintLayout减少层级 -->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="标题"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="副标题"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/title"/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<!-- ❌ 不好的做法:嵌套过多 -->
|
||||
<LinearLayout>
|
||||
<LinearLayout>
|
||||
<TextView/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
```
|
||||
|
||||
### 2. 避免过度绘制
|
||||
|
||||
#### 减少背景绘制
|
||||
|
||||
```xml
|
||||
<!-- ✅ 好的做法:移除不必要的背景 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/white">
|
||||
<!-- 子视图不需要再设置背景 -->
|
||||
</LinearLayout>
|
||||
|
||||
<!-- ❌ 不好的做法:多层背景 -->
|
||||
<LinearLayout android:background="@color/background1">
|
||||
<LinearLayout android:background="@color/background2">
|
||||
<!-- 导致过度绘制 -->
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
```
|
||||
|
||||
#### 使用clipToPadding
|
||||
|
||||
```xml
|
||||
<!-- 使用clipToPadding减少绘制区域 -->
|
||||
<RecyclerView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:padding="16dp"/>
|
||||
```
|
||||
|
||||
### 3. RecyclerView优化
|
||||
|
||||
#### ViewHolder复用
|
||||
|
||||
```kotlin
|
||||
// ✅ 好的做法:正确实现ViewHolder
|
||||
class UserAdapter : RecyclerView.Adapter<UserAdapter.ViewHolder>() {
|
||||
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
val nameTextView: TextView = itemView.findViewById(R.id.name)
|
||||
val emailTextView: TextView = itemView.findViewById(R.id.email)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.item_user, parent, false)
|
||||
return ViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val user = users[position]
|
||||
holder.nameTextView.text = user.name
|
||||
holder.emailTextView.text = user.email
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 使用DiffUtil
|
||||
|
||||
```kotlin
|
||||
// 使用DiffUtil优化列表更新
|
||||
class UserDiffCallback(
|
||||
private val oldList: List<User>,
|
||||
private val newList: List<User>
|
||||
) : DiffUtil.Callback() {
|
||||
override fun getOldListSize() = oldList.size
|
||||
override fun getNewListSize() = newList.size
|
||||
|
||||
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
|
||||
return oldList[oldItemPosition].id == newList[newItemPosition].id
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
|
||||
return oldList[oldItemPosition] == newList[newItemPosition]
|
||||
}
|
||||
}
|
||||
|
||||
// 使用
|
||||
val diffResult = DiffUtil.calculateDiff(UserDiffCallback(oldList, newList))
|
||||
adapter.users = newList
|
||||
diffResult.dispatchUpdatesTo(adapter)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 网络优化实践
|
||||
|
||||
### 1. 请求优化
|
||||
|
||||
#### 请求合并
|
||||
|
||||
```kotlin
|
||||
// ✅ 好的做法:合并请求
|
||||
class ApiService {
|
||||
@GET("users")
|
||||
suspend fun getUsers(@Query("ids") ids: String): List<User>
|
||||
// 一次请求获取多个用户,而不是多次请求
|
||||
}
|
||||
|
||||
// ❌ 不好的做法:多次请求
|
||||
// for (id in userIds) {
|
||||
// apiService.getUser(id)
|
||||
// }
|
||||
```
|
||||
|
||||
#### 使用缓存
|
||||
|
||||
```kotlin
|
||||
// 使用OkHttp缓存
|
||||
val client = OkHttpClient.Builder()
|
||||
.cache(Cache(cacheDir, 10 * 1024 * 1024)) // 10MB缓存
|
||||
.build()
|
||||
|
||||
// 使用Retrofit缓存
|
||||
@Headers("Cache-Control: max-age=3600")
|
||||
@GET("users/{id}")
|
||||
suspend fun getUser(@Path("id") id: String): User
|
||||
```
|
||||
|
||||
### 2. 数据压缩
|
||||
|
||||
#### 使用Gzip
|
||||
|
||||
```kotlin
|
||||
// 服务器端启用Gzip压缩
|
||||
// 客户端自动处理Gzip响应
|
||||
// OkHttp自动支持Gzip解压
|
||||
```
|
||||
|
||||
#### 数据格式优化
|
||||
|
||||
```kotlin
|
||||
// 使用Protobuf替代JSON
|
||||
// Protobuf更小、更快
|
||||
data class User(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val email: String
|
||||
)
|
||||
|
||||
// 转换为Protobuf
|
||||
val userProto = UserProto.newBuilder()
|
||||
.setId(user.id)
|
||||
.setName(user.name)
|
||||
.setEmail(user.email)
|
||||
.build()
|
||||
```
|
||||
|
||||
### 3. 网络监控
|
||||
|
||||
#### 监控网络请求
|
||||
|
||||
```kotlin
|
||||
// 使用OkHttp Interceptor监控
|
||||
class NetworkMonitorInterceptor : Interceptor {
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
val request = chain.request()
|
||||
val startTime = System.currentTimeMillis()
|
||||
|
||||
val response = chain.proceed(request)
|
||||
|
||||
val duration = System.currentTimeMillis() - startTime
|
||||
Log.d("Network", "${request.url} - ${duration}ms")
|
||||
|
||||
return response
|
||||
}
|
||||
}
|
||||
|
||||
val client = OkHttpClient.Builder()
|
||||
.addInterceptor(NetworkMonitorInterceptor())
|
||||
.build()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 性能监控
|
||||
|
||||
### 1. 性能指标收集
|
||||
|
||||
#### 使用Firebase Performance
|
||||
|
||||
```gradle
|
||||
// build.gradle
|
||||
dependencies {
|
||||
implementation 'com.google.firebase:firebase-perf-ktx:20.4.1'
|
||||
}
|
||||
```
|
||||
|
||||
```kotlin
|
||||
// 监控自定义性能指标
|
||||
val trace = FirebasePerformance.getInstance().newTrace("load_user_data")
|
||||
trace.start()
|
||||
|
||||
// 执行操作
|
||||
loadUserData()
|
||||
|
||||
trace.stop()
|
||||
```
|
||||
|
||||
#### 自定义性能监控
|
||||
|
||||
```kotlin
|
||||
class PerformanceTracker {
|
||||
private val metrics = mutableMapOf<String, Long>()
|
||||
|
||||
fun startTrace(name: String) {
|
||||
metrics[name] = System.currentTimeMillis()
|
||||
}
|
||||
|
||||
fun endTrace(name: String) {
|
||||
val startTime = metrics[name]
|
||||
if (startTime != null) {
|
||||
val duration = System.currentTimeMillis() - startTime
|
||||
Log.d("Performance", "$name: ${duration}ms")
|
||||
metrics.remove(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 使用
|
||||
val tracker = PerformanceTracker()
|
||||
tracker.startTrace("load_data")
|
||||
// 执行操作
|
||||
tracker.endTrace("load_data")
|
||||
```
|
||||
|
||||
### 2. 性能分析工具
|
||||
|
||||
#### 使用Android Profiler
|
||||
|
||||
```kotlin
|
||||
// Android Studio Profiler可以监控:
|
||||
// - CPU使用率
|
||||
// - 内存使用情况
|
||||
// - 网络请求
|
||||
// - 电量消耗
|
||||
```
|
||||
|
||||
#### 使用Systrace
|
||||
|
||||
```bash
|
||||
# 使用Systrace分析性能
|
||||
python systrace.py -t 10 -o trace.html sched freq idle am wm gfx view binder_driver hal dalvik camera input res
|
||||
|
||||
# 在代码中标记
|
||||
Trace.beginSection("my_section")
|
||||
// 执行代码
|
||||
Trace.endSection()
|
||||
```
|
||||
|
||||
#### 使用Perfetto
|
||||
|
||||
```kotlin
|
||||
// Perfetto是更强大的性能分析工具
|
||||
// 可以分析:
|
||||
// - CPU调度
|
||||
// - 内存分配
|
||||
// - 文件I/O
|
||||
// - 网络活动
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
性能优化是一个持续的过程,需要:
|
||||
|
||||
1. **测量优先**:先测量性能,找出瓶颈
|
||||
2. **优化关键路径**:优先优化影响用户体验的关键路径
|
||||
3. **启动优化**:减少启动时间,提升用户体验
|
||||
4. **内存优化**:避免内存泄漏,优化内存使用
|
||||
5. **布局优化**:减少布局层级,避免过度绘制
|
||||
6. **网络优化**:优化网络请求,使用缓存
|
||||
7. **性能监控**:持续监控性能指标,及时发现问题
|
||||
|
||||
通过遵循这些最佳实践,可以显著提升应用的性能和用户体验。
|
||||
|
||||
@@ -1,9 +1,789 @@
|
||||
# 用户体验最佳实践
|
||||
|
||||
## 待补充内容
|
||||
用户体验(UX)是Android应用成功的关键因素。良好的用户体验能够提升用户满意度、增加用户留存率,并提升应用在应用商店的评分。本文档介绍Android应用开发中的用户体验最佳实践。
|
||||
|
||||
- UX设计原则
|
||||
- 界面设计
|
||||
- 交互设计
|
||||
- 可访问性
|
||||
- 国际化
|
||||
## 目录
|
||||
|
||||
- [UX设计原则](#ux设计原则)
|
||||
- [界面设计](#界面设计)
|
||||
- [交互设计](#交互设计)
|
||||
- [可访问性](#可访问性)
|
||||
- [国际化](#国际化)
|
||||
- [性能与体验](#性能与体验)
|
||||
|
||||
---
|
||||
|
||||
## UX设计原则
|
||||
|
||||
### 1. 以用户为中心
|
||||
|
||||
#### 理解用户需求
|
||||
|
||||
```kotlin
|
||||
// 在设计功能前,先理解用户需求
|
||||
// 1. 用户画像分析
|
||||
// 2. 用户场景分析
|
||||
// 3. 用户痛点识别
|
||||
|
||||
// 示例:电商应用
|
||||
// 用户需求:快速找到商品并完成购买
|
||||
// 设计要点:
|
||||
// - 搜索功能突出
|
||||
// - 商品信息清晰
|
||||
// - 购买流程简化
|
||||
```
|
||||
|
||||
#### 用户反馈机制
|
||||
|
||||
```kotlin
|
||||
// 建立用户反馈渠道
|
||||
class FeedbackManager {
|
||||
fun collectUserFeedback(feedback: String) {
|
||||
// 收集用户反馈
|
||||
Analytics.logEvent("user_feedback", mapOf("content" to feedback))
|
||||
}
|
||||
|
||||
fun showFeedbackDialog(context: Context) {
|
||||
// 显示反馈对话框
|
||||
MaterialAlertDialogBuilder(context)
|
||||
.setTitle("反馈建议")
|
||||
.setView(EditText(context))
|
||||
.setPositiveButton("提交") { _, _ ->
|
||||
collectUserFeedback()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 简洁性原则
|
||||
|
||||
#### 界面简洁
|
||||
|
||||
```xml
|
||||
<!-- ✅ 好的设计:简洁清晰 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="标题"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="描述信息"
|
||||
android:textSize="14sp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- ❌ 不好的设计:信息过载 -->
|
||||
<!-- 避免在一个界面显示过多信息 -->
|
||||
```
|
||||
|
||||
#### 操作简化
|
||||
|
||||
```kotlin
|
||||
// ✅ 好的设计:简化操作流程
|
||||
class CheckoutActivity : AppCompatActivity() {
|
||||
fun proceedToPayment() {
|
||||
// 一键支付,减少步骤
|
||||
if (validateOrder()) {
|
||||
startActivity(Intent(this, PaymentActivity::class.java))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ❌ 不好的设计:操作步骤过多
|
||||
// 避免让用户进行过多步骤才能完成操作
|
||||
```
|
||||
|
||||
### 3. 一致性原则
|
||||
|
||||
#### 视觉一致性
|
||||
|
||||
```kotlin
|
||||
// 使用统一的主题和样式
|
||||
// styles.xml
|
||||
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight">
|
||||
<item name="colorPrimary">@color/primary</item>
|
||||
<item name="colorPrimaryVariant">@color/primary_variant</item>
|
||||
<item name="colorSecondary">@color/secondary</item>
|
||||
<item name="textAppearanceHeadline1">@style/TextAppearance.Headline1</item>
|
||||
<item name="textAppearanceBody1">@style/TextAppearance.Body1</item>
|
||||
</style>
|
||||
```
|
||||
|
||||
#### 交互一致性
|
||||
|
||||
```kotlin
|
||||
// 统一的交互模式
|
||||
class NavigationHelper {
|
||||
companion object {
|
||||
// 统一的导航方式
|
||||
fun navigateToDetail(context: Context, itemId: String) {
|
||||
val intent = Intent(context, DetailActivity::class.java)
|
||||
intent.putExtra("item_id", itemId)
|
||||
context.startActivity(intent)
|
||||
}
|
||||
|
||||
// 统一的返回方式
|
||||
fun handleBackPress(activity: Activity): Boolean {
|
||||
if (activity.supportFragmentManager.backStackEntryCount > 0) {
|
||||
activity.supportFragmentManager.popBackStack()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 反馈原则
|
||||
|
||||
#### 操作反馈
|
||||
|
||||
```kotlin
|
||||
// 用户操作后提供即时反馈
|
||||
class UserFeedbackHelper {
|
||||
fun showLoading(context: Context) {
|
||||
// 显示加载状态
|
||||
ProgressDialog.show(context, "加载中", "请稍候...")
|
||||
}
|
||||
|
||||
fun showSuccess(context: Context, message: String) {
|
||||
// 显示成功提示
|
||||
Snackbar.make(
|
||||
findViewById(android.R.id.content),
|
||||
message,
|
||||
Snackbar.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
|
||||
fun showError(context: Context, message: String) {
|
||||
// 显示错误提示
|
||||
MaterialAlertDialogBuilder(context)
|
||||
.setTitle("错误")
|
||||
.setMessage(message)
|
||||
.setPositiveButton("确定", null)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 状态反馈
|
||||
|
||||
```kotlin
|
||||
// 使用状态指示器
|
||||
class StatusIndicator {
|
||||
fun showStatus(view: View, status: Status) {
|
||||
when (status) {
|
||||
Status.LOADING -> {
|
||||
view.alpha = 0.5f
|
||||
view.isEnabled = false
|
||||
}
|
||||
Status.SUCCESS -> {
|
||||
view.alpha = 1.0f
|
||||
view.isEnabled = true
|
||||
}
|
||||
Status.ERROR -> {
|
||||
view.alpha = 0.5f
|
||||
view.isEnabled = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 界面设计
|
||||
|
||||
### 1. Material Design
|
||||
|
||||
#### Material Design组件
|
||||
|
||||
```xml
|
||||
<!-- 使用Material Design组件 -->
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
app:cardCornerRadius="8dp"
|
||||
app:cardElevation="4dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="标题"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"/>
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="描述"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"/>
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
```
|
||||
|
||||
#### Material主题
|
||||
|
||||
```xml
|
||||
<!-- themes.xml -->
|
||||
<resources>
|
||||
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
||||
<!-- 主色调 -->
|
||||
<item name="colorPrimary">@color/primary</item>
|
||||
<item name="colorPrimaryVariant">@color/primary_variant</item>
|
||||
<item name="colorOnPrimary">@color/on_primary</item>
|
||||
|
||||
<!-- 次要色调 -->
|
||||
<item name="colorSecondary">@color/secondary</item>
|
||||
<item name="colorSecondaryVariant">@color/secondary_variant</item>
|
||||
<item name="colorOnSecondary">@color/on_secondary</item>
|
||||
|
||||
<!-- 背景色 -->
|
||||
<item name="android:colorBackground">@color/background</item>
|
||||
<item name="colorSurface">@color/surface</item>
|
||||
|
||||
<!-- 错误色 -->
|
||||
<item name="colorError">@color/error</item>
|
||||
<item name="colorOnError">@color/on_error</item>
|
||||
</style>
|
||||
</resources>
|
||||
```
|
||||
|
||||
### 2. 响应式设计
|
||||
|
||||
#### 多屏幕适配
|
||||
|
||||
```xml
|
||||
<!-- 使用ConstraintLayout实现响应式布局 -->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="标题"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:layout_margin="16dp"/>
|
||||
|
||||
<RecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/title"
|
||||
app:layout_constraintBottom_toBottomOf="parent"/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
```
|
||||
|
||||
#### 横竖屏适配
|
||||
|
||||
```kotlin
|
||||
// 横竖屏布局适配
|
||||
class MainActivity : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
// 根据屏幕方向加载不同布局
|
||||
val layoutRes = if (isLandscape()) {
|
||||
R.layout.activity_main_landscape
|
||||
} else {
|
||||
R.layout.activity_main_portrait
|
||||
}
|
||||
|
||||
setContentView(layoutRes)
|
||||
}
|
||||
|
||||
private fun isLandscape(): Boolean {
|
||||
return resources.configuration.orientation ==
|
||||
Configuration.ORIENTATION_LANDSCAPE
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 布局优化
|
||||
|
||||
#### 减少布局层级
|
||||
|
||||
```xml
|
||||
<!-- ❌ 不好的设计:嵌套过多 -->
|
||||
<LinearLayout>
|
||||
<LinearLayout>
|
||||
<LinearLayout>
|
||||
<TextView/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- ✅ 好的设计:使用ConstraintLayout减少层级 -->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout>
|
||||
<TextView
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
```
|
||||
|
||||
#### 使用ViewStub延迟加载
|
||||
|
||||
```xml
|
||||
<!-- 使用ViewStub延迟加载非关键视图 -->
|
||||
<ViewStub
|
||||
android:id="@+id/viewStub"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout="@layout/expensive_view"/>
|
||||
```
|
||||
|
||||
```kotlin
|
||||
// 在需要时加载ViewStub
|
||||
class MainActivity : AppCompatActivity() {
|
||||
private lateinit var viewStub: ViewStub
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
viewStub = findViewById(R.id.viewStub)
|
||||
|
||||
// 延迟加载
|
||||
button.setOnClickListener {
|
||||
if (viewStub.parent != null) {
|
||||
viewStub.inflate()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 交互设计
|
||||
|
||||
### 1. 导航设计
|
||||
|
||||
#### 底部导航
|
||||
|
||||
```xml
|
||||
<!-- 使用BottomNavigationView -->
|
||||
<com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
android:id="@+id/bottomNavigation"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:menu="@menu/bottom_navigation"
|
||||
app:itemIconTint="@color/bottom_nav_color"
|
||||
app:itemTextColor="@color/bottom_nav_color"/>
|
||||
```
|
||||
|
||||
```kotlin
|
||||
// 处理底部导航点击
|
||||
bottomNavigation.setOnItemSelectedListener { item ->
|
||||
when (item.itemId) {
|
||||
R.id.nav_home -> {
|
||||
// 导航到首页
|
||||
navigateToHome()
|
||||
true
|
||||
}
|
||||
R.id.nav_search -> {
|
||||
// 导航到搜索
|
||||
navigateToSearch()
|
||||
true
|
||||
}
|
||||
R.id.nav_profile -> {
|
||||
// 导航到个人中心
|
||||
navigateToProfile()
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 导航组件
|
||||
|
||||
```kotlin
|
||||
// 使用Navigation Component
|
||||
class MainActivity : AppCompatActivity() {
|
||||
private lateinit var navController: NavController
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
val navHostFragment = supportFragmentManager
|
||||
.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
|
||||
navController = navHostFragment.navController
|
||||
|
||||
// 设置导航监听
|
||||
navController.addOnDestinationChangedListener { _, destination, _ ->
|
||||
// 根据目标更新UI
|
||||
updateUI(destination.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 手势交互
|
||||
|
||||
#### 滑动操作
|
||||
|
||||
```kotlin
|
||||
// 实现滑动删除
|
||||
class SwipeToDeleteCallback(
|
||||
private val adapter: RecyclerView.Adapter<*>
|
||||
) : ItemTouchHelper.SimpleCallback(
|
||||
0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
|
||||
) {
|
||||
override fun onMove(
|
||||
recyclerView: RecyclerView,
|
||||
viewHolder: RecyclerView.ViewHolder,
|
||||
target: RecyclerView.ViewHolder
|
||||
): Boolean = false
|
||||
|
||||
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
||||
val position = viewHolder.adapterPosition
|
||||
// 删除项
|
||||
adapter.notifyItemRemoved(position)
|
||||
}
|
||||
}
|
||||
|
||||
// 使用
|
||||
val itemTouchHelper = ItemTouchHelper(SwipeToDeleteCallback(adapter))
|
||||
itemTouchHelper.attachToRecyclerView(recyclerView)
|
||||
```
|
||||
|
||||
#### 点击反馈
|
||||
|
||||
```kotlin
|
||||
// 使用Ripple效果提供点击反馈
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"/>
|
||||
```
|
||||
|
||||
### 3. 动画设计
|
||||
|
||||
#### 过渡动画
|
||||
|
||||
```kotlin
|
||||
// Activity过渡动画
|
||||
class DetailActivity : AppCompatActivity() {
|
||||
companion object {
|
||||
fun start(context: Context, item: Item, imageView: ImageView) {
|
||||
val intent = Intent(context, DetailActivity::class.java)
|
||||
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(
|
||||
context as Activity,
|
||||
imageView,
|
||||
"item_image"
|
||||
)
|
||||
context.startActivity(intent, options.toBundle())
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```xml
|
||||
<!-- 在DetailActivity中设置共享元素 -->
|
||||
<ImageView
|
||||
android:id="@+id/itemImage"
|
||||
android:transitionName="item_image"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"/>
|
||||
```
|
||||
|
||||
#### 微交互动画
|
||||
|
||||
```kotlin
|
||||
// 使用属性动画
|
||||
fun animateButtonClick(button: View) {
|
||||
button.animate()
|
||||
.scaleX(0.9f)
|
||||
.scaleY(0.9f)
|
||||
.setDuration(100)
|
||||
.withEndAction {
|
||||
button.animate()
|
||||
.scaleX(1.0f)
|
||||
.scaleY(1.0f)
|
||||
.setDuration(100)
|
||||
.start()
|
||||
}
|
||||
.start()
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 可访问性
|
||||
|
||||
### 1. 内容标签
|
||||
|
||||
#### 添加内容描述
|
||||
|
||||
```xml
|
||||
<!-- 为视图添加内容描述 -->
|
||||
<ImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/icon_description"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/submitButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/submit"
|
||||
android:contentDescription="@string/submit_button_description"/>
|
||||
```
|
||||
|
||||
```kotlin
|
||||
// 动态设置内容描述
|
||||
imageView.contentDescription = getString(R.string.icon_description)
|
||||
button.contentDescription = getString(R.string.submit_button_description)
|
||||
```
|
||||
|
||||
### 2. 触摸目标大小
|
||||
|
||||
#### 最小触摸区域
|
||||
|
||||
```xml
|
||||
<!-- 确保触摸目标至少48dp x 48dp -->
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="48dp"
|
||||
android:minHeight="48dp"
|
||||
android:padding="12dp"/>
|
||||
```
|
||||
|
||||
### 3. 文字大小
|
||||
|
||||
#### 支持文字缩放
|
||||
|
||||
```xml
|
||||
<!-- 使用sp单位,支持系统文字缩放 -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="16sp"/>
|
||||
|
||||
<!-- 避免使用dp单位设置文字大小 -->
|
||||
<!-- ❌ android:textSize="16dp" -->
|
||||
```
|
||||
|
||||
### 4. 颜色对比度
|
||||
|
||||
#### 确保足够的对比度
|
||||
|
||||
```kotlin
|
||||
// 检查颜色对比度
|
||||
fun checkContrastRatio(foreground: Int, background: Int): Boolean {
|
||||
val foregroundLuminance = calculateLuminance(foreground)
|
||||
val backgroundLuminance = calculateLuminance(background)
|
||||
|
||||
val contrastRatio = if (foregroundLuminance > backgroundLuminance) {
|
||||
(foregroundLuminance + 0.05) / (backgroundLuminance + 0.05)
|
||||
} else {
|
||||
(backgroundLuminance + 0.05) / (foregroundLuminance + 0.05)
|
||||
}
|
||||
|
||||
// WCAG AA标准:文字至少4.5:1,大文字至少3:1
|
||||
return contrastRatio >= 4.5
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 国际化
|
||||
|
||||
### 1. 字符串资源
|
||||
|
||||
#### 外部化字符串
|
||||
|
||||
```xml
|
||||
<!-- strings.xml (默认) -->
|
||||
<resources>
|
||||
<string name="welcome_message">Welcome</string>
|
||||
<string name="button_submit">Submit</string>
|
||||
</resources>
|
||||
|
||||
<!-- values-zh/strings.xml (中文) -->
|
||||
<resources>
|
||||
<string name="welcome_message">欢迎</string>
|
||||
<string name="button_submit">提交</string>
|
||||
</resources>
|
||||
|
||||
<!-- values-es/strings.xml (西班牙语) -->
|
||||
<resources>
|
||||
<string name="welcome_message">Bienvenido</string>
|
||||
<string name="button_submit">Enviar</string>
|
||||
</resources>
|
||||
```
|
||||
|
||||
```kotlin
|
||||
// 使用字符串资源
|
||||
textView.text = getString(R.string.welcome_message)
|
||||
|
||||
// 带参数的字符串
|
||||
// strings.xml
|
||||
<string name="welcome_user">Welcome, %1$s!</string>
|
||||
|
||||
textView.text = getString(R.string.welcome_user, userName)
|
||||
```
|
||||
|
||||
### 2. 布局适配
|
||||
|
||||
#### RTL支持
|
||||
|
||||
```xml
|
||||
<!-- 使用start/end替代left/right -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:gravity="start"/>
|
||||
|
||||
<!-- ❌ 避免使用left/right -->
|
||||
<!-- android:layout_marginLeft="16dp" -->
|
||||
```
|
||||
|
||||
```kotlin
|
||||
// 在代码中使用start/end
|
||||
view.setPaddingRelative(
|
||||
paddingStart,
|
||||
paddingTop,
|
||||
paddingEnd,
|
||||
paddingBottom
|
||||
)
|
||||
```
|
||||
|
||||
### 3. 日期和数字格式
|
||||
|
||||
#### 本地化格式
|
||||
|
||||
```kotlin
|
||||
// 使用本地化的日期格式
|
||||
fun formatDate(date: Date, locale: Locale): String {
|
||||
val dateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM, locale)
|
||||
return dateFormat.format(date)
|
||||
}
|
||||
|
||||
// 使用本地化的数字格式
|
||||
fun formatNumber(number: Number, locale: Locale): String {
|
||||
val numberFormat = NumberFormat.getNumberInstance(locale)
|
||||
return numberFormat.format(number)
|
||||
}
|
||||
|
||||
// 使用本地化的货币格式
|
||||
fun formatCurrency(amount: Double, locale: Locale): String {
|
||||
val currencyFormat = NumberFormat.getCurrencyInstance(locale)
|
||||
return currencyFormat.format(amount)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 性能与体验
|
||||
|
||||
### 1. 启动优化
|
||||
|
||||
#### 减少启动时间
|
||||
|
||||
```kotlin
|
||||
// 延迟初始化非关键组件
|
||||
class MyApplication : Application() {
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
// 关键初始化
|
||||
initCriticalComponents()
|
||||
|
||||
// 延迟初始化非关键组件
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
initNonCriticalComponents()
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 流畅度优化
|
||||
|
||||
#### 避免主线程阻塞
|
||||
|
||||
```kotlin
|
||||
// 使用协程处理耗时操作
|
||||
class MainActivity : AppCompatActivity() {
|
||||
private val viewModelScope = ViewModelScope()
|
||||
|
||||
fun loadData() {
|
||||
viewModelScope.launch {
|
||||
// 在后台线程执行
|
||||
val data = withContext(Dispatchers.IO) {
|
||||
repository.loadData()
|
||||
}
|
||||
|
||||
// 在主线程更新UI
|
||||
withContext(Dispatchers.Main) {
|
||||
updateUI(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 加载状态
|
||||
|
||||
#### 优雅的加载体验
|
||||
|
||||
```kotlin
|
||||
// 使用Skeleton Screen
|
||||
class SkeletonHelper {
|
||||
fun showSkeleton(view: View) {
|
||||
val skeleton = SkeletonScreen.Builder()
|
||||
.load(R.layout.skeleton_layout)
|
||||
.color(R.color.skeleton_color)
|
||||
.angle(0)
|
||||
.duration(1000)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
良好的用户体验需要从多个方面考虑:
|
||||
|
||||
1. **设计原则**:以用户为中心,保持简洁和一致
|
||||
2. **界面设计**:遵循Material Design,支持多屏幕适配
|
||||
3. **交互设计**:提供清晰的导航和流畅的交互
|
||||
4. **可访问性**:确保所有用户都能使用应用
|
||||
5. **国际化**:支持多语言和不同地区的用户
|
||||
6. **性能优化**:确保应用流畅运行,提供良好的体验
|
||||
|
||||
通过遵循这些最佳实践,可以创建出用户喜爱的高质量Android应用。
|
||||
|
||||
1241
docs/Obsidian笔记体系/Projects/saars开发/aiapply/未命名 1.md
Normal file
1241
docs/Obsidian笔记体系/Projects/saars开发/aiapply/未命名 1.md
Normal file
File diff suppressed because it is too large
Load Diff
183
docs/Obsidian笔记体系/Projects/saars开发/aiapply/未命名.md
Normal file
183
docs/Obsidian笔记体系/Projects/saars开发/aiapply/未命名.md
Normal file
@@ -0,0 +1,183 @@
|
||||
项目低代码智能体功能扩展
|
||||
|
||||
该项目可扩展低代码快速搭建智能体的功能吗
|
||||
|
||||
检查项目结构,评估是否可扩展低代码快速搭建智能体的功能。
|
||||
|
||||
继续查看项目架构,评估是否可扩展低代码智能体搭建功能。
|
||||
|
||||
查看 API 路由和核心组件,评估扩展能力:
|
||||
|
||||
基于对项目的分析,评估如下:
|
||||
|
||||
## 项目扩展低代码智能体搭建的可行性评估
|
||||
|
||||
### 当前项目优势
|
||||
|
||||
1. 模板系统
|
||||
|
||||
- 结构化模板(角色、任务、上下文、约束)
|
||||
|
||||
- 变量系统(文本、选择、数字、布尔等)
|
||||
|
||||
- 模板管理和分享
|
||||
|
||||
1. 多模型支持
|
||||
|
||||
- 支持 OpenAI、Anthropic、DeepSeek
|
||||
|
||||
- 单模型和批量测试
|
||||
|
||||
1. 部署能力
|
||||
|
||||
- 代码生成(Python、JavaScript、TypeScript)
|
||||
|
||||
- 多框架支持(FastAPI、Express 等)
|
||||
|
||||
1. 用户系统
|
||||
|
||||
- 认证、权限、模板归属
|
||||
|
||||
### 需要扩展的核心功能
|
||||
|
||||
文档中提到了“提示词链”概念,但尚未实现。要支持低代码智能体搭建,需要添加:
|
||||
|
||||
#### 1. 工作流编排系统
|
||||
|
||||
- 将多个模板串联成工作流
|
||||
|
||||
- 节点连接(顺序、并行、条件分支)
|
||||
|
||||
- 数据流转(上一步输出作为下一步输入)
|
||||
|
||||
#### 2. 可视化工作流编辑器
|
||||
|
||||
- 拖拽式节点编辑器
|
||||
|
||||
- 节点类型:模板节点、条件节点、工具节点、数据节点
|
||||
|
||||
- 连线表示数据流
|
||||
|
||||
#### 3. 智能体运行时引擎
|
||||
|
||||
- 工作流执行引擎
|
||||
|
||||
- 状态管理(持久化、恢复)
|
||||
|
||||
- 错误处理和重试
|
||||
|
||||
#### 4. 工具集成能力
|
||||
|
||||
- 外部 API 调用
|
||||
|
||||
- 数据库操作
|
||||
|
||||
- 文件处理
|
||||
|
||||
- 定时任务
|
||||
|
||||
#### 5. 触发器系统
|
||||
|
||||
- Webhook 触发
|
||||
|
||||
- 定时触发
|
||||
|
||||
- 事件触发
|
||||
|
||||
- 手动触发
|
||||
|
||||
### 实现建议
|
||||
|
||||
基于现有架构,建议按以下步骤扩展:
|
||||
|
||||
#### 阶段一:工作流基础(核心)
|
||||
|
||||
1. 数据库扩展:添加工作流表、节点表、连接表
|
||||
|
||||
2. 工作流 API:创建、执行、管理
|
||||
|
||||
3. 简单执行引擎:顺序执行多个模板
|
||||
|
||||
#### 阶段二:可视化编辑器
|
||||
|
||||
1. 使用 React Flow 或类似库
|
||||
|
||||
2. 节点组件:模板节点、条件节点
|
||||
|
||||
3. 连线与数据映射
|
||||
|
||||
#### 阶段三:高级功能
|
||||
|
||||
1. 条件分支与循环
|
||||
|
||||
2. 工具集成(API、数据库)
|
||||
|
||||
3. 触发器系统
|
||||
|
||||
### 技术实现路径
|
||||
|
||||
// 1. 扩展数据库模型(Prisma Schema)
|
||||
|
||||
model Workflow {
|
||||
|
||||
id String @id @default(cuid())
|
||||
|
||||
name String
|
||||
|
||||
description String?
|
||||
|
||||
nodes WorkflowNode[]
|
||||
|
||||
edges WorkflowEdge[]
|
||||
|
||||
authorId String
|
||||
|
||||
// ...
|
||||
|
||||
}
|
||||
|
||||
model WorkflowNode {
|
||||
|
||||
id String @id @default(cuid())
|
||||
|
||||
workflowId String
|
||||
|
||||
type String // 'template' | 'condition' | 'tool' | 'data'
|
||||
|
||||
templateId String? // 如果是模板节点
|
||||
|
||||
config String // JSON 配置
|
||||
|
||||
// ...
|
||||
|
||||
}
|
||||
|
||||
// 2. 工作流执行引擎
|
||||
|
||||
class WorkflowEngine {
|
||||
|
||||
async execute(workflowId: string, input: any) {
|
||||
|
||||
// 执行工作流逻辑
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
### 结论
|
||||
|
||||
- 可行性:高。项目具备良好基础,架构清晰,易于扩展。
|
||||
|
||||
- 工作量:中等。核心工作流功能约 2-3 周,完整低代码平台约 2-3 个月。
|
||||
|
||||
- 建议:先实现工作流编排和执行引擎,再逐步添加可视化编辑器和高级功能。
|
||||
|
||||
需要我帮你开始实现工作流系统吗?我可以:
|
||||
|
||||
1. 设计数据库模型
|
||||
|
||||
2. 创建工作流 API
|
||||
|
||||
3. 实现基础执行引擎
|
||||
|
||||
4. 构建可视化编辑器界面
|
||||
1475
docs/android面试/系统原理/WMS面试.md
Normal file
1475
docs/android面试/系统原理/WMS面试.md
Normal file
File diff suppressed because it is too large
Load Diff
1345
docs/dify/用户注册及添加成员.md
Normal file
1345
docs/dify/用户注册及添加成员.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -185,6 +185,8 @@ nav:
|
||||
- Obsidian笔记体系/Projects/知你-调测/知你--调测.md
|
||||
- Obsidian笔记体系/Daily/2026-01-14.md
|
||||
- Obsidian笔记体系/Daily/2026-01-15.md
|
||||
- Obsidian笔记体系/Projects/saars开发/aiapply/未命名 1.md
|
||||
- Obsidian笔记体系/Projects/saars开发/aiapply/未命名.md
|
||||
- gerrit:
|
||||
- gerrit/gerrit上传代码详细指南.md
|
||||
- gerrit/gerrit分支规范.md
|
||||
@@ -199,6 +201,7 @@ nav:
|
||||
- dify/使用dify,可以生成一个专项事务助手吗,比如公司正规化事务助手.md
|
||||
- dify/密码输入错误次数超过限制时的机制.md
|
||||
- dify/管理员账户.md
|
||||
- dify/用户注册及添加成员.md
|
||||
- git:
|
||||
- git/Git add . 后如何撤销.md
|
||||
- git/git同步远程分支总结.md
|
||||
@@ -285,6 +288,7 @@ nav:
|
||||
- android面试/项目经验/问题排查经验.md
|
||||
- android面试/项目经验/项目架构设计.md
|
||||
- android面试/技术面试问题回答.md
|
||||
- android面试/系统原理/WMS面试.md
|
||||
- Google开发文档体系:
|
||||
- Google开发文档体系/API参考/Android_API概览.md
|
||||
- Google开发文档体系/API参考/Jetpack_API.md
|
||||
|
||||
Reference in New Issue
Block a user