博文
博文

一、Activity

1.什么是activity?

Activity一个应用程序的组件,它提供一个屏幕来与用户交互,以便做一些诸如打电话、发邮件和看地图之类的事情,原话如下:
An [Activity](http://developer.android.com/reference/android/app/Activity.html) is an application component that provides a screen with which users can interact in order to do something, such as dial the phone, take a photo, send an email, or view a map.

2.activity四种状态

running / paused /stopped / killed

running:处于活动状态,用户可以点击屏幕,屏幕会做出相应的响应,处于
activity栈顶的一个状态。

paused:失去焦点的状态,或者是被一个非全屏的activity所占据,又或是被一个
透明的activity放置再栈顶,只是失去了和用户交互的能力,所有的状态信息和成
员变量都还在,除非在内存非常紧张的情况下会被回收。

stopped:一个activity被另一个activity完全覆盖的时候,被覆盖的activtiy的就会
处于stopped状态,所有的状态信息和成员变量都还在,除非在内存非常紧张的
情况下会被回收。

killed:activity已经被系统回收,所有保存的状态信息和成员变量都将不存在了。

3.activity生命周期
Activity生命周期.gif
  • Activity启动➡️ onCreate()➡️ onStart()➡️ onResume()

    onCreate():activity被创建的时候调用,生命周期的第一个调用方法,创建
    activity的时候一定要重写该方法,可以做一些初始化的操作,比如布局资源的加
    载或者一些数据的加载。

    onStart():表明此时activity正在启动,已经处于用户可见的状态,当然此时还没
    有处于前台的显示,就是说此时用户还不能与用户进行交互操作;总结就是一个
    可以看见但是无法触摸的状态。

    onResume():已经在前台进行展示,可以与用户进行交互了。

  • 点击Home键回到手机桌面(Activity不可见):onPause()➡️ onstop()

    onPause():activity处于一个暂停的状态,可见但是不可交互

    onstop():整个activity表明已经完全停止,或者完全被覆盖的状态,完全不可
    见,处于后台的状态,如果处于内存紧张的状态下就有可能会被系统回收掉。

  • 当我们再次回到原Activity的时候:onRestart()➡️ onStart()➡️ onResume()

    onStart():表示activity正在重新启动,是由不可见状态变为可见状态的时候调用

  • 退出Activity时:onPause()➡️ onStop()➡️ onDestory()
    onDestory():当前activity正在销毁,是生命周期中的最后一个方法,可以做一
    些回收工作以及资源的释放
4.进程优先级

可见进程:处于可见但是用户不可点击的进程

前台进程:处于前台正在与用户交互的,或者前台activity绑定的service

后台进程:前台进程点击了home键回到桌面后,这个进程就会转变为后台进
程,不会立马被kill掉,会根据内存的情况来做回收。

服务进程:在后台开启的service服务

空进程:五个进程中优先级最低,是一个没有活跃的组件,只是出于缓存的目的
而保留

二、Activity任务栈

一个用来管理Activity的容器,后进先出的栈管理模式,一个应用中可以有多个任
务栈,也可以只有一个任务栈,这得根据启动模式来决定。

三、Activity启动模式

1.standard

每次启动activity都会重新创建一个activity的实例,然后将它加到任务栈中,不会
去考虑任务栈是否有该activity的实例,默认的情况就是此种启动模式

2.singleTop(栈顶复用模式)

如果需要创建的activity实例刚好处于任务栈的栈顶,那么就不会再去创建一个新
的activity实例了,而是复用这个栈顶的activity。

3.singleTask(栈内复用模式)

是一个单例模式,当需要创建一个activity的时候,会去检测整个任务栈中是否存
在该activity实例,如果存在,会将其置于任务栈的栈顶,并将处于该activity以上
的activity都移除销毁,这时会回调一个onNewIntent()方法。

  • 补充

    默认情况下taskAffinity的值就是包名,taskAffinity相同就说明是在同一个栈中,TaskId即一样
4. singleInstance

singleTask的升级版,整个系统中只有一个实例,启动一singleInstanceActivity
时,系统会创建一个新的任务栈,并且这个任务栈只有他一个Activity,一般用于
多个应用之间的跳转。

  • singleTask与singleTask的却别:
    一个系统中可能存在多个singleTask Activity实例,
    一个系统中只能存在一个singleInstance Activity实例。

四、scheme跳转协议

android中的scheme是一种页面内跳转协议,是一种非常好的实现机制,通过定义自己的scheme协议,可以非常方便的跳转app中的各个页面;通过scheme协议,服务器可以定制化的告诉app跳转哪个页面,可以通过消息栏定义化跳转页面,可以通过H5页面跳转页面等。

二、Fragment

1.Fragment为什么被称为第五大组件

首先在使用上是不属于其他四大组件的,它有自己的生命周期,同时它可以灵活
动态的加载到activity中去,同时它并不是像activity一样完全独立的,它必须依附
于activity,而且还需要加载到activity中去。

2.Fragment加载到Activity中的两种方式

静态加载:只需要把Fragment当成一个普通UI控件放到界面Layout中就完事了

动态加载:得先通过FragmentManage来获取FragmentTransaction(事务),然后使用add或者replace的方式加载fragment,最后commit提交事物。

3.FragmentPagerAdapter与FragmentStatePagerAdapter的区别

FragmentPagerAdapter适用于页面较少的情况下,在源码中destoryItem()函数中的最后一行可以看到,调用的是detach方法,该方法的作用是将fragment与activity的关联取消掉,并没有做内存回收处理,所以会更加的消耗内存


FragmentPagerAdapter_destoryItem.png

FragmentStatePagerAdapter适用于页面较多的情况下,查看源码可发现调用的是remove的方法,会进行内存回收处理。


FragmentStatePagerAdapter_destoryItem.png
4.commit和commitAllowingStateLoss区别

commit:该方法必须在存储状态之前调用,也就是onSaveInstanceState(),如果在存储状态之后调用就会报出:
Can not perform this action after onSaveInstanceState错误信息

commitAllowingStateLoss:允许在存储状态之后再调用,但是这是危险的,因为如果activity需要从其状态恢复,那么提交就会丢失,因此,只有在用户可以意外的更改UI状态的情况下,才可以使用该提交。

以下是部分的核心源码:

commit与commitAllowingStateLoss源码对比.png
5.Fragment的生命周期

下面列出两张图,第一张可用于面试记忆,第二张可以详细的摸索清楚activity与
fragment生命周期的走向。

Fragment生命周期.png fragment与activity生命周期关联图.png

onAttach:将fragment与activity关联
onCreate:初次创建fragment时调用,只是用来创建fragment,此时的activity并没有创建完成
onCreateView:系统在fragment首次绘制界面的时候调用
onViewCreated:fragment的界面已经绘制完毕。

Activity - onCreate:初始化activity中的布局

onActivityCreate:activity渲染绘制完毕

Activity - onStart:activity可见但不可交互操作
onStart:fragment可见但不可交互操作

Activity - onResume:activity已经在前台进行展示,可以与用户进行交互了
onResume:fragment已经在前台进行展示,可以与用户进行交互了

onPause:fragment处于一个暂停的状态,可见但是不可交互
Activity - onPause:activity处于一个暂停的状态,可见但是不可交互

onStop:fragment不可见
Activity - onStop:activity不可见

onDestoryView:与onCreateView关联,fragment即将结束会被保存
onDestory:销毁Fragment,通常按Back键退出或者Fragment被回收时调用此方法
onDetach:会将fragment从activity中移除,和remove()不同,此时fragment的状态依然由FragmentManager维护

Activity - onDesroty:activity销毁

3.Fragment之间的通信

在Fragment中获取Activity中的方法:getActivity
在Activity中获取Fragment中的方法:接口回调
在Fragment中获取Fragment中的方法:先使用getActivity获取到activity,然后使
用getFragmentById

三、Service

1.service是什么

service(服务)是一个可以在用户后台执行长时间操作,而没有用户界面的应用组件
注意:service是运行在主线程中的,绝对是不能做耗时操作的。
请勿将服务与子线程混为一谈

2.两种启动service的方式
  • startService
    1、定义一个类继承Service
    2、在Manifest.xml文件中配置该service
    3、使用Context中的startService(Intent)方法启动该Service
    4、不使用时,使用stopService(Intent)方法停止该服务
    5、onCreate()方法只会调用一次, onStartCommand()方法可以重复调用
    6、服务一旦启动,服务即可在后台无限期的运行,即便启动服务的组件被销毁
    了也不会受影响,除非手动关闭,已启动的服务通常是执行单一操作,而且不会
    将结果返回给调用方

  • bindService
    1、创建BindService服务端,继承自service并在类中,创建一个实现IBinder接
    口的实例对象并提供公共方法给客户端使用
    2、从onBind()回调方法返回此Binder实例
    3、在客户端中,从onServiceConncted()回调方法接收Binder,并使用提供的方法调用绑定服务
    4、当应用组件通过调用 bindService() 绑定到服务时,服务即处于“绑定”状态。
    绑定服务提供了一个客户端-服务器接口,允许组件与服务进行交互、发送请求、
    获取结果,甚至是利用进程间通信 (IPC) 跨进程执行这些操作。 仅当与另一个应
    用组件绑定时,绑定服务才会运行。 多个组件可以同时绑定到该服务,但全部取
    消绑定后,该服务即会被销毁

最后附上一张官方生命周期图:


service生命周期图.png

参考博文

四、Broadcast receiver

1.广播定义

在Android中,Broadcast是一种广泛运用在应用程序之间传输信息的机制,我们
要发送的广播内容是一个Intent,这个Itent中可以携带我们需要传输的数据。

2.广播的使用场景

1、同一app具有多个进程的不同组件之间的消息通信
2、不同app之间的组件之间消息通信

3.广播种类

1、普通广播(Normal Broadcast):Context.sendBroadcast
2、系统广播(System Broadcast):Android中内置了多个系统广播,只要涉及到手
机的基本操作(如开机、网络状态变化、拍照等等),都会发出相应的广播
2、有序广播(Ordered Broadcast):Context.sendOrderedBroadcast
3、本地广播(Local Broadcast):只在app内传播

4.广播的实现

1、静态注册:在AndroidManifest中注册就可以,注册完成就一直运行,如果它
所依赖的activity销毁了,仍然能够接受广播,甚至app 进程杀死仍然能够接受广

2、动态注册:跟随activity生命周期,在代码中调用Context.registerReceiver方法

// 选择在Activity生命周期方法中的onResume()中注册@Override  protected void onResume(){      super.onResume();    // 1. 实例化BroadcastReceiver子类 &  IntentFilter     mBroadcastReceiver mBroadcastReceiver = new mBroadcastReceiver();     IntentFilter intentFilter = new IntentFilter();    // 2. 设置接收广播的类型    intentFilter.addAction(android.net.conn.CONNECTIVITY_CHANGE);    // 3. 动态注册:调用Context的registerReceiver()方法     registerReceiver(mBroadcastReceiver, intentFilter); }// 注册广播后,要在相应位置记得销毁广播// 即在onPause() 中unregisterReceiver(mBroadcastReceiver)// 当此Activity实例化时,会动态将MyBroadcastReceiver注册到系统中// 当此Activity销毁时,动态注册的MyBroadcastReceiver将不再接收到相应的广播。 @Override protected void onPause() {     super.onPause();      //销毁在onResume()方法中的广播     unregisterReceiver(mBroadcastReceiver);     }}

⚠️特别注意

动态广播最好在Activity 的 onResume()注册、onPause()注销。
原因:
对于动态广播,有注册就必然得有注销,否则会导致内存泄露
重复注册、重复注销也不允许

5.广播内部实现机制

1、自定义广播接受者BroadcastReceiver,并复写onRecvice()方法;
2、接收者通过Binder机制向AMS(Activity Manager Service)进行注册;
3、广播发送着通过Binder机制向AMS发送广播;
4、AMS查找符合相应条件(IntentFilter/Permission等)的BrocastReceiver,将广播发送到BrocasrReceiver(一般是Activity)相应的循环队列中;
5、消息循环执行拿到该广播,回调BrocastReceiver的onRecerve()方法

6.LocalBroadcastManager详解

1.LocalBroadcastManager高效的原因主要是它的内部是通过Handler实现的,
sendBrocast()方法含义并非和我们平时所用一样,它的sendBrocast()其实是通过
handler发送一个Message实现的

2.既然它内部是通过Handler来实现广播的发送,那么相比与系统使用Binder实现
自然是更加高效了,同时使用Handler来实现,别的应用无法向我们的应用发送
广播,而我们应用内发送的广播也不会离开我们的广播

有序广播与无序广播的区别

1.无序广播
通过Context.sendBroadcast()方法来发送,它是完全异步的。
所有的receivers(接收器)的执行顺序不确定,因此所有的receivers(接收器)接收broadcast的顺序不确定。
这种方式效率更高,但是BroadcastReceiver无法使用setResult系列、getResult系列及abortbroadcast(中止)系列API。
广播不能被终止,数据不能被修改。
2.有序广播
有序广播,即从优先级别最高的广播接收器开始接收,接收完了如果没有丢弃,就下传给下一个次高优先级别的广播接收器进行处理,依次类推,直到最后。如果多个应用程序设置的优先级别相同,则谁先注册的广播,谁就可以优先接收到广播。通过Context.sendorderBroadCast()方法来发送,sendOrderedBroadcast(intent,
receiverPermission, resultReceiver, scheduler, initialCode, initialData, initialExtras);,其中的参数resultReceiver,可以自己重写一个类,作为一个最终的receive 最后都能够接收到广播,最终的receiver 不需要再清单文件里面配置,initialData可以作为传输的数据
广播可以被终止,数据传输过程中可以被修改。

参考博文
进程保活

五、Webview

1.WebView安全漏洞

Android API level 16以及之前的版本存在远程代码执行安全漏洞,该漏洞源于程
序没有正确限制使用addJavascriptInterface方法,远程攻击者可通过使用
Java Reflection API 利用该漏洞执行任意java对象的方法

2.WebView的性能优化

如果WebView是使用xml进行加载的往往在关闭Activity的时候WebView的资源并不会随着Activity的销毁而销毁。

网上推荐的方法解决大体分为2种:
一种是使用代码动态的创建WebView,对传入WebView中的Context使用弱引用,
动态添加WebView的意思是在布局创建一个ViewGroup用来放置
WebView,Activity创建时add进来,在Activity停止时remove掉

第二种是为WebView新开一个进程,不过这样的话使用起来会比较麻烦,效果还
是挺简单粗暴的,据说QQ微信也在使用这种方式:参考

六、Binder

一、Linux内核的基础知识

1.进程隔离/虚拟地址控件

在操作系统中,为了保护某些进程之间的互不干扰,设计了一个叫进程隔离的技术,就是为了避免进程A可以去操作进程B而实现的;进程的隔离实现用到了虚拟地址空间,就是说进程A的虚拟地址空间和进程B的虚拟空间是不一样的,这样,就防止了进程A的数据可以写到进程B中;所以操作系统中不同的进程之间数据是不共享的,如果需要进程间的数据通信,就需要某种进程间通信的机制来完成,在android中就是使用Binder这个机制来完成的。

不同进程间的引用叫做句柄,相同进程间的应用叫做指针,也就是对进程空间中虚拟地址的引用

2.系统调用

对内核有保护机制,对于应用程序只可以访问某些许可的资源,不许可的资源是不可以访问的。

3.Binder驱动

在android系统中,是运行在内核之中的;负责各个用户进程通过Binder通信的内核来交互的模块叫Binder驱动

二、Binder通信机制介绍

1.为什么使用Binder

1)Android使用的Linux内核拥有着非常多的跨进程通信机制
2)性能:在移动设备上(性能受限制的设备,比如要省电),广泛地使用跨进
程通信对通信机制的性能有严格的要求,Binder相对出传统的Socket方式,更加
高效。Binder数据拷贝只需要一次,而管道、消息队列、Socket都需要2次,共
享内存方式一次内存拷贝都不需要,但实现方式又比较复杂。
3)安全:传统的进程通信方式对于通信双方的身份并没有做出严格的验证,比
如Socket通信ip地址是客户端手动填入,很容易进行伪造,而Binder机制从协议
本身就支持对通信双方做身份校检,因而大大提升了安全性

2.Binder通信模型

Binder通信图.png

客户端进程只不过是持有了我们一个服务端的一个代理

3.到底什么是Binder??
1)通常意义下,binder指的是一种通信机制
2)对于Server进程来说,Binder指的是Binder本地对象;
对于Client对象来说,Binder指的是Binder代理对象
3)对于传输过程而言,Binder是可以进行跨进程传递的对象

博文参考
博文参考
博文参考

七、Handler

1、处理过程:
从handler中获取一个消息对象,把数据封装到消息对象中,通过handler的
send…方法把消息push到MessageQueue队列中。
Looper对象会轮询MessageQueue队列,把消息对象取出。
通过dispatchMessage分发给Handler,再回调用Handler实现的handleMessage
方法处理消息

[图片上传中...(Handler流程图.png-523e3e-1515822798479-0)] Handler流程图.png

2、Handler中为什么要使用ThreadLocal来获取Lopper呢?
因为在不同线程访问同一ThreadLocal时,不管是set方法还是get方法对
ThreadLocal所做的读写操作仅限与各自线程内部,这样就可以使每一个线程有
单独唯一的Lopper。
ThreadLocal博文

3、Handler引起的内存泄漏以及解决办法
原因:在java中,非静态的内部类和匿名内部类都会隐式的持有一个外部类的引
用,所以原因就是匿名内部类持有外部类的引用,导致外部Activity无法释放

解决办法:
1、最直接的思路就是避免使用非静态内部类。使用Handler的时候,放在一个新建的文件中来继承Handler或者使用静态的内部类来替代。静态内部类不会隐含的持有外部类的引用,因此这个activity也就不会出现内存泄漏问题。
2、如果你需要在Handler内部调用外部Activity的方法,你可以让这个Handler持有这个Activity的弱引用,这样便不会出现内存泄漏的问题了。
3、另外,对于匿名类Runnable,我们同样可以设置成静态的,因为静态内部类不会持有外部类的引用。
4、注意:如果使用Handler发送循环消息,最好是在Activity的OnDestroy方法中调用mLeakHandler.removeCallbacksAndMessages(null);移除消息。(这不是解决内存泄漏的方法)
博文

八、AnsycTask

1.Asynctask是什么?
它是Android提供的一个抽象类,他本质上就是一个封装了线程池和handler的异
步框架,主要是来执行异步任务的,由于它内部继承了handler,所以他可以在工
作线程和UI线程中随意切换。

注意:Asynctask能够让你避免使用线程类thread和handler直接处理后台操作,
他可以把运算好的结果交给UI 线程来显示,不过Asynctask只能做一些耗时较短
的操作,如果我们要做耗时较长的操作,我们还是尽量使用线程池。

2.Asynctask使用
它主要有三个重要参数,五个重要方法

  • 三个参数,三种泛型分别代表:
public abstract class AsyncTask { }第一个:“启动任务执行的输入参数”。第二个:“后台任务执行的进度”。第三个:“后台计算结果的类型”。
  • 五个方法:
第一种:execute(params…params):执行一个异步任务,需要我们在代码中调用此方法,触发异步任务的执行(必须在UI线程中调用)第二种:onPreExecute(),在execute(Params… params)被调用后立即执行,一般用来在执行后台任务前对UI做一些标记(也可以理解为初始化数据)。第三种:doInBackground(Params… params),在onPreExecute()完成后立即执行,用于执行较为费时的操作,此方法将接收输入参数和返回计算结果。在执行过程中可以调用publishProgress(Progress… values)来更新进度信息。第四种:onProgressUpdate(Progress… values),在调用publishProgress(Progress… values)时,此方法被执行,直接将进度信息更新到UI组件上。第五种:onPostExecute(Result result),当后台操作结束时,此方法将会被调用,计算结果将做为参数传递到此方法中,直接将结果显示到UI组件上。

3.Asynctask的内部原理

1、Asynctask的本质是一个静态的线程池,Asynctask派生出的子类可以显现不同的异步任务,这些任务都是提交到静态的线程池中执行。2、线程池中的工作线程执行doinbackground(mparams)方法来执行异步操作。3、当任务状态改变后,工作线程会向UI线程发送消息,Asynctask内部的internalHandler相应这些消息,并调用相关的回调。

4.Asynctask的注意事项

一、内存泄露。和handler造成的问题一样。非静态内部类持有外部类的匿名引用,导致外部activity像被内存回收,由于非静态内部类还持有外部类的引用,导致外部类不能被系统回收,造成内存泄露。解决方法: 1、谁为静态内部类。 2、非静态内部类使用弱引用。 3、在destroy方法中finish。5.为什么AsyncTask只会执行一次AsyncTask定义了一个mStatus变量,表示异步任务的运行状态,分别是PENDING、RUNNING、FINISHED,当只有PENDING状态时,AsyncTask才会执行,这样也就保证了AsyncTask只会被执行一次二、生命周期如果在外部类被销毁的时候,没有执行onCancelled方法,这有可能让我们的activity在销毁之前导致崩溃。因为Asynctask正在处理某些view。但是activity不存在了,所以就会崩溃。三、结果丢失 假如说,activity被系统回收或者横竖屏切换,导致activity被重新创建,而之前运行的Asynctask会持有之前activity的引用,但是这个引用已经无效,所以这个时候,你调用onpostExecute()方法去更新界面就会失败,因为结果已经丢失。

5.为什么AsyncTask只能执行一次?
应该说同一时间段内,只执行一次,防止对同一件事进行操作,造成的混乱。我
们知道异步的线程执行的顺序很大程度上是不确定的,有可能先执行的最后执行
完成,后执行的反而先完成。如果在同一时间段内,执行多个线程这里就会出现
混乱。

6.什么是并行,什么是串行,两者的区别是什么
在android1.6版本之前,Asynctask都是串行,也就是他会把所有任务,一串一串
的放在线程池中。但是在1.6到2.3的版本中,它改成了并行。为了维护系统的稳
定,2.3版本后又改回了串行,但是还是可以执行并行的,建议使用串行

来自博文
推荐

九、HandlerThread

1.由来
当我们需要做一个耗时操作时,自然而然会启动一个子线程去执行,当子线程执
行完成之后,就会自动关闭掉;如果过了一会我们又想执行一个耗时操作呢,那
就又得重新开启一个子线程,这样重复的来回开启是非常消耗性能的;而在
android中就为我们提供了HandlerThrea来完成这样的操作。

2.特点

  • HandlerThread本质上是一个线程类,它继承了Thread;
  • HandlerThread有自己内部的Lopper对象,可以进行looper循环;
  • HandlerThread中是不可以处理界面更新的,还是得交给Handler处理;
  • 优点是不会堵塞,减少了对性能的消耗;缺点是不能同时进行多任务
    的处理,需要等待进行处理,处理效率比较低;
  • 与线程池重并发不同,HandlerThread是一个串行队列,HandlerThread背后只
    有一个线程

推荐博文

十、intentService

1.IntentService是什么
IntentService是继承并处理异步请求的一个类,在IntentService内有一个工作线程来处理耗时操作,启动IntentService的方式和启动传统的Service一样,同时,当任务执行完后,IntentService会自动停止,而不需要我们手动去控制或stopSelf()。另外,可以启动IntentService多次,而每一个耗时操作会以工作队列的方式在IntentService的onHandlerIntent回调方法中执行,并且,每次只会执行一个工作线程,执行完第一个再执行第二个。

2.IntentService使用方法
创建IntentService时,只需要实现onHandlerIntent方法和构造方法,onHandlerIntent为异步方法,可以执行耗时操作。

十一、View绘制

首先是measure(测量) 其次 layout(布局) 最后 draw(绘制)

1.measure

测量中首先得知道测量规则,MeasureSpec这个类

MeasureSpec这个类包括测量模式(specModel)
和测量模式下的规格大小(specSize)

MeasureSpec表示形式是32位的int值
前两位是specModel 后30位是specSize

我们都知道SpecMode的尺子类型有很多,不同的尺子有不同的功能,而SpecSize刻度是固定的一种,所以SpecMode又分为三种模式

UNSPECIFIED:父容器不对View有任何大小的限制,这种情况一般用于系统内部,表示一种测量状态
EXACTLY:父容器检测出View所需要的精确大小,这时候View的值就是SpecSize
AT_MOST:父容器指定了一个可用大小即SpecSize,View的大小不能大于这个值

measure是final修饰的,需要重写的方式是onMeasure()

measure.png
2.layout
3.draw
4.Android视图工作机制中的重绘

一、invalidate()和requestLayout()

invalidate()和requestLayout(),常用于View重绘和更新,其主要区别如下

invalidate方法只会执行onDraw方法
requestLayout方法只会执行onMeasure方法和onLayout方法,并不会执行onDraw方法。
所以当我们进行View更新时,若仅View的显示内容发生改变且新显示内容不影响View的大小、位置,则只需调用invalidate方法;若View宽高、位置发生改变且显示内容不变,只需调用requestLayout方法;若两者均发生改变,则需调用两者,按照View的绘制流程,推荐先调用requestLayout方法再调用invalidate方法

二、invalidate()和postInvalidate()

invalidate方法用于UI线程中重新绘制视图
postInvalidate方法用于非UI线程中重新绘制视图,省去使用handler

博文

十二、事件分发

1、简要的谈谈Android的事件分发机制?

当点击事件发生时,首先Activity将TouchEvent传递给Window,再从Window传递给顶层View。TouchEvent会最先到达最顶层 view 的 dispatchTouchEvent ,然后由 dispatchTouchEvent 方法进行分发,如果dispatchTouchEvent返回true ,则整个事件将会被销毁,如果dispatchTouchEvent返回 false ,则交给上层view的 onTouchEvent 方法来开始处理这个事件,如果 interceptTouchEvent 返回 true ,也就是拦截掉了,则交给自身的 onTouchEvent 来处理,如果 interceptTouchEvent 返回 false ,那么事件将继续传递给子 view ,由子 view 的 dispatchTouchEvent 再来开始这个事件的分发。如果事件传递到某一层的子 view 的 onTouchEvent 上了,且这个方法返回了 false ,那么这个事件会从这个 view 往上传递,都是 onTouchEvent 来接收。而如果传递到最顶view的 onTouchEvent 也返回 false 的话,这个事件就会消失,直到onTouchEvent返回true为止。

2、为什么View有dispatchTouchEvent方法?

因为View可以注册很多事件的监听器,如长按、滑动、点击等,它也需要一个管理者来分发

3、ViewGroup中可能有很多个子View,如何判断应该分配给哪一个?

根据源码可知,它会分配给在点击范围内的子View

4、当点击时,子View重叠应该如何分配?

一般分配给最上层的子View,这是由于安卓的渲染机制导致的

博文

1.简介

Android事件分发机制的发生在View与View之间或者ViewGroup与View之间具有镶嵌的视图上,而且视图上必须为点击可用。当一个点击事件产生后,它的传递过程遵循如下顺序:Activity->Window->View,即事件先传递给Activity,再到Window,再到顶级View,才开始我们的事件分发

2.概念

Android事件分发机制主要由三个重要的方法共同完成的

dispatchTouchEvent:用于进行点击事件的分发
onInterceptTouchEvent:用于进行点击事件的拦截
onTouchEvent:用于处理点击事件
这里需要注意的是View中是没有onInterceptTouchEvent()方法的

dispatchTouchEvent

  • return true:表示该View内部消化掉了所有事件
  • return false:表示事件在本层不再继续进行分发,并交由上层控件的onTouchEvent方法进行消费
  • return super.dispatchTouchEvent(ev):默认事件将分发给本层的事件拦截onInterceptTouchEvent方法进行处理

onInterceptTouchEvent

  • return true:表示将事件进行拦截,并将拦截到的事件交由本层控件的onTouchEvent进行处理
  • return false:表示不对事件进行拦截,事件得以成功分发到子View
  • return super.onInterceptTouchEvent(ev):默认表示不拦截该事件,并将事件传递给下一层View的dispatchTouchEvent

onTouchEvent

  • return true:表示onTouchEvent处理完事件后消费了此次事件
  • return fasle:表示不响应事件,那么该事件将会不断向上层View的onTouchEvent方法传递,直到某个View的onTouchEvent方法返回true
  • return super.dispatchTouchEvent(ev):表示不响应事件,结果与return false一样

十三、ListView

参考

十四、构建

十五、Okhttp

1.Okhttp简单使用

//首先创建一个Handlerprivate Handler mHandler = new Handler() {        @Override        public void handleMessage(Message msg) {            switch (msg.what) {                    imageView.setImageBitmap((Bitmap) msg.obj);                    break;            }        }};//然后使用OkHttp发送网络请求,并将结果通过Handler发送到主线程public void sendRequestByOkHttp() {        //创建Request对象,并传入请求地址        Request request = new Request.Builder().url( "http://tnfs.tngou.net/img/ext/160321/e57d5816cb72d7486aa6dbf19a7d0c6c.jpg").build();        //构造Call对象--其实是AsyncCall对象        Call call = client.newCall(request);        //调用Call.enqueue方法进行异步请求        call.enqueue(new Callback() {            @Override            public void onFailure(Call call, IOException e) {                //网络请求失败                Log.d("lenve", "onFailure: " + e.getMessage());            }            @Override            public void onResponse(Call call, Response response) throws IOException {                //网络请求成功,将请求的图片信息显示的ImageView控件上                Bitmap bitmap = BitmapFactory.decodeStream(   response.body().byteStream());                Message msg = mHandler.obtainMessage();                msg.what = 1;                msg.obj = bitmap;                mHandler.sendMessage(msg);            }        });    }

2.如何使用OkHttp进行异步网络请求,并根据请求结果刷新UI

3.可否介绍一下OkHttp的整个异步请求流程

4.OkHttp对于网络请求都有哪些优化,如何实现的

5.OkHttp框架中都用到了哪些设计模式

OkHttp的底层是通过Java的Socket发送HTTP请求与接受响应的(这也好理解,HTTP就是基于TCP协议的),但是OkHttp实现了连接池的概念,即对于同一主机的多个请求,其实可以公用一个Socket连接,而不是每次发送完HTTP请求就关闭底层的Socket,这样就实现了连接池的概念。而OkHttp对Socket的读写操作使用的OkIo库进行了一层封装。

博文

十六、retrofit

retrofit的原理其实就是这样,拦截到方法、参数,再根据我们在方法上的注解,去拼接为一个正常的Okhttp请求,然后执行。

十七、volley

1.首先我们初始化一个RequestQueue,因为初始化RequestQueue非常的消耗性能,所以只需要初始化一次即可
2.在newRequestQueue中我们会发现,如果Android版本大于2.3会调用HttpUrlConnection的HurlStack,如果Android版本小于2.3会调用一个HttpClient的HttpClickStack
3.接下来Volley会创建RequestQueue,并调用他的start方法
4.接着会创建1个缓存调度线程(CacheDispatcher)和4个网络调度线程(netDispatcher),并分别调用他们的start方法,所以会默认开启5个后台线程,线程优先级都为10
5.然后分别去轮询各自的消息队列
6.Volley是如何把请求的数据回调回主线程中的?

使用Handler.postRunnable(Runnable)方法回调回主线程中进行处理,ExecutorDelivery的构造方法中可以看到这段代码


volley工作流程.png

博文
推荐博文

十八、ButterKnife

1.使用
  • 绑定一个view
  • 给一个view添加点击事件
  • 给多个view添加点击事件
  • 给ListView 设置 setItemClickListener
2.原理

⚠️ 首先我们应该避免一个误区,butterknife中并不是用的反射的原理来实现的,
因为用反射的话性能会很差

自己用反射实现原理:通过反射获得类中所有注解的属性,并且获得注解当中资
源中Layout的值,最后通过反射findViewById获取到这个View,并赋值给Activity
当中的属性。
很大的缺点是在运行时会大量的使用到反射,而反射其实会影响app的性能,特
别是运行时的性能,容易造成卡顿;又会产生很多的临时变量,临时变量又会引
起垃圾回收,在UI优化中,大量频繁的变量的垃圾回收会造成UI卡顿,UI卡顿也
是一种性能的标志之一。

而我们的butterknife没有使用这样的方法。butterknife使用的是java的注解处理技
术,也就是在代码编译的时候,编译成字节码的时候,它就处理好了注解操作

注意:java的注解处理技术是在编译阶段执行的,它的原理是通过读入我们的源
代码,解析注解然后生产新的java代码,而新生产的java代码当中,最后被编译
成java字节码,由于注解解释器它是不能改变读入的java类的。这就是butterknife的注解原理。

梳理ButterKnife原理流程

  • 开始它会扫描java代码中所有的ButterKnife注解
  • ButterKnifeProcssor --->$$ViewBinder
  • 调用bind方法加载生成的ViewBinder类
3.绑定view的时候,为什么不能private和static??

因为性能问题,如图我们我们把这个view设置成private,那么这个框架他就不能
通过注解的方式来获取了,只能通过反射来获取,此时不管你的性能有多快,都
会影响性能。这是必须注意并且避免的。这也就是和其他注解方式不同的一点。

来自博文

十九、Glide加载图片

Glide原理的核心是为bitmap维护一个对象池。对象池的主要目的是通过减少大对象内存的分配以重用来提高性能
glide与picasso对比
glide原理

二十、ANR

1.什么是ANR?

Application Not Responding

在主线程中做耗时的操作,导致长时间无法对用户的操作做出响应,Activity最长
为5秒,BrocastReceiver最长为10秒,Service最长为20秒。

2.原因:应用程序的响应性是由Activity Manager和Window Manager系统服务监视的,当它监测到组件在相应的响应的时间没有响应就会弹出ARN的提示。
  • 哪些操作是在主线程中呢?
    Activity中所有生命周期的回调都是在主线程中。

    Service默认是执行在主线程中的。

    BrocastReceiver中的onReceve()回调执行在主线程中。

    没有使用子线程的Lopper的Handler的handlerMessage,post(Runner)是执行在
    主线程中的。

    AsyncTask中除了doInBackground,其他方法都是执行在主线程中的。

3.处理:
  • 使用AnsycTask处理耗时IO操作
  • 使用Thread或者HandlerThread提高优先级
  • 使用Handler来处理工作线程的耗时任务
  • Activity的onCreate()和onResume()回调中尽量避免耗时代码

来自博文

二十一、OOM

1.什么是oom?

当前占用的内存加上我们申请的内存资源超过了Dalvik虚拟机的最大内存限制就
会抛出的out of memory 异常。

博文

二十二、bitmap

1.recycle
2.LRU

LRUCache原理
LruCache中维护了一个集合LinkedHashMap,该LinkedHashMap是以访问顺序(accessOrder为true,其余非构造函数此值全为false)排序的。当调用put()方法时,就会在集合中添加元素,并调用trimToSize()判断缓存是否已满,如果满了就用LinkedHashMap的迭代器删除队尾元素,即近期最少访问的元素。当调用get()方法访问缓存对象时,就会调用LinkedHashMap的get()方法获得对应集合元素,同时会更新该元素到队头。
LinkedHashMap内部是使用双向循环链表来存储数据的。也就是每一个元素都持有他上一个元素的地址和下一个元素的地址。

3.计算inSampleSize
4.缩略图
5.三级缓存

二十三、UI卡顿问题

1.UI卡顿原理

60fps --> 16ms
60fps:1秒60桢,1000/16
16ms:android中每隔16ms会进行一个渲染,16ms以内完成不了就会导致界面
的卡顿

每次dalvik虚拟机进行GC的时候,所有的线程都会暂停,所以说在16ms内正在
渲染的时候正好遇到了大量的GC操作,就会导致渲染时间不够,从而导致UI卡顿.

overdraw:过度绘制,在屏幕上,它的某个像素在同一桢的时间内绘制了很多
次,经常出现在多层次的UI结构中。可以在手机中的GPU选项中进行观察,有蓝
色、淡绿、淡红、红色、深红色,减少红色尽量出现蓝色。

2.UI卡顿原因分析

1.人为的在UI线程中做轻微的耗时操作,导致UI线程卡顿;
2.布局Layout过于复杂,无法在16ms内完成渲染;
3.同一时间动画执行的次数过多,导致CPU或GPU负载过重;
4.View过度绘制,导致某些像素在同一帧时间内被绘制多次,从而使CPU或GPU
负载过重;
5.View频繁的触发measure、layout,导致measure、layout累计耗时过多及整个View频繁的重新渲染;
6.内存频繁触发GC过多(同一帧中频繁创建内存),导致暂时阻塞渲染操作;
7.冗余资源及逻辑等导致加载和执行缓慢;
8.臭名昭著的ANR;

3.总结

1.布局优化;尽量使用include、merge、ViewStub标签,尽量不存在冗余嵌套及过于复杂布局(譬如10层就会直接异常),尽量使用GONE替换INVISIBLE,使用weight后尽量将width和heigh设置为0dp减少运算,Item存在非常复杂的嵌套时考虑使用自定义Item View来取代,减少measure与layout次数等。

2.列表及Adapter优化;尽量复用getView方法中的相关View,不重复获取实例导致卡顿,列表尽量在滑动过程中不进行UI元素刷新等。

3.背景和图片等内存分配优化;尽量减少不必要的背景设置,图片尽量压缩处理显示,尽量避免频繁内存抖动等问题出现。

4.自定义View等绘图与布局优化;尽量避免在draw、measure、layout中做过于耗时及耗内存操作,尤其是draw方法中,尽量减少draw、measure、layout等执行次数。

5.避免ANR,不要在UI线程中做耗时操作,遵守ANR规避守则,譬如多次数据库操作等

来自博文

二十四、内存泄漏

1.java内存泄漏基础知识

该被释放的对象没有被释放,一直被某个或者某个实例所持有,导致不能垃圾回收

博文

二十五、内存管理

博文

二十六、冷启动优化

1.什么是冷启动
2.冷启动流程
3.如何对冷启动的时间进行优化

博文

二十七、其他优化

1.android中不用静态变量存储数据
2.关于sp的安全问题
3.内存对象序列化
4.避免ui线程中做繁重的操作

博文

二十七、mvc/mvp/mvvm

博文1
博文2
博文3

二十八、插件化

二十九、热修复
原理
三十、进程保活

1.Android进程的优先级
  • 前台进程
  • 可见进程
  • 服务进程
  • 后台进程
    *空进程
2.Android进程的回收策略
  • Low momery killer:通过一些比较复杂的评分机制,对进程进行打分,然后将分数高的进程判定为bad进程,杀死并释放内存。

  • OOM_ODJ:判读进程优先级

3.进程保活方案
  • 利用系统广播进行拉活
  • 利用系统的Service机制拉活
  • 利用native进程拉活(5.0以后无法实现)
  • 利用 JobScheuler机制拉活(5.0以后新加)
  • 利用账号同步机制拉活

博文
保活

三十一、UIL

三十二、Lint

1.什么是Android lint 检查

Android Lint 是一个静态代码分析工具,它能够对你的Android项目中潜在的bug、可优化的代码、安全性、性能、可用性、可访问性、国际化等进行检查。

2.工作流程

他有个lint tool 工具,它会把我们的Android源代码和lint xml配置文件打包成一个文件之后,输出一个lint output ,并展现出具体哪行代码有问题,我们可以定位到具体问题所在,改写代码,保证上线时的代码质量

3.配置lint

1.配置lint.xml
2.java代码和xml布局文件如何忽略lint检查
3.自定义lint
1)原因

三十三、Kotlin

1.kotlin到底是什么
  • 是一种基于JVM的编程语言
  • 是对java的一种扩展
  • kotlin支持函数式编程
  • kotlin类与java类能相互调用
2.kotlin的环境搭建
  • Android studio安装kotlin插件
  • 编写kotlinActivity测试
  • kotlin会自动配置kotlin

三十四、LruCache

LruCache源码分析
DiskLruCache

LruCache是一个针对内存层面的缓存,基于LRU算法(最近最少使用)实现的。
内部主要是使用LinkHashMap来存储数据,LinkHashMap是继承HashMap的,主要是重写了get方法,当获取数据的时候会把自己添加到节点的头部,保证处于链表的最前面;不常用的数据就会排在最后面,当缓存的数据满了之后,一般是申请进程空间的1/8,就会清楚掉排在最后的这些数据,trimToSize()方法中执行

public V get(Object key) {        //使用HashMap的getEntry方法,验证了我们所说的查询效率为O(1)        LinkedHashMapEntry e = (LinkedHashMapEntry)getEntry(key);        if (e == null)            return null;        e.recordAccess(this);//在找到节点后调用节点recordAccess方法 往下看        return e.value;    }        //LinkedHashMapEntry        void recordAccess(HashMap m) {            LinkedHashMap lm = (LinkedHashMap)m;            if (lm.accessOrder) {//accessOrder该值默认为false 但是  在LruCache中设置为true                lm.modCount++;                remove();//该方法将本身节点移除链表                addBefore(lm.header);//将自己添加到节点头部 保证最近使用的节点位于链表前边            }        }

三十五、算法

八大算法

更多相关文章

  1. Android(安卓)后台任务(六)IntentService
  2. android TextView实现多种颜色显示
  3. Android跨线程消息机制
  4. android 设置activity启动退出动画 | 解决设置activity 动画不生
  5. [Android(安卓)Training视频系列]6.1 Saving Key-Value Sets
  6. Android之不同Activity之间的数据传递
  7. Android(安卓)ANR 分析解决方法
  8. Android调用ITelephony类的endCall()方法 实现来电拒接
  9. 多条未接来电只显示来电数,同时拨号应用通知小圆点一直显示

随机推荐

  1. Android(安卓)获取新装程序信息
  2. 关于Contacts的那点事儿
  3. 利用wifi连接Android真机调试React Nativ
  4. Content Providers
  5. android菜鸟学习笔记31----Android使用百
  6. Android(安卓)实现一个简单的画板功能
  7. odex 转 dex
  8. android 网络连接保活
  9. Android引导页Splash设计
  10. Android(安卓)6.0新控件属性记录