At work I’m a big proponent of vim. Needless to say, I code Android in vim. One thing that many Android developers do is debug their apps using their IDEs built in debugger. Luckily, there is still a way to debug Android apps without an IDE such as Eclipse or Android Studio. What you can use is jdb, a simple command-line debugger for Java classes.
In order to do this, you must first launch your app, find your apps PID, forward a local socket connection to that PID, attach jdb and start debugging.
To demonstrate how all this ties together I created a very simple app. Install
that app on a phone or emulator via ant:
ant debug install
Once you have it on your phone or emulator, launch the app. Now we need to find the PID of this app.
Luckily for us, adb has a command we can execute
adb jdwp. This will simply list all PIDs hosting
a JDWP transport (the protocol used for communication between jdb and your app). Since we just
launched the app, the apps PID will be at the bottom of the list:
$ adb jdwp 58 108 115 114 120 151 157 169 176 192 208 233 243 290
In my case, I need to forward a local socket to PID 290 to start debugging. I can do this by using
adb forward tcp:8700 jdwp:290. This tells adb to forward a local socket on port 8700
to the app with PID 290 that’s hosting a JDWP transport. Now we can start debugging the simple test
app by issuing
jdb -attach localhost:8700 -sourcepath /path/to/test/app/src. If all goes well,
you’ll have a jdb prompt in which you can start adding breakpoints.
Let’s start by placing a breakpoint when we click the button. Since the
technically a nested class we cannot simply place a breakpoint at line 22 like you’d expect.
Instead we must inspect the
MainActivity class by issuing:
> class com.test.MainActivity Class: com.test.MainActivity extends: android.app.Activity nested: com.test.MainActivity$1
Ah, there we see the nested class which is our
OnClickListener, but just to be sure let’s inspect
that as well:
> class com.test.MainActivity$1 Class: com.test.MainActivity$1 extends: java.lang.Object implements: android.view.View$OnClickListener
Right there we can see it is indeed our
OnClickListener for the button and we can now add a
breakpoint to the
onClick method by issuing
stop in com.test.MainActivity$1.onClick. Now
when we click our button in the app, the breakpoint will be hit and we can start using jdb
to debug the app how we see fit.
We can even create a script to automate launching the app, forwarding the socket and attaching the debugger like so:
1 2 3 4 5 6 7 8 9 10 11
NOTE: One caveat to all this is if your app runs in multiple processes, then you’ll have to forward a port for each process the app runs in and run multiple jdb sessions.