I have an activity with a viewpager of fragments filled with results of an api call:
ArtistFragment.java
public static ArtistFragment newInstance(String artistName, String imageUrl) {
ArtistFragment artistFragment = new ArtistFragment();
Bundle args = new Bundle();
args.putString(ARTIST_NAME, name);
args.putString(IMAGE_URL, imageUrl);
artistFragment.setArguments(args);
return artistFragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
BaseActivity activity = BaseActivity.get(getActivity());
AppComponent appComponent = activity.getAppComponent();
appComponent.inject(this);
imageUrl = getArguments().getString(IMAGE_URL);
title = getArguments().getString(TITLE);
}
MainActivity.java
public class MainActivity extends BaseActivity {
#Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
appComponent.inject(this);
}
#Override
public void onResume() {
super.onResume();
subscriptions.add(client.searchArtists("Impressionist")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<Artist>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
Toast.makeText(getApplicationContext(),
e.getMessage(), Toast.LENGTH_LONG)
.show();
}
#Override
public void onNext(List<Artist> artists) {
ArtistPagerAdapter pagerAdapter = new
ArtistPagerAdapter(getSupportFragmentManager(),
artists);
viewPager.setAdapter(pagerAdapter);
}
}));
}
BaseActivity.java:
public class BaseActivity extends AppCompatActivity {
#Inject CompositeSubscription subscriptions;
AppComponent appComponent;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
appComponent = MyApplication.get(this)
.getAppComponent();
appComponent.inject(this);
}
public static BaseActivity get(Context context) {
return (BaseActivity) context;
}
public AppComponent getAppComponent() {
return appComponent;
}
MyApplication.java
public class MyApplication extends Application {
private AppComponent appComponent;
public static MyApplication get(Context context) {
return (MyApplication) context.getApplicationContext();
}
#Override
public void onCreate() {
super.onCreate();
appComponent = DaggerAppComponent.builder()
.appModule(new AppModule(this))
.build();
}
public AppComponent getAppComponent() {
return appComponent;
}
}
ArtistPagerAdapter:
public class ArtistPagerAdapter extends FragmentStatePagerAdapter {
private List<Artist> results;
public ArtistPagerAdapter(FragmentManager fragmentManager,
List<Artist> results) {
super(fragmentManager);
this.results = results;
}
#Override
public int getCount() {
return results.size();
}
#Override
public Fragment getItem(int position) {
Artist artist = results.get(position);
String name = artist.getFullName();
String imageUrl = artist.getImageUrl();
return ArtistFragment.newInstance(name, imageUrl);
}
}
ApplicationComponent.java
#Component(modules = AppModule.class)
#Singleton
public interface AppComponent {
void inject(BaseActivity activity);
void inject(MainActivity activity);
void inject(ArtistFragment fragment);
}
I added log statements in the Activity and Fragment onCreate. If I rotate screen, why is the fragment's onCreate called prior to activity's onCreate?
put your code fragments in
onActivityCreated()
rather than
onCreate()
usually this happens when your memory is low and the activity is recycled.
Related
I created an abstract GlobalActivity extending AppCompatActivity and a GlobalViewModel extending ViewModel, in order to have some LiveData always ready to show Dialog messages and Toast messages, as well as displaying and hiding a ProgressBar. Problem is that the LoginActivity is not observing the LiveData object I mentioned above, so is not reacting to changes nor calls. Here is my code:
GlobalActivity:
public abstract class GlobalActivity extends AppCompatActivity {
protected GlobalViewModel mGlobalViewModel = new GlobalViewModel();
private Consumer<Throwable> errorHandler = throwable -> {
Timber.e(throwable);
DialogUtils.showOneButtonDialog(this, R.string.unexpected_error, null);
};
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
RxJavaPlugins.setErrorHandler(this.errorHandler);
setUpBasicViewModel();
mGlobalViewModel.getDialogMessage().observe(this, mssg -> DialogUtils.showOneButtonDialog(GlobalActivity.this, mssg, null));
mGlobalViewModel.getToastMessage().observe(this, mssg -> DialogUtils.showMessage(mssg));
mGlobalViewModel.getIsLoading().observe(this, bool -> setLoadingState(bool));
}
public abstract void setLoadingState(boolean bool);
public abstract void setUpBasicViewModel();
}
GlobalViewModel:
public class GlobalViewModel extends ViewModel {
protected MutableLiveData<String> dialogMessage = new MutableLiveData<>();
protected MutableLiveData<String> toastMessage = new MutableLiveData<>();
protected SingleLiveEvent<Boolean> isLoading = new SingleLiveEvent<>();
public GlobalViewModel(){}
public MutableLiveData<String> getDialogMessage() {
return dialogMessage;
}
public MutableLiveData<String> getToastMessage() {
return toastMessage;
}
public SingleLiveEvent<Boolean> getIsLoading() {
return isLoading;
}
}
LoginActivity:
public class LoginActivity extends GlobalActivity {
private LoginViewModel mLoginViewModel;
private ActivityLoginBinding mDataBinding;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mLoginViewModel = new ViewModelProvider(this, new LoginViewModelFactory()).get(LoginViewModel.class);
mDataBinding = DataBindingUtil.setContentView(this, R.layout.activity_login);
mDataBinding.setLifecycleOwner(this);
mDataBinding.setViewModel(mLoginViewModel);
}
#Override
public void setLoadingState(boolean bool) {
mDataBinding.progressBar.setVisibility(mDataBinding.progressBar.isShown() ? View.GONE : View.VISIBLE);
}
#Override
public void setUpBasicViewModel() {
mGlobalViewModel = ViewModelProviders.of(this).get(GlobalViewModel.class);
}
...
}
I am trying to inject context in my Interactor class which is giving me a null pointer exception.
I have used the MVP pattern and I am trying to get access to the context in my non-activity class.
I am not really sure if this is the best technique used.
Module:
#Module
public class ContextModule {
private final Context context;
public ContextModule(Context context) {
this.context = context;
}
#Singleton
#Provides
public Context getContext() {
return this.context;
}
}
Component:
#Singleton
#Component(modules = {ContextModule.class})
public interface AppComponent {
void inject(MainActivity mainActivity);
}
App
public class App extends Application {
private AppComponent appComponent;
#Override
public void onCreate() {
super.onCreate();
appComponent = DaggerAppComponent.builder()
.contextModule(new ContextModule(this))
.build();
}
public AppComponent getAppComponent() {
return appComponent;
}
}
MainActivity
public class MainActivity extends AppCompatActivity implements
TaskContract.IMainView {
#Inject
MainInteractor mainInteractor;
private MainPresnter mainPresnter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((App) getApplication()).getAppComponent().inject(this);
mainPresnter = new MainPresnter(this);
}
#Override
public void getRandomNumber(int rNum) {
Toast.makeText(this, "" + rNum, Toast.LENGTH_SHORT).show();
}
#Override
protected void onResume() {
super.onResume();
mainPresnter.fetchFromService();
}
}
Presenter
public class MainPresnter implements TaskContract.IMainPresenter,
TaskContract.OnTaskCompletionResult {
private TaskContract.IMainView mainView;
private MainInteractor mainInteractor;
public MainPresnter(TaskContract.IMainView mainView) {
this.mainView = mainView;
mainInteractor = new MainInteractor(this);
}
#Override
public void fetchFromService() {
mainInteractor.callService();
}
#Override
public void onSuccess(int rNum) {
mainView.getRandomNumber(rNum);
}
}
Interactor
public class MainInteractor implements TaskContract.IMainInteractor {
private static final int JOB_ID = 100 ;
private Context context;
#Inject
public MainInteractor(Context context) {
this.context = context;
}
public MainInteractor(TaskContract.OnTaskCompletionResult completionListener)
{
TaskService.setCompletionListener(completionListener);
}
#Override
public void callService() {
JobInfo jobInfo = new JobInfo.Builder(JOB_ID,
new ComponentName(context, TaskService.class))
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.setPeriodic(10000)
.build();
JobScheduler jobScheduler = (JobScheduler)
context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
jobScheduler.schedule(jobInfo);
}
}
Gradle
implementation 'com.google.dagger:dagger-android:2.11'
annotationProcessor 'com.google.dagger:dagger-compiler:2.11'
You don't inject the Interactor within your Presenter - therefore it won't have a context.
You could probably restructure your Presenter to require the Interactor as a dependency - this would also mean you'd need to restructure how the completion listener is set.
I can make DI from some class to use in application such as Retrofit, Picasso.
they can work fine when i use them on Activity but when i try to use some DI on other class i get NULL, for exmple this code work fine
public class ActivityRegister extends BaseActivities {
#Inject
GithubService githubService;
#Inject
JobManager jobManager;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
...
repositoryCall = githubService.getAllRepositories();
...
}
private void getRepositories() {
repositoryCall.enqueue(new Callback<List<GithubRepo>>() {
#Override
public void onResponse(Call<List<GithubRepo>> call, Response<List<GithubRepo>> response) {
List<GithubRepo> repoList = new ArrayList<>();
repoList.addAll(response.body());
Log.e("JOB ", "OK");
}
#Override
public void onFailure(Call<List<GithubRepo>> call, Throwable t) {
Log.e("JOB ", "NO!!");
}
});
}
for GithubService i get created instance successfull, not i'm trying to use that into GetLatestRepositories, but i get NULL, how can i define correctly that to injecting into class?
public class GetLatestRepositories extends Job {
#Inject
GithubService githubService;
private Call<List<GithubRepo>> repositoryCall;
public GetLatestRepositories() {
super(new Params(Priority.MID).requireNetwork().persist());
repositoryCall = githubService.getAllRepositories();
}
#Override
public void onAdded() {
}
#Override
public void onRun() throws Throwable {
repositoryCall.enqueue(new Callback<List<GithubRepo>>() {
#Override
public void onResponse(Call<List<GithubRepo>> call, Response<List<GithubRepo>> response) {
List<GithubRepo> repoList = new ArrayList<>();
repoList.addAll(response.body());
Log.e("JOB ", "OK");
}
#Override
public void onFailure(Call<List<GithubRepo>> call, Throwable t) {
Log.e("JOB ", "NO!!");
}
});
}
#Override
protected void onCancel(int cancelReason, #Nullable Throwable throwable) {
}
#Override
protected RetryConstraint shouldReRunOnThrowable(#NonNull Throwable throwable, int runCount, int maxRunCount) {
return null;
}
}
ActivityRegisterComponent component class:
#ActivityRegisterScope
#Component(modules = ActivityRegisterModule.class, dependencies = GithubApplicationComponent.class)
public interface ActivityRegisterComponent {
void injectActivityRegister(ActivityRegister homeActivity);
}
GithubApplicationComponent:
#GithubApplicationScope
#Component(modules = {GithubServiceModule.class, PicassoModule.class, JobManagerModule.class, ActivityModule.class})
public interface GithubApplicationComponent {
Picasso getPicasso();
GithubService getGithubService();
JobManager getJobManager();
}
Application class:
public class Alachiq extends Application {
...
public static Alachiq alachiq;
public static String packageName;
public static Resources resources;
private static Context context;
private GithubService githubService;
private Picasso picasso;
private GithubApplicationComponent component;
private JobManager jobManager;
private static Alachiq instance;
#Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
}
#Override
public void onCreate() {
super.onCreate();
//#formatter:off
resources = this.getResources();
context = getApplicationContext();
packageName = getPackageName();
//#formatter:on
Timber.plant(new Timber.DebugTree());
component = DaggerGithubApplicationComponent.builder()
.contextModule(new ContextModule(this))
.build();
githubService = component.getGithubService();
picasso = component.getPicasso();
jobManager = component.getJobManager();
}
public GithubApplicationComponent component() {
return component;
}
public static Alachiq get(Activity activity) {
return (Alachiq) activity.getApplication();
}
public static Alachiq getInstance() {
return instance;
}
public static Context getContext() {
return context;
}
}
and ActivityRegister onCreate:
ApplicationComponent component = DaggerApplicationComponent.builder()
.githubApplicationComponent(Alachiq.get(this).component())
.build();
GithubService class:
public interface GithubService {
#GET("users/{username}/repos")
Call<List<GithubRepo>> getReposForUser(#Path("username") String username);
#GET("repositories")
Call<List<GithubRepo>> getAllRepositories();
#GET("users/{username}")
Call<GithubUser> getUser(#Path("username") String username);
}
Before you can even use the object from GithubService class you need to do the following:
myComponent= DaggerMyComponent.builder().computerModule(new ComputerModule()).build();//replace accordingly.
In your case you will have to do something like:
githubService = DaggerGithubApplicationComponent.builder().nameofYourModule(new NameOfTheClass()).build);
Now you can use the githubService object:
repositoryCall = githubService.getAllRepositories();
This is a type of design pattern called creational.
I'm learning to use otto as an event system. I can register my activity and receive the message from bus.post(). However, if I register my Application class, the subscribed method doesn't get called. What I'm I doing wrong?
This is my activity:
public class MainActivity extends AppCompatActivity {
#Inject
BusWorker busWorker;
#Inject
LogWorker logWorker;
ActivityMainBinding binding;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
inject();
ButterKnife.bind(this);
busWorker.register(this);
binding.setHandlers(this);
}
#Subscribe
public void recievedMessage(Message message) {
logWorker.log("recievedMessage: " + message.getMessage());
}
public void onClickButton(View view) {
switch (view.getId()) {
case R.id.button:
busWorker.post("Test message");
break;
default:
break;
}
}
#Override
protected void onStop() {
super.onStop();
busWorker.unRegister(this);
}
void inject() {
((App) getApplication()).getGeneralComponent().inject(this);
((App) getApplication()).getSchoolComponent().inject(this);
}
}
App class
public class App extends Application {
private NetComponent netComponent;
private GeneralComponent generalComponent;
#Inject
public App() {}
#Override
public void onCreate() {
super.onCreate();
netComponent = DaggerNetComponent.builder()
.appModule(new AppModule(this))
.netModule(new NetModule())
.build();
generalComponent = DaggerGeneralComponent.builder()
.netComponent(netComponent)
.generalModule(new GeneralModule())
.build();
netComponent.BusWorker().register(this);
}
#Subscribe
public void recievedMessage(Message message) {
netComponent.logWorker().log("recievedMessage: " + message.getMessage());
}
public GeneralComponent getGeneralComponent() {
return generalComponent;
}
public NetComponent getNetComponent() {
return netComponent;
}
}
My NetModule (partially)
#Module
public class NetModule {
public NetModule() {
}
#Provides
#NetScope
BusWorker provideBusWorker(){
return new BusWorker();
}
}
NetScope
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface NetScope {
}
My NetComponent
#NetScope
#Component(modules={AppModule.class, NetModule.class})
public interface NetComponent {
NetWorker netWorker();
DbWorker dbWorker();
LogWorker logWorker();
SharedPreferencesWorker sharedPreferences();
BusWorker BusWorker();
}
I modified the components, modules and the way they're being initialized in the Application class. This is the working code where the right message is received in the Application, Activity and fragment:
SchoolComponent
#SchoolScope
#Component(modules={SchoolModule.class})
public interface SchoolComponent {
void inject(MainActivity activity);
void inject(FragmentTest fragment);
void inject(App app);
School provideSchool();
}
NetComponent
#NetScope
#Component(modules={NetModule.class})
public interface NetComponent {
void inject(MainActivity activity);
void inject(FragmentTest fragment);
void inject(App app);
NetWorker provideNetWorker();
DbWorker provideDbWorker();
LogWorker provideLogWorker();
SharedPreferencesWorker provideSharedPreferences();
}
NetModule
#Module
public class NetModule {
#Provides
#NetScope
SharedPreferencesWorker provideSharedPreferences(){
return new SharedPreferencesWorker();
}
#Provides
#NetScope
NetWorker provideNetWorker(){
return new NetWorker();
}
#Provides
#NetScope
DbWorker provideDbWorker(){
return new DbWorker();
}
#Provides
#NetScope
LogWorker provideLogWorker(){
return new LogWorker();
}
#Provides
#NetScope
BusWorker provideBusWorker(){
return new BusWorker();
}
}
Application class
public class App extends Application {
private NetComponent netComponent;
private SchoolComponent schoolComponent;
#Inject
BusWorker busWorker;
#Inject
public App() {
}
#Override
public void onCreate() {
super.onCreate();
schoolComponent = DaggerSchoolComponent.create();
schoolComponent.inject(this);
netComponent = DaggerNetComponent.create();
netComponent.inject(this);
busWorker.register(this);
}
#Subscribe
public void recievedMessage(Message message) {
Log.d("Dagger", "recievedMessage App: " + message.getMessage());
}
public SchoolComponent getSchoolComponent() {
return schoolComponent;
}
public NetComponent getNetComponent() {
return netComponent;
}
}
MainActivity
public class MainActivity extends AppCompatActivity {
#Inject
BusWorker busWorker;
#Inject
LogWorker logWorker;
#Inject
School school;
#Bind(R.id.name)
TextView name;
ActivityMainBinding binding;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
ButterKnife.bind(this);
inject();
}
#Override
protected void onResume() {
super.onResume();
busWorker.register(this);
}
#Override
protected void onPause() {
super.onPause();
busWorker.unRegister(this);
}
#Subscribe
public void recievedMessage(Message message) {
logWorker.log("recievedMessage Activity: " + message.getMessage());
}
public void onClickButton(View view) {
switch (view.getId()) {
case R.id.button:
busWorker.post(new Message("blablabla"));
break;
default:
break;
}
}
void inject() {
((App) getApplication()).getNetComponent().inject(this);
((App) getApplication()).getSchoolComponent().inject(this);
}
}
I have a custom class Fragment AFragment that has an injected attribute : AController controller.
The problem is that when I call this : controller.onStart() --> controller is null.
The code :
Class AFragment :
public class AFragment extends Fragment {
#Inject
AController controller;
#Override
public void onStart() {
super.onStart();
controller.onStart();
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
controller.onCreate();
}
}
Class AController :
public class AController {
private final DataInteractor dataInteractor;
#Inject
public AController(DataInteractor dataInteractor){
this.dataInteractor = dataInteractor;
}
public void onCreate(){
}
public void onStart(){
}
}
The only thing you need is create a component and inject AFragment into it.
#Singleton
#Component
public interface ApplicationComponent {
void inject(AFragment fragment);
}
Because in AController class you make a Constructor inject so you don't need to make a module for your component.
And also you need to create the component when application start. So just init it in your Application extended class.
public class DemoApplication extends Application {
private ApplicationComponent mComponent;
#Override
public void onCreate() {
super.onCreate();
mComponent = DaggerApplicationComponent.builder()
.build();
}
public ApplicationComponent getComponent() {
return mComponent;
}
}
The last step is what i say before, inject AFragment into the component.
public class AFragment extends Fragment {
#Inject
AController controller;
#Override
public void onStart() {
super.onStart();
controller.onStart();
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((DemoApplication) getApplication()).getComponent().inject(this);
controller.onCreate();
}
}