Инъекция кинжала - когда вызываются методы предоставления

Я немного экспериментирую с Dagger на Android, который кажется хорошим инструментом для изоляции зависимостей. Во-первых, я скопировал пример графиков активности Android с GitHub: https://github.com/square/dagger/tree/master/examples/android-activity-graphs

Затем я добавил пару классов в ActivityModule.

@Module(
    injects = {
            HomeActivity.class,
            HomeFragment.class
    },
    addsTo = AndroidModule.class,
    library = true
)

public class ActivityModule {
    private static final String TAG = "Activity_Module";
    private final DemoBaseActivity activity;

    public ActivityModule(DemoBaseActivity activity) {
        this.activity = activity;
    }

    /**
    * Allow the activity context to be injected but require that it be         annotated with
    * {@link ForActivity @ForActivity} to explicitly differentiate it from application context.
    */
    @Provides
    @Singleton
    @ForActivity
    Context provideActivityContext() {
        return activity;
    }

    @Provides
    @Singleton
    ActivityTitleController provideTitleController() {
        return new ActivityTitleController(activity);
    }

    //My addition from here
    @Provides
    @Singleton
    Player providePlayer() {
        Log.i(TAG, "in providePlayer()");
        return new MyAndroidTestPlayer(activity);
    }

    @Provides
    RandomNumberGenerator provideRandomNumberGenerator() {
        Log.i(TAG, "in provideRandomNumberGenerator()");
        return new RealRandomNumberGenerator();
    }
}

В остальном инициализация графа идентична примеру с github.

Что меня озадачивает, так это тот факт, что введенный объект становится нулевым после создания класса, в который они внедряются (HomeFragment)... какое-то время. Опять же, HomeFragment более или менее идентичен HomeFragment из примеров на github, с некоторыми моими дополнениями. Если я вызываю что-либо для любого из внедренных объектов Player или RandomNumberGenerator в onCreateView() HomeFragment, я получаю сообщение об ошибке, говорящее, что они равны нулю. Однако, если я вызываю их внутри внутреннего OnClickListener - onClick(), они работают, как и ожидалось.

Может ли кто-нибудь указать мне на часть знаний, которых мне не хватает, чтобы понять, что здесь происходит?

public class HomeFragment extends DemoBaseFragment {
public static final String TAG = "HOME_FRAGMENT";

public static HomeFragment newInstance() {
    return new HomeFragment();
}

@Inject
ActivityTitleController titleController;
@Inject
Player player;
@Inject
RandomNumberGenerator randomNumberGenerator;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    TextView tv = new TextView(getActivity());
    if (randomNumberGenerator != null) {
        Log.i(TAG, "randomNumberGenerator is NOT null");
    } else {
        Log.e(TAG, "randomNumberGenerator is NULL!");
    }
    if (player != null) {
        Log.i(TAG, "player is NOT null");
    } else {
        Log.e(TAG, "player is NULL!");
    }
    //int randomNumber = randomNumberGenerator.getIntegerInRange(48, 50);
    //player.playTestNote();
    tv.setGravity(CENTER);
    tv.setText("Play test note");
    tv.setTextSize(40);
    tv.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            player.playTestNote();
            int randomNumber = randomNumberGenerator.getIntegerInRange(48, 50);
            Log.i(TAG, "Text view clicked, random number is: " + randomNumber);
        }
    });
    return tv;
}

Классы, которые я использую для тестирования, довольно тривиальны (RandomNumberGenerator в большей степени, чем класс Player). Я пропущу RandomNumberGenerator. Вот MyAndroidTestPlayer, который реализует Player (всего один метод playTestNote()).

public class MyAndroidTestPlayer implements Player {

SoundPool soundPool;
private static final int MAX_STREAMS = 10;
private static final int DEFAULT_SRC_QUALITY = 0;
private static final int HARDCODED_SOUND_RESOURCE_C3 = R.raw.midi_48_c3;
private static final int DEFAULT_PRIORITY = 1;
private static final String TAG = "MyAndroidTestPlayer";
private Context context;
private boolean isLoaded = false;
private int streamId;
private int soundId;

public MyAndroidTestPlayer(Context context) {
    this.context = context;
    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        createNewSoundPool();
    } else {
        createOldSoundPool();
    }
}

protected void createOldSoundPool() {
    soundPool = new SoundPool(MAX_STREAMS, AudioManager.STREAM_MUSIC, DEFAULT_SRC_QUALITY);
    Log.i(TAG, "created old sound pool");
    loadSoundPool();
}

protected void createNewSoundPool() {
    AudioAttributes attributes = new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).build();
    soundPool = new SoundPool.Builder().setAudioAttributes(attributes).build();

    Log.i(TAG, "created new sound pool");
    loadSoundPool();
}

private void loadSoundPool() {
    soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {

        @Override
        public void onLoadComplete(SoundPool soundPool, int sampleId,
                                   int status) {
            isLoaded = true;
            Log.i(TAG, "Loaded");
            Log.i(TAG, "Status: " + status);
        }
    });
    soundId = soundPool.load(context, HARDCODED_SOUND_RESOURCE_C3, DEFAULT_PRIORITY);
}

@Override
public void playTestNote() {
    Log.i(TAG, "before loaded check");
    if (isLoaded) {
        streamId = soundPool.play(soundId, 1, 1, 1, 0, 1f);
        Log.i(TAG, "Played Sound");
        Log.i(TAG, "streamId: " + streamId);
    }
}

}

Заранее спасибо.


person zob    schedule 18.03.2015    source источник


Ответы (1)


Думаю, я понял. HomeFragment должен был @Override onCreate и внедрить себя со следующей строкой

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    ((DemoBaseActivity) getActivity()).inject(this);
}

Тогда это работает для меня.

Я надеюсь, что это поможет другим пользователям на моем уровне понимания.

person zob    schedule 18.03.2015