我的模块类中有一组@Singleton
和@Provides
方法,用于在整个应用程序中创建Singleton实例。一切都很好,除了以下几个瓶颈场景:
步骤1.我正在从OKHttpClient
创建一个带有Auth令牌的Retrofit实例,以便每次都进行经过身份验证的api调用(通过SharedPreferences
处理身份验证令牌检索和插入)。但是,在我通过清除数据库和共享首选项值注销应用程序之后重新启动活动时,问题就开始了。
步骤2.注销后,我再次请求获取身份验证令牌并再次插入SharedPreferences
以供将来使用。
步骤3:现在,如果我继续进行其余的api调用,则前一个Dagger @Singleton
和@Provides
方法的实例保持不变,除非直到我重新启动应用程序,方法是从最近的任务。 (新的身份验证令牌未更新)
需要修复:
如何强制触发Dagger提供程序方法再次触发或撤消它?
是否有任何方法可以将应用程序类数据刷新为与应用程序重新启动时类似的行为。?
请找到我项目中使用的Dagger 2架构:
NetworkModule.java (Dagger模块类)
@Module
public class NetworkModule {
private Context context;
public NetworkModule(Application app) {
this.context = app;
}
@Provides
@Named("network.context")
Context providesContext() {
return context;
}
@Singleton
@Provides
OkHttpClient providesOkHttpClient(@Named("network.context")final Context context) {
final UserProfile userProfile = GsonUtils.createPojo(SharedPrefsUtils.getString(Constants.SHARED_PREFS.USERS_PROFILE, "",context), UserProfile.class);
Logger.i(userProfile != null && !TextUtils.isEmpty(userProfile.getAuth_token()) ? userProfile.getAuth_token() : "----OAuth token empty---");
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(new Interceptor() {
@Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request original = chain.request();
Request request = original.newBuilder()
.header("Accept", "application/json")
.header("Content-Type", "application/json")
.header("Api-Version", "application/vnd.addo-v1+json")
.header("Access-Token", userProfile != null && !TextUtils.isEmpty(userProfile.getAuth_token()) ? userProfile.getAuth_token() : "")
.header("App-Version", Utils.getVersionName(context))
.header("Device-Platform","android")
.method(original.method(), original.body())
.build();
return chain.proceed(request);
}
});
return httpClient.build();
}
@Provides
@Named(Constants.INJECTION.BASE_URL)
String providebaseURL() {
return Constants.URL.BASE_URL;
}
@Singleton
@Provides
Retrofit providesRetrofit(@Named("network.context")Context context, @Named(Constants.INJECTION.BASE_URL) String baseURL, OkHttpClient okHttpClient) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseURL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.client(okHttpClient)
.build();
return retrofit;
}
@Singleton
@Provides
NetworkApiService providesNetworkApiService(Retrofit retrofit){
return retrofit.create(NetworkApiService.class);
}
@Singleton
@Provides
ProjectPresenter providesProjectPresenter(NetworkApiService networkApiService){
return new ProjectPresenterImpl(networkApiService);
}
}
AppComponent.java (Dagger组件类)
@Singleton
@Component(modules = {NetworkModule.class})
public interface AppComponent {
//ACtivity
void inject(AuthenticationActivity authenticationActivity);
void inject(MainActivity mainActivity);
//Fragments
void inject(ProjectsListFragment projectsListFragment);
}
Application.java (用于创建Dagger组件的类)
public class Application extends Application {
private AppComponent appComponent ;
@Override
public void onCreate() {
super.onCreate();
Realm.init(this);
ButterKnife.setDebug(BuildConfig.DEBUG);
appComponent = DaggerAppComponent.builder().appModule(new AppModule(this)).networkModule(new NetworkModule(this)).build();
}
public AppComponent getAppComponent() {
return appComponent;
}
}
请帮助我提出建议或提示,以解决Dagger 2这种奇怪的行为。任何类型的解决方案都会对我有所帮助,因为我在过去6天内完全坚持了这一点。我无能为力,因为我的完整架构是建立在此之上的。请原谅我的拼写错误和更正。如果对此有任何澄清,请告诉我。提前致谢。
答案 0 :(得分:6)
您的问题是@Singleton
。 @Singleton
告诉Dagger您希望Dagger缓存和管理实例状态,并且当您这样做时,您无法获得大量控制来刷新实例。但是,欢迎您从@Singleton
方法中删除@Provides
并自行管理该实例。如果没有范围,Dagger将针对每个注入请求调用您的@Provides
方法,这将允许您返回您希望的任何实例并在适当时使其无效。
请参阅this answer from yesterday,顺便提一下,它也是一个服务于改造的NetworkModule,以及在AppComponent上刷新实例的范围问题。 (你们两个不在同一个团队,是吗?)
/* Module fields */
OkHttpClient myClient;
String lastToken;
/** Not @Singleton */
@Provides
OkHttpClient providesOkHttpClient(
@Named("network.context") final Context context, TokenManager tokenManager) {
String currentToken = getToken(); // gets token from UserProfile
if (myInstance == null || !lastToken.equals(currentToken)) {
lastToken = currentToken;
myInstance = createInstance(currentToken); // As you have it above
}
return myInstance;
}
没有办法自动刷新共享首选项,但是使用上面的按需创建结构,只要当前令牌发生更改,您就可以轻松地将其写入数据持有者。此时,在另一个答案中提取NetworkManager可能是有意义的。
答案 1 :(得分:5)
如何强制触发Dagger提供程序方法再次触发或撤消它?
是否有任何方法可以像应用程序重新启动时那样刷新应用程序类数据?
不,没有这样的触发器。 Component
负责为您提供依赖。如果您完成了一个Component
并且想要使其无效(即您要再次创建依赖项),则必须从中处置(null out)并创建一个新的Component
。现在,您的所有依赖项将再次创建。
答案 2 :(得分:5)
根据azizbekian解决方案,我修改了一些代码,它就像一个魅力。非常感谢!
如果使用单击注销按钮,我将清除SharedPreference并通过应用程序clearComponent()
中的自定义创建方法将dagger组件指定为null,然后将用户导航到另一个身份验证屏幕。请查看完整代码,如下所示。希望它会帮助一个人!
@OnClick(R.id.img_logout)
void logout() {
AlertDialog.Builder alertDialog = new AlertDialog.Builder(getActivity());
alertDialog
.setMessage("Do you really want to logout?")
.setCancelable(false)
.setPositiveButton("Logout", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialogBox, int id) {
// ToDo get user input here
SharedPrefsUtils.remove(KEY_USERPROFILE, getActivity());
((Application) getActivity().getApplication()).clearComponent();
ActivityUtils.launchActivity(getActivity(), AuthenticationActivity.class, true);
}
})
.setNegativeButton("Cancel",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialogBox, int id) {
dialogBox.cancel();
}
});
AlertDialog alertDialogAndroid = alertDialog.create();
alertDialogAndroid.show();
}
<强> Application.java 强>
public class Application extends Application {
private AppComponent appComponent ;
@Override
public void onCreate() {
super.onCreate();
Realm.init(this);
ButterKnife.setDebug(BuildConfig.DEBUG);
appComponent = createDaggerComponent();
}
public AppComponent getAppComponent() {
return appComponent == null ? createDaggerComponent() : appComponent;
}
public void clearComponent() {
appComponent = null;
}
private AppComponent createDaggerComponent() {
return DaggerAppComponent.builder().appModule(new AppModule(this)).networkModule(new NetworkModule(this)).build();
}
}