Android&Jetpack

狗熊镇楼

什么是 Android Jetpack?Android Jetpack 是一套组件、工具和指导,可以帮助您构建出色的 Android 应用


  • google爸爸
    官方图片
  • Architecture:体系结构
  • Foundation:基础
  • UI:viewhi
  • Behavior:行为
  • 每个组件都可以单独采用和构建,以保持向后兼容性
  • Android体系结构组件非常的模块化,我们就能够选择我们想要的使用我们程序的功能集
    对生命周期有意识的观察者和可观察组件,允许关注点的分离,从而方便测试,提供可靠的高质量应用程序
  • 充分利用了Koltin语言功能,使工作效率更加的高
  • 数据库持久性一直是Android的噩梦,而Room是个巨大的改进
    Room:可测试性:能够在持久层上添加测试覆盖,而不需要构建任何基础架构
  • 借助Android Jetpack 消除了所有类型的问题
    使代码更加容易的维护
  • 所有组件都可以在旧版本使用

  • Android Jetpack 基于现代化设计做法构建,其中包括问题与可测试性分离,以及 Kotlin 集成等可以提高效率的功能。这让您可以更轻松地构建稳定、优质的应用,同时减少代码数量。虽然 Android Jetpack 的组件为了协同操作而构建(例如,生命周期感知和实时数据),但是您不必使用所有组件。您可以集成能够解决您的问题的 Android Jetpack 部分,同时保留您的应用中运行出色的部分。

  • Android Jetpack 附带五个新组件
  • WorkManager alpha 版
    主要来约束后台作业(需要有保障的执行)提供一站式解决的方案,消除了使用作业或者异步Adapter的架构需求,主要是结合在 OneTimeWorkRequestBuilder 使用,而SeedDatabaseWorker其实CoroutineWorker 就是协程
 val request = OneTimeWorkRequestBuilder<SeedDatabaseWorker>().build()
  //将一个或多个项目排队进行后台处理。
   WorkManager.getInstance(context).enqueue(request)
  • 导航 navigation alpha 版,其实就是在res文件下创建一个navgation目录
    image.png

    在以前Activity作为系统的应用程序入口,但是在分享数据和转场方面表现的不够灵活(我觉得还好)。这个导航组件重点就是让单Activity应用成为架构的首选,利用导航组件对Fragment的原生支持,可以获得架构组件所有的好处:生命周期和ViewModel。导航组件可以声明转场,同时在最新版的 Android Studio 3.2可以很直白的看到和管理导航属性
    image.png
  • 个人的理解就是包裹着Fragment,同时指定了跳转的方法



    官方的Demo
    • 分页 Paging
      应用中呈现的数据可能非常大,这就导致加载的开销比较大,因此,避免一次下载、创建或呈现过多数据就显得非常重要。分页组件1.0.0 版让您可以轻松加载和呈现大型数据集,同时在您的 RecyclerView 中进行快速、无限滚动。它可以从本地存储和/或网络加载分页数据,并让您能够定义内容的加载方式。此组件原生支持 Room、LiveData 和 RxJava。
      分页库的关键组件是 PagedList 类,它分块加载应用程序的数据或页面。当需要更多数据时,会被加载到已有的 PagedList 对象当中。 如果任何加载的数据发生更改,则会从 LiveData 或基于 RxJava2 的对象向可观察数据持有者发射新的 PagedList 实例。PagedList 对象一旦生成,应用的 UI 就会展示内容,同时遵循 UI 控制器的 生命周期
  • 切片
    切片”是一种以搜索结果形式在 Google 智能助理内部显示应用界面的方式 ! 我自己写的一篇文章Android 9 Pie 正式版总结,有去分析这个切片的过程
  • Android KTX
    Android KTX 可以让您将类似下面所示的 Kotlin 代码:

    转换成如下所示的更精简的 Kotlin 代码:
    view.doOnPreDraw { actionToBeTriggered() }

对官方的android-sunflower的解读

  • 1、入口GardenActivity类,明显的单Actvitity+多Fragment的应用
        val binding: ActivityGardenBinding = DataBindingUtil.setContentView(this,
                R.layout.activity_garden)
        drawerLayout = binding.drawerLayout
        mLayout= binding.mLayout
         //查找给定视图ID及其包含的[NavController]
        navController = findNavController(R.id.garden_nav_fragment)
        // navController.graph  获取与此导航控制器关联的最顶层导航图。
        appBarConfiguration = AppBarConfiguration(navController.graph, drawerLayout)

        // Set up ActionBar
        setSupportActionBar(binding.toolbar)
        setupActionBarWithNavController(navController, appBarConfiguration)

        // Set up navigation menu
        binding.navigationView.setupWithNavController(navController)

  • 在布局中最关键的布局 ,指向了 app:navGraph="@navigation/nav_garden"
  <fragment
                android:id="@+id/garden_nav_fragment"
                android:name="androidx.navigation.fragment.NavHostFragment"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                app:defaultNavHost="true"
                app:navGraph="@navigation/nav_garden" />
  • 2、navigation/nav_garden,新的一个导航
    把所有的Fragment放在一起,指明了转场的动画和方式,目的的Fragment的全包名的路径,同时指定了Fragment 的layout的文件,
<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/garden_fragment">
     <!--把所有的 Fragment 定义一起啊  然后制定了第一个Fragment的Id  -->
    <fragment
        android:id="@+id/garden_fragment"
        android:name="com.google.samples.apps.sunflower.GardenFragment"
        android:label="@string/my_garden_title"
        tools:layout="@layout/fragment_garden">
         <!--destination 目的地-->
        <action
            android:id="@+id/action_garden_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_list_fragment"
        android:name="com.google.samples.apps.sunflower.PlantListFragment"
        android:label="@string/plant_list_title"
        tools:layout="@layout/fragment_plant_list">

        <action
            android:id="@+id/action_plant_list_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.google.samples.apps.sunflower.PlantDetailFragment"
        android:label="@string/plant_details_title"
        tools:layout="@layout/fragment_plant_detail">
        <argument
            android:name="plantId"
            app:argType="string" />
    </fragment>

</navigation>
  • 3、Fragment 的layout的文件
    在布局文件中指明了这个页面我需要什么的数据和数据类型
   <data>

        <variable
                name="hasPlantings"
                type="boolean" />

    </data>

同时在相对于的View上去引用这个方法,当然这个方法是自定义的,比如以下布局
RecyclerView可以指定LinearLayoutManager,而不用去代码中使用,也可以指定item的布局

 <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/garden_list"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:clipToPadding="false"
                android:paddingLeft="@dimen/margin_normal"
                android:paddingRight="@dimen/margin_normal"
                app:isGone="@{!hasPlantings}"
                app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
                tools:listitem="@layout/list_item_garden_planting"/>

        <TextView
                android:id="@+id/empty_garden"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:gravity="center"
                android:text="@string/garden_empty"
                android:textSize="24sp"
                app:isGone="@{hasPlantings}"/>

注意观察这个方法app:isGone,其实需要自己去定义,具体使用如下

@BindingAdapter("isGone")
fun bindIsGone(view: View, isGone: Boolean) {
    view.visibility = if (isGone) {
        View.GONE
    } else {
        View.VISIBLE
    }
}

方便简洁

  • 4、如何自定义去赋值布局文件
    首先定义一个tip类型为String的,我先说一个坑,这是我自己创建,不是上个页面传递给的,所以这个tip为null

  • 在布局中去引用


  • 代码中去Binding


  • 最后的结果


  • 如上面我们所说的问题 setTip为null

    image.png

所以我们的定义方法的时候,就要告诉编译期我们的setTip有可能为null ,setTip: String?,要不然就会报错!

  • 5、在GardenFragment中代码简洁的让人难以置信
class GardenFragment : Fragment() {

    private val viewModel: GardenPlantingListViewModel by viewModels {
        InjectorUtils.provideGardenPlantingListViewModelFactory(requireContext())
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val binding = FragmentGardenBinding.inflate(inflater, container, false)
        val adapter = GardenPlantingAdapter()
        binding.gardenList.adapter = adapter
        subscribeUi(adapter, binding)
        return binding.root
    }

    private fun subscribeUi(adapter: GardenPlantingAdapter, binding: FragmentGardenBinding) {
        viewModel.gardenPlantings.observe(viewLifecycleOwner) { plantings ->
            binding.hasPlantings = !plantings.isNullOrEmpty()
        }

        viewModel.plantAndGardenPlantings.observe(viewLifecycleOwner) { result ->
            if (!result.isNullOrEmpty())
                adapter.submitList(result)
        }
    }
}
  • 6、kotlin中定义工具类非常的简单,使用object修饰就可以了


    内部调用的就用private去修饰,不是的话,就去掉这个关键字就行了

  • 7、LiveData

LiveData是一个可以在给定生命周期内观察到的数据持有者类。

  • LiveData是一个可观测数据的容器类。与普通的可观测类不同,LiveData 能感知生命周期,也就意味着它合乎 activity、fragment 或 service 等其他应用组件的生命周期。因此,LiveData 确保只在这些可观测的应用组件处于活动状态的时候才会更新它们。

  • 使用 LiveData 的好处

  • 确保您的 UI 和数据的状态保持一致,UI 的代码放到这些 Observer 的观测者对象中,从而让代码更稳健

  • 没有内存泄漏,观测者受 LifeCycle 的作用域限制,而且还会在与其关联的生命周期被销毁后自动清理自己。

  • 不会因为 activity 停止而导致应用崩溃,如果观测者的生命周期处于非活动状态,例如在回退栈(back stack)里的 activity,那么它就不会收到任何 LiveData 的事件。

  • 无须手动管理生命周期,UI 组件只观测相关的数据,无须手动开始或停止。LiveData 会自动处理好这一切,因为它能在观测的同时,感知到相关的生命周期状态变动。

  • 总是最新的数据,即使一个生命周期进入非活动状态,在它恢复活动状态的时候仍能接收最新的数据。例如,一个后台的 activity 在回到前台的时刻就会立即接收到最新的数据。

  • 安稳度过配置变更(configuration changes,如果一个 activity 或 fragment 因屏幕旋转等配置变更而被重建,它仍能立即接收到最新的可用的数据。

  • 分享资源,您可以使用单例模式来继承一个 LiveData 对象来把系统服务包装起来,因而使其能够分享给整个应用

  • 8、关于项目中的单利模式实现
    在读它项目的代码的时候,我发现它的实现的方式和我实际在项目中使用的不一样,使用了Volatile关键字和同步锁。

    单利

  • volatile关键字 Java多线程的三大核心
  • 1、 原子性 :java原子性和数据库事务的原子性差不多,一个操作要么是全部执行成功或者是失败.
  • 2、可见性 :现在的计算机,由于 cpu 直接从 主内存中读取数据的效率不高。所以都会对应的 cpu高速缓存,先将主内存中的数据读取到缓存中,线程修改数据之后首先更新到缓存中,之后才会更新到主内存。如果此时还没有将数据更新到主内存其他的线程此时读取就是修改之前的数据
    volatile关键字就是用于保存内存的可见性,当线程A更新了volatite的修饰的变量的话,他会立即刷新到主线程,并且将其余缓存中该变量的值清空,导致其余线程只能去主内存读取最新的值
    synchronized 和加锁也能保证可见性,实现原理就是在释放锁之前其余线程是访问不到这个共享变量的。但是和volatile 相比较起来开销比较大 !
    但是volatile不能够替换synchronized因为volatile 不能够保证原子性 (要么执行成功或者失败,没有中间的状态)
  • 3、顺序性
int a = 100 ; //1
int b = 200 ; //2
int c = a + b ; //3

正常的代码的执行顺序应该是1》》2》》3。但是有时候 JVM为了提高整体的效率会进行指令重排导致执行顺序可能是 2》》1》》3。但是JVM 也不能是 什么都进行重排,是在保证最终结果和代码顺序执行结果是一致的情况下才可能会进行重排
java中可以使用 volatile 关键字来保证顺序性,synchronized和lock 也可以来保证有序性,和保证 原子性的方式一样,通过同一段时间只能一个线程访问来实现的
常用集合的原理分析

  • 9、我自己实现的单利模式:RetrofitManager
  • 在我实际的项目中,具体如下
  • 私有化构造方法
    class RetrofitManager private constructor()
  • 初始化
  private val instance: RetrofitManager by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
          RetrofitManager()
      }
  • 然后通过谷歌爸爸提供的Demo,我来修改一下我的单利实现的方式,由于项目中有些遗留原因,后续回去更新一下代码


    image.png
  • 10、官方还在不断的更新中,好多的依赖都是alpha版本,所以不建议现在在实际项目中去使用

安利一个小工具,如何去使用vector绘制drawable的xml

  • 举个例子


<vector android:height="24dp" android:tint="#FFFFFF"
    android:viewportHeight="24.0" android:viewportWidth="24.0"
    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
    <path android:fillColor="#FF000000" android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z"/>
</vector>

  • 我们可以发现这个path毫无规律,根本都不知道怎么绘制,对吧?不要慌,看下图
    image.png

    image.png
image.png
  • 同时也可以导入SVG的图片格式
    推荐一个 SVG在线作图的网站

  • 同时也可以使用 Iamge Asset 去生成自己想要的图片

image.png
image.png
  • 最后说声谢谢,这两天没有人打扰我,要不然累死了
  • 官方Demo的注释版本,我自己加了一些注释 android-sunflower
  • 感谢以下文档、博客和视屏对我的帮助

Android Jetpack 官方文档 中文翻译
使用 Android Jetpack 加快应用开发速度
YouTube
常用集合的原理分析
SVG在线作图

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容