1.介绍
开发过程中往往会用到一些开发的工具,仅仅在Debug模式下使用。并不将该工具打入release包中
比如:
- 1.查看设备信息
- 2.查看构建时间
- 3.查看项目版本
...
2.开始设计
2.1.创建新的Android Library
1.添加我们buildConfigField,在我们debug环境使用
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
//buildConfigField添加属性
debug {
/**
* 参数1:字段类型
* 参数2:字段名称
* 参数3:返回值
*/
buildConfigField("String","BUILD_TIME","\""+ buildTime() + "\"")
}
}
def buildTime(){
//EE表示星期几
//TimeZone表示时区
return new Date().format("EE HH:mm:ss",TimeZone.getTimeZone("GMT+08:00"))
}
2.项目Rebuild后BuildConfig中就会出现我们添加的属性BUILD_TIME
public final class BuildConfig {
...
// Fields from build type: debug
public static final String BUILD_TIME = "星期二 16:10:26";
}
2.2.创建DebugTools类
作用:该类主要就是工具需要显示的功能,最终会通过反射获取该类方法的实现,并执行。
好处:每次需要新增一个功能,我们只需要添加一个方法即可。
/**
* TODO:创建DebugTools工具类
* 1.该类提供我们工具需要的一些功能方法。
* 2.最终通过反射来获取该类中的所有方法,并执行方法内的方法体。
* 注意:如果方法没有返回值,那么我们可以通过注解的方式来实现Tools上显示方法名称
*/
class DebugTools {
/**
* TODO:显示项目的构建版本号
* 1.0.1
*/
fun buildVersion():String{
return "构建版本:${BuildConfig.VERSION_CODE}.${BuildConfig.VERSION_NAME}"
}
/**
* TODO:显示项目的构建时间
* 星期二 22:14:54
*/
fun buildTime():String{
//new date() 当我们在运行时拿到的时间。也就是这个包打出来的时间
return "构建时间:${BuildConfig.BUILD_TIME}"
}
/**
* TODO:显示项目的构建环境
* 通过BuildConfig.Debug
*/
fun buildEnvironment():String{
return "构建环境:${if (BuildConfig.DEBUG) "测试环境" else "正式环境"}"
}
/**
* TODO:降级Https为Http
* 此时是需要具体的实现,并非是一个String类型的返回值,所以我们此时通过HiDebug注解来实现Tools上显示的名称。
*/
@HiDebug(name = "一键开启Https降级",desc = "将继承Http,可以使用抓包工具明文抓包")
fun degradeHttp(){
//此时我们可以通过MMKV(https://github.com/Tencent/MMKV)来存储我们的baseUrl,将其修改为http的。如果需要重启应用生效,那么我们此时直接重启应用
//...
val content = AppGlobals.get()?.applicationContext?:return
//获取需要启动应用的Intent的意图
val intent = content.packageManager.getLaunchIntentForPackage(content.packageName)
intent?.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
content.startActivity(intent)
//杀死当前进程,并主动启动新的启动页,完成重启动作。
Process.killProcess(Process.myPid())
}
}
/**
* TODO:注解的作用
* @Target:表示作用于哪里(类,方法,属性等等)FUNCTION方法
* @Retention:作用于什么时候,RUNTIME运行时期
*/
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
annotation class HiDebug(val name:String,val desc:String = "")
2.3.创建实现类
作用:该类主要就是弹窗显示上方定义的DebugTools中的每一个方法,通过反射获取该类方法的实现,并执行。
class DebugToolDialogFragment :AppCompatDialogFragment(){
//通过数据类进行遍历获取类中所有方法
private val debugTools = arrayOf(DebugTools::class.java)
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
//获取我们试图的跟布局android.R.id.content,我们可以进行修改宽高大小。
val parent = dialog?.window?.findViewById<ViewGroup>(android.R.id.content)?:container
val view = inflater.inflate(R.layout.hi_debug_tool,parent,false)
//指定弹窗的宽高
dialog?.window?.setLayout(
(HiDisplayUtil.getDisplayWidthInPx(view.context) * 0.7f).toInt(),
WindowManager.LayoutParams.WRAP_CONTENT)
//设置背景圆角
dialog?.window?.setBackgroundDrawableResource(R.drawable.shape_hi_debug_tool)
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//设置列表项分割线
val itemDecoration = DividerItemDecoration(view.context,DividerItemDecoration.VERTICAL)
itemDecoration.setDrawable(
ContextCompat.getDrawable(
view.context,
R.drawable.shape_hi_debug_divider)!!)
var functions = mutableListOf<DebugFunction>()
//获取数组元素个数
val size = debugTools.size
//遍历
for (index in 0 until size){
//获取所有类对象
val claz = debugTools[index]
//实例化数组每一个元素对象
val target = claz.getConstructor().newInstance()
//获取类中所有方法
val declaredMethods= target.javaClass.declaredMethods
for (method in declaredMethods){
var title = ""
var desc = ""
var enable = false
val annotation = method.getAnnotation(HiDebug::class.java)
//包含@HiDebug注解的方法都可以被点击
if (annotation!=null){
title = annotation.name
desc = annotation.desc
enable = true
}else{
method.isAccessible = true //开启访问权限
title = method.invoke(target) as String
}
val func = DebugFunction(title,desc,method,enable,target)
functions.add(func)
}
}
recycler_view.addItemDecoration(itemDecoration)
recycler_view.layoutManager = LinearLayoutManager(context,LinearLayoutManager.VERTICAL,false)
recycler_view.adapter = DebugToolAdapter(functions)
}
inner class DebugToolAdapter(val list:List<DebugFunction>): RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
//创建item
var itemView = layoutInflater.inflate(R.layout.hi_debug_tool_item,parent,false)
return object :RecyclerView.ViewHolder(itemView){
}
}
override fun getItemCount(): Int {
return list.size
}
//绑定数据
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val debugFunction = list[position]
val itemTitle = holder.itemView.findViewById<TextView>(R.id.item_title)
val itemDesc = holder.itemView.findViewById<TextView>(R.id.item_desc)
itemTitle.text = debugFunction.name
if (TextUtils.isEmpty(debugFunction.desc)){
itemDesc.visibility = View.GONE
}else{
itemDesc.visibility = View.VISIBLE
itemDesc.text = debugFunction.desc
}
//是否可以点击
if (debugFunction.enable){
holder.itemView.setOnClickListener {
dismiss()
debugFunction.invoke()
}
}
}
}
/**
* name:方法名称
* desc:方法描述
* method:方法的实体
* enable:是否可以点击,有些返回字符串不需要点击
* target:方法所在类的对象
*/
data class DebugFunction(
val name:String,
val desc:String,
val method:Method,
val enable:Boolean,
val target:Any) {
//点击事件,通过反射就执行该方法
fun invoke() {
method.invoke(target)
}
}
}
3.使用
1.我们在主模块中添加依赖
//只有在debug下才会打入apk当中(只用于debug中)
debugImplementation project(path: ":hidebugtools" )
2.可以在任意地方调用(我是通过手机音量键)
/**
* 监听音量键按下
*/
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN){
//显示弹窗,只有在Debug的情况下显示
if (BuildConfig.DEBUG){
//通过反射获取类
try {
val clazz = Class.forName("com.yc.hidebugtool.DebugToolDialogFragment")
val target:DialogFragment = clazz.getConstructor().newInstance() as DialogFragment
target.show(supportFragmentManager,"debug_tool")
}catch (e:ClassNotFoundException){
e.printStackTrace()
}
//正常使用
try {
val target: DialogFragment = DebugToolDialogFragment()
target.show(supportFragmentManager, "debug_tool")
}catch (e: ClassNotFoundException){
e.printStackTrace()
}
}
}
return super.onKeyDown(keyCode, event)
}
3.总结
好处:
1.我们通过debugImplementation 只有在debug模式才会打入包中。
2.完全耦合,我们需要任意一种工具功能,我们只需要在DebugTools类中添加对应的方法,并实现。
github地址:https://github.com/HuiZaierr/DebugTools