Deprecated: Function get_magic_quotes_gpc() is deprecated in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 99

Deprecated: The each() function is deprecated. This message will be suppressed on further calls in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 619

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1169

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176
8000 GitHub - kibotu/Borg: A powerful, coroutine-based dependency initialization orchestrator for Android applications. Borg ensures your components are initialized in the correct order, with maximum parallelization and bulletproof thread safety.
Nothing Special   »   [go: up one dir, main page]

Skip to content
/ Borg Public

A powerful, coroutine-based dependency initialization orchestrator for Android applications. Borg ensures your components are initialized in the correct order, with maximum parallelization and bulletproof thread safety.

License

Notifications You must be signed in to change notification settings

kibotu/Borg

Repository files navigation

Borg πŸ€–

Maven Central Version Android CI API API API Gradle Version Kotlin License

Resistance is futile - your components will be assimilated in perfect order.

πŸš€ About Borg is a high-performance Android initialization orchestrator that brings order to your app's startup chaos. It automatically manages complex dependency graphs, parallelizes independent initializations, and provides bulletproof thread safety - all with the elegance of Kotlin coroutines.

Check out the article on Medium

screenshot

Table of Contents πŸ“‘

Why Borg? πŸ€”

Modern Android apps face complex initialization challenges:

Common Problems

  • ❌ Race conditions in component initialization
  • ❌ Unclear dependency ordering
  • ❌ Blocking main thread during setup
  • ❌ Hard to test initialization logic
  • ❌ Difficult error recovery
  • ❌ Poor performance from sequential initialization

Borg's Solutions

  • βœ… Thread-safe, deterministic initialization
  • βœ… Automatic dependency resolution
  • βœ… Non-blocking coroutine-based setup
  • βœ… Easy to test with constructor injection
  • βœ… Structured error handling
  • βœ… Maximum parallel initialization

Key Features 🌟

  • Type-Safe Dependencies: Compile-time verification of dependency graph
  • Parallel Initialization: Automatic parallelization of independent components
  • Coroutine Support: Native suspend function support for async operations
  • Thread Safety: Bulletproof concurrency handling with deadlock prevention
  • Error Handling: Rich exception hierarchy with detailed context
  • Testing Support: Easy mocking and testing through constructor injection
  • Performance: Optimal initialization order with parallel execution
  • Flexibility: Generic context type for any initialization needs

Installation πŸ“¦

Maven Central

allprojects {
    repositories {
        mavenCentral()
    }
}

dependencies {
    implementation 'net.kibotu:Borg:{latest-version}'
}

JitPack (Alternative)

  1. Add JitPack repository:
// settings.gradle.kts
dependencyResolutionManagement {
    repositories {
        maven { url = uri("https://jitpack.io") }
    }
}
  1. Add the dependency:
// build.gradle.kts
dependencies {
    implementation("com.github.kibotu:Borg:latest-version")
}

Quick Start πŸš€

1. Define Your Components

Create a drone for each component that needs initialization:

// 1. Simple configuration
class ConfigDrone : BorgDrone<AppConfig, Context> {
    override suspend fun assimilate(context: Context, borg: Borg<Context>) =
        AppConfig.load(context.assets.open("config.json"))
}

// 2. Database with config dependency
class DatabaseDrone : BorgDrone<AppDatabase, Context> {
    override fun requiredDrones() = listOf(ConfigDrone::class.java)
    
    override suspend fun assimilate(context: Context, borg: Borg<Context>): AppDatabase {
        val config = borg.requireAssimilated(ConfigDrone::class.java)
        return Room.databaseBuilder(context, AppDatabase::class.java, config.dbName)
            .build()
    }
}

// 3. Repository combining multiple dependencies
class RepositoryDrone : BorgDrone<Repository, Context> {
    override fun requiredDrones() = listOf(
        DatabaseDrone::class.java,
        ApiDrone::class.java
    )
    
    override suspend fun assimilate(context: Context, borg: Borg<Context>) = Repository(
        database = borg.requireAssimilated(DatabaseDrone::class.java),
        api = borg.requireAssimilated(ApiDrone::class.java)
    )
}

2. Initialize the Collective

class App : Application() {

    private val applicationScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
    
    override fun onCreate() {
        super.onCreate()
        
        lifecycleScope.launch {
            try {
                // Create and initialize the collective
                val borg = Borg(setOf(
                    ConfigDrone(),
                    DatabaseDrone(),
                    ApiDrone(),
                    RepositoryDrone()
                ))
                
                // Assimilate all components
                borg.assimilate(applicationContext)
                
                // Store initialized components
                appContainer.repository = borg.requireAssimilated(RepositoryDrone::class.java)
                
            } catch (e: BorgException) {
                handleInitializationError(e)
            }
        }
    }
}

Advanced Usage πŸ”§

Parallel Initialization

Borg automatically parallelizes initialization of independent components:

val drones = setOf(
    AnalyticsDrone(),     // No dependencies - Parallel
    ConfigDrone(),        // No dependencies - Parallel
    DatabaseDrone(),      // Depends on Config - Waits for Config
    ApiDrone()           // Depends on Config - Waits for Config
)

// Visualization of parallel execution:
// Time β†’
// Analytics   β–“β–“β–“β–“β–“β–“β–“
// Config      β–“β–“β–“β–“
// Database         β–“β–“β–“β–“ (starts after Config)
// Api              β–“β–“β–“β–“ (starts after Config)

Handling Optional Dependencies

Use getAssimilated() for optional dependencies:

class AnalyticsDrone : BorgDrone<Analytics, Context> {
    override fun requiredDrones() = listOf(UserDrone::class.java)
    
    override suspend fun assimilate(context: Context, borg: Borg<Context>): Analytics {
        val analytics = FirebaseAnalytics.getInstance(context)
        
        // Optional user identification
        borg.getAssimilated(UserDrone::class.java)?.let { user ->
            analytics.setUserId(user.id)
        }
        
        return analytics
    }
}

Fallback Handling

Implement graceful degradation:

class RemoteConfigDrone : BorgDrone<Config, Context> {
    override suspend fun assimilate(context: Context, borg: Borg<Context>): Config {
        return try {
            // Try remote config first
            FirebaseRemoteConfig.getInstance()
                .fetchAndActivate()
                .await()
                .let { RemoteConfig() }
        } catch (e: Exception) {
            // Fall back to local config
            LocalConfig.fromAssets(context)
        }
    }
}

Enabling Logging πŸ“

To enable logging in Borg, you can use the enableLogging parameter in the Borg constructor. This will automatically set up logging for all Borg operations.

Example:

val borg = Borg(
    drones = setOf(
        ConfigDrone(),
        DatabaseDrone(),
        ApiDrone(),
        RepositoryDrone()
    ),
    enableLogging = true
)

By setting enableLogging to true, Borg will log important lifecycle events and errors, helping you to monitor and debug the initialization process effectively.

You can still configure the logging level and customize the logger as needed, but the enableLogging parameter provides a quick and easy way to get started with logging in Borg.

Best Practices πŸ’‘

1. Keep Drones Focused

// ❌ Bad: Too many responsibilities
class MonolithicDrone : BorgDrone<AppServices, Context> {
    override suspend fun assimilate(context: Context, borg: Borg<Context>): AppServices {
        val db = Room.databaseBuilder(/*...*/).build()
        val api = Retrofit.Builder().build()
        val analytics = FirebaseAnalytics.getInstance(context)
        return AppServices(db, api, analytics)
    }
}

// βœ… Good: Single responsibility
class DatabaseDrone : BorgDrone<AppDatabase, Context> {
    override suspend fun assimilate(context: Context, borg: Borg<Context>) =
        Room.databaseBuilder(context, AppDatabase::class.java, "app.db").build()
}

2. Handle Errors Gracefully

class ApiDrone : BorgDrone<ApiClient, Context> {
    override fun requiredDrones() = listOf(ConfigDrone::class.java)
    
    override suspend fun assimilate(context: Context, borg: Borg<Context>): ApiClient {
        try {
            val config = borg.requireAssimilated(ConfigDrone::class.java)
            
            // Validate configuration
            require(config.apiUrl.isNotBlank()) { "API URL is required" }
            
            return ApiClient(config.apiUrl)
                .also { client ->
                    // Verify connectivity
                    client.ping()
                        .await()
                        .also { response ->
                            require(response.isSuccessful) {
                                "API not reachable: ${response.code()}"
                            }
                        }
                }
                
        } catch (e: Exception) {
            throw BorgException.AssimilationException(
                drone = this::class.java,
                cause = e
            )
        }
    }
}

3. Document Dependencies

class RepositoryDrone : BorgDrone<Repository, Context> {
    override fun requiredDrones() = listOf(
        DatabaseDrone::class.java,
        ApiDrone::class.java,
        CacheDrone::class.java
    )
    
    override suspend fun assimilate(context: Context, borg: Borg<Context>): Repository {
        val db = borg.requireAssimilated(DatabaseDrone::class.java)
        val api = borg.requireAssimilated(ApiDrone::class.java)
        val cache = borg.getAssimilated(CacheDrone::class.java)
        
        return Repository(db, api, cache)
    }
}

Error Handling 🚨

Borg provides structured error handling:

try {
    borg.assimilate(context)
} catch (e: BorgException) {
    when (e) {
        is BorgException.CircularDependencyException -> {
            // Circular dependency detected
            Log.e("Borg", "Dependency cycle: ${e.cycle.joinToString(" -> ")}")
        }
        is BorgException.DroneNotFoundException -> {
            // Missing required drone
            Log.e("Borg", "${e.drone} requires ${e.requiredDrone}")
        }
        is BorgException.AssimilationException -> {
            // Initialization failed
            Log.e("Borg", "Failed to initialize ${e.drone}", e.cause)
        }
        is BorgException.DroneNotAssimilatedException -> {
            // Accessed uninitialized drone
            Log.e("Borg", "Drone not ready: ${e.drone}")
        }
    }
}

Testing πŸ§ͺ

Borg is designed for testability:

class RepositoryTest {
    @Test
    fun `test repository initialization`() = runTest {
        // Given
        val mockDb = mockk<AppDatabase>()
        val mockApi = mockk<ApiClient>()
        
        val dbDrone = object : BorgDrone<AppDatabase, Context> {
            override suspend fun assimilate(context: Context, borg: Borg<Context>) = mockDb
        }
        
        val apiDrone = object : BorgDrone<ApiClient, Context> {
            override suspend fun assimilate(context: Context, borg: Borg<Context>) = mockApi
        }
        
        val repositoryDrone = RepositoryDrone(dbDrone, apiDrone)
        
        // When
        val borg = Borg(setOf(dbDrone, apiDrone, repositoryDrone))
        borg.assimilate(mockk())
        
        // Then
        val repository = repositoryDrone.assimilate(mockk(), borg)
        assertNotNull(repository)
    }
}

Comparison with Alternatives πŸ”„

vs androidx.startup

Feature Borg androidx.startup
Dependency Resolution βœ… Automatic, type-safe with compile-time validation βœ… Automatic via dependencies() method
Parallel Initialization βœ… Automatic parallel execution of independent components ❌ Sequential execution only
Coroutine Support βœ… Native suspend function support ❌ Blocking calls only
Error Handling βœ… Structured exception hierarchy with dependency context ❌ Basic exceptions without dependency context
Thread Safety βœ… Full thread safety with deadlock prevention βœ… Basic thread safety
Initialization Caching βœ… Thread-safe result caching βœ… Component-level caching
Circular Dependency Detection βœ… Compile-time detection with clear error messages βœ… Runtime detection
Testing Support βœ… Easy to mock with direct instantiation ❌ Requires ContentProvider mocking
Lazy Initialization βœ… On-demand initialization with dependency tracking βœ… Manual lazy initialization
Configuration βœ… Runtime configuration with constructor params ❌ Manifest metadata only
Auto-initialization ❌ Manual Application.onCreate() call βœ… Automatic via ContentProvider
Library Size ❌ Larger due to coroutine support βœ… Very small footprint

When to Use What?

Choose Borg when you need:

  • Type-safe dependency management with compile-time validation
  • Maximum performance through parallel initialization
  • Async operations with coroutine support
  • Rich error context for debugging dependency issues
  • Runtime configuration flexibility
  • Direct component testing without Android dependencies
  • Complex dependency graphs with clear visualization

Choose androidx.startup when:

  • You want automatic initialization without Application class changes
  • Your initialization chain is relatively simple
  • You prefer configuration through AndroidManifest.xml
  • Minimal library footprint is critical
  • You're strictly following Android component lifecycle
  • You don't need async initialization support

vs Dagger/Hilt

Feature Borg Dagger/Hilt
Focus Initialization order Dependency injection
Learning Curve πŸ“Š Medium πŸ“ˆ Steep
Compile-time Safety βœ… Yes βœ… Yes
Initialization Control βœ… Explicit ❌ Implicit
Parallel Init βœ… Automatic ❌ Manual
Android Integration βœ… Native context βœ… Full framework

Contributing 🀝

We welcome contributions!

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License πŸ“„

This project is licensed under the Apache 2.0 License - see the LICENSE file for details.

Acknowledgments πŸ™

  • Inspired by Star Trek's Borg Collective & androidx.startup
  • Built with Kotlin Coroutines
  • Made with ❀️ by kibotu

About

A powerful, coroutine-based dependency initialization orchestrator for Android applications. Borg ensures your components are initialized in the correct order, with maximum parallelization and bulletproof thread safety.

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project

Languages

0