每天保持好的精神状态和好的行为习惯
周六:星巴克一杯咖啡,看一下午书(最近在看东野圭吾的黎明之街)缓解一下周一到周五紧张的神经。慢慢的品尝美食。晚上看个电影(古仔演反派还是帅)
周日:窝在家里追一天剧,出门散散步
更多完整项目下载。未完待续。源码。图文知识后续上传github。
可以点击关于我 联系我获取
由于面的Android岗位,需要准备一些Java和Android方面的知识,有很多的文章都会去介绍怎么去复习基础的知识或者进阶知识,或者是阅读一些经典的面试数据。大家可以根据网上的文章和一些数据梳理一个自己的知识体系,将一个一个知识点形成点再到面,这样能更加深刻的理解,而且有自己的侧重点,我就列举一下我的准备的方向:
onCreate() -> onStart() -> onResume() -> onPause() -> onStop() -> onDetroy()
service
启动方式有两种
startService()
方式进行启动bindService()
方式进行启动。不同的启动方式他们的生命周期是不一样.
通过startService()
这种方式启动的service
生命周期是这样:
调用startService() --> onCreate()--> onStartConmon()--> onDestroy()。
这种方式启动的话,需要注意一下几个问题:
startService
被调用以后,多次在调用startService(),onCreate()
方法也只会被调用一次,而onStartConmon()
会被多次调用,当我们调用stopService()
的时候,onDestroy()
就会被调用,从而销毁服务。startService
启动时候,通过intent传值,在onStartConmon()
方法中获取值的时候,一定要先判断intent是否为null。通过bindService()
方式进行绑定,这种方式绑定service
生命周期:
bindService-->onCreate()-->onBind()-->unBind()-->onDestroy() bindservice
这种方式进行启动service好处是更加便利activity中操作service,如果要在activity中调用,在需要在activity获取ServiceConnection
对象,通过ServiceConnection
来获取service中内部类的类对象,然后通过这个类对象就可以调用类中的方法,当然这个类需要继承Binder对象
app
启动的过程有两种情况
launcher
上点击相应的应用图标startActivity
来启动一个新的activity。
我们创建一个新的项目,默认的根activity
都是MainActivity
,而所有的activity
都是保存在堆栈中的,我们启动一个新的activity就会放在上一个activity上面,而我们从桌面点击应用图标的时候,由于launcher
本身也是一个应用,当我们点击图标的时候,系统就会调用startActivitySately()
,一般情况下,我们所启动的activity的相关信息都会保存在intent中,比如action,category
等等。我们在安装这个应用的时候,系统也会启动一个PackaManagerService
的管理服务,这个管理服务会对AndroidManifest.xml
文件进行解析,从而得到应用程序中的相关信息,比如service,activity,Broadcast
等等,然后获得相关组件的信息。当我们点击应用图标的时候,就会调用startActivitySately()
方法,而这个方法内部则是调用startActivty()
,而startActivity()
方法最终还是会调用startActivityForResult()
这个方法。而在startActivityForResult()
这个方法。因为startActivityForResult()
方法是有返回结果的,所以系统就直接给一个-1,就表示不需要结果返回了。而startActivityForResult()
这个方法实际是通过Instrumentation
类中的execStartActivity()
方法来启动activity,Instrumentation
这个类主要作用就是监控程序和系统之间的交互。而在这个execStartActivity()
方法中会获取ActivityManagerService
的代理对象,通过这个代理对象进行启动activity。启动会就会调用一个checkStartActivityResult()
方法,如果说没有在配置清单中配置有这个组件,就会在这个方法中抛出异常了。当然最后是调用的是Application.scheduleLaunchActivity()
进行启动activity,而这个方法中通过获取得到一个ActivityClientRecord
对象,而这个ActivityClientRecord
通过handler来进行消息的发送,系统内部会将每一个activity组件使用ActivityClientRecord
对象来进行描述,而ActivityClientRecord
对象中保存有一个LoaderApk
对象,通过这个对象调用handleLaunchActivity
来启动activity组件,而页面的生命周期方法也就是在这个方法中进行调用。
此处延伸:什么情况下用动态注册
Broadcast
广播,注册方式主要有两种.
第一种是静态注册
,也可成为常驻型广播,这种广播需要在Androidmanifest.xml
中进行注册,这中方式注册的广播,不受页面生命周期的影响,即使退出了页面,也可以收到广播这种广播一般用于想开机自启动啊等等,由于这种注册的方式的广播是常驻型广播,所以会占用CPU的资源。
第二种是动态注册
,而动态注册的话,是在代码中注册的,这种注册方式也叫非常驻型广播,受到生命周期的影响,退出页面后,就不会收到广播,我们通常运用在更新UI方面。这种注册方式优先级较高。最后需要解绑,否会会内存泄露
广播是分为有序广播和无序广播。
此处延伸:Volley
里用的哪种请求方式
首先HttpClient
和HttpUrlConnection
这两种方式都支持Https
协议,都是以流的形式进行上传或者下载数据,也可以说是以流的形式进行数据的传输,还有ipv6
,以及连接池等功能。HttpClient
这个拥有非常多的API,所以如果想要进行扩展的话,并且不破坏它的兼容性的话,很难进行扩展,也就是这个原因,Google在Android6.0的时候,直接就弃用了这个HttpClient.
而HttpUrlConnection
相对来说就是比较轻量级了,API比较少,容易扩展,并且能够满足Android大部分的数据传输。比较经典的一个框架volley,在Android 2.3版本以前都是使用Android HttpClient,
在Android 2.3以后就使用了HttpUrlConnection
。
Java虚拟机:
java
虚拟机基于栈。java
虚拟机运行的是java
字节码。java
类会被编译成一个或多个字节码.class
文件.Dalvik虚拟机:
dalvik
虚拟机是基于寄存器的Dalvik
运行的是自定义的.dex
字节码格式。dx
工具将所有的.class文件转换成一个.dex
文件,然后dalvik
虚拟机会从其中读取指令和数据.linux
线程,都运行在自己的沙盒中,不同的应用在不同的进程中运行。每个android dalvik
应用程序都被赋予了一个独立的linux PID(app_*)
此处延伸:进程的优先级是什么
当前业界的Android进程保活手段主要分为黑、白、灰三种,其大致的实现思路如下:
黑色保活:
不同的app进程,用广播相互唤醒,包括利用系统提供的广播进行唤醒.
黑色保活
所谓黑色保活,就是利用不同的app进程使用广播来进行相互唤醒。举个3个比较常见的场景:
SDK
也会唤醒相应的app进程,如微信sdk
会唤醒微信,支付宝sdk
会唤醒支付宝。由此发散开去,就会直接触发了下面的 场景3白色保活
白色保活手段非常简单,就是调用系统api启动一个前台的Service进程,这样会在系统的通知栏生成一个Notification,用来让用户知道有这样一个app在运行着,哪怕当前的app退到了后台。
灰色保活
灰色保活,这种保活手段是应用范围最广泛。它是利用系统的漏洞来启动一个前台的Service进程,与普通的启动方式区别在于,它不会在系统通知栏处出现一个Notification
,看起来就如同运行着一个后台Service
进程一样。这样做带来的好处就是,用户无法察觉到你运行着一个前台进程(因为看不到Notification
),但你的进程优先级又是高于普通后台进程的。那么如何利用系统的漏洞呢,大致的实现思路和代码如下:
思路一:
API < 18
,启动前台Service
时直接传入new Notification()
;
思路二:API >= 18
,同时启动两个id相同的前台Service
,然后再将后启动的Service做stop处理
熟悉Android系统的童鞋都知道,系统出于体验和性能上的考虑,app在退到后台时系统并不会真正的kill掉这个进程,而是将其缓存起来。打开的应用越多,后台缓存的进程也越多。在系统内存不足的情况下,系统开始依据自身的一套进程回收机制来判断要kill掉哪些进程,以腾出内存来供给需要的app。这套杀进程回收内存的机制就叫Low Memory Killer
,它是基于Linux内核的OOM Killer(Out-Of-Memory killer)
机制诞生。
进程的重要性,划分5级:
(Foreground process)
(Visible process)
(Service process)
(Background process)
(Empty process)
了解完Low Memory Killer,再科普一下oom_adj
。什么是oom_adj
?它是 linux
内核分配给每个系统进程的一个值,代表进程的优先级,进程回收机制就是根据这个优先级来决定是否进行回收。对于oom_adj
的作用,你只需要记住以下几点即可:
进程的oom_adj
越大,表示此进程优先级越低,越容易被杀回收;越小,表示进程优先级越高,越不容易被杀回收
普通app进程的oom_adj>=0
,系统进程的oom_adj
才可能<0
有些手机厂商把这些知名的app放入了自己的白名单中,保证了进程不死来提高用户体验(如微信、QQ、陌陌都在小米的白名单中)。如果从白名单中移除,他们终究还是和普通app一样躲避不了被杀的命运,为了尽量避免被杀,还是老老实实去做好优化工作吧。
所以,进程保活的根本方案终究还是回到了性能优化上,进程永生不死终究是个彻头彻尾的伪命题!
Context是一个抽象基类。在翻译为上下文,也可以理解为环境,是提供一些程序的运行环境基础信息。Context下有两个子类,ContextWrapper
是上下文功能的封装类,而ContextImpl
则是上下文功能的实现类。而ContextWrapper
又有三个直接的子类, ContextThemeWrapper
、Service
和Application
。其中,ContextThemeWrapper
是一个带主题的封装类,而它有一个直接子类就是Activity,所以Activity和Service以及Application的Context是不一样的,只有Activity需要主题,Service不需要主题。
Context一共有三种类型,分别是Application
、Activity
和Service
。这三个类虽然分别各种承担着不同的作用,但它们都属于Context的一种,而它们具体Context的功能则是由ContextImpl
类去实现的,因此在绝大多数场景下,Activity、Service和Application这三种类型的Context都是可以通用的。
不过有几种场景比较特殊,比如启动Activity,还有弹出Dialog。出于安全原因的考虑,Android是不允许Activity或Dialog凭空出现的,一个Activity的启动必须要建立在另一个Activity的基础之上,也就是以此形成的返回栈。而Dialog则必须在一个Activity上面弹出(除非是System Alert类型的Dialog),因此在这种场景下,我们只能使用Activity类型的Context,否则将会出错。
getApplicationContext()
和getApplication()
方法得到的对象都是同一个application对象,只是对象的类型不一样。
Context数量 = Activity数量 + Service数量 + 1 (1为Application)
这个问题真的很不好回答。所以这里先来个算是比较恰当的比喻来形容下它们的关系吧。Activity像一个工匠(控制单元),Window像窗户(承载模型),View像窗花(显示视图)LayoutInflater
像剪刀,Xml
配置像窗花图纸。
1:Activity构造的时候会初始化一个Window,准确的说是
PhoneWindow
。
2:这个PhoneWindow
有一个ViewRoot
,这个ViewRoot
是一个View或者说ViewGroup
,是最初始的根视图。
3:ViewRoot
通过addView
方法来一个个的添加View。比如TextView
,Button等
4:这些View的事件监听,是由WindowManagerService
来接受消息,并且回调Activity函数。比如onClickListener
,onKeyDown
等。
此处延伸:栈(First In Last Out)
与队列(First In First Out)
的区别
栈与队列的区别:
standard 模式
这是默认模式,每次激活Activity时都会创建Activity实例,并放入任务栈中。使用场景:大多数Activity。
singleTop 模式
如果在任务的栈顶正好存在该Activity的实例,就重用该实例( 会调用实例的onNewIntent()),否则就会创建新的实例并放入栈顶,即使栈中已经存在该Activity的实例,只要不在栈顶,都会创建新的实例。使用场景如新闻类或者阅读类App的内容页面。
singleTask 模式
如果在栈中已经有该Activity的实例,就重用该实例(会调用实例的onNewIntent()
)。重用时,会让该实例回到栈顶,因此在它上面的实例将会被移出栈。如果栈中不存在该实例,将会创建新的实例放入栈中。使用场景如浏览器的主界面。不管从多少个应用启动浏览器,只会启动主界面一次,其余情况都会走onNewIntent
,并且会清空主界面上面的其他页面。
singleInstance 模式
在一个新栈中创建该Activity的实例,并让多个应用共享该栈中的该Activity实例。一旦该模式的Activity实例已经存在于某个栈中,任何应用再激活该Activity时都会重用该栈中的实例( 会调用实例的 onNewIntent()
)。其效果相当于多个应用共享一个应用,不管谁激活该Activity 都会进入同一个应用中。使用场景如闹铃提醒,将闹铃提醒与闹铃设置分离。singleInstance
不要用于中间页面,如果用于中间页面,跳转会有问题,比如:A -> B (singleInstance) -> C
,完全退出后,在此启动,首先打开的是B。
自定义控件:
1、组合控件。这种自定义控件不需要我们自己绘制,而是使用原生控件组合成的新控件。如标题栏。
2、继承原有的控件。这种自定义控件在原生控件提供的方法外,可以自己添加一些方法。如制作圆角,圆形图片。
3、完全自定义控件:这个View上所展现的内容全部都是我们自己绘制出来的。比如说制作水波纹进度条。
View的绘制流程:OnMeasure()——>OnLayout()——>OnDraw()
第一步:OnMeasure()
:测量视图大小。从顶层父View到子View递归调用measure方法,measure方法又回调OnMeasure。
第二步:OnLayout()
:确定View位置,进行页面布局。从顶层父View向子View的递归调用view.layout方法的过程,即父View根据上一步measure子View所得到的布局大小和布局参数,将子View放在合适的位置上。
第三步:OnDraw()
:绘制视图。
ViewRoot
创建一个Canvas对象,然后调用OnDraw()
。
六个步骤:
1.Touch
事件分发中只有两个主角:ViewGroup
和View
。
ViewGroup
包含onInterceptTouchEvent
、dispatchTouchEvent
、onTouchEvent
三个相关事件。
View
包含dispatchTouchEvent
、onTouchEvent
两个相关事件。
其中ViewGroup
又继承于View。
2.ViewGroup
和View
组成了一个树状结构,根节点为Activity内部包含的一个ViewGroup
。
3.触摸事件由Action_Down、Action_Move、Aciton_UP
组成,其中一次完整的触摸事件中,Down和Up都只有一个,Move有若干个,可以为0个。
4.当Acitivty
接收到Touch事件时,将遍历子View进行Down事件的分发。ViewGroup
的遍历可以看成是递归的。分发的目的是为了找到真正要处理本次完整触摸事件的View,这个View会在onTouchuEvent
结果返回true。
5.当某个子View返回true时,会中止Down事件的分发,同时在ViewGroup
中记录该子View。接下去的Move和Up事件将由该子View直接进行处理。由于子View是保存在ViewGroup
中的,多层ViewGroup
的节点结构时,上级ViewGroup
保存的会是真实处理事件的View所在的ViewGroup
对象:如ViewGroup0-ViewGroup1-TextView
的结构中,TextView
返回了true,它将被保存在ViewGroup1
中,而ViewGroup1
也会返回true,被保存在ViewGroup0
中。当Move和UP事件来时,会先从ViewGroup0
传递至ViewGroup1
,再由ViewGroup1
传递至TextView
。
6.当ViewGroup
中所有子View都不捕获Down事件时,将触发ViewGroup
自身的onTouch
事件。触发的方式是调用super.dispatchTouchEvent
函数,即父类View的dispatchTouchEvent
方法。在所有子View都不处理的情况下,触发Acitivity的onTouchEvent
方法。
7.onInterceptTouchEvent
有两个作用:
ViewGroup
捕获Up和Move事件。onSaveInstanceState(Bundle)
会在activity转入后台状态之前被调用,也就是onStop()
方法之前,onPause
方法之后被调用;
帧动画:
指通过指定每一帧的图片和播放时间,有序的进行播放而形成动画效果,比如想听的律动条。
补间动画:
指通过指定View的初始状态、变化时间、方式,通过一系列的算法去进行图形变换,从而形成动画效果,主要有Alpha、Scale、Translate、Rotate四种效果。
注意:只是在视图层实现了动画效果,并没有真正改变View的属性,比如滑动列表,改变标题栏的透明度。
属性动画:
在Android3.0的时候才支持,通过不断的改变View的属性,不断的重绘而形成动画效果。相比于视图动画,View的属性是真正改变了。比如view的旋转,放大,缩小。
Android跨进程通信,像intent,contentProvider.广播,service都可以跨进程通信。
intent:
这种跨进程方式并不是访问内存的形式,它需要传递一个uri,比如说打电话。
contentProvider:
这种形式,是使用数据共享的形式进行数据共享。
service:
远程服务,比如aidl
广播:
广播 包含静态广播,动态广播。
此处延伸:简述Binder
AIDL:
每一个进程都有自己的Dalvik VM
实例,都有自己的一块独立的内存,都在自己的内存上存储自己的数据,执行着自己的操作,都在自己的那片狭小的空间里过完自己的一生。而aidl
就类似与两个进程之间的桥梁,使得两个进程之间可以进行数据的传输,跨进程通信有多种选择,比如BroadcastReceiver
, Messenger
等,但是 BroadcastReceiver
占用的系统资源比较多,如果是频繁的跨进程通信的话显然是不可取的;Messenger进行跨进程通信时请求队列是同步进行的,无法并发执行。
Binder机制简单理解:
在Android系统的Binder机制中,是有Client,Service,ServiceManager,Binder
驱动程序组成的,其中Client,service,Service Manager
运行在用户空间,Binder驱动程序是运行在内核空间的。而Binder就是把这4种组件粘合在一块的粘合剂,其中核心的组件就是Binder驱动程序,Service Manager
提供辅助管理的功能,而Client
和Service
正是在Binder驱动程序和Service Manager
提供的基础设施上实现C/S 之间的通信。其中Binder驱动程序提供设备文件/dev/binder
与用户控件进行交互,
Client、Service,Service Manager
通过open
和ioctl
文件操作相应的方法与Binder驱动程序进行通信。而Client
和Service
之间的进程间通信是通过Binder驱动程序间接实现的。而Binder Manager是一个守护进程,用来管理Service,并向Client提供查询Service接口的能力。
Android中主线程是不能进行耗时操作的,子线程是不能进行更新UI的。所以就有了handler,它的作用就是实现线程之间的通信。
handler整个流程中,主要有四个对象,
handler,Message,MessageQueue,Looper
。当应用创建的时候,就会在主线程中创建handler对象,
我们通过要传送的消息保存到Message
中,handler通过调用sendMessage
方法将Message发送到MessageQueue
中,Looper
对象就会不断的调用loop()方法
不断的从MessageQueue
中取出Message交给handler进行处理。从而实现线程之间的通信。
在Android系统的Binder机制中,是有Client,Service,ServiceManager,Binder
驱动程序组成的,其中Client,service,Service Manager
运行在用户空间,Binder驱动程序是运行在内核空间的。而Binder就是把这4种组件粘合在一块的粘合剂,其中核心的组件就是Binder驱动程序,Service Manager提供辅助管理的功能,而Client和Service正是在Binder驱动程序和Service Manager提供的基础设施上实现C/S之间的通信。其中Binder驱动程序提供设备文件/dev/binder
与用户控件进行交互,Client、Service,Service Manager
通过open和ioctl文件操作相应的方法与Binder驱动程序进行通信。而Client和Service之间的进程间通信是通过Binder驱动程序间接实现的。而Binder Manager是一个守护进程,用来管理Service,并向Client提供查询Service接口的能力。
我们知道Java虚拟机 —— JVM
是加载类的class文件的,而Android虚拟机——Dalvik/ART VM
是加载类的dex
文件,而他们加载类的时候都需要ClassLoader,ClassLoader
有一个子BaseDexClassLoader
,而BaseDexClassLoader
下有一个数组——DexPathList
,是用来存放dex
文件,当BaseDexClassLoader
通过调用findClass
方法时,实际上就是遍历数组,找到相应的dex
文件,找到,则直接将它return。而热修复的解决方法就是将新的dex
添加到该集合中,并且是在旧的dex
的前面,所以就会优先被取出来并且return返回。
OOM
)和内存泄露(对象无法被回收)的区别。LeakCanary
内存溢出out of memory
:
是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory
;
比如申请了一个integer
,但给它存了long
才能存下的数,那就是内存溢出。内存溢出通俗的讲就是内存不够用。
内存泄露memory leak
:
是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光
内存泄露原因:
一、Handler 引起的内存泄漏。
解决:将Handler声明为静态内部类,就不会持有外部类SecondActivity
的引用,其生命周期就和外部类无关,
如果Handler里面需要context的话,可以通过弱引用方式引用外部类
二、单例模式引起的内存泄漏。
解决:Context
是ApplicationContext
,由于ApplicationContext
的生命周期是和app一致的,不会导致内存泄漏
三、非静态内部类创建静态实例引起的内存泄漏。
解决:把内部类修改为静态的就可以避免内存泄漏了
四、非静态匿名内部类引起的内存泄漏。
解决:将匿名内部类设置为静态的。
五、注册/反注册未成对使用引起的内存泄漏。
注册广播接受器、EventBus
等,记得解绑。
六、资源对象没有关闭引起的内存泄漏。
在这些资源不使用的时候,记得调用相应的类似close()、destroy()、recycler()、release()
等方法释放。
七、集合对象没有及时清理引起的内存泄漏。
通常会把一些对象装入到集合中,当不使用的时候一定要记得及时清理集合,让相关对象不再被引用。
Fragment
中调用另外一个Fragment
中的方法字体使用sp
,
dp
,多使用match_parent,wrap_content,weight
图片资源,不同图片的的分辨率,放在相应的文件夹下可使用百分比代替。
app优化:
工具:Hierarchy Viewer 分析布局
工具:TraceView测试分析耗时
App启动优化
布局优化
响应优化
内存优化
电池使用优化
网络优化
App启动的方式有三种:
冷启动:
App没有启动过或App进程被killed, 系统中不存在该App进程, 此时启动App即为冷启动。
热启动:
热启动意味着你的App进程只是处于后台, 系统只是将其从后台带到前台, 展示给用户。
介于冷启动和热启动之间,
一般来说在以下两种情况下发生:
(1)用户
back
退出了App
, 然后又启动.
App进程可能还在运行, 但是activity需要重建。
(2)用户退出App后, 系统可能由于内存原因将App杀死, 进程和activity都需要重启, 但是可以在onCreate
中将被动杀死锁保存的状态(saved instance state)
恢复。
启动优化:
Application的onCreate
(特别是第三方SDK初始化),首屏Activity的渲染都不要进行耗时操作,如果有,就可以放到子线程或者IntentService
中。
布局优化
尽量不要过于复杂的嵌套。可以使用
响应优化
Android系统每隔16ms会发出VSYNC信号重绘我们的界面(Activity)。
页面卡顿的原因:
(1)过于复杂的布局.
(2)UI线程的复杂运算
(3)频繁的GC
导致频繁GC有两个原因:
1、内存抖动, 即大量的对象被创建又在短时间内马上被释放.
2、瞬间产生大量的对象会严重占用内存区域。
电池使用优化
工具:Batterystats & bugreport
(1)优化网络请求
(2)定位中使用GPS, 请记得及时关闭
网络优化
网络连接对用户的影响:流量,电量,用户等待,可在Android studio下方logcat
旁边那个工具Network Monitor检测
API设计:
App与Server之间的API设计要考虑网络请求的频次, 资源的状态等. 以便App可以以较少的请求来完成业务需求和界面的展示.
Gzip压缩:
使用Gzip来压缩request和response, 减少传输数据量, 从而减少流量消耗.
图片的Size:
可以在获取图片时告知服务器需要的图片的宽高, 以便服务器给出合适的图片, 避免浪费.
网络缓存:
适当的缓存, 既可以让我们的应用看起来更快, 也能避免一些不必要的流量消耗.
(1)对图片本身进行操作。
尽量不要使用setImageBitmap、setImageResource、BitmapFactory.decodeResource
来设置一张大图,因为这些方法在完成decode后,最终都是通过 java 层的createBitmap
来完成的,需要消耗更多内存.
(2)图片进行缩放的比例,SDK中建议其值是2的指数值,值越大会导致图片不清晰。
(3)不用的图片记得调用图片的recycle()方法
Android与JS
通过WebView
互相调用方法,实际上是:
Android去调用JS的代码
通过WebView
的loadUrl()
,使用该方法比较简洁,方便。但是效率比较低,获取返回值比较困难。
通过WebView
的evaluateJavascript()
,该方法效率高,但是4.4以上的版本才支持,4.4以下版本不支持。所以建议两者混合使用。
JS去调用Android的代码
WebView
的addJavascriptInterface()
进行对象映射 ,该方法使用简单,仅将Android对象和JS对象映射即可,但是存在比较大的漏洞。(java.lang.Runtime 类)
,从而进行任意代码执行。解决方法:
(1)Google 在Android 4.2版本中规定对被调用的函数以
@JavascriptInterface
进行注解从而避免漏洞攻击。
(2)在Android 4.2版本之前采用拦截prompt()进行漏洞修复。
WebViewClient
的shouldOverrideUrlLoading ()
方法回调拦截url 。(1)Android通过 WebViewClient 的回调方法shouldOverrideUrlLoading ()拦截 url
(2)解析该url的协议
(3)如果检测到是预先约定好的协议,就调用相应方法
WebChromeClient
的onJsAlert()
、onJsConfirm()
、onJsPrompt()
方法回调拦截JS对话框alert()、confirm()、prompt() 消息垃圾收集算法的核心思想是:
对虚拟机可用内存空间,即堆空间中的对象进行识别,如果对象正在被引用,那么称其为存活对象,反之,如果对象不再被引用,则为垃圾对象,可以回收其占据的空间,用于再分配。垃圾收集算法的选择和垃圾收集系统参数的合理调节直接影响着系统性能。
ANR全名Application Not Responding, 也就是"应用无响应". 当操作在一段时间内系统无法处理时, 系统层面会弹出上图那样的ANR对话框.
产生原因:
(1)5s内无法响应用户输入事件(例如键盘输入, 触摸屏幕等).
(2)BroadcastReceiver
在10s内无法结束
(3)Service 20s内无法结束(低概率)
解决方式:
(1)不要在主线程中做耗时的操作,而应放在子线程中来实现。如
onCreate()
和onResume()
里尽可能少的去做创建操作。
(2)应用程序应该避免在BroadcastReceiver
里做耗时的操作或计算。
(3)避免在Intent Receiver里启动一个Activity,因为它会创建一个新的画面,并从当前用户正在运行的程序上抢夺焦点。
(4)service是运行在主线程的,所以在service中做耗时操作,必须要放在子线程中。
此处延伸:Double Check
的写法被要求写出来。
单例模式:分为恶汉式和懒汉式
恶汉式:
public class Singleton
{
private static Singleton instance = new Singleton();
public static Singleton getInstance()
{
return instance ;
}
}
懒汉式:
public class Singleton02
{
private static Singleton02 instance;
public static Singleton02 getInstance()
{
if (instance == null)
{
synchronized (Singleton02.class)
{
if (instance == null)
{
instance = new Singleton02();
}
}
}
return instance;
}
}
此处延伸:手写mvp例子,与mvc之间的区别,mvp的优势
MVP模式
对应着
Model
:业务逻辑和实体模型,
view
:对应着activity,负责View的绘制以及与用户交互,
Presenter
:负责View和Model之间的交互,
MVP模式是在MVC
模式的基础上,将Model与View彻底分离使得项目的耦合性更低,在Mvc
中项目中的activity对应着mvc
中的C–Controllor
,而项目中的逻辑处理都是在这个C中处理,同时View与Model之间的交互,也是也就是说,mvc
中所有的逻辑交互和用户交互,都是放在Controllor
中,也就是activity中。View和model是可以直接通信的。而MVP模式则是分离的更加彻底,分工更加明确Model业务逻辑和实体模型,view负责与用户交互,Presenter 负责完成View于Model间的交互,MVP
和MVC最大的区别是MVC中是允许Model和View进行交互的,而MVP中很明显,Model与View之间的交互由Presenter完成。还有一点就是Presenter
与View
之间的交互是通过接口的
(1)安装和下载
Cygwin
,下载Android NDK
(2)在ndk
项目中JNI
接口的设计
(3)使用C/C++实现本地方法
(4)JNI生成动态链接库.so文件
(5)将动态链接库复制到java工程,在java工程中调用,运行java工程即可
RecyclerView
可以完成ListView
,GridView
的效果,还可以完成瀑布流的效果。同时还可以设置列表的滚动方向(垂直或者水平);
RecyclerView
中view
的复用不需要开发者自己写代码,系统已经帮封装完成了。
RecyclerView
可以进行局部刷新。
RecyclerView
提供了API
来实现item
的动画效果。
在性能上:
如果需要频繁的刷新数据,需要添加动画,则RecyclerView
有较大的优势。
如果只是作为列表展示,则两者区别并不是很大。
更多完整项目下载。未完待续。源码。图文知识后续上传github。
可以点击关于我 联系我获取