Jestem dość nowy w rozwoju Androida i nowszym w Di. Używam Kotlin na osobistym projekcie, w którym eksperymentuję z sztyletem 2. Udało mi się ustawić go na klasę UTIL, ale gdzie muszę mieć kontekst, aby użyć go do wstrzykiwania klasy, która wymaga kontekstu (menedżer SharedPref) Klasa), zawiodłem. Oto mój kod, a oto błąd (NPE), który dostaję. Z góry dziękuję.

Mój klasa modułu

package com.android.pine

import android.content.Context
import com.android.pine.utils.SharedPreferencesManager
import dagger.Module
import dagger.Provides
import javax.inject.Singleton

@Module
class AppModule {

    @Provides
    @Singleton
    fun context(pineApplication: PineApplication): Context = pineApplication.applicationContext

    @Provides
    @Singleton
    fun provideSharedPrefManager(context: Context): SharedPreferencesManager = SharedPreferencesManager(context)
}

Moja klasa komponentów:

package com.android.pine

import com.android.pine.home.HomePresenter
import com.android.pine.home.categories.CategoryAdapter
import dagger.Component
import javax.inject.Singleton

@Singleton
@Component(modules = arrayOf(AppModule::class))
interface AppComponent {
    fun inject(categoryAdapter: CategoryAdapter)
    fun inject(homePresenter: HomePresenter)
}

Edytuj: Dodano poniższe informacje, jak nazywam wstrzyknięciem SharedPreferencesManager:

class HomePresenter : BasePresenter<HomeView>() {

    @Inject
    lateinit var sharedPreferencesManager: SharedPreferencesManager
.
.
.

Również w mojej klasie homePresenter, w oczyszczonej metodzie nadpisują:

 DaggerAppComponent.create().inject(this)

Wygląda na to, że moja klasa Anapplication Class i SharedPrefManager:

class PineApplication @Inject constructor(): Application()

SharedPref:

class SharedPreferencesManager @Inject constructor(context: Context) {
.
.
.

Crash, nie może uzyskać pineapplication.getContext () (edytowany, dodano pełny ślad stosu)

     06-02 11:57:01.028 14840-14840/com.android.pine.debug E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.android.pine.debug, PID: 14840
java.lang.RuntimeException: Unable to resume activity {com.android.pine.debug/com.android.pine.home.HomeActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.Context android.content.Context.getApplicationContext()' on a null object reference
    at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3429)
    at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3469)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2732)
    at android.app.ActivityThread.-wrap12(ActivityThread.java)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1477)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:154)
    at android.app.ActivityThread.main(ActivityThread.java:6119)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
 Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.Context android.content.Context.getApplicationContext()' on a null object reference
    at android.content.ContextWrapper.getApplicationContext(ContextWrapper.java:106)
    at com.android.pine.AppModule.context(AppModule.kt:12)
    at com.android.pine.AppModule_ContextFactory.proxyContext(AppModule_ContextFactory.java:34)
    at com.android.pine.DaggerAppComponent.getContext(DaggerAppComponent.java:29)
    at com.android.pine.DaggerAppComponent.getSharedPreferencesManager(DaggerAppComponent.java:34)
    at com.android.pine.DaggerAppComponent.injectHomePresenter(DaggerAppComponent.java:59)
    at com.android.pine.DaggerAppComponent.inject(DaggerAppComponent.java:49)
    at com.android.pine.home.HomePresenter.onAttached(HomePresenter.kt:31)
    at com.android.pine.home.HomePresenter.onAttached(HomePresenter.kt:10)
    at com.android.pine.core.BaseActivity.onResume(BaseActivity.kt:34)
    at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1269)
    at android.app.Activity.performResume(Activity.java:6783)
    at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3406)
    at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3469) 
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2732) 
    at android.app.ActivityThread.-wrap12(ActivityThread.java) 
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1477) 
    at android.os.Handler.dispatchMessage(Handler.java:102) 
    at android.os.Looper.loop(Looper.java:154) 
    at android.app.ActivityThread.main(ActivityThread.java:6119) 
    at java.lang.reflect.Method.invoke(Native Method) 
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776) 
4
paxcow 2 czerwiec 2018, 13:02

4 odpowiedzi

Najlepsza odpowiedź

Nie możesz użyć class PineApplication @Inject constructor(): Application(), aby utworzyć PineApplication. To klasa ramowa i musi być tworzona przez ramy android.

Zrób więc sztylet stworzy PineApplication, ale applicationContext powróci null, ponieważ nigdy nie został zainicjowany (przez system).

Nie używaj wtrysku konstruktora do klas ram i nie tworzyć samego siebie. Użyj @Bindsintance, aby dodać obiekt do komponentu z jego konstruktorem lub użyj modułu, aby go dostarczyć.

4
David Medenjak 2 czerwiec 2018, 11:10

Tak się dzieje. Korzystanie @bindsInStance w komponencie zostanie wstrzykiwanie aplikacji do wszystkich twoich modułów.

@Singleton
@Component(modules = arrayOf(AppModule::class))
interface AppComponent {
    @Component.Builder
    interface Builder() {
        fun build(): AppComponent

        @BindsInstance
        fun application(application: Application): Builder
    }
}

** Usuń kod do "Zapewnia aplikację" funkcję w module aplikacji i upewnij się, że przekazujesz kontekst aplikacji do tworzenia SharedPreferences.

@Module
class AppModule {


@Provides
@Singleton
fun provideSharedPrefManager(context: Application): SharedPreferencesManager = 
    SharedPreferencesManager(context)
}

A teraz w twoim kreskowym klamry

DaggerAppComponent.builder().application(this).build()

Opcjonalnie: Jeśli chcesz coś wstrzyknąć do klasa aplikacji, wykonaj następujące czynności

 DaggerAppComponent.builder().application(this).build().inject(this)
4
Prakash 16 sierpień 2019, 14:49

W celu zapewnienia kontekstu aplikacji możesz utworzyć klasę, jak ComponentsManager z:

public class ComponentsManager {

    private Context context;
    private AppComponent appComponent;

    public ComponentsManager(Context context) {
        this.context = context.getApplicationContext();
    }

    public AppComponent getAppComponent(){
        if (appComponent == null){
            appComponent = DaggerAppComponent.builder()
                    .appModule(new AppModule(context))
                    .build();
        }
        return appComponent;
    }
}

Oraz w swojej klasie aplikacji init to ComponentsManager w ten sposób:

public class YourApp extends Application {

    private static ComponentsManager componentsManager;

    @Override
    public void onCreate() {
        super.onCreate();
        initComponentsManager();
        initAppComponent();
    }

    public static ComponentsManager getComponentsManager(){
        return componentsManager;
    }

    private void initComponentsManager(){
        componentsManager = new ComponentsManager(this);
    }

    private void initAppComponent(){
        componentsManager.getAppComponent();
    }
}
0
Peter Hall 2 czerwiec 2018, 19:03

Możesz spróbować modyfikować swój moduł aplikacji w ten sposób.

@Module
class ApplicationModule(private val application: Application) {

    @Provides
    @Singleton
    fun provideContext(): Context {
        return application.applicationContext
    }

    @Provides
    @Singleton
    fun provideSharedPreferences(context: Context): SharedPreferences {
        return context.getSharedPreferences(PREF_FILE_NAME, Context.MODE_PRIVATE)
    }
}

Następnie możesz zbudować taki komponent sztylet z klasy aplikacji.

val appComponent = DaggerAppComponent.builder()
                .applicationModule(ApplicationModule(this))
                .build()

Wstrzyknąć wartości prezentera w ten sposób.

application.appComponent.inject(this)
0
Kalyan Dechiraju 5 czerwiec 2018, 18:51