Subcomponent.Builder is missing setters - java

I need help with Dagger2.13 for Android.
I am following several examples on the internet but I am now facing an error that I can not solve.
Error:(23, 14) error: #Subcomponent.Builder is missing setters for
required modules or subcomponents:
[com.hugothomaz.fipe.Module.DIMarcaModulo]
I thought it best to post the problem classes in GITHub and include the repository link here.
https://github.com/hugothomaz/FIPE_Test_Dagger2.11
-FipeApplication-
public class FipeApplication extends Application implements HasActivityInjector, HasFragmentInjector{
private static final String URL_SEARCH = "http://fipeapi.appspot.com/api/1/";
#Inject
DispatchingAndroidInjector<Fragment> dispatchingAndroidInjectorFragment;
#Inject
DispatchingAndroidInjector<Activity> dispatchingAndroidInjectorActivity;
#Override
public void onCreate() {
super.onCreate();
initializeApplicationComponente();
}
#Override
public void onTerminate() {
super.onTerminate();
}
private void initializeApplicationComponente() {
Log.i("app", "FipeApplication initializeApplicationComponente");
//DaggerDIApplicationComponent.builder().(this).build();
}
#Override
public AndroidInjector<Fragment> fragmentInjector() {
return dispatchingAndroidInjectorFragment;
}
#Override
public AndroidInjector<Activity> activityInjector() {
return dispatchingAndroidInjectorActivity;
}
}
-DIApplicationModulo-
#Module(subcomponents = {DIMarcaComponent.class})
public class DIApplicationModulo {
#Provides
#Singleton
GsonConverterFactory provideGsonConverterFactory(){
GsonConverterFactory factory = GsonConverterFactory.create();
return factory;
}
#Provides
#Singleton
OkHttpClient provideOkHttpClient(){
return new OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.build();
}
#Provides
#Singleton
RxJavaCallAdapterFactory provideRxJavaCallAdapterFactory(){
return RxJavaCallAdapterFactory.create();
}
#Provides
#Singleton
Retrofit provideRetrofit(OkHttpClient client,
GsonConverterFactory converterFactory,
RxJavaCallAdapterFactory adapterFactory, String mBaseURL){
return new Retrofit.Builder()
.baseUrl(mBaseURL)
.addConverterFactory(converterFactory)
.addCallAdapterFactory(adapterFactory)
.client(client)
.build();
}
}
-DIApplicationComponent-
#Singleton
#Component(modules = {
AndroidInjectionModule.class,
DIApplicationModulo.class,
ViewBuilderModule.class
})
public interface DIApplicationComponent extends AndroidInjector<FipeApplication>{
#Component.Builder
interface Builder{
#BindsInstance
DIApplicationComponent.Builder baseURL(String mBaseURL);
DIApplicationComponent build();
}
}
-ViewBuilderModule-
#Module(subcomponents = {DIMarcaComponent.class})
public abstract class ViewBuilderModule {
#Binds
#IntoMap
#FragmentKey(MarcaFragment.class)
abstract AndroidInjector.Factory<? extends Fragment> bindMarcaFragment(DIMarcaComponent.Builder bulder);
}
-DIMarcaModulo-
#Module
public class DIMarcaModulo {
private MarcaFragment mView;
private MarcaAdapterRecyclerImpl mAdapter;
public Context mContext;
public DIMarcaModulo(MarcaFragment view, MarcaAdapterRecyclerImpl adapter){
this.mView = view;
this.mAdapter = adapter;
this.mContext = view.getActivity().getBaseContext();
Log.i("app", "DIMarcaModulo instanciado");
if(adapter==null){
Log.i("app", "DIMarcaModulo - adapter : nao instanciado");
}else{
Log.i("app", "DIMarcaModulo - adapter : instancied");
}
}
#Provides
#PerFragment
IMarcaAPI provideMarcaApi(Retrofit retrofit){
Log.i("app", "DIMarcaModulo provideMarcaApi");
return retrofit.create(IMarcaAPI.class);
}
#Provides
#PerFragment
BaseView provideMarcaFragment(){
Log.i("app", "DIMarcaModulo provideMarcaFragment");
return mView;
}
#Provides
#PerFragment
IMarcaAdapterModel provideMarcaAdapterModel(){
Log.i("app", "DIMarcaModulo provideMarcaAdapterModel");
return mAdapter;
}
#Provides
#PerFragment
IMarcaPresenter provideMarcaPresenter(){
Log.i("app", "DIMarcaModulo provideMarcaPresenter");
return new MarcaPresenterImpl();
}
#Provides
#PerFragment
ControllerServiceAPIRest provideControllerServiceAPIRest(){
Log.i("app", "DIMarcaModulo ControllerServiceAPIRest");
return new ControllerServiceAPIRest();
}
#Provides
#PerFragment
Context exposeContext() {
return mContext;
}
}
-DIMarcaComponent-
#PerFragment
#Subcomponent(modules = {DIMarcaModulo.class})
public interface DIMarcaComponent extends AndroidInjector<MarcaFragment> {
#Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<MarcaFragment>{
}
}
-MarcaFragment-
public class MarcaFragment extends BaseFragment implements IMarcaView, HasFragmentInjector{
private MarcaAdapterRecyclerImpl mMarcaAdapter;
private LinearLayoutManager llm;
private View view = null;
#Inject
DispatchingAndroidInjector<Fragment> fragmentDispatchingAndroidInjector;
#Inject
public IMarcaPresenter mMarcaPresenter;
#BindView(R.id.rv_marca)
protected RecyclerView mRecyclerMarca;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
if(view==null){
view = inflater.inflate(R.layout.fragment_marca, container, false);
}
setUnBinder(ButterKnife.bind(this, view));
return view;
}
#Override
protected void onViewReady(Bundle saveInstanceState, Intent intent) {
initializeRecyclerMarca();
super.onViewReady(saveInstanceState, intent);
if(mMarcaPresenter != null){
Log.i("app", "MarcaFragment - Presenter nao esta vazio");
}else{
Log.i("app", "MarcaFragment - Presenter esta vazio");
}
mMarcaPresenter.initSerice();
}
private void initializeRecyclerMarca() {
if(mMarcaAdapter==null){
mMarcaAdapter = new MarcaAdapterRecyclerImpl();
}
mRecyclerMarca.setHasFixedSize(true);
llm = new LinearLayoutManager(getActivity().getBaseContext());
llm.setOrientation(LinearLayoutManager.VERTICAL);
mRecyclerMarca.setLayoutManager(llm);
mRecyclerMarca.setAdapter(mMarcaAdapter);
}
#Override
public void onOpenVehicleFragmentByMarcaClicked(Marca marca) {
// recebendo o item clicado para chamar proxima tela com a Marca clicada.
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
AndroidInjection.inject(this);
}
#Override
public AndroidInjector<Fragment> fragmentInjector() {
return fragmentDispatchingAndroidInjector;
}
}
-MarcaPresenterImpl-
public class MarcaPresenterImpl extends BasePresenter<IMarcaView> implements IMarcaPresenter{
private static final String TAG_MARCA_PRESENTER = "MarcaPresenterImpl";
#Inject
public IMarcaAdapterModel mMarcaAdapterModel;
#Inject
public ControllerServiceAPIRest mControllerServiceAPIRest;
#Inject
public MarcaPresenterImpl(){
}
#Override
public void onShowMessage(String message) {
getView().onShowMessage(message);
}
#Override
public void onHideMessage() {
getView().onHideMessage();
}
#Override
public void onShowProgress(String message) {
getView().onShowProgress(message);
}
#Override
public void onHideProgress() {
getView().onHideProgress();
}
#Override
public void onShowToast(String message) {
getView().onShowToast(message);
}
public void refresh() {
mMarcaAdapterModel.refresh();
}
#Override
public void refreshItem(int id) {
}
#Override
public void listMarcaByServiceForView(List<Marca> listMarca) {
mMarcaAdapterModel.setListMarca(listMarca);
}
#Override
public void initSerice() {
mControllerServiceAPIRest.getMarca();
}
#Override
public void getMarcaClicked(#NonNull Marca marca) {
getView().onOpenVehicleFragmentByMarcaClicked(marca);
}
}
-ControllerServiceAPIRest-
public class ControllerServiceAPIRest implements Observer<List<Marca>> {
#Inject
public IMarcaPresenter mPresenter;
#Inject
public IMarcaAPI mMarcaAPI;
private List<Marca> listMarca;
#Inject
public ControllerServiceAPIRest() {
}
protected void getMarca(){
if(listMarca == null){
listMarca = new ArrayList<>();
}
mPresenter.onShowProgress("Carregando Marca de Veículos...");
Observable<List<Marca>> cakesResponseObservable = mMarcaAPI.getAllMarca();
subscribe(cakesResponseObservable, this);
}
private void subscribe(Observable<List<Marca>> observable, Observer<List<Marca>> observer){
observable.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
}
#Override
public void onError(Throwable e) {
mPresenter.onHideMessage();
mPresenter.onHideProgress();
mPresenter.onShowToast("Erro ao carregar Marcas!");
Log.e("app", "Falha no carregamento de Marcas: " + e.getMessage());
new Exception(e.getMessage());
}
#Override
public void onComplete() {
mPresenter.onHideMessage();
mPresenter.onHideProgress();
Log.i("app", "ControllerServiceAPIRest - listMarca Position 0: " + listMarca.get(0));
if (listMarca==null && !listMarca.isEmpty() && listMarca.get(0)!=null){
mPresenter.listMarcaByServiceForView(listMarca);
mPresenter.onShowToast("Marcas carregadas!");
}else{
mPresenter.onShowToast("Lista não foi carregada!");
}
}
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(List<Marca> list) {
listMarca = list;
}
}

You require your Module:
#PerFragment
#Subcomponent(modules = {DIMarcaModulo.class})
public interface DIMarcaComponent extends AndroidInjector<MarcaFragment> {
And Dagger can't create it, because it doesn't have a public parameterless constructor:
#Module
public class DIMarcaModulo {
// ...
public DIMarcaModulo(MarcaFragment view, MarcaAdapterRecyclerImpl adapter){
But you bind in your Builder directly:
#Module(subcomponents = {DIMarcaComponent.class})
public abstract class ViewBuilderModule {
#Binds
#IntoMap
#FragmentKey(MarcaFragment.class)
abstract AndroidInjector.Factory<? extends Fragment>
bindMarcaFragment(DIMarcaComponent.Builder bulder);
}
So when dagger.android tries to create your object:
// AndroidInjector.Builder
abstract class Builder<T> implements AndroidInjector.Factory<T> {
#Override
public final AndroidInjector<T> create(T instance) {
seedInstance(instance);
return build();
}
It notices that you haven't provided a DIMarcaModulo instance and fails. You'll need to follow the advice in the SO question Dagger 2.10 Android subcomponents and builders, which means either giving DIMarcaModulo a public parameterless constructor that can inject MarcaFragment, or by overriding DIMarcaComponent.Builder#seedInstance:
#Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<MarcaFragment> {
// This method tells Dagger you need to supply your own DIMarcoModulo.
public abstract void diMarcoModulo(DIMarcaModulo modulo);
// dagger.android calls this method automatically, but only this method, so
// you'll need to call diMarcoModulo from it.
#Override public void seedInstance(MarcaFragment fragment) {
diMarcoModulo(fragment, fragment.getMMarcaAdapter());
bindMarcaFragment(fragment); // OPTIONAL: See below
}
// If you want MarcaFragment to remain injectable, you might need to call into
// a different #BindsInstance method you define, because you've prevented
// seedInstance from doing that for you.
#BindsInstance public abstract void bindMarcaFragment(MarcaFragment fragment);
}
As you can see, MarcaFragment will automatically be available in the graph that DIMarcoModulo installs, so if you can avoid the constructor parameters and instead receive the fragment as a parameter to #Provides methods, your code might be easier to read. You also will have trouble with the method I called fragment.getMMarcaAdapter(), because you inject onAttach and you get access to the RecyclerView onCreateView. However, this shouldn't be a big problem if you remove the constructor parameters and if you can make sure you don't try to access the RecyclerView until after Android has a chance to create it.

Thanks Jeff Bowman, it was my dependencies that were wrong.
Now it works like this:
annotationProcessor "com.google.dagger:dagger-compiler:2.13"
annotationProcessor "com.google.dagger:dagger-android-processor:2.13"
compile "com.google.dagger:dagger:2.13"
compile "com.google.dagger:dagger-android:2.13"
compile 'com.google.dagger:dagger-android-support:2.13'

Related

Waiting for two onChange to trigger with MediatorLiveData

I'm currently trying to learn how to use MediatorLiveData as described here:
https://developer.android.com/reference/androidx/lifecycle/MediatorLiveData
What I want to do is wait for two livedata object to get an update then do some logic on both.
So in my activity i currently got this. While this works I'm currently only getting Orders while i would like to wait for Orders AND Orderrows to finish and then make some change.
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button myButt;
private MainViewModel mvw;
private TextView myView;
private MediatorLiveData data;
#Override
protected void onCreate(Bundle savedInstanceState) {
mvw = new ViewModelProvider(this).get(MainViewModel.class);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myButt = findViewById(R.id.button);
myView = findViewById(R.id.textview);
myButt.setOnClickListener(this);
data = new MediatorLiveData<>();
data.addSource(mvw.getAllOrders(), new Observer<Orders>() {
#Override
public void onChanged(Orders orders) {
data.setValue(orders);
}
});
data.addSource(mvw.getAllOrderRows(), new Observer<OrderRows>() {
#Override
public void onChanged(OrderRows orderRows) {
data.setValue(orderRows);
}
});
data.observe(this, new Observer<Orders>() { //this observers Orders but how do i get orders AND orderrows?
#Override
public void onChanged(Orders order) {
myView.setText(mvw.extractDate(order));
//Here i want to manipulate order and orderrows
Log.i("livedata" , order.getOrders().toString());
}
});
}
#Override
public void onClick(View view) {
switch(view.getId()){
case R.id.button:
Log.i("button","clicked");
mvw.updateOrderData(); // calls for new values to be fetched
mvw.updateOrderRowData();
}
}
}
in my viewmodel:
public class MainViewModel extends ViewModel {
private GetOrder getOrderRepo;
private MutableLiveData<Orders> allOrders;
private MutableLiveData<OrderRows> allOrderRows;
public MainViewModel(){
getOrderRepo = new GetOrder();
}
public MutableLiveData<Orders> getAllOrders() {
if(allOrders == null){
allOrders = new MutableLiveData<>();
allOrders = getOrderRepo.getAllOrders();
}
return allOrders;
}
public MutableLiveData<OrderRows> getAllOrderRows() {
if(allOrderRows == null){
allOrderRows = new MutableLiveData<>();
allOrderRows = getOrderRepo.getAllOrderRows();
}
return allOrderRows;
}
public void updateOrderData(){
Log.i("updating","updating data");
Orders orders = getOrderRepo.getAllOrders().getValue();
allOrders.setValue(orders);
}
public void updateOrderRowData(){
Log.i("updating","updating data");
OrderRows orderRows = getOrderRepo.getAllOrderRows().getValue();
allOrderRows.setValue(orderRows);
}
public String extractDate(Orders orders){
ArrayList<Order> listOfOrders = orders.getOrders();
Log.i("extractDate", ""+(listOfOrders.size()-1));
String date = listOfOrders.get(listOfOrders.size()-1).getOrderTime();
return date;
}
}
In the repostiory.
public class GetOrder {
private ApiService mAPIService;
MutableLiveData<Orders> allOrders;
MutableLiveData<OrderRows> allOrderRows;
public GetOrder(){
mAPIService = ApiUtils.getAPIService();
allOrders = new MutableLiveData<Orders>();
allOrderRows = new MutableLiveData<OrderRows>();
}
public MutableLiveData<Orders> getAllOrders(){
Log.i("func","starting func");
mAPIService.getOrders().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Orders>() {
#Override
public void onCompleted() {
Log.i("func","onComplete");
}
#Override
public void onError(Throwable e) {
Log.i("onError",e.toString());
}
#Override
public void onNext(Orders orders) {
Log.i("Repo",orders.toString());
allOrders.setValue(orders);
}
});
return allOrders;
}
public MutableLiveData<OrderRows> getAllOrderRows(){
Log.i("func","starting func");
mAPIService.getOrderRows().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<OrderRows>() {
#Override
public void onCompleted() {
Log.i("func","onComplete");
}
#Override
public void onError(Throwable e) {
Log.i("onError",e.toString());
}
#Override
public void onNext(OrderRows orderRows) {
Log.i("Repo",orderRows.toString());
allOrderRows.setValue(orderRows);
}
});
return allOrderRows;
}
}

Best way to use Filter or Search in Paging Library, Android

I searched but didn't find any good solution for my situation which is:
I need to filter existing List<Article> which is generated thru the paging lib, but can't figure it out how to retrieve actual articleList from api request in FeedDataSource class at FeedActivity. I need to filter actual list before adapter.submitList(pagedList);
FeedDataSurce class
public class FeedDataSource extends PageKeyedDataSource<Long, Article>{
private static final String QUERY = "movies";
private static final String API_KEY = "079dac74a5f94ebdb990ecf61c8854b7";
private static final String TAG = FeedDataSource.class.getSimpleName();
private RestApi restApi;
private MutableLiveData networkState;
public FeedDataSource() {
restApi = RestApiFactory.feedRequest();
networkState = new MutableLiveData();
}
public MutableLiveData getNetworkState() {
return networkState;
}
#Override
public void loadInitial(#NonNull LoadInitialParams<Long> params,
#NonNull LoadInitialCallback<Long, Article> callback) {
networkState.postValue(NetworkState.LOADING);
restApi.fetchFeed(QUERY, API_KEY, 1, params.requestedLoadSize)
.enqueue(new Callback<FeedResponse>() {
#Override
public void onResponse(Call<FeedResponse> call, Response<FeedResponse> response) {
if(response.isSuccessful()) {
callback.onResult(response.body().getArticles(), null, 2l);
networkState.postValue(NetworkState.LOADED);
} else {
networkState.postValue(new NetworkState(NetworkState.Status.FAILED, response.message()));
}
}
#Override
public void onFailure(Call<FeedResponse> call, Throwable t) {
String errorMessage = t == null ? "unknown error" : t.getMessage();
networkState.postValue(new NetworkState(NetworkState.Status.FAILED, errorMessage));
}
});
}
#Override
public void loadBefore(#NonNull LoadParams<Long> params,
#NonNull LoadCallback<Long, Article> callback) {
}
#Override
public void loadAfter(#NonNull LoadParams<Long> params, #NonNull LoadCallback<Long, Article> callback) {
Log.d(TAG, "Loading Rang " + params.key + " Count " + params.requestedLoadSize);
networkState.postValue(NetworkState.LOADING);
restApi.fetchFeed(QUERY, API_KEY, params.key, params.requestedLoadSize).enqueue(new Callback<FeedResponse>() {
#Override
public void onResponse(Call<FeedResponse> call, Response<FeedResponse> response) {
if(response.isSuccessful()) {
callback.onResult(response.body().getArticles(), params.key + 1);
networkState.postValue(NetworkState.LOADED);
} else
networkState.postValue(new NetworkState(NetworkState.Status.FAILED, response.message()));
}
#Override
public void onFailure(Call<FeedResponse> call, Throwable t) {
String errorMessage = t == null ? "unknown error" : t.getMessage();
networkState.postValue(new NetworkState(NetworkState.Status.FAILED, errorMessage));
}
});
}
DataFactory class
public class FeedDataFactory extends DataSource.Factory {
private MutableLiveData<FeedDataSource> mutableLiveData;
public FeedDataFactory() {
this.mutableLiveData = new MutableLiveData<>();
}
#Override
public DataSource create() {
FeedDataSource feedDataSource = new FeedDataSource();
mutableLiveData.postValue(feedDataSource);
return feedDataSource;
}
public MutableLiveData<FeedDataSource> getMutableLiveData() {
return mutableLiveData;
}
ViewModel
public class FeedViewModel extends ViewModel {
private Executor executor;
private LiveData<NetworkState> networkState;
private LiveData<PagedList<Article>> articleLiveData;
public FeedViewModel() {
init();
}
private void init() {
executor = Executors.newFixedThreadPool(5);
FeedDataFactory feedDataFactory = new FeedDataFactory();
networkState = Transformations.switchMap(feedDataFactory.getMutableLiveData(),
dataSource -> dataSource.getNetworkState());
PagedList.Config pagedListConfig = new PagedList.Config.Builder()
.setEnablePlaceholders(true)
.setInitialLoadSizeHint(20)
.setPageSize(20)
.build();
articleLiveData = new LivePagedListBuilder(feedDataFactory, pagedListConfig)
.setFetchExecutor(executor)
.build();
}
public LiveData<NetworkState> getNetworkState() {
return networkState;
}
public LiveData<PagedList<Article>> getArticleLiveData() {
return articleLiveData;
}
Activity
public class FeedActivity extends AppCompatActivity {
private FeedListAdapter adapter;
private FeedViewModel feedViewModel;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/*
* Step 2: Initialize the ViewModel
*
* */
feedViewModel = new FeedViewModel();
/*
* Step 2: Setup the adapter class for the RecyclerView
*
* */
RecyclerView recyclerView = findViewById(R.id.list_feed);
recyclerView.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
adapter = new FeedListAdapter(getApplicationContext());
/*
* Step 4: When a new page is available, we call submitList() method
* of the PagedListAdapter class
*
* */
feedViewModel.getArticleLiveData().observe(this, pagedList -> {
Log.d("TAG", "onCreate: ");
adapter.submitList(pagedList);
});
/*
* Step 5: When a new page is available, we call submitList() method
* of the PagedListAdapter class
*
* */
feedViewModel.getNetworkState().observe(this, networkState -> {
adapter.setNetworkState(networkState);
});
recyclerView.setAdapter(adapter);
}

Memory leak issues in Fragment

I have implemented MVP pattern in my app. And I'm using WeakReferences to store View's reference in my Presenter. But still my fragments are not being claimed by GC upon destroying. Below is the screenshot of problem. Any idea what is causing this and how to remove this issue?
Below is the code for my Presenter:
public class ProductDetailPresenter implements ProductDetailContract.Presenter {
private final WeakReference<ProductDetailContract.View> view;
private CategoriesDataSource repo;
public ProductDetailPresenter(ProductDetailContract.View view, CategoriesDataSource repo) {
this.view = new WeakReference<>(view);
this.repo = repo;
view.setPresenter(this);
}
#Override
public void start() {
}
#Override
public void submitRating(final Product product, final float mRating) {
final ProductDetailContract.View view =ProductDetailPresenter.this.view.get();
if (view != null) {
repo.submitRating(product.getId(), mRating, true, new CategoriesDataSource.SubmitRatingCallback() {
#Override
public void onRatingSubmitted() {
product.setRating(mRating);
product.setRated(true);
product.setUpdatedAt(new Date(System.currentTimeMillis()));
repo.updateProductInDB(product);
if (!view.isActive()) return;
view.onRatingSubmitted(true, mRating);
}
#Override
public void onError(Throwable throwable) {
if (!view.isActive()) return;
view.onRatingSubmitted(false, 0);
}
});
}
}
#Override
public void onRateKarenClicked() {
ProductDetailContract.View view = this.view.get();
if (view != null) {
view.openDialog();
}
}
#Override
public void onAbhiKhareediyeClicked(Product product) {
EventBus.getDefault().post(
new ProductDetailContract.ContractEventMessages(
ProductDetailContract.ContractEventMessages.EVENT_START_QUANTITY_SCREEN, product));
}
}
This is the problem:
#Override
public void submitRating(final Product product, final float mRating) {
final ProductDetailContract.View view =ProductDetailPresenter.this.view.get(); <-- this is bad
you have a final object that is being passed to the repo. Delete the whole line. You don't need it. Use in the view.get() inside the onRatingSubmitted and onError

Using Dagger 2 in SyncAdapter class

I am using Sync Adapter along with Dagger 2 for dependency injection. I am stuck since I cannot seem to figure out where should I use XYZ.inject since SyncAdapter class does not provide OnCreate or an Activity to stick to. Can someone suggest how to deal with Dependency injection in case of Sync Adapter alike classes which do not belong to activity/fragment?
PS: I have looked at several similar questions but failed to find a solution to my problem.
SyncAdapter.java
public class SyncAdapter extends AbstractThreadedSyncAdapter {
ContentResolver mContentResolver;
//Injects here
#Inject
SyncCenterPresenter mSyncCenterPresenter;
private final AccountManager mAccountManager;
Context context;
public SyncAdapter(Context context, boolean autoInitialize) {
super(context, autoInitialize);
mContentResolver = context.getContentResolver();
mAccountManager = AccountManager.get(context);
this.context=context;
}
Account mainAccount;
public static final int SYNC_INTERVAL = 60 * 1;
public static final int SYNC_FLEXTIME = SYNC_INTERVAL/3;
#Override
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
Log.v("Sync class me","sync adapter on perform sync");
if (mSyncCenterPresenter == null){
Log.v("messsage","null");
} else {
Log.v("messsage","not null");
mSyncCenterPresenter.loadDatabaseCenterPayload();
mSyncCenterPresenter.syncPayload();
}
}
/**
* Helper method to schedule the sync adapter periodic execution
*/
public static void configurePeriodicSync(Context context, int syncInterval, int flexTime) {
Account account = myAccount;
String authority = "com.mifos.provider";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// we can enable inexact timers in our periodic sync
SyncRequest request = new SyncRequest.Builder().
syncPeriodic(syncInterval, flexTime).
setSyncAdapter(account, authority).
setExtras(new Bundle()).build();
ContentResolver.requestSync(request);
} else {
ContentResolver.addPeriodicSync(account,
authority, new Bundle(), syncInterval);
}
}
static Account myAccount;
public static void onAccountCreated(Account newAccount, Context context) {
/*
* Since we've created an account
*/
myAccount = newAccount;
SyncAdapter.configurePeriodicSync(context, SYNC_INTERVAL, SYNC_FLEXTIME);
/*
* Without calling setSyncAutomatically, our periodic sync will not be enabled.
*/
ContentResolver.setSyncAutomatically(newAccount, "com.mifos.provider", true);
/*
* Finally, let's do a sync to get things started
*/
syncImmediately(context);
}
public static Account getSyncAccount(Context context) {
// Create the account type and default account
Account newAccount = new Account(
context.getString(R.string.app_name), "com.mifos");
return newAccount;
}
/**
* Helper method to have the sync adapter sync immediately
* #param context The context used to access the account service
*/
public static void syncImmediately(Context context) {
Bundle bundle = new Bundle();
bundle.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
ContentResolver.requestSync(getSyncAccount(context),
"com.mifos.provider", bundle);
}
}
SyncCenterPresenter.java
public class SyncCenterPresenter {
private final DataManagerCenter mDataManagerCenter;
private CompositeSubscription mSubscriptions;
List<CenterPayload> centerPayloads;
int mCenterSyncIndex = 0;
#Inject
public SyncCenterPresenter(DataManagerCenter dataManagerCenter) {
Log.v("messsage","const me");
mDataManagerCenter = dataManagerCenter;
mSubscriptions = new CompositeSubscription();
centerPayloads = new ArrayList<>();
}
public void loadDatabaseCenterPayload() {
Log.v("messsage","load me");
mSubscriptions.add(mDataManagerCenter.getAllDatabaseCenterPayload()
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber<List<CenterPayload>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(List<CenterPayload> centerPayloads) {
showCenters(centerPayloads);
}
}));
}
public void syncCenterPayload(CenterPayload centerPayload) {
mSubscriptions.add(mDataManagerCenter.createCenter(centerPayload)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Observer<SaveResponse>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(SaveResponse center) {
showCenterSyncResponse();
}
}));
}
public void deleteAndUpdateCenterPayload(int id) {
mSubscriptions.add(mDataManagerCenter.deleteAndUpdateCenterPayloads(id)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Observer<List<CenterPayload>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(List<CenterPayload> centerPayloads) {
showPayloadDeletedAndUpdatePayloads(centerPayloads);
}
}));
}
public void showCenters(List<CenterPayload> centerPayload) {
centerPayloads = centerPayload;
}
public void showCenterSyncResponse() {
deleteAndUpdateCenterPayload(centerPayloads
.get(mCenterSyncIndex).getId());
}
public void showPayloadDeletedAndUpdatePayloads(List<CenterPayload> centers) {
mCenterSyncIndex = 0;
this.centerPayloads = centers;
}
public void syncPayload() {
for (int i = 0; i < centerPayloads.size(); ++i) {
if (centerPayloads.get(i).getErrorMessage() == null) {
syncCenterPayload(centerPayloads.get(i));
mCenterSyncIndex = i;
break;
} else {
Log.v("messsage","else block");
}
}
}
}
ActivityComponent
#PerActivity
#Component(dependencies = ApplicationComponent.class, modules =
ActivityModule.class)
public interface ActivityComponent {
void inject(LoginActivity loginActivity);
void inject(PassCodeActivity passCodeActivity);
//other methods
void inject(SyncAdapter syncAdapter);
}
ActivityModule
#Module
public class ActivityModule {
private Activity mActivity;
public ActivityModule(Activity activity) {
mActivity = activity;
}
#Provides
Activity provideActivity() {
return mActivity;
}
#Provides
#ActivityContext
Context providesContext() {
return mActivity;
}
}
EDIT
SyncService.java
public class SyncService extends Service {
private static final Object sSyncAdapterLock = new Object();
private static SyncAdapter sSyncAdapter = null;
#Override
public void onCreate() {
synchronized (sSyncAdapterLock) {
if (sSyncAdapter == null) {
sSyncAdapter = new SyncAdapter(getApplicationContext(), true);
}
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return sSyncAdapter.getSyncAdapterBinder();
}
}
Can someone please help me to get this working? since I can't figure out where to use the inject and how to do it without an Activity Component? A different and a better approach would be appreciated as well.
Thanks
You can implement the constructor, so please use constructor injection to initialize your adapter.
public class SyncAdapter extends AbstractThreadedSyncAdapter {
// ...
#Inject
public SyncAdapter(Context context, boolean autoInitialize) { /*...*/ }
}
Then you simply inject the service that returns the SyncAdapter like you would anything else...
public class SyncService extends Service {
#Inject SyncAdapter syncAdapter;
#Override
public void onCreate() {
AndroidInjection.inject(this);
// or
DaggerSyncServiceComponent.create().inject(this);
}
#Override
public IBinder onBind(Intent intent) {
return syncAdapter;
}
}
And that's it.

Migrating from java.lang.Observable to RxJava

I'm working on an Android application that features a shopping cart. The Cart object extends java.lang.Observable so if there are any changes, they are saved to the disk immediately and also so that the badge icon can be updated.
public class Cart extends Observable{
private Set<Product> products;
public Cart(){
products = new HashSet<>();
}
public Cart(Collection<Product> products){
this.products = new HashSet<>(products);
}
public int getTotalItems() {
return products.size();
}
public void clear(){
products.clear();
setChanged();
notifyObservers();
}
public void addProduct(Product product){
products.add(product);
setChanged();
notifyObservers();
}
public void removeProduct(Product product){
products.remove(product);
setChanged();
notifyObservers();
}
public void updateProduct(Product product){
products.remove(product);
products.add(product);
setChanged();
notifyObservers();
}
public List<Product> getProducts() {
return new ArrayList<>(products);
}
}
Example usage
public class MainActivity extends BaseActivity implements Observer {
Cart mCart;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
getApp().getCart().addObserver(this);
setCartItemsCount(getApp().getCart().getTotalItems());
//...
}
#Override
protected void onDestroy() {
super.onDestroy();
if (getApp().getCart() != null) {
getApp().getCart().deleteObserver(this);
}
}
#Override
public void update(Observable observable, Object data) {
if (observable instanceof Cart) {
setCartItemsCount(((Cart) observable).getTotalItems());
}
}
}
I'd like to migrate this code to RxJava but i don't have a clear idea on how to go about it. From what I read, I'm supposed to use BehavioralSubject but I don't know how to adapt the examples I've read to my scenario.
I would appreciate if some guidance or an example.
I made some small example that can help you catch the idea.
I have used this library https://github.com/trello/RxLifecycle, I also recommend you to use this http://google.github.io/dagger/
public class MainActivity extends RxActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final CartPresenter cartPresenter = new CartPresenter();
final TextView counterTextView = (TextView) findViewById(R.id.counter);
final Button button = (Button) findViewById(R.id.button2);
RxView.clicks(button)
.subscribe(new Action1<Void>() {
#Override
public void call(Void aVoid) {
cartPresenter.getCardModel().addProduct(new Random().nextInt(100));
}
});
cartPresenter.getCardObservable()
.compose(this.<CartPresenter.CardModel>bindToLifecycle())
.subscribe(new Action1<CartPresenter.CardModel>() {
#Override
public void call(CartPresenter.CardModel cardModel) {
counterTextView.setText(String.valueOf(cardModel.getProducts().size()));
}
});
}
}
One solution simpler to understand for a beginner
public class CartPresenter {
private final PublishSubject<CardModel> dataChangedSubject = PublishSubject.create();
private final Observable<CardModel> cardObservable;
private final CardModel cardModel;
public CartPresenter() {
cardModel = new CardModel();
this.cardObservable = dataChangedSubject.startWith(cardModel)
.flatMap(new Func1<CardModel, Observable<CardModel>>() {
#Override
public Observable<CardModel> call(CardModel cardModel) {
return Observable.just(cardModel);
}
});
}
public CardModel getCardModel() {
return cardModel;
}
#NonNull
public Observable<CardModel> getCardObservable() {
return cardObservable;
}
class CardModel {
final private Set<Integer> products;
public CardModel() {
this.products = new HashSet<>();
}
public void addProduct(Integer integer) {
products.add(integer);
dataChangedSubject.onNext(this);
}
public Set<Integer> getProducts() {
return products;
}
}
}
And for some more advanced users
public class CartPresenter {
private final PublishSubject<CardModel> dataChangedSubject = PublishSubject.create();
private final Observable<CardModel> cardObservable;
private final CardModel cardModel;
public CartPresenter() {
cardModel = new CardModel();
this.cardObservable = Observable.just(cardModel)
.compose(new Observable.Transformer<CardModel, CardModel>() {
#Override
public Observable<CardModel> call(final Observable<CardModel> cardModelObservable) {
return dataChangedSubject.switchMap(new Func1<Object, Observable<? extends CardModel>>() {
#Override
public Observable<? extends CardModel> call(Object o) {
return cardModelObservable;
}
});
}
});
}
public CardModel getCardModel() {
return cardModel;
}
#NonNull
public Observable<CardModel> getCardObservable() {
return cardObservable;
}
class CardModel {
final private Set<Integer> products;
public CardModel() {
this.products = new HashSet<>();
}
public void addProduct(Integer integer) {
products.add(integer);
dataChangedSubject.onNext(null);
}
public Set<Integer> getProducts() {
return products;
}
}
}

Categories