这么好的Android开发辅助工具App不白嫖可惜了

b5a29210de90d8653aaa79bb8362ede9.jpeg

过年期间闲来没事,手撸了一个辅助Android开发调试的工具App,适合Android开发者和测试同学使用。

Github地址下载, Gitee地址下载(需要登录gitee)    https://gitee.com/luqinx/codecrafts-docs/raw/master/codecrafts-release-latest-version.apk

功能概览

对我这样的懒人开发者来说,反复的做同样一件事简直太煎熬了,因此我把我平时开发中需要反复操作的命令和一些繁琐的操作整理成了一个工具。

废话不多说, 先上图了解下工具的大概功能有哪些(内容比截图丰富,欢迎下载体验)

f0d6f7ed169bef26077e574578eaaa46.jpeg f985a22d67762866f50fc58c8c31a714.jpeg 7df81fa9a3025b22b936fee8cb119410.jpeg 2fae3f07db0fac472d0287d76d1a6eb6.jpeg cdc36dba9ddef3bba3e5fef620ebe406.jpeg 82ea48feeb8405aa6c4eaf071adea0ee.jpeg

CodeCrafts的核心是一个可拖动的侧边栏的悬浮窗,悬浮窗可以折叠或展开,悬浮窗中包含5大块功能分别对应一个TAB, 这5大块功能分别是应用控制、开发者选项、常用功能,常用系统设置和全局功能

功能明细

1. 应用控制

应用控制能力将一些日常开发过程中对应用的一些繁琐的操作或者命令行指令转变为可视化的操作,而且还有自动收集和整理Crash, ANR日志,并且可以自动关联Logcat日志

文字太繁琐, 请直接看视频,地址如下

https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/898d95dad52d45af9e246b4863904cde~tplv-k3u1fbpfcp-watermark.image?

2. 开发者选项

这里的开发者选项功能是将系统的开发者选项中一些最常用的开关放在悬浮窗中, 随时启用或关闭。优势是不需要频繁去系统的开发者选项中去找对应开关,一键开闭。

我调研了其他有类似能力的工具App,都是引导用户去开发者选项中去开启或关闭功能。CodeCrafts一键开闭,无需跳转到系统开发者选项页面。

3. 最常用功能

没什么好介绍的,略。

4. 常用系统设置页面

这里承载了一些开发过程中经常需要打开的系统设置页面的快捷按钮,没什么好介绍的,略

5. 全局功能

这里的全局是相对于应用控制的,应用控制可以选择你正在开发的任意一款App, 然后应用控制中的所有能力都是对你的这个App的操作。而全局控制中的功能不针对选中的App,所有App都适用

5.1 实时数据(Realtime data)

实时数据会随着当前页面变化或者系统事件实时变化

fba06404f85e0e2512edd1e9b96ed405.jpeg

(以上图为例介绍, 实时数据的内容不仅仅只有这些)

内容含义用途
org.chromium.chrome.browser.firstrun.FirstRunActivity当前Activity的类名代码定位
launch time: 208ms当前Activity的冷启动耗时启动优化
com.android.chrome当前Activity所在应用的包名常用信息
Chrome(uid: 10163)当前Activity所在应用的名称和UID常用信息
pid: 23017当前Activity的进程ID常用信息
192.168.2.56,...当前系统的IP地址,可能有多个adb connect等
system当前应用是系统应用
allowBackUp当前应用有allowBackUp属性告警

实时数据未来还会有更多的扩展内容

5.2 不锁定屏幕

不会进入锁屏状态,也不会灭屏,避免开发过程中老是自动锁屏。

和系统开发者选项中的功能类似,区别是无论是否插入USB线都有效,开发者选项中的拔掉USB线后就无效了。都可以用,具体选择看你的使用场景。

5.3 Latest Crashes

显示缓存中最近发生的Crash的调用堆栈,可能为空也可能不止一个Crash堆栈, 需要自行查看是否是你关注的Crash。

使用说明

CodeCrafts的很多功能依赖Shell权限, 如果发现存在功能不可用的情况,一般都是shell权限获取失败了, 只需要通过在电脑终端输入adb命令"adb tcpip 5555"指令, CodeCrafts就可以自动获取shell权限了。

9a28b6934e4f755ad2a294b40a72bdff.jpeg

adb tcpip 5555

  1. 第一次使用,连接电脑终端发送"adb tcpip 5555" 或

  2. 手机断电重启,连接电脑终端发送"adb tcpip 5555" 或

  3. 莫名其妙功能不能用了,连接电脑终端发送"adb tcpip 5555"

建设中

  1. 文件沙盒, 快速浏览App的文件目录

  2. 自动化,自动化点击,输入(比如自动跳广告,自动输入账号密码?)

  3. 组件检查, 快速查看View的类型, id, 颜色等

  4. ...

后期规划

  1. 悬浮窗的tab和内容可动态配置

  2. 应用控制增加应用性能数据

  3. 提供外部SDK接口,外部应用可接入CodeCrafts进行定制化改造

CodeCrafts持续更新中...

Github地址下载, Gitee地址下载(需要登录gitee)  https://gitee.com/luqinx/codecrafts-docs/raw/master/codecrafts-release-latest-version.apk

断点调试

如果你的App有多个进程,你是否被子进程如何使用断点来调试Application.onCreate() 等进程启动前期的初始化代码困扰过?

我就是被这个问题困扰过很长一段时间,起初我都是在想要断点的地方手动加上Debug.waitForDebugger(),然后重新编译,最后再完成调试后再手动删除Debug.waitForDebugger()。繁琐低效不说,如果一不小心把它提交到代码仓库那就很尴尬了。

CodeCrafts的断点调试可以在进程启动前,提前将进程设置为断点调试状态,这样就能解决子进程的一些初始化代码不方便调试的问题。这是Android Studio无法做到的,先看图

14a0364f90046afe48b6ceb017a2c21a.jpeg52582e9a56759a46276ca3ecef27d3e3.jpeg

实现原理

原理其实很简单,就是利用am set-debug-app, 但是set-debug-app这个名称并不准确,导致这个命令容易被用错。

set-debug-app和clear-debug-app

set-debug-app: 设置待调试的App, 实际上这个命令的名字和描述都不太准确,应该叫set-debug-process才对,因为set-debug-app 命令最后的参数是processName 并不是packageName。

clear-debug-app: 相反,是清除待调试的App

set-debug-appclear-debug-app是am下面的一个子命令, 看下am的定义

# adb shell am 
...
  set-debug-app [-w] [--persistent] <PACKAGE>
      Set application <PACKAGE> to debug.  Options are:
      -w: wait for debugger when application starts
      --persistent: retain this value
  clear-debug-app
      Clear the previously set-debug-app.
...

为什么说set-debug-app 命令的最后的参数是processName而不是packageName, 看一下它在源码中的实现就知道了

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
private boolean attachApplicationLocked(@NonNull IApplicationThread thread, int pid, int callingUid, long startSeq) {    ....
        try {            
            int testMode = ApplicationThreadConstants.DEBUG_OFF;


            // mDebugApp是命令后的入参,与之判断相等的是processName,而不是packageName
            if (mDebugApp != null && mDebugApp.equals(processName)) {
                  testMode = mWaitForDebugger
                    ? ApplicationThreadConstants.DEBUG_WAIT
                    : ApplicationThreadConstants.DEBUG_ON;


              // app是个ProcessRecord对象,这里将这个进程打上Debugging标记
                app.setDebugging(true); 
                if (mDebugTransient) {
                    mDebugApp = mOrigDebugApp;
                    mWaitForDebugger = mOrigWaitForDebugger;
                }
            }




     ....
}

如果你真的输入packageName, 那么就没办法为子进程设置调试了。

CodeCrafts的断点调试实现

CodeCrafts有Shell权限,当然可以直接执行am set-debug-app来设置要对App的哪个进程来进行断点。但这样有点简单粗暴,执行结果也很难控制。

set-debug-appam下的一个子命令,因此推断它的实现肯定是在ActivityManagerService(后面简称AMS)中, 最终很容易找到了AMSsetDebugApp方法。

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
 public void setDebugApp(String packageName, boolean waitForDebugger,
            boolean persistent) {
        enforceCallingPermission(android.Manifest.permission.SET_DEBUG_APP,
                "setDebugApp()");


        final long ident = Binder.clearCallingIdentity();
        try {
            // Note that this is not really thread safe if there are multiple
            // callers into it at the same time, but that's not a situation we
            // care about.
            if (persistent) {
                final ContentResolver resolver = mContext.getContentResolver();
                Settings.Global.putString(
                    resolver, Settings.Global.DEBUG_APP,
                    packageName);
                Settings.Global.putInt(
                    resolver, Settings.Global.WAIT_FOR_DEBUGGER,
                    waitForDebugger ? 1 : 0);
            }


            synchronized (this) {
                if (!persistent) {
                    mOrigDebugApp = mDebugApp;
                    mOrigWaitForDebugger = mWaitForDebugger;
                }
                mDebugApp = packageName;
                mWaitForDebugger = waitForDebugger;
                mDebugTransient = !persistent;
                if (packageName != null) {
                    forceStopPackageLocked(packageName, -1, false, false, true, true,
                            false, UserHandle.USER_ALL, "set debug app");
                }
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

但是ActivityManager(后面简称AM)中并没有开放这个接口, 普通的App没有办法直接调用这个系统未公开的API。

那有没有间接一点的方法呢?

我们知道AMAMS在应用进程中的代理,他们之间是通过Binder通信的,Binder通信一般需要定义一个aidl,IActivityManager.aidl 就是他们之间的接口定义, 而且IActivityManager.aidl中也有定义setDebugApp方法。

frameworks/base/core/java/android/app/IActivityManager.aidl 
interface IActivityManager {
    ....


    void setDebugApp(in String packageName, boolean waitForDebugger, boolean persistent);
    ....
}

IActivityManager.aidl 会在编译期间会生成

IActivityManager.java

,IActivityManager.java中当然也会有setDebugApp接口方法。

虽然AM没有开放setDebugApp接口,但是根据aidl的实现规则, IActivityManager.java中必定有个Proxy类,并且Proxy必定实现了IActivityManager接口,我们只要拿到这个Proxy类的对象就能调用到setDebugApp方法。

如何拿到这个Proxy类的对象呢?

frameworks/base/core/java/android/app/ActivityManagerNative.java
public abstract class ActivityManagerNative {....


    /**
     * Cast a Binder object into an activity manager interface, generating
     * a proxy if needed.
     *
     * @deprecated use IActivityManager.Stub.asInterface instead.
     */
    @UnsupportedAppUsage
    static public IActivityManager asInterface(IBinder obj) {
        return IActivityManager.Stub.asInterface(obj);
    }


    /**
     * Retrieve the system's default/global activity manager.
     *
     * @deprecated use ActivityManager.getService instead.
     */
    @UnsupportedAppUsage
    static public IActivityManager getDefault() {
        return ActivityManager.getService();
}


....}

ActivityManagerNative提供的getDefault()方法获取的正是这个Proxy类。

虽然ActivityManagerNative和IActivityManager都是隐藏类App无法直接访问,但是我们只需要定义这两个类的同包名、同类名的文件即可解决编译问题。根据类加载双亲委派机制,会自动忽略这两个文件。这块不是重点,有空再细讲。

作者:小码哥哥
链接:https://juejin.cn/post/7201721224124629047

关注我获取更多知识或者投稿

a19adb80acd82676e3eebe9c0c29261e.jpeg

bf795b0abce6fd7a918c67075795ab95.jpeg

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值