Better Debugging Quality in Android Development
There are some phases in development that you can’t help to rewrite that login or registration form again and again just to find that stubborn little bug which ruins your itty bitty tiny immaculate feature. This process my friends, takes so much from us for no reason and its time to say stop.
In this article we will go through a bossy journey which is so much normal for us, the developers, since we love giving commands to left and right, here and there 😀.
Today we will not only control our emulators or test devices but also our primal instincts to fill that form countless times.
You may ask: How is that ?? We will utilize adbcommands for that. ADB(Android Debug Bridge) is a command line tool to control our Android devices or emulators. STOP. DO NOT GO ANYWHERE with just hearing “android debu…” “comma…li…to”. I know they sound a little boring but I promise i will do my best to make it less gross.
Actually in the end you won’t need to use any of these commands more than once or twice or thrice or… we will make them of our service with just a shortcut that you will choose. (or at least we will reduce the work to just one command in case you are using Windows. 🙂).
Creating Customizable Input Flow Scripts
First things first, here are the commands to interact with our device/emulator:
# Open or run commands in a terminal on the host Android device.
adb shell
# Types given string to currently active TextField
adb shell input text "Hello World!"
# Taps to given coordinates on the device screen
adb shell input tap 700 1500Now that you might think “Will we use static coordinates to find our textfield?” No absolutely not, but you can, if you want to make things harder for you :D. To find the coordinates of the UI elements we expect to be present in our screens, we will use uiautomator’s dump. You’ll need python3 to download it.
adb exec-out uiautomator dump /dev/ttyThis command is where the magic begins. UI automator creates a dump for the current screen view hierarchy in XML format. When you type this command you will see this file in your terminal. And the part we interested in is actually [bounds] tag of each element in this file and here is the beautified version of one of its nodes.
Note: uiautomator is hybrid in terms of UI tool you are using. Compose and Views both are working fine.
With that being said I guess you get the drill. We should create a parser to get these bounds for each element when desired and of course the way to reach related elements is calling by their name :). In the script below we effectively reach that goal utilizing terminal commands.
# Function to tap on a UI element if it exists
function tapIfExists(){
# Get the coordinates of the UI element
coords=$(getCoords "${1}")
# Check if coordinates are found
if [[ "$coords" != "-" ]]; then
# Tap on the coordinates using adb
adb shell input tap "$coords"
fi
}
# Function to get the coordinates of a UI element based on its text
function getCoords(){
# Dump the UI hierarchy and store it in a variable
fastdump=$(/usr/bin/python3 -c "import uiautomator2 as u2;d=u2.connect(); out=d.dump_hierarchy(); print(out)")
# Check if the element with the specified text is present
present=$(echo "$fastdump" | grep "text=\"${1}\"")
# If the element is found
if [[ $? -eq 0 ]]; then
# Extract coordinates from the dump
arr=($(echo "$fastdump" | sed 's/></>\n</g' | grep "text=\"${1}\"" | grep -o "\[.*\]" | sed 's/\]\[/,/g' | sed 's/\]//g;s/\[//g;s/,/\n/g'))
# Calculate the center x-coordinate
x=$(echo "(${arr[0]}+${arr[2]})/2" | bc)
# Calculate the center y-coordinate
y=$(echo "(${arr[1]}+${arr[3]})/2" | bc)
# Output the coordinates
echo "$x $y"
else
# If the element is not found, output "-"
echo "-"
fi
}Having that, everything is much much easier. I created a login flow for example:
# ...
# Adding your custom logic at the end of the file above...
tapIfExists "E-mail"
adb shell input text "sevbanbuyer@gmail.com"
tapIfExists "Password"
adb shell input text "asdasdfasd"
tapIfExists "SIGN IN"With this script we reduced our register flow to just one command: ./<your-script-file-name> but we are not done yet. If you’re using Mac we can create a shortcut for our input flow. For that, first you need to create a Applescript workflow with Automator app in Macs.
#Put this to your applescript in Automator
on run {input, parameters}
do shell script "<path-to-your-script>"
end runIMPORTANT: When creating an Applescript you must be using full path of your adb program in your PC. So you can modify your input script like this:
#!/bin/bash
ADB_PATH="<full-path-to-adb>" # CAUTION !
function tapIfExists(){
coords=$(getCoords "${1}")
if [[ "$coords" != "-" ]]; then
$ADB_PATH shell input tap "$coords" # CAUTION !
fi
}
function getCoords(){
fastdump=$(/usr/bin/python3 -c "import uiautomator2 as u2;d=u2.connect(); out=d.dump_hierarchy(); print(out)")
present=$(echo "$fastdump" | grep "text\=\"${1}\"")
if [[ $? -eq 0 ]]; then
arr=($(echo "$fastdump" | sed 's/></>\n</g' | grep "text\=\"${1}\"" | grep -o "\[.*\]" | sed 's/\]\[/,/g' | sed 's/\]//g;s/\[//g;s/,/\n/g'))
x=$(echo "(${arr[0]}+${arr[2]})/2" | bc)
y=$(echo "(${arr[1]}+${arr[3]})/2" | bc)
echo "$x $y"
else
echo "-"
fi
}
tapIfExists "E-mail"
$ADB_PATH shell input text "sevbanbuyer@gmail.com" # CAUTION !
tapIfExists "Password"
$ADB_PATH input text "asdasdfasd" # CAUTION !
tapIfExists "SIGN IN"After giving our full pathed script to Applescript we should go to Settings > Keyboard> Keyboard Shortcuts > Services > General and you will see your script in that section. Now you can click on them and assign a shortcut you want.
And here is the output:
Dumpsys
dumpsys command provides diagnostic about for all system services running on a device.
You can get info about;
- activities & fragments
- battery
- alarms
- package info
- job scheduler
You can make educated guesses about bugs as you can find out what’s really going on in those services when interacting them with your code.
Get Sevban Bayır’s stories in your inbox
Join Medium for free to get updates from this writer.
“I set an alarm with AlarmManager but is it really set ?”
“I inflated a fragment but I can’t see anything Did I do it correctly ?”
- To analyze apps you are reviewing and see what you are actually looking at,
dumpsysis a blessed tool.
Here is the command:
adb shell dumpsys <service>
adb shell dumpsys batteryWe can search in dumpsys using grep. For example:
adb shell dumpsys | grep "ACTIVITY"
adb shell dumpsys | grep "BottomNavigationItemView"Viewing/Altering Local Files’ Content
We often face challenges when dealing with local storage and since;
- setting breakpoints to query storage data
- adding printlines or Logs
requires edit & build and this is time consuming we have the opportunity to optimize these debugging process as well.
- app’s internal data is stored in
data/path. - and in theory we can go see these data in our devices using regular terminal commands like cd, ls, echo, nano etc. but in practice you will face this:
Android simply does not allow access to an app’s resources that easy and this is a good thing. I guess.
By the way the exact path of your app’s internal data is: /data/user/0/<your.package.name> but as you can see it does not help to know this exact path to get in there so we need another way out.
For this we will benefit the fact that every app in Android is actually a user called with package name as an alias:
We utilized run-as command to pretend like we are our app. Now we can lsin our app’s internal storage and see data like sharedPref, databases or files you created in your codebase with file APIs.
and BUM! here is my app’s shared preferences file with its onboarding_key and value of it:
Now you will ask how can i edit these preferences values to not to lose time with rebuilding my app without being sure that it is changed. Well, we can not use any text editors we normally use in our terminals in adb shell but there is a workaround for this: sedcommand. With the -iflag it can override files in place so here is the application of this for my app’s onboarding key:
emu64a:/data/user/0/com.sevban.tradejournal/shared_prefs $ grep "onboarding_key" presentation.MainActivity.xml
<boolean name="onboarding_key" value="true" />
emu64a:/data/user/0/com.sevban.tradejournal/shared_prefs $
cat presentation.MainActivity.xml |
grep "onboarding_key" |
sed -i "s/true/false/g" presentation.MainActivity.xml
emu64a:/data/user/0/com.sevban.tradejournal/shared_prefs $ grep "onboarding_key" presentation.MainActivity.xml
<boolean name="onboarding_key" value="false" />Conclusion
Enhancing your debugging process in Android development doesn’t have to be tedious. By leveraging ADB and tools like uiautomator, you can automate repetitive tasks, such as form-filling, and gain deeper insights into your app’s behavior.
Creating customizable input flow scripts allows you to automate UI interactions, making debugging more efficient and less monotonous. Using dumpsys and accessing local files with run-as and sed commands enables you to diagnose and fix issues more effectively.
These techniques can transform your debugging process, making it more efficient and insightful. Embrace these tools, automate where possible, and enjoy a smoother, more productive development experience.
References:










