Android面试题(window、进程、线程篇)
Android面试题(四大组件篇)
window、进程、线程篇
Android面试题(数据存储、view篇)
Window
Q:Activity、View、Window三者之间的关系?
https://blog.csdn.net/qq_21399461/article/details/79836806
https://blog.csdn.net/A448955639/article/details/77430263
Activity通过Window来实现视图元素的展示,window可以理解为一个容器,盛放着一个个的view,用来执行具体的展示工作。
Q:Window有哪几种类型?
https://blog.csdn.net/leif_/article/details/52787908
frameWork定义了三种窗口类型,三种类型定义在WindowManager。
1.应用窗口,对应于一个Activity。加载Activity由AmS完成,创建一个应用窗口只能在Activity内部完成。
2.子窗口,必须依附于任何类型的父窗口。
3.系统窗口,不需要对应任何Activity,应用程序不能创建系统窗口。
WindowManager为这个三类进行了细化,把每一种类型都有int常量标识,WmS进行窗口叠加的时候会按照该int常量的大小分配不同层,int值越大层位置越靠上面。
这里写图片描述
这里写图片描述
这里写图片描述
Q:Activity创建和Dialog创建过程的异同?
https://www.jianshu.com/p/c1d7ebc57fb0
1,Activity就是个容器,跟form差不多,不过form上就可以直接放控件了,Activity还需要贴个View
2,dialog也是个Activity,是不可能加到别的Activity上去的,不过两个Activity是可以共用一个View的
Q:AlertDialog和popupWindow区别
AlertDialog builder:用来提示用户一些信息,用起来也比较简单,设置标题类容 和按钮即可,如果是加载的自定义的view ,调用 dialog.setView(layout);加载布局即可(其他的设置标题 类容 这些就不需要了)
popupWindow:就是一个悬浮在Activity之上的窗口,可以用展示任意布局文件
区别:AlertDialog是非阻塞式对话框:AlertDialog弹出时,后台还可以做事情;而PopupWindow是阻塞式对话框:PopupWindow弹出时,程序会等待,在PopupWindow退出前,程序一直等待,只有当我们调用了dismiss方法的后,PopupWindow退出,程序才会向下执行。并且AlertDialog的位置固定,而PopupWindow的位置可以随意。
IPC
Q:Android中进程和线程的关系?区别?
进程是系统进行资源分配和调度的一个独立单位。可以申请和拥有系统资源,是一个动态的概念,是一个活动的实体,是一个“执行中的程序”。即:进程是程序执行的最小单位
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程比进程更小,基本上不拥有系统资源,故对它的调度所用资源小,能更高效的提高系统内多个程序间并发执行的。 即:线程是CPU调度的最小单位。
Q:为何需要进行IPC?多进程通信可能会出现什么问题?
为了实现安全的数据交互,同步数据等。
一般来说,使用多进程会造成如下几方面的问题:
静态成员和单例模式完全失效
线程同步机制完全失效
SharedPreferences的可靠性下降
Application会多次创建
Q:什么是序列化?Serializable接口和Parcelable接口的区别?为何推荐使用后者?
序列化 (Serialization)将对象的状态信息转换为可以存储或传输的形式的过程。
Serializable是JAVA本身自带的序列化接口,用起来简单但开销较大
Parcelable是Android中的序列化方式,使用较麻烦但效率很高
Parcelable主要用作内存序列化上,而要将对象序列化到存储设备上或序列化后要通过网络传输,建议使用Serializable。
Q:Linux现有的所有进程间IPC方式
管道:在创建时分配一个page大小的内存,缓存区大小比较有限;
消息队列:信息复制两次,额外的CPU消耗;不合适频繁或信息量大的通信;
共享内存:无须复制,共享缓冲区直接付附加到进程虚拟地址空间,速度快;但进程间的同步问题操作系统无法实现,必须各进程利用同步工具解决;
套接字:作为更通用的接口,传输效率低,主要用于不通机器或跨网络的通信;
信号量:常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
信号: 不适用于信息交换,更适用于进程中断控制,比如非法内存访问,杀死某个进程等;
Q:Android中为何新增Binder来作为主要的IPC方式?
https://blog.csdn.net/gityuan/article/details/50815110
(1)从性能的角度
数据拷贝次数:Binder数据拷贝只需要一次,而管道、消息队列、Socket都需要2次,但共享内存方式一次内存拷贝都不需要;从性能角度看,Binder性能仅次于共享内存。
socket作为一款通用接口,其传输效率低,开销大,主要用在跨网络的进程间通信和本机上进程间的低速通信。消息队列和管道采用存储-转发方式,即数据先从发送方缓存区拷贝到内核开辟的缓存区中,然后再从内核缓存区拷贝到接收方缓存区,至少有两次拷贝过程。共享内存虽然无需拷贝,但控制复杂,难以使用。
(2)从稳定性的角度
Binder是基于C/S架构的,简单解释下C/S架构,是指客户端(Client)和服务端(Server)组成的架构,Client端有什么需求,直接发送给Server端去完成,架构清晰明朗,Server端与Client端相对独立,稳定性较好;
而共享内存实现方式复杂,没有客户与服务端之别, 需要充分考虑到访问临界资源的并发同步问题,否则可能会出现死锁等问题;从这稳定性角度看,Binder架构优越于共享内存。
仅仅从以上两点,各有优劣,还不足以支撑google去采用binder的IPC机制,那么更重要的原因是:
(3)从安全的角度
传统Linux IPC的接收方无法获得对方进程可靠的UID/PID,从而无法鉴别对方身份;而Android作为一个开放的开源体系,拥有非常多的开发平台,App来源甚广,因此手机的安全显得额外重要;对于普通用户,绝不希望从App商店下载偷窥隐射数据、后台造成手机耗电等等问题,传统Linux IPC无任何保护措施,完全由上层协议来确保。
Android为每个安装好的应用程序分配了自己的UID,故进程的UID是鉴别进程身份的重要标志,前面提到C/S架构,Android系统中对外只暴露Client端,Client端将任务发送给Server端,Server端会根据权限控制策略,判断UID/PID是否满足访问权限,目前权限控制很多时候是通过弹出权限询问对话框,让用户选择是否运行。Android 6.0,也称为Android M,在6.0之前的系统是在App第一次安装时,会将整个App所涉及的所有权限一次询问,只要留意看会发现很多App根本用不上通信录和短信,但在这一次性权限权限时会包含进去,让用户拒绝不得,因为拒绝后App无法正常使用,而一旦授权后,应用便可以胡作非为。
针对这个问题,google在Android M做了调整,不再是安装时一并询问所有权限,而是在App运行过程中,需要哪个权限再弹框询问用户是否给相应的权限,对权限做了更细地控制,让用户有了更多的可控性,但同时也带来了另一个用户诟病的地方,那也就是权限询问的弹框的次数大幅度增多。对于Android M平台上,有些App开发者可能会写出让手机异常频繁弹框的App,企图直到用户授权为止,这对用户来说是不能忍的,用户最后吐槽的可不光是App,还有Android系统以及手机厂商,有些用户可能就跳果粉了,这还需要广大Android开发者以及手机厂商共同努力,共同打造安全与体验俱佳的Android手机。
Android中权限控制策略有SELinux等多方面手段,下面列举从Binder的一个角度的权限控制:
Android源码的Binder权限是如何控制? -Gityuan的回答
传统IPC只能由用户在数据包里填入UID/PID;另外,可靠的身份标记只有由IPC机制本身在内核中添加。其次传统IPC访问接入点是开放的,无法建立私有通道。从安全角度,Binder的安全性更高。
说到这,可能有人要反驳,Android就算用了Binder架构,而现如今Android手机的各种流氓软件,不就是干着这种偷窥隐射,后台偷偷跑流量的事吗?没错,确实存在,但这不能说Binder的安全性不好,因为Android系统仍然是掌握主控权,可以控制这类App的流氓行为,只是对于该采用何种策略来控制,在这方面android的确存在很多有待进步的空间,这也是google以及各大手机厂商一直努力改善的地方之一。在Android 6.0,google对于app的权限问题作为较多的努力,大大收紧的应用权限;另外,在Google举办的Android Bootcamp 2016大会中,google也表示在Android 7.0 (也叫Android N)的权限隐私方面会进一步加强加固,比如SELinux,Memory safe language(还在research中)等等,在今年的5月18日至5月20日,google将推出Android N。
话题扯远了,继续说Binder。
(4)从语言层面的角度
大家多知道Linux是基于C语言(面向过程的语言),而Android是基于Java语言(面向对象的语句),而对于Binder恰恰也符合面向对象的思想,将进程间通信转化为通过对某个Binder对象的引用调用该对象的方法,而其独特之处在于Binder对象是一个可以跨进程引用的对象,它的实体位于一个进程中,而它的引用却遍布于系统的各个进程之中。可以从一个进程传给其它进程,让大家都能访问同一Server,就像将一个对象或引用赋值给另一个引用一样。Binder模糊了进程边界,淡化了进程间通信过程,整个系统仿佛运行于同一个面向对象的程序之中。从语言层面,Binder更适合基于面向对象语言的Android系统,对于Linux系统可能会有点“水土不服”。
Q:使用Binder进行数据传输的具体过程?
https://blog.csdn.net/freshui/article/details/55051268
Q:Binder框架中ServiceManager的作用?
https://blog.csdn.net/hu3167343/article/details/38441119
Android系统进程间通信机制Binder的总体架构由Client、Server、ServiceManager和驱动程序Binder四个组件构成。
ServiceManager是整个Binder IPC通信过程中的守护进程,本身也是一个Binder服务,但并没有采用libbinder中的多线程模型来与Binder驱动通信,而是自行编写了binder.c直接和Binder驱动来通信,并且只有一个循环binder_loop来进行读取和处理事务,这样的好处是简单而高效。
ServiceManager本身工作相对并不复杂,主要就两个工作:查询和注册服务。
Q:Android中有哪些基于Binder的IPC方式?简单对比下?
名称 优点 缺点 适用场景
Bundle 简单易用 只能传输 Bundle 支持的数据类型 四大组件的进程间
文件共享 简单易用 不适合高并发场景,并且无法做到进程间的即时通信 无并发访问情形,交换简单的数据实时性不高的场景
AIDL 功能强大,支持一对多并发,支持实时通信
使用稍复杂,需要处理好线程同步
一对多通信且有 RPC 需求
Messenger 功能一般,支持一对多串行通信,支持实时通信 不能很好处理高并发,只能传输 Bundle 数据 低并发一对多
ContentProvider 在数据源访问方面功能强大,支持一对多并发数据共享,可通过 Call 方法扩展其它操作 可以理解为受约束的 AIDL 一对多的进程间的数据共享
Socket 功能强大,可以通过网络传输字节流,支持一对多并发实时通信 实现细节稍微有点烦琐,不支持直接的 RPC 网络数据交换
Q:是否了解AIDL?原理是什么?如何优化多模块都使用AIDL的情况?
AIDL是Android Interface Define Language 安卓接口语言缩写。实现了服务端和客户端的通信。
服务端:创建一个Service用来监听客户端的链接需求,然后创建一个AIDL文件,将暴露给客户端的接口在这个AIDL文件中声明,最后在Service中实现这个接口
客户端:绑定服务端的Service,将服务端返回的Binder对象转成AIDL接口所属的类型,接着就可以调用AIDL中的方法了。
对于多模块,可用Binder连接池的思想:
每个业务模块创建自己的AIDL接口并实现此接口,但是不同模块之间不能耦合,所有实现单独开来,然后向服务端提供自己的唯一标识和其相对应的Binder对象,对于服务端来说,只需要一个Service就可以了,服务端提供一个queryBinder接口,这个接口根据业务模块特征返回相应打的Binder对象给客户端,不同业务模块拿到所需的Binder对象后就可以进行远程方法的调用了。由此可见,Binder连接池的作用就是将每个业务模块的Binder请求统一转发到远程Service中去执行,从而避免了重复创建Service的过程
Handler
Q:谈谈消息机制Hander?作用?有哪些要素?流程是怎样的?
Message:主要功能是进行消息的封装,同时可以指定消息的操作形式;
Looper:消息循环泵,用来为一个线程跑一个消息循环。每一个线程最多只可以拥有一个。
MessageQueue:就是一个消息队列,存放消息的地方。每一个线程最多只可以拥有一个。
Handler:消息的处理者,handler 负责将需要传递的信息封装成Message,发送给Looper,继而由Looper将Message放入MessageQueue中。当Looper对象看到MessageQueue中含有Message,就将其广播出去。该handler 对象收到该消息后,调用相应的handler 对象的handleMessage()方法对其进行处理。
Q:为什么系统不建议在子线程访问UI?
可能在非UI线程中刷新界面的时候,UI线程(或者其他非UI线程)也在刷新界面,这样就导致多个界面刷新的操作不能同步,导致线程不安全。
Q:一个Thread可以有几个Looper?几个Handler?
①一个线程中只能有一个Looper,只能有一个MessageQueue,可以有多个Handler,多个Messge;
②一个Looper只能维护唯一一个MessageQueue,可以接受多个Handler发来的消息;
③一个Message只能属于唯一一个Handler;
④同一个Handler只能处理自己发送给Looper的那些Message;
Q:如何将一个Thread线程变成Looper线程?Looper线程有哪些特点?
新建一个Looper,添加looper.prepare和looper.loop即可
特点:可以创建handler,可以拥有自己的消息队列
Q:可以在子线程直接new一个Handler吗?那该怎么做?
不可以,会报错线程中不存在Looper,新建一个Looper,添加looper.prepare和looper.loop即可
Q:Message可以如何创建?哪种效果更好,为什么?
public class MainActivity extends Activity {
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
Toast.makeText(MainActivity.this, "hanlder", Toast.LENGTH_SHORT).show();
super.handleMessage(msg);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
// Message msg = new Message(); //直接初始化一个Message对象,很普通的方法
// Message msg = Message.obtain();
Message msg = handler.obtainMessage();
//后面两个是从整个Messge池中返回一个新的Message实例,通过obtainMessage能避免重复Message创建对象。
//一般在用到线程池的时候就会用到这2种。
msg.arg = 1;
handler1.sendMessage(msg);
}
}).start();
}
}
Q:ThreadLocal有什么作用?
ThreadLocal采用了类似于哈希表的形式轻松实现Looper在线程中的存取,可以在多个线程中互不干扰地存储和修改数据。
Q:主线程中Looper的轮询死循环为何没有阻塞主线程?
卡死主线程的操作是在回调方法onCreate/onStart/onResume等操作时间过长,会导致掉帧,甚至发生ANR,looper.loop本身不会导致应用卡死。
Q:使用Hanlder的postDealy()后消息队列会发生什么变化?
https://blog.csdn.net/qingtiantianqing/article/details/72783952
线程
Q:Android中还了解哪些方便线程切换的类?
AsyncTask:底层采用了线程池
HandlerThread:底层使用了线程
IntentService:底层使用了线程
Q:AsyncTask相比Handler有什么优点?不足呢?
AsyncTask是一种轻量级的异步任务类,它可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并在主线程中更新UI。从实现上来说,AsyncTask封装了Thread和Handler。但是AsyncTask并不适合进行特别耗时的后台任务,对于特别耗时的任务来说,建议使用线程池
Q:使用AsyncTask需要注意什么?
https://blog.csdn.net/qq_30379689/article/details/53203556
AsyncTask的类必须在主线程中加载,这就意味着第一次访问AsyncTask必须发生在主线程中
AsyncTask的对象必须在主线程中创建
execute方法必须在UI线程中调用
不能直接在程序中调用着四个方法:
onPreExecute():异步任务开启之前回调,在主线程中执行
doInBackground():执行异步任务,在线程池中执行
onProgressUpdate():当doInBackground中调用publishProgress时回调,在主线程中执行
onPostExecute():在异步任务执行之后回调,在主线程中执行
onCancelled():在异步任务被取消时回调
一个AsyncTask对象只能执行一次,即只能调用一次excute方法,否则会报运行时异常。
Q:AsyncTask中使用的线程池大小?
https://www.cnblogs.com/Doing-what-I-love/p/5532984.html
1.corePoolSize=CPU核心数+1;
2.maximumPoolSize=2倍的CPU核心数+1;
3.核心线程无超时机制,非核心线程在闲置时间的超时时间为1s;
4.任务队列的容量为128。
处理任务的优先级为:
核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务(一般为抛出java.util.concurrent.RejectedExecutionException异常)。
Q:HandlerThread有什么特点?
https://www.jianshu.com/p/e9b2c0831b0d
https://blog.csdn.net/lsmfeixiang/article/details/42213119
HandlerThread本质上就是一个普通Thread,只不过内部建立了Looper。
HandlerThread将loop转到子线程中处理,说白了就是将分担MainLooper的工作量,降低了主线程的压力,使主界面更流畅。
开启一个线程起到多个线程的作用。处理任务是串行执行,按消息发送顺序进行处理。
相比多次使用new Thread(){…}.start()这样的方式节省系统资源。
但是由于每一个任务都将以队列的方式逐个被执行到,一旦队列中有某个任务执行时间过长,那么就会导致后续的任务都会被延迟处理。
HandlerThread拥有自己的消息队列,它不会干扰或阻塞UI线程。
通过设置优先级就可以同步工作顺序的执行,而又不影响UI的初始化;
Q:快速实现子线程使用Handler
https://www.cnblogs.com/lang-yu/p/6228832.html
使用HandlerThread即可,如果单独在子线程中创建Looper,有可能程序执行到Handler时,Looper还没初始化完成,造成程序崩溃。所以最好用HandlerThread,因为其内部确保了Looper的初始化完成。
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
Q:IntentService的特点?
IntentService是Service的子类,比普通的Service增加了额外的功能。优先级比普通的线程高。
先看Service本身存在两个问题:Service不会专门启动一条单独的进程,Service与他所在应用位于同一个进程中。 Service也不是专门一条新进程,因此不应该在Service中直接处理耗时的任务。
特点: IntentService会创建独立的worker线程来处理所有的Intent请求; 会创建独立的worker线程来处理onHandleIntent()方法实现的代码,无需处理多线程的问题;其内部也是封装了Handler。 所有请求处理完成后,IntentService会自动停止,无需调用stopSelf()方法停止Service; 为Service的onBind()提供默认实现,返回null; 为Service的onStartCommand提供默认实现,将请求Intent添加到队列中;
Q:为何不用bindService方式创建IntentService?
IntentService本身设计就不支持bind操作。查看IntentService源码,其中的onBind()函数被实现,而且返回null。这从侧面就证明了以上结论。再者,IntentService本身就是异步的,本身就不能确定是否在activity销毁后还是否执行,如果用bind的话,activity销毁的时候,IntentService还在执行任务的话就很矛盾了。
Q:线程池的好处、原理、类型?
https://blog.csdn.net/mine_song/article/details/70948223
管理重复线程,避免创建大量的线程增加开销。
除了降低开销以外,线程池也可以提高响应速度。
Q:ThreadPoolExecutor的工作策略?
(1)如果线程池中的线程数量未达到核心线程的数量,那么会直接启动一个核心线程来执行任务。
(2)如果线程池中的线程数量已经达到或超过了核心线程的数量,那么任务会进入任务队列中排队等待。
(3)如果在步骤(2)中无法将任务插入到任务队列,这往往是因为任务队列已满,这时候如果线程数量未达到线程池规定的最大值,那么会立即启动一个非核心线程来执行任务。
(4)如果步骤(3)中线程的数量已经达到了线程池规定的最大值,那么就拒绝执行此任务。
Q:什么是ANR?什么情况会出现ANR?如何避免?在不看代码的情况下如何快速定位出现ANR问题所在?
应用程序无响应,即应用程序5秒内没有反应就会提示应用程序无响应,应避免在主线程中进行耗时操作。
性能优化
Q:项目中如何做性能优化的?
https://blog.csdn.net/gs12software/article/details/51173392
Q:了解哪些性能优化的工具?
https://www.jianshu.com/p/31485a3cf5ca
Q:布局上如何优化?列表呢?
布局采用include,merge,viewstub
Q:内存泄漏是什么?为什么会发生?常见哪些内存泄漏的例子?都是怎么解决的?
多见于无限循环的动画和handler中没有及时清空message队列,在Activity的destroy()方法中关闭动画或清空队列即可
Q:内存泄漏和内存溢出的区别?
https://blog.csdn.net/ruiruihahaha/article/details/70270574
1、内存泄漏memory leak :是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄漏似乎不会有大的影响,但内存泄漏堆积后的后果就是内存溢出。
2、内存溢出 out of memory :指程序申请内存时,没有足够的内存供申请者使用,或者说,给了你一块存储int类型数据的存储空间,但是你却存储long类型的数据,那么结果就是内存不够用,此时就会报错OOM,即所谓的内存溢出。
3、二者的关系
内存泄漏的堆积最终会导致内存溢出
内存溢出就是你要的内存空间超过了系统实际分配给你的空间,此时系统相当于没法满足你的需求,就会报内存溢出的错误。
内存泄漏是指你向系统申请分配内存进行使用(new),可是使用完了以后却不归还(delete),结果你申请到的那块内存你自己也不能再访问(也许你把它的地址给弄丢了),而系统也不能再次将它分配给需要的程序。就相当于你租了个带钥匙的柜子,你存完东西之后把柜子锁上之后,把钥匙丢了或者没有将钥匙还回去,那么结果就是这个柜子将无法供给任何人使用,也无法被垃圾回收器回收,因为找不到他的任何信息。
内存溢出:一个盘子用尽各种方法只能装4个果子,你装了5个,结果掉倒地上不能吃了。这就是溢出。比方说栈,栈满时再做进栈必定产生空间溢出,叫上溢,栈空时再做退栈也产生空间溢出,称为下溢。就是分配的内存不足以放下数据项序列,称为内存溢出。说白了就是我承受不了那么多,那我就报错,
---------------------
作者:华-山
来源:CSDN
原文:https://blog.csdn.net/mountain_hua/article/details/82709919
版权声明:本文为博主原创文章,转载请附上博文链接!

浙公网安备 33010602011771号