Need help with thrifty?
Click the “chat” button below for chat support from the developer who created it, or find similar developers for support.

About the developer

495 Stars 89 Forks Apache License 2.0 587 Commits 15 Opened issues


Thrift for Android that saves you methods

Services available


Need anything else?

Contributors list


Build status Android Arsenal codecov

Thrifty is an implementation of the Apache Thrift software stack, which uses 1/4 of the method count taken by the Apache Thrift compiler, which makes it especially appealing for use on Android.

Thrift is a widely-used cross-language service-definition software stack, with a nifty interface definition language from which to generate types and RPC implementations. Unfortunately for Android devs, the canonical implementation generates very verbose and method-heavy Java code, in a manner that is not very Proguard-friendly.

Like Square's Wire project for Protocol Buffers, Thrifty does away with getters and setters (and is-setters and set-is-setters) in favor of public final fields. It maintains some core abstractions like Transport and Protocol, but saves on methods by dispensing with Factories, omitting server implementations by default and only generating code for the protocols you actually need.

Thrifty was born in the Outlook for Android codebase; before Thrifty, generated thrift classes consumed 20,000 methods. After Thrifty, the thrift method count dropped to 5,000.


Add the runtime to your project


repositories {

// For snapshot builds maven { url '' } }

dependencies { implementation '' }

Generate code from your thrift files

On the command line:

java -jar thrifty-compiler.jar --out=path/to/output file_one.thrift file_two.thrift file_n.thrift

Or, with the Gradle plugin:

buildscript {
  dependencies {
    classpath ''

apply plugin: ''

thrifty { // Optionally configure things, see thrifty-gradle-plugin/ // for all the details }


./gradlew build


./gradlew check


We welcome contributions at all levels. Contributions could be as simple as bug reports and feature suggestions, typo fixes, additional tests, bugfixes, even new features. If you wish to contribute code, please be sure to read our Contributing Guide.

Differences with Apache Thrift

Thrifty structs and clients are 100% compatible with Apache Thrift services.

The major differences are:

  • Thrifty structs are immutable.
  • Thrifty structs are always valid, once built.
  • Fields that are neither required nor optional (i.e., "default") are treated as optional; a struct with an unset default field may still be serialized.
  • TupleProtocol is unsupported at present.
  • Server-specific features are only supported for Kotlin code generation and currently considered experimental

Guide To Thrifty

Thrift is a language-agnostic remote-procedure-call (RPC) definition toolkit. Services, along with a rich set of structured data, are defined using the Thrift Interface Definition Language (IDL). This IDL is then compiled into one or more target languages (e.g. Java), where it can be used as-is to invoke RPC methods on remote services.

Thrifty is an alternate implementation of Thrift targeted at Android usage. Its benefits over the standard Apache implementation are its greatly reduced method count and its increased type-safety. By generating immutable classes that are validated before construction, consuming code doesn't have to constantly check if required data is set or not.

Interface Definition Language

The Thrift IDL is a simple and standardized way to define data, data structures, and services:

// Let's call this example.thrift

namespace java

struct Query { 1: required string text, 2: optional i64 resultsNewerThan }

struct SearchResult { 1: required string url, 2: required list keywords = [], // A list of keywords related to the result 3: required i64 lastUpdatedMillis // The time at which the result was last checked, in unix millis }

service Google { list search(1: Query query) }

For an authoritative source on Thrift IDL, Thrift: The Missing Guide is an excellent introduction.

Generating Code


to compile IDL into Java classes:
java -jar thrifty-compiler.jar --kt-file-per-type --out=path/to/output example.thrift

The example file will result in the following files being generated:

  - com/foo/bar/
    - Google.kt
    - GoogleClient.kt
    - Query.kt
    - SearchResult.kt

The interesting files here are, of course, our domain objects


The latter looks like this:


import import import import import import import kotlin.Long import kotlin.String import kotlin.Unit import kotlin.jvm.JvmField

public data class SearchResult( @JvmField @ThriftField(fieldId = 1, isRequired = true) public val url: String,

@JvmField @ThriftField(fieldId = 2, isRequired = true) public val keywords: List,

@JvmField @ThriftField(fieldId = 3, isRequired = true) public val lastUpdatedMillis: Long ) : Struct { public override fun write(protocol: Protocol): Unit { ADAPTER.write(protocol, this) }

private class SearchResultAdapter : Adapter { // Uninteresting but important serialization code }

public companion object { @JvmField public val ADAPTER: Adapter = SearchResultAdapter() } }

The struct itself is immutable and has a minimal number of methods. It can be constructed only with all required fields (all of them, in this example). An Adapter implementation (whose body we omit here because it is long and mechanical) handles reading and writing

structs to and from

Finally and separately, note

- the former is an interface, and the latter is an autogenerated implementation.

You may notice the similarity to protobuf classes generated by Wire - this is intentional! The design principles codified there - immutable data, build-time validation, preferring fields over methods, separating data representation from serialization logic - lead to better, safer code, and more breathing room for Android applications.

Using Generated Code

Given the example above, the code to invoke
might be:
// Transports define how bytes move to and from their destination
val transport = SocketTransport.Builder("", 80).build().apply { connect() }

// Protocols define the mapping between structs and bytes val protocol = BinaryProtocol(transport)

// Generated clients do the plumbing val client = GoogleClient(protocol, object : AsyncClientBase.Listener { override fun onTransportClosed() {


override fun onError(throwable: Throwable) {
    throw AssertionError(throwable)


val query = Query(text = "thrift vs protocol buffers")

// RPC clients are asynchronous and callback-based, object : ServiceMethodCallback> { override fun onSuccess(response: List) { // yay }

override fun onError(throwable: Throwable) {
    Log.e("GoogleClient", "Search error: $throwable")


// ...unless coroutine clients were generated: val results = async { }.await()


Every project has its own requirements, and no one style of boilerplate can fill them all. Thrifty offers a small but powerful plugin model that you can implement, using the standard Java SPI mechanism, which will allow one to customize each generated class before it is written out to disk. Read more about it in the thrifty-compiler-plugins README. You can see a worked example in thrifty-example-postprocessor.

Hiding PII with Redaction and Obfuscation

Personally-Identifiable Information (PII) is an inevitability in most systems, and often there are legal consequences if it is not handled carefully. Thrifty allows you to avoid logging PII contained in generated classes by supporting both total redaction and obfuscation. It is as simple as adding annotations to your Thrift IDL:

struct User {
  1: required string email (obfuscated)
  2: required string ssn (redacted)

The difference between redaction and obfuscation is small but important. In

fields are totally replaced with the string
- no information survives. This meets the goal of not leaking PII, but has the consequence that sometimes debugging can be difficult.
fields, on the other hand, are treated differently. Their values are hashed, and this hash is printed. This allows one to distinguish between unique values in log files, without compromising user privacy.

The Thrift annotations

are also accepted by the compiler.

The Thrift example above leads to code similar to the following:

public data class User(
    @ThriftField(fieldId = 1, required = true)
    public val email: String,

@ThriftField(fieldId = 2, required = true)
public val ssn: String

) : Struct { public override fun toString() = "User(email=${ObfuscationUtil.hash(email)}, ssn=)"

// more code }

Obfuscated fields that are collections are not hashed; instead, their type is printed, along with the collection size, e.g.


Close readers will note that the compiler will also respond to

in field documentation; this is currently valid but not supported and subject to change in future releases. It is a legacy from the time before Thrifty implemented Thrift annotations.

Java Support

Thrifty generates Kotlin code by default, but if needed it can also produce Java. Generated Java code has very slightly more method references than Kotlin. Instead of data classes, Java structs are final classes with public final fields, and are constructable only with dedicated


To generate Java classes, pass

to the compiler (if using the command-line compiler), or provide a
java {}
block within the thrifty Gradle plugin configuration block.

Language-specific command-line options

Kotlin-specific command-line options

There are a few new command-line options to control Kotlin code generation:

java -jar thrifty-compiler.jar \
    --lang=kotlin \
    --service-type=coroutine \
    --kt-file-per-type \
    --omit-file-comments \
    --kt-struct-builders \
    --experimental-kt-generate-server \

By default, generated service clients are callback-based:

public interface Google {
  public fun search(query: Query, callback: ServiceMethodCallback>)

If, instead, you wish to have a coroutine-based client, specify

public interface Google {
  public suspend fun search(query: Query): List

Builders are unnecessary, and are not included by default. For compatibility with older code, you can use the

flag, which will result in Java-style classes with Builders.

By default, Thrifty generates one Kotlin file per JVM package. For larger thrift files, this can be a little hard on the Kotlin compiler. If you find build times or IDE performance suffering, the

flag can help. Outlook Mobile's single, large, Kotlin file took up to one minute just to typecheck, using Kotlin 1.2.51! For these cases,
will tell Thrifty to generate one single file per top-level class - just like the Java code.

enabled code generation for the server portion of a thrift service. You can use this to implement a thrift server with the same benefits as the kotlin client: no runtime surprises thanks to structs being always valid by having nullability guarantees and unions represented as sealed classes. See Server Support.

Server Support

Support for generating a Kotlin server implementation was only added very recently, and while it passes the 'official' Java client integration test, you should consider this code experimental.

Thrifty generates a

implementation that you pass an input
, an output
and a service handler and the code will take care of reading the request, passing it to the handler and returning the correct response to the output.

If you want to use it, you need to wrap an appropriate communication layer around it, e.g. an HTTP server. You can have a look at the integration tests for a basic example.

Java-specific command-line options

Thrifty can be made to add various kinds of nullability annotations to Java types with the

flag. Valid options are
(the default),
, and
. Specifying
will cause generated code to use
from the
package. Similarly, specifying
will use analogous annotations from


Thrifty owes an enormous debt to Square and the Wire team; without them, this project would not exist. Thanks! An equal debt is owed to Facebook and Apache for developing and opening Thrift to the world.

Copyright © Microsoft Corporation

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.