Table of Contents
Getting Ready to Run Your App
When your first Android project is created, Android Studio generates a basic app that can already run. Before focusing on new code, it is important to learn how to launch this app, how to observe what it does, and how to find and fix problems when something goes wrong.
To run an Android app you always need a target device. This can be an Android Virtual Device in the emulator or a physical phone or tablet. You learned how to set up both earlier, so here you will focus on how to select a device and start the app from Android Studio.
At the top of Android Studio you will see a device selector and a run configuration selector. The run configuration usually matches your main activity, for example app or app.main. Next to it there is a green triangle icon for Run and a green bug icon for Debug.
To run the app, make sure the correct device is selected from the device drop down, then click the Run icon. Android Studio builds your project, installs it on the selected device, and starts the main activity. You can watch the status at the bottom of the window, where the Build tool window shows progress. If the build is successful, the app opens on the device screen. If build errors occur, they appear in the Build tool window with red text and you will not see the app start until they are fixed.
You can run the same app on different devices by simply switching the selected device and pressing Run again. This is how you can quickly check how your layout looks on different screen sizes or different Android versions.
You can only run an app on one device at a time per Run action. Always confirm that the intended device is selected before clicking Run.
Understanding Build and Install Problems
When you press Run, Android Studio performs several steps. It compiles your Kotlin code, processes your resources, packages everything into an APK or app bundle, then installs and launches it on the device. Problems can appear at different stages, and recognizing where they occur is the first part of debugging.
If the problem happens during compilation, Android Studio shows a red error in the Build tool window. Often you will see a link to the exact file and line number where the compiler failed. At this point nothing is installed on the device, so you must correct the code or resources and try again.
If compilation succeeds but installation fails, you might see messages about the device being offline, out of storage, or having an incompatible app version. In this case the problem is not in your code but in the connection or device state. Restarting the emulator, reconnecting the physical device, or freeing some space on the device often solves it.
Sometimes the app installs successfully but closes immediately when it starts. This usually means your app crashed at runtime. The app window may briefly appear and then disappear. In this situation, Logcat becomes your most important source of information.
Using Run vs Debug Modes
There are two main ways to start your app from Android Studio. You can run it normally, or you can start it in debug mode. Both build and install the app, but they offer different levels of insight while it is running.
The normal Run option is best when you only want to see how the app behaves from the user perspective. It launches quickly, and you can interact with the app on the device as any user would. You still have access to Logcat output, but the debugger does not attach to the app.
The Debug option starts your app under the control of the debugger. When you use Debug, Android Studio can pause your app at specific lines of code, inspect variables, and step through methods line by line. Debug mode is slower than a normal run, but it is essential when you are trying to understand why a function does not behave as expected.
To start in debug mode, select your device as usual, then click the green bug icon instead of the triangle. The app installs, launches, and the debugger attaches to its process. If you set breakpoints, execution will stop automatically when your app reaches those locations.
Use Run for quick checks of visual behavior. Use Debug when you need to inspect code execution and variable values in detail.
Working with Logcat Output
Logcat is a continuous stream of messages from your app and the Android system. It is one of the most important tools for observing what happens while your app runs. Even when your app seems to work correctly, Logcat helps you confirm that the correct methods are called and that no hidden errors are occurring in the background.
In Android Studio, open the Logcat window usually located at the bottom. You will see a device selector and an app process selector. Make sure your current device and your app package are chosen so that you only see relevant messages.
By default Logcat can show many messages from the entire system. To reduce noise you can filter by log level. Common levels are Verbose, Debug, Info, Warn, Error, and Assert. When tracking down a crash, filtering by Error is often the most helpful. When following your own debug messages, filtering by Debug or Info is usually enough.
You can also search within Logcat by typing text in the search field. When your app crashes, the most important part of Logcat is the stack trace. This is the block of text that begins with an exception name such as java.lang.NullPointerException followed by a list of method calls. Within this stack trace, look for lines that reference your own package name and the exact file and line number where the error occurred. That is where you need to investigate.
Adding Your Own Log Messages
You do not need to rely only on system messages. You can also print your own messages into Logcat to understand what your app is doing at runtime. This is especially useful if your app does not crash but behaves in an unexpected way.
In Kotlin code you can use the Log class to write messages. For example, to log that a function has started you can write:
import android.util.Log
class MainActivity : AppCompatActivity() {
private val TAG = "MainActivity"
override fun onStart() {
super.onStart()
Log.d(TAG, "onStart called")
}
}
Here Log.d stands for a debug-level message. You can choose other levels such as Log.i for informational messages or Log.e for errors. In Logcat you can filter using the same tag name that you used in code, such as MainActivity, so that only your messages are visible.
It is common to log the values of variables when you are not sure they contain what you expect. For example, Log.d(TAG, "User name is $userName") helps you see the actual value at runtime. After you find and fix the problem you can remove or lower the number of log messages to keep Logcat clean.
Avoid leaving sensitive information such as passwords or personal user data in log messages. Logs can be visible to other tools and should never contain private information.
Setting and Using Breakpoints
Breakpoints are special markers that tell the debugger to pause your app at a specific line of code. When execution stops at a breakpoint, you can examine every variable in the current scope, evaluate expressions, and step through the code one line at a time. This process is central to understanding the real behavior of your code.
To set a breakpoint in Android Studio, click in the left gutter next to the line number in your Kotlin file. A red dot appears, marking the breakpoint. Once you run the app in Debug mode, the debugger stops at the first breakpoint it reaches.
When the app is paused, Android Studio highlights the current line. At this point the UI on the device is frozen until you resume execution. In the Debug tool window you can see the call stack showing how the program reached this line. You also see a Variables panel listing all local variables and their current values.
You can control execution with several buttons. Step Over moves to the next line in the same method, Step Into moves inside a method call, and Step Out completes the current method and returns to the caller. Resume continues normal execution until the next breakpoint or until the app finishes. By stepping line by line you can observe exactly where the behavior diverges from your expectations.
If you no longer need a breakpoint, click again on the red dot to remove it, or disable it temporarily if you want to keep it for later. You can also right click a breakpoint to make it conditional so that it only stops when a specific expression is true, for example when a variable has a certain value.
Watching Variables and Expressions
Sometimes you need to follow the value of one particular variable as your app runs through several lines or methods. While the debugger is paused at a breakpoint, you can add variables to the Watches list. The debugger then keeps track of those variables and updates their values whenever you stop again.
In the Debug tool window, use the Watches section to add an expression such as userName or even simple calculations like items.size. Every time the app pauses, you can quickly see how these values have changed. This makes it easier to confirm whether your logic manipulates data correctly.
You can also hover the mouse over a variable directly in the editor while paused. Android Studio shows a small popup with the current value. For more complex objects, such as data classes or lists, you can expand them in the Variables or Watches panel to see their internal fields and elements.
For one-time checks you can use the Evaluate Expression feature. This opens a dialog where you can type any valid Kotlin expression using the current variables, then see the result immediately. You can try different expressions without changing your actual source code.
When the debugger is paused at a breakpoint, the app on the device is temporarily frozen. Do not confuse this paused state with a crash. Execution will resume when you use the Debug controls.
Handling and Investigating Crashes
Crashes are a normal part of development. What matters is how quickly you can find the cause and fix it. When your app stops unexpectedly, the device typically shows a message that the app has stopped. At this moment Logcat and the debugger can help you understand why.
If you are running in normal mode, switch to the Logcat window and look for a recent Error entry with the word "Exception" in it. Expand the stack trace and look for lines that mention your package name and include a file and line number. Open that file and inspect the code around that line.
If you are in debug mode and a crash occurs, the debugger usually stops on the line where the exception was thrown. You can examine variables and the call stack directly. Often the cause is an unexpected null value, an out of bounds list index, or a wrong type cast. Once identified, fix the underlying logic or add proper checks before using the value.
Crashes during app startup can be particularly confusing because you may not see any UI at all. In that case rely heavily on Logcat and the debugger. Set breakpoints in onCreate or other early lifecycle methods to see how far the app runs before failing.
After you apply a fix, run the app again and repeat the same user actions that caused the crash earlier. Confirm that the crash no longer occurs, and check Logcat to ensure there are no new errors.
Rebuilding and Rerunning After Changes
Every time you modify code or resources, Android Studio needs to rebuild the app before changes appear on the device. When you click Run or Debug, Android Studio performs an incremental build that includes the latest changes. Sometimes, especially after larger changes to Gradle or resource configurations, an incremental build can behave strangely. In these cases selecting Rebuild Project from the Build menu forces a full clean build.
If you change only small parts of the code, Android Studio may apply the changes more quickly using hot swap or similar incremental techniques. Even then you should still watch the Build tool window to confirm that the build succeeded.
Each time you rerun the app, the previous version on the device is usually replaced with the new one. If you see unexpected behavior that does not match your current code, double check that the correct build variant is active and that the correct device is used. Occasionally uninstalling the app from the device and installing it again from Android Studio can clear old data and resolve confusion.
By repeatedly editing, building, running, and debugging, you develop a tight feedback loop. This loop is how you gradually move from a simple template app to a stable and reliable application that behaves exactly as you intend.