Finch
Finch 提供了一个可定制的调试菜单,用于安卓应用开发。它不会影响生产代码。开发者可以通过简单的步骤轻松添加自定义调试功能。
Gradle 依赖
在根目录的 build.gradle 文件中,在 repositories 的末尾添加:
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
mavenCentral()
maven { url 'https://jitpack.io' }
}
}
选择一个 UI 实现并添加依赖:
- ui-activity - 调试菜单作为新屏幕。
- ui-bottom-sheet - 调试菜单作为模态底部弹出层。
- ui-dialog - 调试菜单作为模态对话框。
- ui-drawer - 调试菜单作为侧边导航抽屉。
- ui-view - 调试菜单作为视图。
- noop - 用于发布版本。
dependencies {
debugImplementation 'com.github.kernel0x.finch:ui-drawer:2.3.6'
releaseImplementation 'com.github.kernel0x.finch:noop:2.3.6'
// optional only for OkHttp
debugImplementation 'com.github.kernel0x.finch:log-okhttp:2.3.6'
releaseImplementation 'com.github.kernel0x.finch:log-okhttp-noop:2.3.6'
// optional only for GRPC
debugImplementation 'com.github.kernel0x.finch:log-grpc:2.3.6'
releaseImplementation 'com.github.kernel0x.finch:log-grpc-noop:2.3.6'
// optional only for logs
debugImplementation 'com.github.kernel0x.finch:log:2.3.6'
releaseImplementation 'com.github.kernel0x.finch:log-noop:2.3.6'
}
工作原理
初始化 Finch 的一个实例(最好在应用程序的 onCreate() 方法中)
各种自定义设置通过 Configuration 对象进行。Finch.initialize(this)
接下来,您需要添加希望在调试菜单中显示的组件。可选地,您还可以额外配置日志记录和拦截网络事件(使用 OkHttp)。
日志记录
要在调试菜单中添加日志消息,只需调用 Finch.log() 并将 FinchLogger 添加到 Configuration 对象中。
Finch.log("message")
Finch.initialize(
application = this,
configuration = Configuration(
logger = FinchLogger,
...
),
)OkHttp
在构建 OkHttp 客户端时,将 FinchOkHttpLogger.logger 添加到 addInterceptor 方法中,并将 FinchOkHttpLogger 添加到 Configuration 对象中。
OkHttpClient.Builder()
.addInterceptor(FinchOkHttpLogger.logger as? Interceptor ?: Interceptor { it.proceed(it.request()) })
.build()
Finch.initialize(
application = this,
configuration = Configuration(
networkLoggers = listOf(FinchOkHttpLogger),
...
),
)Grpc
在构建 ManagedChannel 时,将 FinchGrpcLogger.logger 添加到方法拦截器中,并将 FinchGrpcLogger 添加到 Configuration 对象中。
ManagedChannelBuilder.forAddress(networkConfig.hostname, networkConfig.port)
.intercept(FinchGrpcLogger.logger as? ClientInterceptor ?: object : ClientInterceptor {
override fun interceptCall(
method: MethodDescriptor?,
callOptions: CallOptions?,
next: Channel?
): ClientCall {
return object : ForwardingClientCall.SimpleForwardingClientCall(
next?.newCall(
method,
callOptions
)
) {}
}
})
.build()
Finch.initialize(
application = this,
configuration = Configuration(
networkLoggers = listOf(FinchGrpcLogger),
...
),
)示例初始化
这里是一个适用于大多数项目的最小示例
Finch.initialize(
application = this,
configuration = Configuration(
logger = FinchLogger,
networkLoggers = listOf(FinchOkHttpLogger)
),
components = arrayOf(
Header(
title = getString(R.string.app_name),
subtitle = BuildConfig.APPLICATION_ID,
text = "${BuildConfig.BUILD_TYPE} v${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})"
),
Padding(),
Label("Tools", Label.Type.HEADER),
DesignOverlay(),
AnimationSpeed(),
ScreenCaptureToolbox(),
Divider(),
Label("Logs", Label.Type.HEADER),
LifecycleLogs(),
NetworkLogs(),
Logs(),
Divider(),
Label("Other", Label.Type.HEADER),
DeviceInfo(),
AppInfo(),
DeveloperOptions(),
ForceCrash()
)
)常见情况
#### 后端环境
data class Environment(
val type: Type,
override val title: Text = Text.CharSequence(type.name)
) : FinchListItemenum class Type {
TEST,
PROD
}
SingleSelectionList(
title = "Backend environment",
items = listOf(Environment(Type.TEST), Environment(Type.PROD)),
initiallySelectedItemId = Type.TEST.name,
isValuePersisted = true,
onSelectionChanged = {
when (it?.type) {
Type.TEST -> {
...
} Type.PROD -> {
...
}
else -> {
// nothing
}
}
}
),
#### 功能开关
fun Application.initializeDebugMenu(
featureManager: FeatureManager
) {
val toggles = featureManager.getAll().map {
Switch(
text = it.description,
initialValue = it.isEnabled(),
isEnabled = true,
onValueChanged = { value ->
featureManager.save(it.key, value)
if (!it.canChangedInRuntime) {
Toast.makeText(this, "Restart app to apply changes!", Toast.LENGTH_LONG).show()
}
}
)
}
Finch.initialize(
...
components = arrayOf(
...
Divider(),
Label("Feature Toggles", Label.Type.HEADER),
Switch(
text = "Show",
initialValue = false,
isEnabled = true,
id = "feature_toggles",
onValueChanged = {
if (it) {
Finch.add(
components = toggles.toTypedArray(),
position = Position.Below("feature_toggles")
)
} else {
toggles.forEach { item ->
Finch.remove(item.id)
}
}
}
),
...
)
)
}
#### 日志class LogTree : Timber.Tree() {
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
FinchLogger.log(message)
}
}
fun Application.initializeDebugMenu() {
...
Timber.plant(LogTree())
Finch.initialize(
application = this,
configuration = Configuration(
logger = FinchLogger,
),
...
)
}Components
CheckBox Divider ItemList KeyValueList Label LongText MultipleSelectionList Padding ProgressBar SingleSelectionList Slider Switch TextInput AnimationSpeed AppInfo DesignOverlay DeveloperOptions DeviceInfo ForceCrash Header LifecycleLogs Logs LoremIpsumGenerator NetworkLogsProguard
-keep,allowobfuscation,allowshrinking class com.google.gson.reflect.TypeToken
-keep,allowobfuscation,allowshrinking class * extends com.google.gson.reflect.TypeToken版本发布
请查看版本发布标签获取所有发布信息。
--- Tranlated By Open Ai Tx | Last indexed: 2025-12-25 ---