1.Android Touch事件传递机制
在我们点击屏幕时,会有下列事件发生: Activity调用dispathTouchEvent()方法,把事件传递给Window; Window再将事件交给DecorView(DecorView是View的根布局); DecorView再传递给ViewGroup;
Activity ——> Window ——> DecorView ——> ViewGroup——> View
事件分发的主要有三个关键方法 dispatchTouchEvent() 分发 onInterceptTouchEvent() 拦截 ,只有ViewGroup独有此方法 onTouchEvent() 处理触摸事件
Activity首先调用dispathTouchEvent()进行分发,接着调用super向下传递
ViewGroup首先调用dispathTouchEvent()进行分发,接着会调用onInterceptTouchEvent()(拦截事件)。若拦截事件返回为true,表示拦截,事件不会向下层的ViewGroup或者View传递;false,表示不拦截,继续分发事件。默认是false,需要提醒一下,View是没有onInterceptTouchEvent()方法的
事件在ViewGroup和ViewGroup、ViewGroup和View之间进行传递,最终到达View;
View调用dispathTouchEvent()方法,然后在OnTouchEvent()进行处理事件;OnTouchEvent() 返回true,表示消耗此事件,不再向下传递;返回false,表示不消耗事件,交回上层处理。
2.Android Handler消息机制?
作用:
跨线程通信。当子线程中进行耗时操作后需要更新UI时,通过Handler将有关UI的操作切换到主线程中执行。
四要素: Message(消息):需要被传递的消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,最终由Handler处理。 MessageQueue(消息队列):用来存放Handler发送过来的消息,内部通过单链表的数据结构来维护消息列表,等待Looper的抽取。 Handler(处理者):负责Message的发送及处理。通过 Handler.sendMessage() 向消息池发送各种消息事件;通过 Handler.handleMessage() 处理相应的消息事件。 Looper(消息泵):通过Looper.loop()不断地从MessageQueue中抽取Message,按分发机制将消息分发给目标处理者。
Handler.sendMessage()发送消息时,会通过MessageQueue.enqueueMessage()向MessageQueue中添加一条消息; 通过Looper.loop()开启循环后,不断轮询调用MessageQueue.next(); 调用目标Handler.dispatchMessage()去传递消息,目标Handler收到消息后调用Handler.handlerMessage()处理消息
3.RecyclerView和ListView的区别(这个是必问的)
RecyclerView可以完成ListView,GridView的效果,还可以完成瀑布流的效果。同时还可以设置列表的滚动方向(垂直或者水平); RecyclerView中view的复用不需要开发者自己写代码,系统已经帮封装完成了。 RecyclerView可以进行局部刷新。 RecyclerView提供了API来实现item的动画效果。
在性能上: 如果需要频繁的刷新数据,需要添加动画,则RecyclerView有较大的优势。 如果只是作为列表展示,则两者区别并不是很大。
4. Activity的四种启动模式对比?
- Standard 标准化启动模式
每启动一个Activity,都会重新创建Activity的新的实例,将其放在栈的顶部。不需要考虑这个实例是否已经存在。
每一次启动,它的onCreate()、onStart()、onResume()方法都会被依次调用。
- SingeTop 栈顶复用模式
当前栈中已经有该Activity实例,并且该实例位于栈顶时,会去调用onNewIntent()方法。
当前栈中已有该Activity的实例但是该实例不在栈顶时,依然会去创建Activity。
当前栈中不存在该Activity实例时,会去新创建一个该Activity。
应用场景:IM对话框、新闻客户端推送。
- SingleTask 栈内复用模式
它主要检测【寻找,通过taskAffinity】整个栈中是否已经存在当前想要启动的Activity,存在的话直接将该Activity置于栈顶,之前位于该Activity上面的Activity将被销毁,同时调用onNewIntent()方法,而不存在的话进行创建。
应用场景:应用主界面。
- singleInstance
一个人独享一个任务栈。当该Activity启动时,系统会创建一个新的任务栈,同时将Activity放到这个新的任务栈当中,有别的应用来启动该Activity时,由于栈内复用的特性,不会再去创建相应Activity任务栈,而是这两个应用独享一个Activity实例。
例如:应用A中现有两个Activity E、Activity F,为standard启动模式,应用B中有一个Activity G,但其启动模式是singleInstance。应用A想用应用B任务栈当中的Activity G,尽管在不同的应用下,但是应用A仍然会直接复用Activity G。
特性:
1、以SingleInstance模式启动的Activity具有全局唯一性【全局唯一性即指在整个系统当中只会存在一个这样的实例】;
2、如果在启动这样一个Activity时,【整个系统都是单例的】,已经存在了一个实例;
3、以SingleInstance模式启动的Activity具有独占性。
应用场景:呼叫来电。
5.内存泄漏和内存溢出是什么?一般怎么处理内存泄漏?
(1)内存溢出(OOM)和内存泄露(对象无法被回收)的区别。
(2)引起内存泄露的原因
(3)内存泄露检测工具 ------>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()等方法释放。 七、集合对象没有及时清理引起的内存泄漏。 通常会把一些对象装入到集合中,当不使用的时候一定要记得及时清理集合,让相关对象不再被引用。
6.ANR是什么?怎样避免和解决ANR
Application Not Responding,即应用无响应
出现的原因有三种: a)KeyDispatchTimeout(5 seconds)主要类型按键或触摸事件在特定时间内无响应 b)BroadcastTimeout(10 seconds)BoradcastReceiver在特定的时间内无法处理 c)ServiceTimeout(20 seconds)小概率类型Service在特定的时间内无法处理完成
避免ANR最核心的一点就是在主线程减少耗时操作。通常需要从那个以下几个方案下手: a)使用子线程处理耗时IO操作 b)降低子线程优先级,使用Thread或者HandlerThread时,调用Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)设置优先级,否则仍然会降低程序响应,因为默认Thread的优先级和主线程相同 c)使用Handler处理子线程结果,而不是使用Thread.wait()或者Thread.sleep()来阻塞主线程 d)Activity的onCreate和onResume回调中尽量避免耗时的代码 e)BroadcastReceiver中onReceiver代码也要尽量减少耗时操作,建议使用intentService处理。intentService是一个异步的,会自动停止的服务,很好解决了传统的Service中处理完耗时操作忘记停止并销毁Service的问题.
7.Android异步消息处理机制
异步消息处理机制主要是用来解决子线程更新UI的问题
主要有四个部分:
- 7.1). Message (消息) 在线程之间传递,可在内部携带少量信息,用于不同线程之间交换数据 可以使用what、arg1、arg2字段携带整型数据 obj字段携带Object对象
- 7.2). Handler (处理者) 主要用于发送和处理消息,sendMessage()用来发送消息,最终会回到handleMessage()进行处理
- 7.3). MessageQueue (消息队列) 主要存放所有通过Handler发送的消息,它们会一直存在于队列中等待被处理 每个线程只有一个MessageQueue
- 7.4). Looper (循环器) 调用loop()方法后,会不断从MessageQueue 取出待处理的消息,然后传递到handleMessage进行处理
8.Android Handler四大组件?
- 8.1).Message
Message是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据。 例:Message的what字段、arg1字段、arg2字段来携带整型数据,obj字段携带一个Object对象。
- 8.2).Handler
处理者,它主要用来发送和处理消息。发送消息一般是使用Handler的sendMessage()方法,消息经过处理后,最终传递到Handler的handlerMessage()方法中。
- 8.3).MessageQueue
消息队列,它主要用来存放所有通过Handler发送的消息,这部分消息会一直存在于消息队列中,等待被处理。注意:每个线程中只会有一个MessageQueue对象。
- 8.4).Looper
是每个线程中MessageQueue的管家,调用Looper的loop()方法后,就会进入到一个无限循环当中,每当发现MessageQueue中存在一条消息,就会将其取出传递到Handler的handleMessage()方法当中。
注意:每个线程中只会有一个Looper对象。
异步消息处理流程:
在主线程当中创建一个Handler对象;
重写handleMessage()方法;
当子线程需要进行UI操作时,创建一个Message对象,并通过Handler将消息发送出去;
消息添加到MessageQueue的队列中等待被处理;
Looper在MessageQueue中取出待处理消息,发回Handler的handleMessage()方法中。
【由于Handler是在主线程中创建的,因此我们的handleMessage()方法中的代码也会在主线程中执行,避免了异常的产生】
9.图片加载框架有哪些?他们之间的区别是什么?
-
9.1).ImageLoader :
优点:
① 支持下载进度监听;
② 可以在 View 滚动中暂停图片加载;
③ 默认实现多种内存缓存算法这几个图片缓存都可以配置缓存算法,不过 ImageLoader 默认实现了较多缓存算法,如 Size 最大先删除、使用最少先删除、最近最少使用、先进先删除、时间最长先删除等; ④ 支持本地缓存文件名规则定义; 缺点: 缺点在于不支持GIF图片加载, 缓存机制没有和http的缓存很好的结合, 完全是自己的一套缓存机制 -
9.2).Picasso:
优点:
① 自带统计监控功能,支持图片缓存使用的监控,包括缓存命中率、已使用内存大小、节省的流量等。
② 支持优先级处理
③ 支持延迟到图片尺寸计算完成加载
④ 支持飞行模式、并发线程数根据网络类型而变,手机切换到飞行模式或网络类型变换时会自动调整线程池最大并发数。
⑤ “无”本地缓存。Picasso 自己没有实现本地缓存,而由okhttp 去实现,这样的好处是可以通过请求 Response Header 中的 Cache-Control 及 Expired 控制图片的过期时间。 缺点: 于不支持GIF,默认使用ARGB_8888格式缓存图片,缓存体积大。 -
9.3).Glide:
优点:
① 图片缓存->媒体缓存 ,支持 Gif、WebP、缩略图。甚至是 Video。
② 支持优先级处理
③ 与 Activity/Fragment 生命周期一致,支持 trimMemory
④ 支持 okhttp、Volley。Glide 默认通过 UrlConnection 获取数据,可以配合 okhttp 或是 Volley 使用。实际 ImageLoader、Picasso 也都支持 okhttp、Volley。
⑤ 内存友好,内存缓存更小图片,图片默认使用默认 RGB565 而不是 ARGB888 缺点: 清晰度差,但可以设置 -
9.4).Fresco:
优点:
① 图片存储在安卓系统的匿名共享内存, 而不是虚拟机的堆内存中,所以不会因为图片加载而导致oom, 同时也减少垃圾回收器频繁调用回收Bitmap导致的界面卡顿,性能更高.
② 渐进式加载JPEG图片, 支持图片从模糊到清晰加载
③ 图片可以以任意的中心点显示在ImageView, 而不仅仅是图片的中心.
④ JPEG图片改变大小也是在native进行的, 不是在虚拟机的堆内存, 同样减少OOM
⑤ 很好的支持GIF图片的显示 缺点: 框架较大, 影响Apk体积,使用较繁琐
Android 三级缓存?
网络加载,不优先加载,速度慢,浪费流量 本地缓存,次优先加载,速度快 内存缓存,优先加载,速度最快 首次加载Android App时,肯定要通过网络交互来获取图片,之后我们可以将图片保存至本地SD卡和内存中,之后运行APP时,优先访问内存中的图片缓存,若内存中没有,则加载本地SD卡中图片,最后选择访问网络
Android 广播的分类?
分为有序广播和无序广播两类。
无序广播发送代码:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void click(View v){
//启动界面 startActivity
//发送广播 sendBroadcast
Intent intent = new Intent();
intent.setAction("com.itheima.cctv.action.NEWS");
intent.putExtra("data", "我是一个无须的广播");
sendBroadcast(intent);
}
}
}
无序广播的监听代码:
public class CctvReceiver extends BroadcastReceiver {
private static final String TAG = "CctvReceiver";
@Override
public void onReceive(Context context, Intent intent) {
String data = intent.getStringExtra("data");
Log.d(TAG, "data==="+data);
}
}
<receiver android:name="com.itheima.cctv.CctvReceiver">
<intent-filter >
<!--这里监听的广播就是上面发送广播设置的 intent.setAction("com.itheima.cctv.action.NEWS");-->
<action android:name="com.itheima.cctv.action.NEWS"/>
</intent-filter>
</receiver>
有序广播发送:
public class ShengReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.d("vivi", "我是省级部门,我收到的指令是:"+getResultData());
//getResultData()是用来获取有序广播里面的数值.这里的信息是:
//主席讲话:每人奖励10斤土豆
setResultData("主席讲话:每人奖励7斤土豆");//有序广播的数值,可以被修改,后面的程序在接受到这个广播,就会变成,现在我们改变的值了
//有序广播传输是可以终止的.但是最终的接受者就算在终止之后,也是可以接受到数据的
//abortBroadcast();
}
}
public class ShiReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.d("vivi", "我是市级部门,我收到的指令是:"+getResultData());
//因为上面修改了数据,所以这里获取到的数据是:主席讲话:每人奖励7斤土豆
}
}
<!--有序广播的优先级别使用 android:priority=""来指定,最高是1000,最低是-1000-->
<receiver android:name="com.itheima.region.ShengReceiver">
<intent-filter android:priority="1000">
<action android:name="com.itheima.gov.action.POTATO"/>
</intent-filter>
</receiver>
<receiver android:name="com.itheima.region.ShiReceiver">
<intent-filter android:priority="500">
<action android:name="com.itheima.gov.action.POTATO"/>
</intent-filter>
</receiver>
有序接收:
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.d("vivi", "我是恩恩主席的内线,我收到的指令是:"+getResultData());
}
}
Android中的动画有哪些
逐帧动画(Frame Animation) 加载一系列Drawable资源来创建动画,简单来说就是播放一系列的图片来实现动画效果,可以自定义每张图片的持续时间
补间动画(Tween Animation) Tween可以对View对象实现一系列动画效果,比如平移,缩放,旋转,透明度等。但是它并不会改变View属性的值,只是改变了View的绘制的位置,比如,一个按钮在动画过后,不在原来的位置,但是触发点击事件的仍然是原来的坐标。
属性动画(Property Animation) 动画的对象除了传统的View对象,还可以是Object对象,动画结束后,Object对象的属性值被实实在在的改变了
网络框架有哪些?他们之间的区别是什么?
Xutils 这个框架非常全面,可以进行网络请求,可以进行图片加载处理,可以数据储存,还可以对view进行注解,使用这个框架非常方便,但是缺点也是非常明显的,使用这个项目,会导致项目对这个框架依赖非常的严重,一旦这个框架出现问题,那么对项目来说影响非常大的
OKhttp Android开发中是可以直接使用现成的api进行网络请求的。就是使用HttpClient,HttpUrlConnection进行操作。okhttp针对Java和Android程序,封装的一个高性能的http请求库,支持同步,异步,而且okhttp又封装了线程池,封装了数据转换,封装了参数的使用,错误处理等。API使用起来更加的方便。但是我们在项目中使用的时候仍然需要自己在做一层封装,这样才能使用的更加的顺手。
Volley Volley是Google官方出的一套小而巧的异步请求库,该框架封装的扩展性很强,支持HttpClient、HttpUrlConnection, 甚至支持OkHttp,而且Volley里面也封装了ImageLoader,所以如果你愿意你甚至不需要使用图片加载框架,不过这块功能没有一些专门的图片加载框架强大,对于简单的需求可以使用,稍复杂点的需求还是需要用到专门的图片加载框架。Volley也有缺陷,比如不支持post大数据,所以不适合上传文件。不过Volley设计的初衷本身也就是为频繁的、数据量小的网络请求而生。
Retrofit Retrofit是Square公司出品的默认基于OkHttp封装的一套RESTful网络请求框架,RESTful是目前流行的一套api设计的风格, 并不是标准。Retrofit的封装可以说是很强大,里面涉及到一堆的设计模式,可以通过注解直接配置请求,可以使用不同的http客户端,虽然默认是用http ,可以使用不同Json Converter 来序列化数据,同时提供对RxJava的支持,使用Retrofit + OkHttp + RxJava + Dagger2 可以说是目前比较潮的一套框架,但是需要有比较高的门槛。
Volley VS OkHttp Volley的优势在于封装的更好,而使用OkHttp你需要有足够的能力再进行一次封装。而OkHttp的优势在于性能更高,因为 OkHttp基于NIO和Okio ,所以性能上要比 Volley更快。IO 和 NIO这两个都是Java中的概念,如果我从硬盘读取数据,第一种方式就是程序一直等,数据读完后才能继续操作这种是最简单的也叫阻塞式IO,还有一种是你读你的,程序接着往下执行,等数据处理完你再来通知我,然后再处理回调。而第二种就是 NIO 的方式,非阻塞式, 所以NIO当然要比IO的性能要好了,而 Okio是 Square 公司基于IO和NIO基础上做的一个更简单、高效处理数据流的一个库。理论上如果Volley和OkHttp对比的话,更倾向于使用 Volley,因为Volley内部同样支持使用OkHttp,这点OkHttp的性能优势就没了, 而且 Volley 本身封装的也更易用,扩展性更好些。
OkHttp VS Retrofit 毫无疑问,Retrofit 默认是基于 OkHttp 而做的封装,这点来说没有可比性,肯定首选 Retrofit。
Volley VS Retrofit 这两个库都做了不错的封装,但Retrofit解耦的更彻底,尤其Retrofit2.0出来,Jake对之前1.0设计不合理的地方做了大量重构, 职责更细分,而且Retrofit默认使用OkHttp,性能上也要比Volley占优势,再有如果你的项目如果采用了RxJava ,那更该使用 Retrofit 。所以这两个库相比,Retrofit更有优势,在能掌握两个框架的前提下该优先使用 Retrofit。但是Retrofit门槛要比Volley稍高些,要理解他的原理,各种用法,想彻底搞明白还是需要花些功夫的,如果你对它一知半解,那还是建议在商业项目使用Volley吧。
Android与服务器交互的方式中的对称加密和非对称加密是什么?
对称加密,就是加密和解密数据都是使用同一个key,这方面的算法有DES。 非对称加密,加密和解密是使用不同的key。发送数据之前要先和服务端约定生成公钥和私钥,使用公钥加密的数据可以用私钥解密,反之。这方面的算法有RSA。ssh 和 ssl都是典型的非对称加密。
谈谈ContentProvider、ContentResolver、ContentObserver之间的关系?
ContentProvider:
-
四大组件的内容提供者,主要用于对外提供数据
-
实现各个应用程序之间的(跨应用)数据共享,比如联系人应用中就使用了ContentProvider,你在自己的应用中可以读取和修改联系人的数据,不过需要获得相应的权限。其实它也只是一个中间人,真正的数据源是文件或者SQLite等
-
一个应用实现ContentProvider来提供内容给别的应用来操作,通过ContentResolver来操作别的应用数据,当然在自己的应用中也可以
ContentResolver: -
内容解析者,用于获取内容提供者提供的数据
-
ContentResolver.notifyChange(uri)发出消息
ContentObserver: -
内容监听器,可以监听数据的改变状态
-
目的是观察(捕捉)特定Uri引起的数据库的变化,继而做一些相应的处理,它类似于数据库技术中的触发器(Trigger),当ContentObserver所观察的Uri发生变化时,便会触发它。触发器分为表触发器、行触发器,相应地ContentObsever也分为表ContentObserver、行ContentObserver,当然这是与它所监听的Uri MIME Type有关的
-
ContentResolver.registerContentObserver()监听消息
Android service生命周期?
- 被启动的服务(startService())的生命周期。 如果一个Service被某个Activity 调用Context.startService() 方法启动,那么不管是否有Activity使用bindService()绑定或unbindService()解除绑定到该Service,该Service都在后台运行。如果一个Service被多次执行startService(),它的onCreate()方法只会调用一次,也就是说该Service只会创建一个实例,而它的onStartCommand()将会被调用多次(对应调用startService()的次数)。该Service将会一直在后台运行,直到被调用stopService(),或自身的stopSelf方法。当然如果系统资源不足,系统也可能结束服务。
- 被绑定的服务(bindService())的生命周期。 如果一个Service被调用 Context.bindService ()方法绑定启动,不管调用bindService()调用几次,onCreate()方法都只会调用一次,而onStartCommand()方法始终不会被调用,这时会调用onBind()方法。当连接建立之后,Service将会一直运行,除非调用Context.unbindService() 断开连接或者之前调用bindService() 的 Context 不存在了(如该Activity被finish),系统将会自动停止Service,对应onDestroy()将被调用。
- 被启动又被绑定的服务的生命周期。 如果一个Service又被启动又被绑定,则该Service将会一直在后台运行。调用unbindService()将不会停止Service,而必须调用stopService()或Service的stopSelf()方法来停止服务。
- 当服务被停止时清除服务。 当一个Service被终止时,Service的onDestroy()方法将会被调用,在这里应当做一些清除工作,如停止在Service中创建并运行的线程等。
activity和service的绑定方式以及怎么在Activity 中启动自己对应的Service?
-
activity能进行绑定得益于Serviece的接口。为了支持Service的绑定,实现onBind方法。
-
Service和Activity的连接可以用ServiceConnection来实现。需要实现一个新的ServiceConnection,重现onServiceConnected和OnServiceDisconnected方法,一旦连接建立,就能得到Service实例的引用。
-
执行绑定,调用bindService方法,传入一个选择了要绑定的Service的Intent(显示或隐式)和一个你实现了的ServiceConnection的实例
Android Service启动方式
1.startService
①.定义一个类继承service
②.在manifest.xml文件中配置该service
③.使用context的startService(intent)启动该service
④.不再使用时,调用stopService(Intent)停止该服务
2.bindService
①.创建bindService服务段,继承自service并在类中,创建一个实现binder接口的实例对象并提供公共方法给客户端调用
②.从onbind()回调方法返回此binder实例
③.在客户端中,从onserviceconnected()回调方法接收binder,并使用提供的方法调用绑定服务
Object… objects 和Object[] obj 有什么区别? 注意前一个Object 后面的三个“.”
日常使用不定参数时候基本是Object…可以等同于 Object[] obj一维数组理解的
数组本质是连续的指针地址 其中数组的头指针是必须的 但是二者的不同在于何处呢?
很明显
Obj…是针对方法传入参数的 即只能在method()内使用
即 不定参数只能作为方法的形参,不能用作变量
public viod method(Object[] obj)必须传入已经初始化的数组 而已经初始化的数组是定常度的 所以实际没有实现真正的变长度参数
而
public viod method(Object…) 直接传入内容即可 其中的OBJ…对象是存在指针的
不定参数有两个规定:
第一,方法的参数列表中最多只有一个不定长度的参数;
第二,就是不定长度的数组的位置必须是最后一个参数。不然不能通过编译。