Last active
December 29, 2021 16:23
-
-
Save Moes81/85c165b6e828342e07dcfaa7ce660886 to your computer and use it in GitHub Desktop.
Some useful delegates
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package delegates | |
import java.lang.ref.SoftReference | |
import java.lang.ref.WeakReference | |
import kotlin.properties.ReadWriteProperty | |
import kotlin.reflect.KProperty | |
// Idea taken from: https://medium.com/@pgopinath/idiomatic-weakreference-implementation-by-delegate-in-kotlin-65ec60287de6 | |
fun <T>weakReference(tIn: T? = null): ReadWriteProperty<Any?, T?> { | |
return object : ReadWriteProperty<Any?, T?> { | |
var t = WeakReference<T?>(tIn) | |
override fun getValue(thisRef: Any?, property: KProperty<*>): T? { | |
return t.get() | |
} | |
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) { | |
t = WeakReference(value) | |
} | |
} | |
} | |
fun <T>softReference(tIn: T? = null): ReadWriteProperty<Any?, T?> { | |
return object : ReadWriteProperty<Any?, T?> { | |
var t = SoftReference<T?>(tIn) | |
override fun getValue(thisRef: Any?, property: KProperty<*>): T? { | |
return t.get() | |
} | |
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) { | |
t = SoftReference(value) | |
} | |
} | |
} | |
class Employee(val name: String, val id: String) | |
fun main() { | |
// This example may not work a expected, mostly because System.gc() need not trigger garbage collection. | |
// It is just a directive to the JVM but it need not trigger GC. | |
// There is no other way to simulate gc call other than triggering gc in a loop | |
run { | |
var employeeWeak: Employee? by weakReference(null) | |
employeeWeak = Employee("alok", "sjdfsdkjfdl") | |
println(employeeWeak?.name) | |
System.gc() // This may not trigger but it happens in JVM long running processes as needed. | |
println(employeeWeak?.name) // May be null if gc has been performed by the JVM. | |
} | |
run { | |
// Remember, soft reference are cleared only when there is a clear memory shortage | |
var employeeWeak: Employee? by softReference(Employee("gecko", "sjdfsdkjfd11l")) | |
println(employeeWeak?.name) | |
System.gc() // This may not trigger but it happens in JVM long running processes as needed. | |
println(employeeWeak?.name) // Need not be null. Mostly non-null | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.view.LayoutInflater | |
import android.view.View | |
import android.view.ViewGroup | |
import androidx.appcompat.app.AppCompatActivity | |
import androidx.fragment.app.DialogFragment | |
import androidx.fragment.app.Fragment | |
import androidx.lifecycle.DefaultLifecycleObserver | |
import androidx.lifecycle.Lifecycle | |
import androidx.lifecycle.LifecycleOwner | |
import androidx.viewbinding.ViewBinding | |
import kotlin.properties.ReadOnlyProperty | |
import kotlin.reflect.KProperty | |
// | |
// Using these delegates requires `androidx.lifecycle:lifecycle-common-java8` as dependency. This dependency includes | |
// the [DefaultLifecycleObserver] used in this file. | |
// Idea taken from: https://gist.github.com/gmk57/aefa53e9736d4d4fb2284596fb62710d | |
// | |
/** | |
* Activity binding delegate, may be used since onCreate up to onDestroy (inclusive). | |
* | |
* Usage: | |
* ``` | |
* class MainActivity : AppCompatActivity() { | |
* private val binding by viewBinding(ActivityMainBinding::inflate) | |
* | |
* override fun onCreate(savedInstanceState: Bundle?) { | |
* super.onCreate(savedInstanceState) | |
* setContentView(binding.root) | |
* binding.button.text = "Bound!" | |
* } | |
* } | |
* ``` | |
* | |
* */ | |
inline fun <T : ViewBinding> AppCompatActivity.viewBinding(crossinline factory: (LayoutInflater) -> T) = | |
lazy(LazyThreadSafetyMode.NONE) { | |
factory(layoutInflater) | |
} | |
/** | |
* Fragment binding delegate, may be used since onViewCreated up to onDestroyView (inclusive) | |
* | |
* Usage: | |
* ``` | |
* // Don't forget to pass layoutId in Fragment constructor | |
* class RegularFragment : Fragment(R.layout.fragment_regular) { | |
* private val binding by viewBinding(FragmentRegularBinding::bind) | |
* | |
* override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | |
* super.onViewCreated(view, savedInstanceState) | |
* binding.button.text = "Bound!" | |
* } | |
* } | |
* ``` | |
* | |
*/ | |
fun <T : ViewBinding> Fragment.viewBinding(factory: (View) -> T): ReadOnlyProperty<Fragment, T> = | |
object : ReadOnlyProperty<Fragment, T>, DefaultLifecycleObserver { | |
private var binding: T? = null | |
override fun getValue(thisRef: Fragment, property: KProperty<*>): T = | |
binding ?: factory(requireView()).also { | |
// if binding is accessed after Lifecycle is DESTROYED, create new instance, but don't cache it | |
if (viewLifecycleOwner.lifecycle.currentState.isAtLeast(Lifecycle.State.INITIALIZED)) { | |
viewLifecycleOwner.lifecycle.addObserver(this) | |
binding = it | |
} | |
} | |
override fun onDestroy(owner: LifecycleOwner) { | |
binding = null | |
} | |
} | |
/** | |
* [DialogFragment] binding delegate, may be used since onCreateView/onCreateDialog up to onDestroy (inclusive). | |
* | |
* Usage with [DialogFragment.onCreateView]: | |
* ``` | |
* private val binding by viewBinding(MyDialogFragmentBinding::inflate) | |
* | |
* override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { | |
* return viewBinding.root | |
* } | |
* ``` | |
* | |
* Usage with [DialogFragment.onCreateDialog]: | |
* ``` | |
* private val binding by viewBinding(MyDialogFragmentBinding::inflate) | |
* | |
* override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { | |
* binding.button.text = "Bound!" | |
* return AlertDialog.Builder(requireContext()).setView(binding.root).create() | |
* } | |
* | |
* ``` | |
*/ | |
inline fun <T : ViewBinding> DialogFragment.viewBinding(crossinline factory: (LayoutInflater) -> T) = | |
lazy(LazyThreadSafetyMode.NONE) { | |
factory(layoutInflater) | |
} | |
/** | |
* Not really a delegate, just a small helper for RecyclerView.ViewHolders. | |
* | |
* Prefer to encapsulate view creation & manipulation inside `ViewHolder`. | |
* In this case `BoundHolder` can be used as a superclass. | |
* | |
* Usage: | |
* ``` | |
* class Adapter3 : ListAdapter<String, Adapter3.Holder>(Differ()) { | |
* override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = Holder(parent) | |
* | |
* override fun onBindViewHolder(holder: Holder, position: Int) = holder.bind(getItem(position)) | |
* | |
* class Holder(parent: ViewGroup) : BoundHolder<ListItemBinding>(parent.viewBinding(ListItemBinding::inflate)) { | |
* fun bind(item: String) { | |
* binding.textView.text = item | |
* } | |
* } | |
* | |
* private class Differ : DiffUtil.ItemCallback<String>() { ... } | |
* } | |
* | |
* abstract class BoundHolder<T : ViewBinding>(protected val binding: T) : RecyclerView.ViewHolder(binding.root) | |
* ``` | |
*/ | |
inline fun <T : ViewBinding> ViewGroup.viewBinding(factory: (LayoutInflater, ViewGroup, Boolean) -> T) = | |
factory(LayoutInflater.from(context), this, false) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment