memory leaks: Empty Android project + Dagger2 + LeakCanary - dagger-2

Faced such a problem.
I create a new project with Empty Activity, and add the dependencies of the project, dagger2 and leakcanary.
Run the application, then close and the leakcanary displays a memory leak.
Add dependencies
android {
compileSdkVersion 26
buildToolsVersion '26.0.3'
defaultConfig {
applicationId "xxxxx"
minSdkVersion 16
targetSdkVersion 26
versionCode 1
versionName "1.0"
}
}
//Dagger
implementation "com.google.dagger:dagger:2.14.1"
kapt "com.google.dagger:dagger-compiler:2.14.1"
//Debug
debugImplementation "com.squareup.leakcanary:leakcanary-android:1.5.4"
Create appkication class and init leakcanary
class AppDelegate: Application() {
override fun onCreate() {
super.onCreate()
onApplicationCreate()
initDagger(this)
}
private fun initDagger(app: Application): AppComponent =
DaggerAppComponent.builder()
.appModule(AppModule(app))
.dataModule(DataModule())
.build()
private fun onApplicationCreate() {
if (BuildConfig.DEBUG) {
if (LeakCanary.isInAnalyzerProcess(this)) {
return
}
LeakCanary.install(this)
}
}
}
MainActivity class
class MainActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
Create dagger component
#Singleton
#Component(modules = arrayOf(AppModule::class, DataModule::class))
interface AppComponent {
}
#Module
class AppModule(private val appContext: Application) {
#Provides
fun provideContext() = appContext
}
#Module
class DataModule {
}
Here is what leakcanary shows screen
If the dependence of the dagger2 is removed, then there is no leakage.
What am I doing wrong?

Related

ViewModel Junit Test fails in case when i use Junit5 and Rxjava. Method getMainLooper in android.os.Looper not mocked

I am trying to wrote test case for my view model and I am using junit5 along with mockito. The issue is my test case is always failing it gives me this error
at android.os.Looper.getMainLooper(Looper.java)
at io.reactivex.android.schedulers.AndroidSchedulers$MainHolder.<clinit>
Now I did a lot of research on this issue and found out that this is due to Rxjava scheduler as it is not able to run on the background thread I followed this link and did the required step so after that my view model looks like this
MainViewModelTest.kt
#ExtendWith(value = [InstantExecutorExtension::class,TestSchedulerExtension::class])
class MainViewModelTest {
#Mock
private lateinit var repository: GithubRepository
private lateinit var viewModel: MainViewModel
#Before
fun init() {
MockitoAnnotations.initMocks(this)
viewModel = MainViewModel(repository)
}
#Test
fun testNull(){
assertThat(viewModel.observeTrendingRepository(), notNullValue())
verify(repository, never()).makeRequestForTrendingRepo(anyBoolean())
}
#Test
fun fetchTrendingGitHubRepository_emptyDbFirstTimeCase(){
`when`(repository.makeRequestForTrendingRepo(anyBoolean()))
.thenReturn(Flowable.just(Resource.Loading(listOf())))
viewModel.fetchTrendingGitHubRepository(false) // this is where it fails
assertEquals(Resource.Loading<List<Repository>>(listOf()),LiveDataTestUtil.getValue(viewModel.observeTrendingRepository()));
}
}
Here are the InstantExecutorExtension and TestSchedulerExtension that take care of live data and Rxjava respectively.
package com.rajat.zomatotest.utils
import androidx.arch.core.executor.ArchTaskExecutor
import androidx.arch.core.executor.TaskExecutor
import org.junit.jupiter.api.extension.AfterEachCallback
import org.junit.jupiter.api.extension.BeforeEachCallback
import org.junit.jupiter.api.extension.ExtensionContext
class InstantExecutorExtension : BeforeEachCallback, AfterEachCallback {
override fun beforeEach(context: ExtensionContext?) {
ArchTaskExecutor.getInstance().setDelegate(object : TaskExecutor() {
override fun executeOnDiskIO(runnable: Runnable) {
runnable.run()
}
override fun postToMainThread(runnable: Runnable) {
runnable.run()
}
override fun isMainThread(): Boolean {
return true
}
})
}
override fun afterEach(context: ExtensionContext?) {
ArchTaskExecutor.getInstance().setDelegate(null)
}
}
package com.rajat.zomatotest.utils
import io.reactivex.android.plugins.RxAndroidPlugins
import io.reactivex.plugins.RxJavaPlugins
import io.reactivex.schedulers.Schedulers
import org.junit.jupiter.api.extension.AfterTestExecutionCallback
import org.junit.jupiter.api.extension.BeforeTestExecutionCallback
import org.junit.jupiter.api.extension.ExtensionContext
class TestSchedulerExtension : BeforeTestExecutionCallback, AfterTestExecutionCallback {
override fun beforeTestExecution(context: ExtensionContext?) {
RxJavaPlugins.setIoSchedulerHandler { Schedulers.trampoline() }
RxJavaPlugins.setComputationSchedulerHandler { Schedulers.trampoline() }
RxJavaPlugins.setNewThreadSchedulerHandler { Schedulers.trampoline() }
RxAndroidPlugins.setMainThreadSchedulerHandler { Schedulers.trampoline() }
}
override fun afterTestExecution(context: ExtensionContext?) {
RxJavaPlugins.reset()
RxAndroidPlugins.reset()
}
}
I couldn't understand why is it giving me error, I have correctly added TestSchedulerExtension.kt so this should work!!
I run into the same problem, and I found that the way to fix this was by using
RxAndroidPlugins.setInitMainThreadSchedulerHandler { scheduler }
instead of
RxAndroidPlugins.setMainThreadSchedulerHandler { scheduler }
In this link you will find more information.

Cannot access 'TAG': it is invisible (private in supertype) in 'AppCompatActivity'

I am a beginner in android programming. I'm currently using android studio 3.2.1.
I am trying to monitor the different states of an android activity in log. I have written the code shown below, but I keep receiving the error message:
Cannot access 'TAG': it is invisible (private in supertype) in 'AppCompatActivity'.
Even after searching, I cannot figure the error. Can some one help?
Code:
package com.cooperation.bestech.test1
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.util.Log;
class MainActivity : AppCompatActivity() {
private static final String TAG = "MyMessage";
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.i(TAG, "onCreate");
}
override fun onStart() {
super.onStart()
Log.i(TAG, "onStart");
}
override fun onPause() {
super.onPause()
Log.i(TAG, "onPause");
}
override fun onResume() {
super.onResume()
Log.i(TAG, "onResume");
}
override fun onStop() {
super.onStop()
Log.i(TAG, "onStop");
}
override fun onRestart() {
super.onRestart()
Log.i(TAG, "onRestart");
}
override fun onDestroy() {
super.onDestroy()
Log.i(TAG, "onDestroy");
}
override fun onSaveInstanceState(outState: Bundle?) {
super.onSaveInstanceState(outState)
Log.i(TAG, "onSaveInstanceState");
}
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
super.onRestoreInstanceState(savedInstanceState)
Log.i(TAG, "onRestoreInstanceState");
}
}
Since the original question is written in Kotlin (from comment), ignore the request of "launch Java project instead of the default Kotlin" may be a better solution.
And, applying the correct way to declare and assign a variable, your code segment should be like this:
class MainActivity : AppCompatActivity() {
val TAG = "MyMessage"
import android.content.ContentValues.TAG
Seems you are using Kotlin.So you need to define TAG in kotlin way:
In Kotlin constants located in the companion object:
class MyClass {
companion object {
private val TAG = "ClassName"
}
}

Dagger2 Circular Dependency Error

I am using dagger2 2.16 version for dependency injection inside mine android project. I examine a lot of examples, and although I do not have a similar approach I get the error of "circular dependency".
Mine source code;
AppComponent.kt
#Singleton
#Component(
modules = [
AndroidSupportInjectionModule::class,
AppModule::class,
ActivityBuilderModule::class]
)
interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): AppComponent
}
fun inject(app: App)
}
App.kt
class App : Application(), HasActivityInjector {
#Inject
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Activity>
override fun onCreate() {
super.onCreate()
AppInjector.init(this)
initOneSignal()
}
private fun initOneSignal() = OneSignal.startInit(this).setNotificationOpenedHandler(CustomNotificationOpenedHandler()).inFocusDisplaying(OneSignal.OSInFocusDisplayOption.Notification).init()
override fun activityInjector() = dispatchingAndroidInjector
}
ActivityBuilderModule.kt
#Module
abstract class ActivityBuilderModule {
#ContributesAndroidInjector
abstract fun contributeSplashActivity(): SplashActivity
}
AppModule.kt
#Module(includes = [(ViewModelModule::class)])
class AppModule {
#Singleton
#Provides
fun provideContext(app: Application): Context = app.applicationContext;
#Singleton
#Provides
fun provideApiService(client: OkHttpClient): ApiService {
return Retrofit.Builder()
.baseUrl(Constants.baseUrl)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
.create(ApiService::class.java)
}
#Singleton
#Provides
fun provideOkHttpClient(interceptor: HttpLoggingInterceptor): OkHttpClient {
return OkHttpClient.Builder().addInterceptor(interceptor).build()
}
#Singleton
#Provides
fun provideHttpLoggingInterceptor(): HttpLoggingInterceptor {
val interceptor = HttpLoggingInterceptor()
interceptor.level = HttpLoggingInterceptor.Level.BODY
return interceptor
}
}
If I remove the ActivityBuilderModule from the AppComponent, the project is compiled without problems. But if you add to the modules section, the project gives the error below.
error: [ComponentProcessor:MiscError]
dagger.internal.codegen.ComponentProcessor was unable to process this
interface because not all of its dependencies could be resolved. Check
for compilation errors or a circular dependency with generated code.
Please help me.
In App.kt you need to initialize component with the application context. the line
AppInjector.init(this)
should be inside the Activity i.e. splashActivity in which you're going to inject the dependencies.
The above mentioned error message might also appear, because kotlin stdlib is not declared as dependency. So adding e.g. implementation("org.jetbrains.kotlin:kotlin-stdlib:1.3.60") in your build.gradle(.kts) file might also help.

Dagger 2 with Instant App

I've been working on instant app for a few days. I didn't get it compiled yet due to Dagger issue.
The project is split into base, export, preview as features.
In export module
#ExportScope
#Component(
modules = [DrawExportModule::class],
dependencies = [AppComponent::class])
interface DrawExportComponent: AndroidInjector<LiveDrawingExportActivity> {
#Component.Builder
abstract class Builder : AndroidInjector.Builder<LiveDrawingExportActivity>(){
abstract fun plus(component: AppComponent): Builder
}
}
AppComponent.kt
#Singleton
#Component(modules = [
AndroidInjectionModule::class,
AppModule::class,
ActivityBindingModule::class,
ServiceBindingModule::class])
interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
fun application(context: Context): Builder
fun build(): AppComponent
}
fun inject(app: LiveMessageApplication)
}
AppModule.kt
#Module(includes = [(DataModule::class)])
class AppModule {
#Singleton
#Provides
fun provideStateManager(): StateManager {
return StateManager.getInstance()
}
}
Compile error log
/Users/*/features/export/build/tmp/kapt3/stubs/debug/*/ui/draw/di/DrawExportComponent.java:8:
error: [Dagger/MissingBinding] [dagger.android.AndroidInjector.inject(T)] *.ui.StateManager cannot be provided without an #Inject constructor or an #Provides-annotated method.
public abstract interface DrawExportComponent extends dagger.android.AndroidInjector<com.ctech.livemessage.ui.draw.LiveDrawingExportActivity> {
^
*.ui.StateManager is injected at
*.LiveDrawingExportActivity.stateManager
*.LiveDrawingExportActivity is injected at
dagger.android.AndroidInjector.inject(T)
It seems to me dependency component doesn't work in this case. I have no clue to go forward.
More information
base/build.gradle
apply plugin: 'com.android.feature'
apply plugin: 'io.fabric'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion deps.build.compileSdkVersion
baseFeature = true
defaultConfig {
minSdkVersion deps.build.minSdkVersion
targetSdkVersion deps.build.targetSdkVersion
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.debug
}
}
compileOptions {
sourceCompatibility 1.8
targetCompatibility 1.8
}
}
dependencies {
application project(":installed")
feature project(":features:export")
feature project(":features:preview")
}
export/build.gradle
apply plugin: 'com.android.feature'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion deps.build.compileSdkVersion
defaultConfig {
minSdkVersion deps.build.minSdkVersion
targetSdkVersion deps.build.targetSdkVersion
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
debuggable true
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), '../../proguard.pro'
signingConfig signingConfigs.debug
}
}
sourceSets {
main {
jniLibs.srcDirs 'libs'
}
}
compileOptions {
sourceCompatibility 1.8
targetCompatibility 1.8
}
}
dependencies {
implementation project(':features:base')
kapt deps.dagger.compiler
kapt deps.dagger.androidCompiler
}
The issue is that DrawExportComponent doesn't see StateManager, because the latter is part of AppComponent.
Unlike subcomponents, with component dependencies, dependent components don't get access to all of parent component. You have to explicitly specify what's shared, by adding:
fun stateManager(): StateManager
to your AppComponent.
More info in official documentation

fragment inner static class and gradle issues

Initially I was getting an error "This fragment inner class should be static" in the following code:
public class Activity4 extends FragmentActivity {
EditText mEdit;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_activity4);
}
public void selectDate(View view) {
DialogFragment newFragment = new SelectDateFragment();
newFragment.show(getSupportFragmentManager(), "DatePicker");
}
public void populateSetDate(int year, int month, int day) {
mEdit = (EditText) findViewById(R.id.editText);
mEdit.setText(day + "/" + month + "/" + year);
}
//error was here at SelectDateFragment
public class SelectDateFragment extends DialogFragment implements DatePickerDialog.OnDateSetListener {
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Calendar calendar = Calendar.getInstance();
int yy = calendar.get(Calendar.YEAR);
int mm = calendar.get(Calendar.MONTH);
int dd = calendar.get(Calendar.DAY_OF_MONTH);
return new DatePickerDialog(getActivity(), this, yy, mm, dd);
}
public void onDateSet(DatePicker view, int yy, int mm, int dd) {
populateSetDate(yy, mm + 1, dd);
}
}
}
I tried to figure out the solution & read on some post here that its a lint error and can be resolved by adding this to the gradle file:
lintOptions {
abortOnError false
}
but when I did that, I get the message that "cannot resolve symbol getDefaultProguardFile". This is my gradle file:
apply plugin: 'com.android.application'
android {
compileSdkVersion 22
buildToolsVersion "22.0.1"
defaultConfig {
applicationId "com.sample.pat1"
minSdkVersion 16
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
lintOptions {
abortOnError false
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.2.0'
}
And the previous error "This fragment inner class should be static" still persists. Surprisingly the app works just fine despite all this.. but I would like to know where I'm going wrong.. Please help!
If you don't want to rewrite the communication mechanic, you can mark fragment as a valid manually:
#SuppressLint("ValidFragment")
DialogFragment newFragment = new SelectDateFragment();
More description here
P.S. Proguard - it's totally another issue.