Android开发艺术探讨精华(完结)



请尊重原创,转载请注明出处:http://blog.csdn.net/mabeijianxi/article/details/73330588

拾起了一年多前看过的一本书,这里记录一些概念或流程,以便以后巩固复习或者面试。


1、configChanges=“orientation|screenSize”,禁止屏幕旋转时重建Activity。这时将回调Activity的onConfigurationChanged 

2、当启动Activity时加上 FLAG_ACTIVITY_NEW_TASK   标记位后,这个时候待启动ACTIVITY实际上是以singleTask启动的。

3、singleTask(singleInstance拥有其全部特性,只是其只能独立在一个栈中):栈内复用,当某Activity B被 A以singleTask模式启动后,首先会找是否有其想要的任务栈(通过TaskAffinity控制,默认是包名,所以都不指定的话会A与B将会相同),有就掉到前台并执行clearTop,没有就创建。也可能配合allowTaskReparenting使用,其为true时情况是这样的,如有应用 A与B,A启动了和B的一个Activity,这时此Activity将从应用A的任务栈转移到应用B的任务栈。

4、指定启动模式的方式有两种,一种是 activity 标签下指定,一种是代码里指定,代码优先标签。代码里可以设置FLAG_ACTIVITY_CLEAR_TOP,标签不可以。标签可以设置 singleInstance 代码不可以。

5、singleTop、singleTask、singleInstance模式下当Activity不被重建时不走onCreat()、onStart()。会调用onNewIntent(),且在onResume。

6、android:excludeFormRecents=“true”或则FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS 可以让其Activity在历史列表中删除。

7、通过Intent 判断是否含有能启动的Activity。resolveActivity()(返回最佳)   queryIntentActivities()(返回所有)

8、四大组件多进程开启方式:android:process=“:aa”/“com.jianxi.test.aa”,:这种写法会在前面加上包名,且属于当前应用的私有进程,其他应用组件不可以和它跑在一个进程。另外一种是全局进程,其他应用通过ShareUID方式可以和它跑在同一个进程中。注意ShareUID最好在第一版就添加,不然覆盖安装可能会有问题!

9、Android中每个进程都分配了一个独立的虚拟机,所以是无法内存共享的。

10、serializable序列号中serialVersionUID的作用是反序列号的时候检查版本是否一致,比如Class新增或减少了字段。我们应该手动加上这个字段。指定为1L等同于用当前类结构去生成的hash值。特别注意静态成员变量属于类不属于对象,所以不会参与序列化过程,    其次transient关键字标记的成员变量不参数序列化过程。

11、parcelable比较适合在Android系统中传输,其开销相对较小,但是稍微麻烦一点。

12、Binder工作工程:客服端 asInterface 后本地通过 queryLocalInterface 查询是本地服务还是远程服务,本地服务即直接返回此 接口 对象,远程服务返回实现了此接口的 Proxy 对象。然后拿到对象后本地服务调用过程没什么特殊,调用远程服务时通过 Proxy 里面实现的接口方法与 Parcel 对象来实现读写,先调用 Binder 的 transact 把需要写入的参数带过去,这时候服务端onTransact 调用,根据请求的code服务端开始调用不同的方法,调用完成后有返回值就写入返回值,没有就直接返回,这时候就回到了客户端 Proxy 里面的方法,然后再返回我们原始调用处,到此结束一次远程调用。

13、通过binder来进程间通信可以用AIDL然系统给我们生成一个标准的Java代码,我们也可以不写AIDL,自己直接写这个Java代码,效果是一样的。

14、通过实现了Parcelable 的 Bundle 可以进程间通信,四大组件中可以把Bundle放入Intent中实现,但是通信数据必须是Bundle所支持的类型。

15、文件共享的方法来进程间通信,这种方式在多线程下将不那么靠谱,系统Sharedpreferences由于内存中的缓存机制,多进程的时候有很大几率丢失数据。

16、Messenger来进程间通信,其为AIDL的封装形式。它在服务端一次处理一个请求,不存在并发执行的情形。使用方法也简单方便,服务端:new 一个 Messenger 并传入一个处理消息的Handler,在服务的onBind里面返回 messenger.getBinder()。客户端可以new 一个Messenger 传入 IBinder , 再new 一个  Messenger 传入 处理 服务器消息的Handler,第一个 Messenger 里面传入需要传递的 Message对象,且第二个 Messenger 对象需要放在此 Message 对象中,最后调用第一个 Messenger的send(msg) 方法发送消息到服务器。服务器这时可在处理消息的Handler里面拿到 Message,然后取出数据,这时候如果服务器需要回复客服端,可通过 msg.replyTo() 拿到我们在客服端中new 的第二个 Messenger,然后调用其 send(),这是客服端的Handler就会收到回复,也就完成了一次对话。(Messenger 不适合大量并发的请求,且不能调用函数

17、客服端在通过Binder调用服务端方法时,由于其当前线程会被挂起,所以如果知道服务端有耗时操作时,不能在ui线程调用,而服务端由于其运行在Binder线程池中,所以不需要单独再看线程执行。

18、AIDL接口中是可以通过参数传递AIDL接口的,也就是可以实现观察者模式,当服务端需要回调客服端的时候可以通过这个注册进去的AIDL接口来回调。但是直接解注册是没用的,因为,进程间传递的AIDL接口对象是被序列号后的,服务端和客服端的不是同一个对象,解决办法是因为每个AIDL对象包含一个唯一的 Binder对象 ,它是唯一的,在传递过也不会发生改变,所以可以通过它来标记。系统里面有一个封装好的List叫 RemoteCallbackList 用它可以轻松实现,里面维护了一个ArrayMap<IBinder,Callback>,但使用方法与一般List有所不同。

19、Binder死亡处理:可以设置 DeathRecipient 监听,死亡时会在Binder线程池回调 binderDied ,也可在 onServiceDisconnected 中处理,其回调的在ui线程。 

20、AIDL使用中安全验证:可以在onBInd中验证是否有某个权限,没有就返回null。也可以在onTransact( )中getuid来获得包名来验证。

21、ContentProvider进程间通信:

        其onCreat()是回调在主线程的,其他方法回调在Binder线程池。

        我们可以crud数据库,也可以是文件,比较自由。

        xml中指定的  android:authorities=“com.jianxi.test.provider” 是其唯一标示

        也可以为其指定一个权限,然其他要用的必须声明这个权限, permission=“com.jian.lalala”。还可以更细:android:readPermission=“” 与 android:writePermission=“”。

        调用:
               Uri uri = Uri.parse(“content://com.jianxi.test.provider")
                getContentResolver().query(uri,null,null,null,null);

        在 ContentProvider 中可以通过 UriMatcher 这个工具来添加 URI_CODE,以实现区分不同的 URI 查询不同的表。

        一般在 ContentProvider 中当数据改变时会调用 getContentResolver().notifyChange(uri,null) ,来通知外界,外界可以通过                                 ContentResolver 的 registerContentObserver 来注册,以获取这一通知。

        ContentProvider中的 q u i d ,四个方法由于可能并发,所以需要做好同步。如果是 SQLLiteDatabase,那其内部其实是有同步处理的,但是多个的时候可能就不行了。 
        
22、通过 Socket 实现进程间通信
    以TCP为例:
        服务端:监听一个端口(new ServerSocket(8888)),当有请求时阻塞式方法 serverSocket.accept() 返回一个新的 Socket 对象,然后可以对这个请求单独开个线程处理相应实践,在这个Socket 对象用我们可以拿到其相应的输入流和输出流,然后就可以进行通信了。(当客服端断开后这个 Socket 对象里面的输入流会返回null ,也就是 read方法返回null)

       客户端:创建一个 Socket 对象,并连接到对象ip、port。如 new socket(“localhost”,8888),通过这个socket对象也可以获得相应的输入输出流,然后和服务器通信。

2017.06.17更

23、CountDownLatch:可在构造中出传入一个数值,代表等待的线程数目。在需要等待的线程调用 CountDownLatch.await(),在执行完操作的线程调用CountDownLatch.countDown(),调用一次数值减一,当为0后,调用 CountDownLatch.await的线程开始执行后面的代码。

24、Binder 连接池:服务端返回一个带 queryBinder 方法的Binder,客服端通过向 queryBinder 传入不同的 code 得到不同业务的 Binder 对象。

25、 View 的 left、r、t、b、x、y、translationX、translationY都是相对于父View的,x=left+translationX;

26、MotionEvent的getX、getY是获取相对于当前View左上的坐标,getRawX、getRawY获取的是相当于手机屏幕左上的坐标。

27、TouchSlop:系统能识别出的最小滑动距离,小于它系统不认为你是在滑动,默认8dp,可通过ViewConfiguration.get(getContext()).getScaledTouchSlop()获取。

28、VelocityTracker:获取滑动速度
        使用:
                VelocityTracker velocityTracker = VelocityTracker.obtain();
                velocityTracker.addMovemnet(event);

                需要计算时:
                velocityTracker.computeCurrentVelocity(1000)//传入的是单位为ms的时间,即在这个时间内所划过的像素
                velocityTracker.getXVelocity()//获取X方向的速度

                最后:
                 velocityTracker.clear();
                velocityTracker.recycle();

29、GestureDetector 手势监控器:
        可以传入 OnGestureListener与OnDoubleTapListener。
        在 onTouchEvent() 中 添加:
       return mGestureDetector.onTouchEvent(event);

30、View的 scrollTo/scrollBy
        scrollBy是相对位置、scrollBy是绝对位置,其内容改变都是相对于当前容器改变的,也就是说View的布局位置不会改变,只是绘制来让人感觉移动了,我觉得应该就是绘制位置在改变而已。

31、用View动画或者属性动画可以让 View动起来。

32、改变布局参数可以让View动起来,如:
       MarginLayoutParms params = (MarginLayoutParms)view.getLayoutParams();
        params.width +=100;
        params.leftMargin +=100;
        view.requsetLayout();

33、使用 Scroller 来实现物理性滑动,步骤:mScroller.startScroll(scrollX,0,deltax,0,1000),这个时候 startScroll 会调用 invalidate 然后 onDraw( ) 会执行,onDraw 里面会调用 computeScroll ,computeScroll 这个方法是空方法,当然我们可以复写它,在里面再调用 scrollTo(mScroller.getCurrX(),mScroller.getCurrY( ) ) 让 View 假装滑动,再调用 postInvalidata ,这样就产生了循环,当 mScroller.computeScrollOffset() 返回false的时候就结束。

34、通过 ValueAnimator 这个值动画可以做太多事了,它可以回调一段时间内一个值到另外一个值的变化数据,当然还可以加入差值器让其做更多有趣变化。

35、事件分发过程:

   当事件到达 Activity 的 dispatchTouchEvent 后,开始调用 Window 实现类 PhoneWindow 的 superDispatchTouchEvent ,在   superDispatchTouchEvent 里又调用了  PhoneWindow 内部类 DecorView 的 superDispatchTouchEvent ,DecorView 又是继承 Framlayout 的。我们通过 setContentView 设置的View 其实就是 DecorView 的一个子 View DecorView 的 superDispatchTouchEvent 里面又调用 了自己的 dispatchTouchEvent ,dispatchTouchEvent 里面又执行了 super.dispatchTouchEvent ,这个 super 就是 ViewGroup ,现在事件马上就可以穿到我们 setContentView 设置的 View 了。
    达到 ViewGroup 的 dispatchTouchEvent 后就开始执行分发逻辑,其先会判断当前事件是否是 DOWN ,是的话会重设一些东西,比如用来获取 disallowIntercept 这个值的标记 FLAG_DISALLOW_INTERCEPT,也就是说子 View 调用 requestDisallowIntercept 来设置标记时对 DOWN 无效!。接着判断(==DOWN || mfirstTouchTarget!=null),这里 mfirstTouchTarget 代表处理事件的子 View ,如果有那么有子 View 处理就不为空,也就是说为 DOWN或者有子 View 处理当前事件时就会执行里面的代码块,代码块里面会调用本 View 的 onInterceptTouchEvent ,单 是否调用也会受到 disallowIntercept 值的影响。调用后会得到一个 bool 值,这个值就是经常说的拦截不拦截。
    如果不拦截那么就会如找 mfirstTouchTarget 这个目标子View, 这个子View 所在区域必须包含点击事件的坐标,其次没有在播放动画,如果找到了 mfirstTouchTarget,那么就为它赋值且调用它的 dispatchTouchEvent,如果 子View dispatchTouchEvent 返回了false,那下面就可能调用当前 View 的 super.dispatchTouchEvent,这时候自身的 onTouchEvent 将会被调用。
    如果拦截,也分情况,mfirstTouchTarget 不为null,也就是子 View 已经在处理某个事件了,我们中途拦截了它,这时子View 还是会得到一次分发,这个事件是 cancel ,接着 mfirstTouchTarget 也会变成 null,比如当时是 move ,中途被拦截后会被改成 cancel,也就是说这次 move 事件被转化了,下次分发就直接是当前 view 处理了。为 null 就简单了,将很快调用 super.dispatchTouchEvent 这时自身 onTouchEvent 就执行了。下面是一个很简化很简化的流程图: 




2017.06.20更

36、ViewRoot 对应于 ViewRootImpl,它是连接 WindowManager 和 DecorView 之间的纽带,View 的 measure、layout、draw 都是通过 ViewRoot 完成的。View的绘制是从 performTraversals开始的,如图:


37、DecorView 的高度就是屏幕真正的高度(包括了系统状态栏)。

38、getDecorView().findViewById(android.R.id.content).getChildAt(0)即可得到我们 setContentView 设置的View.

39、MeasureSpec 是一个32位int值,高2位代表 SpecMode,低30位代表 SpecSize 。

40、UNSPECIFIED:父容器不对View 做任何限制,要多大给多大。(如ListView)

41、EXACTLY:精确模式,父容器已经检测出 View 所需要的精确大小,对应于 match_parent 于与具体数值这两种模式。

42、AT_MOST:父容器指定了一个可用大小,View 的大小不能大于这个值。对应于 LayoutParams 中的 wrap_content。

43、对于 DecorView,其 MeasureSpec 由窗口的尺寸和其自身的 LayoutParams 来共同确定;对于普通 View ,其 MeasureSpec 由父容器的 MeasureSpec 与自身的 LayoutParams 共同确定。

44、翻开 25的 SDK 发现 DecorView 已经被从 PhoneWinodw 的内部类中单独抽出来了。 

45、普通 View 的 MeasureSpec 创建规则:




2017.06.22更


46、在某些极端情况下,系统可能需要多次 measure 才能确定最终的测量宽/高,在这种情况下,在 onMeasure 方法中拿到的测量宽高可能不准确,一个较好的习惯是在 onLayout 方法中去获取 View 的测量宽高或者最终宽高。

47、Activty中四种方法获取测量宽高:
        onWindowFocusChanged
        view.post
        ViewTreeObserver
        手动 view.measure

48、getHeight 与 getMeasureedHeight 在不做特殊处理的情况下最终的值都是相等的,只是赋值时机不一样.

49、View draw过程:
        绘制背景 background.draw(canvas)
        绘制自己 (onDraw)
        绘制 children (dispatchDraw)
        绘制装饰 (onDrawScrollBars)

50、View 里面 setWillNotDraw 可以设置是否绘制的标志,View 是默认绘制的,ViewGroup 默认会设置会不绘制,不绘制 background.draw onDraw 将不会调用。

51、Drawable 的内部宽高不等同于它的大小,一般来说,Drawable是没有大小概念的,当用其做背景时可能会被拉伸至View大小。

52、可以在 xml中定义 bitmap、nine-patch、shape 等 Drawable,里面可以很多的属性标签,比如 gravity,当图片小于容器时也许会用得上。

53、一个 layer-list 中可以包含多个 item,每个 item 表示一个 Drawable,item 里面可以直接指定已有的 Drawable 即 android:drawable=“”,也可以直接生产 Drawable,如可以添加 shape 标签。

54、StateListDrawable,也就是经常用的 selector 标签:
        android:constantSize        StateListDrawable 大小是否随着其状态改变而改变(可能每个 Drawable 的大小都是不一样的 ),默认是false,改变。
        android:variablePadding        表示 StateListDrawable 的 padding 是否随着状态改变而改变,默认 false,不改变。

55、LevelListDrawable,对应标签为 <level-list>,可以为每个 item 指定一个 maxLevel 与 minLevel ,当当前LevelListDrawable 设置的等级在这个范围内时既切换当这个 item。ImageView 中可以通过 setImageLevel 方法来切换。范围0~10000

56、TransitionDrawable 可以实现淡入淡出的效果,对应 <transition> 标签, 当设置为 View 背景后可以 getBackground() 得到,然后调用 startTransition 或 reverseTransition 来实现。

57、InsetDrawable,对应 <inset> 标签,可以为内嵌的 Drawable 留出一定边距,当 View 希望自己的背景比自己小时非常的适用。

58、ScaleDrawable 对应标签为 <scale>,里面可以添加:
            android:scaleHeight=“25%" 
            android:scaleWidht=“25%” 

        这里25%表示缩放为原来的大小的 75%。 
        调用 setLevel 的时候等级范围:(0,10000] 

2017.06.23更

59、ClipDrawable 对应于 <clip> 标签,可以根据设定来剪切一个 Drawable
        android:clipOrientation=“”
        android:gravity=“”

        剪切方向与位置可以根据上面的属性组合控制。
        剪切大小通过 setLevel 来控制,范围 [0,10000],0代表完全剪裁,10000是不剪裁。

60、View 动画:
    TranslateAnimation        对应    <translate>
    ScaleAnimation             对应     <scale> 
    RotateAnimation           对应      <rotate>
    AlphaAnimation             对应      <alpha>

    在 XML 中定义 View 动画的路径是:res/anim/filename.xml
    
   形式如下:
    <set>
        <alpha/>
        <scale/>
        <set>
            ...
        </set>
    </set>
    View动画既可以是单个动画,也可以是组合动画,组合动画内部还和嵌套其他组合动画。
    set 标签下的一些属性:
    android:interpolator        动画差值器
    android:shareInterpolator    集合内是否共享一个动画差值器.

    代码加载:
        Animation anim = AnimationUtils.loadAnimation(context,R.anim.anim_id)
        view.start(anim)

    完全代码创建 View 动画:
        AlphaAnimation anim = new AlphaAnimation(0,1);
        anim.set...
        view.startAnimation(anim);

   61、帧动画
        是 View 动画的一种,就是一帧一帧的播放动画。
        XML中定义位置:res/drawable/anim.xml
        <animation-list>
            <item …  android:duration=“” />
            <item …  android:duration=“” />
            ...
        </animation-list>

    代码加载:
        view.setBackgroundResource(R.drawable.anim)
       AnimationDrawable anim =  (AnimationDrawable)view.getBackground();
        anim.start();

   62、在 ViewGroup 中设置子 View 的出场动画:
        定义 LayoutAnimation:
        位置:res/anim/anim.xml
        <layoutAnimation
        delay=“0.5”
        android:animationOrder = “normal"
        android:animation = “@anim/anim"
        />
        delay:每个 item 延迟播放时间
        animationOrder:播放顺序,有normal、reverse、random,表示前到后、后到前、随机。

        定义完成后在 ViewGroup 的标签中指定:android:layoutAnimation=“@anim/…"

63、Activity/Fragment 切换效果:
        在 startActivity 或者 finish 后调用 overridePendingTransition 即可实现 Activity 的出入场动画。
        在 FragmentTransaction 中调用 setCustomAnimation 即可实现 Fragment 切换动画。

64、属性动画
    属性动画是 API11 以后才有的,及时使用了兼容库,11以前本质开始 View 动画。常用的几个动画是 ValueAnimator、ObjectAnimator、AnimatorSet。其中 ObjectAnimator 继承于 ValueAnimator ,AnimatorSet 是动画集合。

ObjectAnimator 使用:
    ValueAnimator colorAnim= ObjectAnimator.ofInt(view,”backgroundColor”,0xffff0000,0xffff00ff);
    colorAnim.set…
    colorAnim.start();

AnimatorSet:
AnimatorSet set = new AnimatorSet();
set.palyTogether(
ObjectAnimator.of…,
ObjectAnimator.of…,
ObjectAnimator.of...
)
set.set…
set.start();

    XML定义:
        位置:res/animator/anim.xml
ValueAnimator    对应标签    <animator>
ObjectAnimator      对应标签    <objectAnimator>
AnimatorSet        对应    <set>

<set>
    <objectAnimator/>
    <animator />
    <set>
    </set>
</set>

代码中加载:
AnimatorSet set = (AnimatorSet)AnimatorInflater.loadAnimator(context,R.anim.anim);
set.setTarget(view);
set.start();

65、WindowManager.LayoutParams 的 type 表示 Window 的层级指定。1~999是应用层级。1000~1999 是子 Window 层级,子 Window 不能单独存在,需要依附到父 Window 上,如 Dialog。2000~2999 是系统层级。系统层级的 Window 需要获取 SYSTEM_ARLRT_WINDOW 权限。其 WindowManager 提供的方法也很简单主要是 addView、updateViewLayout、removeView。

66、Window 是一个抽象的概念,每一个Window 都对应着一个 View 和一个 ViewRootImpl,Window 和 View 通过 ViewRootImpl 来建立联系。

67、Window 的添加过程:
    通过 WindowManager 接口 addView,然后就到了其实现类 WindowManagerImpl addView 里了, WindowManagerImpl 只是桥接了下,全部委托给了 WindowManagerGlobal 来处理,这时 WindowManagerGlobal 的 addView 将被调用,WindowManagerGlobal 内部主要维护了4个 List ,分别存放所有的添加 View、ViewRootImpl、WindowManager.LayoutParams、正在删除的 View。 WindowManagerGlobal 的 addView 时会先 new 一个 ViewRootImpl,然后把这四个对象分别存入相应 List ,然后再调用 root.setView(view, wparams, panelParentView) 让 View 与 ViewRootImpl 建立联系。ViewRootImpl 的内部会调用 View 的绘制入口 scheduleTraversals 。接着会通过 WindowSession 调用 WindowManagerService 来完成 Window 的添加,这个过程是 IPC,WindowSession 其实是一个 Binder, WindowManagerService 内部也会为每个应用 保存一个单独的 Session。

68、Activity 的 Window 创建于添加过程(setContentView 的深入思考):
    从 ActivityThread 的 performLaunchActivity 开始说,里面通过类加载器加载了 Activity 实例后会调用其 Activity 的 attach 方法,在这个方法里面 通过 PolicyManager.makeNewWindow 最终会 new 一个 PhoneWindow 对象并且返回。当我们调用 setContentView 设置 Activity 的布局时,其实是调用了实现了 Window 接口的 PhoneWIndow 的 setContentView 方法,里面会根据情况初始化好 DecorView,还有初始化另外一个包含 id 为 com.android.internal.R.id.content 容器的容器,然后把此容器添加到 DecorView,最后把我们设置的 View 添加到 id 为 com.android.internal.R.id.content 的容器,这时 Window 还不可见,需要等到 ActivityThread 调用完这个 Activity 的 onResume 方法后再调用 makeVisible 时才会把这个 DecorView 通过 WindowManager 的 addView 方法把它和系统建立关联。后面就是 Window 的添加逻辑了。

69、Dialog 的 Window 添加过程和 Activity 很像 也是通过 PolicyManager.makeNewWindow 最终会 new 一个 PhoneWindow 对象并且返回,然后 setContentView 的过程也差不多,在调用 show 的时候会通过 WindowManager 把 DecorView 和系统产生关联。

70、Activity 启动流程:

发起 startActivity 请求后,会调用到 Activity 的 startActivityForResult 方法,其在里面会调用 Instrumentation 的 execStartActivity ,其中有个参数很关键类型是 IBinder 类型的,名字叫 contextThread,实际传入的是 ActivityThread 的内部类 ApplicationThread,ApplicationThread 继承于 ApplicationThreadNative, ApplicationThreadNative 是个Binder ,也就是说 ApplicationThread 也是一个 Binder,还实现了 IApplicationThread 接口,这个接口主要是提供给系统回调用的。在 execStartActivity 方法中会通过一系列调用,然后通过 ServiceManager.getServer(“activity”) 拿到一个类型为 IActivityManager 的 Binder, 这个 Binder 的具体实现在 ActivityManagerService 中,也就是说我们拿到这个 IActivityManager 来通过 IPC 调用 ActivityManagerService 中的方法。是的这就调用了 ActivityManagerService 中的 startActivty 。在 ActivityManagerService startActivty 方法调用过程中会启用 ActivityStackSupervisor 与 AcitvityStack,在里面会有写 栈管理处理,很复杂,最后是调用 ActivityStackSupervisor 的 realStartActivityLocked,在里面会回调相应的应用进程,也就是调用 ApplicationThread 这个 Binder 里面的 scheduleLaunchActivity 方法。scheduleLaunchActivity 会发个消息 外部类 ActivityThread 收到后就调用了 handlerLaunchActivity ,然后就可以看到一些熟悉的方法, 其中 performLaunchActivity 方法是创建 Activity 的,里面通过 Intrumentation new 了一个 Activity,之后会检查 Application 类是否存在,不存在也会通过  Intrumentation new 一个,之后就开始调用这个新 Activity 的 attach 生命周期,attach 需要做很多操作,如创建 PhoneWindow ,且设置一堆监听以获得关联。到此 Activity 基本初始化完毕。


2017.06.24 更

71、理清 Content 的关系:

    
       
    嗯,首先 Context 是一个抽象类,其子类包含有 ContextWrapper ,顾名思义是用来包装 Context 的,打开以后你会发现果然是这个屌样,这里是典型的桥接模式,就是你传入一个 Context 的非抽象实例,然后 ContextWrapper 里面再调用这个类已经实现的方法。就是说这个 ContextWrapper 的子类,如 Service 啊 Activity 啊在实例化的时候都是必须在构造里面传入一个 Context 的实现实例的,它其实就是 ContextImpl,当在 Service 啊 Activity 啊 里面调用 Context 的方法的时候经常是调用的 ContextImpl 里面的方法,当然如果子类重写的情况也很多,如 Activity 就重写了 startActivity 这类方法,不过不管哪里 start 最后肯定还是走那个 Instrumentation 这个入口的。

72、Service 的启动流程:
   
经过 71 的分析后接下的流程就明了多了,首先当 startService 时,就是调用到了其 Dad ContextWrapper 的 startService,然后呢 ContextWrapper 的 startService 又调用了 ContextImpl 的 startService。好的, ContextImpl 的 startService  调用了 startServiceCommon, 其又在里面通过 ActivityManagerNative.getDefault 搞到了 ActivityManagerService,这个前面已经分析过,ActivityManagerService 就是个 Binder ,我们用它来进程间通信。到现在就交给 ActivityManagerService 的 startService ,然后 startService 方法中又交给了 ActivService ,经过错综复杂的调用,终于调用 ActivService 的 realStarServiceLocked ,这个也和 ActivityStackSupervisor 里面的 realStartActivityLocked 很像,在里面会调用 ApplicationThread 这个 Binder 的 scheduleCreateService  ,顾名思义就是要安排创建我们的 Service 了,里面的流程和 Activity 的创建差不多,scheduleCreateService 里面通过 Handler 发送消息,然后会执行 ActivityThread 里面的 handleCreateService, handleCreateService 里面会通过 ClassLoader 创建出这个新 Service,然后调用它的 attach 绑定数据,然后调用 onCreate ,到此 Service 的启动流程就结束了。

73、Service 的绑定过程:
     前面过程进本差不多,从 调用 bindService 开始,然后会到达 ContextImpl 的 bindServiceCommon ,由于 bindService 中 传递的参数 ServiceConnection 不是 Binder 类型的,但绑定过程中存在进程间通信,所以需要先把这个 ServiceConnection 包装下,于是 bindServiceCommon 里面先调用了 LoadedApk 这个类的 getServiceDispatcher ,里面会为每一个 ServiceConnection 创建一个 ServiceDispatcher 对象,且用 ArrayMap 存起来了,ServiceDispatcher 里面又会创建一个 ServiceDispatcher 的内部类 InnerConnection 的对象,InnerConnection 就是一个 Binder ,所以用它在进程间通信是不会有问题的,getServiceDispatcher 里返回的也是这个 InnerConnection 对象。然后在 bindServiceCommon 方法里面往下执行,这就到了我们熟悉的 ActivityManagerNative.getDefault 方法,通过此方法获取了 ActivityManagerService 这个 BInder ,然后调用里面的 bindService ,且带上了 InnerConnection 这个 Binder ,进去后还是通过 ActiveService 调用了相应进程的 ApplicationThread 的 scheduleCreateService ,这个过程就和 72 一样了。不一样的是执行完 schduleCreateService 后这里还会执行 ApplicationThread 的 scheduleBindService ,在 scheduleBindService 里面也是通过 Handle 发送消息然后开始执行 ActivityThread 的 handleBindService,在这里会调用 Service 的 onBind 方法,也就是我们经常复写的方法。这时候基本绑定完毕,但是客户端还没得到通知。于是马上就又通过 ActivityManagerNative.getDefault 获得 ActivityManagerService ,然后调用其 publishService 且传入了 onBind 返回的 Binder,于是又到了 ActivityManagerService 的 publishService 方法里,然后又调用了 ActiveService 的 publishServiceLocked 方法,在里面会找到我们以前传入的那个 InnerConnection ,然后调用 connected ,并且传入 onBind 返回的 Binder 对象,这时候进程通过 Binder 机制又切换了,在 InnerConnection 的 connected 里会找他它外部类 ServiceDispatcher 所对应的一个 ServiceConnection ,这个就是以前我们设置的绑定回调,然后通过 ActivityThread 里面的 Handler post 一个 Runnable,在 Runnable 里面执行了 onServiceConnected ,并把 一个 Binder 传给了我们,这里注意由于是通过 ActivityThread 的那个 Handler post 的所以 onServiceConnected 是在主线程回调的。


2017.06.30 更

74、BroadcastReceiver 的工作过程。
    广播的注册过程:
        其分为静态注册和动态注册,静态注册的广播和其他三大组件一样在安装时由 packageManagerService 解析并完成注册。接着分析广播的动态注册过程。首先当调用 registerReceiver 时,其实还是调用了 Context 的实现类 ContextImpl 的 registerReceiver 方法,这个方法又调用了 registerReceiverInternal ,在 registerReceiverInternal 里的操作和 Service 的绑定有点像,其调用了 LoadedApk 这个类的 getReceiverDispatcher ,在里面会为 BroadcastReceiver 创建一个对应的 ReceiverDispatcher ,然后缓存到 Map 中,ReceiverDispatcher 也是有一个 Binder 类型的内部类,叫 InnerReceiver 用于代替 BroadCastReceiver 进程间通信。getReceiverDispatcher 的返回值就是这个 InnerReceiver , 然后往下就绪执行 registerReceiverInternal ,这时就捡到了老朋友 ActivityManagerNative.getDefault 了,其返回一个 ActivityManagerService ,然后就调用了其 registerReceiver 方法,并传入 ApplicationThread 、InnerReceiver 等几个 Binder 。在 ActivityManagerService 的 registerReceiver 中经过一系列复杂的操作后会把传入的 InnerReceiver 的 Binder 与 IntentFilter 都存起来,这样就完成了广播的注册。

广播的发送和接受过程:
    广播分为有序广播、无序广播、粘性广播,下面已无序广播来分析。
    当调用 sendBroadcast 时,其 ContextWrapper 的 sendBroadcast 被调用,接着 ContextImpl 的 sendBoradcast 被调用,这里面调用了 ActivityManagerService 的 broadcastIntent , broadcastIntent 调用了很复杂的 broadcastIntentLocked,在 从Android 3.1 开始 broadcastIntentLocked 里面会默认给 Intent add 一个  Intent.FLAG_EXCLUDE_STOPPED_PACKAGES 的 flag ,有了这个 flag 后就广播不会发送给停止状态的应用,所谓停止状态就是要么安装后一次都没运行,那么就是被手动或者其他应用强停了。broadcastIntentLocked 里会根据 intent-filter 查找出匹配的广播接受者并经过一系列的条件过滤,把满足条件的广播接受者添加到 BroadcastQueue 中,接着通过 BroadcastQueue 把广播发送给相应广播接受者,具体就是 BroadcastQueue 会调用广播接受者 ApplicationThread 的 scheduleRegisteredReceiver , scheduleRegisteredReceiver 里又会调用 InnerReceiver 也就是 IIntentReceiver 这个 Binder 的 performReceive 方法,最后会找出注册时储存的 BroadcastReceiver ,然后调用它的 onReceive.

75、ContentProvider 所在进程启动时, ContentProvider 会同时启动并发布到 AMS 中。需要注意的是,这个时候 ContentProvider 的 onCreate 要先于 Application 的 onCreate 执行,这是四大组件中少有的现象。

76、ContentProvider 的工作过程
    从 getContentResolver 的 query 开始,getContentResolver 返回的其实一个 ApplicationContentResolver ,它是 这个抽象类的子类也是 ContentImpl 的内部类,在 ContentResolver 的 query 方法中会调用 acquireUnstableProvider 方法,其是子类 ApplicationContentResolver 实现的,在 ApplicationContentResolver 的 acquireUnstableProvider 方法中又把任务交给了 ActivityThread ,调用了其 acquireProvider 方法,在这个方法里会先查找 ActivityThread 中是否已经保存目标 ContentProvider ,如果有就直接返回,没有就通过 ActivityManagerNative.getDefault().getContentResolver() 来向 ActivityManagerService 获取。ContentProvider 所在进程启动时会向 AMS 注册 ContentProvider ,也就是保存在 AMS 中 ,在 AMS 的 ActivityManagerNative.getDefault().getContentResolver() 方法里会检查 AMS 中是否已经保存目标 ContentProvider,应该说是 IContentProvider 这个 Binder,有的话经过一些操作后就返回,没有就调用 startProcessLocked 这个方法启动进程,又是经过一系列复杂的操作,最后会启动目标进程的 ActivityThread 的 静态 main 方法,内部会创建 ActivityThread 实例,并调用 attach 方法将 ApplicationThread 对象通过 AMS 的 attachApplication 方法跨进程传递给 AMS,AMS 的 attachApplication 方法调用了 attachApplicationLocked ,然后 attachApplicationLocked 又调用了 ApplicationThread 这个 Binder 的 bindApplication ,没错又从系统进程切换回来了,bindApplication 通过 Handler 又调用了 ActivityThread 的 handleBindApplication, handleBindApplication 完成了 Application 与 ContentProvider 的创建,ContentProvider 创建完成后通过 AMS 的 publishContentProvider 来注册 ContentProvider ,也就是被存储在了AMS 里,这样其他进程需要的时候就可以通过 AMS 来获取了。 



2017.07.01 更


77、MessageQueue 内部存储结构并不是个真走的队列,而是采用了单链表的数据结构来储存。

78、为什么不允许在子线程访问 UI ?因为 Android 的 UI 控件不是线程安全的,并发访问可能会绘制错乱。

79、系统为何不对 UI 控件加锁?首先 ui 控件逻辑会变得更加复杂,其次由于阻塞了访问线程 ,UI 访问效率也会降低。

80、ThreadLocal 工作原理
    ThreadLocal 能为每个线程保留一份副本,其主要方法是 set 和 get ,当 set 时其实就去获取了当前线程对象里面的一个 Values 对象,然后把 set 的数据存入   Values 里面的一个数组里,当然 get 的时候也就是去当前线程对象里面取出 Values 对象,然后再从对象里面的数组里取出对应的值,每个线程都有不同 Values ,所以就实现了用同一个 ThreadLocal 为每个线程创建副本。

81、由 Handler、Looper、MessageQueue 组成的消息系统
    想要使用消息系统首先要对当前线程的 Looper 进行初始化,以子线程来说就是调用 Looper 的静态 prepare 方法,这时创建 Looper 实例,并在构造中初始化 MessageQueue ,创建完成后把 Looper 实例通过 ThreadLocal 进行 set ,也就是说每个线程的 Looper 将是独立存在的,想要 Looper 开始工作那么就得调用 Looper 的 loop 方法,这时会开启一个无限循环,里面会调用 MessageQueue 的 next 方法, next 的方法里面也是一个无限循环,于是 next 方法就形成了一个阻塞式的方法,当 MessageQueue 里面维护的单链表有没执行的数据时且有一个符合执行时间时, MessageQueue 的 next 的方法会把它取出并返回这个消息,这时, loop 方法的循环体里面就得到一个消息,然后通过  Handler 的 diapathMessage 方法开始分配执行,如果我们 post 了一个 Runnable diapathMessage 就只调用它 run 即可 ,如果没有就会执行 new Handler 时构造里面传入的 CallBack ,如果 CallBack 都没有就调用自身 handleMessage 了。这个过程可以实现任意线程间的通信,原因就是每个线程都有自己的 Looper ,你在任何线程通过一个 new Handler 然后关联上一个需要与之通信线程创建的 Looper,也就是说你可以在需要执行消息的线程 new Handler 给其他线程用(默认关联当前线程的 Looper),也可以在其他线程 new Handler ,但是需要在构造中传入一个需要执行消息的线程的 Looper 对象。在通信过程中可能会因为异步遇到 Looper.prepare 还没执行完,然后其他线程就开始用这个线程的 Looper 的情况,使用 HandlerThread 将可以帮助你完成这个同步。
    HandlerThread 的原理很简单,就是一个同步,加循环的机制。当你需要获取其 Looper 时,将会被检查是否会空,为空将会等待,直到创建完毕然后才返回,所以你拿到的 Looper 就是没问题的。 


2017.07.12 更

82、Looper 提供了 getMainLooper 方法,它可以在任何地方获得主线程的 Looper。

83、Looper 是可以退出的,通过 quit 与 quitSafely 来退出一个 Looper ,二者区别是后者后把剩下的消息执行完才退出。

84、通过 Handler 的 sendMessageAtFrontOfQueue 方法可以向把消息加入到队列前面有限执行,其实原理很简单,这个方法把时间戳传入为 0 了,于是在加入到实质性是链表的队列时根据执行时间的大小重新排序了,小的肯定就排在前面!

85、AsyncTask 在 3.0 以后是串行的,但是你可以通过 executeOnExecutor 传入 一个线程池,比如 AsyncTask.THREAD_POOL_EXECUTOR ,平时调用 execute 时默认传入的是 SerialExecutor 这个线程池,它是用于串行的。

86、3.0 以后 AsyncTask 的默认执行流程:
execute 里调用了  executeOnExecutor,且传入了 SerialExecutor 这个线程池与可变参数,接着调用 onPreExecute ,然后参数会被封装到一个实现了 Callable 的 WorkRunnable 里面( WorkRunnable 在 AsyncTask 构造中就被封装到了一个 FutureTask 里面),接着调用 SerialExecutor 的 execute 并传入 FutureTask ,在 SerialExecutor 的 execute 里面实现了个阻塞的队列,会顺序执行传入的 Runnable ,当 FutureTask 的 run 被调用时,其 WorkRunnable 的 call 被调用,然后 call 方法中会调用 doBackGround,然后调用 postResult ,postResult 中会利用拥有主线程 Looper 的 Handler 来发消息,最后执行 finish 方法,此方法中会调用到 onPostExecute。

87、在 4.1以前(不包括4.1)AsyncTask 第一次访问必须在主线程中加载,这是因为里面的 Handler  需要主线程的 Looper,而这些版本  AsyncTask 里面 Handler 的创建是静态的。4.1 到 5.0 在 ActivityThread 的 main 方法中会调用 AsyncTask 的 init 于是就不存在前面的那些情况了,在 5.0 以后 init 方法没有了,而是采用在 Handler 的派生类的构造中传入一个 Looper.getMainLooper 这种方式。

88、AsyncTask 的 execute 只能执行一次否则会报异常。


89、ThreaPpoolExecutor :
    当执行任务时,如果线程池中的线程数没达到核心线程数时,那么会直接启动一个核心线程来执行,如果已经超过了核心线程数那么就加入到队列中等候,如果队列也满了,但是现在的总线程数没达到最大线程数那么就会立即启动一个非核心线程来执行,如果总线程数达到了最大线程数队列也满了,将会拒绝执行此任务!

90、FixedTheadPool
    其线程数量固定,闲置并不会被回收,全是核心线程,且核心线程无超时,任务队列无限制。

91、CachedThreadPool
    是一种线程数量不固定的线程池,其全部都是非核心线程,最大线程数为 Integer.MAX_VALUE,当有获取请求时如果有闲置线程那么就用闲置线程执行,如果没有那么就创建新线程执行,其每个线程超时时长为60秒,超时将被回收。适合执行大量耗时较少的任务。

92、ScheduledThreadPool
    其核心线程数固定,非核心线程数为 Integer.MAX_VALUE,闲置时非核心线程数会被立即回收。适合定时任务或者周期任务。

93、SingleThreadExecutor
    只有一个核心线程,其确保所有任务只在一个线程顺序执行!

94、Bitmap 的缩放加载:
    缩放步骤:
        实例化 BitmapFactory.Options
        设置其 inJustDecodeBounds = true
         BitmapFactory.decode…
         修改 Options 里面采样率 inSampleSize 的值(大于1有效,一般为 2 的指数)
         设置 inJustDecodeBounds = false
         BitmapFactory.decode… 获取缩放后的 bitmap

95、程序异常处理:
    Thread 支持通过 setDefaultUncaughtExceptionHandler 的方式设置一个 UncaughtExceptionHandler 来处理程序的异常,可以在 UncaughtExceptionHandler 的 unCaughtException 实现处理逻辑,比如保存或者上传错误信息等。

96、当发生 ANR 时会在 /data/anr 目录下创建一个叫 traces.txt 的文件,可通过它追踪发生未知。 


©️2020 CSDN 皮肤主题: 大白 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值