Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Android Architecture Components

Android Architecture Components

Apresentada no DevFest Sul de Minas 2018

Rafael Toledo

November 24, 2018
Tweet

More Decks by Rafael Toledo

Other Decks in Programming

Transcript

  1. Lifecycle Componentes de lifecycle realizam ações reagindo a eventos de

    ciclo de vida de componentes do Android. Eles ajudam a escrever código mais fáceis de ler e manter e, frequentemente, mais enxutos.
  2. internal class MyLocationListener( private val context: Context, private val callback:

    (Location) -> Unit ) { fun start() { // connect to system location service } fun stop() { // disconnect from system location service } }
  3. class MyActivity : AppCompatActivity() { private lateinit var myLocationListener: MyLocationListener

    override fun onCreate(...) { myLocationListener = MyLocationListener(this) { location -> // update UI } } ... }
  4. class MyActivity : AppCompatActivity() { ... public override fun onStart()

    { super.onStart() myLocationListener.start() // manage other components that need to respond // to the activity lifecycle } public override fun onStop() { super.onStop() myLocationListener.stop() // manage other components that need to respond // to the activity lifecycle } }
  5. public override fun onStart() { super.onStart() Util.checkUserStatus { result ->

    // e se esse callback for chamado DEPOIS que a activity encerrar? if (result) { myLocationListener.start() } } }
  6. class MyObserver : LifecycleObserver { @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) fun connectListener() { ...

    } @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) fun disconnectListener() { ... } }
  7. class MyActivity : AppCompatActivity() { private lateinit var myLocationListener: MyLocationListener

    override fun onCreate(...) { myLocationListener = MyLocationListener(this, lifecycle) { location -> // update UI } Util.checkUserStatus { result -> if (result) { myLocationListener.enable() } } } }
  8. internal class MyLocationListener(private val context: Context, private val lifecycle: Lifecycle,

    private val callback: (Location) -> Unit) { private var enabled = false @OnLifecycleEvent(Lifecycle.Event.ON_START) fun start() { if (enabled) { // connect } } fun enable() { enabled = true if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) { // connect if not connected } } @OnLifecycleEvent(Lifecycle.Event.ON_STOP) fun stop() { // disconnect if connected } }
  9. internal class MyLocationListener(private val context: Context, private val lifecycle: Lifecycle,

    private val callback: (Location) -> Unit) { private var enabled = false @OnLifecycleEvent(Lifecycle.Event.ON_START) fun start() { if (enabled) { // connect } } fun enable() { enabled = true if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) { // connect if not connected } } @OnLifecycleEvent(Lifecycle.Event.ON_STOP) fun stop() {
  10. LiveData É uma classe observável que guarda dados, porém, diferentemente

    de um observable comum, ela respeita eventos de ciclo de vida de outros componentes, como Activity, Fragment e Services.
  11. class NameViewModel : ViewModel() { // Create a LiveData with

    a String val currentName: MutableLiveData<String> by lazy { MutableLiveData<String>() } // Rest of the ViewModel... }
  12. class NameViewModel : ViewModel() { // Create a LiveData with

    a String val currentName: MutableLiveData<String> by lazy { MutableLiveData<String>() } // Rest of the ViewModel... }
  13. class NameActivity : AppCompatActivity() { private lateinit var mModel: NameViewModel

    override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Get the ViewModel. mModel = ViewModelProviders.of(this).get(NameViewModel::class.java) // Create the observer which updates the UI. val nameObserver = Observer<String> { newName -> // Update the UI, in this case, a TextView. mNameTextView.text = newName } // Observe the LiveData, passing in this activity as the LifecycleOwner and the observer. mModel.currentName.observe(this, nameObserver) } }
  14. // Estendendo LiveData class StockLiveData(symbol: String) : LiveData<BigDecimal>() { private

    val mStockManager = StockManager(symbol) private val mListener = { price: BigDecimal -> value = price } override fun onActive() { mStockManager.requestPriceUpdates(mListener) } override fun onInactive() { mStockManager.removeUpdates(mListener) } }
  15. ViewModel É uma classe projetada para guardar e gerenciar dados

    relativos a UI de uma forma que permita aos dados sobreviverem a mudanças de configuração, como por exemplo a mudança de orientação
  16. class MyViewModel : ViewModel() { private lateinit var users: MutableLiveData<List<User>>

    fun getUsers(): LiveData<List<User>> { if (!::users.isInitialized) { users = MutableLiveData() loadUsers() } return users } private fun loadUsers() { // Faça alguma tarefa assíncrona para obter os usuários. } }
  17. class MyActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) {

    // Cria um ViewModel da primeira vez que o método onCreate() // da Activity for chamado. Activities recriadas recebem // a mesma instância de MyViewModel criada pela primeira Activity. val model = ViewModelProviders.of(this).get(MyViewModel::class.java) model.getUsers().observe(this, Observer<List<User>>{ users -> // atualiza a UI }) } }
  18. class MasterFragment : Fragment() { private lateinit var itemSelector: Selector

    private lateinit var model: SharedViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) model = activity?.run { ViewModelProviders.of(this).get(SharedViewModel::class.java) } ?: throw Exception("Activity inválida") itemSelector.setOnClickListener { item -> // Atualiza a UI } } }
  19. class DetailFragment : Fragment() { private lateinit var model: SharedViewModel

    override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) model = activity?.run { ViewModelProviders.of(this).get(SharedViewModel::class.java) } ?: throw Exception("Activity inválida") model.selected.observe(this, Observer<Item> { item -> // Atualiza a UI }) } }
  20. Room Room é uma biblioteca de persistência de dados que

    fornece uma camada de abstração sobre o SQLite, permitindo o acesso de dados robusto, ao mesmo tempo em que permite utilizar todo o poder do banco de dados SQLite
  21. @Entity data class User( @PrimaryKey var uid: Int, @ColumnInfo(name =

    "first_name") var firstName: String?, @ColumnInfo(name = "last_name") var lastName: String? )
  22. @Dao interface UserDao { @Query("SELECT * FROM user") fun getAll():

    List<User> @Query("SELECT * FROM user WHERE uid IN (:userIds)") fun loadAllByIds(userIds: IntArray): List<User> @Query("SELECT * FROM user WHERE first_name LIKE :first AND " + "last_name LIKE :last LIMIT 1") fun findByName(first: String, last: String): User ... }
  23. @Database(entities = arrayOf(User::class), version = 1) abstract class AppDatabase :

    RoomDatabase() { abstract fun userDao(): UserDao } val db = Room.databaseBuilder( applicationContext, AppDatabase::class.java, "database-name" ).build()
  24. Room - outras features - Migrations - FTS (Full Text

    Search) - a partir da versão 2.1 - Paginação - em conjunto com a Paging
  25. Paging A Paging Library fornece APIs para o carregamento progressivo

    de dados em um RecyclerView. Os dados podem ser advindos tanto de uma fonte de dados locais (banco de dados), quanto remotos (API)
  26. class ConcertViewModel { fun search(query: String): ConcertSearchResult { val boundaryCallback

    = ConcertBoundaryCallback(query, myService, myCache) // Use a LiveData object to communicate your network's state back // to your app's UI, as in the following example. Note that error // handling isn't shown in this snippet. // val loadingState: LiveData<MyNetworkState> = // boundaryCallback.loadingState } }
  27. class ConcertBoundaryCallback( private val query: String, private val service: MyService,

    private val cache: MyLocalCache ) : PagedList.BoundaryCallback<Concert>() { // Requests initial data from the network, replacing all content currently // in the database. override fun onZeroItemsLoaded() { requestAndReplaceInitialData(query) } // Requests additional data from the network, appending the results to the // end of the database's existing data. override fun onItemAtEndLoaded(itemAtEnd: Concert) { requestAndAppendData(query, itemAtEnd.key) }
  28. @Dao interface ConcertDao { // The Int type parameter tells

    Room to use a PositionalDataSource // object, with position-based loading under the hood. @Query("SELECT * FROM concerts ORDER BY date DESC") fun concertsByDate(): DataSource.Factory<Int, Concert> }
  29. class ConcertViewModel(concertDao: ConcertDao) : ViewModel() { val concertList: LiveData<PagedList<Concert>> =

    LivePagedListBuilder( concertDao.concertsByDate(), /* page size */ 20).build() }
  30. class ConcertActivity : AppCompatActivity() { public override fun onCreate(savedInstanceState: Bundle?)

    { super.onCreate(savedInstanceState) val viewModel = ViewModelProviders.of(this) .get(ConcertViewModel::class.java!!) val recyclerView = findViewById(R.id.concert_list) val adapter = ConcertAdapter() viewModel.concertList.observe(this, { pagedList -> adapter.submitList(pagedList) }) recyclerView.setAdapter(adapter) } }
  31. class ConcertAdapter() : PagedListAdapter<Concert, ConcertViewHolder>(DIFF_CALLBACK) { fun onBindViewHolder(holder: ConcertViewHolder, position:

    Int) { val concert = getItem(position) if (concert != null) { holder.bindTo(concert) } else { // Null defines a placeholder item - PagedListAdapter automatically // invalidates this row when the actual object is loaded from the // database. holder.clear() } } ... }
  32. class ConcertAdapter() : PagedListAdapter<Concert, ConcertViewHolder>(DIFF_CALLBACK) { ... companion object {

    private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<Concert>() { // Concert details may have changed if reloaded from the database, // but ID is fixed. override fun areItemsTheSame(oldConcert: Concert, newConcert: Concert): Boolean = oldConcert.id == newConcert.id override fun areContentsTheSame(oldConcert: Concert, newConcert: Concert): Boolean = oldConcert == newConcert } }
  33. Navigation O Navigation Architecture Component simplifica a implementação de navegação

    entre "destinos" do seu aplicativo. Um conjunto de "destinos" compõe o grafo de navegação do seu aplicativo.
  34. <?xml version="1.0" encoding="utf-8"?> <navigation xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android" app:startDestination="@id/blankFragment"> <fragment android:id="@+id/blankFragment"

    android:name="com.example.cashdog.cashdog.BlankFragment" android:label="fragment_blank" tools:layout="@layout/fragment_blank" > <action android:id="@+id/action_blankFragment_to_blankFragment2" app:destination="@id/blankFragment2" /> </fragment> <fragment android:id="@+id/blankFragment2" android:name="com.example.cashdog.cashdog.BlankFragment2" android:label="fragment_blank_fragment2" tools:layout="@layout/fragment_blank_fragment2" /> </navigation>
  35. WorkManager O WorkManager é uma API que facilita a criação

    de tarefas assíncronas e condicionar a execução delas. Você pode criar uma task e decidir se ela deve ser executada imediatamente ou após determinado período ou condição.
  36. // Agendamento Simples - com parâmetros val firstWork = OneTimeWorkRequestBuilder<FirstWorker>()

    .setInputData(Data.Builder() .putString("key", "value") .build()) .build() WorkManager.getInstance().enqueue(firstWork)
  37. // Agendamento Simples - com parâmetros val firstWork = OneTimeWorkRequestBuilder<FirstWorker>()

    .setInputData(mapOf("value" to "key").toWorkData()) .build() WorkManager.getInstance().enqueue(firstWork) // Dentro do método doWork() inputData.getString("key", "Default Value")
  38. // Agendamento com Constraints val constraints = Constraints.Builder() .setRequiredNetworkType(NetworkType.CONNECTED) .setRequiresCharging(true)

    .setRequiresBatteryNotLow(true) .build() val firstWork = OneTimeWorkRequestBuilder<FirstWorker>() .setConstraints(constraints) .setInputData(mapOf("value" to "key").toWorkData()) .build()
  39. // Encadeamento de Tasks val firstWork = OneTimeWorkRequestBuilder<FirstWorker>() .build() val

    secondWork = OneTimeWorkRequestBuilder<SecondWorker>() .build() WorkManager.getInstance().beginWith(firstWork) .then(secondWork).enqueue()
  40. WorkManager - é executado após o sistema obter um wakelock

    - não é necessário requisitar - é executado numa thread em background - não deve iniciar nenhuma nova thread - deve retornar um status - não pode rodar pra sempre - ~10 minutos
  41. DataBinding A DataBinding é uma biblioteca de suporte que permite

    fazer a ligação de componentes de UI com fontes de dados de forma declarativa, em vez de programaticamente.