Installation
Basic usage
Loader
ZenEngine accepts an optional ZenLoader that serves decisions by key. Use Static, Filesystem, or Zip for common backends, or Callback for custom loading logic.
Static
Register decisions in memory. Use this for rules bundled in your app’s assets:File system
Load decisions from files under a root directory, such as internal storage. Keys resolve to paths relative to the root:Zip archive
Pass the bytes of a zip archive. Every.json entry becomes a decision keyed by its path within the archive. This pairs naturally with BRMS release ZIPs - download the release once and hand the bytes to the engine:
Custom loader
For any other backend, implementZenDecisionLoaderCallback and wrap it in ZenLoader.Callback:
null from the callback reports the key as not found.
Batch evaluation
Evaluate many requests in one call. Each result reports its own success or failure, so one bad input never fails the batch:Coroutines
Evaluation functions aresuspend functions, integrating natively with Kotlin coroutines:
Error handling
Tracing
Enable tracing to inspect decision execution:Expression utilities
Evaluate ZEN expressions outside of a decision context:Performance note
The Android bindings use JNA (Java Native Access) for interoperability with the native Rust engine. This introduces some overhead compared to native Rust or direct bindings. We plan to revisit this when the FFM (Foreign Function & Memory) API becomes more widely adopted.
Best practices
Use.use {} for resource management. ZenEngine implements AutoCloseable to release native resources.
ZenEngine instance at application startup and reuse it for all evaluations.
Cache decisions persistently. Use internal storage or SharedPreferences to cache downloaded decisions for offline use.
Evaluate on background threads. Use Dispatchers.Default or Dispatchers.IO to avoid blocking the main thread.
Bundle fallback decisions. Include decisions in assets as fallback for first launch or network failures.