G E T S T R E A M . I O Library & SDK Lifecycles Design process 1. Define the problem 2. Research overall solutions 3. Verify the feasibility of the solutions 4. Define dependency relationships 5. Draw blueprints of the final usage in real-world projects 6. Design the interfaces
G E T S T R E A M . I O Development Strategies Minimize API surfaces class ApiResponseCallAdapter constructor( val resultType: Type ) : CallAdapter>> { override fun responseType(): Type { return resultType } override fun adapt(call: Call): Call> { return ApiResponseCallDelegate(call) } }
G E T S T R E A M . I O Development Strategies Minimize API surfaces internal class ApiResponseCallAdapter constructor( private val resultType: Type ) : CallAdapter>> { override fun responseType(): Type { return resultType } override fun adapt(call: Call): Call> { return ApiResponseCallDelegate(call) } }
G E T S T R E A M . I O Development Strategies Minimize API surfaces fun Context.dp2Px(dp: Int): Int { val scale = resources.displayMetrics.density return (dp * scale).toInt() } fun Context.px2Sp(px: Int): Int { val scale = resources.displayMetrics.scaledDensity return (px / scale).toInt() } val px = context.dp2Px(dp = 22) val sp = context.px2Sp(px = 22)
G E T S T R E A M . I O Development Strategies Minimize API surfaces internal fun Context.dp2Px(dp: Int): Int { val scale = resources.displayMetrics.density return (dp * scale).toInt() } internal fun Context.px2Sp(px: Int): Int { val scale = resources.displayMetrics.scaledDensity return (px / scale).toInt() } val px = context.dp2Px(dp = 22) // Unresolved reference val sp = context.px2Sp(px = 22) // Unresolved reference
G E T S T R E A M . I O Development Strategies Minimize API surfaces internal fun Context.dp2Px(dp: Int): Int { val scale = resources.displayMetrics.density return (dp * scale).toInt() } internal fun Context.px2Sp(px: Int): Int { val scale = resources.displayMetrics.scaledDensity return (px / scale).toInt() } int px = ContextExtensionKt.dp2Px(context, 11); int sp = ContextExtensionKt.px2Sp(context, 11);
G E T S T R E A M . I O Development Strategies Minimize API surfaces @JvmSynthetic internal fun Context.dp2Px(dp: Int): Int { val scale = resources.displayMetrics.density return (dp * scale).toInt() } @JvmSynthetic internal fun Context.px2Sp(px: Int): Int { val scale = resources.displayMetrics.scaledDensity return (px / scale).toInt() } int px = ContextExtensionKt.dp2Px(context, 11); // Unresolved reference int sp = ContextExtensionKt.px2Sp(context, 11); // Unresolved reference
G E T S T R E A M . I O Development Strategies Exposing resources /classes.jar /res/ /R.txt /public.txt /assets/ /libs/name.jar /jni/abi_name/name.so /proguard.txt /lint.jar /api.jar AAR file
G E T S T R E A M . I O Development Strategies Exposing resources /classes.jar /res/ /R.txt /public.txt /assets/ /libs/name.jar /jni/abi_name/name.so /proguard.txt /lint.jar /api.jar AAR file res/drawable res/layout res/menu res/values res/xml res/raw res/anim res/animator res/mipmap res/font string.xml colors.xml styles.xml dimens.xml arrays.xml … /res/ /res/values/
G E T S T R E A M . I O Development Strategies Exposing resources ● Some issues have been reported in Crashlytics. ● But the reports were not helpful at all. ● Occasionally the library behaves very weirdly. ● Also it’s really difficult to reproduce.
G E T S T R E A M . I O Development Strategies Exposing resources android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center" android:clipChildren="false" android:clipToPadding="false" android:padding="10dp"> android:id="@+id/text" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center_vertical" android:textColor="@android:color/white" /> … Library (layout_balloon.xml) Project (layout_balloon.xml) android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center" android:clipChildren="false" android:clipToPadding="false" android:padding="10dp"> android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="12dp" android:text="skydoves" android:textSize="16sp" android:textStyle="bold" /> ...
G E T S T R E A M . I O Development Strategies Exposing resources android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center" android:clipChildren="false" android:clipToPadding="false" android:padding="10dp"> android:id="@+id/text" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center_vertical" android:textColor="@android:color/white" /> … Library (layout_balloon.xml) // wrong behaviors Project (layout_balloon.xml) // runtime error! android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center" android:clipChildren="false" android:clipToPadding="false" android:padding="10dp"> android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="12dp" android:text="skydoves" android:textSize="16sp" android:textStyle="bold" /> ...
G E T S T R E A M . I O Development Strategies Non transitive R class :app :library :material (MDC) implementation implementation com.google.android.material.R (:material)
G E T S T R E A M . I O Development Strategies Non transitive R class :app :library :material (MDC) implementation implementation com.my.library.R (:material) (:library) com.google.android.material.R (:material)
G E T S T R E A M . I O Development Strategies Non transitive R class :app :library :material (MDC) implementation implementation com.my.library.R (:material) (:library) com.google.android.material.R (:material) com.my.app.R (:material) (:library) (:app)
G E T S T R E A M . I O Development Strategies Non transitive R class :app :library :material (MDC) implementation implementation Transitive dependency com.my.library.R (:material) (:library) com.google.android.material.R (:material) com.my.app.R (:material) (:library) (:app)
G E T S T R E A M . I O Development Strategies Non transitive R class :app :library :material (MDC) implementation implementation com.my.library.R (:material) (:library) com.google.android.material.R (:material) com.my.app.R (:material) (:library) (:app) Wow, a million of LoC in R classes in a minute!
G E T S T R E A M . I O Development Strategies Non transitive R class Dex format has 64K limit for methods and fields references! 64K (65,536 = 64 x 1024 (2^10))
G E T S T R E A M . I O Development Strategies Non transitive R class :app :library :material (MDC) implementation implementation com.my.library.R (:library) com.google.android.material.R (:material) com.my.app.R (:app) Refer to its own resources without pulling resources from dependencies.
G E T S T R E A M . I O Development Strategies Non transitive R class aar size diff 226054 bytes -> 214538 bytes (11516 bytes reduced) 10,000 projects 2260540000 bytes -> 2145380000 bytes (≈115 MB reduced) 100,000 projects 22605400000 bytes -> 21453800000 bytes (≈1.1 GB reduced) 1,000,000 projects 226054000000 bytes -> 214538000000 bytes (≈10 GB reduced) 100,000,000 end-users 22605400000000 bytes -> 21453800000000 bytes (≈1 TB reduced)
G E T S T R E A M . I O Development Strategies Non transitive R class :app :feature1 :feature2 :feature3 com.my.app.R (:feature1) (:feature2) (:feature3) (:app) Unresolved reference
G E T S T R E A M . I O Development Strategies Non transitive R class :app :feature1 :feature2 :feature3 com.my.app.R (:feature1) (:feature2) (:feature3) (:app) full name package import with namespacing
G E T S T R E A M . I O Release Maven central repository 1. Registering a Sonatype account 2. Generating a GPG key pair 3. Setting up publication in your project a. Maven publishing setup with Gradle b. Signing artifacts with GPG key c. Configure pom files d. Per-module Gradle setup 4. Publish to sonatype repository 5. Evaluate and close the ticket on staging repositories