All Projects → agoda-com → Boots

agoda-com / Boots

Licence: apache-2.0
Lightweight bootstrap library for your Kotlin, Java and Android apps

Programming Languages

java
68154 projects - #9 most used programming language
kotlin
9241 projects

Projects that are alternatives of or similar to Boots

Unifiedtransform
A school management Software
Stars: ✭ 2,248 (+2513.95%)
Mutual labels:  library, bootstrap
Fe
前端热门文章阅读
Stars: ✭ 174 (+102.33%)
Mutual labels:  library, bootstrap
Blazorise
Blazorise is a component library built on top of Blazor with support for CSS frameworks like Bootstrap, Bulma, AntDesign, and Material.
Stars: ✭ 2,103 (+2345.35%)
Mutual labels:  library, bootstrap
Library Management System
📚 An automated library management system developed in Laravel 4.2 PHP MVC Framework
Stars: ✭ 189 (+119.77%)
Mutual labels:  library, bootstrap
D2dlib
A .NET library for hardware-accelerated, high performance, immediate mode rendering via Direct2D.
Stars: ✭ 84 (-2.33%)
Mutual labels:  library
Startbootstrap Sb Admin 2
Start Bootstrap is an open source library of free Bootstrap templates and themes. All of the free templates and themes on Start Bootstrap are released under the MIT license, which means you can use them for any purpose, even for commercial projects.
Stars: ✭ 9,038 (+10409.3%)
Mutual labels:  bootstrap
Blank Bootstrap Edition
BL4NK Bootstrap Edition is a free template for Joomla!™ for faster and easier web development
Stars: ✭ 82 (-4.65%)
Mutual labels:  bootstrap
Shiftregister74hc595
Arduino library that simplifies the usage of shift registers
Stars: ✭ 82 (-4.65%)
Mutual labels:  library
Fann
Official github repository for Fast Artificial Neural Network Library (FANN)
Stars: ✭ 1,259 (+1363.95%)
Mutual labels:  library
Angular Full Stack
Angular Full Stack project built using Angular, Express, Mongoose and Node. Whole stack in TypeScript.
Stars: ✭ 1,261 (+1366.28%)
Mutual labels:  bootstrap
Abdk Libraries Solidity
Open-Source Libraries for Solidity by ABDK Consulting
Stars: ✭ 84 (-2.33%)
Mutual labels:  library
Materialdesign
✒6200+ Material Design Icons from the Community
Stars: ✭ 9,669 (+11143.02%)
Mutual labels:  bootstrap
Forcetouch
Simple implementation of ForceTouch on Android
Stars: ✭ 84 (-2.33%)
Mutual labels:  library
Internettools
XPath/XQuery 3.1 interpreter for Pascal with compatibility modes for XPath 2.0/XQuery 1.0/3.0, custom and JSONiq extensions, XML/HTML parsers and classes for HTTP/S requests
Stars: ✭ 82 (-4.65%)
Mutual labels:  library
Community Modules
Stars: ✭ 1,258 (+1362.79%)
Mutual labels:  bootstrap
Startbootstrap 3 Col Portfolio
A three column Bootstrap HTML portfolio template - created by Start Bootstrap
Stars: ✭ 82 (-4.65%)
Mutual labels:  bootstrap
Snowsaw
A lightweight, plugin-driven and dynamic dotfiles bootstrapper.
Stars: ✭ 83 (-3.49%)
Mutual labels:  bootstrap
Ladda Bootstrap
Ladda buttons concept originally by @hakimel, example using Bootstrap 3 by @msurguy
Stars: ✭ 1,258 (+1362.79%)
Mutual labels:  bootstrap
Digitspeedview
Awesome digital speedometer library for android
Stars: ✭ 83 (-3.49%)
Mutual labels:  library
Paris
TypeScript library for implementing Domain-Driven Design in web apps
Stars: ✭ 83 (-3.49%)
Mutual labels:  library

Boots

Bintray version Kotlin version badge codecov

Lightweight and easy-to-use bootstrap library for Java | Kotlin | Android

The problem

At Agoda, our mobile app had a huge problem with modifying and executing the initialization logic. Our Application and SplashActivity classes were more that 2k lines of undocumented and uncommented code. Every change in this classes could possibly lead to unexpected behaviour that could not be discovered by unit or ui testing.

Another problem we had is that when our app was restoring after being terminated by the system, we had to manually stop current screen and launch our SplashActivity to ensure that all our systems can operate normally.

On top of that, all this logic run mostly on the main thread, which made our TTI (time-to-interactable) as long as 4-6 seconds on a mid-range devices.

We had a strong feeling that we can change this.

Goals we want to achieve

  • Simplicity
  • Speed
  • Safety
  • Extendability

Solution

That's how we implemented Boots, the lightweight and easy-to-use bootstrap library which you can use for all of your projects running Java or Kotlin as well as Android apps.

The library allows you to easily decouple your initialization logic by splitting it into Bootables, which you can connect through simple dependency declarations. Then it's up to the library to do the rest for you.

Capabilities

  • Automatic boot task resolving (dependencies, critical bootables)
  • Runtime SCC (strong connected component) check
  • Concurrent execution
  • Time measurements
  • Simple listener callbacks
  • Custom component implementations

Usage

Making a bootable

Bootable is the base construction that is used in the library to decouple your initialization logic. Most of the library's integration process is defining your bootables so it is very important to understand it's capabilities.

abstract class Bootable {
    abstract val key: Key.Single

    open val dependencies: Key.Multiple = multiple()
    open val isConcurrent: Boolean = true
    open val isCritical: Boolean = false

    @Throws(Throwable::class)
    abstract fun boot()
}

Lets go through it's properties and see what they mean:

  • key is a unique identifier of your bootable (string id)
  • dependencies is a key that contains keys of bootables that your bootable depend on
  • isConcurrent is a flag that determines whether your bootable can be executed on a separate thread
  • isCritical is a flag that determines whether your bootable should be executed before any other non-critical. If any critical bootable fails to load, all tasks will be stopped.
  • boot() is a function of your bootable that gets invoked by the library.

Now that you know what bootable consist of, you can create your own one. But before that, you need to define a key:

object Keys {
    @JvmField val DEVICE_ID = single("device_id")
}

Keys were introduced to remove the necessity of having references on actual bootables when working with a library (especially in a modularized project). String could work as well, but keys are more flexible since they allow us to combine multiple identifiers as well as define filtering keys (Critical and All).

Now it's time to define a bootable:

class DeviceIdBootable(private val context: Context) : Bootable() {
    override val key = Keys.DEVICE_ID
    override val isCritical = true

    override fun boot() {
        val preferences = PreferenceManager.getDefaultSharedPreferences(context)

        if (!preferences.contains("device_id")) {
            preferences
                    .edit()
                    .putString("device_id", UUID.randomUUID().toString())
                    .apply()
        }
    }
}

Or you can use Kotlin DSL to define and add bootable at the same time:

Boots {
    add(Keys.DO_SOMETHING, isCritical = true) {
        doSomething()
    }
}

So, you're ready to launch your boot sequence. You can do it using simple Kotlin DSL, or plain old Java way:

Boots {
    // Adding instance of bootable to the system
    add(DeviceIdBootable(applicationContext))

    // Requesting to boot all bootables and saving listener instance
    listener = boot(all()) {
        onBoot = { /* do something */ }
    }
}
Boots.add(new DeviceIdBootable(getApplicationContext()));

listener = Boots.boot(all(), new Builder() {
    @Override
    public void onBoot(@NotNull Report report) {}

    @Override
    public void onFailure(@NotNull Report report) {}
});
Checking state and observing

To get the current status of your bootable or list of bootables, you can generate the report:

Boots {
    val report = report(all())
}
Report report = Boots.report(all());

Report contains current status of bootable or common status of list of bootables and time measurement information if the bootable has finished booting or failed.

Also you can listen for bootable events with the subscribe() functions:

Boots {
    if (report(all()).status !is Booted) {
        listener = subscribe(all()) {
            onBoot = { /* do something */ }
            onFailure = { /* do something */ }
        }
    }
}
if (!(Boots.report(all()) instanceof Booted)) {
    listener = Boots.subscribe(all(), new Builder() {
        @Override
        public void onBoot(@NotNull Report report) {}

        @Override
        public void onFailure(@NotNull Report report) {}
    });
}

IMPORTANT: default implementation of Notifier holds strong references on the Listener instances. It is user's responsibility to unsubscribe if the listener holds a reference on outer objects that need to be recycled. Holding weak references is not the best solution, since GC time is undetermined and invoking weak referenced listeners may lead to unexpected behaviour (IllegalStateExceptions in fragments, etc.).

Customization

Library provides ability to customize it's behaviour through providing custom implementation of component interfaces. The components are:

  • Executor
  • Notifier
  • Reporter
  • Sequencer
  • Logger

Each of them is responsible for a specific portion of library's behaviour and can be overridden with custom logic. Let's take RxAndroidExecutor, for example, which uses RxJava under the hood to execute your bootables and also enables context switching, so that all callbacks and non-concurrent bootables can be executed on Android's main thread:

Boots {
    configure {
        executor = RxAndroidExecutor(Schedulers.io())
    }
}
Boots.configure(new Configuration.Builder()
        .setExecutor(new RxAndroidExecutor(Schedulers.io()))
        .build());

All custom implementations are provided via additional artifacts, so that you can grab only core library with default implementation if that is fully satisfies your needs. The list of available artifacts is at the Setup section.

More

For any additional information please refer to library's documentation.

Testing

Library provides a testing artifact to help you easily mock your boot process. mockito and mockito-kotlin are used to provide you with this functionality. To mock library's logic, you can use Kotlin DSL or Java style:

Mocker {
    mock(Keys.DEVICE_ID, booting())
}
new Mocker()
        .mock(Keys.DEVICE_ID, booting())
        .apply();

When you use Mocker, it automatically mocks all interactions as Booted by default and immediately triggers all added listeners.

Setup

Maven

<!-- core library !-->
<dependency>
  <groupId>com.agoda.boots</groupId>
  <artifactId>core</artifactId>
  <version>LATEST_VERSION</version>
  <type>pom</type>
</dependency>

<!-- android logger !-->
<dependency>
  <groupId>com.agoda.boots</groupId>
  <artifactId>logger-android</artifactId>
  <version>LATEST_VERSION</version>
  <type>pom</type>
</dependency>

<!-- rx android executor !-->
<dependency>
  <groupId>com.agoda.boots</groupId>
  <artifactId>executor-rx-android</artifactId>
  <version>LATEST_VERSION</version>
  <type>pom</type>
</dependency>

<!-- coroutine executor !-->
<dependency>
  <groupId>com.agoda.boots</groupId>
  <artifactId>executor-coroutine</artifactId>
  <version>LATEST_VERSION</version>
  <type>pom</type>
</dependency>

<!-- coroutine android executor !-->
<dependency>
  <groupId>com.agoda.boots</groupId>
  <artifactId>executor-coroutine-android</artifactId>
  <version>LATEST_VERSION</version>
  <type>pom</type>
</dependency>

<!-- pure android executor !-->
<dependency>
  <groupId>com.agoda.boots</groupId>
  <artifactId>executor-android</artifactId>
  <version>LATEST_VERSION</version>
  <type>pom</type>
</dependency>

<!-- test mocker !-->
<dependency>
  <groupId>com.agoda.boots</groupId>
  <artifactId>test</artifactId>
  <version>LATEST_VERSION</version>
  <type>pom</type>
</dependency>

or Gradle:

repositories {
    jcenter()
}

dependencies {
    // core library
    implementation 'com.agoda.boots:boots:LATEST_VERSION'

    // pure android logger
    implementation 'com.agoda.boots:logger-android:LATEST_VERSION'

    // rx android executor
    implementation 'com.agoda.boots:executor-rx-android:LATEST_VERSION'

    // coroutine executor
    implementation 'com.agoda.boots:executor-coroutine:LATEST_VERSION'

    // coroutine android executor
    implementation 'com.agoda.boots:executor-coroutine-android:LATEST_VERSION'

    // pure android executor
    implementation 'com.agoda.boots:executor-android:LATEST_VERSION'

    // test mocker
    testImplementation 'com.agoda.boots:test:LATEST_VERSION'
}

Contribution Policy

Boots is an open source project, and depends on its users to improve it. We are more than happy to find you interested in taking the project forward.

Kindly refer to the Contribution Guidelines for detailed information.

Code of Conduct

Please refer to Code of Conduct document.

License

Boots is available under the Apache License, Version 2.0.

Thanks to

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].