A scope tree based Dependency Injection (DI) library for Java / Kotlin / Android.

Overview

Toothpick (a.k.a T.P. like a teepee)







Visit TP wiki !

What is Toothpick ?

Toothpick is a scope tree based Dependency Injection (DI) library for Java.

It is a full-featured, runtime based, but reflection free, implementation of JSR 330.

What does Toothpick offer ?

//a typical Toothpick scope tree during the execution of an Android app.

           @ApplicationScope 
             /          |    \  
            /           |     \
           /            |      \
   @ViewModelScope      |   Service 2
         /              | 
        /            Service 1  
       /            
 @Activity1Scope
      /
     /
Activity 1
   /   \
  /   Fragment 2
 /
Fragment 1

Scopes offer to compartmentalize memory during the runtime of an app and prevent memory leaks. All dependencies created via Toothpick, and available for injections, will be fully garbage collected when this scope is closed. To learn more about scopes, read TP wiki.

Toothpick is :

Examples

This is the example:

Setup

The latest version of TP is provided by a badge at the top of this page.

For Android :

#android setup using gradle 5.5.1
buildscript {
  repositories {
    google()
    jcenter()
  }
  dependencies {
    classpath 'com.android.tools.build:gradle:3.4.x'
  }
}

...
#for java
dependencies {
  implementation 'com.github.stephanenicolas.toothpick:toothpick-runtime:3.x'
  // and for android -> implementation 'com.github.stephanenicolas.toothpick:smoothie-androidx:3.x'
  annotationProcessor 'com.github.stephanenicolas.toothpick:toothpick-compiler:3.x'

  //highly recommended
  testImplementation 'com.github.stephanenicolas.toothpick:toothpick-testing-junit5:3.x'
  testImplementation 'mockito or easymock'
}

#for kotlin
dependencies {
  implementation 'com.github.stephanenicolas.toothpick:ktp:3.x'
  kapt 'com.github.stephanenicolas.toothpick:toothpick-compiler:3.x'

  //highly recommended
  testImplementation 'com.github.stephanenicolas.toothpick:toothpick-testing-junit5:3.x'
  testImplementation 'mockito or easymock'
}

For java:

<!--java setup with maven -->
  <dependencies>
    <dependency>
      <groupId>com.github.stephanenicolas.toothpick</groupId>
      <artifactId>toothpick-compiler</artifactId>
      <version>3.x</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>com.github.stephanenicolas.toothpick</groupId>
      <artifactId>toothpick-runtime</artifactId>
      <version>3.x</version>
      <scope>compile</scope>
    </dependency>
    
    <!-- highly recommended-->
    <dependency> 
      <groupId>com.github.stephanenicolas.toothpick</groupId>
      <artifactId>toothpick-testing</artifactId>
      <version>3.x</version>
      <scope>test</scope>
    </dependency>
    <dependency>
    <easymock or mockito>
    </dependency>
  </dependencies>

Support

TP is actively maintained and we provide support to questions via the Toothpick-di tag on Stack Over Flow.

Ask questions on Stack Over Flow while keeping the GitHub issue board for real issues. Thx in advance !

Talks & Articles

Wanna know more ?

Visit Toothpick's wiki !

Alternative Dependency Injection (DI) engines for Android

Libs / Apps using TP 2

  • Okuki is a simple, hierarchical navigation bus and back stack for Android, with optional Rx bindings, and Toothpick DI integration.
  • KotlinWeather is a simple example of using ToothPick with Kotlin and gradle integration using kapt.

Credits

TP 1 & 3 have been developped by Stephane Nicolas and Daniel Molinero Reguera. Most of the effort on version 2 has been actively supported by Groupon. Thanks for this awesome OSS commitment !

Comments
  • Generate registries in a way that works with obfuscation

    Generate registries in a way that works with obfuscation

    With these changes it's possible to use obfuscation with Toothpick provided the following:

    1. -adaptclassstrings added to proguard rules
    2. support_obfuscation : "true" added to annotation processing arguments.
    3. Configuration with registries is used.

    The issue is described here: https://github.com/stephanenicolas/toothpick/issues/146

    Switching on class name strings doesn't work after -adaptclassstrings is applied because the hashcodes of strings are calculated and written into bytecode before the obfuscation. I replaced switch with a HashMap. The changes take effect only if support_obfuscation : "true" was set.

    RTM 
    opened by pshmakov 36
  • Reflection Free Configuration and Proguard

    Reflection Free Configuration and Proguard

    Is there any guidance on how to use the reflection free configuration with proguard and obfuscation? Based on looking at the factories, it looks like I'll need to keep my class names at a minimum (possibly method names too).

    -keepnames class com.myapp.**

    Do you have any advice on how I can make this more specific?

    question wontfix 
    opened by philipbjorge 20
  • Kotlin Sample

    Kotlin Sample

    I've tried using this project on a kotlin Android application but could not make it work with the reflection free configuration. I tried:

    kapt {
        generateStubs = true
    
        arguments {
            arg("toothpick_registry_package_name", "myproject.package")
            arg("toothpick_registry_children_package_names", "toothpick.smoothie")
        }
    }
    

    and

    dependencies {
        // ...
        kapt 'com.github.stephanenicolas.toothpick:toothpick-compiler:1.0.6'
        compile "com.github.stephanenicolas.toothpick:toothpick-runtime:1.0.6"
        compile "com.github.stephanenicolas.toothpick:smoothie:1.0.6"
    }
    

    But the processor is not generating classes for the reflection free configuration.

    opened by cs-victor-nascimento 15
  • superMemberInjector generated with missing type information

    superMemberInjector generated with missing type information

    In Toothpick 3.0.0 there's an issue where the generated __MemberInjector has an incorrect superMemberInjector field i.e. it does not set the type argument correctly.

    E.g. assume we have AddNewActivity with @Inject annotated fields. AddNewActivity extends BaseActivity which has a type parameter e.g.

    
    public class AddNewActivity extends BaseActivity<Integer> {
     // ...
    }
    

    where BaseActivity is:

    abstract class BaseActivity<T> : SomeInterfaceWithGeneric<T>, AppCompatActivity() {
    
        @Inject
        lateinit var dummyDependency: DummyDependency
    }
    

    When you try to build the project KAPT will fail with the following reason:

    > Task :kaptDebugKotlin FAILED
    e: /Users/me/workspace/toothpick/toothpick-sample/build/generated/source/kapt/debug/com/example/toothpick/activity/AddNewActivity__MemberInjector.java:9: error: cannot find symbol
      private MemberInjector<BaseActivity<T>> superMemberInjector = new com.example.toothpick.activity.BaseActivity__MemberInjector();
                                          ^
      symbol:   class T
      location: class AddNewActivity__MemberInjector
    e: /Users/me/workspace/toothpick/toothpick-sample/build/generated/source/kapt/debug/com/example/toothpick/activity/AdvancedBackpackItemsActivity__MemberInjector.java:9: error: cannot find symbol
      private MemberInjector<BaseActivity<T>> superMemberInjector = new com.example.toothpick.activity.BaseActivity__MemberInjector();
    

    Generated AddNewActivity__MemberInjector

    public final class AddNewActivity__MemberInjector implements MemberInjector<AddNewActivity> {
      private MemberInjector<BaseActivity<T>> superMemberInjector = new com.example.toothpick.activity.BaseActivity__MemberInjector();
    
      @Override
      public void inject(AddNewActivity target, Scope scope) {
        superMemberInjector.inject(target, scope);
        target.backpackItemValidator = scope.getInstance(BackpackItemValidator.class);
      }
    }
    

    I've added an example in toothpick-sample app to reproduce this: https://github.com/stephanenicolas/toothpick/compare/master...zawadz88:super-member-injector-bug

    This happens if AddNewActivity is in either Java or Kotlin and has @Inject annotated fields/properties. The issue does not occur in Kotlin when using the newly introduced inject delegates (thanks for introducing these!).

    opened by zawadz88 14
  •  toothpick.locators.NoFactoryFoundException in Signed APK [2.1.0]

    toothpick.locators.NoFactoryFoundException in Signed APK [2.1.0]

    Hi Together

    I got an issue when building the apk and trying to run it.

    I know others had such problems as well but none of those issues sadly seem to apply to me. :/

    I am creating an Android Library and so far everything worked fine when running it through Android Studio in a demo app. However once I create the signed APK some things seem to go a bit awry. Suddenly Toothpick complains that it cannot locate certain Member Injectors. Even though I am binding & injecting the instances myself.

    Am I missing something during the build phase which I need to change?

    Information

    I am sry I know its a lot. But I think something in here is contributing to the issue but I cant figure out what it is.

    Here I am binding all required instances to the Scope FeedbackSingleton.class

    @SuppressWarnings("Injectable")
    public class FeedbackSetupService implements IFeedbackExitPoint {
        ...
        @Inject
        IFeedbackExitPoint feedbackExitPoint;
        ...
        public void open(final Activity fromActivity) {
    
            Scope feedbackScope = Toothpick.openScope(FeedbackSingleton.class);
    
            feedbackScope.installModules(new Module() {{
                bind(Application.class).toInstance(fromActivity.getApplication());
                bind(NetworkService.class).toInstance(new NetworkService());
            }});
    
            feedbackScope.installModules(new Module() {{
                bind(FeedbackRoomDatabase.class).toProviderInstance(new FeedbackRoomDatabaseProvider());
                final AssetsServiceProvider assetsServiceProviderInstance = new AssetsServiceProvider();
                bind(AssetsServiceProvider.class).toInstance(assetsServiceProviderInstance);
                bind(AssetsService.class).toProviderInstance(assetsServiceProviderInstance);
                bind(FileService.class).toProviderInstance(new FileServiceProvider());
            }});
    
           // Here I changed the way I create the module to see if that changes things but it doesnt
            Module tpModStage3 = new Module();
            tpModStage3.bind(FeedbackRepository.class).toInstance(new FeedbackRepository());
            tpModStage3.bind(FeedbackConfigurationService.class).toInstance(new FeedbackConfigurationService());
            tpModStage3.bind(IFeedbackExitPoint.class).toInstance(this);
            feedbackScope.installModules(tpModStage3);
    
           // Interestingly enough TP does not complain here about missing Injector for the same class
           // That is `IFeedbackExitPoint.java`
            Toothpick.inject(this, feedbackScope);
        }
       ...
    }
    

    This activity fails to inject suddenly in the signed APK.

    public class TextSideActivity extends BaseActivity {
        ...
        // This one fails to inject
        @Inject
        IFeedbackExitPoint feedbackExitPoint;
        ...
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            requestWindowFeatureCompat(Window.FEATURE_NO_TITLE);
            contentView = getLayoutInflater().inflate(R.layout.textside_activity, null);
            setContentView(contentView); // Cannot be done by ButterKnife - only calls which can be done by findViewById()
    
            Toothpick.inject(this, Toothpick.openScope(FeedbackSingleton.class));
            ButterKnife.bind(this);
            init();
            initValueListeners();
        }
        ...
    }
    

    Information about IFeedbackExitPoint

    IFeedbackExitPoint#close takes care of closing the TP scope as well as making sure to tear down any other ressources used.

    public interface IFeedbackExitPoint {
        void close();
    }
    

    This is the stacktrace which I am suddenly getting in the singed APK.

    2019-08-07 12:03:07.338 17155-17155/? E/AndroidRuntime: FATAL EXCEPTION: main
        Process: ch.fhnw.edu.fcp.feedbackcommprocess, PID: 17155
        java.lang.RuntimeException: Unable to start activity ComponentInfo{ch.fhnw.edu.fcp.feedbackcommprocess/ch.fhnw.edu.fcp.postcard.activity.TextSideActivity}: toothpick.locators.NoFactoryFoundException: No factory could be found for class ch.fhnw.edu.fcp.postcard.IFeedbackExitPoint. Check that the class has either a @Inject annotated constructor or contains @Inject annotated members.
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2919)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3054)
            at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
            at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
            at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1814)
            at android.os.Handler.dispatchMessage(Handler.java:106)
            at android.os.Looper.loop(Looper.java:280)
            at android.app.ActivityThread.main(ActivityThread.java:6710)
            at java.lang.reflect.Method.invoke(Native Method)
            at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
         Caused by: toothpick.locators.NoFactoryFoundException: No factory could be found for class ch.fhnw.edu.fcp.postcard.IFeedbackExitPoint. Check that the class has either a @Inject annotated constructor or contains @Inject annotated members.
            at toothpick.locators.FactoryLocator.getFactory(FactoryLocator.java:21)
            at toothpick.ScopeImpl.lookupProvider(ScopeImpl.java:329)
            at toothpick.ScopeImpl.getInstance(ScopeImpl.java:58)
            at toothpick.ScopeImpl.getInstance(ScopeImpl.java:49)
            at ch.fhnw.edu.fcp.postcard.activity.TextSideActivity__MemberInjector.inject(TextSideActivity__MemberInjector.java:12)
            at ch.fhnw.edu.fcp.postcard.activity.TextSideActivity__MemberInjector.inject(TextSideActivity__MemberInjector.java:9)
            at toothpick.InjectorImpl.inject(InjectorImpl.java:23)
            at toothpick.Toothpick.inject(Toothpick.java:163)
            at ch.fhnw.edu.fcp.postcard.activity.TextSideActivity.onCreate(TextSideActivity.java:141)
            at android.app.Activity.performCreate(Activity.java:7136)
            at android.app.Activity.performCreate(Activity.java:7127)
            at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1272)
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2899)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3054) 
            at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78) 
            at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108) 
            at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68) 
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1814) 
            at android.os.Handler.dispatchMessage(Handler.java:106) 
            at android.os.Looper.loop(Looper.java:280) 
            at android.app.ActivityThread.main(ActivityThread.java:6710) 
            at java.lang.reflect.Method.invoke(Native Method) 
            at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) 
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858) 
         Caused by: java.lang.ClassNotFoundException: ch.fhnw.edu.fcp.postcard.IFeedbackExitPoint__Factory
            at java.lang.Class.classForName(Native Method)
            at java.lang.Class.forName(Class.java:453)
            at java.lang.Class.forName(Class.java:378)
            at toothpick.locators.FactoryLocator.getFactory(FactoryLocator.java:18)
            at toothpick.ScopeImpl.lookupProvider(ScopeImpl.java:329) 
            at toothpick.ScopeImpl.getInstance(ScopeImpl.java:58) 
            at toothpick.ScopeImpl.getInstance(ScopeImpl.java:49) 
            at ch.fhnw.edu.fcp.postcard.activity.TextSideActivity__MemberInjector.inject(TextSideActivity__MemberInjector.java:12) 
            at ch.fhnw.edu.fcp.postcard.activity.TextSideActivity__MemberInjector.inject(TextSideActivity__MemberInjector.java:9) 
            at toothpick.InjectorImpl.inject(InjectorImpl.java:23) 
            at toothpick.Toothpick.inject(Toothpick.java:163) 
            at ch.fhnw.edu.fcp.postcard.activity.TextSideActivity.onCreate(TextSideActivity.java:141) 
            at android.app.Activity.performCreate(Activity.java:7136) 
            at android.app.Activity.performCreate(Activity.java:7127) 
            at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1272) 
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2899) 
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3054) 
            at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78) 
            at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108) 
            at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68) 
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1814) 
            at android.os.Handler.dispatchMessage(Handler.java:106) 
            at android.os.Looper.loop(Looper.java:280) 
            at android.app.ActivityThread.main(ActivityThread.java:6710) 
            at java.lang.reflect.Method.invoke(Native Method) 
            at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) 
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858) 
         Caused by: java.lang.ClassNotFoundException: Didn't find class "ch.fhnw.edu.fcp.postcard.IFeedbackExitPoint__Factory" on path: DexPathList[[zip file "/mnt/expand/0c815ad2-6acf-42db-ab64-6125c2e82827/app/ch.fhnw.edu.fcp.feedbackcommprocess-wjud-Zz1fbQxV8atrA5G5Q==/base.apk"],nativeLibraryDirectories=[/mnt/expand/0c815ad2-6acf-42db-ab64-6125c2e82827/app/ch.fhnw.edu.fcp.feedbackcommprocess-wjud-Zz1fbQxV8atrA5G5Q==/lib/arm64, /system/lib64, /vendor/lib64]]
            at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:134)
            at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
            at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
            at java.lang.Class.classForName(Native Method) 
            at java.lang.Class.forName(Class.java:453) 
            at java.lang.Class.forName(Class.java:378) 
            at toothpick.locators.FactoryLocator.getFactory(FactoryLocator.java:18) 
            at toothpick.ScopeImpl.lookupProvider(ScopeImpl.java:329) 
            at toothpick.ScopeImpl.getInstance(ScopeImpl.java:58) 
            at toothpick.ScopeImpl.getInstance(ScopeImpl.java:49) 
            at ch.fhnw.edu.fcp.postcard.activity.TextSideActivity__MemberInjector.inject(TextSideActivity__MemberInjector.java:12) 
            at ch.fhnw.edu.fcp.postcard.activity.TextSideActivity__MemberInjector.inject(TextSideActivity__MemberInjector.java:9) 
            at toothpick.InjectorImpl.inject(InjectorImpl.java:23) 
            at toothpick.Toothpick.inject(Toothpick.java:163) 
            at ch.fhnw.edu.fcp.postcard.activity.TextSideActivity.onCreate(TextSideActivity.java:141) 
            at android.app.Activity.performCreate(Activity.java:7136) 
            at android.app.Activity.performCreate(Activity.java:7127) 
            at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1272) 
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2899) 
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3054) 
            at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78) 
            at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108) 
            at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68) 
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1814) 
            at android.os.Handler.dispatchMessage(Handler.java:106) 
            at android.os.Looper.loop(Looper.java:280) 
            at android.app.ActivityThread.main(ActivityThread.java:6710) 
            at java.lang.reflect.Method.invoke(Native Method) 
            at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) 
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858) 
    

    Using gradle directly lint complains as well

    .gradle/caches/modules-2/files-2.1/com.github.stephanenicolas.toothpick/toothpick-runtime/2.1.0/e4456493ef995a4e7c4a541a64106a5b4c78a685/toothpick-runtime-2.1.0.jar: Error: Invalid package reference in library; not included in Android: javax.inject. Referenced from toothpick.ScopeNode. [InvalidPackage]
    

    App Gradle (Demo App)

    apply plugin: 'com.android.application'
    
    android {
        android {
            lintOptions {
                // abortOnError false
                // warning 'InvalidPackage'
            }
        }
        signingConfigs {
            release {
                storeFile file('/home/lukaswillin/Clouds/OwnCloud/SwitchDrive/IP6/Sonstiges/apk-signing-keystore.jks')
                storePassword '**********'
                keyAlias = 'fhnwkey'
                keyPassword '***********'
            }
        }
        compileSdkVersion 28
        defaultConfig {
            applicationId "ch.fhnw.edu.fcp.feedbackcommprocess"
            minSdkVersion 14
            targetSdkVersion 28
            versionCode 1
            versionName "1.0"
            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
            vectorDrawables.useSupportLibrary = true
            signingConfig signingConfigs.release
        }
        buildTypes {
            release {
                minifyEnabled false
                useProguard false
                debuggable = true
                signingConfig signingConfigs.release
                multiDexEnabled = true
            }
            debug {
                signingConfig signingConfigs.debug
            }
        }
        compileOptions {
            sourceCompatibility JavaVersion.VERSION_1_8
            targetCompatibility JavaVersion.VERSION_1_8
        }
    }
    
    dependencies {
        implementation project(':feedbackpostcard')
        implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
    
        implementation 'androidx.appcompat:appcompat:1.0.2'
        implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
        implementation 'com.google.android.material:material:1.0.0'
        testImplementation 'junit:junit:4.12'
        androidTestImplementation 'androidx.test:runner:1.2.0'
        androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    
        // View Injection / Butterknife
        // Known Warning variantOutput.getProcessResources() is obsolete caused by the library version of butterknife
        apply plugin: 'com.jakewharton.butterknife'
    
        implementation 'com.jakewharton:butterknife:10.1.0'
        annotationProcessor 'com.jakewharton:butterknife-compiler:10.1.0'
    
        // Room components
        // Alpha 4 has been chosen since Alpha 5 would break the build
        implementation "androidx.room:room-runtime:2.1.0-alpha04"
        annotationProcessor "androidx.room:room-compiler:2.1.0-alpha04"
        androidTestImplementation "androidx.room:room-testing:2.1.0-alpha04"
    
        // Lifecycle components
        implementation "androidx.lifecycle:lifecycle-extensions:2.0.0"
        annotationProcessor "androidx.lifecycle:lifecycle-compiler:2.0.0"
    
        // Joda DateTime Library
        implementation 'joda-time:joda-time:2.10.2'
    
        // Toothpick DI
        implementation 'com.github.stephanenicolas.toothpick:toothpick-runtime:2.1.0'
        annotationProcessor 'com.github.stephanenicolas.toothpick:toothpick-compiler:2.1.0'
        testImplementation 'com.github.stephanenicolas.toothpick:toothpick-testing-junit5:2.1.0'
        testImplementation 'org.mockito:mockito-core:2.28.2'
    
        // CompletableFuture Backport
        implementation 'net.sourceforge.streamsupport:android-retrofuture:1.7.1'
    }
    

    Library Module Gradle (Feedback)

    apply plugin: 'com.android.library'
    
    android {
        android {
            lintOptions {
                // abortOnError false
                // warning 'InvalidPackage'
            }
        }
        signingConfigs {
            release {
                storeFile file('/home/lukaswillin/Clouds/OwnCloud/SwitchDrive/IP6/Sonstiges/apk-signing-keystore.jks')
                storePassword '*************'
                keyAlias = 'fhnwkey'
                keyPassword '*************
            }
        }
        compileSdkVersion 28
    
        defaultConfig {
            minSdkVersion 14
            targetSdkVersion 28
            versionCode 1
            versionName "1.0"
    
            testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    
            javaCompileOptions {
                annotationProcessorOptions {
                    arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
                }
            }
            vectorDrawables.useSupportLibrary = true
        }
    
        buildTypes {
            release {
                useProguard false
                minifyEnabled false
                debuggable = true
                signingConfig signingConfigs.release
            }
            debug {
                signingConfig signingConfigs.debug
                multiDexEnabled = true
            }
        }
    
        compileOptions {
            sourceCompatibility JavaVersion.VERSION_1_8
            targetCompatibility JavaVersion.VERSION_1_8
        }
    
        dataBinding {
            enabled = true
        }
    
    }
    
    dependencies {
        implementation fileTree(dir: 'libs', include: ['*.jar'])
    
        implementation 'androidx.appcompat:appcompat:1.0.2'
        implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
        implementation 'com.google.android.material:material:1.0.0'
        implementation 'androidx.vectordrawable:vectordrawable:1.0.1'
        implementation 'androidx.legacy:legacy-support-v4:1.0.0'
        testImplementation 'junit:junit:4.12'
        androidTestImplementation 'androidx.test:runner:1.2.0'
        androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    
        // View Injection / Butterknife
        // Known Warning variantOutput.getProcessResources() is obsolete caused by the library version of butterknife
        apply plugin: 'com.android.library'
        apply plugin: 'com.jakewharton.butterknife'
    
        implementation 'com.jakewharton:butterknife:10.1.0'
        annotationProcessor 'com.jakewharton:butterknife-compiler:10.1.0'
    
        // Room components
        implementation "androidx.room:room-runtime:2.1.0-alpha04"
        annotationProcessor "androidx.room:room-compiler:2.1.0-alpha04"
        androidTestImplementation "androidx.room:room-testing:2.1.0-alpha04"
    
        // Lifecycle components
        implementation "androidx.lifecycle:lifecycle-extensions:2.0.0"
        annotationProcessor "androidx.lifecycle:lifecycle-compiler:2.0.0"
    
        // Joda DateTime Library
        implementation 'joda-time:joda-time:2.10.2'
    
        // Toothpick DI
        implementation 'com.github.stephanenicolas.toothpick:toothpick-runtime:2.1.0'
        annotationProcessor 'com.github.stephanenicolas.toothpick:toothpick-compiler:2.1.0'
        testImplementation 'com.github.stephanenicolas.toothpick:toothpick-testing-junit5:2.1.0'
        testImplementation 'org.mockito:mockito-core:2.+'
    
        // CompletableFuture Backport
        implementation 'net.sourceforge.streamsupport:android-retrofuture:1.7.1'
    
        // Phototeditor
        implementation 'ja.burhanrashid52:photoeditor:0.4.0'
    }
    
    opened by LukasWillin 14
  • KTP draft specs

    KTP draft specs

    Kotlin Toothpick draft specs - WIP

    Use delegate properties for field injection

    We should not use annotations for field injection, instead we will use delegate properties. For example:

    by ktp.inject() // default scope
    by ktp.lazy() // lazy
    by ktp.provider() // provider
    

    Moreover, there should be a way to:

    • Specify when to use the default scope and when to wait for a scope to be provided.
    • Use a named injection.

    Kotlin friendly API for

    • Module and bindings definition (https://github.com/sporttotal-tv/toothpick-kotlin-extensions and https://github.com/stephanenicolas/toothpick/issues/305)
    • Scope creation and "scope binding".
    • Configuration of injectable classes
      • Should we use the @Inject annotation? and where?
      • How to define the constructor to use
      • How to define a singleton
      • How to define in what scope an instance will be created

    Generate code compatible with optional parameters (possible?)

    WIP

    Moreover, KTP will be based on TP 2.0 -> no registries (no configuration needed), incremental annotation processing, better scope annotations, ... (https://github.com/stephanenicolas/toothpick/issues/315, https://github.com/stephanenicolas/toothpick/issues/225)

    Something you miss? Any suggestion? Please comment 🙂

    opened by dlemures 14
  • Espresso and Toothpick

    Espresso and Toothpick

    Hi,

    I am getting the following error when i have more than 1 test in my espresso test class. java.lang.IllegalStateException: TestModules can only be installed once per scope.

    My test rule is as follows;

    I noticed it didn't matter if i reset or closed the scope i still got this error. I looked into your code and could the problem be due to a flag not being reset?

    ActivityTestRule testRule = new ActivityTestRule(ExampleMainActivity.class, false) {

        @Override
        protected void beforeActivityLaunched() {
            super.beforeActivityLaunched();
            Context appContext = InstrumentationRegistry.getTargetContext().getApplicationContext();
            scope = Toothpick.openScope(appContext);
            scope.installTestModules(new ExampleMainActivityTest.TestModule());
        }
    
        @Override
        protected void afterActivityFinished() {
            super.afterActivityFinished();
            Toothpick.reset();
        }
    };
    

    It all seems to work when i have just one test. Thanks for looking into it.

    opened by markchristopherng 14
  • java.lang.NoSuchMethodError on Toothpick.reset() on Instrumentation Tests

    java.lang.NoSuchMethodError on Toothpick.reset() on Instrumentation Tests

    Hi,

    I had an issue while running a couple of instrumentation tests on Android with Toothpick 1.0.3. The issue is the following and doesn't appear to happen with v1.0.2:

    java.lang.NoSuchMethodError: No virtual method keySet()Ljava/util/concurrent/ConcurrentHashMap$KeySetView; in class Ljava/util/concurrent/ConcurrentH
    ashMap; or its super classes (declaration of 'java.util.concurrent.ConcurrentHashMap' appears in /system/framework/core-libart.jar)
            at toothpick.Toothpick.reset(Toothpick.java:125)...
    

    This always happens when the tear down method calls the Toothpick.reset() method.

    bug has PR 
    opened by kelsos 14
  • Sni/lock free

    Sni/lock free

    DO NOT MERGE

    Besides Daniel, I would love Michael & Henri to review this. The goal of this PR is to

    • make TP crash free in a multi threading context. It appears that we have been making a few mistakes on this since the beginning.
    • pass tests that heavily use the data structure (scope tree) concurrently
    • make TP lock free. Each injection will see a snapshot of TP scope tree, before or after any modification but not in between. This makes the most normal usages of TP much faster.

    This version provides an improvement for massive DI :

    image

    For 55 K injections.

    enhancement 
    opened by stephanenicolas 14
  • Using toothpick in libraries

    Using toothpick in libraries

    I'm trying to use toothpick in a library project (no toothpick classes/objects exposed to the clients of the library). But I faced a problem that if a client of the library uses preventMultipleRootScopes configuration it will fail because the library opens new root scope. Also, we can't set isolated toothpick configuration in the library and in the client project because ConfigurationHolder.configuration field is static. How can I use multiple toothpicks with different configuration at the same time? Is it intended to use tootpick in this way?

    invalid question 
    opened by lukaville 13
  • Unit tests with Mockito

    Unit tests with Mockito

    Hi, I'm trying to do some unit testing with Toothpick and Mockito, but I have a null pointer exception, when I try to inject the class under test (ContactsPresenter mContactsPresenter).

    import org.junit.Before;
    import org.junit.Rule;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.mockito.Mock;
    import org.mockito.MockitoAnnotations;
    import org.mockito.junit.MockitoJUnitRunner;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import javax.inject.Inject;
    
    import io.reactivex.Single;
    import toothpick.testing.ToothPickRule;
    
    import static org.mockito.Mockito.verify;
    import static org.mockito.Mockito.when;
    
    
    @RunWith(MockitoJUnitRunner.class)
    public class ContactsPresenterTest {
        @Rule public ToothPickRule toothPickRule = new ToothPickRule(this);
    
        @Mock ContactsView mContactsView;
    
        @Mock UserModel mUserModel;
        @Mock ContactsDataSource mContactsDataSource;
        @Inject ContactsPresenter mContactsPresenter;
    
        @Before
        public void setUp() throws Exception {
            MockitoAnnotations.initMocks(this);
            when(mUserModel.fetchCountries("")).thenReturn(Single.just((List<Country>) new ArrayList<Country>(0)));
            toothPickRule.inject(this);
            mContactsPresenter.fetchUsers();
        }
    
        @Test
        public void testUserCalled() {
            verify(mContactsView).onUsersListFetching();
        }
    
    }
    

    I have these dependencies :

        compile 'com.github.stephanenicolas.toothpick:toothpick-runtime:1.0.6'
        testCompile 'com.github.stephanenicolas.toothpick:toothpick-testing:1.0.6'
        annotationProcessor 'com.github.stephanenicolas.toothpick:toothpick-compiler:1.0.6'
        testCompile "org.mockito:mockito-core:2.8.9"
    

    This is the generated factory :

    import java.lang.Override;
    import toothpick.Factory;
    import toothpick.MemberInjector;
    import toothpick.Scope;
    
    public final class ContactsPresenter$$Factory implements Factory<ContactsPresenter> {
      private MemberInjector<ContactsPresenter> memberInjector = new ....ContactsPresenter$$MemberInjector();
    
      @Override
      public ContactsPresenter createInstance(Scope scope) {
        scope = getTargetScope(scope);
        ContactsPresenter contactsPresenter = new ContactsPresenter();
        memberInjector.inject(contactsPresenter, scope);
        return contactsPresenter;
      }
    
      @Override
      public Scope getTargetScope(Scope scope) {
        return scope;
      }
    
      @Override
      public boolean hasScopeAnnotation() {
        return false;
      }
    
      @Override
      public boolean hasProvidesSingletonInScopeAnnotation() {
        return false;
      }
    }
    

    java.lang.NullPointerException at ...ContactsPresenterTest.setUp(ContactsPresenterTest.java:47) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24) at toothpick.testing.ToothPickStatement.evaluate(ToothPickStatement.java:17) at org.junit.rules.RunRules.evaluate(RunRules.java:20) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.mockito.internal.runners.DefaultInternalRunner$1.run(DefaultInternalRunner.java:78) at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:84) at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:39) at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:161) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:117) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:262) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:84) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)

    opened by fabiogrum 12
  • Migration guide

    Migration guide

    Is there any migration guide to migrate from this version:

    implementation 'com.github.stephanenicolas.toothpick:ktp:1.1.2'
    kapt 'com.github.stephanenicolas.toothpick:toothpick-compiler:1.1.2'
    

    Almost all classes not exist anymore.

    opened by pavelpoley 0
  • Test Smell: It is not a good test practice to use the random number generator in test code

    Test Smell: It is not a good test practice to use the random number generator in test code

    Hi! We notice that the random number generator (RNG) is used to produce test code in your project. For example, a random generator in the test method named ''concurrentScopeAdditionsAndRemovals_shouldNotCrash()'' in ''ScopeTreeManipulationsMultiThreadTest.java''.

    But generating random numbers in test code is not a good test practice. Because 1. using a random number generator in test code makes the test non-deterministic since it is hard to know which random number causes when the test case fails. 2. Random number generation can create couplings between classes and timing artifacts because most random number generator classes are thread-safe and therefore introduce additional synchronization. So, a potential problem is that a test that should fail due to incorrect synchronization in the class under test might pass because of synchronization in the RNG used in the test code. 3. cost too many resources.

    In your code, you use the random number in the if-condition, but why not just test the edge cases instead of using a random number generator? You can test your code under input value = 49, 50, 51.

    if (random.nextInt(100) < 50) { runnable = new RemoveNodeThread(ROOT_SCOPE); } else { runnable = new AddNodeThread(ROOT_SCOPE); }

    opened by TestSmell 0
  • How to cache a singleton instance in a subscope of a binding?

    How to cache a singleton instance in a subscope of a binding?

    Suppose I am building a Web server. I tried to organize my scopes as such:

    AppScope -> SessionScope -> RequestScope -> MyScope

    How would I go about binding singleton instances to last scope and not to the scope where they were installed? My motivation for such approach is performance. Suppose there are a lot of bindings in RequestScope that need to be installed (say thousands), but very few of them are ever used. It would be very expensive to install those bindings on every request when they are not needed. If I could just install them once but instantiate them in subscope that would be great!

    So I'd like to resolve those few instances and cache them during request in MyScope. However, current behavior is that RequestScope caches them and I am stuck. I tried playing with supportScopeAnnotations without luck.

    opened by macabrus 0
  • Fix tests for Windows developers

    Fix tests for Windows developers

    When executing ./gradlew check on Windows in fails due to end-of-line characters.

    The issue is described here: #431

    In this PR I propose to use \n to be platform-agnostic, as it also work on Windows.

    opened by olivierperez 0
  • Unit Tests fail on Windows

    Unit Tests fail on Windows

    Hello, I was going to try to contribute but the Unit Tests fail because of line separator.

    Unix line separator is \n Window line separator is \r\n

    For instance CyclicDependencyException uses System.getProperty("line.separator") or %n to generate error messages, but CyclicDependencyExceptionTest check result with \n.

    If you're OK, I can do the fix myself and open a PR.

    I think I may fix it by using \n everywhere because Windows understand it perfectly. So the code won't be platform-dependent.

    opened by olivierperez 0
Owner
Stéphane Nicolas
Open source enthusiast, Java & Linux fan, Android dev. Favorite topics: quality, performance, testability, dependency injection, continuous integration, gradle.
Stéphane Nicolas
Lightweight, minimalistic dependency injection library for Kotlin & Android

‼️ This project is in maintenance mode and not actively developed anymore. For more information read this statement. ‼️ Katana Katana is a lightweight

REWE Digital GmbH 179 Nov 27, 2022
Painless Kotlin Dependency Injection

KOtlin DEpendency INjection Kodein-DI is a very simple and yet very useful dependency retrieval container. it is very easy to use and configure. Kodei

null 2.9k Jan 1, 2023
Koin - a pragmatic lightweight dependency injection framework for Kotlin

What is KOIN? - https://insert-koin.io A pragmatic lightweight dependency injection framework for Kotlin developers. Koin is a DSL, a light container

insert-koin.io 7.8k Jan 8, 2023
:syringe: Transfuse - A Dependency Injection and Integration framework for Google Android

Transfuse Transfuse is a Java Dependency Injection (DI) and integration library geared specifically for the Google Android API. There are several key

John Ericksen 224 Nov 28, 2022
The dependency injection Dagger basics and the concepts are demonstrated in different branches

In this project we will look at the dependency injection Dagger basics and the concepts are demonstrated in different branches What is an Dependency?

Lloyd Dcosta 6 Dec 16, 2021
A fast dependency injector for Android and Java.

Dagger A fast dependency injector for Java and Android. Dagger is a compile-time framework for dependency injection. It uses no reflection or runtime

Google 16.9k Jan 5, 2023
A fast dependency injector for Android and Java.

Dagger 1 A fast dependency injector for Android and Java. Deprecated – Please upgrade to Dagger 2 Square's Dagger 1.x is deprecated in favor of Google

Square 7.3k Jan 5, 2023
A multi-purpose library containing view injection and threading for Android using annotations

SwissKnife A multi-purpose Groovy library containing view injection and threading for Android using annotations. It's based on both ButterKnife and An

Jorge Martin Espinosa 251 Nov 25, 2022
A SharedPreference "injection" library for Android

PreferenceBinder A SharedPreferences binding library for Android. Using annotation processing, this library makes it easy to load SharedPreferences va

Denley Bihari 232 Dec 30, 2022
DependencyProperty is a dependency resolution library by Delegated Property.

DependencyProperty is a dependency resolution library by Delegated Property. Overview DependencyProperty is simple in defining and

wada811 10 Dec 31, 2022
Simple Android Library, that provides easy way to start the Activities with arguments.

Warning: Library is not maintained anymore. If you want to take care of this library, propose it via Pull Request. It needs adjustmensts for newer ver

Marcin Moskała 429 Dec 15, 2022
Fast Android Development. Easy maintainance.

Fast Android Development. Easy maintenance. AndroidAnnotations is an Open Source framework that speeds up Android development. It takes care of the pl

null 11.1k Dec 31, 2022
Fast Android Development. Easy maintainance.

Fast Android Development. Easy maintenance. AndroidAnnotations is an Open Source framework that speeds up Android development. It takes care of the pl

null 11.1k Dec 31, 2022
Bind Android views and callbacks to fields and methods.

Butter Knife Attention: This tool is now deprecated. Please switch to view binding. Existing versions will continue to work, obviously, but only criti

Jake Wharton 25.7k Jan 3, 2023
Google Guice on Android, version 3.0 [RETIRED]

As of August 2016, RoboGuice is no longer supported. For nearly 5 years it was the #1 dependency injection framework on Android due to its ease-of-use

null 3.8k Dec 26, 2022
Bind Android views and callbacks to fields and methods.

Butter Knife Attention: This tool is now deprecated. Please switch to view binding. Existing versions will continue to work, obviously, but only criti

Jake Wharton 25.7k Mar 22, 2021
lite your android ! the code is on the way~

android-lite-auto lite your android, the code is on the way~ ! LiteAuto是一个代码生成框架,思路参考 JakeWharton 的开源项目 ButterKnife,在它的思路基础添加了一些自己的想法,从0到1设计并实现的。 Lite

马天宇 32 Nov 2, 2022
Tree View; Mind map; Think map; tree map

A custom tree view for Android, designed for easy drawing some tree nodes (e.g. thind mind and tree nodes). Includes smoothly zoom, move, limit and center fix animation support, and allows easy extension so you can add your own child node's customs view and touch event detection.

怪兽N 304 Jan 3, 2023
A sample Grocery Store app built using the Room, MVVM, Live Data, Rx Java, Dependency Injection (Kotlin Injection) and support Dark Mode

Apps Intro A sample Grocery Store app built using the Room, MVVM, Live Data, Rx Java, Dependency Injection (Kotlin Injection) and support Dark Mode In

Irsyad Abdillah 25 Dec 9, 2022
An easy-to-use Android library that will help you to take screenshots of specif views of your app and save them to external storage (Including API 29 Q+ with Scope Storage)

???? English | ???? Português (pt-br) ???? English: An easy to use Library that will help you to take screenshots ?? of the views in your app Step 1.

Thyago Neves Silvestre 2 Dec 25, 2021