1. App的启动流程

Android开发基础(一)
基础
1. Zygote:翻译为“受精卵”。因为Android系统是基于Linux系统的,而Linux都是通过init来fork出来的。但是在Android中,当开机的时候init会先创建一个Zygote进程,它的作用和init是相似的。由于他的快速分裂的特性,所以系统启动之后所有的App进程都是基于Zygote的来fork出来的。所以可以说所有的App进程都是源于Zygote进程。
2. system_server: 他也是一个进程,所以也是Zygote来创建出来的。他主要负责创建系统里面重要的服务,其中包括:ActivityManagerService,PackageManagerService等。
3. ActivityManagerService: 当SystemService进程开启的时候就会自动调用ActivityManagerService,其作用就是负责系统中所有的Activity的生命周期。
4. Launcher:Launcher本质上也是一个应用程序,也继承自Activity

启动流程
1. Launcher采用BinderIPC向system_service发起startActivity请求
2. system_service接受到请求之后向Zygote发送创建进程的请求
3. Zygote进程fork出子进程,并将其放入App进程中去
4. App进程采用BinderIPC向system_service发送一个attachApplication的请求
5. system_service收到请求后通过BinderIPC向APP进程发送scheduleLaunchActivity请求
6. App接受到请求之后,使用handler想住线程发送LAUNCH_ACTIVITY消息
7. 住线程收到消息之后创建目标Activity,并毁掉Activity.onCreate方法

2. 五种布局方式
  1. FrameLayout:最简单的布局方式,Android中这种方式会默认将所有的空间都放在视图的左上角,可以使用margin,gravity去控制位置
  2. LinearLayout:分为水平布局和垂直布局两种。
  3. AbsoluteLayout:通过空间的xy坐标来确定空间的位置
  4. RelativeLayout:以另一个控件作为参考系来确定位置
  5. TableLayout:将自元素的位置分配到行或者列汇总,一个TableLayout由许多的TableRow组成
3. Activity的生命周期
  1. 启动:onCreate() ->onStart() ->onResume()
  2. Activity移到后台:这种情况常见于:转到新的activity中或者按home键返回桌面
    onPause() -> onStop()此时进入停滞状态
  3. Activity返回前台:onRestart() -> onStart() -> onResume()
  4. Activity移到后台,此时内存空间不足了,会被杀死,重新打开:onCreate() -> onStart() -> onResume()
  5. 锁屏:onPause() -> onStop()
  6. 解锁:onRestart() ->onStart() -> onResume()
  7. 切换横屏:onPause -> onStop() -> onDestroy -> onCreate() -> onStart() -> onResume()
  8. 配置了configChanges="orientation|screenSize"之后切换横屏:onConfigurationChanged()
4. Activity启动模式
  1. standard:标准模式,每次启动新的Activity都换创建一个新的Activity实例,并且将其押入任务栈中
  2. singleTop:栈顶复用模式,若新启动的Activity已经在栈顶了,那么直接回调onNewIntent()方法就可以了
  3. singleTask:栈内复用模式,在创建新的Activity之前系统会先在栈中找一下是否已经有了这个Activity的实例,若有的话直接调用onNewIntent()方法,并且把当前Activity上面所有的Activity全都清理掉
  4. singleInstance:这种模式的Activity只能单独存在于一个新的任务栈之中。
5. Activity的缓存机制
  1. onSaveInstanceState(Bundle outState):必须在onStop()前执行,但不能保证是在onPause()前还是后执行。用于保存当前Activity的相关的状态。onSaveInstanceState()遵循一个重要原则就是:当系统未经过用户的允许就销毁了Activity的时候就需要调用此方法。而正是因为他的不确定性,所以他一般只用于保存activity 的瞬态而不是进行存储持久化数据。
  2. onRestoreInstanceState(Bundle outState): 这两个方法并不一定是成对出现的!!!比如目前正在执行activityA, 然后按home键回到桌面,然后迅速返回,此时的activity并没有被杀,所以自然也不用调用onRestoreInstanceState方法。此方法在onStart()方法之后执行,他的参数也会传递到onCreate方法中去。
6. Fragment的生命周期

Android开发基础(一)

7. 为什么用Service而不用Thread呢
  1. Thread是线程,是程序执行的最小单元,是CPU分配任务的基本单位,Thread常用来执行一些异步的操作。
  2. Service:当Service是LocalService的时候,他是运行在主线程上的;当Service是RemoteService的时候,他是运行在另一个进程的主线程上的。
  3. 理由1:Thread的运行是和Activity独立的,也就是说即使Activity被finish掉了但是没有主动停止Thread那么Thread会继续去执行,那么此时Activity不在拥有对Thread的引用,也没有办法在不同的Activity中对同一个Thread进行控制。
8. Service的基础知识
  1. 启动方式1-startService():一个Service被某个Activity调用了Context.startService方法启动,即使多次调用startService也只会执行一次onCreate方法,而onStart将会被多次调用。该Service会一直在后台运行,不管调用他的Activity还活着没有。直到调用stopService才会结束服务。
  2. 启动方式2-bindService():一个Service被某个Activity嗲用了Context.bindService方法启动,即使多次调用bindService也只会执行一次onCreate方法,同时onStart方法不会被调用。连接建立之后除非断开连接,Service会一直在后台运行,
  3. 两种启动方式的区别:startService启动在调用者退出之后Service仍然会存在;bindService启动在调用者退出之后也会随即推出。
  4. 先start一个Service再bind:此时会调用onBind()方法,如果向destroy该Service应当调用onUnBind()方法。
  5. 通过bindService可以在Activity中实现一个ServiceConnection接口,并将接口传递个bindService()方法,在ServiceConnection接口的onServiceConnection()方法中执行相关操作。
  6. Service中不能进行耗时工作:不能!UI线程怎么能执行耗时的操作呢。要执行一些耗时操作可以新建一个线程或者使用IntentService。
  7. onStartCommand返回的类型值:
    1. START_NOT_STICKY:被系统杀掉之后不会被重建
    2. START_STICKY:在被系统杀掉之后其状态依然是started,但是获取不到之前传入的intent,之后进行重建Service
    3. START_REDELIVER_INTENT :在重建Service的时候会传入之前的intent
      Android开发基础(一)
9. Android中的动画类型
  1. tween补间动画:指定View的初末状态和变化的时间、方式,来对图形变化实现动画效果
  2. frame动画:帧动画
  3. PropertyAnimation:属性动画
10. 事件分发机制

Android的事件分发机制较为复杂,参考资料:

https://www.jianshu.com/p/38015afcdb58

事件分发其本质就是把点击事件传递给某个具体的View以及其处理的过程。
首先事件的分发分为三个部分,分别是Activity, ViewGroup, View这三个部分,其中有三个比较重要的方法,分别是:onTouchEvent(负责点击事件的处理), onInterceptTouchEvent(负责判断是否拦截,只有ViewGroup才有这个方法)和dispatchTouchEvent(负责事件的分发)。

Android开发基础(一)
Activity:

    public boolean dispatchTouchEvent(MotionEvent ev) {

            // 一般事件列开始都是DOWN事件 = 按下事件,故此处基本是true
            if (ev.getAction() == MotionEvent.ACTION_DOWN) {

                onUserInteraction();
                // ->>分析1

            }

            // ->>分析2
            if (getWindow().superDispatchTouchEvent(ev)) {

                return true;
                // 若getWindow().superDispatchTouchEvent(ev)的返回true
                // 则Activity.dispatchTouchEvent()就返回true,则方法结束。即 :该点击事件停止往下传递 & 事件传递过程结束
                // 否则:继续往下调用Activity.onTouchEvent

            }
            // ->>分析4
            return onTouchEvent(ev);
        }
  1. 在事件发生后首先调用了Activity.dispatchTouchEvent方法
  2. 在其中先对Action进行判断是否为按下的动作,如果是的话则进入onUserInteration方法,该方法的作用是实现屏保的功能。
  3. 进入第二个if判断,进入getWindow.superDispatchTouchEvent方法。
  4. 该方法会接着调用mDecor.superDispatchTouchEvent方法,mDecor是顶层View的实例
  5. 4中的方法会接着调用super.dispatchTouchEvent方法进而去调用ViewGroup的dispatchTouch方法

ViewGroup:

public boolean dispatchTouchEvent(MotionEvent ev) { 

    ... // 仅贴出关键代码

        // 重点分析1:ViewGroup每次事件分发时,都需调用onInterceptTouchEvent()询问是否拦截事件
            if (disallowIntercept || !onInterceptTouchEvent(ev)) {  

            // 判断值1:disallowIntercept = 是否禁用事件拦截的功能(默认是false),可通过调用requestDisallowInterceptTouchEvent()修改
            // 判断值2: !onInterceptTouchEvent(ev) = 对onInterceptTouchEvent()返回值取反
                    // a. 若在onInterceptTouchEvent()中返回false(即不拦截事件),就会让第二个值为true,从而进入到条件判断的内部
                    // b. 若在onInterceptTouchEvent()中返回true(即拦截事件),就会让第二个值为false,从而跳出了这个条件判断
                    // c. 关于onInterceptTouchEvent() ->>分析1

                ev.setAction(MotionEvent.ACTION_DOWN);  
                final int scrolledXInt = (int) scrolledXFloat;  
                final int scrolledYInt = (int) scrolledYFloat;  
                final View[] children = mChildren;  
                final int count = mChildrenCount;  

        // 重点分析2
            // 通过for循环,遍历了当前ViewGroup下的所有子View
            for (int i = count - 1; i >= 0; i--) {  
                final View child = children[i];  
                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE  
                        || child.getAnimation() != null) {  
                    child.getHitRect(frame);  

                    // 判断当前遍历的View是不是正在点击的View,从而找到当前被点击的View
                    // 若是,则进入条件判断内部
                    if (frame.contains(scrolledXInt, scrolledYInt)) {  
                        final float xc = scrolledXFloat - child.mLeft;  
                        final float yc = scrolledYFloat - child.mTop;  
                        ev.setLocation(xc, yc);  
                        child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;  

                        // 条件判断的内部调用了该View的dispatchTouchEvent()
                        // 即 实现了点击事件从ViewGroup到子View的传递(具体请看下面的View事件分发机制)
                        if (child.dispatchTouchEvent(ev))  { 

                        mMotionTarget = child;  
                        return true; 
                        // 调用子View的dispatchTouchEvent后是有返回值的
                        // 若该控件可点击,那么点击时,dispatchTouchEvent的返回值必定是true,因此会导致条件判断成立
                        // 于是给ViewGroup的dispatchTouchEvent()直接返回了true,即直接跳出
                        // 即把ViewGroup的点击事件拦截掉

                                }  
                            }  
                        }  
                    }  
                }  
            }  
            boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||  
                    (action == MotionEvent.ACTION_CANCEL);  
            if (isUpOrCancel) {  
                mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;  
            }  
            final View target = mMotionTarget;  

        // 重点分析3
        // 若点击的是空白处(即无任何View接收事件) / 拦截事件(手动复写onInterceptTouchEvent(),从而让其返回true)
        if (target == null) {  
            ev.setLocation(xf, yf);  
            if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {  
                ev.setAction(MotionEvent.ACTION_CANCEL);  
                mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;  
            }  
            
            return super.dispatchTouchEvent(ev);
            // 调用ViewGroup父类的dispatchTouchEvent(),即View.dispatchTouchEvent()
            // 因此会执行ViewGroup的onTouch() ->> onTouchEvent() ->> performClick() ->> onClick(),即自己处理该事件,事件不会往下传递(具体请参考View事件的分发机制中的View.dispatchTouchEvent())
            // 此处需与上面区别:子View的dispatchTouchEvent()
        } 

        ... 

}
/**
  * 分析1:ViewGroup.onInterceptTouchEvent()
  * 作用:是否拦截事件
  * 说明:
  *     a. 返回true = 拦截,即事件停止往下传递(需手动设置,即复写onInterceptTouchEvent(),从而让其返回true)
  *     b. 返回false = 不拦截(默认)
  */
  public boolean onInterceptTouchEvent(MotionEvent ev) {  
    
    return false;

  } 
  // 回到调用原处
  1. ViewGroup会先去调用onInterceptTouchEvent方法区判断是否对事件进行拦截,默认是false
  2. 若不拦截则会用for循环遍历这个ViewGroup去寻找被点击的子控件直到找到被点击的子控件
  3. 调用子控件的dispatchTouchEvent方法

View:

**
  * 源码分析:View.dispatchTouchEvent()
  */
  public boolean dispatchTouchEvent(MotionEvent event) {  

        if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&  
                mOnTouchListener.onTouch(this, event)) {  
            return true;  
        } 
        return onTouchEvent(event);  
  }
  // 说明:只有以下3个条件都为真,dispatchTouchEvent()才返回true;否则执行onTouchEvent()
  //     1. mOnTouchListener != null
  //     2. (mViewFlags & ENABLED_MASK) == ENABLED
  //     3. mOnTouchListener.onTouch(this, event)
  // 下面对这3个条件逐个分析


/**
  * 条件1:mOnTouchListener != null
  * 说明:mOnTouchListener变量在View.setOnTouchListener()方法里赋值
  */
  public void setOnTouchListener(OnTouchListener l) { 

    mOnTouchListener = l;  
    // 即只要我们给控件注册了Touch事件,mOnTouchListener就一定被赋值(不为空)
        
} 

/**
  * 条件2:(mViewFlags & ENABLED_MASK) == ENABLED
  * 说明:
  *     a. 该条件是判断当前点击的控件是否enable
  *     b. 由于很多View默认enable,故该条件恒定为true
  */

/**
  * 条件3:mOnTouchListener.onTouch(this, event)
  * 说明:即 回调控件注册Touch事件时的onTouch();需手动复写设置,具体如下(以按钮Button为例)
  */
    button.setOnTouchListener(new OnTouchListener() {  
        @Override  
        public boolean onTouch(View v, MotionEvent event) {  
     
            return false;  
        }  
    });
    // 若在onTouch()返回true,就会让上述三个条件全部成立,从而使得View.dispatchTouchEvent()直接返回true,事件分发结束
    // 若在onTouch()返回false,就会使得上述三个条件不全部成立,从而使得View.dispatchTouchEvent()中跳出If,执行onTouchEvent(event)
  1. 调用View.onTouch()方法, 若返回true则不调用onClick方法
  2. 若onTouch()方法返回false则会先调用onTouchEvent方法,然后进入performClick方法,最后调用onClick方法

相关文章: