Fortnightly Tech Info - 20181203

Weekly Tech Info - 20181203 又开始咯。

改成 Fortnightly Tech Info - 20181203。

Coroutines

非常轻量级的异步解决方案。已经成功使用在我 mvvm-android 里面,代码在github上,总结在Everything about Kotlin 里。

Sign APK elegantly with Proguard

Step 1 - Create a local file under project called “keystore.properties”

PASSWORD=xxxxxxxx
ALIAS=xxxxxxx

Step 2 - In app/build.gradle

Put there lines before android {...}

def keystorePropertiesFile = rootProject.file("./keystore.properties")
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))

And refer it in signingConfigs

signingConfigs {
    release {
        keyAlias keystoreProperties["ALIAS"]
        keyPassword keystoreProperties["PASSWORD"]
        storeFile file("../key.store")
        storePassword keystoreProperties["PASSWORD"]
    }
    debug {

    }
}

Step 3 - Yes, generate your own keystore file and put it locally (e.g.”../key.store”), don't put it in git

Step 4 - Update buildTypes

buildTypes {
    release {
        signingConfig signingConfigs.release
        minifyEnabled true
        shrinkResources true
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
    debug {
        signingConfig signingConfigs.debug
    }
}

Now release is ready to go. But I want it to go with proguard, so:

Step 5 - There is my script in proguard-rules.pro

# Remove debugging logs
-assumenosideeffects class android.util.Log {
    public static *** d(...);
    public static *** v(...);
    public static *** i(...);
    public static *** w(...);
    public static *** e(...);
    public static *** wtf(...);
}

# For Retrofit
-keep class retrofit2.** { *; }
-dontwarn retrofit2.**
-keepattributes Signature
-keepattributes Exceptions

# okio
-dontwarn okio.**

# okhttp3
-keep class okhttp3.*
-dontwarn okhttp3.**
-dontwarn com.squareup.okhttp.**

# kotlin coroutines
-keepnames class kotlinx.coroutines.internal.MainDispatcherFactory {}
-keepnames class kotlinx.coroutines.CoroutineExceptionHandler {}
-keepclassmembernames class kotlinx.** {
    volatile <fields>;
}

# Glide
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public class * extends com.bumptech.glide.module.AppGlideModule
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
**[] $VALUES;
public *;
}

##---------------Begin: proguard configuration for Gson  ----------
# Gson uses generic type information stored in a class file when working with fields. Proguard
# removes such information by default, so configure it to keep all of it.
-keepattributes Signature

# For using GSON @Expose annotation
-keepattributes *Annotation*

# Gson specific classes
-dontwarn sun.misc.**
#-keep class com.google.gson.stream.** { *; }

# Application classes that will be serialized/deserialized over Gson
-keep class com.mocoven.whatsgood.network.model.** { *; }

# Prevent proguard from stripping interface information from TypeAdapterFactory,
# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer
##---------------End: proguard configuration for Gson  ----------

Fragment and ViewModel survive when rotating

One - In Activity, make sure you have the code

if (savedInstanceState == null) {
    supportFragmentManager.beginTransaction()
        .replace(R.id.container, MainFragment.newInstance())
        .commitNow()
}

Two - In Fragment, observe viewmodel in onCreate(), and of course retainInstance = true

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    retainInstance = true
    setupViewModel()
}

override fun onActivityCreated(savedInstanceState: Bundle?) {
    super.onActivityCreated(savedInstanceState)
    viewModel.popularData.value?.data?.let { initViews(it) }
}

private fun setupViewModel() {
    viewModel = ViewModelProviders.of(this, mainViewModelFactory).get(MainViewModel::class.java)
    viewModel.popularData.observe(this, Observer { response -> processResult(response = response) })
    viewModel.getPopularData()
}