Lifecycle基础(一)

491 阅读12分钟

LifeCycle基础(一)

  • LifeCycle的职责

    • 可以检测Activity或者Fragment的生命周期函数

    • 依托观察者设计模式实现

      • Activity/Fragment:Observable(被观察者)
      • LifeCycle:Observer(观察者)
    • 图示:

      image-20211125164840846

  • 不使用LifeCycle,去监听Activity/Fragment的生命周期(使用监听器)

    • 工程结构

      image-20211125171247009

    • 思路:

      • 创建一个监听器类,编写函数(监听器的动作)

         package com.derry.lifecycle.user1
         ​
         import android.util.Log
         ​
         class MyListener {
         ​
             private val TAG = "MyListener"
         ​
             fun start() = Log.d(TAG, "start run ...")
         ​
             fun stop() = Log.d(TAG, "stop run ...")
         }
        
      • 在MainActivity处,实例化监听器,重写生命周期函数,在其中调用监听器方法

         package com.derry.lifecycle.user1
         ​
         import android.os.Bundle
         import androidx.appcompat.app.AppCompatActivity
         import com.derry.lifecycle.R
         ​
         // TODO 第一个版本 监听器监听生命周期
         class MainActivity : AppCompatActivity() {
         ​
             private var myListener: MyListener ? = null
         ​
             override fun onCreate(savedInstanceState: Bundle?) {
                 super.onCreate(savedInstanceState)
                 setContentView(R.layout.activity_main)
         ​
                 myListener = MyListener()
             }
         ​
             override fun onStart() {
                 super.onStart()
                 myListener ?.start() // 会出现人为失误,一旦出现人为失误 造成不一致性功能问题
             }
         ​
             override fun onStop() {
                 super.onStop()
                 myListener ?.stop() // 会出现人为失误
             }
         }
        
    • 运行结果

      • 启动APP时:

        image-20211125171339872

      • 退出APP时(按返回键退出):

        image-20211125171422493

    • 实现细节:

      • 需要在MainActivity(被监听处)中的onCreate方法中声明实例化监听器

        • 实际上就是实现类间通信,在类A中调用类B的方法而已
      • 推荐将监听器写成全局变量

      • 注意Kotlin的?使用:就是在声明以及调用的时候要用到

  • 第二版(模仿MVP架构,P层监听生命周期)

    • 为什么P层需要去监听生命周期函数

      • 场景:

        当P层数据需要回馈给Activity,此时当Activity消亡后,再回馈就会出问题;因此需要在P层监听

    • 工程结构

      image-20211126170516049

    • 思路:

      • 创建MyPresenter.kt:在这个里面编写函数(当监听到不同生命周期时,执行相应的动作)

         package com.derry.lifecycle.user2
         ​
         import android.util.Log
         ​
         class MyPresenter {
         ​
             private val TAG = "MyPresenter"
         ​
             fun onResume() = Log.d(TAG, "onResume run ...")
         ​
             fun onPause() = Log.d(TAG, "onPause run ...")
         }
        
      • 创建MainActivity.kt

        • 在MainActivity处,实例化监听器,重写生命周期函数(在其中调用监听器方法)
         package com.derry.lifecycle.user2
         ​
         import android.os.Bundle
         import androidx.appcompat.app.AppCompatActivity
         import com.derry.lifecycle.R
         ​
         // TODO 第二个版本 MVP 的 P层
         class MainActivity : AppCompatActivity() {
         ​
             private var myPresenter: MyPresenter ? = null
         ​
             override fun onCreate(savedInstanceState: Bundle?) {
                 super.onCreate(savedInstanceState)
                 setContentView(R.layout.activity_main)
         ​
                 myPresenter = MyPresenter()
             }
         ​
             override fun onResume() {
                 super.onResume()
                 myPresenter ?.onResume()  // 会出现人为失误,这个万一写掉了相应的功能就完犊子了(加入有100个Activity)会造成不一致性
             }
         ​
             override fun onPause() {
                 super.onPause()
                 myPresenter ?.onPause()  // 会出现人为失误
             }
         }
        
    • 运行结果:

      image-20211126171334098

    • 分析总结:

      • 这个看起来跟使用监听器进行生命周期函数监听差不多,这个实际上是去模仿了一下业务场景;就是去引出LifeCycle这个东西
  • 第三版(基础版)

    • 继承关系:会实现LifecycleOwner(这个在AndroidX中会实现这个借口)

      • 将光标放在MainActivity上,CTRL+H

        image-20211127141411150

    • 结构:

      image-20211127142551715

    • 创建MainActivity(实现观察者与被观察者的关联)

       package com.derry.lifecycle.user3
       ​
       import android.os.Bundle
       import androidx.appcompat.app.AppCompatActivity
       import com.derry.lifecycle.R
       ​
       // TODO 第二个版本 MVP 的 P层
       class MainActivity : AppCompatActivity() {
       ​
           override fun onCreate(savedInstanceState: Bundle?) {
               super.onCreate(savedInstanceState)
               setContentView(R.layout.activity_main)
       ​
               // 一般是在BaseActivity 关联注册的
               // 观察者 与 被观察者 关联的环节
               lifecycle.addObserver(MyObserver())//在kotlin中会将get 省略掉
           }
       ​
       }
      
    • 创建观察者类MyObserver(眼睛):实现观察者接口+使用注解来监听生命周期

       package com.derry.lifecycle.user3
       ​
       import android.util.Log
       import androidx.lifecycle.Lifecycle
       import androidx.lifecycle.LifecycleObserver
       import androidx.lifecycle.OnLifecycleEvent
       ​
       // 观察者  眼睛
       class MyObserver : LifecycleObserver {
       ​
           private val TAG = "MyObserver"
       ​
           @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) // 画面可见 就连接
           fun connectListener() = Log.d(TAG,"connectListener run ...")
       ​
           @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)  // 画面不可见 就断开连接,通过注解来监听生命周期
           fun disconnectListener() = Log.d(TAG,"disconnectListener run ...")
       ​
       }
      
    • 运行截图:查看日志文件

      image-20211127143515590

  • 第三版(plus)

    • 基础版本的缺点:

      • 使用注解才能监听生命周期
      • 不能拿到环境(Activity,Fragment所有环境),拿到了就可以Toast,增加弹窗了
    • 解决办法:让观察者实现DefaultLifecycleObserver接口

    • 接口继承关系:

      • DefaultLifecycleObserver->FullLifecycleObserver->LifecycleObserver
    • 观察者2(第二双眼睛)MyObserver2

       package com.derry.lifecycle.user3
       ​
       import android.util.Log
       import androidx.lifecycle.DefaultLifecycleObserver
       import androidx.lifecycle.LifecycleOwner
       ​
       // DefaultLifecycleObserver 就是对 LifecycleObserver 二次封装  为了用户好用
       class MyObserver2 : DefaultLifecycleObserver {
       ​
           private val TAG = "MyObserver2"
       ​
           override fun onCreate(owner: LifecycleOwner) {
               super.onCreate(owner)
               Log.d(TAG,"onCreate run ..."
           }
       ​
           override fun onResume(owner: LifecycleOwner) {
               super.onResume(owner)
               Log.d(TAG,"onResume run ...")
           }
       ​
           override fun onPause(owner: LifecycleOwner) {
               super.onPause(owner)
               Log.d(TAG,"onPause run ...")
           }
       ​
       }
      
       在MainActivity中注册:
       lifecycle.addObserver(MyObserver2())
      
    • 工程规范,一般情况下是在BaseActivity中进行观察注册的

    • 应用场景:

      • 使用LifeCycle观察这个Activity,Fragment的变化并做逻辑处理

      • 例如:当画面可见的时候才弹窗

        • 微信中:当手机熄屏的时候,会收到消息,但只有在当页面可见的时候,这一瞬间才更新UI
      • 画面不可见的时候,不干活:这个时候使用LifeCycle可能会造成内存泄漏

  • 第四版:内部类实现接口去监听(总是感觉怪怪的)

    • 好处

      • 不用定义额外的类
      • 不用额外重写方法
      • 分层结构化,看起来更加清晰
    • MainActivity

       package com.derry.lifecycle.user4
       ​
       import android.os.Bundle
       import android.util.Log
       import androidx.appcompat.app.AppCompatActivity
       import androidx.lifecycle.Lifecycle
       import androidx.lifecycle.LifecycleObserver
       import androidx.lifecycle.OnLifecycleEvent
       import com.derry.lifecycle.R
       ​
       // TODO 第四个版本  会内部类 监听
       class MainActivity : AppCompatActivity() {
       ​
           private val TAG = "MainActivity"
       ​
           override fun onCreate(savedInstanceState: Bundle?) {
               super.onCreate(savedInstanceState)
               setContentView(R.layout.activity_main)
       ​
               // 一般是在BaseActivity 关联注册的
               lifecycle.addObserver(MyObserver())
           }
       ​
           inner class MyObserver : LifecycleObserver {
       ​
               @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
               fun onResume() {
                   Log.d(TAG, "Lifecycle call onResume");
                   // 逻辑
               }
       ​
               @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
               fun onPause() {
                   Log.d(TAG, "Lifecycle call onPause");
               }
           }
       ​
           /*override fun onResume() {
               super.onResume()
               // 逻辑
           }*/
       }
      
  • 第五个版本:接口监听法(使用较多的)

    • 在工程中多用这个

    • 工程结构:

      image-20211127145758941

    • IPresenter

       package com.derry.lifecycle.user5
       ​
       import androidx.lifecycle.Lifecycle
       import androidx.lifecycle.LifecycleObserver
       import androidx.lifecycle.OnLifecycleEvent
       ​
       interface IPresenter : LifecycleObserver {
       ​
           @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
           fun onResume()
       ​
           @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
           fun onPause()
       ​
       }
      
    • MainActivity

       package com.derry.lifecycle.user5
       ​
       import android.os.Bundle
       import android.util.Log
       import androidx.appcompat.app.AppCompatActivity
       import androidx.lifecycle.Lifecycle
       import androidx.lifecycle.LifecycleObserver
       import androidx.lifecycle.OnLifecycleEvent
       import com.derry.lifecycle.R
       ​
       // TODO 第五个版本  接口监听法,设计模式的设计环节,你会见到这种写法
       class MainActivity : AppCompatActivity() {
       ​
           private val TAG = "MainActivity"
       ​
           private var myPresenter : IPresenter ? = null
       ​
           override fun onCreate(savedInstanceState: Bundle?) {
               super.onCreate(savedInstanceState)
               setContentView(R.layout.activity_main)
       ​
               // 一般是在BaseActivity 关联注册的   (省略:工厂设计模式)
               myPresenter = MyPresenter3()//使用工厂设计模式就可以改变观察的对象
       ​
               //关联一下
               lifecycle.addObserver(myPresenter !!)
           }
       }
      
    • MyPresenter

       package com.derry.lifecycle.user5
       ​
       import android.util.Log
       ​
       class MyPresenter : IPresenter {
           private val TAG = "test"
       ​
           override fun onResume() {
               Log.d(TAG, "Lifecycle call onResume")
           }
       ​
           override fun onPause() {
               Log.d(TAG, "Lifecycle call onPause")
           }
       }
      
    • MyPresenter2

       package com.derry.lifecycle.user5
       ​
       import android.util.Log
       ​
       class MyPresenter2 : IPresenter {
           private val TAG = "test"
       ​
           override fun onResume() {
               Log.d(TAG, "Lifecycle call onResume")
           }
       ​
           override fun onPause() {
               Log.d(TAG, "Lifecycle call onPause")
           }
       }
      
    • MyPresenter3

       package com.derry.lifecycle.user5
       ​
       import android.util.Log
       ​
       class MyPresenter3 : IPresenter {
           private val TAG = "test"
       ​
           override fun onResume() {
               Log.d(TAG, "Lifecycle call onResume")
           }
       ​
           override fun onPause() {
               Log.d(TAG, "Lifecycle call onPause")
           }
       }
      

源码分析

  • 工程结构:

    image-20211127155222087

  • 代码展示:这里模拟了一个地图APP,通过生命周期函数来分析Lifecycle源码

    MainActivity

     package com.derry.lifecycle.source1
     ​
     import android.os.Bundle
     import androidx.appcompat.app.AppCompatActivity
     import com.derry.lifecycle.R
     ​
     // TODO 被观察者
     // 源码分析
     // 不在Activity里面写   是为了降低耦合吗  ?
     // Google考虑的是,你继承AppCompatActivity 可以,你继承 XXXXActivity 也可以
     class MainActivity : AppCompatActivity() {
     ​
         override fun onCreate(savedInstanceState: Bundle?) {
             super.onCreate(savedInstanceState)
             setContentView(R.layout.activity_main)
     ​
             // 观察者 == MyLocationListener
             // 1.主线流程,支线流程不管(防止淹死源码)
             // 2.支线流程
             lifecycle.addObserver(MyLocationListener())//在kotlin中是不用new的
         }
     ​
         override fun onResume() {
             super.onResume()
     ​
             // CREATE --->  START  ---> RESUME
         }
     ​
         override fun onStop() {
             super.onStop()
             // initial -> create 要执行onCreate
             /*addObsever()的时候是初始状态
               后面只执行了onDestroyed()
             所以是onCreate和onDestroyed*/
             // lifecycle.addObserver(MyLocationListener())
         }
     }
    

    MyLocationListener

     package com.derry.lifecycle.source1
     ​
     import android.util.Log
     import androidx.lifecycle.Lifecycle
     import androidx.lifecycle.LifecycleObserver
     import androidx.lifecycle.OnLifecycleEvent
     ​
     // 地图定位功能的模拟  眼睛 Observer 观察者,眼睛去看什么就在这个里面去看,在MainActivity中去关联被观察者
     class MyLocationListener : LifecycleObserver {
     ​
         @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
         fun create() = Log.d("Derry", "create 正在启动系统定位服务中...")
     ​
         @OnLifecycleEvent(Lifecycle.Event.ON_START)
         fun start() = Log.d("Derry", "start 连接系统定位服务...")
     ​
         @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
         fun resume() = Log.d("Derry", "resume 系统定位的界面展示...")
     ​
         @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
         fun pause() = Log.d("Derry", "pause 系统定位的界面关闭...")
     ​
         @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
         fun stop() = Log.d("Derry", "stop 断开系统定位服务...")
     ​
         @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
         fun destroy() = Log.d("Derry", "destroy 正在停止系统定位服务...")
     }
    
  • 分析方法:

    • 始终抓住主线流程(决定走向的地方)
  • 分析实例:自己创建的观察者MyLocationListener在系统中是如何调用的

    • 步骤:点击红色箭头查看源码(CTRL+鼠标左键)

      image-20211128104227864

      • 跳转到MainActivity中(添加眼睛:将观察者与被观察者进行关联)

      image-20211128104359533

      • 点击这个接口按钮进行跳转

      image-20211128104519632

      • 可以看到眼睛作为ObserverWithState类的构造参数,再次跳转(查看ObserverWithState具体的构造函数),点击红色箭头

      image-20211128104709034

      • 在ObserverWithState类的构造方法中,可以看到眼睛observer,作为函数实参传入了这个Lifecycling.lifecycleEventObserver()中,继续点击进行跳转

      image-20211128105014841

      • 在Lifecycling.lifecycleEventObserver()中,可以看到它将参入的参数弄成了一个object

      image-20211128105223447

      • 滑动鼠标滚轮,在这个LifecycleEventObserver的最后一行,发现这个object作为构造参数,进行了ReflectiveGenericLifecycleObserver类的实例化,点击,继续跳转

      image-20211128105440810

      • 下图中,可以看到ReflectiveGenericLifecycleObserver的构造方法中,先对传入的参数做了暂存,放在了mWrapped中,然后使用了参数的字节码文件(引入反射),继续点击红色箭头

      image-20211128105819536

      • 此时,跳转到了CallbackInfo类中,下滑鼠标,找到private CallbackInfo createInfo(Class<?> klass, @Nullable Method[] declaredMethods) {}这个函数;
      • 下图中的红色框中,发现在这个我们将创建的MyLocationListener,通过反射获取到其字节码文件并将其放到了Map里面去:方便后续宿主(Activity或者Fragment的生命周期)通过反射进行激活

      image-20211128110125471

  • 源码分析经验:使用Map提升性能(时间换空间)

         CallbackInfo getInfo(Class<?> klass) {
             //当第一次拿到这个观察者的话,就将他塞到这个Map里面去。下一次再碰到这个(相同的观察者)就直接从Map里面去拿,大多数系统源码都会这样去搞一个Map缓存:
             //反射往往搭配Map进行使用
             //第一次创建,第二次从缓存里面去拿,
             CallbackInfo existing = mCallbackMap.get(klass);
             if (existing != null) {
                 return existing;
             }
             existing = createInfo(klass, null);
             return existing;
         }
    
  • 观察者会被放到Map里面,ReflectiveGenericLifecycleObserver--->ClassesInfoCache这个类里面

     //使用cache保存了观察者的类注解等信息全部放到Map里面去。方便宿主通过反射进行激活
     CallbackInfo info = new CallbackInfo(handlerToEvent);
     mCallbackMap.put(klass, info);
     mHasLifecycleMethods.put(klass, hasLifecycleMethods);
    
  • 状态与事件(这个就是一个难点了)

    • 当宿主发生生命周期后,怎么在LifeCycle中进行感知?(引入状态机)

    • 先解决这个怎么关联的问题

      • 进入MainActivity的父类:AppCompatActivity

      image-20211128111601743

      • 进入AppCompatActivity的父类:FragmentActivity

      image-20211128111659013

      • 再进入FragmentActivity的父类:ComponentActivity

      image-20211128111747477

      • 在这个ComponentActivity里面,实现了一个接口,点进去

      image-20211128111850126

      • 发现:有这个getLifecycle();这个,就相当于在MainActivity中的关联lifecycle.addObserver(MyLocationListener())

      image-20211128112018294

  • 开始处理感知问题:返回上一步(ComponentActivity类),去找他的onCreate方法,这里会创建一个空白(无UI)的Fragment

    image-20211128112711477

    • 在Gradle源码中,常常使用一个空白(无UI的Fragment)黏贴到你想要监听的地方,宿主去执行onStart,这个空白的Fragment(ReportFragment)也会执行onStart,

      image-20211128112900643

    • 在这个ReportFragment.java中下滑鼠标,这个是有对应的六个生命周期函数的,牵涉到了事件的分发(这里面会做版本控制,低于29直接开始分发事件;高于29会使用切面的方式,切出去,但是最后还是会回来,上图;)

    image-20211128113905616

    image-20211128113806562

    • 就是这个接口了 image-20211128114123858
  • 现在开始最难的地方了:处理事件分发

    • 拿到事件,扔到getStateAfter里面去:这里是根据event事件拿到状态next,再根据状态去对齐

    image-20211128114602195

    • 使用switch-case:对应不同的事件,返回不同的状态

    image-20211128114709428

    • 事件取值代码展示

    image-20211128115301894

    • 状态取值代码展示

    image-20211128115222560

  • 事件驱动状态:这个就是状态机

    • ON_RESUME没有倒退状态
    • mState:会记录所有的状态
    • mActive:bool值,当状态为STAERED或者RESUMED,这个变量为true,其他的状态都是false,这个变量会在LiveData中作为权限标识,只有在true的时候才会去干活
    • 状态机如图(通过事件来决定状态转移的过程):从左至右枚举值逐次递增 image-20211128121615066
  • 状态对齐:保证观察者与被观察者状态相同(被观察者往往要走的快一点,比如被观察者在STARTED,观察者还在CREATED)

    image-20211128125303186

    • 当状态对齐完成(这个是怎么对齐的?代码在哪里呢),就退出同步操作
    • 当状态不同时,通过枚举类型比大小,决定是前进(forwardPass)还是后退(bankwdPass)

    image-20211128125912066

  • 实例:以倒退流程举例:

    image-20211128130309460

    • 查看源码,确实会执行ON_STOP事件(这里就是通过状态退出事件)
    ![image-20211128130400559](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ca60acfdaac54143b25b4384bffd4962~tplv-k3u1fbpfcp-zoom-1.image)
    
    • 实例:当观察者为CREATED,被观察者是STARTED,被观察者大,就前进

      此时观察者就会前进,更新状态(在比较枚举值的时候,观察者小于被观察者)

      image-20211128130851766

      • 这个时候,需要将观察者更新,需要前进,进人upEvent函数(根据观察者的状态拿到这个函数的返回值(需要执行的一个事件))这里还有一次检测,

      image-20211128130953041

      • 处理事件分发,当被观察者是STARTED,返回ON_RESUME作为dispatch的参数

      image-20211128131245854

      • 这里会进行二次检测:

      image-20211128132409356

      • 继续点击

      image-20211128132513937

      • 继续点击

      image-20211128132554586

      • 继续点(这个就是前面留的那个坑)

      image-20211128132614175

      • 继续,反射执行

      image-20211128132736399

      • 继续,反射执行

      image-20211128132809346

      • 就到这里了,这里会通过反射进行调用MyLocationListener的ON_RESUME方法:实现了事件分发

      image-20211128132842101

      • 会执行MyLocationListener中注解处理器对应的ON_RESUME

      image-20211128134043417

  • 处理坑:mActive

    • LiveData能拿到LifeCycle状态,所以它才会有许多的功能特性

      image-20211128142958201

      • 解绑解决内存泄漏,当Activity为DESTROYED,将LiveData的观察者解绑,这样就不会造成内存泄漏

        image-20211128143533875

      • 这个LiveData可以拿到Lifecycle的状态,就可以去偷懒了

        image-20211128144119539

    • 填坑:注册位置不同:在onResume中,onStop中注册

       class MainActivity : AppCompatActivity() {
       ​
           override fun onCreate(savedInstanceState: Bundle?) {
               super.onCreate(savedInstanceState)
               setContentView(R.layout.activity_main)
       ​
               // 观察者 == MyLocationListener
               // 1.主线流程,支线流程不管(防止淹死源码)
               // 2.支线流程
               lifecycle.addObserver(MyLocationListener())//在kotlin中是不用new的
           }
       ​
           override fun onResume() {
               super.onResume()
       ​
               // CREATE --->  START  ---> RESUME:在这里注册,也可以用,就会在while连续对齐两次
               lifecycle.addObserver(MyLocationListener())
           }
       ​
           override fun onStop() {
               super.onStop()
               // initial -> create 要执行onCreate
               /*addObsever()的时候是初始状态
                 后面只执行了onDestroyed()
               所以是onCreate和onDestroyed*/
               // lifecycle.addObserver(MyLocationListener())
           }
       }
      
    • 状态机相关:

      • 为什么要搞状态机,直接在更新的时候赋值就行

        • 状态机只能按照事件改改变状态
        • 这个是为了给以后的框架使用,不是给程序猿用的
      • 状态机的起点是INITIALIZED,状态只能是通过事件进行更新
      • 观察者与被观察者都只能保持一个状态,通过这个状态来决定是前进还是倒退
  • 知识细节

    • 被观察者(Observable)--->宿主:实现了LifeCycleOwner的,用这个接口来维护

      • Activity
      • Fragment
    • 使用的是support4不是AndroidX的话,就没有Jetpack,没有这个接口(AndroidX是默认的)

    • 观察者Observer:实现了LifeCycleObserver接口,这个就是眼睛

    • 使用LifeCycle.addObserver(观察者):这样就关联起来了

  • 知识补充:

    • 生命周期函数的执行时机

    • as快捷键查看继承关系:

      • 将光标悬停在想要查看的类上
      • CTRL+H
    • as快捷键查看类的结构

      • ALT+ 7
      • CTRL+F12
    • as快捷键分析源码

      • 回退到上一步:CTRL+ALT+左方向键
      • 前进到下一步:CTRL+ALT+右方向键(基于查看的历史)
    • 怎么去制作动图:

    • 如何实现枚举大小比较:

      image-20211128144330288

    • 工厂设计模式

    • 反射

  • 异常处理

    • 在导入别人的文件的时候

      • 异常信息

         Caused by: org.gradle.api.internal.plugins.PluginApplicationException: Faile
        
      • 解决办法

        在这个文件里面添加如下语句后刷新,即可

        image-20211125165524364

          android.overridePathCheck=true
        
    • 在其中项目的时候,发现运行的不是自己想要的那个

      • 设置AndroidManifest.xml

        image-20211125170644070

      • 即可启动这个user1/MainActivity

    • 启动失败

      • 同步一下Gradle文件+重新启动即可
      • 或者对照日志进行处理