{"id":18478540,"url":"https://github.com/growthbook/growthbook-sdk-java","last_synced_at":"2026-01-29T10:11:14.019Z","repository":{"id":61371324,"uuid":"550553875","full_name":"growthbook/growthbook-sdk-java","owner":"growthbook","description":"The Java SDK for GrowthBook (JVM, Android)","archived":false,"fork":false,"pushed_at":"2025-12-18T09:07:10.000Z","size":2036,"stargazers_count":13,"open_issues_count":5,"forks_count":15,"subscribers_count":10,"default_branch":"main","last_synced_at":"2025-12-21T16:11:15.179Z","etag":null,"topics":["ab-testing","feature-flags","java","sdk"],"latest_commit_sha":null,"homepage":"https://docs.growthbook.io/lib/java","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/growthbook.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-10-13T00:44:25.000Z","updated_at":"2025-12-18T09:06:36.000Z","dependencies_parsed_at":"2024-05-07T09:26:08.362Z","dependency_job_id":"bea07fc2-95f8-4a61-a622-26c4134e788d","html_url":"https://github.com/growthbook/growthbook-sdk-java","commit_stats":null,"previous_names":[],"tags_count":45,"template":false,"template_full_name":null,"purl":"pkg:github/growthbook/growthbook-sdk-java","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/growthbook%2Fgrowthbook-sdk-java","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/growthbook%2Fgrowthbook-sdk-java/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/growthbook%2Fgrowthbook-sdk-java/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/growthbook%2Fgrowthbook-sdk-java/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/growthbook","download_url":"https://codeload.github.com/growthbook/growthbook-sdk-java/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/growthbook%2Fgrowthbook-sdk-java/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28875450,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-29T09:47:23.353Z","status":"ssl_error","status_checked_at":"2026-01-29T09:47:19.357Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ab-testing","feature-flags","java","sdk"],"created_at":"2024-11-06T12:10:30.007Z","updated_at":"2026-01-29T10:11:14.007Z","avatar_url":"https://github.com/growthbook.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"![](growthbook-hero-java.png)\n\n[![](https://jitpack.io/v/growthbook/growthbook-sdk-java.svg)](https://jitpack.io/#growthbook/growthbook-sdk-java)\n\n# GrowthBook Java SDK\n\n- [Requirements](#requirements)\n- [Documentation](#documentation)\n- [Contributing](#contributing)\n  - [Releasing a new version](#releasing-a-new-version)\n\n## Requirements\n\n- Java version 1.8 or later\n\n## Documentation\n\n- [Usage Guide](https://docs.growthbook.io/lib/java)\n- [JavaDoc class documentation](https://growthbook.github.io/growthbook-sdk-java/)\n\n## Installation\n\n### Gradle\n\nTo install in a Gradle project, add Jitpack to your repositories, and then add the dependency with the latest version to your project's dependencies.\n\n```groovy\nallprojects {\n    repositories {\n        maven { url 'https://jitpack.io' }\n    }\n}\n\ndependencies {\n    implementation 'com.github.growthbook:growthbook-sdk-java:0.5.0'\n}\n```\n\n### Maven\n\nTo install in a Maven project, add Jitpack to your repositories:\n\n```java\n\u003crepositories\u003e\n    \u003crepository\u003e\n        \u003cid\u003ejitpack.io\u003c/id\u003e\n        \u003curl\u003ehttps://jitpack.io\u003c/url\u003e\n    \u003c/repository\u003e\n\u003c/repositories\u003e\n```\n\nNext, add the dependency with the latest version to your project's dependencies:\n\n```java\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.github.growthbook\u003c/groupId\u003e\n    \u003cartifactId\u003egrowthbook-sdk-java\u003c/artifactId\u003e\n    \u003cversion\u003e0.5.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n\u003e We are proposing two way of initializing SDK:\n\n### GrowthBookClient\n\n`GrowthBookClient` lets you share the same instance for all requests with an ability to accept the user attributes\nwhile calling the feature methods like `isOn()`. This `GrowthBookClient` instance is decoupled from the `GBContext`,\ncreates a singleton featureRepository based on your refreshStrategy and uses the latest features at the time of\nevaluation, all managed internally.\n\n```java\n\n// build options to configure your Growthbook instance\nOptions options = Options.builder()\n        .apiHost(\"https://cdn.growthbook.io\")\n        .clientKey(\"sdk-abc123\")\n        .build();\n\n// Create growthbook instance using the options you need\nGrowthBookClient gb = new GrowthBookClient(options);\n\n// call the init method to load features \ngb.initialize();\n\ngb.isOn(\"featureKey\", UserContext.builder()\n    .attributesJson(\"{\\\"id\\\" : \\\"123\\\"}\").build()\n);\n```\n\n### Manually create separate instance of GBContext, Repository and Growthbook classes\n\n```java\nGBFeaturesRepository featuresRepository = GBFeaturesRepository\n    .builder()\n    .apiHost(\"https://cdn.growthbook.io\")\n    .clientKey(\"\u003cenvironment_key\u003e\") // replace with your client key\n    .encryptionKey(\"\u003cclient-key-for-decrypting\u003e\") // optional, nullable\n    .refreshStrategy(FeatureRefreshStrategy.SERVER_SENT_EVENTS) // optional; options: STALE_WHILE_REVALIDATE, SERVER_SENT_EVENTS (default: STALE_WHILE_REVALIDATE)\n    .build();\n\n// Optional callback for getting updates when features are refreshed\nfeaturesRepository.onFeaturesRefresh(new FeatureRefreshCallback() {\n    @Override\n    public void onRefresh(String featuresJson) {\n        System.out.println(\"Features have been refreshed\");\n        System.out.println(featuresJson);\n    }\n    @Override\n    public void onError(Throwable throwable) {\n        System.out.println(\"Features refreshed with error\");\n    }\n});\n\ntry {\n    featuresRepository.initialize();\n} catch (FeatureFetchException e) {\n    // TODO: handle the exception\n    e.printStackTrace();\n}\n\n// Initialize the GrowthBook SDK with the GBContext and features\nGBContext context = GBContext\n    .builder()\n    .featuresJson(featuresRepository.getFeaturesJson())\n    .attributesJson(userAttributesJson)\n    .build();\n\nGrowthBook growthBook = new GrowthBook(context);\n\ngrowthBook.isOn(\"featureKey\");\n```\n\n## Usage\n## Caching \u0026 Refresh Strategy\n\n### Cache modes\n\nThe SDK supports multiple cache modes for persisting feature payloads:\n\n- FILE: persist to a writable directory (configurable). Defaults to a safe OS-specific cache dir (or `java.io.tmpdir`).\n- MEMORY: in-process cache only (no filesystem writes).\n- NONE: no cache I/O. Runtime still holds the latest fetched features.\n- CUSTOM: supply your own `GbCacheManager` implementation.\n\nYou can configure these through `Options` when using `GrowthBookClient`, or via the repository builder’s `cacheManager` directly. When cache is disabled (`isCacheDisabled=true`), the repository won’t attempt any persistence.\n\n### STALE_WHILE_REVALIDATE (default)\n\nWith the SWR strategy, the repository will:\n\n- Perform an initial synchronous fetch during `initialize()`.\n- Start a lightweight background poller that revalidates features on a fixed delay (by default equal to the TTL). The poller is protected against overlapping runs and logs start/end of each polling cycle.\n- Keep the latest features in memory and invoke registered `FeatureRefreshCallback`s when updated so the `GlobalContext` stays fresh.\n\nFor SSE connections, the repository establishes a server‑sent events stream and updates as changes arrive; the SWR poller is not used.\n\n\n- The `evalFeature()` method evaluates a feature based on the provided parameters.\nIt takes three arguments: a string representing the unique identifier of the feature,\na generic class valueTypeClass that specifies the type of the result value (e.g., Integer, String, Boolean),\nan UserContext object, which contains attributes, forceVariations and forceFeatureValues to provide a more flexible way of evaluating features.\nThe method returns a FeatureResult object, which contains the evaluated result of the feature along with any additional metadata.\n\n```java\npublic \u003cValueType\u003e FeatureResult\u003cValueType\u003e evalFeature(String key, Class\u003cValueType\u003e valueTypeClass, UserContext userContext);\n```\n\n- `getFeatureValue()` the same purpose as in `evalFeature()`, but have ability to provide default value\n\n```java\npublic \u003cValueType\u003e ValueType getFeatureValue(String featureKey, ValueType defaultValue, Class\u003cValueType\u003e gsonDeserializableClass, UserContext userContext);\n```\n\n- The `isOn()` / `isOff()` method takes a string argument, which is the unique identifier for the feature, and UserContext, which contains attributes, forceVariations and forceFeatureValues to provide a more flexible way of evaluating features. Functions return the feature state on/off\n\n```java\npublic Boolean isOn(String featureKey, UserContext userContext);\n\npublic Boolean isOff(String featureKey, UserContext userContext);\n```\n\n- The `run()` method takes an Experiment object and UserContext. Function returns an ExperimentResult\n\n```java\npublic \u003cValueType\u003e ExperimentResult\u003cValueType\u003e run(Experiment\u003cValueType\u003e experiment, UserContext userContext);\n```\n\n- If you changed, added or removed any features, you can call the `refreshCache()` / `refreshCacheForRemoteEval()` method to clear the cache and download the latest feature definitions.\n\n```java\npublic void refreshFeature();\n\npublic void refreshForRemoteEval(RequestBodyForRemoteEval requestBodyForRemoteEval);\n```\n\n## Remote Evaluation\n\nThis mode brings the security benefits of a backend SDK to the front end by evaluating feature flags exclusively on a\nprivate server. Using Remote Evaluation ensures that any sensitive information within targeting rules or unused feature\nvariations are never seen by the client. Note that Remote Evaluation should not be used in a backend context.\n\nYou must enable Remote Evaluation in your SDK Connection settings. Cloud customers are also required to self-host a\nGrowthBook Proxy Server or custom remote evaluation backend.\n\nTo use Remote Evaluation, set the `FeatureRefreshStrategy = REMOTE_EVAL_STRATEGY` property to your Repository or Options instance. A new evaluation API call will be\nmade any time a user attribute or other dependency changes.\n\n\u003e If you would like to implement Sticky Bucketing while using Remote Evaluation, you must configure your remote evaluation\n\u003e backend to support Sticky Bucketing. You will not need to provide a StickyBucketService instance to the client side SDK.\n\n## Sticky Bucketing\n\nBy default, GrowthBook does not persist assigned experiment variations for a user.\nWe rely on deterministic hashing to ensure that the same user attributes always map to the same experiment variation.\nHowever, there are cases where this isn't good enough. For example, if you change targeting conditions\nin the middle of an experiment, users may stop being shown a variation even if they were previously bucketed into it.\nSticky Bucketing is a solution to these issues. You can provide a Sticky Bucket Service to the GrowthBook instance\nto persist previously seen variations and ensure that the user experience remains consistent for your users.\n\nSticky bucketing ensures that users see the same experiment variant, even when user session, user login status, or\nexperiment parameters change. See the [Sticky Bucketing docs](https://docs.growthbook.io/app/sticky-bucketing) for more\ninformation. If your organization and experiment supports sticky bucketing, you can implement an instance of\nthe `StickyBucketService` to use Sticky Bucketing. For simple bucket persistence using the CachingLayer.\n\nSticky Bucket documents contain three fields:\n\n- attributeName - The name of the attribute used to identify the user (e.g. id, cookie_id, etc.)\n- attributeValue - The value of the attribute (e.g. 123)\n- assignments - A dictionary of persisted experiment assignments. For example: {\"exp1__0\":\"control\"}\n\nThe attributeName/attributeValue combo is the primary key.\n\nHere's an example implementation using a theoretical db object:\n\n```java\npublic class InMemoryStickyBucketServiceImpl implements StickyBucketService {\n    private final Map\u003cString, StickyAssignmentsDocument\u003e localStorage;\n\n    /**\n     * Constructs a new {@code InMemoryStickyBucketServiceImpl} with the specified local storage.\n     *\n     * @param localStorage a map to store sticky assignments documents in memory.\n     */\n    public InMemoryStickyBucketServiceImpl(Map\u003cString, StickyAssignmentsDocument\u003e localStorage) {\n        this.localStorage = localStorage;\n    }\n\n    /**\n     * Method for getting all assignments document from cache (in memory: hashmap)\n     *\n     * @param attributeName  attributeName with attributeValue together present\n     *                       a key that us for find proper StickyAssignmentsDocument\n     * @param attributeValue attributeName with attributeValue together present\n     *                       a key that us for find proper StickyAssignmentsDocument\n     * @return StickyAssignmentsDocument\n     */\n    @Override\n    public StickyAssignmentsDocument getAssignments(String attributeName, String attributeValue) {\n        return localStorage.get(attributeName + \"||\" + attributeValue);\n    }\n\n    /**\n     * Method for saving assignments document to cache (in memory: hashmap)\n     *\n     * @param doc StickyAssignmentsDocument\n     */\n    @Override\n    public void saveAssignments(StickyAssignmentsDocument doc) {\n        localStorage.put(doc.getAttributeName() + \"||\" + doc.getAttributeValue(), doc);\n    }\n\n    /**\n     * Method for getting sticky bucket assignments from cache (in memory: hashmap) by attributes of context\n     *\n     * @param attributes Map of String key and String value that you have in GBContext\n     * @return Map with key String and value StickyAssignmentsDocument\n     */\n    @Override\n    public Map\u003cString, StickyAssignmentsDocument\u003e getAllAssignments(Map\u003cString, String\u003e attributes) {\n        Map\u003cString, StickyAssignmentsDocument\u003e docs = new HashMap\u003c\u003e();\n\n        for (Map.Entry\u003cString, String\u003e entry : attributes.entrySet()) {\n            String key = entry.getKey();\n            String value = entry.getValue();\n            StickyAssignmentsDocument doc = getAssignments(key, value);\n\n            if (doc != null) {\n                String docKey = doc.getAttributeName() + \"||\" + doc.getAttributeValue();\n                docs.put(docKey, doc);\n            }\n        }\n\n        return docs;\n    }\n}\n```\n\n## Contributing\n\n### Releasing a new version\n\nWe use [Release-Please](https://github.com/googleapis/release-please) to automate our release process. The release process follows these steps:\n\n1. **Make changes using Conventional Commits**: When making changes, format your commit messages following the [Conventional Commits](https://www.conventionalcommits.org/) spec:\n   - `fix: message` - for bug fixes (triggers a patch version bump)\n   - `feat: message` - for new features (triggers a minor version bump)\n   - `feat!: message` or `fix!: message` - for breaking changes (triggers a major version bump)\n   - `docs: message` - for documentation changes (no version bump)\n   - `chore: message` - for maintenance changes (no version bump)\n\n2. **Automated Release PR**: When commits are pushed to the `main` branch, Release-Please will automatically create or update a release PR that:\n   - Updates the version in `gradle.properties` and `Version.java`\n   - Updates the `CHANGELOG.md` with all the changes since the last release\n   - Groups changes by type (features, fixes, etc.)\n\n3. **Review and Merge**: Review the Release PR and merge it when ready to trigger a release.\n\n4. **Automated Release**: Upon merging the Release PR, the action will:\n   - Create a Git tag for the new version\n   - Create a GitHub Release with release notes\n   - Upload build artifacts to the GitHub Release\n\n5. **Jitpack Integration**: Jitpack will automatically detect the new release tag and make it available for download.\n\nNo manual version updates are required!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgrowthbook%2Fgrowthbook-sdk-java","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgrowthbook%2Fgrowthbook-sdk-java","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgrowthbook%2Fgrowthbook-sdk-java/lists"}