Android面试题(window、进程、线程篇)

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方式?简单对比下?

 

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个,结果掉倒地上不能吃了。这就是溢出。比方说栈,栈满时再做进栈必定产生空间溢出,叫上溢,栈空时再做退栈也产生空间溢出,称为下溢。就是分配的内存不足以放下数据项序列,称为内存溢出。说白了就是我承受不了那么多,那我就报错

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值