I'm using Roboguice for DI in my project. As per android documentation only one instance of application can exists. But instances which crated by OS and by Roboguice is different.
How to force Roboguice inject application created by OS and disable creation of new instance?
Some code which illustrates situation below
public class MyApplication extends Application {
public static MyApplication getInstance() {
if (instance == null) {
throw new IllegalStateException("Application isn't initialized yet!");
}
return instance;
}
#Override
public void onCreate() {
super.onCreate();
instance = this;
}
}
public class MyActivity extends RoboActivity {
// roboApp and osApp two different objects but expected that roboApp the same as osApp
#Inject
private MyApplication roboApp;
private MyApplication osApp = MyApplication.getInstance();
}
RoboGuice doesn't call MyApplication.getInstance() but will instead call new MyApplication()
You can write a Provider that calls MyApplication.getInstance() instead. This would look like:
public MyAppProvider implements Provider<MyApplication> {
#Override
public MyApplication get() {
return MyApplication.getInstance();
}
}
You can then bind this in your module like: bind(MyApplication.class).toProvider(MyAppProvider.class);
Related
I'm learning how to use Dagger2 and MVP, so i created project, contains one main activity with viewPager and two fragments.
Code is working, but i'm think i do something wrong when i create my Dagger classes. I think if i contunue, a create a monster =)
I'm asking you help to comment my code architecture, associated with Dagger2. Maybe this question should be on CodeReview: if so, i del and move it.
Ok, i show it step by step. Main suspicions of bad code on step 3.
At the first, main activity called HomeActivity, and fragment(i show one) called HomePacksFragment. App about animals, and fragment show packs of them, it's why it called so.
1) I have two modules: PresentersModule, provide presenters for HomeActivity and HomePacksFragment:
#Module
public class PresentersModule {
#Provides
HomePresenter provideHomePresenter(AnimalDatabase animalDatabase) {
return new HomePresenter(animalDatabase);
}
#Provides
HomePacksFragmentPresenter provideHomePacksFragmentPresenter(AnimalDatabase animalDatabase) {
return new HomePacksFragmentPresenter(animalDatabase);
}
}
and RoomModule prodive access to database and DAO:
#Module
public class RoomModule {
private AnimalDatabase db;
public RoomModule(Application mApplication) {
db = Room.databaseBuilder(mApplication,
AnimalDatabase.class, AnimalDatabase.DATABASE_NAME)
.allowMainThreadQueries()
.fallbackToDestructiveMigration()
.build();
}
#Singleton
#Provides
AnimalDatabase providesRoomDatabase() {
return db;
}
#Singleton
#Provides
DAO providesProductDao(AnimalDatabase db) {
return db.daoAccess();
}
}
2) Then i have component AppComponent who knows all my modules and can inject dependences in concrete view:
#Singleton
#Component(modules = {PresentersModule.class, RoomModule.class})
public interface AppComponent {
void injectsHomeActivity(HomeActivity homeActivity);
void injectsHomePacksFragment(HomePacksFragment homePacksFragment);
DAO animalDao();
AnimalDatabase animalDatabase();
}
3) Then i have class AnimalsLibraryApp extended Application, and this is the place i make wrong in my opinion.
public class AnimalsLibraryApp extends Application {
private static AnimalsLibraryApp instance;
private static AppComponent homeActivityComponent;
private static AppComponent homeFragmentPacksComponent;
#Override
public void onCreate() {
super.onCreate();
instance = this;
if (BuildConfig.DEBUG) {
Timber.plant(new Timber.DebugTree());
}
createHomeActivityComponent();
createHomePacksComponent();
}
public static AnimalsLibraryApp getInstance() {
return instance;
}
public static AppComponent getHomeActivityComponent() {
return homeActivityComponent;
}
public static AppComponent getHomeFragmentPacksComponent() {
return homeFragmentPacksComponent;
}
private void createHomeActivityComponent() {
homeActivityComponent = DaggerAppComponent.builder()
.presentersModule(new PresentersModule())
.roomModule(new RoomModule(instance))
.build();
}
private void createHomePacksComponent() {
homeFragmentPacksComponent = DaggerAppComponent.builder()
.presentersModule(new PresentersModule())
.roomModule(new RoomModule(instance))
.build();
}
}
Idea of this class was provide singletons of AppComponent. Technicaly it works, but what if i have 10 or 20 views? Class, extended Application is basic class, it will not contain only AppComponent create logic, so much more will be here. Do i write it correct?
4) It's end. I create my HomeActivity and inject dependences with simple(is't it?) line.
public class HomeActivity extends AppCompatActivity implements HomeContract.View {
#Inject
HomePresenter presenter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
AnimalsLibraryApp.getHomeActivityComponent().injectsHomeActivity(this);
presenter.attachView(this);
presenter.viewIsReady();
DatabaseUtils.copyDatabase(this, AnimalDatabase.DATABASE_NAME);
ViewPager viewPager = findViewById(R.id.pager);
HomeMenuAdapter myPagerAdapter = new HomeMenuAdapter(getSupportFragmentManager());
viewPager.setAdapter(myPagerAdapter);
TabLayout tabLayout = findViewById(R.id.tablayout);
tabLayout.setupWithViewPager(viewPager);
tabLayout.getTabAt(0).setIcon(R.drawable.help);
tabLayout.getTabAt(1).setIcon(R.drawable.help);
}
}
I do right things or i must to change something until it's not too late?
UPD:
Ok, i google to simple Dagger 2 examples and found this App class version:
public class MyApp extends Application {
private static MyApp app;
private AppModule appModule;
private BasicComponent basicComponent;
#Override
public void onCreate() {
super.onCreate();
app = this;
appModule = new AppModule(this);
basicComponent = DaggerBasicComponent.builder()
.appModule(appModule)
.build();
}
public static MyApp app() {
return app;
}
public AppModule appModule() {
return appModule;
}
public BasicComponent basicComponent() {
return basicComponent;
}
It's look like better and cleaner that my, but i have a question. In this example we always retutn basicComponent, so it means that it must included all modules? It's normal practice to create "god" component? =)
If i have 3 modules, i need to write(for my example):
appComponent = DaggerAppComponent
.builder()
.appModule(appModule)
.presentersModule(new PresentersModule())
.roomModule(new RoomModule(this))
.build();
According to my idea and the reference as you said you are using AnimalsLibraryApp in a bad way.
In the application class, you just need to declare one dagger component then in every view, you will inject the module with that component in the view.
If you google a dagger2 sample and check the class that extended the application you will understand.
I am beggining with Dagger, I am using 1.2 version of it, and I have the following scenario:
Module:
#Module(injects = {
AuthenticationService.class
})
public class ServiceModule {
#Provides
AuthenticationService provideAuthenticationService() {
return ServiceFactory.buildService(AuthenticationService.class);
}
}
On my Application class I create the ObjectGraph:
public class FoxyRastreabilidadeApplication extends Application {
private static FoxyRastreabilidadeApplication singleton;
#Override
public void onCreate() {
super.onCreate();
createObjectGraph();
singleton = this;
}
private void createObjectGraph() {
ObjectGraph.create(ServiceModule.class);
}
}
and finally, at my LoginActivity, I try to inject my AuthenticationService:
public class LoginActivity extends Activity implements LoaderCallbacks<Cursor> {
private UserLoginTask mAuthTask = null;
#Inject
AuthenticationService authenticationService;
}
At this point, when I try to access my AuthenticationService instance it is always null, meaning it wasnt injected at all, I debugged my provider method to be sure of it, so, the question is, am I missing something? If so, what is it?
You need to hold on to the ObjectGraph and ask it to inject your objects directly.
Add an instance variable to your custom Application subclass to hold on to the created ObjectGraph:
this.objectGraph = ObjectGraph.create(ServiceModule.class);
Add a convenience method to your Application to do injection:
public void inject(Object object) {
this.objectGraph.inject(object);
}
Call that method wherever you need injection to happen, for example in your Activity:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((MyApplication)getApplication()).inject(this);
...
}
I want to replace our component registry (with dexfile class loader magic) with an dependency injection framework for Android.
The first try is dagger.
When trying I get the following error:
11-06 13:05:41.040 16269-16269/com.daggertoolkitexample E/AndroidRuntime﹕ FATAL EXCEPTION: main
java.lang.RuntimeException: Unable to start activity ComponentInfo{daggertoolkitexample/com.dagger.MyActivity}: java.lang.IllegalArgumentException: No inject registered for members/com.dagger.MyActivity. You must explicitly add it to the 'injects' option in one of your modules.
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2295)
...
Caused by: java.lang.IllegalArgumentException: No inject registered for members/com.dagger.MyActivity. You must explicitly add it to the 'injects' option in one of your modules.
at dagger.ObjectGraph$DaggerObjectGraph.getInjectableTypeBinding(ObjectGraph.java:302)
at dagger.ObjectGraph$DaggerObjectGraph.inject(ObjectGraph.java:279)
at com.dagger.MyApplication.inject(MyApplication.java:39)
at com.dagger.MyBaseActivity.onCreate(MyBaseActivity.java:18)
at com.dagger.MyActivity.onCreate(MyActivity.java:22)
at android.app.Activity.performCreate(Activity.java:5372)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1104)
...
I can fix it if i inject my activity in the #Module. Its work without exception.
#Module(
library = true,
injects = MyActivity.class)
public class AuthManagementModul {...}`
But this is not that i want.
I don´t can and want to know all users of my component.
Has everyone an idea what's wrong?
Here is my example code:
public class MyBaseActivity extends ActionBarActivity {
#Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((MyApplication) getApplication()).inject(this);
}
}
...
public class MyActivity extends MyBaseActivity {
#Inject AuthManagement authManagement;
...
}
...
public class MyApplication extends Application{
private ObjectGraph graph;
#Override
public void onCreate() {
super.onCreate();
graph = ObjectGraph.create(new AuthManagementModul(this));
}
public void inject(Object object) {
graph.inject(object);
}
}
...
#Module(
library = true
)
public class AuthManagementModul {
private final Application application;
public AuthManagementModul(Application application) {
this.application = application;
}
#Provides
#Singleton
AuthManagement provideAuthManagement() {
return new AuthManagementImpl(application);
}
}
In this case, you don't want to add injects= to your AuthManagementModul, but rather to an activity-specific module which includes it.
Dagger 1.x uses injects= as a signal for what graph-roots to analyze, so they must be present -but they need not be present on leaf-node library modules - just on a module the activity uses. Consider breaking up your modules on more partitioned lines like so:
#Module(
injects = {
... all your activities
},
includes = {
AuthManagementModul.class,
ApplicationModule.class
}
)
class EntryPointsModule {}
#Module(library = true, complete = false)
class AuthManagementModul {
#Provides
#Singleton
AuthManagement provideAuthManagement(Application application) {
return new AuthManagementImpl(application);
}
}
#Module(library = true)
class ApplicationModule {
private final Application application;
public ApplicationModule(Application application) {
this.application = application;
}
#Provides
#Singleton
Application application() {
return application;
}
}
Then create your graph like so:
public class MyApplication extends Application{
private ObjectGraph graph;
#Override
public void onCreate() {
super.onCreate();
// AuthManagementModul is automatically included because it has a default
// constructor and is included by EntryPointsModule
graph = ObjectGraph.create(new EntryPointsModule(), new ApplicationModule(this));
}
public void inject(Object object) {
graph.inject(object);
}
}
There are other ways to structure this - you could just have ApplicationModule include the AuthModule and declare injects, so you only have two modules, etc. I suggested this way because ApplicationModule is then a separate concern whose only role is to hoist the Application instance into the graph, AuthManagementModul is exclusively there to support the auth function, and EntryPointsModule is there to be the front of the whole graph.
If you migrate to Dagger2, this structure is also convenient in that EntryPointsModule naturally converts to a #Component.
I am trying to write a JUnit test for an Android Service using RoboGuice 2.0. I have a test module that binds injected dependencies to Mockito mock objects. However, when I run the test, the real implementations from my app module get injected instead. Here is some of the relevant code:
MainApplication.java:
public class MainApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
RoboGuice.setBaseApplicationInjector(this, RoboGuice.DEFAULT_STAGE,
RoboGuice.newDefaultRoboModule(this), new MainModule());
startService(new Intent(this, NotificationService.class));
}
}
MainModule.java:
public class MainModule extends AbstractModule {
#Override
protected void configure() {
bind(IFooManager.class).to(FooManagerImpl.class).in(Scopes.SINGLETON);
}
}
NotificationService.java:
public class NotificationService extends RoboService {
#Inject
private NotificationManager notificationManager;
#Inject
private SharedPreferences prefs;
#Inject
private IFooManager fooManager;
private IFooListener listener = new FooListener();
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
fooManager.addListener(listener);
}
#Override
public void onDestroy() {
super.onDestroy();
fooManager.removeListener(listener);
}
private class FooListener implements IFooListener {
// Do stuff that fires Notifications
}
}
NotificationServiceTest.java:
public class NotificationServiceTest extends ServiceTestCase<NotificationService> {
#Mock
private MockFooManager fooManager;
#Mock
private MockSharedPreferences prefs;
public NotificationServiceTest() {
super(NotificationService.class);
}
public void testStart() {
startService(null);
verify(fooManager).addListener(isA(IFooListener.class));
}
public void testStop() {
shutdownService();
verify(fooManager).removeListener(isA(IFooListener.class));
}
#Override
protected void setUp() throws Exception {
super.setUp();
MockitoAnnotations.initMocks(this);
Application app = new MockApplication();
setApplication(app);
RoboGuice.setBaseApplicationInjector(app, RoboGuice.DEFAULT_STAGE, new TestModule());
}
#Override
protected void tearDown() throws Exception {
super.tearDown();
RoboGuice.util.reset();
}
private class TestModule extends AbstractModule {
#Override
protected void configure() {
bind(Context.class).toInstance(getContext());
bind(IFooManager.class).toInstance(fooManager);
bind(SharedPreferences.class).toInstance(prefs);
}
}
}
MockFooManager and MockSharedPreferences are empty abstract implementations of IFooManager and SharedPreferences, needed because RoboGuice can't inject mocks of interfaces. I am using Mockito with Dexmaker to support bytecode generation for mocked classes. Also, I am not using Robolectric, I am running these tests on a device or in the emulator.
When I run this test, I get the error Wanted but not invoked: fooManager.addListener(isA(com.example.IFooListener)). After stepping through this with the debugger, I found that RoboGuice is injecting the dependencies from MainModule instead of TestModule, so the test is using FooManagerImpl instead of MockFooManager. I don't understand how RoboGuice even knows about MainModule in the test code.
Here are some other things I tried to fix this, but none had any effect:
Specify app modules in roboguice.xml instead of calling RoboGuice.setBaseApplicationInjector in MainApplication.onCreate
Use Modules.override when calling RoboGuice.setBaseApplicationInjector instead of just passing the module list directly.
How do I get RoboGuice to use TestModule and ignore MainModule in my unit test?
It seems like you are missing a call to do the injection in your NotificationServiceTest. This is done as follows:
RoboGuice.getInjector(app).injectMembers(this);
You will need to add this at some point after you set the base injector and before the tests are run.
Use
RoboGuice.overrideApplicationInjector(app,RoboGuice.newDefaultRoboModule(app), new TestModule())
instead of
RoboGuice.setBaseApplicationInjector(app, RoboGuice.DEFAULT_STAGE, new TestModule());
I'm newbe in Roboguice, please help. I have an application calss MyApplication in which in onCreate method i initialize some data. Also i have a POJO with buisiness logic which i want to use in my MainActivity (See code snippets below). I need to inject MyApplication into POJO to get access to data which i initialize in application's onCreate, but this code called before onCreate and i've got a NullPointerException.
public class MyApplication extends Application {
private Properties applicationProperties;
#Override
public void onCreate() {
super.onCreate();
applicationProperties = loadApplicationProperties(APPLICATION_PROPERTIES_ASSET);
}
#SuppressWarnings("unchecked")
public String getProperty(String key) {
return applicationProperties.getProperty(key);
}
}
#Singleton
public class POJO {
#Inject
private MyApplication application;
#Inject
public void init() {
// NPE here, because application onCreate not called at this moment
serverURL = application.getProperty(Constants.SERVER_URL);
}
}
public class MainActivity extends RoboActivity {
#Inject
private POJO myPOJO;
}
EDIT: Found a way to do this in RoboGuice 2.0 based on the answer in RoboGuice custom module application context.
Inject the application context in AbstractModule constructor, then bind it in configure() for later injection:
public final class MyModule extends AbstractModule
{
private final MyApplication context;
#Inject
public MyModule(final Context context)
{
super();
this.context = (MyApplication)context;
}
#Override
protected void configure() {
bind(MyApplication.class).toInstance(context);
}
}
If the data you need doesn't require a context, just access to XML resources or res/raw, you can inject that from anywhere.
Just use Roboguice.getInjector() to obtain a copy of the Resources object.