Jetpack Demo
1. Demo GitHub
- 运用Jetpack技术点:
ViewModel, ViewModelProvider.Factory, AbstractSavedStateViewModelFactory, Room, LiveData, MVVM, bindAdapter, lifecycleOwner, Navigate, WorkManager
等 - demo下载
2. Demo流程
1. DataBase:Dao + Entity + Repository——对plant的增删改查
-
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() } }
-
@Entity(tableName = "plants")
-
@Dao interface PlantDao
SQL语句拼接增删改查接口 -
data class
数据类,连接VM与DB -
对外接口数据操作类
// 这是一个对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
-
将数据内置到assets/plant.json文件中
-
创建一个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" } }
-
生成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
-
VM 结合
class GardenPlantingListViewModel internal constructor( gardenPlantingRepository: GardenPlantingRepository ) : ViewModel() { val plantAndGardenPlantings: LiveData<List<PlantAndGardenPlantings>> = gardenPlantingRepository.getPlantedGardens() }
-
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 } }
-
在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
-
在
build.gradle
中添加android { dataBinding { enabled =true //允许使用dataBinding } }
-
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
-
Adapter中新增绑定layout中事件
@BindingAdapter("isGone") fun bindIsGone(view: View, isGone: Boolean) { view.visibility = if (isGone) { View.GONE } else { View.VISIBLE } }
-
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 }
-
加载VM中的数据
// 加载数据 private val viewModel: GardenPlantingListViewModel by viewModels { GardenPlantManager.provideGardenPlantingListVieModelFactory( requireContext() ) }
6. Navigation
-
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>
-
Home页加载UI
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView<ActivityMainBinding>(this, R.layout.activity_main) }
-
在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) } }
-
点击跳转
private fun navigateToPlant(plantId: String, view: View) { val direction = HomeViewPagerFragmentDirections.actionViewPagerFragmentToPlantDetailFragment(plantId) // 找到的Navigation控制器,然后导航到相应页面,但direction其实就是一个fragment view.findNavController().navigate(direction) }