Android阿里、京东、美团等大厂面试Android篇

webView相关知识点

原生和H5交互

一、原生访问 h5

H5暴露全局方法提供给app调用
1.使用webView.loadUrl(javascript: jsString), jsString是要调用的js代码的字符串。

2.主要步骤:

(1)在H5页面写一个方法

(2)原生通过webView.loadUrl直接调用

 

二、 H5访问原生的方法

原理:原生通过webViewd的API-addJavascriptInterFace,传递一个自己事先写好的接口类并声明接口类的名称,接口类通过注解:@Javascriptinterface暴露自己的方法给h5 , h5通过注解直接访问这个接口类的方法去获取js页面的值,原生再通过传递给H5的对象获取js传递过来的值。

主要步骤:

  1. webView设置属性setJavaScriptEnable允许原生加载JS
  2. 编写js接口类。
  3. 使用webView加载js的API- addJavascriptInterFace。

完整示例如下:

(一)编写js接口类

注意这个js中的方法执行实在异步线程,要想设置UI需要使用Handler来实现。

(二)编写js代码:

(三)原生加载html,调用JS方法

三、webView的加载优化

1、离线缓存:webView的缓存功能。

setAppCacheEnabled(true);  setDatabaseEnabled(true);

setDomStorageEnabled(true);//开启DOM缓存,关闭的话H5自身的一些操作是无效的settings.setCacheMode(WebSettings.LOAD_DEFAULT);

WebSettings.LOAD_DEFAULT是默认的缓存策略,它在缓存可获取并且没有过期的情况下加载缓存,否则通过网络获取资源。减少页面的网络请求次数,在加载页面的时候可以通过判断网络状态,在无网络的情况下更改webview的缓存策略。无网就用缓存。

二、预加载:解决方案是将这些资源打包进APK里面,然后当页面加载这些资源的时候让它从本地asset目录下去获取,这样可以提升加载速度也能减少服务器压力。

四、webView加载异常问题的处理

(一)页面加载白屏

1.通过清除webview的缓存,让app每次进入该H5界面时都重新加载

// 清缓存和记录,缓存引起的白屏

 mWebView.clearCache(true);

 mWebView.clearHistory();

mWebView.requestFocus();

2.可以通过setAppCacheEnabled方法来控制webview是否缓存

// 应用可以有缓存 true false 没有缓存

webSettings.setAppCacheEnabled(false);

3.webview加载H5界面时,H5中的一些控件标签可能使用后android中不支持,可以使用setDomStorageEnabled方法来处理:

// 解决对某些标签的不支持出现白屏

webSettings.setDomStorageEnabled(true);

4.在不同android版本上出现白屏的情况:

mWebView.setWebViewClient(new WebViewClient() {       

     @Override

        public boolean shouldOverrideUrlLoading(WebView view, String url) {

// 重写此方法表明点击网页里面的链接还是在当前的webview里跳转,不另跳浏览器

       // 在2.3上面不加这句话,可以加载出页面,在4.0上面必须要加入,不然出现白屏

       if (url.startsWith("http://") || url.startsWith("https://")) {

                                   view.loadUrl(url);

                                   mWebView.stopLoading();

                                   return true;

                            }

                            return false;

                     }

                     @Override

                     public void onReceivedError(WebView view, int errorCode,

                                   String description, String failingUrl) {

                            super.onReceivedError(view, errorCode, description, failingUrl);

                     }

              });


5.Webview的控件布局时设置:

    <WebView

        android:id="@+id/web"

        android:layout_width="fill_parent"

        android:layout_height="fill_parent"

        android:layerType="software"

        android:scrollbars="none" />


6.通过android系统的加速器来配置://开启硬件加速

< application    android:hardwareAccelerated="true" ...>   

() 重定向

1.原因:

WebView首先加载A链接,然后在WebView上点击一个B链接进行加载,B链接会自动跳转到C链接,这个时候调用WebView的goback方法,会返回到加载B链接,但是B链接又会跳转到C链接,从而导致没法返回到A链接界面

2、解决方案:

(1)自己维护一个webview的历史栈,根据自己的需求进行过滤跳转或者重新加载页面:判断到当前为重定向后的链接,那么那么当回退的时候就需要忽略上一级的链接,不使用webview.goback(),移除重定向和重定向后的url,获取到初始页面链接后自己进行loadUrl()操作。

(2)还有一种方法,和方法2类似,需要自己维护webview的历史栈,但是需要前端的配合,提供js函数获取网页是否进行重定向

在webviewClient回调shouldoverloading()中过滤url时,若属于重定向的地址,则不加入栈中,回退时根据历史栈加载即可。

(三)webView加载遇到的安全证书问题
1.加载的SSL页面的证书有问题,比如过期、信息不正确、发行机关不被信任等,WebView就会拒绝加载该网页实际上在WebView里onReceivedSslError()中用handler.proceed();,以实现加载证书有问题的页面。

  Android ClassLoader的原理和机制

  • APK文件目录

classLoader主要是把classes.dex文件加载到内存中。

二、类加载过程中3个重要的类加载器

1、系统类加载器:AppClassLoader:负责在JVM启动时,主要加载classpath路径下的包。

2、扩展类加载器:ExtClassLoader:主要负责加载Java的扩展类库,默认加载JAVA_HOME/jre/lib/ext/目录下的扩展包。

3、启动类加载器:BootstrapClassLoader:纯C++实现的类加载器,没有对应的java类,主要加载jre/lib/目录下核心库。

三、类加载加载机制原理

AppClassLoader类的加载过程(父委托机制(安全性高)):一个classLoader通过loadClass(String name)加载一个class。

源码剖析:

从代码看,通过方法findLoadClass来判断一个类是否已经被加载,如果已经被加载直接返回类对象,如果第一次加载,先判断parent类(ExtClassLoader),如不为空,用parent去加载,如果加载出现异常就自己去加载,如果parent为空,就用Native的BootstrapClassLoader去加载。

项目中一个普通类被加载的图示:

四、Android中常用的类加载器

1、PathClassLoader:主要加载/data/app目录下的apk文件。

2、DexClassLaoder:加载任何路径下的apk、dex、jar包。

继承关系图:

五、Android中的分包和动态加载方案。

(一)插入式动态加载方案原理描述:

一个APK的默认类加载器是PathClassLoader,apk用它去加载一个类,它首先会去委托父类BootClassLoader去加载,如果需要加载指定的dex文件,则把自定义的dexClassLoader插入PathClassLoader和BootClassLoader之间,PathClassLoader先委托dexClassLoader去加载,dexClassLoader会委托BootClassLoader去加载。

代码实现:

  1. 在APP启动的最早时机去加载自定义dex文件。

  1. 创建存放dex和odex的目录

3、copy assets目录下的libs.apk到dexDir目录下。

4、获取application的类加载器

第一个参数需传分dex的路径;第二个参数需传odex的路径;第三个参数需传so库的路径;第四个参数传loader.getParent();

如果项目涉及so库,则获取方法:

5、将loader的加载器设置成dexClassLoader. 将dexClassLoader插入PathClassLoader和BootClassLoader之间。

6、清单文件配置自定义的application

代表:Ant+eclispe实现分包:Ant是一种基于Javabuild工具。ANT使用需下安装,配置量,拷antbuild.xml文件到项目目录中去,然后进行修改。这种方案可以指定哪些类放入分Dex,但分Dex不能混淆,如果分Dex中引用了主Dex中的类,那么此方法失效

(二)末尾追加式动态加载方案原理描述:

主dex就一个文件,分dex可以有一个或者多个文件,可以将dexClassLoader中的element数组追加到PathClassLoader加载路经中。

1.通过反射找到loader的pathlist

2、追加dex文件到数组中

gradle+AS实现分包

1、在defaultConfig中添加multiDexEnabled true这个配置项。完成后还需要在dependencies中添加multidex的依赖:

compile 'com.android.support:multidex:1.0.0'

2、重写Application的attachBaseContext方法,该方法放到onCreate前执行

public class MyApplication extends Application{

@Override

    protected void attachBaseContext(Context base) {

        super.attachBaseContext(base);

        MultiDex.install(this);

    }

}

注:Dex可以混淆,无法定制放入哪些类到分Dex

                  JNI开发

一、NDK开发

定义:Native Development Kit,是Android的一种开发工具包

作用:快速开发C、 C++的动态库,并自动将so和应用一起打包成 APK,即可通过 NDK在 Android中 使用 JNI与本地代码(如C、C++)交互。

二、什么是JNI?

定义:Java Native Interface,通过JNI能使java调用c++

三、Android中使用JNI七个步骤:

1.创建一个android工程

2.JAVA代码中写声明native 方法 public native String helloFromJNI();

  1. 用javah工具生成头文件

java中声明的Native方法要在C中生成头文件,头文件可以理解为要在C中实现的方法,其中 JENEnv* 代表的是java环境 , 通过这个环境可以调用java的方法,jobject 表示哪个对象调用了 这个c语言的方法, this就表示的是当前的对象。

  1. 创建jni目录,引入头文件,根据头文件实现c代码

  1. 编写Android.mk文件

6.Ndk编译生成动态库

7.Java代码load 动态库.调用native代码和java代码相互调用。

 

 一次完整的HTTP请求过程

一、 HTTP请求和响应步骤

1.TCP三次握手

TCP协议提供可靠的连接服务,通过三次握手实现连接,三次握手的目的是同步连接双方的序列号和确认号并交换 TCP窗口大小信息。

https://imgconvert.csdnimg.cn/aHR0cDovL3VwbG9hZC1pbWFnZXMuamlhbnNodS5pby91cGxvYWRfaW1hZ2VzLzM5ODU1NjMtZjJmZTM3NzViZDI2NzhjMi5wbmc?x-oss-process=image/format,png

第一次握手:建立连接客户端发送连接请求报文段,客户端进入发送状态,等待服务器的确认;

第二次握手:服务器收到SYN报文段并确认。确认无误后并将自己要返回的响应报文和收到的报文打包一起发送给客户端,此时服务器进入接收状态;

第三次握手:客户端收到服务器的SYN+ACK报文段。向服务器发送ACK报文段,这个报文段发送完毕以后,客户端和服务器端都进入连接状态,完成TCP三次握手。

(2)TCP四次挥手

https://imgconvert.csdnimg.cn/aHR0cDovL3VwbG9hZC1pbWFnZXMuamlhbnNodS5pby91cGxvYWRfaW1hZ2VzLzM5ODU1NjMtYzFjNTkxNDhmOGIyNmM0My5wbmc?x-oss-process=image/format,png
第一次分手:断开主动方向被动方发送一个FIN报文段;主动方进入FIN_WAIT_1状态。

第二次分手:被动方收到主动方发送的FIN报文段,向主动方回一个ACK报文段,表示 “同意”关闭请求;

第三次分手:被动方向主动方发送FIN报文段,请求关闭连接,同时进入LAST_ACK状态;

第四次分手:主动方收到被动方发送的FIN报文段,向被动方发送ACK报文段,进入TIME_WAIT状态;被动方收到主动方的ACK报文段以后,就关闭连接。四次挥手结束。
二、TCP/IP协议四层架构

   https://imgconvert.csdnimg.cn/aHR0cDovL3VwbG9hZC1pbWFnZXMuamlhbnNodS5pby91cGxvYWRfaW1hZ2VzLzM5ODU1NjMtZTUzM2IwY2RkN2ZjYTM1OS5wbmc?x-oss-process=image/format,png
(1)链路层:操作系统中的设备驱动程序和计算机中的硬件接口

(2)网络层:也称作互联网层处理分组在网络中的活动,网络层主要的协议指IP协议,TCP和UDP的每组数据都通过端系统和每个中间路由器中的IP层在互联网中进行传输。

(3)传输层:主要指TCP(传输控制协议)和UDP(用户数据报协议)。TCP为两台主机提供高可靠性的数据通信。它所做的工作包括把应用程序交给它的数据分成合适的小块交给下面的网络层,确认接收到的分组,为了提供可靠的服务,TCP采用了超时重传、发送和接收端到端的确认分组等机制UDP只是把称作数据报的分组从一台主机发送到另一台主机,但并不保证该数据报能到达另一端

 (4)应用层:应用层决定了向用户提供应用服务时通信的活动。包括 HTTP,FTP(File Transfer Protocol,文件传输协议),DNS(Domain Name System,域名系统)服务。

三、HTTP请求报文

一个HTTP请求报文由:请求行(request line)、请求头部(header)、空行和请求数据4个部分组成

https://imgconvert.csdnimg.cn/aHR0cDovL3VwbG9hZC1pbWFnZXMuamlhbnNodS5pby91cGxvYWRfaW1hZ2VzLzM5ODU1NjMtY2Q1OWEzODk5ZWY1NDZlMS5wbmc?x-oss-process=image/format,png

1.请求行

请求行分为三个部分:请求方法、请求地址和协议版本

请求方法:GET、POST

请求地址:URL:统一资源定位符

协议版本:常用的有HTTP/1.0和HTTP/1.1

2.请求头部

请求头部为请求报文添加了一些附加信息,由“名/值”对组成。

3.请求数据

四、HTTP响应报文

https://imgconvert.csdnimg.cn/aHR0cDovL3VwbG9hZC1pbWFnZXMuamlhbnNodS5pby91cGxvYWRfaW1hZ2VzLzM5ODU1NjMtYzZlZThmODUyNmY1OWZjMC5wbmc?x-oss-process=image/format,png

HTTP响应报文主要由状态行、响应头部、空行以及响应数据组成。

1.状态行:由3部分组成为:协议版本,状态码,状态码描述。

2.响应头部:与请求头部类似,为响应报文添加了一些附加信息

3.响应数据:用于存放需要返回给客户端的数据信息。

                       

                       push机制

  • 含义:消息推送就是从服务器端向移动终端发送连接,传输一定的信息。比如一些新闻客户端,每隔一段时间收到一条或者多条通知,这就是从服务器端传来的推送消息;还比如常用的一些IM软件如微信、GTalk等,都具有服务器推送功能。

二、流程:

1.APP 向操作系统注册 push 通知

2.操作系统向服务商申请 device token

3.APP 将拿到的 device token 与用户相关信息(如 use rid)发送给业务服务器,由业务服务器进行记录

4.业务服务器将 use rid 对应的 device token 拿到,配合业务文案,发给服务商

5.服务商通过长链接,将信息推给 APP,来实现“push notification”

          Android各个版本API的区别

1、android5 :引入margindesign设计风格

2、android6 :软件权限管理。

3、android7:支持多窗口,增加了JIT编译器。

4、android8 :取消静态广播注册,限制后台进程调用手机资源。

5、android9 :系统界面添加了Home虚拟键,提供人工智能API,支持免打扰模式。

                   Android操作系统

一、Android操作系统的理解

基于Linux内核系统分为四层结构安全、开放具有灵活性

1、Java应用程序层:应用程序层

2、Java应用程序框架层:隐藏在每个应用后面的是一系列的服务和系统。它主要包括以下几部分:

  1. 视图(Views):UI的控件从这扩展。
  2. 内容提供器(Content Providers)使得应用程序可以访问另一个应用程序的数据(如联系人数据库), 或者共享它们自己的数据。
  3. 资源管理器(Resource Manager)提供非代码资源的访问,如本地字符串,图形,和布局文件。
  4. 通知管理器 (Notification Manager) 使得应用程序可以在状态栏中显示自定义的提示信息。
  5. 活动管理器( Activity Manager) 用来管理应用程序生命周期并提供常用的导航回退功能。
  6. AWS/WMS/定位服务、电话、包的管理等

3、C、C++本地库和Android运行时环境,主要核心库有:

(1)Surface Manager: 负责管理显示与存取操作间的互动,另外也负责将2D绘图与3D绘图进行显示上的合成

(2)Media Framework: 开源的多媒体框架,允许我们创造出更高质量与全新的播放器效果

(3)SQLite: 安卓自带嵌入式数据库。

(4)OpenGL ES:OpenGL 三维图形 API 的子集 3D效果库。

(5)Web Kit:开源的浏览器引擎。

(6)Lab: c层中最基本的函数库

Android运行时环境( Android Runtime)提供了核心链接库和 Dalvi VM虚拟系统。
Android采用 Dalvi VM来代替 Java VM,将写好的 Java程序“.java”先编译成“.class”程序,接下来再次编译成可以在 Dalvi VM执行的“.dex”程序,最后包装成 Android可执行“.apk”文件每个Android 应用都运行在自己的进程上,Dalvi 虚拟机为它分配自有的实例。

4Linux内核与驱动层

Linux内核层包括系统层安全机制内存管理、进程管理、网络堆栈及一系列的驱动模块,位于硬件与其他的软件层之间,提供与硬件的交互.

二、Android应用程序的启动流程

1、app的启动的步骤:

第一步:Launcher进程响应用户的点击事件,通知AMS需要启动

Activity。

第三步:AMS所在的systemSevice进程收到Launcher通知后,与Zygote进程协商创建一个新进程。

第四步:新的进程导入Activity Thread类,调用main方法,开启消息循环队列。

第五步:systems vice进程将APP进程信息注册到AMS中,AMS再在堆栈顶部取得要启动的Activity,通过一系列链式调用去完成App启动。

下面这张图很好的描述:

2、几个重要类的作用

(1)ActivityManagerService:(AMS)由SystemServer的Server Thread线程创建,是Android中最核心的服务之一,主要负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作,它本身也是一个Binder的实现类。

(2)Activity Thread:Android的主线程,但是并不是一个Thread类。UI主线程不是Activity Thread。Activity Thread类是Android APP进程的初始类,它的main函数是这个APP进程的入口Activity Thread通过handler消息机制启动一个Activity。

(3)Application Thread是Activity Thread的内部类,继承ApplicationThreadNative,也是一个Binder对象。在此处它是作为IApplicationThread对象的server端等待client端的请求然后进行处理,最大的client就是AMS.

三、Android系统重要的服务--WMS

1、WindowManagerService服务:为整个系统所有窗口的UI分配Surface。有序地排布在屏幕上,管理Surface的显示顺序、尺寸、位置。

2、WMS服务启动流程

(1)初始化was服务

(2)将was服务添加到systemserver中

(3)初始化窗口管理策略类windowmanagerpolicy,调用nontreaty方法

(4)通知was 的显示准备完毕,调用displayReady

(5)通知was系统准备完毕,调用systemReady此时就可以读取屏幕的相关熟悉信息。

四、Androidapplication

1. 定义:代表应用程序的类,也属于Android中的一个系统组件,继承自 ContextWarpper 类

2. 特点

(1) 实例创建方式:单例模式

App运行时,会首先自动创建Application 类并实例化 Application 对象,且只有一个。

(2)实例形式:全局实例

即不同的组件(如Activity、Service)都可获得Application对象且都是同一个对象

(3)生命周期:等于 App 的生命周期

3. 方法介绍

(1) on Create():Application 实例创建时调用,Android系统的入口是Application类的 on Create()。

作用 :初始化应用程序级别的资源,如全局对象、环境配置变量、图片资源、推送服务的注册、设置全局共享数据,如全局共享变量、方法等。

注:请不要执行耗时操作,否则会拖慢应用程序启动速度,这些共享数据只在应用程序的生命周期内有效,所以只能存储一些具备 临时性的共享数据。

(3)onTrimMemory():通知内存使用情况

(4)onLowMemory():监听 Android系统整体内存较低时刻

(5)onConfigurationChanged():监听应用程序配置信息的改变,如屏幕旋转等。

(6)registerActivityLifecycleCallbacks() & unregisterActivityLifecycleCallbacks():注册/注销对应用程序内所有Activity的生命周期监听。

(7)onTerminate():调用时刻:应用程序结束时调用

4. 具体使用:新建Application子类,继承 Application 类

示例:public class CarsonApplication extends Application

Android四大组件:Activty、Content Provider 、Broadcast、Service

五、Activty

1.Activity的加载过程

(1)Activity调用startActivity或者startActivityForResult,Activity Thread传递进程间消息给AMS,AMS接收到创建Activity的请求之后会执行初始化操作,解析启动模式,保存请求信息等一系列操作,将当前系统栈顶的Activity执行onPause操作,继续执行启动Activity的逻辑,A通过socket与Zygote通讯,并告知Zygote进程fork出一个新的应用程序进程,然后执行ActivityThread的mani方法执行初始化操作, Activity Thread通过反射机制创建出Activity对象,并执行Activity的on Create方法,on Start方法,on Resume方法,ActivityThread执行完成on Resume方法之后告知AMS on Resume执行完成,开始执行栈顶Activity的on Stop方法;AMS开始执行栈顶的on Stop方法并告知Activity Thread;Activity Thread执行真正的on Stop方法。

六、Activity的启动模式

 (1)standard-默认模式

每次启动一个Activity都会重写创建一个新的实例,不管这个实例存不存在,谁启动了该模式的Activity,该Activity就属于启动它的Activity的任务栈中。

 (2)singleTop-栈顶复用模式

新的activity已经位于栈顶,那么这个Activity不会被重写创建,同时它的onNewIntent方法会被调用,通过此方法的参数我们可以去除当前请求的信息。如果栈顶不存在该Activity的实例,则情况与standard模式相同。standard和single Top启动模式都是在原任务栈中新建Activity实例,不会启动新的Task。

应用场景:single Top适合接收通知启动的内容显示页面,例如新闻客户端的新闻内容页面。

(3)singleTask-栈内复用模式

如果栈中存在这个Activity的实例就会复用这个Activity,不管它是否位于栈顶,复用时,会将它上面的Activity全部出栈,并且会回调该实例的onNewIntent方法。其实这个过程还存在一个任务栈的匹配,因为这个模式启动时,会在自己需要的任务栈中寻找实例,这个任务栈就是通过taskAffinity属性指定。如果这个任务栈不存在,则会创建这个任务栈。 

应用场景:singleTask适合作为程序入口点。例如浏览器的主界面

(4)singleInstance-全局唯一模式

该模式具备singleTask模式的所有特性外,与它的区别就是,这种模式下的Activity会单独占用一个Task栈,以 singleInstance模式启动的Activity在整个系统中是单例的。

应用场景:singleInstance适合需要与程序分离开的页面。例如闹铃提醒

七、Activity的缓存

(1)onSaveInstanceState()

Activity中的onSaveInstanceState()回调方法用于保存临时数据和状态,比如UI控件的状态, 成员变量的值等,不能用来保存持久化的数据持久化数据应该当用户离开当前的 activity时,在 on Pause() 中保存,比如将数据保存到数据库或文件中。在activity的on Stop()方法之前调用。在onSaveInstanceState()方法中有个参数Bundle,可以通过,putting()、putString()等方法传入需要

保存的数据和状态。如下:

@Override 

    protected void onSaveInstanceState(Bundle outState) { 

        //注意:先保存数据,然后调用父类方法

        outState.putString("data","temp data"); 

        super.onSaveInstanceState(outState); 

    } 

(2)onRestoreInstanceState()

Activity中的onRestoreInstanceState()回调方法用于恢复数据和状态这个方法会在activity的onstart()方法之后调用。 在onRestoreInstanceState()方法中有个参数Bundle,bundle会传递到activity的onCreate()中,所以可以选择在onCreate方法中做数据和状态还原。如下:

@Override

public void onRestoreInstanceState(Bundle savedInstanceState) {

        super.onRestoreInstanceState(savedInstanceState);

        //注意:先调用父类方法,再取出自己保存的数据

        String tempData= savedInstanceState.getString("data");

}

//在activity的onCreate()也可以恢复数据

  @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        //这里,当Acivity第一次被创建的时候为空

        //所以我们需要判断一下

        if( savedInstanceState != null ){

            savedInstanceState.getString("anAnt");

        }

    }

(3)onSaveInstanceState() 执行情况

    1. 用户按下Home键

    2. 用户关闭电源键

    3. 用户从最近应用中选择其他的应用

    4. 从当前的activity跳到一个新的activity

    5. 手机横竖屏切换(不指定configchange属性)

    6. 手机系统设置的改变:如语言改变、键盘弹出

(4)总结

      1.在手机内存不足等原因,actvity未经系统许可异常销毁并且需要重新创建时,onSaveInstanceState()才有会调用。

      2.onSaveInstanceState()方法和onRestoreInstanceState()方法不一定”是成对的被调用的

八、Activity的生命周期

(1)四种状态:

正常情况下:依次会走Activity启动--onCreate()--onStart()--onResume()--Activity此时呈现在用户面前--当停掉Activity时会执行onPuse()--Activity不可见时执行onStop()--Activity被销毁时---onDestory()

当Activity暂停之后,用户又返回到Activity时:依次会执行onResume()--Activity此时呈现在用户面前--当停掉Activity时会执行onPuse()--Activity不可见时执行onStop()--Activity被销毁时---onDestory()

当Activity长时间不可见,用户又重新导航到Activity:依次会执行onRestart()--onStart()--onResume()--Activity此时呈现在用户面前--当停掉Activity时会执行onPuse()--Activity不可见时执行onStop()--Activity被销毁时---onDestory()

当Activity被释放掉后又重新导航回来:依次会执行onCreate()--onStart()--onResume()--Activity此时呈现在用户面前--当停掉Activity时会执行onPuse()--Activity不可见时执行onStop()--Activity被销毁时---onDestory()

(2)按键对生命周期的影响

BACK

  当我们按BACK键时,我们这个应用程序将结束,这时候我们将先后调用onPause()->onStop()->onDestory()三个方法。

再次启动App时,会执行onCreate()->onStart()->onResume()

HOME:

  按HOME的时候,Activity先后执行了onPause()->onStop()这两个方法,这时候应用程序并没有销毁。

而当我们从桌面再次启动应用程序时,则先后分别执行了onRestart()->onStart()->onResume()三个方法。

(3)横竖屏切换时Activity的生命周期:

 从竖屏--横屏:从当前界面依次会走onPuse()--onStop()--onDestory()--onCreate()--onStart()--onResume()可以看出竖屏会销毁,横屏会重新创建。

九、Activity之间的数据通信方式

 (1) 在Intent跳转时携带数据 

 (2)借助类的静态变量来实现 

 (3)借助全局变量来实现/Application 

(4)借助外部存储来实现通讯SharedPreferenceSQLite、File文件储存

(5)借助Service来实现

  ContentProvider

一、原理

1、contentProvider的底层实现是通过Binder实现

二、使用场景

1.使用场景:通讯录,日程表等,要跨进程访问数据信息,只需要通过ContentResolver的query、update、insert、update、insert、delete方法即可。
三、使用步骤

1、自定义ContentProvider,继承ContentProvider类并实现OnCreate、query、update、insert、delete和getType六个抽象方法,除了OnCreate由系统回调并运行在主线程里,其他五个方法均由外界回调并运行在Binder线程池中。

2、代码演示:

(1)初始化数据库,SQLite

(2)在清单文件注册:

(3)在调用处实现增删改查

   service的使用

  • Service的生命周期与使用

1.服务基本上分为两种形式: 
 (1) 启动 startService():一旦启动服务即可在后台无限期运行即使启动服务的组件已被销毁也不受影响

 (2)绑定 bindService()绑定服务提供了一个客户端-服务器接口,允许组件与服务进行交互、发送请求、获取结果。

2、生命周期

(1)onCreate() :做一些初始化操作并且只会被执行一次;哪怕启动多次服务或绑定多次服务。启动和绑定状态均会经历该状态。 
(2)onStartCommand():一旦执行此方法,服务即会启动并可在后台无限期运行。在服务工作完成后,需要通过调用stopSelf()或stopService()来停止服务
(3)onBind() 调用bindService()绑定服务后,将会执行该方法。在该方法的实现中,必须通过返回IBinder提供一个饥饿哭,供客户端用来与服务进行通信。

(4)onUnbind() 当所有绑定的客户端解除绑定时,系统将会调用该方法。 
(5)onDestory() 当服务不再使用且将被销毁时,系统将调用此方法。

BroadcastReceiver

1、广播有两个角色:广播发送者和接收者

2、广播按照类型分为两种:

(1)全局广播:发出的广播可以被其他任意的应用程序接收,或者可以接收来自其他任意应用程序的广播。

(2)本地广播:只能在应用程序的内部进行传递的广播,广播接收器也只能接收内部的广播。

按照广播机制也可以分为两种,标准广播和有序广播

无序广播:所有的接收者都会接收事件,不可以被拦截,不可以被修改。
有序广播按照优先级,一级一级的向下传递,接收者可以修改广播数据,也可以终止广播事件。

3.使用本地广播

(1)使用广播接收器接收广播:定义一个广播类继承BroadcastReceiver复写其中的onrecevie方法

 (2)对广播进行注册:一种是动态注册,另外一种则是静态注册。

动态注册的步骤:

1.在相关的activity文件中new一个刚才我们定义的广播类

2.new一个intentFilter类,调用其的setAction方法,参数中传入相关值的action。

3.调用context.registerReceiver方法进行注册,方法的第一个参数为广播类,第二个则是intentFilter类

private myreceiver recevier;

private IntentFilter intentFilter;

@Override

protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

    recevier = new myreceiver();

    intentFilter = new IntentFilter();

intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");

    //当网络发生变化的时候,系统广播会发出值为android.net.conn.CONNECTIVITY_CHANGE这样的一条广播

    registerReceiver(recevier,intentFilter);

}

需要注意的是,动态注册的广播接收器一定要注销,在onDestroy方法中调用unregisterReceiver(recevier);

静态注册的步骤:

1.在AndroidMainFest中的application标签下加上receiver的子标签

2.与通过name属性指定注册一个广播类,也就是我们刚才定义的那个广播类,还有enabled与exported属性,enabled代表是否启用这个广播接收器,exported属性表示是否允许这个广播接收器接受本程序以外的广播

3.之后在receiver标签下加上intent-filter标签,设置其的action

<receiver android:name=".myreceiver"
            android:exported="true"
android:enabled="true">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED">

          //开机完成后系统广播发出的一条值为android.intent.action.BOOT_COMPLETED的广播
            </intent-filter>
</receiver>
(3)在AndroidMainFest声明相关的权限

  在最后贴张图补充一下关于动态注册与静态注册的区别

https://images2017.cnblogs.com/blog/1210268/201801/1210268-20180124181046272-915743709.png(4)使用广播发送者发送自定义广播

前面介绍的是接收广播,这里介绍如何发送自定义广播

Intent intent = new Intent();
                intent.setAction("com.example.mymessage");
                //Intent intent = new Intent("com,example.mymessage");
                //也可以像注释这样写
                sendBroadcast(intent);//发送标准广播
                sendOrderedBroadcast(intent,null);//发送有序广播

//意思就是发送值为com.example.mymessage的这样一条广播



广播接收器设置优先度,如果使用的是动态注册,直接调用intentFilter.setPriority();如果是静态注册,则是设置intent-fliter的中Priority属性,优先度的大小设置范围为-1000~1000。

https://images2017.cnblogs.com/blog/1210268/201801/1210268-20180124184205615-1984761711.png想要截断广播,只需要在onreceive()方法中调用aboryBroadcast()即可是广播不再传递下去。

4、生命周期:

生命周期只有十秒左右,如果在 onReceive() 内做超过十秒内的事情,就会报ANR(Application No Response) 程序无响应的错误信息。

                     Android多进程

(一)概述:进程是系统分配资源的最小单位、不同进程间的资源和数据是相互隔离的,进程内可以运行多个线程。

(二)进程间通信

1.进程间通信(Inner-Process Comunication,简称IPC),指不同进程之间通过内核提供的数据交换

2、Linux进程间通信的主要方式

(1)管道:分为匿名和有名管道,匿名管道用于父子、兄弟进程之间,有名管道可以用于两个不同的进程,基于内存中的缓冲区来实现的,大小为固定的4KB。它在读取时都要确保对端的存在。从头读,在末尾写。数据传输是单向性。

应用场景:轻量级的进程通信

(2)消息队列:存在内核中的消息链表,在内核中把消息写入消息队列中,所有进程都可以去内核的消息队列中去读取。

(3)信号:

(4)共享机制:多个进程同时读取同一块内存空间。

(5)信号量:计数器,用于多进程对共享数据的访问进行控制,实现进程间同步。

  1. socket:

3、Android中进程间通信的主要方式

1.Android选择Binder 通讯机制的优势

(1)从性能的角度
数据拷贝次数:只需要一次,而管道、消息队列、套接字都需要2次,但共享内存方式一次内存拷贝都不需要;从性能角度看,Binder性能仅次于共享内存。
(2)从稳定性的角度
Binder是基于C/S架构的,架构清晰明朗,Server端和Client端相对独立,稳定性较好

(3)从安全的角度
  Android为每个安装好的应用程序分配了自己的UID,故进程的UID是鉴别进程身份的重要标志。Client端将任务发送给Server端,Server端会根据权限控制策略,判断UID/PID是否满足访问权限,让用户选择是否运行。
(4)从语言层面的角度
Binder恰恰符合面向对象思想,将进程间通信转化为对某个Binder对象的引用,调用该对象的方法。

2.Binder 通信原理

Binder框架定义了四个角色:Server,Client,ServiceManager(以后简称SM)以及Binder驱动其中Server,Client,SM运行于用户空间,驱动运行于内核空间。

以一个案例来大致说明Binder的通信过程, Client A进程调用Server B进程的computer对象的add方法。

(1)ServerB进程向ServiceManager注册自己能做的事, Client A进程向ServiceManager查询某一个自己想要的功能由于Binder Client和ServiceManager处于不同的进程,所以这个过程也要经过Binder驱动,当向ServiceManager查询完毕,Binder驱动将computer对象转换成了computerProxy代理对象,并转发给了Client A进程, 当Client A进程调用add方法,这个消息发送给Binder驱动,这时驱动发现,原来是computerProxy,那么Client A进程应该是需要调用computer对象的add方法的,这时驱动通知Server B进程,调用你的computer对象的add方法,将结果给我。然后Server B进程就将计算结果发送给驱动,驱动再转发给Client A进程,这时ClientA进程并没有调用Server B进程真实的computer对象的add方法,其实他只是调用了代理而已, Binder驱动起到了中转的作用。

3.进程间通信常见方式

(1)Bundle/Intent传递数据:

主要用于传递基本类型,String,实现了Serializable或Parcellable接口的数据结构。Serializable是Java的序列化方法,Parcellable是Android的序列化方法,前者代码量少(仅一句),但I/O开销较大,一般用于输出到磁盘或网卡;后者实现代码多,效率高,一般用户内存间序列化和反序列化传输

(2)文件共享:通过文件共享信息,如 SharedPreferences 。通过序列化对象到文件中,从文件中反序列化恢复对象。

(3)Messenger:Messenger是基于AIDL实现的,服务端(被动方)提供一个Service来处理客户端(主动方)连接,维护一个Handler来创建Messenger,在onBind时返回Messenger的binder。

双方用Messenger来发送数据,用Handler来处理数据。Messenger处理数据依靠Handler,所以是串行的,也就是说,Handler接到多个message时,就要排队依次处理。

(4)AIDL:

AIDL接口定义语言。

使用步骤:

一.建立一个AIDL文件(即一个Interface)

  1. interface Add{
  2.     int add(in int num1, in int num2);
  3. }


一旦文件被保存,Android的AIDL工具会在gen/com/android/hellosumaidl这个文件夹里自动生成对应的Add.java这个文件。这个文件里就包含了Stub,我们接下来要为我们的远程服务实现这个Stub。

二、在已有的Service中重写OnBind()方法为下一步的ServiceConnection做好准备。


public class AddService extends Service {

    @Override

    public void onCreate() {

       super.onCreate();

   }                                                                                                                                                                                                                                                                                                                                                                                                  

   @Override

   public IBinder onBind(Intent intent) {

       return new Add.Stub() {

           @Override

          public int add(int num1, int num2) {

               return num1 + num2;

           }

       };

    }

                                                                                                                                                                                                                                                                                                                                                                                                        

    @Override

    public void onDestroy() {

        super.onDestroy();

    }

}

三.建立连接

在activity中建立一个内部类并实现ServiceConnection接口。重写2个未实现的方法,为onServiceConnected、onServiceDisconnected。
 

四、通信

private Add service;

private AddServiceConnection connection                                     

    @Override

   public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);                                                                                                       initService()                                                                                      

        but.setOnClickListener(new OnClickListener() {

                                                                               

           @Override

           public void onClick(View v) {

                int n1, n2, r;

               n1 = Integer.parseInt(num1.getText().toString());

                n2 = Integer.parseInt(num2.getText().toString());                                                                                                                                                                  r = service.add(n1, n2);               

              res.setText(Integer.valueOf(r).toString());

         }

      });

   }

                                                                                                                            

    @Override

   protected void onDestroy() {

       super.onDestroy();

       closeService();

  }

                                                                                                                           

 

   class AddServiceConnection implements ServiceConnection {

       public void onServiceConnected(ComponentName name, IBinder boundService) {

          service = Add.Stub.asInterface((IBinder)boundService);

           //其他功能

        }

                                                                                                                               

        public void onServiceDisconnected(ComponentName name) {

           service = null;

          //其他功能

       }

    }

                                                                                                                            

    private void initService() {//初始化

       connection = new AddServiceConnection();

        Intent i = new Intent();

        i.setClassName("com.android.aidl",

com.android.aidl.AddService.class.getName());//包名和服务类名

        bindService(i, connection, Context.BIND_AUTO_CREATE);

    }                                                                                                                  

    private void closeService() {//关闭服务

       unbindService(connection);

       connection = null;

   }

(5)ContentProvider:见(上)

(6)Socket:在服务器中定义ServerSocket来监听端口,客户端使用Socket来请求端口,连通后就可以进行通信。

十、Android中线程的使用

(一)线程的创建三种方式
1、新建一个类继承自Thread,然后重写父类的run()方法,并在里面编写要处理的事务。

class MyThread extends Thread{

      @Override

      public void run(){

              //处理事务

      }

}

启动线程的时候需要new出MyThread实例然后调用start()方法。new MyThread().start();

2、实现Runnable接口:

class MyThread implements Runnable{

@Override

public void run(){

     //处理事务

}

}

MyThread myThread = new MyThread();

new Thread(myThread).start();

3、使用匿名类的方式,不用专门再定义一个类实现Runnable接口。

new Thread(new Runnable(){

   @Override

  public void run(){

       //处理事务

   }

}).start();

                  Android线程间的通信

一、重要作用:Handler是Android中线程间通信的重要方式,常见的使用场景就是完成两个线程之间的通信和传递数据。更新界面数据、发送和处理消息。

二、使用方式

1.在主线程中创建一个Handler,重写handlerMessage方法,传入参数Message, 该参数就是我们从其他线程传递过来的信息。

2.在子线程中通过Handler的obtainMessage()方法获取到一个Message实例:最后通过sendMessage将Message发送出去

(1)Message.what----->判断出来自不同地方的信息来源 。

(2)Message.arg1/Message.arg2->用来传递int类型值的两个变量 。

(3)Message.obj------>用来传递任何实例化对象 ,最后通过sendMessage将Message发送出去。

private Handler mHandler = new Handler(){

    public void handleMessage(Message msg){

        mTextView.setText(""+msg.arg1+"-"+msg.arg2);

    };

};

new Thread(){

    @Override

    public void run() {

        try {

            Thread.sleep(2000);

         Message message = Message.obtain();

            message.arg1 = 88;

            mHandler.sendMessage(message);

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

    }

}.start();

post方法:主要用于更新主线程UI

主线程中创建Handler。在子线程中调用Handler的post方法,并向其中传递一个Runnable为参数,在Runnable中更新UI。

private TextView mTextView;

private Handler mHandler = new Handler();

mTextView = findViewById(R.id.tv_text);

new Thread(new Runnable() {

    @Override

    public void run() {

        try {

            Thread.sleep(2000);

            mHandler.post(new Runnable() {

                @Override

                public void run() {

                    mTextView.setText("change");

                }

            });

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

    }

}).start();

 三、原理讲解

在主线程中创建:Handler对象、处理器对象(Looper)、消息队列对象(Message Queue),子线程通过Handler发送Message到Message Queue中。Looper从Message Queue中循环取出Message,Looper将取出的Message给Handler分发和处理。

四、主要注意的事项: 

1handler所创建的线程需要维护一个唯一的Looper对象, 每个线程对应一个Looper,Looper对象的内部又维护有唯一的一个MessageQueue,所以一个线程可以有多个handler,但是只能有一个Looper和一个MessageQueue。

2、Looper扮演着消息循环的角色,不停地从MessageQueue 中查看是否有新的消息,有就会立刻处理,否则就一直阻塞在那里。Handler 的工作需要 Looper,没有 Looper 的线程就会报错,通过 Looper.prepare()即可为当前线程创建一个 Looper,接着通过 Looper.loop()来开启消息循环,如下所示:

  new Thread(){

            @Override

            public void run() {

                Looper.prepare();

                Handler handler = new Handler();

                Looper.loop();

            }

    }.start();

 Looper 的 quit 方法跳出循环。

3.ThreadLocal 的工作原理:一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,只有在指定线程中可以获取到存储的数据,对于其他线程来说则无法获取到数据。

4. MessageQueue工作原理:主要包含两个操作:插入和读取。

enqueueMessage的作用是往消息队列中插入一条消息next 的作用是从消息队列中取出一条消息并将其从消息队列中移除;它的内部实现并不是用的队列,而是通过一个单链表的数据结构来维护消息列表,单链表在插入和删除上比较有优势。

五、handler造成的内存泄漏

1.原因:非静态内部类是会隐式持有外部类的引用,所以当其他线程持有了该Handler,线程没有被销毁,则意味着Activity会一直被Handler持有引用而无法导致回收。如果MessageQueue中如果存在未处理完的Message,Message的target也是对Activity等的持有引用,也会造成内存泄漏。

2.解决的办法:

 (1). 使用静态内部类+弱引用的方式:

private Handler sHandler = new TestHandler(this);

static class TestHandler extends Handler {
    private WeakReference<Activity> mActivity;
    TestHandler(Activity activity) {
        mActivity = new WeakReference<>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        Activity activity = mActivity.get();
        if (activity != null) {
            //TODO:
        }
    }
}
(2).将MessageQueue中的消息清空。在Activity的onDestroy时将消息清空。

@Override
protected void onDestroy() {
    handler.removeCallbacksAndMessages(null);
    super.onDestroy();
}

(三)线程池:为更好的管理线程资源,性能调优

1. 线程池的实现类ThreadPoolExecutor,其参数主要有:

(1)corePoolSize

线程池中的线程默认个数为0,有任务来就会创建一个线程去执行,线程数达到corePoolSize后,就会把到达的任务放到缓存队列当中。核心线程allowCoreThreadTimeout被设置为true时会超时退出,默认情况下不会退出。

(2)maxPoolSize

当线程数大于或等于核心线程,且任务队列已满时,线程池会创建新的线程,直到线程数量达到maxPoolSize。如果线程数已等于maxPoolSize,且任务队列已满,则已超出线程池的处理能力,线程池会拒绝处理任务而抛出异常

(3)keepAliveTime

当线程空闲时间达到keepAliveTime,该线程会退出,直到线程数量等于corePoolSize。如果allowCoreThreadTimeout设置为true,则所有线程均会退出直到线程数量为0

(4)queueCapacity

任务队列容量。从maxPoolSize的描述上可以看出,任务队列的容量会影响到线程的变化,因此任务队列的长度也需要恰当的设置。

(5)workQueue:一个阻塞队列,用来存储等待执行的任务

(6)threadFactory:线程工厂,主要用来创建线程。

2、线程池优点及类型

线程池的优点

1)避免线程的创建和销毁带来的性能开销

2)避免大量的线程间因互相抢占系统资源导致的阻塞现象

3)能够对线程进行简单的管理并提供定时执行、间隔执行等功能

3、Executors 提供四种线程池:

1)newCachedThreadPool根据需要创建新线程的线程池,调用 execute() 将重用以前构造的线程并终止从缓存中移除那些已有 60 秒钟未被使用的线程。

2)newSingleThreadExecutor :单线程池,线程池只有一个线程在工作,所有的任务是串行执行的此线程池保证所有任务的执行顺序按照任务的提交顺序执行,如果线程因为异常结束,会有一个新的线程来替代它。

3)newFixedThreadPool 创建固定大小的线程池线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

4)newScheduledThreadPool 创建一个大小无限的线程池,此线程池支持定时以及周期性执行任务的需求

线程池的关闭: shutdown():不会立即的终止线程池

而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务。shutdownNow():立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务。

9.死锁

(1)产生死锁的原因主要是:由于两个或以上的线程互相持有对方需要的资源,导致这些线程处于等待状态,无法执行

(2)如何避免死锁

加锁顺序:确保所有的线程都是按照相同的顺序获得锁,那么死锁就不会发生。

加锁时限:线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁。

死锁检测: 一个可行的做法是释放所有锁回退,并且等待一段随机的时间后重试。另一个更好的方案是给这些线程设置优先级

11. 如何确保线程安全

(1)原子性提供互斥访问,同一时刻只能有一个线程对数据进行操作。用atomic开头的类AtomicInteger,AtomicLong,AtomicBoolean

(2)使用synchronized是一种同步锁,通过锁实现原子操作

要点:在用synchronized关键字的时候,能缩小代码段的范围就尽量缩小,能在代码段上加同步就不要再整个方法上加同步。这叫减小锁的粒度,使代码更大程度的并发,对于非static的synchronized方法,锁的就是对象本身也就是this。static synchronized方法,锁的是类的Class对象,所以,static synchronized方法也相当于全局锁,相当于锁住了代码段同步块是更好的选择,因为它不会锁住整个对象

(2)Volatile:由于不会引起线程上下文的切换和调度,它轻量、执行成本低,用于多线程之间内存的共享。

volatile实现原理:被修饰变量值修改时处理器会将缓存行数据写入到主内存中,如果变量被声明为volatile,当进行写操作时,JVM会向处理器发送一条Lock前缀的指令,然后将变量在缓存中所修改的值写入到系统内存中。

 

十一、Android View相关

一、Android 的事件分发机制

1、概念:某一个事件从屏幕传递各个View,由View来使用(或不使用)这一事件整个过程的控制。

2、事件主要传递的对象和顺序

Activity => ViewGroup => View 的顺序进行事件分发

3、Activity 的事件分发

(1)主要方法:

   dispathchTouchEvent()        onTouchEvent()

4、ViewGroup的事件分发

(1)主要方法

dispathchTouchEvent()  onInterceptTouchEvent()   onTouchEvent()

(2) 示意图

5、View 的事件分发

(1)主要方法

dispathchTouchEvent()      onTouchEvent()

(2)示意图

 

二、Android的View绘制流程

View的绘制流程:OnMeasure()——>OnLayout()——>OnDraw()

1.OnMeasure():测量视图大小测量主要靠MeasureSpec 的值由specSize和specMode共同组成的,specSize记录的是大小,specMode记录的是规格。

specMode一共有三种类型:

EXACTLY :表示父视图希望子视图的大小应该是由specSize的值来决定的

AT_MOST :表示子视图最多只能是specSize中指定的大小,并且保证不会超过specSize。

UNSPECIFIED :表示开发人员可以将视图按照自己的意愿设置成任意的大小,没有任何限制。

2.OnLayout():确定View位置,进行页面布局

3.OnDraw():绘制视图。ViewRoot创建一个Canvas对象,然后调用OnDraw()。

六个步骤:

①、绘制视图的背景;

②、保存画布的图层(Layer);

③、绘制View的内容;

④、绘制View子视图,如果没有就不用;

⑤、还原图层(Layer);

⑥、绘制滚动条。

三、Android 性能优化UI

(一)Android 渲染原理和理解UI卡顿

1.Android系统每隔16s发出一个UI更新信号,当系统受到这个信号的时候就会一个UI界面的更新,UI界面的更新和渲染是由CPU和Gpu共同完成的。

2.UI卡顿的原因:没有在16ms之内完成一些我们想要的操作,从而发生了丢帧的现象。

解决卡顿的2个方向:

Cpu:减少布局的嵌套,布局层次扁平化。

GPU:检查过度渲染,让一个屏幕,一个像素点尽可能只渲染一次。

(二)、ANR分析和结局

1.ANR简介

应用程序无响应。

2.产生的条件:必须满足以下三个条件

3.ANR产生的情况

4.使用trace文件分析ANR

5.解决ANR:

新建子线程,在子线程处理复杂的业务逻辑

(三)UI层次结构优化

UI层级结构优化的常用技巧

1<include>标签介绍和使用:多个界面会重用同一个布局

2、<merge>标签介绍和使用:线性布局嵌套时使用

3、<ViewStub>标签介绍和使用:界面占位符,界面加载时才渲染

 

        十二、Android 性能优化内存管理

(一)内存优化理论

  1. Android 的内存管理方式

  1. 内存的回收机制:

可见进程:没有任何前台组件,但仍然会影响用户在屏幕上所见内容的一些进程,系统一般也不会杀死它。

服务进程:下载或者播放音乐

后台进程:对用户体验没有任何影响,也不存在任何的服务进程,系统可以随时终止。

空进程:不含任何活动组件的,系统保存它的唯一目的就是保存缓存,方便进程再次启动。

                内存优化的方式--图片优化

一、图片OOM的原因:单页面加载过多的图片,加载大图片没有压缩、列表加载大量bitmap没有使用缓存。

二、图片几种格式

(1)png:无损压缩,支持完整的透明通道,占用的空间比较大

(2)jpeg:有损压缩,不支持透明通道

(3)webp:支持有损和无损压缩,支持完整的透明通道,支持多帧动画,  在保证质量,又要限制大小的情况下,它是首选。

(4)gif:支持多帧动画。

三、一张图片内存大小的计算:宽X高X一个像素占用的内存大小

四、优化方向:

1、尺寸压缩

(1)options.inJustDecodeBounds = true;它可以实现图片不加载进内存的情况下,去片的高,从何去算出合适的压缩

(2)inSampleSize >= 2,可以降低片的采率,从而减小片的内存占用。

2、质量压缩:图片在设置到UI上之前需要经过解码,使用RGB_565(一个像素占2个字节)来替代ARGB_8888(一个像素占4个字节)可以降低图片占用内存,此方案可以降低一个像素占用内存的大小。

3、内存重用:bitmap的内存Inbitmap重用属性

图片压缩的示例:

五、bitmap的内存管理

1.3.0之前对于像素数据的支持保存在本地内存中,需要调用bitmap.recyler去释放这些数据,3.0之后,像素数据和位图都存储在Dalvik堆中。没有对象引用图片时,垃圾处理器会自动回收这些数据,

六、图片的加载优化

(一)概念普及

1、mipmap:是android api17之后为了提高bitmap渲染速度和质量的技术,系统通过setHasMipMap=true来实现,谷歌建议把启动图标放在mipmap文件夹下,其它图标依然放在drawable下,

2、Android图片如何适配各种分辨率的手机多文件夹匹配:

(1)分辨率:屏幕上的像素个数,单位为Px.

 (2)DPI:每英寸屏幕上的像素个数。

市面主流手机的dpi:

3.Android图标匹配规则

4、内存占用与drawable文件夹的关系

同一张图标放在不同目录下,会生成不同大小的bitmap,建议图标都放在xxh文件夹下。因为主流手机都是这个dpi.

(二)方案:

(三)超大图片的加载BitmapRegionDecoder

 

         内存优化的方式数据结构优化

一、优化方向

1、频繁字符串拼接使用StringBuilder,字符串通过+的方式进行拼接会产生中间无用的字符串内存块,同时也是低效耗时的。

2、使用ArrayMap、SparseArray替换HashMap,内存使用更小。HashMap一个entry需要额外占用32B。

3、内存抖动:短时间内有大量的对象被创建又被回收。频繁的GC会导致卡顿,严重时还会导致OOM。

解决方案:

4、对象的复用:

(1)复用系统自带的资源

(2)列表ConvertView的复用

(3)避免在onDraw方法里面执行对象的创建。

5、避免内存泄漏

原因:某块内存没有被使用,但由于某种原因,依然被其他东西引用着,GC无法回收

A:单例造成的内存泄露,因为单例是静态的,生命周期是整个应用,如果在用的时候上下文传一个Activity,当这个Activity不用的时候,我们希望被回收,但是Gc无法回收。

单例在使用处

避免单例造成的内存泄露;改造单例模式:尽量使用Application的上下文

B:静态变量导致的内存泄露

静态变量的生命周期是从类加载一直到类卸载,它所持有的引用只有到进程结束的时候才会释放,此时容易造成内存泄露。

尽量少使用静态持有,解决办法:

C:非静态内部类导致的内存泄露

非静态内部类默认会持有外部类的引用,如果非静态内部类里面对象的生命周期如果比外部类的生命周期长的时候就会造成内存泄露

错误图示:

用静态内部类+弱引用改造

或者在onDestroy()方法里移除handler和msg

D:Activity造成的内存泄漏

4.其它优化技巧

 

十三、AsyncTask类

AsyncTask,是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步执行的程度,比Handler更轻量级一些,适用于简单的异步处理。

1.AsyncTask内部封装的方法:

(1)onPreExecute():在后台任务开始之前执行,进行一些界面上的初始化

(2)doInBackground(Object o):运行在AsyncTask内部开辟的子线程,一些耗时会阻塞UI的操作都应该放在这个方法内处理

(3)onProgressSetUpdate():运行在主线程,AsyncTask必须在UI线程声明创建。onProgressSetUpdate(Object o)方法主要是用来显示任务执行的进度,给用户直观的感受。一般会在doInBackGround()方法中调用pubilshProgress(Object o)传参只onProgressSetUpdate(),然后执行控件的刷新。

(4)onPostExecute():运行在主线程doInBackground()会有一个返回值,这个返回值会作为参数传入onPostExecute()方法中,可以利用返回的数据进行一些UI操作,进行收尾工作。

(5)onCancelled() :用户调用取消时,要执行的操作。

十五、Android进程保活

主要分为 黑、白、灰 三种,

实现思路:

(一)、黑色保活不同的app进程,用广播相互唤醒

黑色保活的场景:开机,网络切换、拍照、拍视频时候,利用系统产生的广播唤醒app,接入第三方SDK也会唤醒相应的app进程,

(二)、白色保活:启动前台Service,调用系统api启动一个前台的Service进程,这样会在系统的通知栏生成一个Notification,用来让用户知道有这样一个app在运行着,哪怕当前的app退到了后台。例如QQ音乐

(三)灰色保活:利用系统的漏洞启动前台Service,利用系统的漏洞来启动一个前台的Service进程,它不会在系统通知栏处出现一个Notification,看起来就如同运行着一个后台Service进程一样。让用户无法察觉。

十六、Android 屏幕适配策略

(一)屏幕适配方式:

1.图片适配:drawable/mipmap-*dpi目录,用来适配不同分辨率手机(二)Android系统的适配策略

1.尺寸适配:为了适配不同尺寸手机创建一个values的文件夹。

2. 布局适配:在layout中我们可以针对不同手机的分辨率制定不同的布局。

3. 权重适配:使用属性android:layout_weight="1"起到适配效果。

4. 代码适配:在java代码中动态计算控件的宽度和高度。

5. 百分比适配:计算图片的宽度和高度的比例,根据图片的比例。计算出图片的在设备中的显示的实际宽度和高度。

6.autosize 头条屏幕适配库

十七、Android 5种数据存储方式

(一)SharedPreferences存储数据
1.适用范围保存少量的数据,且这些数据的格式非常简单:字符串型、基本类型的值。

2.核心原理保存基于XML文件存储的key-value键值对数据,通常用来存储一些简单的配置信息。

3.实现SharedPreferences存储的步骤如下:

(1)根据Context获取SharedPreferences对象

(2)利用edit()方法获取Editor对象。

(3)通过Editor对象存储key-value键值对数据。

(4)通过commit()方法提交数据。
(二)文件存储数据

1. 核心原理: Context提供了两个方法来打开数据文件里的文件IO流

(三)SQLite数据库存储数据

1.SQLite是轻量级嵌入式数据库引擎,它支持 SQL 语言,并且只利用很少的内存就有很好的性能。

  2.使用:

(1)创建数据库:继承SQLiteOpenHelper就拥有以下两个方法:

getReadableDatabase() 创建或者打开一个查询数据库

getWritableDatabase()  创建或者打开一个可写数据库

DatabaseHelper database = new DatabaseHelper(context);

SQLiteDatabase db = null;

db = database.getWritableDatabase();

使用db这个对象,你就可以查询或者修改数据库。

(2)创建表和索引

(3)增删改查:insert/update/delete/query

4.ContentProvider存储数据

1.简介:ContentProvider:为存储和获取数据提供统一的接口。可以在不同的应用程序之间共享数据。并且此种方式忽略了底层的数据存储实现。

2. 步骤为:

(1)在当前应用程序中定义一个ContentProvider。

(2)在当前应用程序的清单文件中注册此ContentProvider

(3)其他应用程序通过ContentResolver和Uri来获取此ContentProvider的数据。
(4)ContentResolver提供了insert(), delete(), query()和update()之类的方法。用于实现对ContentProvider中数据的存取操作。

(五)网络存储数据

十八、Android基础动画和属性动画

Android中的动画有分类,根据谷歌官方文档,动画分为3种:

(一)帧动画/图像动画(Drawable Animation)

1.xml方式->配置一组图片,动态播放

(二)补间动画(View Animation)

1.位移动画

2.缩放动画

3.旋转动画

4.透明动画

5.集合

(三)属性动画(Property Animation):拥有补间动画所有的功能

属性动画和补间动画的区别:

补间动画: 是父容器不断的绘制view,看起来像移动了效果,其实view没有变化,还在原地

属性动画: 是通过不断改变自己view的属性值,真正的改变view.

(四)lottite动画:通过加载json文件来实现动画效果。

十九、安卓中的差值器和估值器

1.差值器TimeInterpolator

差值器只是能修改速率的一种东西;比如,匀速,快到慢,慢到快,快慢快或者慢快慢

安卓本身提供给我们两个内置的差值器

(1)匀速插值器:LinearInterpolator
(2)先加速再减速插值器:AccelerateDecelerateInterpolator

2.估值器TypeEvaluator

估值器(Interpolator)决定值的变化规律(匀速、加速blabla),即决定的是变化趋势;而接下来的具体变化数值则交给估值器

系统内置的估值器有3个:

IntEvaluator:以整型的形式从初始值 - 结束值 进行过渡
FloatEvaluator:以浮点型的形式从初始值 - 结束值 进行过渡
ArgbEvaluator:以Argb类型的形式从初始值 - 结束值 进行过渡

 

                  二十、源码码篇

Volley、OKhttp、Retrofit讲解

(一)Volley:

1.简介:小而巧的异步库,设计初衷是为了频繁的、数据量小的网络请求不支持Post大数据,所以不适合上传文件

2.重要实现方法:

(1)Volley首先获得是RequestQueue请求队列实例。

(2)Start(),一个缓存线程和四个网络请求线程会一直在后台运行,等待网络请求。

(3)Add(),判断当前请求是否可以缓存,可以的话加入缓存队列,否则直接加入网络请求队列。

(4)Run(),拿到网络数据后,数据解析逻辑和解析结果逻辑。

(二)、okHttp:

1.简介:高性能http请求库基于NIO非阻塞式OKio更简单的高效处理数据流库支持同步、异步,封装了线程池、数据转换、参数使用、错误处理

2.Okttp的源码解析:

(1)通过构建者模式构建所有的request,

(2)构建好后Dispatcher把request分发到HttpEngine中,HttpEngine首先看一下这次请求有没有缓存,如果有就从Cache里拿到信息直接给我们的response,如果没有它又会把我们这次请求发到ConnectionPool连接池中里面,从连接池中获取Connection,通过Connection去发我们真正的请求,请求到以后通过我们的路由Route和platform找到一个合适平台,最后,通过我们的severScoket获取我们的data。整个流程结束。

okHttp使用流程:

  1. 创建一个OKhttpClient
  2. 创建Request对象
  3. 实例化CALL对象,
  4. 可以执行Execute
  5. 通过respone.isSuccessful的返回值为true或者flase来判断Call对象是否执行成功。

(三)Retrofit:

1.简介基于OKhttp,通过注解直接配置请求,可以使用不同的客户端,可以使用不同的JsonConverter来序列化数据,同时提出对RxJAVA的支持

2.优势:使用注解,效率高;使用简单,结构层次分明;最大自由度支持自定义业务逻辑。

(四)Glide原理

1.Glide构成

(1)请求管理器:RequestManager

(2)数据获取引擎:Engine

(3)数据获取器:Fetcher

(4)内存缓存:MemoryCache

(5)图片处理:Transformation

(6)本地缓存储存:Encoder

(7)图片类型及解析器配置:Registry

(8)目标:Target

2.Glide的处理流程:

创建Request并将它交给RequestManager,Request启动Engine去数据源获取资源,再通过Transformation处理后交给Target去显示。

3.优点:

不仅是一个图片缓存,支持Gif、Webp、缩略图、video

内存缓存更小图片,与Activity和fragment的生命周期一致

4.常用方法:

With,load,into ,disCacheStrategy(设置缓存策略),priority(指定加载优先级),preload(预加载)

(五)EventBus源码解读

观察者模式,实现了一种一对多的依赖关系,并为事件的发送者与接收者之间进行了解耦。

(1)通过单例模式获取EventBus对象:EventBus.getDefault()获得EventBus对象。

(2)订阅者的注册:在获取到EventBus对象以后,通过register方法将订阅者注册到EventBus中。

(3)订阅方法的查找过程:findSubscriberMethods这个方法,主要就是用保存订阅方法的Method对象,线程模式,事件类型,优先级,是否粘性事件等属性。

(4)订阅者的注册过程:首先会根据subscriber和subscriberMethod来创建一个Subscription对象。之后根据事件类型获取或创建一个Subscription集合subscriptions并添加到typesBySubscriber对象中。最后将刚才创建的Subscription对象添加到subscriptions之中。于是这样就完成了一次订阅者的注册操作。

(5)事件的发送

在PostingThreadState中保存了事件队列,以及线程的一些状态信息。首先从PostingThreadState对象中取出事件队列,然后再将当前的事件插入到事件队列当中。最后将队列中的事件依次交由postSingleEvent方法进行处理,并移除该事件。

                  二十一架构篇

MVC/MVP/MVVM

  • mvc

1、架构的图示

2.MVC的工作原理

Model:用于网络请求

View:界面的展示

Controller:指Activity和fragment

重点图:

虚线表示MVC的被动过程。

(二)、MVP架构

(三)、MVVM

                       

                  二十二 新技术篇

热修复框架原理分类三类底层原理和各自的实现

  1. Native Hook(代表框架AndFix

Native指的就是本地语言,Hook就是钩子函数,Hook(钩子)是WINDOWS提供的一种消息处理机制平台,是指在程序正常运 
行中接受信息之前预先启动的函数,用来检查和修改传给该程序的信息,(钩子)实 际上是一个处理消息的程序段,通过系统调用,把它挂入系统。每当特定的消息发出, 在没有到达目的窗口前,钩子程序就先捕获该消息,亦即钩子函数先得到控制权。这时钩子函数即可以加工处理(改变)该消息,也可以不作处理而继续传递该消息,还 可以强制结束消息的传递。 
注意:安装钩子函数将会影响系统的性能。监测“系统范围事件”的系统钩子特 别明显。因为系统在处理所有的相关事件时都将调用您的钩子函数,这样您的系统将 会明显的减慢。所以应谨慎使用,用完后立即卸载。还有,由于您可以预先截获其它进程的消息,所以一旦您的钩子函数出了问题的话必将影响其它的进程。记住:功能强大也意味着使用时要负责任。

Native Hook是通过C和C++层方法替换实现方法热修复,它实现的是方法的热修复,即热修复的级别是方法级别。

修改过程流程:

  1. ClassLoader(代表框架---Nuwa)

原理是基于Multidex方案切入的热修复方案,Android支持Multidex,可以有多个dex文件,那么这多个dex文件就被保存在系统一个叫dexElements的数组中,

修改的图示:

  1. Dex 替换(代表框架---Tinker)

如图:原有bug的为Base.apk,修复后的为New.apk,然后比较这两个apk的dex文件,找到它们的差异,生成一个patch.dex文件,然后,把这个patch.dex发布到手机app里,在app里动态生成一个新的dex文件,用这个新的dex文件全量的去替换原来的dex文件,从而实现了热修复。

流程概述:

这种方案需要重新启动app才可以热修复生效。AnFix则不需要重启就可以实现。

(二)Flutter和RN对比

1.Flutter

(1)Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面。用于创建高性能、跨平台的移动应用的框架。
(2)Flutter的开发语言是Dart

  • RN的效率由于是将View编译成了原生View,所以效率上要比基于Cordova的HTML5高很多, RN的渲染机制是基于前端框架的考虑,复杂的UI渲染是需要依赖多个view叠加.会有多少个对象需要渲染。
  • Flutter在渲染技术上,选择了自己实现(GDI),由于有更好的可控性,使用了新的语言Dart,避免了RN的那种通过桥接器与Javascript通讯导致效率低下的问题,所以在性能方面比RN更高一筹;有经验的开发者可以打开Android手机开发者选项里面的显示边界布局,发现Flutter的布局是一个整体.说明Flutter的渲染没用使用原生控件进行渲染

react-native框架原理图

Flutter实现跨平台采用了更为彻底的方案。它既没有采用WebView也没有采用JavaScriptCore,而是自己实现了一台UI框架,然后直接系统更底层渲染系统上画UI。所以它采用的开发语言不是JS,而Dart。据称Dart语言可以编译成原生代码,直接跟原生通信。

Flutter框架原理图

Flutter将UI组件和渲染器从平台移动到应用程序中,这使得它们可以自定义和可扩展。Flutter唯一要求系统提供的是canvas,以便定制的UI组件可以出现在设备的屏幕上,以及访问事件(触摸,定时器等)和服务(位置、相机等)。这是Flutter可以做到跨平台而且高效的关键。另外Flutter学习了RN的UI编程方式,引入了状态机,更新UI时只更新最小改变区域。系统的UI框架可以取代,但是系统提供的一些服务是无法取代的。Flutter在跟系统service通信方式,采用的是一种类似插件式的方式,或者有点像远程过程调用RPC方式。这种方式据说也要比RN的桥接方式高效。

Flutter与RN异同

Flutter性能会更好无线接近原生的体验,Dart是AOT编译的,编译成快速、可预测的本地代码

RN采用JS语言开发,基于React,对前端工程师更友好。Dart语言受众小

Flutter自己实现了一套UI框架,丢弃了原生的UI框架。而RN还是可以自己利用原生框架,两个各有好处。Flutter的兼容性高,RN可以利用原生已有的优秀UI

RN的布局更像css,而Flutter的布局更像native布局,但是去掉xml通过代码直接写需要适应下

Flutter的开发流程:

(一)环境搭建

1.下载flutterSDK

2.添加配置环境变量

3.运行flutter命令安装各种依赖

(二)flutter基础知识

1.入口程序:lib目录下的main.dart文件的main()函数。Main函数调用RunAPP()函数,它是flutter框架的入口。

2.material design:每个.dart文件第一行几乎会导入flutter/material.dart包。

3.flutter主题

4.无状态组件:是不可变的,有状态组件:持有的状态可能在Widget生命周期中发生变化。

5.导入需要的包资源。

6.http请求

(三)应用发布:跟android和ios一致。

                   

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhwadezh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值