面向对象编程中,封装是提升软件系统的手段
封装是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 {
// 兼容实现
}
}
通过以上封装策略,可以提高代码的:
- 可维护性:结构清晰,易于修改
- 可测试性:依赖明确,易于单元测试
- 可复用性:组件独立,可在不同项目使用
- 可读性:命名规范,逻辑清晰
- 健壮性:错误处理完善,边界情况考虑周全
一个开发者,理应有属于自己的一套封装组件,在需要用时,开箱即用。