To upgrade your app from the Branch Android SDK 5.x to the 6.0 beta, update the SDK in step 1, then follow Upgrade from 5.x. That section takes you through steps 2 to 4 and every other call that changed.
Version 6.0 starts from one configuration object, and your app gets all deep link data from requestDeepLinkData.
Beta
The Android SDK 6.0 is a beta, and its API can still change before the final release. Test it in development and test builds only, and don't ship it to Google Play.
If you find a problem or a gap, email support@branch.io with "Testing new SDK Beta" as the subject.
Use an AI coding assistant (Optional)
Copy this prompt into a coding assistant like Claude Code or Cursor, and it upgrades the SDK for you.
Upgrade this app to the Branch Android SDK 6.0 beta, version 6.0.0-beta.3.
Read this guide and follow it. Don't rely on what you already know about the
Branch SDK, because 6.0 removes or changes most of the 5.x API:
https://help.branch.io/developer-hub/android-sdk-6-integration.md
1. Find the app's Branch integration. Look for io.branch.sdk.android in the
Gradle files, for io.branch entries in AndroidManifest.xml, for
io.branch.sdk string resources, for an assets/branch.json file, and for
Branch calls in the Application class and activities.
2. Before you change any code, collect what you need. Reuse the Branch keys
already in the project, and ask me for any that are missing, including
both the live key and the test key. If the app has product flavors, ask
me which Branch key each flavor uses. If more than one activity handles
links, ask me which one should call requestDeepLinkData.
3. Follow step 1 in the guide, then every step in "Upgrade from 5.x",
including the steps for 5.x code that still builds. Then apply any case
in "Handle edge cases" that fits this app.
4. Write code in the app's language, Kotlin or Java. Don't change the app's
intent filters or launch modes.
5. Build the app and fix any errors.
6. Finish with a list of the files you changed, anything you couldn't
migrate, and the checks from step 4 in the guide for me to run on a
device.Review the changes before you merge them.
1. Update the SDK to the beta version
Change io.branch.sdk.android:library to version 6.0.0-beta.3 exactly, so your app doesn't pick up a different build. Version 6.0 needs a minSdk of 21 or later.
// App module build.gradle.kts
dependencies {
implementation("io.branch.sdk.android:library:6.0.0-beta.3")
// (Optional) Collect the Google Advertising ID
implementation("com.google.android.gms:play-services-ads-identifier:18.0.1")
}If you collect the Google Advertising ID and minify release builds
Add this rule to your ProGuard rules file.
# The SDK finds the Google Advertising ID library by class name,
# so keep the class in minified builds
-keep class com.google.android.gms.ads.identifier.AdvertisingIdClient2. Start the SDK
Build a BranchConfiguration with your Branch key, and pass it to Branch.initialize in your Application class. Use the same key as your 5.x app, which is usually in the io.branch.sdk.BranchKey meta-data in your manifest. If your app doesn't have an Application class yet, create one, and name it in the android:name attribute of the <application> element in your manifest.
import android.app.Application
import io.branch.referral.Branch
import io.branch.referral.BranchConfiguration
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
// Use your test key in debug builds and your live key in release builds
val config = BranchConfiguration.Builder("<branch_key>")
// Automatic opens are on by default. Set this to false to send
// each open yourself, as in "Send opens yourself"
.setAutomaticOpenEvents(true)
// Set any other options on the builder here, as listed in
// "BranchConfiguration reference"
.build()
// Start the SDK here, so it's ready before any activity asks for
// link data
Branch.initialize(this, config)
}
}import android.app.Application;
import io.branch.referral.Branch;
import io.branch.referral.BranchConfiguration;
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
// Use your test key in debug builds and your live key in release builds
BranchConfiguration config =
new BranchConfiguration.Builder("<branch_key>")
// Automatic opens are on by default. Set this to
// false to send each open yourself, as in
// "Send opens yourself"
.setAutomaticOpenEvents(true)
// Set any other options on the builder here, as
// listed in "BranchConfiguration reference"
.build();
// Start the SDK here, so it's ready before any activity asks for
// link data
Branch.initialize(this, config);
}
}Everywhere else, get the SDK from Branch.getInstance(), which is null until this call runs.
Use a different key for debug and release builds
Define the key for each build type. Then pass BuildConfig.BRANCH_KEY to the builder instead of the literal key.
// App module build.gradle.kts
android {
// Required with Android Gradle Plugin 8.0 or later
buildFeatures { buildConfig = true }
buildTypes {
debug { buildConfigField("String", "BRANCH_KEY", "\"<test_key>\"") }
release { buildConfigField("String", "BRANCH_KEY", "\"<live_key>\"") }
}
}3. Handle deep links
Call requestDeepLinkData in your launcher activity's onCreate and onNewIntent methods, and route the user in its callback.
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import io.branch.referral.Branch
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// The app launches. On a new install from a link, this returns
// that link's data
// Skip re-creations so they don't count as opens, for example
// after a rotation
if (savedInstanceState == null) requestBranchData()
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
// The SDK reads the link from the activity's current intent
setIntent(intent)
requestBranchData()
}
private fun requestBranchData() {
// Branch runs the callback on the main thread
// The params object holds the link's data, including any keys you
// added to the link
Branch.getInstance().requestDeepLinkData(this) { params, error ->
if (error != null) {
// The request failed, for example because the device is offline
} else if (params?.optBoolean("+clicked_branch_link") == true) {
// A Branch Link opened the app, so show the content it
// points to
}
}
}
}import android.content.Intent;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import io.branch.referral.Branch;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// The app launches. On a new install from a link, this returns
// that link's data
// Skip re-creations so they don't count as opens, for example
// after a rotation
if (savedInstanceState == null) {
requestBranchData();
}
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
// The SDK reads the link from the activity's current intent
setIntent(intent);
requestBranchData();
}
private void requestBranchData() {
// Branch runs the callback on the main thread
// The params object holds the link's data, including any keys you
// added to the link
Branch.getInstance().requestDeepLinkData(this, (params, error) -> {
if (error != null) {
// The request failed, for example because the device is offline
} else if (params != null
&& params.optBoolean("+clicked_branch_link")) {
// A Branch Link opened the app, so show the content it
// points to
}
});
}
}Each requestDeepLinkData call counts as an install or open, so call it only where this sample does. Branch doesn't count an open on its own when your app returns from the background.
Read link data in this callback. When your app moves to the background, the SDK can clear the data that getLatestReferringParams() returns.
4. Check your integration
After you finish Upgrade from 5.x, run a debug build with verbose logging, open a Branch Link, and confirm that the link data arrives.
- Add
.setLogLevel(BranchLogger.BranchLogLevel.VERBOSE)to the builder in yourApplicationclass, in debug builds only. TheBranchLoggerclass is in theio.branch.referralpackage. - Run the app on a device.
- Filter Logcat by the
BranchSDKtag. - Open a Branch Link for your app from a messaging or notes app. Use a link from the same Branch app as your key, such as a
test-app.linklink for a test key. - Check that the
RequestDeepLink Succeededline in Logcat shows+clicked_branch_linkas true. - Check that your release build uses your live key, without the verbose log level.
If a check fails
- If the link opens a browser instead of your app, Android didn't verify your App Links. On Android 12 or later, run
adb shell pm get-app-links <package_name>and check that each Branch host shows as verified. If one doesn't, check that Branch has the SHA256 fingerprint of the key that signed the build. - If
Branch.getInstance()returnsnull, check that theandroid:nameattribute of<application>names yourApplicationclass. - If your callback gets an error, the
Request DeepLink failed with HTTP codeline in Logcat says why. - If
build()throws anIllegalArgumentException, see Fix a configuration error.
Handle edge cases
Most integrations don't run into these cases. Check them when your app does one of these things, or when something doesn't work as expected.
Fix a configuration error
If the key is empty or a value is out of range, build() throws an IllegalArgumentException that lists each setting that isn't valid. The timeouts and the retry interval must be more than 0, and the retry count must be 0 or more. If you create the builder without a key, Branch.initialize throws the same exception.
Use coroutines
For launches, use the callback version of requestDeepLinkData from step 3, not the suspend version. The suspend version drops its request if something cancels the coroutine before Branch sends it, and a rotation cancels the lifecycleScope of the activity.
Upgrade from 5.x
Most of the upgrade is in how the SDK starts and how your app gets deep link data. Version 6.0 removes getAutoInstance and the session builder. Your app starts the SDK with Branch.initialize, and requestDeepLinkData returns link data.
- Replace
Branch.getAutoInstance(this)in yourApplicationclass by following 2. Start the SDK. - If your manifest names
io.branch.referral.BranchAppor yourApplicationclass extends it, use your ownApplicationclass that extendsandroid.app.Application. Version 6.0 removesBranchApp, so an app whose manifest still names it builds but crashes at launch. - In your launcher activity, replace the
sessionBuilder(...).init()call with theonCreatecode in 3. Handle deep links. The 5.x guide put that call inonStart, but don't callrequestDeepLinkDatathere or in a base class that every activity shares, because each call counts as another open. - In
onNewIntent, replace thereInit()call and thebranch_force_new_sessioncheck around it with theonNewIntentcode in 3. Handle deep links. Version 6.0 ignores that extra. - If you used auto deep linking, start the right activity yourself in your
requestDeepLinkDatacallback. - If you used auto deep linking, remove the
io.branch.sdk.auto_link_keysandio.branch.sdk.auto_link_pathmeta-data from your manifest, because 6.0 ignores it. - If your app has an
assets/branch.jsonfile, move its settings to the builder. Version 6.0 ignores the key in this file, but the file's other settings override the builder's.- The
branchKey,liveKey,testKey, anduseTestInstancesettings become the key you pass to the builder. - The
apiUrl,fbAppId,cppLevel, andenableLoggingsettings map to the builder'ssetApiUrl,setFacebookAppId,setAttributionLevel, andsetLogLevelmethods. - The
installReferrerFetchTimeoutsetting has no replacement. FordeferInitForPluginRuntime, callrequestDeepLinkDatawhen your plugin's runtime is ready.
- The
- Delete the
assets/branch.jsonfile. - If you set the attribution level on the builder, pass each user's saved choice, because
Branch.initializeapplies the builder's level at every launch over the level that Branch saved. If you don't set it, each user's level and opt-out from 5.x carry over. - (Optional) Remove the
io.branch.sdk.BranchKey,io.branch.sdk.BranchKey.test, andio.branch.sdk.TestModeentries from your manifest and string resources. - Build the app.
- Fix each build error by using the following table.
- Change the 5.x calls in the table that still build. These are
disableTrackingandBranch.notifyNativeToInit(). - Run the checks in 4. Check your integration.
This table maps 5.x calls to their 6.0 replacements. In this table, a call that starts with Branch. is static.
| 5.x | 6.0 |
|---|---|
Branch. or Branch. | Branch. |
BranchApp as your Application class | Your own Application class that calls Branch. |
io. in the manifest or in a string resource, the key in branch., or setBranchKey( | BranchConfiguration. |
Branch., Branch., or io. in the manifest or in a string resource | Pass your test or live key to BranchConfiguration. |
Branch. | requestDeepLinkData( in onCreate, not in onStart |
Branch. in onNewIntent | setIntent(, then requestDeepLinkData( for every new intent, with no branch_force_new_session check |
putExtra( with putExtra(, for a push notification or a link inside your app | Open your launcher activity with the branch extra, which requestDeepLinkData( reads. Version 6.0 ignores branch_force_new_session. |
withData( on the session builder | requestDeepLinkData( when uri is the activity's intent data, because that call reads the intent itself. For any other URI, requestDeepLinkData(. |
withCallback( on the session builder | A BranchReferralInitListener. In it, get the object and link properties with BranchUniversalObject. and LinkProperties.. |
withDelay, isReferrable, and ignoreIntent on the session builder, plus removeSessionInitializationDelay, unlockPendingIntent, Branch., Branch., Branch., Branch., Branch., Branch., Branch., and Branch. | Call requestDeepLinkData when your app is ready to handle the link |
Branch. and deferInitForPluginRuntime in branch. | Call requestDeepLinkData when your plugin's runtime is ready. The old call still builds but does nothing. |
Auto deep linking with io. or io., and Branch. | Start the activity yourself in your requestDeepLinkData callback |
setIdentity( | setUserAlias( |
disableTracking( and disableTracking( | setAttributionLevel( in updateConfiguration, or another level to opt back in. The old calls still build, with a deprecation warning. |
getInitState() compared to a SessionState value | getInitState(), which returns a BranchSessionState that you check by type |
Branch. | setEUEndpoint( on the builder |
setNetworkTimeout, setNetworkConnectTimeout, setRetryCount, and setRetryInterval | Builder methods with the same names. An out-of-range value makes build() throw. |
Branch. and Branch. | setApiUrl( and setCdnBaseUrl( on the builder |
Branch. | setRequestTracingCallback( on the builder |
setBranchRemoteInterface( | setRemoteInterface( on the builder |
setDMAParamsForEEA( | setDMAParameters( on the builder, or in updateConfiguration when consent changes, with params built by DMAParameters. |
Branch. | setFacebookAppId( on the builder |
setPreinstallCampaign, setPreinstallPartner, and addInstallMetadata | Builder methods with the same names |
Branch. | setReferringLinkAttributionForPreinstalledApps( on the builder, which has no effect in 6.0.0-beta.3 |
addWhiteListedScheme( and setWhiteListedSchemes( | addWhitelistedScheme( on the builder, once for each scheme |
addUriHostsToSkip( | addUriHostToSkip( on the builder |
Branch. | setUserAgentFetchSync( on the builder |
setLimitFacebookTracking, disableAdNetworkCallouts, and setNoConnectionRetryMax | Removed, with no replacement |
registerView() or registerView( on BranchUniversalObject, or registerView( on Branch | BranchEvent( |
addContentMetadata( and addContentMetadata( on BranchUniversalObject | getContentMetadata(). for each key, which adds to the existing metadata |
getMetadata() on BranchUniversalObject | getContentMetadata(). |
getPrice() and getCurrencyType() on BranchUniversalObject | The price and currencyType fields of getContentMetadata(), which hold the real values. In 5.x, these calls always returned 0.0 and null. The price field is a Double that's null when no price is set, and currencyType is a CurrencyType enum. |
setContentType( on BranchUniversalObject | Delete the call, because it did nothing in 5.x. To set a content type, use getContentMetadata(). with a BranchContentSchema. |
showShareSheet( on BranchUniversalObject, setCallback on BranchShareSheetBuilder, and listeners that implement Branch. | Branch., with a Branch. callback. It needs API level 22 or later. |
Branch., Branch., Branch., setInstantDeepLinkPossible, and isInstantDeepLinkPossible | Removed, with no replacement |
resetUserSession, disableAppList, Branch., Branch., setInstallReferrerTimeout, setDeepLinkDebugMode, getDeeplinkDebugParams, notifyNetworkAvailable, cancelShareLinkDialog, and getBranchPluginSupport | Removed, with no replacement |
setChannelProperties on BranchShareSheetBuilder, Branch., and BranchUniversalObject. | Removed, with no replacement |
getType, setContentIndexingMode, setLocalIndexMode, isPublicallyIndexable, and isLocallyIndexable on BranchUniversalObject | Removed, with no replacement |
The BranchEvent and LinkProperties APIs stay the same.
BranchConfiguration reference
Version 6.0 moves the SDK's settings into one BranchConfiguration, which you create with BranchConfiguration.Builder and pass to Branch.initialize. This sample sets the builder's main options to their defaults or example values, with a note on what each one does.
// Your live or test key
val config = BranchConfiguration.Builder("<branch_key>")
// On by default. With false, Branch holds each open until you call
// sendOpen(callback) or the app goes to the background
.setAutomaticOpenEvents(true)
// The least severe level that Branch logs. Logging stays off until
// you set this or a logging callback.
.setLogLevel(BranchLogger.BranchLogLevel.WARN)
// Your logger instead of Logcat
.setLoggingCallback { message, severity ->
Log.d("Branch", "$severity: $message")
}
// Milliseconds to connect to Branch's servers
.setNetworkConnectTimeout(10_000)
// Milliseconds to wait for a response once connected
.setNetworkTimeout(5_500)
// Retries after a server error or a network failure
.setRetryCount(3)
// Milliseconds before the first retry, doubled for each retry after it,
// up to 30 seconds
.setRetryInterval(1_000)
// FULL is the default; the others are REDUCED, MINIMAL, and NONE.
// Branch applies this level at every launch and replaces the level it
// saved, so if users can change their consent, pass their saved choice
// instead, or leave this line out.
.setAttributionLevel(Defines.BranchAttributionLevel.FULL)
// EEA consent for Google. Branch keeps sending the last values set,
// including values saved by 5.x.
.setDMAParameters(
DMAParameters.Builder()
.setEeaRegion(true)
.setAdPersonalizationConsent(false)
.setAdUserDataUsageConsent(true)
.build()
)
// Turns on the Meta install referrer
.setFacebookAppId("1234567890")
// Sent with the install
.addInstallMetadata("app_variant", "free")
// Only for apps preinstalled on devices
.setPreinstallCampaign("spring_promo")
.setPreinstallPartner("oem_partner")
.build()
Branch.initialize(this, config)// Your live or test key
BranchConfiguration config = new BranchConfiguration.Builder("<branch_key>")
// On by default. With false, Branch holds each open until you
// call sendOpen(callback) or the app goes to the background
.setAutomaticOpenEvents(true)
// The least severe level that Branch logs. Logging stays off
// until you set this or a logging callback.
.setLogLevel(BranchLogger.BranchLogLevel.WARN)
// Your logger instead of Logcat
.setLoggingCallback((message, severity) ->
Log.d("Branch", severity + ": " + message))
// Milliseconds to connect to Branch's servers
.setNetworkConnectTimeout(10_000)
// Milliseconds to wait for a response once connected
.setNetworkTimeout(5_500)
// Retries after a server error or a network failure
.setRetryCount(3)
// Milliseconds before the first retry, doubled for each retry
// after it, up to 30 seconds
.setRetryInterval(1_000)
// FULL is the default; the others are REDUCED, MINIMAL, and NONE.
// Branch applies this level at every launch and replaces the level
// it saved, so if users can change their consent, pass their saved
// choice instead, or leave this line out.
.setAttributionLevel(Defines.BranchAttributionLevel.FULL)
// EEA consent for Google. Branch keeps sending the last values
// set, including values saved by 5.x.
.setDMAParameters(new DMAParameters.Builder()
.setEeaRegion(true)
.setAdPersonalizationConsent(false)
.setAdUserDataUsageConsent(true)
.build())
// Turns on the Meta install referrer
.setFacebookAppId("1234567890")
// Sent with the install
.addInstallMetadata("app_variant", "free")
// Only for apps preinstalled on devices
.setPreinstallCampaign("spring_promo")
.setPreinstallPartner("oem_partner")
.build();
Branch.initialize(this, config);Note
The builder also has setTestMode and setReferringLinkAttributionForPreinstalledApps, which have no effect in 6.0.0-beta.3.
Send opens yourself
With setAutomaticOpenEvents(false), Branch still resolves each link and calls your requestDeepLinkData callback, but it holds the open until you call sendOpen. Use this when the open needs data that arrives later, such as Adobe's Experience Cloud ID (ECID). Set the data with setRequestMetadata, then call sendOpen.
// Call Branch.getInstance().sendOpen after requestDeepLinkData
// in requestBranchData() from step 3
// This example uses Adobe Identity, which returns the ECID asynchronously
Identity.getExperienceCloudId { ecid ->
Branch.getInstance()
.setRequestMetadata("\$marketing_cloud_visitor_id", ecid)
Branch.getInstance().sendOpen { error ->
// error is null when Branch sent the open
}
}// Call Branch.getInstance().sendOpen after requestDeepLinkData
// in requestBranchData() from step 3
// This example uses Adobe Identity, which returns the ECID asynchronously
Identity.getExperienceCloudId(ecid -> {
Branch.getInstance()
.setRequestMetadata("$marketing_cloud_visitor_id", ecid);
Branch.getInstance().sendOpen(error -> {
// error is null when Branch sent the open
});
});- Call
sendOpenonce for eachrequestDeepLinkDatacall. - If you call
sendOpenbefore the link data arrives, Branch sends the open when it does. - If your app goes to the background first, Branch sends the open then. At the
NONEattribution level, Branch waits until the user opts in. - On a new install, events and other requests that need an open fail if they run before Branch sends the open.
Change settings while the app runs
To change a setting after start-up, pass updateConfiguration a configuration that sets only the changed values, as in this consent example. Create its builder without a key.
// In your consent screen's handler, after Branch.initialize
fun onConsentChosen(
level: Defines.BranchAttributionLevel,
adPersonalization: Boolean,
adUserData: Boolean
) {
// Save the choice in your own storage, and pass it to the builder at
// the next launch
saveConsent(level, adPersonalization, adUserData)
// No key
val changes = BranchConfiguration.Builder()
.setAttributionLevel(level)
.setDMAParameters(
DMAParameters.Builder()
.setEeaRegion(true)
.setAdPersonalizationConsent(adPersonalization)
.setAdUserDataUsageConsent(adUserData)
.build()
)
.build()
Branch.getInstance().updateConfiguration(changes)
}// In your consent screen's handler, after Branch.initialize
void onConsentChosen(Defines.BranchAttributionLevel level,
boolean adPersonalization, boolean adUserData) {
// Save the choice in your own storage, and pass it to the builder at
// the next launch
saveConsent(level, adPersonalization, adUserData);
// No key
BranchConfiguration changes = new BranchConfiguration.Builder()
.setAttributionLevel(level)
.setDMAParameters(new DMAParameters.Builder()
.setEeaRegion(true)
.setAdPersonalizationConsent(adPersonalization)
.setAdUserDataUsageConsent(adUserData)
.build())
.build();
Branch.getInstance().updateConfiguration(changes);
}The changes apply to the next request Branch sends, and settings you don't set keep their current values. Branch saves the attribution level and DMA values on the device, so they carry over to the next launch, unless your start-up configuration sets new ones.
An update doesn't start a new session. When a user opts in from NONE, Branch sends the open it held while attribution was off. With setAutomaticOpenEvents(false), Branch sends that open only after you call sendOpen, or once the app has gone to the background. A change between two levels other than NONE sends no open.
The key, setTestMode, setFacebookAppId, the preinstall settings, addInstallMetadata, and setAutomaticOpenEvents only apply at Branch.initialize, so updateConfiguration ignores them and logs a warning.
