Introduction
Android applications have two version identifiers: versionName (a human-readable string like "2.1.0") and versionCode (an integer used by the Play Store to determine updates). You can read both programmatically using PackageManager.getPackageInfo(). In modern Android development (API 28+), use PackageInfo.getLongVersionCode() instead of the deprecated versionCode field for apps with version codes exceeding Integer.MAX_VALUE.
Getting Version in Java
1try {
2 PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
3 String versionName = pInfo.versionName; // "2.1.0"
4 int versionCode = pInfo.versionCode; // 15
5
6 Log.d("AppVersion", "Name: " + versionName + ", Code: " + versionCode);
7} catch (PackageManager.NameNotFoundException e) {
8 e.printStackTrace();
9}
Getting Version in Kotlin
1val pInfo = packageManager.getPackageInfo(packageName, 0)
2val versionName = pInfo.versionName // "2.1.0"
3
4// API 28+ (recommended)
5val versionCode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
6 pInfo.longVersionCode
7} else {
8 @Suppress("DEPRECATION")
9 pInfo.versionCode.toLong()
10}
11
12Log.d("AppVersion", "Name: $versionName, Code: $versionCode")
Using BuildConfig
The simplest approach — access version info directly without PackageManager:
// Java
String versionName = BuildConfig.VERSION_NAME; // "2.1.0"
int versionCode = BuildConfig.VERSION_CODE; // 15
// Kotlin
val versionName = BuildConfig.VERSION_NAME
val versionCode = BuildConfig.VERSION_CODE
BuildConfig is generated at compile time from your build.gradle values. It does not require try-catch and is available without a Context.
Note: In multi-module projects, each module has its own BuildConfig. Import the correct one from your app module.
Setting Version in build.gradle
1// build.gradle (app module)
2android {
3 defaultConfig {
4 applicationId "com.example.myapp"
5 versionCode 15
6 versionName "2.1.0"
7 }
8}
1// build.gradle.kts (Kotlin DSL)
2android {
3 defaultConfig {
4 applicationId = "com.example.myapp"
5 versionCode = 15
6 versionName = "2.1.0"
7 }
8}
Auto-Incrementing Version Code
1// Auto-increment versionCode from git commit count
2def getGitCommitCount = {
3 def process = "git rev-list --count HEAD".execute()
4 return process.text.trim().toInteger()
5}
6
7android {
8 defaultConfig {
9 versionCode getGitCommitCount()
10 versionName "2.1.0"
11 }
12}
Displaying Version in the UI
1// In an Activity or Fragment
2class AboutActivity : AppCompatActivity() {
3 override fun onCreate(savedInstanceState: Bundle?) {
4 super.onCreate(savedInstanceState)
5 setContentView(R.layout.activity_about)
6
7 val versionText = findViewById<TextView>(R.id.version_text)
8 versionText.text = "Version ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})"
9 // Displays: "Version 2.1.0 (15)"
10 }
11}
In Jetpack Compose
1@Composable
2fun VersionInfo() {
3 Text(
4 text = "Version ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})",
5 style = MaterialTheme.typography.bodySmall,
6 color = MaterialTheme.colorScheme.onSurfaceVariant
7 )
8}
Getting Version of Another App
1fun getAppVersion(context: Context, packageName: String): String? {
2 return try {
3 val pInfo = context.packageManager.getPackageInfo(packageName, 0)
4 pInfo.versionName
5 } catch (e: PackageManager.NameNotFoundException) {
6 null // App not installed
7 }
8}
9
10// Usage
11val chromeVersion = getAppVersion(context, "com.android.chrome")
Using PackageInfoCompat (AndroidX)
1import androidx.core.content.pm.PackageInfoCompat
2
3val pInfo = packageManager.getPackageInfo(packageName, 0)
4val versionCode = PackageInfoCompat.getLongVersionCode(pInfo)
5// Works on all API levels — no need for version checks
PackageInfoCompat handles the API 28 deprecation automatically, using longVersionCode on API 28+ and versionCode on older versions.
Version Comparison
1fun isNewerVersion(current: String, latest: String): Boolean {
2 val currentParts = current.split(".").map { it.toIntOrNull() ?: 0 }
3 val latestParts = latest.split(".").map { it.toIntOrNull() ?: 0 }
4
5 val maxLength = maxOf(currentParts.size, latestParts.size)
6 for (i in 0 until maxLength) {
7 val c = currentParts.getOrElse(i) { 0 }
8 val l = latestParts.getOrElse(i) { 0 }
9 if (l > c) return true
10 if (l < c) return false
11 }
12 return false
13}
14
15// Usage
16isNewerVersion("2.1.0", "2.2.0") // true
17isNewerVersion("2.1.0", "2.1.0") // false
Common Pitfalls
Using deprecated versionCode on API 28+: PackageInfo.versionCode is deprecated. Use PackageInfo.getLongVersionCode() on API 28+ or PackageInfoCompat.getLongVersionCode() from AndroidX for all API levels.
Wrong BuildConfig import in multi-module projects: Each module generates its own BuildConfig class. Importing BuildConfig from a library module gives that module's version, not the app's version. Always import from the app module's package.
NameNotFoundException in release builds: This should never happen for getPackageName(), but ProGuard or R8 obfuscation can sometimes interfere with BuildConfig constants. If using BuildConfig, ensure the class is not stripped by adding keep rules if necessary.
versionName is null: versionName can be null if not set in build.gradle. Always handle null: pInfo.versionName ?: "unknown".
versionCode overflow: versionCode is an int (max ~2.1 billion). Google Play requires each update to have a higher versionCode. If using timestamps or large numbers, use longVersionCode (API 28+) which supports values up to Long.MAX_VALUE.
Summary
Use BuildConfig.VERSION_NAME and BuildConfig.VERSION_CODE for the simplest access
Use PackageManager.getPackageInfo() when you need version info from a Context or for other apps
Use PackageInfoCompat.getLongVersionCode() from AndroidX for backward-compatible long version codes
Set versionCode and versionName in build.gradle
versionCode is for the Play Store (must increment); versionName is for users (any string format)