Kotlin, Android, how to debug coroutines correctly? - android-studio

I'm trying to debug my coroutines, and breakpoints placed into suspend function don't work. Pls help me understand why.
Working with Android Studio.
Ok, I launch a coroutine from viewModelScope:
viewModelScope.launch(IO) {
when(val result = interactor.getAllWords()){...}
}
In getAllWords() I wrote:
override suspend fun getAllWords(): WordResult {
val words = mutableListOf<Word>()
when (val wordsResult = getAllWordsWithoutFiltersApplying()) {}
...
return getWordsWithSelectedPattern()
I have two suspend functions: getAllWordsWithoutFiltersApplying() and getWordsWithSelectedPattern(). I have a breakpoints into both of them, but they did't trigger in debug mode.
At the same time, line val words = mutableListOf<Word>() is triggering, when I put breakpoint to its line.
And, if I put some log stuff into "untracing" function, they will be work. I say it to make it clear, suspend function works. Breakpoints are not.
What should I do to debug them?
*Screenshot added. Look at the left side with row of icons. Why my lines are not available?

Based on your sample code, you switch the coroutine context between MAIN and IO so when you set the breakpoint, make sure the suspend option is ALL
To show the option of the breakpoint. Set a breakpoint with the left click of your mouse, and then right click your mouse on the breakpoint.
If you are using the JetBrain IDE, according to the document, when you set the breakpoint to make sure the suspend option is ALL not thread. it works for me.
and more detail you can check the document

It still does not work for me because I run the app first then attach the debugger, but when I use debug instead, it works.

I know I'm late, but this is for those people who made similar mistakes like me. I've also faced this issue recently and the root cause was related to dependencies. Actually I added coroutine core dependency but I forget to add coroutine android dependency. Please make sure both dependencies are present in your gradle file as shown below.
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.4"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.4'

I had forgot minifyEnabled set to true in my build.gradle file
buildTypes {
release {
minifyEnabled true
}
debug {
minifyEnabled true <- Should be false for these breakpoints to work
}
}

Related

How to disable strange Android Studio comment indentation?

I'm seeing a strange thing happening in Android Studio. This is with Kotlin. In an if block, if there is a comment before a return statement, and I hit enter at the end of the comment to presumably write some code (or even another comment), it indents as if it's a new block:
fun someFun() {
if (true) {
// Some comment
// Wants me to indent???
// And again...
// And again...
return
}
}
It's driving me up the wall and I can't figure out why this behavior is happening or how to disable it. If the return statement isn't there, this doesn't happen. It also only happens inside an if block. Any help would be much appreciated.

How to prevent rawproto file generation or delete them automatically?

Android gradle plugin generates tons of .rawproto files in build/android-profile directory. What are they used for? Is there a way to disable this madness or automatically delete them?
I've been bugged by it for a long time, and now that I noticed there's gigabytes of this hogging my smallish SSD, I've decided to figure out a way to disable it. For me the most annoying thing before occupying too much space was gradlew clean leaving a build folder behind.
Only tested with com.android.tools.build:gradle:3.0.1, so YMMV.
TL;DR
For global application read last section, per-project use this in rootProject's build.gradle:
com.android.build.gradle.internal.profile.ProfilerInitializer.recordingBuildListener =
new com.android.build.gradle.internal.profile.RecordingBuildListener(
com.android.builder.profile.ProcessProfileWriter.get());
// and then `gradlew --stop && gradlew clean` to verify no build folder is left behind
Investigation
Thanks to https://stackoverflow.com/a/43910084/253468 linked by #JeffRichards mentioning ProcessProfileWriterFactory.java, I've put a breakpoint there and checked who's calling it by running gradlew -Dorg.gradle.debug=true --info (not to be confused with --debug) and attaching a remote debugger.
I followed the trail and found that ProcessProfileWriter.finishAndMaybeWrite creates the folder and writes. Backtracing on method calls I found that ProfilerInitializer.recordingBuildListener controls whether it's called ... and that is initialized directly by BasePlugin (apply plugin: 'com.android.*').
So in order to prevent anything from happening I opted to try to disable the guard, by pre-initialized that static field. Thankfully Groovy (and hence Gradle) doesn't give a * about JVM visibility modifiers, so without reflection here's the magic line:
com.android.build.gradle.internal.profile.ProfilerInitializer.recordingBuildListener =
new com.android.build.gradle.internal.profile.RecordingBuildListener(
com.android.builder.profile.ProcessProfileWriter.get());
I know, it's a bit verbose, but it works, and if you import stuff it looks better:
ProfilerInitializer.recordingBuildListener = new RecordingBuildListener(ProcessProfileWriter.get());
Applying the magic
In a single-project build (one build.gradle) you must apply this before
apply plugin: 'com.android.application'
In multi-project builds (most template projects: app folder, settings.gradle, and many build.gradles) I suggest you apply it around the buildscript block:
buildscript {
// ...
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
}
}
// magic line here
Make sure it's before any apply plugin:s, and not inside a buildscript block.
Applying the magic globally
Obviously if it bothers us in one project, it will in all cases, so following Gradle's manual, create a file in ~/.gradle/init.gradle or %USERPROFILE%\.gradle\init.gradle (mind you this folder can be relocated with GRADLE_USER_HOME) with the following contents:
// NB: any changes to this script require a new daemon (`gradlew --stop` or `gradlew --no-daemon <tasks>`)
rootProject { rootProject -> // see https://stackoverflow.com/a/48087543/253468
// listen for lifecycle events on the project's plugins
rootProject.plugins.whenPluginAdded { plugin ->
// check if any Android plugin is being applied (not necessarily just 'com.android.application')
// this plugin is actually exactly for this purpose: to get notified
if (plugin.class.name == 'com.android.build.gradle.api.AndroidBasePlugin') {
logger.info 'Turning off `build/android-profile/profile-*.(rawproto|json)` generation.'
// execute the hack in the context of the buildscript, not in this initscript
new GroovyShell(plugin.class.classLoader).evaluate("""
com.android.build.gradle.internal.profile.ProfilerInitializer.recordingBuildListener =
new com.android.build.gradle.internal.profile.RecordingBuildListener(
com.android.builder.profile.ProcessProfileWriter.get());
""")
}
}
}

Android Studio -- clear application data for Instrumentation Test

How can I get Android Studio (AndroidJunitRunner) to clear application data preceding an instrumentation test without manually running adb command?
I discovered that android.support.test.runner.AndroidJUnitRunner kind of cheats -- it never actually invokes connectedCheck or connectedAndroidTest.
When run from command line $ gradle connectedCheck
:MyMainApp:assembleDebug UP-TO-DATE
:MyMainApp:assembleDebugTest UP-TO-DATE
:MyMainApp:clearMainAppData
:MyMainApp:connectedCheck
When run from within IDE by clicking the instrumentation test configuration (green Android robot logo with red/green arrows)
**Executing tasks: [:MyMainAppApp:assembleDebug, :MyMainAppApp:assembleDebugTest]**
As you can see, the last gradle target is assembleDebugTest
I had added a hook onto connectedCheck in build.gradle to clear the data of the main app before starting the instrumentation test.
// Run 'adb' shell command to clear application data of main app for 'debug' variant
task clearMainAppData(type: Exec) {
// we have to iterate to find the 'debug' variant to obtain a variant reference
android.applicationVariants.all { variant ->
if (variant.name.equals("debug")) {
def clearDataCommand = ['adb', 'shell', 'pm', 'clear', getPackageName(variant)]
println "Clearing application data of ${variant.name} variant: [${clearDataCommand}]"
commandLine clearDataCommand
}
}
}
// Clear Application Data (once) before running instrumentation test
tasks.whenTaskAdded { task ->
// Both of these targets are equivalent today, although in future connectedCheck
// will also include connectedUiAutomatorTest (not implemented yet)
if(task.name.equals("connectedAndroidTest") || task.name.equals("connectedCheck" )){
task.dependsOn(clearMainAppData)
}
}
I realize that alternatively I could implement a 'clear data' button in the main app and have the instrumentation app click through the UI, but I find that solution undesirable.
I looked at AndroidJUnitRunner API and there are hooks via Runlistener interface but hooks are during the context of the test app, i.e. running on device, and Android forbids one app from modifying another app.
http://junit.sourceforge.net/javadoc/org/junit/runner/notification/RunListener.html
Best answer goes to you if you can help me trigger one of the following automatically from within Android Studio:
execute a command line adb shell pm clear my.main.app.package,
or preferably invoke my gradle task clearMainAppData
I'm also all ears if there is an alternate way. Surely with device testing automation there should be a clear way to clear application data?
Thank you!
I know it's been a while, and hopefully by now you will have this issue sorted.
I ran into that same issue today, and crashed here without any solution.
But I managed to make it work by calling my task from the test configuration.
Step 1 : Go to your test configuration
Step 2 : Simply add the gradle task you created
By the way, the task in my case simply looks like this :
task clearData(type: Exec) {
def clearDataCommand = ['adb', 'shell', 'pm', 'clear', 'com.your.application']
commandLine clearDataCommand
}
Hope this will help someone :)
With Android Test Orchestrator it is easier to provide this option via gradle script.
android {
defaultConfig {
...
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
// The following argument makes the Android Test Orchestrator run its
// "pm clear" command after each test invocation. This command ensures
// that the app's state is completely cleared between tests.
testInstrumentationRunnerArguments clearPackageData: 'true'
}
Below is the link for Android Test Orchestrator
https://developer.android.com/training/testing/junit-runner#using-android-test-orchestrator

How can I make Visual Studio 2012 break on Debug.Assert for a Windows Store application? [duplicate]

I notice Debug.Assert does not trigger in Metro apps, however, if the project is a traditional one like Console or WinForm, it does trigger. And yes, I am in Debug mode.
Is it a setting not properly set in Visual Studio (11 Beta)? Or Debug.Assert is intended to be disabled in metro apps?
I know many exceptions are swallowed during the execution of Metro apps, but Debug.Assert is so handy that I can't think of a reason why it should be disabled.
Seems like a bug. I would roll out my own assert method. Something like:
[Conditional("DEBUG")]
public static void Assert(bool condition)
{
if (!condition)
System.Diagnostics.Debugger.Break();
}
It does trigger, look in the Output window. It just doesn't automatically prompt you to ask if you want a debugger break and thus just keeps motoring.
The DefaultTraceListener.AssertUIEnabled property is false. That's an implementation problem, can't display a message box on top of Metro UI. Which does actually work but the monitor switches to the desktop, pretty undesirable when you would have liked to click No. Hard to solve and no doubt on the todo list. You can't easily get to the property to set it to true, it is inaccessible from the metadata. Filip's workaround sounds half-decent.
There is the same problem with F# in WinRT, in VS2013. The assert statement, which is an alias for System.Diagnostics.Debug.Assert, does not raise an exception, so unless you are watching the Output window then your assertions can fail without being noticed. Even if you are watching, it is hard to find the spot where the assertion was raised.
I followed Filip's suggestion and wrote a short utility, as follows:
namespace MyProj.Infrastructure
module Diagnostics =
let Assert condition = if not condition then
System.Diagnostics.Debugger.Break()
I chose Debugger.Break over raising an exception because it stops the debugger at the place the assertion fails. However, raising an exception is an acceptable alternative.
I didn't have any suitable global projects or modules already in my solution, so I had to create them just for this, which was quite annoying.

In Qt on Linux/X11, how do I fix main window ordering issue?

I've got a Qt application that has two main windows. On Linux, when one main window brings up a modal dialog, it comes up behind the other main window. What can I do to cause the dialog to always come up on top of ALL main windows?
NOTE: This only happens on Linux. We build this app on MacOSX as well, and the problem does not occur there.
Here's the code that brings up the dialog. The stuff in the #if is all the things I've tried to bring the window forward. I've tried various combinations and orders of these things.
QMessageBox dialog;
dialog.setIcon( QMessageBox::Information );
dialog.setWindowTitle( _documentName );
dialog.setText( tr("This document has unsaved changes. Do you want to save before closing?") );
dialog.setInformativeText( tr("Your changes will be lost if you don't save them.") );
dialog.setStandardButtons( QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel );
dialog.setDefaultButton( QMessageBox::Save );
dialog.setFixedSize( dialog.size() ); // non-resizable window
#if STUFF_I_TRIED
dialog.show();
dialog.setVisible(true);
dialog.open();
dialog.activateWindow();
dialog.raise();
#endif
int result = dialog.exec();
I realize that exec() should be all I need to show the window. My idea in calling show() or open() was just to allow activateWindow() or raise() to take affect. Just foolin' around trying to get that damn dialog to come forward.
TIA for any help!
All the sequcence between #if 1_ and #endif looks pretty weird to me.
Normally, to show modal dialog, only exec() is needed:
QMessageBox msgBox;
msgBox.setText("They killed Kenny, again.");
int ret = msgBox.exec();
Reference.
You are doing quite a bit of things between your #if 1, that is likely confusing X11.
You need only ONE of those. Since you are working with Mac and X11, I suspect you want to use open() and get a sheet.
IIRC, show() vs. open() causes different window flags to be set, so
calling them right after each other may get the window into a strange
state. Also calling show() or open() should always activate or raise the window if it is a dialog, which QMessageBox is.
Try only using one of these and seeing what happens.

Resources