Android开发中的封装思路指导

面向对象编程中,封装是提升软件系统的手段

封装是Android开发中提高代码质量、可维护性和可复用性的关键。以下是系统化的封装指导思路:

一、分层架构思想

1. 清晰的分层结构

text 复制代码
┌─────────────────┐
│     UI层        │ ← Activity/Fragment/View
├─────────────────┤
│   ViewModel层   │ ← ViewModel/Presenter
├─────────────────┤
│   Domain层      │ ← 业务逻辑用例
├─────────────────┤
│   Data层        │ ← Repository/DataSource
├─────────────────┤
│   Model层       │ ← 数据实体/DTO
└─────────────────┘

2. 各层职责明确

  • UI层: 只处理界面展示和用户交互
  • ViewModel/Presenter层: 处理UI逻辑和状态管理
  • Domain层: 纯业务逻辑,不依赖Android框架
  • Data层: 数据获取和存储
  • Model层: 数据结构定义

二、模块化封装原则

1. 单一职责原则(SRP)

kotlin 复制代码
// ❌ 不推荐 - 职责过多
class UserManager {
    fun login() { /* 登录逻辑 */ }
    fun logout() { /* 登出逻辑 */ }
    fun saveToDatabase() { /* 数据库操作 */ }
    fun uploadToServer() { /* 网络请求 */ }
}

// ✅ 推荐 - 职责分离
class AuthService { /* 认证逻辑 */ }
class UserRepository { /* 数据操作 */ }
class NetworkService { /* 网络请求 */ }

2. 依赖倒置原则(DIP)

kotlin 复制代码
// 定义抽象接口
interface UserDataSource {
    suspend fun getUser(id: String): User
}

// 具体实现
class RemoteUserDataSource(
    private val apiService: ApiService
) : UserDataSource {
    override suspend fun getUser(id: String): User {
        return apiService.getUser(id)
    }
}

// 使用依赖注入
class UserRepository @Inject constructor(
    private val userDataSource: UserDataSource
)

三、常用组件封装策略

1. Base基类封装

kotlin 复制代码
abstract class BaseActivity<VM : ViewModel> : AppCompatActivity() {
    
    protected abstract val viewModel: VM
    protected abstract fun getLayoutId(): Int
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(getLayoutId())
        initView()
        initObserver()
        initData()
    }
    
    open fun initView() {}
    open fun initObserver() {}
    open fun initData() {}
    
    protected fun showLoading() { /* 统一加载框 */ }
    protected fun hideLoading() { /* 隐藏加载框 */ }
    protected fun showToast(message: String) { /* 统一Toast */ }
}

2. 工具类封装

kotlin 复制代码
object NetworkUtils {
    
    @SuppressLint("MissingPermission")
    fun isNetworkAvailable(context: Context): Boolean {
        val connectivityManager = context.getSystemService(
            Context.CONNECTIVITY_SERVICE
        ) as ConnectivityManager
        
        return connectivityManager.activeNetwork?.let { network ->
            connectivityManager.getNetworkCapabilities(network)?.let { capabilities ->
                capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ||
                capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
            }
        } ?: false
    }
}

// 使用扩展函数优化
fun Context.isNetworkAvailable(): Boolean {
    return NetworkUtils.isNetworkAvailable(this)
}

3. View组件封装

kotlin 复制代码
class LoadingButton @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : MaterialButton(context, attrs, defStyleAttr) {
    
    private var originalText: CharSequence? = null
    private val progressBar = ProgressBar(context).apply {
        layoutParams = LayoutParams(
            LayoutParams.WRAP_CONTENT,
            LayoutParams.WRAP_CONTENT
        )
        isIndeterminate = true
        visibility = View.GONE
    }
    
    init {
        addView(progressBar)
    }
    
    fun showLoading() {
        originalText = text
        text = ""
        isEnabled = false
        progressBar.visibility = View.VISIBLE
    }
    
    fun hideLoading() {
        text = originalText
        isEnabled = true
        progressBar.visibility = View.GONE
    }
}

四、网络请求封装

1. Retrofit + Kotlin协程封装

kotlin 复制代码
interface ApiService {
    @GET("user/{id}")
    suspend fun getUser(@Path("id") userId: String): ApiResponse<User>
}

class ApiClient private constructor() {
    
    companion object {
        private const val BASE_URL = "https://api.example.com/"
        
        fun create(): ApiService {
            return Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .client(createOkHttpClient())
                .build()
                .create(ApiService::class.java)
        }
        
        private fun createOkHttpClient(): OkHttpClient {
            return OkHttpClient.Builder()
                .connectTimeout(30, TimeUnit.SECONDS)
                .readTimeout(30, TimeUnit.SECONDS)
                .addInterceptor(createInterceptor())
                .addInterceptor(HttpLoggingInterceptor().apply {
                    level = HttpLoggingInterceptor.Level.BODY
                })
                .build()
        }
        
        private fun createInterceptor(): Interceptor {
            return Interceptor { chain ->
                val request = chain.request().newBuilder()
                    .addHeader("Authorization", "Bearer token")
                    .addHeader("Content-Type", "application/json")
                    .build()
                chain.proceed(request)
            }
        }
    }
}

// 统一响应封装
data class ApiResponse<T>(
    val code: Int,
    val message: String?,
    val data: T?
) {
    fun isSuccess(): Boolean = code == 200
}

五、数据库封装(Room)

kotlin 复制代码
@Entity(tableName = "users")
data class User(
    @PrimaryKey val id: String,
    val name: String,
    val email: String,
    @ColumnInfo(name = "created_at") val createdAt: Long
)

@Dao
interface UserDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insert(user: User)
    
    @Query("SELECT * FROM users WHERE id = :userId")
    suspend fun getUser(userId: String): User?
    
    @Query("SELECT * FROM users")
    fun getAllUsers(): Flow<List<User>>
}

@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
    
    companion object {
        @Volatile
        private var INSTANCE: AppDatabase? = null
        
        fun getInstance(context: Context): AppDatabase {
            return INSTANCE ?: synchronized(this) {
                INSTANCE ?: buildDatabase(context).also { INSTANCE = it }
            }
        }
        
        private fun buildDatabase(context: Context): AppDatabase {
            return Room.databaseBuilder(
                context.applicationContext,
                AppDatabase::class.java,
                "app_database"
            ).build()
        }
    }
}

六、依赖注入封装

1. 使用Hilt/Dagger

kotlin 复制代码
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
    
    @Provides
    @Singleton
    fun provideApiService(): ApiService {
        return ApiClient.create()
    }
    
    @Provides
    @Singleton
    fun provideUserRepository(
        apiService: ApiService,
        userDao: UserDao
    ): UserRepository {
        return UserRepository(apiService, userDao)
    }
}

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    
    @Inject
    lateinit var userRepository: UserRepository
    
    // 或通过ViewModel注入
    private val viewModel: MainViewModel by viewModels()
}

2. 手动依赖注入(无框架时)

kotlin 复制代码
object ServiceLocator {
    
    private val services = mutableMapOf<String, Any>()
    
    inline fun <reified T : Any> register(service: T) {
        services[T::class.java.name] = service
    }
    
    inline fun <reified T : Any> get(): T {
        return services[T::class.java.name] as? T
            ?: throw IllegalStateException("Service not registered: ${T::class.java}")
    }
}

// 初始化
ServiceLocator.register(ApiClient.create())
ServiceLocator.register(UserRepository(...))

七、配置和常量封装

kotlin 复制代码
object AppConfig {
    const val API_TIMEOUT = 30L
    const val DATABASE_NAME = "app.db"
    const val SHARED_PREF_NAME = "app_prefs"
    
    object Endpoints {
        const val BASE_URL = "https://api.example.com/"
        const val USER = "user/{id}"
        const val LOGIN = "auth/login"
    }
    
    object Preferences {
        const val KEY_TOKEN = "token"
        const val KEY_USER_ID = "user_id"
    }
}

// 使用BuildConfig管理不同环境
object Environment {
    val BASE_URL = BuildConfig.BASE_URL
    val IS_DEBUG = BuildConfig.DEBUG
    val VERSION_NAME = BuildConfig.VERSION_NAME
}

八、错误处理封装

kotlin 复制代码
sealed class Result<out T> {
    data class Success<out T>(val data: T) : Result<T>()
    data class Error(val exception: Exception) : Result<Nothing>()
    object Loading : Result<Nothing>()
}

class ErrorHandler {
    
    fun handleError(exception: Exception): String {
        return when (exception) {
            is IOException -> "网络连接失败,请检查网络"
            is HttpException -> when (exception.code()) {
                401 -> "未授权,请重新登录"
                404 -> "资源不存在"
                500 -> "服务器内部错误"
                else -> "网络请求失败: ${exception.message}"
            }
            else -> "未知错误: ${exception.message}"
        }
    }
    
    fun logError(tag: String, exception: Exception) {
        if (BuildConfig.DEBUG) {
            Log.e(tag, exception.message, exception)
        }
        // 生产环境上报到监控平台
        FirebaseCrashlytics.getInstance().recordException(exception)
    }
}

九、测试友好的封装

kotlin 复制代码
// 使用接口便于Mock
interface IUserRepository {
    suspend fun getUser(id: String): User?
    suspend fun saveUser(user: User)
}

// 使用构造函数注入依赖
class UserViewModel(
    private val userRepository: IUserRepository,
    private val errorHandler: ErrorHandler
) : ViewModel() {
    
    private val _userState = MutableStateFlow<Result<User>>(Result.Loading)
    val userState: StateFlow<Result<User>> = _userState
    
    suspend fun loadUser(userId: String) {
        _userState.value = Result.Loading
        try {
            val user = userRepository.getUser(userId)
            _userState.value = user?.let { Result.Success(it) }
                ?: Result.Error(Exception("User not found"))
        } catch (e: Exception) {
            _userState.value = Result.Error(e)
            errorHandler.logError("UserViewModel", e)
        }
    }
}

十、封装的最佳实践总结

1. 遵循SOLID原则

  • 单一职责
  • 开闭原则
  • 里氏替换
  • 接口隔离
  • 依赖倒置

2. 保持适当的封装粒度

  • 避免过度封装(每个方法都单独一个类)
  • 避免封装不足(一个类做所有事情)

3. 考虑可测试性

  • 依赖注入
  • 接口编程
  • 避免静态方法和单例

4. 性能考虑

  • 延迟初始化
  • 缓存策略
  • 避免内存泄漏

5. 文档和注释

  • 公共API必须有文档
  • 复杂逻辑需要注释
  • 保持README更新

6. 版本兼容性

kotlin 复制代码
@RequiresApi(Build.VERSION_CODES.O)
fun newApiMethod() {
    // 高版本API实现
}

fun compatibleMethod() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        newApiMethod()
    } else {
        // 兼容实现
    }
}

通过以上封装策略,可以提高代码的:

  • 可维护性:结构清晰,易于修改
  • 可测试性:依赖明确,易于单元测试
  • 可复用性:组件独立,可在不同项目使用
  • 可读性:命名规范,逻辑清晰
  • 健壮性:错误处理完善,边界情况考虑周全

一个开发者,理应有属于自己的一套封装组件,在需要用时,开箱即用。

相关推荐
Felixwb6662 小时前
Python 爬虫框架设计:类封装与工程化实践
前端
广州华水科技2 小时前
潜力榜单2025年单北斗GNSS位移监测高口碑产品推荐
前端
xinyu_Jina2 小时前
OpenNana 提示词图库:多模态数据检索、分面搜索与前端性能工程
前端
暴富的Tdy2 小时前
【脚手架创建 Vue3 公共组件库】
前端·npm·npm发布
技术宅小温2 小时前
< 前端大小事: 2025年近期CSDN前端技术热点分析 >
前端
知了清语2 小时前
pkg.pr.new 快速验证第三方包-最新修复
前端
iFlow_AI2 小时前
知识驱动开发:用iFlow工作流构建本地知识库
前端·ai·rag·mcp·iflow·iflow cli·iflowcli
wordbaby2 小时前
TanStack Router 文件命名约定
前端
打工人小夏2 小时前
vue3使用transition组件,实现过度动画
前端·vue.js·前端框架·css3