Dagger 注入 - 何时调用 provide 方法
Dagger injection - when do the provide methods get called
我正在 Android 上对 Dagger 进行一些试验,这似乎是一个很好的隔离依赖项的工具。首先,我从 GitHub 复制了 android-activity-graphs 示例:https://github.com/square/dagger/tree/master/examples/android-activity-graphs
然后我向 ActivityModule
添加了几个 classes
@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 中的示例相同。
令我困惑的是,注入的对象在 class 被注入到 (HomeFragment) 之后是空的......有一段时间了。
同样,HomeFragment 与 github 中示例中的 HomeFragment 或多或少相同,只是我自己添加了一些内容。
如果我在 HomeFragment 的 onCreateView() 中调用注入的 Player 或 RandomNumberGenerator 对象中的任何一个,我会收到一条错误消息,指出它们为空。
但是,如果我在内部 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;
}
我用来测试的 classes 非常微不足道(RandomNumberGenerator 比 Player class 更重要)。我将跳过 RandomNumberGenerator。这是实现 Player 的 MyAndroidTestPlayer(只有一个 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);
}
}
}
提前谢谢你。
我想我明白了。 HomeFragment 必须 @Override onCreate 并使用以下行注入自身
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((DemoBaseActivity) getActivity()).inject(this);
}
那对我有用。
我希望这对我理解水平的其他用户有所帮助。
我正在 Android 上对 Dagger 进行一些试验,这似乎是一个很好的隔离依赖项的工具。首先,我从 GitHub 复制了 android-activity-graphs 示例:https://github.com/square/dagger/tree/master/examples/android-activity-graphs
然后我向 ActivityModule
添加了几个 classes@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 中的示例相同。
令我困惑的是,注入的对象在 class 被注入到 (HomeFragment) 之后是空的......有一段时间了。 同样,HomeFragment 与 github 中示例中的 HomeFragment 或多或少相同,只是我自己添加了一些内容。 如果我在 HomeFragment 的 onCreateView() 中调用注入的 Player 或 RandomNumberGenerator 对象中的任何一个,我会收到一条错误消息,指出它们为空。 但是,如果我在内部 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;
}
我用来测试的 classes 非常微不足道(RandomNumberGenerator 比 Player class 更重要)。我将跳过 RandomNumberGenerator。这是实现 Player 的 MyAndroidTestPlayer(只有一个 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);
}
}
}
提前谢谢你。
我想我明白了。 HomeFragment 必须 @Override onCreate 并使用以下行注入自身
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((DemoBaseActivity) getActivity()).inject(this);
}
那对我有用。
我希望这对我理解水平的其他用户有所帮助。