生命周期
每日一题:横竖屏切换时候Activity的生命周期
不设置Activity的android:configChanges时,切屏会重新回掉各个生命周期,切横屏时会执行一次,切竖屏时会执行两次
设置Activity的android:configChanges=”orientation”时,切屏还是会调用各个生命周期,切换横竖屏只会执行一次
设置Activity的android:configChanges=”orientation |keyboardHidden”时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法
每日一题:三种情况下Service的生命周期
1、startService / stopService
生命周期:onCreate --> onStartCommand --> onDestory
如果一个Service被某个Activity调用Context.startService 方法启动,那么不管是否有Activity使用bindService绑定或unbindService解除绑定到该Service,该Service都在后台运行,直到被调用stopService,或自身的stopSelf方法。
当然如果系统资源不足,Android系统也可能结束服务,还有一种方法可以关闭服务,在设置中,通过应用 --> 找到自己应用 --> 停止。
注意:
第一次startService会触发onCreate和onStartCommand,以后在服务运行过程中,每次startService都只会触发onStartCommand;不论startService多少次,stopService一次就会停止服务
2、bindService / unbindService
生命周期:onCreate --> onBind --> onUnbind --> onDestory
如果一个Service在某个Activity中被调用bindService方法启动,不论bindService被调用几次,Service的onCreate方法只会执行一次,同时onStartCommand方法始终不会调用。
当建立连接后,Service会一直运行,除非调用unbindService来解除绑定、断开连接或调用该Service的Context不存在了(如Activity被finish --- 即通过bindService启动的Service的生命周期依附于启动它的Context),系统会在这时候自动停止该Service。
注意:
第一次bindService会触发onCreate和inBind,以后在服务运行过程中,每次bindService都不会触发任何回调
一个应用程序有几个Context?为什么Activity、Service、Application都继承自Context,Context的作用是什么呢?
https://zhuanlan.zhihu.com/p/141126600
先说类关系:
Activity继承自ContextThemeWrapper,而Application和Service继承自ContextWrapper,很显然ContextThemeWrapper在ContextWrapper的基础上又做了一些操作使得Activity变得更强大
Context数量=Activity数量+Service数量+1
应用程序全局信息接口,访问资源、类、以及启动活动广播接受intent
3、混合型
当一个Service再被启动(startService)的同时又被绑定(bindService),该Service将会一直在后台运行,不管调用几次,onCreate方法始终只会调用一次,onStartCommand的调用次数与startService调用的次数一致(使用bindService方法不会调用onStartCommand)。同时,调用unBindService将不会停止Service,必须调用stopService或Service自身的stopSelf来停止服务。
UI
[每日一题]SurfaceView和View的最本质的区别
SurfaceView是在一个新起的单独线程中可以重新绘制画面,而view必须在UI的主线程中更新画面。
在UI的主线程中更新画面可能会引发问题,比如你更新的时间过长,那么你的主UI线程就会被你正在画的函数阻塞。
那么将无法响应按键、触屏等消息。
当使用SurfaceView由于是在新的线程中更新画面所以不会阻塞你的UI主线程。
但这也带来了另外一个问题,就是事件同步。
比如你触屏了一下,你需要SurfaceView中thread处理,一般就需要有一个event queue的设计来保存touchevent,这会稍稍复杂一点,因为涉及到线程安全。
协议
用UDP协议通讯时怎样得知目标机是否获得了数据包?
可以在每个数据包中插入一个唯一的ID,比如timestamp或者递增的int。
发送方在发送数据时将此ID和发送时间记录在本地。
接收方在收到数据后将ID再发给发送方作为回应。
发送方如果收到回应,则知道接收方已经收到相应的数据包;如果在指定时间内没有收到回应,则数据包可能丢失,需要重复上面的过程重新发送一次,直到确定对方收到。
tcp三次握手的过程,accept发生在三次握手哪个阶段?
accept发生在三次握手之后。
第一次握手:客户端发送syn包(syn=j)到服务器。
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个ASK包(ask=k)。
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1)。
三次握手完成后,客户端和服务器就建立了tcp连接。这时可以调用accept函数获得此连接。
其他
如何使用ContentProvider进行批量操作?
通常进行数据的批量操作我们都会使用“事务”,但是ContentProvider如何进行批量操作呢?创建ContentProviderOperation对象数组,然后使用ContentResolver.applyBatch()将其分派给内容提供程序。您需将内容提供程序的授权传递给此方法,而不是特定内容URI。这样可使数组中的每个ContentProviderOperation对象都能适用于其他表。调用ContentResolver.applyBatch()会返回结果数组。
同时我们还可以通过ContentObserver对数据进行观察:
1. 创建我们特定的ContentObserver派生类,必须重载onChange()方法去处理回调后的功能实现
2. 利用context.getContentResolover()获得ContentResolove对象,接着调用registerContentObserver()方法去注册内容观察者,为指定的Uri注册一个ContentObserver派生类实例,当给定的Uri发生改变时,回调该实例对象去处理。
3. 由于ContentObserver的生命周期不同步于Activity和Service等,因此,在不需要时,需要手动的调用unregisterContentObserver()去取消注册。
常用的网络请求框架有哪些?
● Retrofit
● OkHttp
● NoHttp
● Volley
● HttpURLConnection(省电省流量)
● HttpClient(6.0后删除)
● AsyncHttpclient
请求头Map mHeaders,请求参数Map mBodyParams,URLEncoder.encode()
● NetworkExecutor 网络请求线程
● HttpStack Http执行器
● ResponseDelivery Response分发
每日一题:简单的说一下布局相关的 、 控件作用及实现原理
简单的说一下布局相关的 、 控件作用及实现原理
ViewStub本身是一个视图,会被添加到界面上,之所以看不到是因为其设置了隐藏与不绘制
当调用infalte或者ViewStub.setVisibilty(View.VISIBLE)时(两个都使用infalte逻辑),先从父视图上把当前ViewStub删除,再把加载的android:layout视图添加上
把ViewStub LayoutParams 添加到加载的android:layout视图上,而其根节点的LayoutParams设置无效
ViewStub是指用来占位的视图,通过删除自己并添加android:layout视图达到懒加载效果
每日一题:Framework 工作方式及原理,Activity 是如何生成一个 view 的,机制是什么?
参考答案:
所有的框架都是基于反射 和 配置文件(manifest)的。
普通的情况:
Activity 创建一个 view 是通过 ondraw 画出来的, 画这个 view 之前呢,还会调用 onmeasure 方法来计算显示的大小.
特殊情况:
Surfaceview 是直接操作硬件的, 因为 或者视频播放对帧数有要求,
onDraw 效率太低,不够使,Surfaceview 直接把数据写到显存。
Framework 是Android 系统对Linux kenel,lib库的封装,提供WMS,AMS,,Binder机制,handler-message 机制,提供APP使用
简单来说Framework就是提供app生存的环境
1)Activity 在 attach 方法的时候,汇创间一个photowindow(window子类)
2)onCreate中setContentView,会创建DectorView
3)DectorView 的 addView 方法,会把Layout中的布局加载进来
反射,配置文件
每个Activity里面都有Window.callback 和 keyevent.callback一些回调的接口或者函数,框架把Activity创建出来就会回调这些方法和Activity的声明周期方法
Activity 创建一个 view 是通过 ondraw 画出来的, 画这个 view 之前呢,还会调用 onmeasure 方法来计算显示的大小.
特殊情况:
Surfaceview 是直接操作硬件的, 因为 或者视频播放对帧数有要求,
onDraw 效率太低,不够使,Surfaceview 直接把数据写到显存。
每日一题:RelativeLayout和LinearLayout在实现效果同等的情况下使用哪个?为什么?
选择LinearLayout,因为RelativeLayout在measure过程需要两次
//RelativeLayout源码
View[] views = mSortedHorizontalChildren;
int count = views.length;
for (int i = 0; i < count; i++) {
/**/
measureChildHorizontal(child, params, myWidth, myHeight);
}
/**/
for (int i = 0; i < count; i++){
/***/
measureChild(child, params, myWidth, myHeight);
}
//LinearLayout
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mOrientation == VERTICAL) {
measureVertical(widthMeasureSpec, heightMeasureSpec);
} else {
measureHorizontal(widthMeasureSpec, heightMeasureSpec);
}
}
从源码我们发现RelativeLayout会对子View做两次measure。这是为什么呢?首先RelativeLayout中子View的排列方式是基于彼此的依赖关系,而这个依赖关系可能和布局中View的顺序并不相同,在确定每个子View的位置的时候,就需要先给所有的子View排序一下。又因为RelativeLayout允许A B两个子View,横向上B依赖于A,纵向上A依赖于B,所以需要横向纵向分别进行一次排序测量。
RelativeLayout另外一个性能问题:
View的measure方法里对绘制过程做了一个优化,如果我们的子View没有要求强制刷新,而父View给子View的传入值也没有变化,也就说子View的位置没有变化,就不会做无谓的measure。但是上面已经说了RelativeLayout要做两次measure,而在做横向测量时,纵向的测量结果尚未完成,只好暂时使用myHeight传入子View系统,假如子View的Height不等于(设置了margin)myHeight的高度,那么measure中优化则不起作用,这一过程将进一步影响RelativeLayout的绘制性能。而LinearLayout则无这方面的担忧,解决这个问题也很好办,如果可以,尽量使用padding代替margin。
结论
RelativeLayout会让子View调用两次onMeasure,LinearLayout再有weight时,也会调用子View两次onMeasure
RelativeLayout的子View如果高度和RelativeLayout不同,则会引发效率问题。当子View很复杂时,这个问题会更加严重。如果可以,尽量使用padding代替margin
在不影响层级深度的情况下,使用LinaerLayout和FrameLayout而不是RelativeLayout
每日一题:Java 中堆和栈有什么不同?
参考答案:
为什么把这个问题归类在多线程和并发面试题里?因为栈是一块和线程紧密相关的内存区域。每个线程都有自己的栈内存,用于存储本地变量,方法参数和栈调用,一个线程中存储的变量对其它线程是不可见的。而堆是所有线程共享的一片公用内存区域。对象都在堆里创建,为了提升效率线程会从堆中弄一个缓存到自己的栈,如果多个线程使用该变量就可能引发问题,这时 volatile 变量就可以发挥作用了,它要求线程从主存中读取变量的值
每日一题:数据库的四大特征,数据库的隔离级别?
参考答案:
事务(Transaction)是并发控制的基本单位。所谓的事务,它是一个操作序列, 这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位。例如,银 行转账工作:从一个账号扣款并使另一个账号增款,这两个操作要么都执行,要 么都不执行。所以,应该把它们看成一个事务。事务是数据库维护数据一致性的 单位,在每个事务结束时,都能保持数据一致性。
事务具有以下 4 个基本特征:数据库的四大特征:
(1)原子性(Atomicity)
原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚。
(2)一致性(Consistency)
一个事务执行之前和执行之后都必须处于一致性状态。
(3)隔离性(Isolation)
隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个 用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
(4)持久性(Durability)
持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性 的。
数据库的隔离级别:
1)Serializable(串行化):可避免脏读、不可重复读、幻读的发生。
2)Repeatable read (可重复读):可避免脏读、不可重复读的发生。
3)Read committed (读已提交):可避免脏读的发生。
4)Read uncommitted (读未提交):最低级别,任何情况都无法保证。
`每日一题:多进程场景遇见过么?
参考答案:
在新的进程中,启动前台 Service,播放音乐。
一个成熟的应用一定是多 模块化的。首先多进程开发能为应用解决了 OOM 问题,因为 Android 对内存的 限制是针对于进程的,所以,当我们需要加载大图之类的操作,可以在新的进程 中去执行,避免主进程 OOM。而且假如图片浏览进程打开了一个过大的图片, java heap 申请内存失败,该进程崩溃并不影响我主进程的使用。
每日一题:服务启动一般有几种,服务和 activty 之间怎么通信,服务和服务之间怎么通信
参考答案:
1、startService:onCreate()--->onStartCommand() ---> onDestory() 如果服务已经开启,不会重复的执行 onCreate(), 而是会调用 onStartCommand()。一旦服务开启跟调用者(开启者)就没有任何关系了。开启 者退出了,开启者挂了,服务还在后台长期的运行。开启者不能调用服务里面 的方法。
2、bindService:onCreate() --->onBind()--->onunbind()--->onDestory() bind 的方式开启服务,绑定服务,调用者挂了,服务也会跟着挂掉。绑定者可 以调用服务里面的方法。
3、通信:
通过 Binder 对象。
通过 broadcast(广播)。
每日一题:Handler、Thread 和 HandlerThread 的差别
参考答案:
Handler:在 android 中负责发送和处理消息,通过它可以实现其他支线线程 与主线程之间的消息通讯。
Thread:Java 进程中执行运算的最小单位,亦即执行处理机调度的基本单位。某一进程中一路单独运行的程序。
HandlerThread:一个继承自 Thread 的类 HandlerThread,Android 中没有 对 Java 中的 Thread 进行任何封装,而是提供了一个继承自 Thread 的类 HandlerThread 类,这个类对 Java 的 Thread 做了很多便利的封装。HandlerThread 继承于 Thread,所以它本质就是个 Thread。与普通 Thread 的差 别就在于,它在内部直接实现了 Looper 的实现,这是 Handler 消息机制必不可 少的。有了自己的 looper,可以让我们在自己的线程中分发和处理消息。如果 不用 HandlerThread 的话,需要手动去调用 Looper.prepare()和 Looper.loop() 这些方法。
每日一题:Service生命周期
参考答案:
startService() --> onCreate() --> onStartCommand() --> Service running --> onDestory()
bindService() --> onCreate() --> onBind() --> Service running --> onUnbind() --> onDestory()
onCreate():
系统在Service第一次创建时执行此方法,来执行只运行一次的初始化工作,如果service已经运行,这个方法不会调用。
onStartCommand():
每次客户端调用startService()方法启动该Service都会回调该方法(多次调用),一旦这个方法执行,service就启动并且在后台长期运行,通过调用stopSelf()或stopService()来停止服务。
onBind():
当组件调用bindService()想要绑定到service时,系统调用此方法(一次调用),一旦绑定后,下次在调用bindService()不会回调该方法。在你的实现中,你必须提供一个返回一个IBinder来使客户端能够使用它与service通讯,你必须总是实现这个方法,但是如果你不允许绑定,那么你应返回null
onUnbind():
当前组件调用unbindService(),想要解除与service的绑定时系统调用此方法(一次调用,一旦解除绑定后,下次再调用unbindService()会抛异常)
onDestory():
系统在service不在被使用并且要销毁的时候调用此方法(一次调用)。service应在此方法中释放资源,比如线程,已注册的监听器、接收器等等。
每日一题:你知道Android消息机制模型吗?
参考答案:
消息机制主要包含:MessageQueue、Handler、Looper和Message这四大部分。
Message:需要传递的消息,可以传递数据
MessageQueue:消息队列,但是它的内部实现并不是用的队列,实际上是通过一个单链表的数据结构来维护消息列表,因为单链表在插入和删除上比较有优势。主要功能是向消息池传递消息(MessageQueue.enqueueMessage)和取走消息池的消息(MessageQueue.next)
Handler:消息辅助类,主要功能是向消息池发送各种消息事件(Handler.sendMessage)和处理相应消息事件(Handler.handleMessage)
Looper:不断循环执行(Looper.loop),从MessageQueue中读取消息,按分发机制将消息分发给目标处理者。
每日一题:哪些情况下的对象会被垃圾回收机制处理掉?
参考答案:
所有实例都没有活动线程访问。
没有被其他任何实例访问的循环引用实例。
Java 中有不同的引用类型。判断实例是否符合垃圾收集的条件都依赖于它的引用类型。
要判断怎样的对象是没用的对象。这里有 2 种方法:
采用标记计数的方法:给内存中的对象给打上标记,对象被引用一次,计数就加 1,引用被释放了,计数就减一,当这个计数为 0 的时候,这个对象就可以被回收了。当然,这也就引发了一个问题:循环引用的对象是无法被识别出来并且被回收的。所以就有了第二种方法:
采用根搜索算法:从一个根出发,搜索所有的可达对象,这样剩下的那些对象就是需要被回收的
Java
Java 中 static 关键字是什么意思?
static是Java里的非访问修饰符,它可以用来创建类方法和类变量。
当修饰一个变量的时候,此变量就成了独立于对象的静态变量,无论一个类实例化了多少个对象,这个类只有一份这个静态变量的拷贝,所以static修饰的变量,即静态变量,也被叫做类变量。一个局部变量不能被声明为static变量。
当修饰一个方法的时候,此方法就成了独立于对象的静态方法,静态方法不能使用类的非静态变量,因为静态方法和静态变量先于非静态的其他成员初始化,静态方法先出来,然后才是非静态的,所以明白这个顺序很重要。静态方法从参数列表得到数据,然后计算这些数据。
有些人认为static方法不是面向对象的,因为它们确实具有全局函数的语义;使用static方法时,由于不存在this,所以不是通过「向对象发送消息」的方式来完成的。当代码中出现很多static方法时,你应该反思自己的设计的,然而,static确有其用。
总结一下,当static:
修饰方法时,此方法能够不用在初始化对象的前提下直接调用,即,可以直接通过类名.static方法()这样来访问。
修饰代码块时,这部分代码块只会被执行一次。
修饰变量时,这个变量只在内存中有一个副本。
这个 结果如何解释
Integer a = 100,b=100;
Integer c = 1000,d= 1000;
System.out.println(a==b);
System.out.println(c==d);
输出: true,false
原因,Intenger 赋值的时候用的是Integer.valueOf(x)
在int -128·127 有个高速的缓存,如果在此期间直接给缓存