Android

Gradle Dependencies Explained Choosing the Right Type for Your Android Project

Gradle Dependencies Explained: Choosing the Right Type for Your Android Project

If you’ve worked on an Android project, you’ve definitely dealt with Gradle dependencies. They help bring in external libraries, connect different parts of your project, and even let you add custom files. But not all dependencies work the same way. Some are used for linking modules, others for adding external projects, and some for including specific files. Choosing the right type can make your project more organized and easier to maintain.

In this blog, we’ll break down the different types of Gradle dependencies and when to use each one.

Types of Gradle dependencies

Gradle provides three main types of dependencies: 

  • Module dependencies
  • Project dependencies
  • File dependencies

Each type serves a different purpose, and choosing the right one ensures better project organization, maintainability, and performance.

Module Dependencies: The Standard Approach

Module dependencies are the most commonly used in Android development. They allow you to connect different modules within the same project.

Example use case:

  • You have a core module that handles networking and database logic.
  • Your app module depends on core to access those functionalities.

In Gradle, this might look like:

Kotlin
implementation project(":core")

Why use module dependencies?

  • Encourages modularization, making projects easier to scale.
  • Improves build times by allowing Gradle to compile modules separately.
  • Keeps your code organized and avoids duplication.

Project Dependencies: Linking External Projects

Project dependencies come into play when you want to include another Gradle project that isn’t part of your main project by default.

Example use case:

  • You’re working on a shared internal library that’s used across multiple apps.
  • Instead of publishing it to Maven or JCenter every time, you directly link the project.

In Gradle:

Kotlin
implementation project(path: ':shared-library')

Why use project dependencies?

  • Great for internal library development.
  • Lets you work with multiple projects simultaneously without extra publishing steps.
  • Useful in large teams or enterprise-level apps.

File Dependencies: Adding Custom JAR or AAR Files

File dependencies allow you to include JAR or AAR files directly into your project.

Example use case:

  • You’re integrating a third-party SDK that isn’t available in a public Maven repository.
  • You have a legacy .jar file you need for backward compatibility.

In Gradle:

Kotlin
implementation files('libs/custom-library.jar')

Why use file dependencies?

  • Perfect for custom or private libraries.
  • Helps when working with offline builds or older dependencies.

Best practice: Use file dependencies sparingly. If a library is available via Maven Central or Google’s repository, prefer that method — it’s easier to update and maintain.

Best Practices for Managing Gradle Dependencies

  • Prefer remote repositories (Maven Central, Google) over file dependencies.
  • Modularize your project: keep reusable logic in separate modules.
  • Use version catalogs (Gradle 7+) to centralize dependency versions.
  • Keep dependencies updated to avoid security vulnerabilities.
  • Avoid duplication by consolidating commonly used libraries in shared modules.

Conclusion

Gradle dependencies may seem simple, but choosing the right type — module, project, or file — can have a huge impact on your Android project’s structure and maintainability.

  • Use module dependencies for modular apps.
  • Use project dependencies for shared libraries across projects.
  • Use file dependencies only when necessary.

By understanding these distinctions, you’ll write cleaner code, speed up build times, and set yourself up for long-term project success.

FAQ: Gradle Dependencies in Android

Q1: What’s the difference between implementation and api in Gradle?

  • implementation: The dependency is only available in the current module.
  • api: The dependency is exposed to modules that depend on your module.

Q2: When should I use file dependencies in Gradle?

  • Only when the library isn’t available in a Maven or Gradle repository. Otherwise, prefer remote dependencies.

Q3: Can I convert a file dependency into a module or project dependency later?

  • Yes. If you gain access to the source code or publish the library internally, you can switch to module/project dependencies for better maintainability.

Q4: Do Gradle dependencies affect build speed?

  • Yes. Modular dependencies can improve build times, while excessive file dependencies can slow things down.
State Hoisting in Jetpack Compose

State Hoisting in Jetpack Compose: Best Practices for Scalable Apps

When building Android apps with Jetpack Compose, state management is one of the most important pieces to get right. If you don’t handle state properly, your UI can become messy, tightly coupled, and hard to scale. That’s where State Hoisting in Jetpack Compose comes in. In this post, we’ll break down what state hoisting is,...

Membership Required

You must be a member to access this content.

View Membership Levels

Already a member? Log in here
Shared ViewModels in Android

Understanding Shared ViewModels in Android: A Comprehensive Guide

In modern Android development, ViewModel has become an indispensable component for managing UI-related data in a lifecycle-conscious manner. One powerful application of ViewModels is sharing data between multiple fragments or activities. This guide provides a deep dive into shared ViewModels, explaining their purpose, implementation, and best practices for creating seamless data sharing in your Android...

Membership Required

You must be a member to access this content.

View Membership Levels

Already a member? Log in here
State Management in Jetpack Compose

Mastering State Management in Jetpack Compose: A Comprehensive Guide

State management is one of the most critical aspects of building dynamic and interactive Android applications. With Jetpack Compose, Android’s modern UI toolkit, managing state becomes more intuitive, but it also introduces new paradigms that developers need to understand. In this blog, we’ll explore state management in Jetpack Compose in detail. We’ll break down essential...

Membership Required

You must be a member to access this content.

View Membership Levels

Already a member? Log in here
Digital Signature

What Is a Digital Signature & How SSL Certificates Work on Android Devices

In today’s digital world, security and trust are essential, especially when it comes to sensitive information exchanged over the internet. Two foundational technologies that play a critical role in ensuring online security are digital signatures and SSL certificates. If you’re an Android user or developer, understanding these concepts is crucial for protecting your data and...

Membership Required

You must be a member to access this content.

View Membership Levels

Already a member? Log in here
Understanding Android App Standby Buckets: Resource Limits, Job Execution, and Best Practices

Understanding Android App Standby Buckets: Resource Limits, Job Execution, and Best Practices

With Android’s continuous evolution, power management has become increasingly fine-tuned. Starting from Android 9 (API level 28), Android introduced App Standby Buckets, a dynamic classification system that governs how apps can access system resources based on their usage patterns.

These buckets are essential for developers who rely on background jobs, alarms, or network access to power their app’s core functionality.

In this post, we’ll explore what these buckets are, how they limit your app’s capabilities, and how you can optimize your app to function efficiently within these boundaries.

What Are App Standby Buckets?

App Standby Buckets categorize apps based on how frequently they are used. Android uses a combination of machine learning and user behavior analysis to dynamically assign apps to a bucket.

The buckets help Android prioritize system and battery resources without degrading the user experience.

Here are the five main buckets:

  1. Active — App is currently in use or was used very recently.
  2. Working Set — App used often, possibly running in the background.
  3. Frequent — App used regularly but not daily.
  4. Rare — App used infrequently.
  5. Restricted — App is misbehaving or user has manually restricted it.

Resource Limits by Standby Bucket

Each bucket determines how much access an app has to jobs, alarms, and network activity. Below is a breakdown of the execution time windows for each resource type:

Active

  • Regular Jobs: Up to 20 minutes in a rolling 60-minute period
  • Expedited Jobs: Up to 30 minutes in a rolling 24-hour period
  • Alarms: No execution limits
  • Network Access: Unrestricted

Note (Android 16+): Prior to Android 16, apps in the Active bucket had no job execution limit.

Working Set

  • Regular Jobs: Up to 10 minutes in a rolling 4-hour period
  • Expedited Jobs: Up to 15 minutes in a rolling 24-hour period
  • Alarms: Limited to 10 per hour
  • Network Access: Unrestricted

Frequent

  • Regular Jobs: Up to 10 minutes in a rolling 12-hour period
  • Expedited Jobs: Up to 10 minutes in a rolling 24-hour period
  • Alarms: Limited to 2 per hour
  • Network Access: Unrestricted

Rare

  • Regular Jobs: Up to 10 minutes in a rolling 24-hour period
  • Expedited Jobs: Up to 10 minutes in a rolling 24-hour period
  • Alarms: Limited to 1 per hour
  • Network Access: Disabled

Restricted

  • Regular Jobs: Once per day for up to 10 minutes
  • Expedited Jobs: Up to 5 minutes in a rolling 24-hour period
  • Alarms: One per day (exact or inexact)
  • Network Access: Disabled

Regular vs. Expedited Jobs

Android distinguishes between two types of scheduled jobs:

  • Regular Jobs: Standard background tasks scheduled via JobScheduler or WorkManager.
  • Expedited Jobs: Urgent, high-priority jobs using setExpedited(true) or expedited workers in WorkManager.

Expedited jobs have separate quotas from regular jobs. Once those quotas are exhausted, they may still run under the regular job limits.

Best Practice: Use expedited jobs only for urgent tasks. For everything else, rely on regular job scheduling.

Alarm Limits

Starting with Android 12, alarm limits have tightened:

  • Apps in Working Set or below are subject to hourly or daily caps.
  • Apps in the Restricted bucket can schedule only one alarm per day (exact or inexact).

If your app depends on alarms, consider alternatives like JobScheduler or WorkManager, especially for non-critical tasks.

Network Access Limitations

Apps in the Rare and Restricted buckets cannot access the network unless they are running in the foreground.

This has big implications for features like:

  • Background syncing
  • Data uploads
  • Real-time updates

Make sure to test network-reliant tasks across all bucket conditions.

Android 13+ Update: FCM Quota Change

As of Android 13, the number of high-priority Firebase Cloud Messaging (FCM) messages an app can receive is no longer tied to the standby bucket.

This change benefits apps that rely on push messages (like messaging or ride-sharing apps), ensuring more consistent delivery.

Developer Tips for Bucket Optimization

  1. Track App Usage
     Use UsageStatsManager to monitor your app’s current bucket status.
  2. Leverage WorkManager
     It automatically handles job fallback between expedited and regular quotas.
  3. Respect Background Limits
     Overusing background resources can land your app in the Restricted bucket.
  4. Batch and Defer Tasks
     Reduce battery drain and stay in higher buckets longer by batching non-critical jobs.
  5. Test Across Buckets
     Simulate different standby buckets with this ADB command:
JavaScript
adb shell am set-standby-bucket <package_name> <bucket_name>

Conclusion

App Standby Buckets are a key piece of Android’s power management strategy. By tailoring your background behavior to each bucket’s constraints, you not only improve performance and battery life but also ensure a smoother user experience.

Understanding how these limits work — and respecting them — helps you build apps that are efficient, resilient, and Play Store compliant.

FAQs

Q: Can I manually move my app to a different bucket?
 A: No. The system dynamically assigns apps based on usage. You can only simulate bucket placement during testing.

Q: Do background restrictions help battery life?
 A: Yes, but they can also restrict your app’s background capabilities. Design wisely.

Q: How do I test alarms or jobs under low buckets like Rare or Restricted?
 A: Use ADB to simulate conditions, monitor behavior, and fine-tune fallback strategies.

Encryption Best Practices

Encryption Best Practices & Secure Key Management in Kotlin

Encryption is powerful, but if you don’t manage keys securely or follow best practices, your data might still be at risk. Here’s what you should know when working with encryption in Kotlin, especially for Android apps. Why Is Key Management So Important? Think of encryption keys like the keys to your house. If someone steals...

Membership Required

You must be a member to access this content.

View Membership Levels

Already a member? Log in here
Car Service in AOSP

Car Service in AOSP Explained Simply: For Beginners in Android Automotive

If you’re getting started with Android Automotive OS (AAOS), you’ll quickly run into something called Car Service in AOSP. It’s one of those essential components that makes Android work inside a car — not on your phone, but actually on the car’s infotainment system. In this guide, we’ll break down Car Service in AOSP step-by-step, explain how...

Membership Required

You must be a member to access this content.

View Membership Levels

Already a member? Log in here
Salts vs. Pepper

Salts vs. Pepper: The Unsung Heroes of Secure Password Hashing

When we talk about password security, the conversation usually goes straight to hashing algorithms — things like SHA-256, bcrypt, or Argon2.
 But there are two lesser-known players that can make or break your defenses: salts and pepper.

Think of them as seasoning for your password hashes — not for flavor, but for security.

Why Password Hashing Alone Isn’t Enough

Hashing is like putting your password through a one-way blender — you can’t (easily) get the original password back.
 But if attackers get your hashed password database, they can still use rainbow tables or brute-force attacks to figure out the original passwords.

That’s where salts and pepper come in.
 They make every hash unique and harder to crack — even if someone has your database.

Salts vs. Pepper: What’s the Difference?

Salts

  • A random value added to each password before hashing.
  • Stored alongside the hash in the database.
  • Makes it impossible for attackers to use precomputed hash tables.
  • Every user gets a unique salt.

Pepper

  • A secret value added to the password before hashing.
  • Not stored in the database — kept separately (e.g., in environment variables or secure key vaults).
  • Even if the attacker steals your database, they can’t crack hashes without the pepper.

In short:

  • Salt is public but unique per password
  • Pepper is secret and the same for all passwords (or sometimes per user, but still hidden).

Kotlin Example: Salting and Peppering Passwords

Let’s see this in Kotlin. We’ll use the MessageDigest API for hashing (for simplicity), though in real production you should use stronger libraries like BCrypt or Argon2.

Kotlin
import java.security.MessageDigest
import java.security.SecureRandom
import java.util.Base64

object PasswordHasher {

    // Generate a random salt for each password
    fun generateSalt(length: Int = 16): String {
        val random = SecureRandom()
        val salt = ByteArray(length)
        random.nextBytes(salt)
        return Base64.getEncoder().encodeToString(salt)
    }

    // Your secret pepper - should be stored securely (e.g., env variable)
    private const val PEPPER = "SuperSecretPepperValue123!"

    // Hash with salt + pepper
    fun hashPassword(password: String, salt: String): String {
        val saltedPepperedPassword = password + salt + PEPPER
        val digest = MessageDigest.getInstance("SHA-256")
        val hashBytes = digest.digest(saltedPepperedPassword.toByteArray(Charsets.UTF_8))
        return Base64.getEncoder().encodeToString(hashBytes)
    }

    // Verify password
    fun verifyPassword(inputPassword: String, storedSalt: String, storedHash: String): Boolean {
        val inputHash = hashPassword(inputPassword, storedSalt)
        return inputHash == storedHash
    }
}

fun main() {
    val password = "MySecurePassword!"

    // 1. Generate salt
    val salt = PasswordHasher.generateSalt()

    // 2. Hash password with salt + pepper
    val hashedPassword = PasswordHasher.hashPassword(password, salt)

    println("Salt: $salt")
    println("Hash: $hashedPassword")

    // 3. Verify
    val isMatch = PasswordHasher.verifyPassword("MySecurePassword!", salt, hashedPassword)
    println("Password match: $isMatch")
}

Salt Generation

  • We create a random salt using SecureRandom.
  • This ensures no two hashes are the same, even if passwords are identical.

Pepper Usage

  • The pepper is stored outside the database, often in environment variables or secure vaults.
  • It’s the “secret ingredient” that attackers won’t see if they only have the database.

Hashing

  • We combine the password + salt + pepper before hashing with SHA-256.
  • In production, replace SHA-256 with bcrypt or Argon2 for better resistance against brute force.

Verification

  • When a user logs in, we retrieve the stored salt, hash the provided password with the same pepper, and compare the results.

Best Practices for Salts and Pepper

  • Always use a unique salt for each password. Never reuse salts.
  • Store salts with the hash in the database.
  • Keep pepper secret — in an environment variable, key management system, or hardware security module.
  • Use a slow, memory-hard hashing algorithm like bcrypt, scrypt, or Argon2.
  • Rotate peppers periodically for maximum security.
  • Never hard-code pepper in your source code for production.

Why Salts and Pepper Matters

Attackers thrive on shortcuts. Salts remove the shortcut of using rainbow tables. Pepper blocks attackers even if they have your entire password database.
 Together, they make your password security significantly harder to break.

Conclusion

When it comes to security, the little details — like salts and pepper — make a big difference.
 Hashing without them is like locking your front door but leaving the window wide open.
 So next time you store a password, make sure it’s seasoned with both.

Base64 Encoding

Base64 Encoding Demystified: How It Works, When to Use It, and Why It Matters

If you’ve ever poked around APIs, email attachments, or image data in HTML, chances are you’ve stumbled upon a long, strange-looking string of characters — often ending with = signs. That’s Base64 Encoding. In this post, we’ll break down what Base64 Encoding is, how it works, when to use it, and why it’s so widely used in...

Membership Required

You must be a member to access this content.

View Membership Levels

Already a member? Log in here
error: Content is protected !!