0

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

Falcon
  • 372
  • 4
  • 20

1 Answers1

1

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.

David Medenjak
  • 33,993
  • 14
  • 106
  • 134
  • Cannot resolve symbol AndroidInjection or DaggerSyncServiceComponent. What am I missing? – Falcon May 17 '18 at 08:20
  • 1
    @Falcon `AndroidInjection` is for when you use `dagger.android`, `DaggerSyncServiceComponent` should be replaced with whichever component you want to use to inject your service. Just inject it like you would your activities, etc – David Medenjak May 17 '18 at 08:32
  • Thanks for the answer, I would try doing that. Is there an alternative way to do this without using the dagger.android in gradle, since this is for an open source project and I don't want to break anything and adding dagger.android gave me a lot of errors. – Falcon May 17 '18 at 10:28
  • @Falcon You can use any one of your components to inject it or create a new, dedicated one for it. In the end you just need to call `someComponent.inject(this)` to inject the fields. – David Medenjak May 17 '18 at 10:30