Jet_Demo

2020/06/22

Jetpack Demo

1. Demo GitHub

  1. 运用Jetpack技术点:ViewModel, ViewModelProvider.Factory, AbstractSavedStateViewModelFactory, Room, LiveData, MVVM, bindAdapter, lifecycleOwner, Navigate, WorkManager
  2. demo下载

2. Demo流程

1. DataBase:Dao + Entity + Repository——对plant的增删改查

  1. AppDataBase

    @Database(entities = [GardenPlanting::class, Plant::class], version = 1, exportSchema = false)
    @TypeConverters(Converters::class)
    abstract class AppDatabase : RoomDatabase() {
    		abstract fun gardenPlantingDao(): GardenPlantingDao
        abstract fun plantDao(): PlantDao
           
        private fun buildDatabase(context: Context): AppDatabase {
        return Room.databaseBuilder(context, AppDatabase::class.java, DATABASE_NAME)
                    .addCallback(object: RoomDatabase.Callback(){
                        override fun onCreate(db: SupportSQLiteDatabase) {
                            super.onCreate(db)
                        }
                    })
                    .build()
        }
    }
    
  2. @Entity(tableName = "plants")

  3. @Dao interface PlantDao SQL语句拼接增删改查接口

  4. data class 数据类,连接VM与DB

  5. 对外接口数据操作类

// 这是一个对GardenPlanting 表的操作类
class GardenPlantingRepository private constructor(
    // 私有构造函数
    private val gardenPlantingDao: GardenPlantingDao
) {

    suspend fun createGardenPlanting(plantId: String) {
        val gardenPlanting = GardenPlanting(plantId)
        gardenPlantingDao.insertGardenPlanting(gardenPlanting)
    }

    suspend fun removeGardenPlanting(gardenPlanting: GardenPlanting) {
        gardenPlantingDao.deleteGardenPlanting(gardenPlanting)
    }

    fun isPlanted(plantId: String) = gardenPlantingDao.isPlanted(plantId)

    fun getPlantedGardens() = gardenPlantingDao.getPlantedGardens()

}

2. WorkManager

  1. 将数据内置到assets/plant.json文件中

  2. 创建一个CoroutineWorker,在子线程中执行文件读取和DB操作

    class SeedDatabaseWorker(
        context: Context,
        workerParams: WorkerParameters
    ): CoroutineWorker(context, workerParams) {
        // 实现doWork
        override suspend fun doWork(): Result = coroutineScope {
            try {
                // 这里就是子线程读取文件,然后插入DB
                applicationContext.assets.open(PLANT_DATA_FILENAME).use { inputStream ->
                    // JsonReader 是com.google.gson.stream.JsonReader
                    JsonReader(inputStream.reader()).use { jsonReader ->
                        val plantType = object : TypeToken<List<Plant>>() {}.type
                        val plantList: List<Plant> = Gson().fromJson(jsonReader, plantType)
                        val database = AppDatabase.getInstance(applicationContext)
                        database.plantDao().insertAll(plantList)
                        Log.e(TAG, "jsonReader :" + plantList.toString())
                        Result.success()
                    }
                }
            } catch (ex: Exception) {
                // 有个异常,没加PLANT_DATA_FILENAME json文件
                Log.e(TAG, "jsonReader222 :", ex)
                Result.failure()
            }
        }
       
        companion object {
            private const val TAG = "SeedDatabaseWorker"
        }
    }
    
  3. 生成DB时,执行后台任务

     private fun buildDatabase(context: Context): AppDatabase {
                return Room.databaseBuilder(context, AppDatabase::class.java, DATABASE_NAME)
                    .addCallback(object: RoomDatabase.Callback(){
                        override fun onCreate(db: SupportSQLiteDatabase) {
                            super.onCreate(db)
                            // 子线程操作DB
                            val request = OneTimeWorkRequestBuilder<SeedDatabaseWorker>().build()
                            WorkManager.getInstance(context).enqueue(request)
                        }
                    })
                    .build()
            }
    

3. ViewModel 和 Factory

  1. VM 结合

    class GardenPlantingListViewModel internal constructor(
        gardenPlantingRepository: GardenPlantingRepository
    ) : ViewModel() {
        val plantAndGardenPlantings: LiveData<List<PlantAndGardenPlantings>> =
            gardenPlantingRepository.getPlantedGardens()
    }
    
  2. Factory

    class GardenPlantingListViewModelFactory(
        private val repository: GardenPlantingRepository
    ) : ViewModelProvider.Factory {
        @Suppress("UNCHECKED_CAST")
        override fun <T : ViewModel?> create(modelClass: Class<T>): T {
            return GardenPlantingListViewModel(repository) as T
        }
    }
    
  3. 在Manager类中奖VM与Factory进行整合

    // 获取我的花园 种植的植物 工厂
        fun provideGardenPlantingListVieModelFactory(context: Context): GardenPlantingListViewModelFactory {
            val repository = getGardenPlantingRepository(context)
            return GardenPlantingListViewModelFactory(
                repository
            )
        }
       
        // 获取种植的花园植物 Dao
        private fun getGardenPlantingRepository(context: Context): GardenPlantingRepository {
            return GardenPlantingRepository.getInstance(
                AppDatabase.getInstance(context.applicationContext).gardenPlantingDao()
            )
        }
    

4. Layout

  1. build.gradle中添加

    android {
    		dataBinding {
            enabled =true //允许使用dataBinding
        }
     }
    
  2. xml布局中添加 , 使用变量 app:isGone="@{viewModel.isPlanted}"

    <layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools">
       
        <data>
            <variable
                    name="hasPlantings"
                    type="boolean" />
        </data>
       
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:orientation="vertical"
            app:isGone="@{hasPlantings}">
       
            <TextView
                android:id="@+id/empty_garden"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/garden_empty"
                android:textAppearance="?attr/textAppearanceHeadline5" />
          </LinearLayout>
    </layout>
       
    

5. Fragment + Adapter

  1. Adapter中新增绑定layout中事件

    @BindingAdapter("isGone")
    fun bindIsGone(view: View, isGone: Boolean) {
        view.visibility = if (isGone) {
            View.GONE
        } else {
            View.VISIBLE
        }
    }
    
  2. Fragment中使用binding和Adapter

    override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
        ): View? {
            binding = FragmentGardenBinding.inflate(inflater, container, false)
            val adapter = GardenPlantingAdapter()
            binding.gardenList.adapter = adapter
            binding.addPlant.setOnClickListener {
                navigateToPlantListPage()
            }
            subscribeUI(adapter, binding)
            return binding.root
        }
    
  3. 加载VM中的数据

    // 加载数据
        private val viewModel: GardenPlantingListViewModel by viewModels {
            GardenPlantManager.provideGardenPlantingListVieModelFactory(
                requireContext()
            )
        }
    

6. Navigation

  1. Home页Layout

    <?xml version="1.0" encoding="utf-8"?>
    <layout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto">
       
        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
       
            <fragment
                android:id="@+id/nav_host"
                android:name="androidx.navigation.fragment.NavHostFragment"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                app:defaultNavHost="true"
                app:navGraph="@navigation/nav_garden"/>
       
        </FrameLayout>
       
    </layout>
    
  2. Home页加载UI

    override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView<ActivityMainBinding>(this, R.layout.activity_main)
        }
    
  3. 在res/navigation/nav_garden.xml中添加跳转逻辑

    <navigation xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        app:startDestination="@id/view_pager_fragment">
       
        <fragment
            android:id="@+id/view_pager_fragment"
            android:name="com.lq.he.sum.home.HomeViewPagerFragment"
            >
       
            <action
                    android:id="@+id/action_view_pager_fragment_to_plant_detail_fragment"
                    app:destination="@id/plant_detail_fragment"
                    app:enterAnim="@anim/slide_in_right"
                    app:exitAnim="@anim/slide_out_left"
                    app:popEnterAnim="@anim/slide_in_left"
                    app:popExitAnim="@anim/slide_out_right" />
       
        </fragment>
       
        <fragment
            android:id="@+id/plant_detail_fragment"
            android:name="com.lq.he.sum.pdetail.PlantDetailFragment"
            tools:layout="@layout/fragment_plant_detail"
            android:label="@string/plant_details_title">
            <argument
                android:name="plantId"
                app:argType="string" />
        </fragment>
       
    </navigation>
       
    会自动生成
    class HomeViewPagerFragmentDirections private constructor() {
      private data class ActionViewPagerFragmentToPlantDetailFragment(
        val plantId: String
      ) : NavDirections {
        override fun getActionId(): Int = R.id.action_view_pager_fragment_to_plant_detail_fragment
       
        override fun getArguments(): Bundle {
          val result = Bundle()
          result.putString("plantId", this.plantId)
          return result
        }
      }
       
      companion object {
        fun actionViewPagerFragmentToPlantDetailFragment(plantId: String): NavDirections =
            ActionViewPagerFragmentToPlantDetailFragment(plantId)
      }
    }
    
  4. 点击跳转

    private fun navigateToPlant(plantId: String, view: View) {
                val direction = HomeViewPagerFragmentDirections.actionViewPagerFragmentToPlantDetailFragment(plantId)
                // 找到的Navigation控制器,然后导航到相应页面,但direction其实就是一个fragment
                view.findNavController().navigate(direction)
            }
    

文内导航