Dalvik与ART区别:
Dalivk是在运行时编译,用它的时候再进行组装。在Android5.X版本开始,ART模式已经取代了Dalvik模式,ART是一种安装时编译的模式,在安装时候编译完成,在用的时候直接拿来用就可以了。
Android View的测量方式:
在系统绘制View前,需要对View进行测量,测量的过程在onMeasure()方法中进行。View的测量模式有三种:EXACTLY,AT_MOST,UNSPECIFIED。
View类的默认onMeasure()方法只支持精准模式测量。如果想让自定义的view支持wrap_content属性,必须在onMeasure()方法中调用setMeasuredDimension()来制定View的宽和高,如果使用match_parent或者一个具体的dp的值,直接调用super.onMeasure()即可。
getWidth()和getMeasuredWidth()方法的区别是什么?
- getWidth()
必须执行完onMeasure()方法才会有值,可能会发生改变。如果在onLayout中没有对子View实际显示的宽高进行修改,那么getWidth()和getMeasuredWidth()的数值是一样的。 - getMeasuredWidth()
执行完setMeasuredDimension()方法就有值了。
对View的测量模板如下:
public int measureWidth(int measureSpec){
int result = 0; //默认大小
//第一步 提取具体的测量模式和大小
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpce);
//第二步 通过不同的测量模式得到不到的测量值
if(specMode == MeasureSpec.EXACTLY){
result = specSize;
}else{
result = 200;
if(specMode == MeasureSpec.AT_MOST){
result = Math.min(result,specSize);//选取最小值
}
}
return result;
}
Activity的启动过程
简单理解,启动Activity的请求会都交给Instrumentation处理,然后它会通过Binder向AMS请求,AMS内部维护这一个ActivityStack并负责栈内Activity的状态同步,AMS通过ActivityThread去同步Activity的状态从而完成生命周期的调用。
在新的Activity启动之前,栈顶的Activity先执行onPause方法,然后新的Activity才能启动。最终会调用ActivityThread中的ApplicationThread的scheduleLaunchActivity方法去完成新的Activity的onCreate等生命周期。
在scheduleLaunchActivity方法中会调用performLaunchActivity()和handleResumeActivity()方法启动新的Activity。
Activity的启动模式
Standard/singleTop/singleTask/singleInstance;
Standard:默认模式,每次都会创建一个新的实例。
SingleTop:栈顶复用,如果新的Activity在栈顶,则调用onNewIntent()方法,不会被重建。
SingleTask:栈内复用,如果新的Activity在栈内存在,那么该Activity之上的Activity出栈,调用onNewIntent()方法。默认具有clearTop效果。
SingleInstance:单例模式,Activity单独存在一个栈内。
默认情况下,所有Activity所需任务栈的名字为应用的名字,通过TaskAffinity属性可以指定任务栈。TaskAffinity属性主要和SingleTask启动模式或者allowTaskReparenting属性配合使用,其他情况无意义。
也可以通过设置标志位来为Activity指定启动模式。
通过设置标志位的方式优先级要高一些,两者的限定范围不同,通过AndroidMenifest配置无法为Activity设定FLAG_ACTIVITY_CLEAR_TOP标识;而通过标志位无法为Activity指定singleInstance模式。
FLAG_ACTIVITY_CLEAR_TOP一般要和FLAG_ACTIVITY_NEW_TASK配合使用,在这种情况下被启动的Activity如果已经存在,则调用onNewIntent。如果被启动的Activity是默认模式,则连它之上的Activity进行出栈,通过创建一个新的实例放入栈中。
IntentFilter匹配原则
- action匹配原则
Intent中action存在且必须和过滤规则中的其中一个action相同,区分大小写。 - category匹配原则
Intent中可以没有category,但是如果有category,不管有几个,都需要和过滤规则中相同。 - data匹配原则
由mimeType和URI组成。URI模式值为content和file,如果没有指定URI,但是Intent中的URI部分的schema必须为content或者file才能匹配。
当我们通过隐式方式去启动一个Activity时候,可以做一个判断,看是否存在能够匹配我们的过滤规则的Activity。通过PackageManger的resolveActivity()方法或者Intent的resolveActivity()方法。还可以通过PackageManger的queryIntentActivities方法来进行匹配。
进程间通信方式
Bundle、文件共享、AIDL、Messenger、ContentProvider、Socket。
通过为组件指定android:process属性可以开启多线程模式。
进程名以”:”开头的进程属于当前应用的私有进程,否则属于全局进程,可以通过ShareUID方式实现其他应用的组件和它跑在一个进程中。
使用多进程会造成的问题:
- 静态成员和单例模式失效,内存不唯一
- 线程同步机制失效,内存不唯一
- SharedPreferences可靠性下降,不支持多进程同时执行写操作
- Application多次创建 ,创建新进程需要分配独立的虚拟机
Binder简单认识
Binder是实现了IBinder接口的类。
从进程间通信角度来讲可以理解为一种跨进程通信方式,还可以理解为一种虚拟的物理设备,设备驱动是/dev/binder。
从FrameWork角度来讲,Binder是ServiceManager连接各种Manager以及对应的ManagerService的桥梁。
从Android应用层来讲,Binder是客户端和服务端通信的媒介,调用bindService时候,服务端会返回一个包含了服务端业务调用的binder对象,通过该对象,客户端可以获取服务和数据。
在AIDL使用过程中,我们首先创建一个aidl文件,系统为会我们创建相应的类文件,在该类文件中,首先会声明我们自定义的方法以及对应的id号,通过该id号可以在transact过程中区分客户端所请求的到底是哪个方法。接着声明一个内部类Stub,该Stub是一个Binder类,当客户端和服务端都位于同一个进程时,方法调用不会走跨进程的transact过程,否则会走transact过程,这个是有Stub内部代理类Proxy来完成的。
asInterface:用于将服务端的Binder对象转化成客户端所需要的AIDL接口类型的对象,当Client和Service位于同一进程中,方法返回就是服务端的Stub本身,否则返回的是Stub.Proxy对象。
asBinder:返回当前的Binder对象。
onTransact:运行在服务端中的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理。服务端通过code来确定请求的方式,然后从data中取出目标所需的参数,执行完毕后,就向reply中写入返回值。
Binder另外两个方法:linkToDeath和unlinkToDeath。
通过linkToDeath,我们可以给Binder设置一个死亡代理,当Binder死亡后,我们就会收到通知。我们也可以通过调用Binder的方法isBinderAlive来判断Binder是否已经死亡。如何实现代理的呢?
第一步:声明一个DeathRecipient对象。
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient(){
@Override
public void binderDied(){
if(mBookManager == null){
return;
}
mBookManager.asBinder().unlinkToDeath(mDeathRecipient,0);
mBookManager = null;
}
}
第二步:客户端绑定远程服务成功以后,给binder设置死亡代理。
mService = IMessageBoxManager.Stub.asInterface(binder);
binder.linkToDeath(mDeathRecipient,0);//第二个参数默认为0就可以了
Messenger简单认识
Messenger对AIDL做了封装,同时由于它一次只处理一个请求,因此在服务端我们不用考虑线程同步的问题。当有大量并发请求时,用Messenger不合适。使用步骤如下:
第一步:服务端进程
在服务端创建一个Service来处理客户端的请求,同时创建一个Handler并通过它创建一个Messenger对象,然后在Service的onBind中返回这个Messenger对象底层的Binder即可。
第二步:客户端进程
首先,绑定服务端Service,绑定成功后用服务端返回的IBinder对象创建一个Messenger对象。如果要实现服务端回应客户端,那么需要在客户端创建Handler并创建一个新的Messenger,并把这个Messenger对象通过Message的replyTo参数传递给服务端,服务端通过这个replyTo参数就可以回应客户端了。
Messenger中进行数据传递必须将数据放入Message中。
AIDL简单认识
第一步:服务端
创建Service用来监听客户端的连接请求,然后创建一个AIDL文件,将暴露给客户端的接口在这个AIDL中声明,然后在Service中实现这个AIDL接口即可。
第二步:服务端
绑定服务端的Service,将服务端返回的Binder对象转化成AIDL接口所属的类型,然后就可以调用AIDL中的方法了。
注意:在AIDL中用到了自定义的Parcelable对象,那么必须创建一个和它同名的AIDL文件,并在其中声明它为Parcelable类型。
AIDL接口中只支持方法,不支持声明静态变量,这一点区别于传统的接口。
远程服务端Service:
可以通过采用CopyOnWriteArrayList来支持并发读/写,通过它可以进行自动的线程同步。
对象是不能跨进程直接传输的,对象的跨进程传输本质上都是反序列化过程,这就是为什么、AIDL中自定义的对象都必须实现Parcelable接口。
RemoteCallbackList是系统专门提供用来删除夸进程listener的接口。它是一个泛型,支持管理任意的AIDL接口。内部有一个Map接口来保存所有的AIDL回调,Map的key是IBinder类型,value是Callback类型。RemoteCallbackList在客户端进程终止后,还能够自动移除客户端所注册的listener。另外,RemoteCallbakList内部自动实现了线程同步的功能。
当我们要遍历RemoteCallbackList时,必须通过beginBroadcast和finishBroadcast配对使用来完成,它并不是一个List。
避免在客户端UI县城中去访问远程方法,客户端的onServiceConnected和onServiceDisconnected方法都运行在UI线程中,所以不能直接在里面调用服务端的耗时方法。同时服务端的方法本身就运行在Binder线程池中,所以不要在服务端方法中开线程去进行异步任务。
Binder意外死亡后重新连接服务的方法有:
1.给Binder设置DeathRecipient监听,当Binder死亡后,会受到binderDied方法的回调。
2.在onServiceDisconnected方法中重连远程服务。
区别在于:onServiceDisconnected方法是在客户端UI线程中被回调,而binderDied在客户端的Binder线程池中被回调。
AIDL常用的权限验证方法:
1.通过在onBind中进行验证,比如使用permission来验证。
2.在onTransact方法中进行验证,如果验证失败,返回false。
ContentProvider简单介绍
底层实现同样使用Binder。创建自定义ContentProvider,需要实现六个抽象方法,onCreate,query,update,insert,delete,getType。onCreate由系统回调并运行在主线程中,其他五个方法由外界调用并运行在Binder线程池中。
当调用update/insert/delete方法后需要通过ContentResolver的notifyChange方法来通知ContentProvider中的数据已经发生了改变。
要观察一个ContentProvider中的数据改变情况,可以通过ContentResolver的registerContentObserver方法来注册观察者,通过unregisterContentObserver方法来解除观察者。
Socket简单介绍
Socket分为流式套接字和用户数据报套接字,分别对应TCP和UDP协议。
TCP面向连接协议,提供双向通信功能能够,连接的建立需要通过三次握手才能完成,本身提供超时重传机制。
UDP是无连接的,也可实现双向通信功能。
使用Socket通信,首先需要声明访问网络的权限。然后要注意不能在主线程中去访问网络。
Binder连接池
Binder连接池的主要作用是将米格业务模块的Binder请求统一转发到远程的Service去执行,避免重复创建Service的过程。