ClassCastException while injecting dependencies with dagger - android

I'm using Dagger2 to inject my dependencies in all of my applications.
Some days ago I started getting Crash Reports for one of the applications from Samsung Android 7.0 (only these) devices.
java.lang.RuntimeException:
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2924)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2985)
..
Caused by: java.lang.ClassCastException:
at de.package.name.MyApplication.get(MyApplication.java:43)
at de.package.name.ui.base.BaseActivity.onCreate(BaseActivity.java:53)
at de.package.name.ui.startup.StartupActivity.onCreate(StartupActivity.java:26)
at android.app.Activity.performCreate(Activity.java:6912)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1126)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2877)
MyApplication class:
public class MyApplication extends MultiDexApplication {
private AppComponent appComponent;
#Override
public void onCreate() {
super.onCreate();
setupAppComponent();
}
private void setupAppComponent() {
appComponent = DaggerAppComponent.builder()
.appModule(new AppModule(this))
.userApiModule(new UserApiModule())
.build();
appComponent.inject(this);
}
public static MyApplication get(Context context) {
return (MyApplication) context.getApplicationContext();
}
}
Relevant part of the BaseActivity class:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyApplication.get(this).getAppComponent().inject(this);
}
And finally, the StartupActivity Part:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setupComponent(MyApplication.get(this).getAppComponent());
setContentView(R.layout.activity_startup);
startupPresenter.bindView(this);
}
public void setupComponent(AppComponent appComponent) {
startupComponent = DaggerStartupComponent.builder()
.appComponent(appComponent)
.startupModule(new StartupModule())
.build();
startupComponent.inject(this);
}
I already updated Dagger to the most recent version (2.11 for now). But I don't have any ideas about this issue. Also, I can't reproduce it on my Samsung S8 7.0 device.
So if you have any ideas, please let me know!
Cheers
edit:
If anyone runs into this problem. Take a look here: RuntimeException with Dagger 2 on Android 7.0 and Samsung devices
This might be your solution.

This has nothing to do with Dagger. The problem is here:
return (MyApplication) context.getApplicationContext();
The Context returned by getApplicationContext() is not guaranteed to be your Application instance. The only situation I've encountered where it wasn't was in an emulator, but it's always possible.
I prefer this approach:
private static MyApplication gInstance;
#Override
public void onCreate() {
gInstance = this;
}
public static MyApplication instance() {
return gInstance;
}
This is safe because the Application instance is created and its onCreate is called before any other Android component is created.

Related

Android: Gradle build task adds a final keyword to a method

I'm experiencing something strange, that never happened to me before.
This is simple class that extends an Application class:
public class MyApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
}
}
It is a library module, than I try to extend it from an application module:
public class MyApp extends MyApplication {
#Override
public void onCreate() {
super.onCreate();
Log.i("MyApp", "Application created");
}
}
Now when compiling with minifyEnabled=true I'm getting strange result:
In library module class get changed to (note the final keyword):
public class MyApplication extends Application {
MyApplication() {
}
public final void onCreate() {
super.onCreate();
}
}
And this causes compilation error, since MyApp cannot override a final method.
Did someone faced this issue?
Thanks in advance.
Happens only with minify enabled. Class MyApplication is added to proguard-rules as exception.
Happens only on Gradle > 7.1.3. Reverting to 7.1.3 fixes the cause.

Dagger2 vs Application class in android

By this simple example
public class MyApp extends Application {
private static MyApp app;
private ImageDownloaderComponent imageDownloaderComponent; // dagger2
ImageDownloader imageDownloader;
#Override
public void onCreate() {
super.onCreate();
app = this;
imageDownloaderComponent = DaggerImageDownloaderComponent.builder().imageDownloaderModule(new ImageDownloaderModule(this)).build();
imageDownloader=new ImageDownloader(this);
}
public static MyApp app(){
return app;
}
public ImageDownloaderComponent getImageDownloaderComponent(){
return this.imageDownloaderComponent;
}
}
using Dagger2
public class MainActivity extends AppCompatActivity {
#Inject ImageDownloader downloader;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyApp.app().getImageDownloaderComponent().inject(this);
ImageView imageView = findViewById(R.id.main_image);
downloader.toImageView(imageView, "https://..../fruits.png");
} }
without dagger2
public class Main2Activity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
ImageView imageView = findViewById(R.id.main_image);
MyApp.app().imageDownloader.toImageView(imageView, "https://---/fruits.png");
}
}
Both the case activity is working fine. my question why we need dagger2 even the same task performed by the application class? how the way its effective? i google it ,i got its easy for testing apart from any benefits there?? which activity is good in above examples? why?
As we know Dagger is Dependency Injection.
Brief which makes the dagger unique:
Benefits:
If we are using dagger in very small projects/tasks like you given then dagger doesn't
deserve for that.It would be more efficient it is used in intermediate,long applications. Because it help us to avoid unwanted object creation in the code.
We can use the dagger to reuse the object through object graph.
We can distribute the dependency like for
Project level
App level
Module level(Like : Home,Account in the app)
We can define custom scope ,also there are some already defined scopes like Singleton , there are good concepts in dagger like component dependency & sub component.
Can inject Class,Object,Constructor.

Android 7.0 and 7.1 getApplication() ClassCastException

In Developer Console I see a lot of crashes with stacktrace like this
java.lang.RuntimeException:
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2984)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3045)
at android.app.ActivityThread.-wrap14(ActivityThread.java:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1642)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6776)
at java.lang.reflect.Method.invoke(Native Method:0)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1520)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1410)
Caused by: java.lang.ClassCastException:
at com.myapp.ui.BaseActivity.getApp(BaseActivity.java:193)
at com.myapp.ui.BaseActivity.onCreate(BaseActivity.java:275)
at com.myapp.ui.CastActivity.onCreate(CastActivity.java:39)
at com.myapp.ui.MainActivity.onCreate(MainActivity.java:268)
at android.app.Activity.performCreate(Activity.java:6955)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1126)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2927)
getApp method of BaseActivity is
public App getApp() {
return (App) getApplication();
}
App class is
public class App extends MultiDexApplication { ...
and in manifest application tag contains reference to this class
<application
android:name="com.myapp.App"
98% of crashes is for android 7.0, rest is 7.1. No other android versions are affected.
EDIT: I use proguard so it can be somehow related but keeping class
-keep class com.myapp.** { *;}
-keep interface com.myapp.** { *;}
Note: It may not be related but in same android versions it looks like App's onCreate method is sometimes not called. I observed it because some objects which are created in onCreate were null when they were accessed from Service (started by AlarmManager) or BroadcastReceiver
Does anyone has idea what can cause it, how to fix it or work around it? Thanks
EDIT 2:
I ended up with something like this:
public App getApp() {
Application application = getApplication();
App app = null;
try {
app = (App) application;
} catch (Exception e) {
if (application != null) {
Log.e(TAG, "getApp Exception: application class: " + application.getClass().getName());
} else {
Log.e(TAG, "getApp Exception: application object is null");
}
}
return app;
}
It at least doesn't crash and I can check getApp() == null
Casting fails because getApplication() returns an Application and NOT the desired sub-class.
I've had some success where I caught the error and asked the user to reboot their device or reinstall the app.
Unfortunately, there's no real fix to this rare crash. Google won't fix the lifecycle-related issue, but said it reduced in Android 7.1+. Source: https://issuetracker.google.com/issues/37137009
I think you should cast getApplicationContext() into App instead.
While I cannot say if this solution works.
I think that static Application instance should solve the problem.
class MyApp extends Application {
private static final sInstance;
public void onCreate() {
sInstance = this;
}
public static MyApp getInstance() {
return sInstance;
}
}
Instead of calling getActivity() if you call MyApp.getInstance() you should not need to cast. So there should not be any ClassCastException
anymore.
You should override attachBaseContext in your application class like this:
#Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
Check this link for more information: https://developer.android.com/reference/android/support/multidex/MultiDexApplication.html
This might help
public class App extends MultiDexApplication {
public static App app = null;
public static App getInstance() {
return app;
}
#Override
public void onCreate() {
super.onCreate();
app = this;
}
}
you doesn't need to cast getApplication(), reason is you are already in Application class so simply just use this keyword to get application instance. Hope you find useful

Dagger 2: injecting singleton with lots of access points

I just started with DI / Dagger 2. I have a huge project. To try dagger 2, I started with injecting my singleton class 'MyPreferences'. This class handles all app actions from and to the SharedPreferences.
Auto-injecting the SharedPreferences in the MyPreferences went perfectly.
However, I am using this singleton in about 50 classes, I used to do this:
MyPreferences.getInstance(context).getXXXSetting();
I have changed this to dagger2 injection in a few classes, and it works fine, but I find myself copying these lines all the time:
#Inject
protected MyPreferences myPreferences;
protected void initInjection(Context context) {
((RootApplicationDi) context.getApplicationContext()).getComponent().injectTo(this);
}
// + call initInjection # onCreate / constructor
For such a simple call I need all these lines in about 35-40 (super) classes. Am I missing something? Is this really the way to go?
My previous answer was for Dagger 1 and thus incorrect. Here is example solution for Dagger 2:
In your application class:
private MyDagger2Component mDependencyInjector;
#Override
public void onCreate() {
super.onCreate();
mDependencyInjector = DaggerMyDagger2Component.builder()...build();
}
public MyDagger2Component getDependencyInjector() {
return mDependencyInjector;
}
In your base Activity class which your activities extend:
protected MyDaggerComponent getDependencyInjector() {
return ((MyApplication) getApplication()).getDependencyInjector();
}
And in your activity you can now have just one line for the injection:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getDependencyInjector().inject(this);
...
}

Context in Marshmallow

Question is about context, it return null on first time running after installing app. Do I need permissions? Earlier versions work. Android developer preview v3 (nexus5)
public class App extends Application {
private static App application;
#Override
public void onCreate() {
super.onCreate();
Log.e("APP::", "CREATE");
application = this;
Log.e("APPLICATION ::", "" + application);
}
public static Context getContext() {
return application != null ? application.getBaseContext() : null;
}
}
You're calling getContext() before onCreate() is called. Don't do that. If possible, don't use a static reference to App at all.

Categories

Resources