前言
拥有或者能够随时查看源代码对于一个程序员来说无疑是一件很幸福的事情,那么高效的探索与学习方法就显得尤为重要。这里以一个例子来浅谈一下我的学习方法。注意区分我认为高效的学习方法对我很重要不等于我的方法很高效啦。
引子问题
因为我经常使用附加调试这种方式来调查bug,这种方法非常高效,相较于与打log的方式更加灵活,便利。但是有时断点在一个比较早期的位置,比如进程刚启动,还没等我执行附加调试就已经走完了,这时我就不得不采用打log的方式了。比如对于一个一闪就没的Activity,因为是源码编译,只能附加调试。但是我注意到AndroidStudio项目在开始调试时,应用启动前总会显示一个dialog显示正在等待附加调试。这说明AndroidStudio可能通过adb命令来让应用先不完全运行,等待调试程序附加后再继续运行。
通过搜索我了解到开发者选项中有两项“Select debug app”和“Wait for debugger”,选择一个待调试的App后再选中“Wait for debugger”,这时这个进程在重新启动时就会先显示一个dialog,等待调试程序附加,当进程被调试程序附加后就会继续运行。这个方法亲测有效,也事实上解决了困扰我很久的难题,但如果到这里就结束了,那么这篇文章应该叫面向百度编程的若干技巧。
以我的习惯是一定要了解到Android Studio所使用的命令的,很明显可以通过看Android Studio源码和Android设定及framework源码这两条思路来解决。这里介绍第二种过程
问题探索过程
基本思路从设定项这一入口由外到内地发现“真相”。
1.设定的部分
先搜索字符串“select debug app”,“wait for debugger”,等等,都没有搜索到,很显然使用了overlay机制,那么搜索Develop(原来我知道开发者选项对应一个设定fragment),找到DevelopmentSettingsDashboardFragment,然后找到添加controller的地方大致找到WaitForDebuggerPreferenceController和SelectDebugAppPreferenceController。
先看WaitForDebuggerPreferenceController,看到下面代码:
private void writeDebuggerAppOptions(String packageName, boolean waitForDebugger,
boolean persistent) {
try {
getActivityManagerService().setDebugApp(packageName, waitForDebugger, persistent);
} catch (RemoteException ex) {
/* intentional no-op */
}
}
所以其实是通过AMS来设定的,设定部分结束,下面分析AMS部分。
2.AMS部分
public void setDebugApp(String packageName, boolean waitForDebugger,
boolean persistent) {
//说明需要特权
enforceCallingPermission(android.Manifest.permission.SET_DEBUG_APP,
"setDebugApp()");
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) {
//设定传过来的就是true,进行持久化保存,所以真正进行保存的不是设定
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);
}
}
通过Android Studio方便的搜索引用的功能(ctrl+鼠标左键),很容易找到ActivityManagerShellCommand的runSetDebugApp,然后找到下面的代码。
int runSetDebugApp(PrintWriter pw) throws RemoteException {
boolean wait = false;
boolean persistent = false;
String opt;
while ((opt=getNextOption()) != null) {
if (opt.equals("-w")) {
wait = true;
} else if (opt.equals("--persistent")) {
persistent = true;
} else {
getErrPrintWriter().println("Error: Unknown option: " + opt);
return -1;
}
}
String pkg = getNextArgRequired();
mInterface.setDebugApp(pkg, wait, persistent);
return 0;
}
@Override
public int onCommand(String cmd) {
if (cmd == null) {
return handleDefaultCommands(cmd);
}
final PrintWriter pw = getOutPrintWriter();
try {
switch (cmd) {
case "start":
case "start-activity":
return runStartActivity(pw);
case "startservice":
case "start-service":
return runStartService(pw, false);
....
//就是这里
case "set-debug-app":
return runSetDebugApp(pw);
case "set-agent-app":
return runSetAgentApp(pw);
case "clear-debug-app":
return runClearDebugApp(pw);
case "set-watch-heap":
return runSetWatchHeap(pw);
所以命令就是adb shell am set-debug-app -w packageName,亲测有效。
本案已破。
后记
事实上直接看Android Studio Debug的Console窗口就会发现,Android Studio使用的是:
adb shell am start -n activity名 -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -D
关键就是指定了-D