5

How can i get multiple instances of same return type like cursor

for example :-

Module
@CursorScope
public class CursorModule {


    @Provides
    Cursor provideSongCursor(
            @Named("Song") Musician musician) {
        return musician.getApplicationContext().getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
                new String[]{
                        BaseColumns._ID,
                        MediaStore.Audio.AudioColumns.TITLE,
                        MediaStore.Audio.AudioColumns.ARTIST,
                        MediaStore.Audio.AudioColumns.ALBUM,
                        MediaStore.Audio.AudioColumns.DURATION
                }, MediaStore.Audio.AudioColumns.IS_MUSIC + "=1", null, null);
    }

    @Provides
    Cursor provideAlbumCursor(
            @Named("Album") Musician musician) {
        return musician.getApplicationContext().getContentResolver().query(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI,
                new String[]{
                        BaseColumns._ID,
                        MediaStore.Audio.AlbumColumns.ALBUM,
                        MediaStore.Audio.AlbumColumns.ARTIST,
                        MediaStore.Audio.AlbumColumns.NUMBER_OF_SONGS,
                        MediaStore.Audio.AlbumColumns.FIRST_YEAR
                }, null, null, null);
    }

    @Provides
    Cursor provideArtistCursor(@Named("Artist") Musician musician) {
        return musician.getApplicationContext().getContentResolver().query(MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI,
                new String[] {
                        BaseColumns._ID,
                        MediaStore.Audio.ArtistColumns.ARTIST,
                        MediaStore.Audio.ArtistColumns.NUMBER_OF_ALBUMS,
                        MediaStore.Audio.ArtistColumns.NUMBER_OF_TRACKS
                }, null, null,null);
    }

    @Provides
    Cursor provideGenreCursor(
            @Named("Genres") Musician musician) {
        return musician.getApplicationContext().getContentResolver().query(MediaStore.Audio.Genres.EXTERNAL_CONTENT_URI,
                new String[] {
                        BaseColumns._ID,
                        MediaStore.Audio.GenresColumns.NAME
                }, null, null, null);
    }

    @Provides
    Cursor providePlaylistCursor(@Named("Playlist") Musician musician) {
        return musician.getApplicationContext().getContentResolver().query(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI,
                new String[] {
                        BaseColumns._ID,
                        MediaStore.Audio.PlaylistsColumns.NAME
                }, null, null, null);
    }
}

which is provided in

@CursorScope
@Subcomponent(modules = CursorModule.class)
public interface CursorComponent {
    Cursor cursor();
}

I get this error

Error:(17, 11) Gradle: error: android.database.Cursor is bound multiple times:
@Provides android.database.Cursor com.merkmod.musician.dependency.CursorModule.provideSongCursor(@Named("Song") com.merkmod.musician.application.Musician)
@Provides android.database.Cursor com.merkmod.musician.dependency.CursorModule.provideAlbumCursor(@Named("Album") com.merkmod.musician.application.Musician)
@Provides android.database.Cursor com.merkmod.musician.dependency.CursorModule.provideArtistCursor(@Named("Artist") com.merkmod.musician.application.Musician)
@Provides android.database.Cursor com.merkmod.musician.dependency.CursorModule.provideGenreCursor(@Named("Genres") com.merkmod.musician.application.Musician)
@Provides android.database.Cursor com.merkmod.musician.dependency.CursorModule.providePlaylistCursor(@Named("Playlist") com.merkmod.musician.application.Musician)

I made multiple instances of Cursor and annotated with @Named at provider level first then it started giving me error with cannot be provided with @Provides annotation so i shifted to using it inside the constructor

like in the code above . The problem is running a cycle again and again and i am like stuck in getting the cursor stuff done , any help will be appreaciated.

mkodekar
  • 109
  • 1
  • 8

2 Answers2

8

when you want to provide multiple variable of one type you must use @Named annotation like below:

Module
@CursorScope
public class CursorModule {
@Provides
@Named("songCursor")
Cursor provideSongCursor(
        @Named("Song") Musician musician) {
    return musician.getApplicationContext().getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
            new String[]{
                    BaseColumns._ID,
                    MediaStore.Audio.AudioColumns.TITLE,
                    MediaStore.Audio.AudioColumns.ARTIST,
                    MediaStore.Audio.AudioColumns.ALBUM,
                    MediaStore.Audio.AudioColumns.DURATION
            }, MediaStore.Audio.AudioColumns.IS_MUSIC + "=1", null, null);
}

@Provides
@Named("albumCursor")
Cursor provideAlbumCursor(
        @Named("Album") Musician musician) {
    return musician.getApplicationContext().getContentResolver().query(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI,
            new String[]{
                    BaseColumns._ID,
                    MediaStore.Audio.AlbumColumns.ALBUM,
                    MediaStore.Audio.AlbumColumns.ARTIST,
                    MediaStore.Audio.AlbumColumns.NUMBER_OF_SONGS,
                    MediaStore.Audio.AlbumColumns.FIRST_YEAR
            }, null, null, null);
}
  @Provides
@Named("artistCursor")
Cursor provideArtistCursor(@Named("Artist") Musician musician) {
    return musician.getApplicationContext().getContentResolver().query(MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI,
            new String[] {
                    BaseColumns._ID,
                    MediaStore.Audio.ArtistColumns.ARTIST,
                    MediaStore.Audio.ArtistColumns.NUMBER_OF_ALBUMS,
                    MediaStore.Audio.ArtistColumns.NUMBER_OF_TRACKS
            }, null, null,null);
}

@Provides
@Named("genreCursor")
Cursor provideGenreCursor(
        @Named("Genres") Musician musician) {
    return musician.getApplicationContext().getContentResolver().query(MediaStore.Audio.Genres.EXTERNAL_CONTENT_URI,
            new String[] {
                    BaseColumns._ID,
                    MediaStore.Audio.GenresColumns.NAME
            }, null, null, null);
}

@Provides
@Named("playListCursor")
Cursor providePlaylistCursor(@Named("Playlist") Musician musician) {
    return musician.getApplicationContext().getContentResolver().query(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI,
            new String[] {
                    BaseColumns._ID,
                    MediaStore.Audio.PlaylistsColumns.NAME
            }, null, null, null);
}


}

then when you want to inject write like below:

@Inject
@Named("soundCursor")
Cursor soundCursor;

@Inject
@Named("albumCursor")
Cursor albumCursor;

@Inject
@Named("artistCursor")
Cursor artistCursor;

@Inject
@Named("genreCursor")
Cursor genreCursor;

@Inject
@Named("playListCursor")
Cursor playListCursor;

if you wan to inject them in constructor injections do like below:

@Inject
public SomeClassConstructor(@Named("album") Cursor cursur)

and what you have written in your subcomponent interface I cant get it what it is, it must be like:

@CursorScope
@Subcomponent(modules = CursorModule.class)
public interface CursorComponent {
   void inject(TheClassThatWantsToUseInject1 obj);
   void inject(TheClassThatWantsToUseInject2 obj);
}

and in your application component:

   YourSubComponentInterface plus(CursorModule module);
Amir Ziarati
  • 14,248
  • 11
  • 47
  • 52
  • problem is still existing in component class – mkodekar Oct 08 '16 at 13:36
  • Error:(17, 11) Gradle: error: android.database.Cursor cannot be provided without an @Provides-annotated method. android.database.Cursor is provided at com.merkmod.musician.dependency.CursorComponent.cursor() – mkodekar Oct 08 '16 at 13:37
  • I know the injection part as well. – mkodekar Oct 08 '16 at 14:12
  • you must pute `@Named` before all of your `cursor` injections. you cant have any `cursor` injection without `@Named` anymore. search for your cursor injections and change all of them adding a `@Named` – Amir Ziarati Oct 09 '16 at 04:31
  • i am sorry its not , if you have a working example , or show me the component class where you are asking for the cursor , i know how the dependency injection graph works but i am not sure about cursor's compatibility with @Named. – mkodekar Oct 09 '16 at 12:35
  • its not about compatibility Dagger only provides the same types with `@Named` annotation, I dont have a sample specifically using `@Named` with cursor. you test what i told ? naming all your provide methods by `@Named` and injecting them with the sme name using `@Named` ? all of them must have `@Named` not just the two i mentioned. i mentioned these two as sample. you need to name ALL of them with the cursor type. – Amir Ziarati Oct 09 '16 at 12:41
  • i did the same before asking the question – mkodekar Oct 09 '16 at 12:43
  • im so eager to solve your problem :D it made me curious badly :/ how can it fail :/ but in your Question you didnt mention `@Named` on the top of your provide you used `@Named` in the provide method's argument :/ im so clear let me i just rewrite all of your code :D – Amir Ziarati Oct 09 '16 at 12:46
  • I UPDATED the answer. look I changed the `@Named` i wrote before cause it was conflicting with your previous named injects (should be unique). test just my code. just what i wrote. and inject just how i did EVERYWHERE in your code. – Amir Ziarati Oct 09 '16 at 12:53
  • my module has undergone everything you have written there can you please tell me what you would write in the component class. – mkodekar Oct 09 '16 at 13:05
  • you need to write a plus method for subcomponents in the main component then an inject function for them in subcomponent interface then calling this inject method whenever you need to inject. look the project of my frien below to understand: https://github.com/mmirhoseini/trakt.tv – Amir Ziarati Oct 09 '16 at 13:10
  • Error again Error:(15, 11) Gradle: error: android.database.Cursor cannot be provided without an @Provides-annotated method. android.database.Cursor is provided at com.merkmod.musician.dependency.CursorSubComponent.cursor() – mkodekar Oct 09 '16 at 13:37
  • Did you call 'plus(yourCursorModule)inject(theClasswantToInject)' where you want to inject the cursor ? Look at the github project i mentioned there is something wrong with you subcomponent im sure. – Amir Ziarati Oct 09 '16 at 13:42
  • The error refers cursorSubcomponent.cursor() which i said to remove. How it refers to that ? Did you changed your subcomponent just like i said ? Wish i could see your project became a maze to me :/ – Amir Ziarati Oct 09 '16 at 13:47
  • i made a subcomponent and added 2 methods with @Named in module and added the refrences to the subcomponent in the maincomponent class now the same error comes again and again , i did the same thing with a beans class and i dont have major problem with the beans class , i dont know whats wrong with cursor, even context works perfectly – mkodekar Oct 09 '16 at 13:52
  • Or maybe There is atleast on place you didnt put '@named' when you want to inject. Check all places you want to inject any type of cursor and put '@named' for all of them. – Amir Ziarati Oct 09 '16 at 13:52
  • i did that bro , your answer is also wrong as well , i am not being harsh but if you can try something with cursor and share your findings , i will be happy. – mkodekar Oct 09 '16 at 13:54
  • Ok ill try soon ;) – Amir Ziarati Oct 09 '16 at 13:55
  • so did you find anything. – mkodekar Oct 10 '16 at 05:59
3

so finally I got the answer to my own question and it was the component part itself , you know software development is a so much of a hectic that too it becomes more of a burden when you quit cigerrates.

so all of the above was a simple approach and i made a different example with sharepreference, because my lappy crashed on archlinux.

so here are the snippet of the code i produced.

so the only thing i should have done is that i should have removed cursor injection params from the component interface.

none the less the snippet might help people.

Component :-

@Singleton
@Component(modules = {MusicianModule.class, SharedPreferencesModule.class})
public interface MusicianComponent {
    void inject(MainActivity mainActivity);
    Musician musician();
}

Module :-

@Module
public class SharedPreferencesModule {

    @Provides
    @Named("default")
    SharedPreferences provideDefaultsharedPreferences(Musician musician) {
        return musician.getSharedPreferences("default", Context.MODE_PRIVATE);
    }

    @Provides
    @Named("secret")
    SharedPreferences provideSecretsharedPreferences(Musician musician) {
        return musician.getSharedPreferences("secret", Context.MODE_PRIVATE);
    }
}

Musician Module :-

@Module
public class MusicianModule {

    private Musician musician;

    public MusicianModule(Musician musician) {
        this.musician = musician;
    }

    @Provides @Singleton
    Musician providemusician() {
        return musician;
    }

    @Provides @Singleton
    Application provideapplication(Musician musician) {
        return musician;
    }
}

Application class :-

public class Musician extends Application {

    private MusicianComponent musicianComponent;
    @Override
    public void onCreate() {
        resolvedependency();
        super.onCreate();
    }


    private void resolvedependency() {
        musicianComponent = DaggerMusicianComponent.builder()
                .musicianModule(new MusicianModule(this))
                .sharedPreferencesModule(new SharedPreferencesModule())
                .build();
    }


    public static MusicianComponent getMusicianComponent(Context context) {
        return ((Musician)context.getApplicationContext()).musicianComponent;
    }
}

And the injection in the MainActivity :-

public class MainActivity extends AppCompatActivity {

    /**
     * The {@link android.support.v4.view.PagerAdapter} that will provide
     * fragments for each of the sections. We use a
     * {@link FragmentPagerAdapter} derivative, which will keep every
     * loaded fragment in memory. If this becomes too memory intensive, it
     * may be best to switch to a
     * {@link android.support.v4.app.FragmentStatePagerAdapter}.
     */
    private SectionsPagerAdapter mSectionsPagerAdapter;



    /**
     * The {@link ViewPager} that will host the section contents.
     */
    private ViewPager mViewPager;

    @Inject @Named("default")
    SharedPreferences defSharedPreferences;

    @Inject @Named("secret")
    SharedPreferences secSharedPreferences;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Musician.getMusicianComponent(this).inject(this);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        // Create the adapter that will return a fragment for each of the three
        // primary sections of the activity.
        mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
        defSharedPreferences.edit().putString("status", "worked").apply();
        secSharedPreferences.edit().putString("status", "worked").apply();

        // Set up the ViewPager with the sections adapter.
        mViewPager = (ViewPager) findViewById(R.id.container);
        mViewPager.setAdapter(mSectionsPagerAdapter);

        TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
        tabLayout.setupWithViewPager(mViewPager);
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    /**
     * A placeholder fragment containing a simple view.
     */
    public static class PlaceholderFragment extends Fragment {
        /**
         * The fragment argument representing the section number for this
         * fragment.
         */
        private static final String ARG_SECTION_NUMBER = "section_number";

        public PlaceholderFragment() {
        }

        /**
         * Returns a new instance of this fragment for the given section
         * number.
         */
        public static PlaceholderFragment newInstance(int sectionNumber) {
            PlaceholderFragment fragment = new PlaceholderFragment();
            Bundle args = new Bundle();
            args.putInt(ARG_SECTION_NUMBER, sectionNumber);
            fragment.setArguments(args);
            return fragment;
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            View rootView = inflater.inflate(R.layout.fragment_song, container, false);
            TextView textView = (TextView) rootView.findViewById(R.id.section_label);
            textView.setText(getString(R.string.section_format, getArguments().getInt(ARG_SECTION_NUMBER)));
            return rootView;
        }
    }

    /**
     * A {@link FragmentPagerAdapter} that returns a fragment corresponding to
     * one of the sections/tabs/pages.
     */
    public class SectionsPagerAdapter extends FragmentPagerAdapter {

        public SectionsPagerAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {
            // getItem is called to instantiate the fragment for the given page.
            // Return a PlaceholderFragment (defined as a static inner class below).
            return PlaceholderFragment.newInstance(position + 1);
        }

        @Override
        public int getCount() {
            // Show 3 total pages.
            return 3;
        }

        @Override
        public CharSequence getPageTitle(int position) {
            switch (position) {
                case 0:
                    return "SECTION 1";
                case 1:
                    return "SECTION 2";
                case 2:
                    return "SECTION 3";
            }
            return null;
        }
    }

defaul prefs

Secret prefs

sharedpreferencefiles

mkodekar
  • 109
  • 1
  • 8