Android面试知识总结

2020-08-05_zk_chu

1.AIDL
2.handler
3.A Activity 起 B Activity ,及Back后生命周期。
4.ANR
5.内存泄漏
6.cotentprovider 怎么用
7.startservice 和 bindService 生命周期区别,如果bind的A Activity销毁了 服务怎么样
8.java类加载: 静态成员 ,静态代码块,构造方法。非静态成员 方法顺序
9.事件分发后如何中断处理。
10.c++ :和::区别。

问题答案整理:

1.AIDL:

使用:
服务端开发步骤:
1.创建一个aidl文件夹,NEW->FOLDER->AIDL FOLDER
2.创建一个AIDL 文件,两种创建方式:
2.1 创建一个Java接口文件,删掉修饰符,将文件后缀名改为aidl
2.2 NEW->AIDL->AIDL FILE
3.rebuild project,检查是否生成对应的Java文件,app->build->generated->source->aidl->debug->FILE JAVA
4.创建一个Service,在Service中定义内部类,继承自AIDL所生成的Java类中的Stub类,并实现该类中的方法,并把这个内部类的实例作为onBind方法 返回值。
5.在清单文件中注册Service,注意添加exported属性和action

客户端开发步骤:
1.将服务端的AIDL文件夹拷贝至客户端的main文件夹下
2.rebuild project,检查是否生成对应的Java文件
3.客户端的activity里连接远程服务 实现aidl通信
创建ServiceConnection实例,实现未实现的方法1.onServiceConnected()回调中mMyRemoteService =MyRemoteService.Stub.asInterface(IBinder)方法返回服务端引用,客户端就可以通过mMyRemoteService 来与服务端通信,调用服务端方法。

原理总结:
通过对AIDL的分析,会发现都是围绕着Binder有序的展开
AIDL通过Stub类用来接收并处理数据,Proxy代理类用来发送数据,而这两个类也是通过对Binder的处理和调用。
所谓的服务端和客户端,拆开看是不同类的处理。

Stub充当服务端角色,持有Binder实体(本地对象)。
1.获取客户端传过来的数据,根据方法 ID 执行相应操作。
2.将传过来的数据取出来,调用本地写好的对应方法。
3.将需要回传的数据写入 reply 流,传回客户端。
Proxy代理类充当客户端角色,持有Binder引用(句柄)。
1.生成 _data 和 _reply 数据流,并向 _data 中存入客户端的数据。
2.通过 transact() 方法将它们传递给服务端,并请求服务端调用指定方法。
3.接收 _reply 数据流,并从中取出服务端传回来的数据。

深入理解Binder(一),从AIDL谈起
深入理解Binder(一),从AIDL谈起__江南一点雨的博客-CSDN博客
Android AIDL使用步骤
Android AIDL使用步骤_csdn_WZM的博客-CSDN博客_aidl使用步骤
Android:学习AIDL,这一篇文章就够了(上)
Android:学习AIDL,这一篇文章就够了(上)_lypeer的博客-CSDN博客_aidl
AIDL的使用流程以及原理详情
AIDL的使用流程以及原理详情_final__static的博客-CSDN博客_aidl流程

2.hanlder

作用:
简单来说,Handler就是用来传递消息的。
Handler可以当成子线程与主线程的消息传送的纽带。
在安卓开发中,在子线程中无法刷新UI,是因为UI在子线程中刷新的话,是不安全的,就比如多个线程刷新UI,会造成UI更新冲突,这样是不安全的
所以,Handler的作用就来了,子线程可以通过Handler来将UI更新操作切换到主线程中执行。

在子线程中,直接new Handler,则运行时会报错,因为在子线程中通过关键字new创建的Handler对象时,子线程里面的Looper对象是没有被创建的,不存在的,直接使用的话,会报异常:Looper.mQueue对象为空
如果要在子线程中创建Handler对象,那么就需要在创建前添加代码:Looper.prepare();来解决问题:
new Thread(new Runnable() {
    @Override
    public void run() {
       Looper.prepare();//Looper初始化
       //Handler初始化 需要注意, Handler初始化传入Looper对象是子线程中缓存的Looper对象
       mHandler = new Handler(Looper.myLooper()){  
          @Override  
          public void handleMessage(Message msg) {  
              Toast.makeText(getApplicationContext(), "handler msg", Toast.LENGTH_LONG).show();  
          }   
       }; 
       Looper.loop();//死循环
       //注意: Looper.loop()之后的位置代码在Looper退出之前不会执行,(并非永远不执行)
      }
 }).start();


工作流程
Handler的主要工作包含消息的发送与接收过程。消息的发送通过post和send的一系列方法来实现,其实post的一系列方法最终还是通过send来实现的。当Handler发送了消息之后,MessageQueue里就插入了一条消息,然后MessageQueue就会返回这条消息给Looper,Looper接收到消息之后就开始处理了,最终消息由Looper交给Handler处理,即Handler的dispatchMessage方法会被调用,这时Handler就进入了处理消息的阶段,最后经过一系列的判断之后,就会调用handleMessage()方法了。


Handler官方文档
Handler  |  Android Developers
Handler的基本用法和原理简介
Handler的基本用法和原理简介_NewOrin的博客-CSDN博客
Handler的理解、用法以及运行机制原理
Handler的理解、用法以及运行机制原理_ttxs99989的博客-CSDN博客_handler机制的原理

3.A Activity 起 B Activity 、及B Back后回到A ,再按Back后返回桌面的生命周期。

A:onCreate->onStart->onResume->
点击B:
A:onPause ->B:onCreate->B:onStart->B:onResume->A:onStop
点击Back键返回A:
B:onPause->A:onRestart->A:onStart->A:onResume->B:onStop->B:onDestroy
再次点击Back返回桌面:
A:onPause ->A:onStop->A:onDestroy
官网Activity生命周期:
了解 Activity 生命周期  |  Android 开发者  |  Android Developers
Activity的生命周期,BACK键和HOME键生命周期
Activity的生命周期,BACK键和HOME键生命周期_奋斗的IT青年的博客-CSDN博客
android Activity A和B互相跳转 生命周期的变化
android Activity A和B互相跳转 生命周期的变化_许佳佳233的博客-CSDN博客_android a跳转到b

4.ANR

ANR:造成ANR原因主要是系统规定在四大组件中不能做过多的耗时操作。

首先ANR的发生是有条件限制的,分为以下三点:
1.只有主线程才会产生ANR,主线程就是UI线程;
2.必须发生某些输入事件或特定操作,比如按键或触屏等输入事件,在BroadcastReceiver或Service的各个生命周期调用函数;
3.上述事件响应超时,不同的context规定的上限时间不同
    a.主线程对输入事件5秒内没有处理完毕
A)KeyDispatchTimeout
这个Key事件分发超时的时间,Android默认是5秒,主要是定义在ActivityManagerService.java
// How long we wait until we timeout on key dispatching.
static final int KEY_DISPATCHING_TIMEOUT = 5*1000;

    b.主线程在执行BroadcastReceiver的onReceive()函数时10秒内没有处理完毕
B)BroadcastTimeOut
广播的超时时间,分为FG和BG,分别是10秒和60秒。同样是定义在ActivitManagerService.java
// How long we allow a receiver to run before giving up on it.
static final int BROADCAST_FG_TIMEOUT = 10*1000;
static final int BROADCAST_BG_TIMEOUT = 60*1000;

    c.主线程在Service的各个生命周期函数时20秒内没有处理完毕。
C)ServiceTimeOut
Service的超时前台服务”20秒、后台服务200秒,定义在ActiveServices.java
// How long we wait for a service to finish executing.
static final int SERVICE_TIMEOUT = 20*1000;
// How long we wait for a service to finish executing.
static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;

    d.主线程在10秒内没有发布其ContentProvider完毕。
D)ContentProviderTimeOut
provider所在进程发布其ContentProvider的超时时长为10s,超过10s则会系统所杀。定义在ActivityManagerService.java
 // How long we wait for an attached process to publish its content providers before we decide it must be hung.
static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10*1000;

那么导致ANR的根本原因是什么呢?简单的总结有以下两点:
1.主线程执行了耗时操作,比如数据库操作或网络编程
2.其他进程(就是其他程序)占用CPU导致本进程得不到CPU时间片,比如其他进程的频繁读写操作可能会导致这个问题。
细分的话,导致ANR的原因有如下几点:
1.耗时的网络访问
2.大量的数据读写
3.数据库操作
4.硬件操作(比如camera)
5.调用thread的join()方法、sleep()方法、wait()方法或者等待线程锁的时候
6.service binder的数量达到上限
7.system server中发生WatchDog ANR
8.service忙导致超时无响应
9.其他线程持有锁,导致主线程等待超时
10.其它线程终止或崩溃导致主线程一直等待

分析ANR:
一般要先分析Main log
  a)先在Main log里面搜索关键字 “ANR”,确定是否真的有发生ANR。
  b)查看在ANR之前,系统是否发生了JE(Java Exception)或者NE(Native Exception),有时候一些ANR是由JE或者NE引起的。
  c)在发生ANR的地方,查看对应的log信息。
 发生ANR的时间,ANR in 对应的进程 , PID:ANR的进程号 ,Reason:ANR的原因,

CPU usage from :CPU使用信息。 ago: 表示ANR发生之前的一段时间内的CPU使用率  later:表示ANR发生之后CPU使用率
 用"Proces"作为filter tag,可以看到
 发送signal3, 也就是SIGNAL_QUI
 用"dalvikvm"作为filter tag,可以看到
 reacting to signal 对信号做出反应
 Wrote stack traces to '/data/anr/traces.txt' 将堆栈跟踪写入traces文件中

主要是dump相关的信息到traces文件里面 traces.txt文件: adb pull /data/anr/traces.txt   traces.txt

只需要看开头,每次发生ANR, 这个文件都会被清空,写入新的内容. 如果想查看以前发生ANR的信息, 可以去查看DB文件.
 Traces文件里面的相关信息要和main log对应上,才能确保是同一个问题
 ----- pid 进程号 at 2019-05-01 xx:xx:xx -----
Cmd line: 进程名
 pid为进程号,anr时间为2019-05-01 xx:xx:xx,进程名一样。
 anr日志获取_文韬_武略的博客-CSDN博客_android anr日志在哪
避免ANR:

那么如何避免ANR的发生呢或者说ANR的解决办法是什么呢?
1.UI主线程尽量只做跟UI相关的工作,避免在主线程执行耗时操作,所有耗时操作f放入单独的线程处理,然后再在主线程更新UI,尽量用Handler来处理UI线程和非UI线程之间的交互。
2.BroadcastReceiver要执行耗时操作时应启动一个service,将耗时操作交给service来完成。
3.避免在IntentReceiver里启动一个Activity,因为它会创建一个新的画面,并从当前用户正在运行的程序上抢夺焦点。如果你的应用程序在响应Intent广 播时需要向用户展示什么,你应该使用Notification Manager来实现。
ANR参考链接:
Android ANR简介_卩s秋灬的博客-CSDN博客

5.内存泄漏

memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。最终的结果就是导致OOM。
   内存泄漏是指你向系统申请分配内存进行使用(new),可是使用完了以后却不归还(delete),结果你申请到的那块内存你自己也不能再访问(也许你把它的地址给弄丢了),而系统也不能再次将它分配给需要的程序。
memory leak堆积会最终会导致out of memory!

以发生的方式来分类,内存泄漏可以分为4类:
1. 常发性内存泄漏。发生内存泄漏的代码会被多次执行到,每次被执行的时候都会导致一块内存泄漏。
2. 偶发性内存泄漏。发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。
3. 一次性内存泄漏。发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块仅且一块内存发生泄漏。
4. 隐式内存泄漏。程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。
从用户使用程序的角度来看,内存泄漏本身不会产生什么危害,作为一般的用户,根本感觉不到内存泄漏的存在。
真正有危害的是内存泄漏的堆积,这会最终消耗尽系统所有的内存。
内存泄漏原因:
单例引用Activity的context引用,销毁时,activity对象就不会被回收,这时就造成了内存泄漏。
非静态内部类中隐式持有外部类的引用,静态变量。
Handler造成的内存泄漏匿名线程、持有Activity变量的强引用。
其他原因:
1.static关键字(非静态内部类的静态引用)
2.构造adapter没有使用缓存Converview
资源未关闭造成的内存泄漏:
1.数据库的cursor没有关闭
2.调用registerReceiver后未调用unregisterReceiver
3.ContentObserver注册未调用unregister
4.未关闭InputStream/OutputStream
5.Bitmap使用未调用recycle

6.ContentProvider

 ContentProvider作用:
 内容提供者组件通过请求从一个应用程序向其他的应用程序提供数据。这些请求由类 ContentResolver 的方法来处理。内容提供者可以使用不同的方式来存储数据。数据可以被存放在数据库,文件,甚至是网络。
 ContentProvider使用:
首先,你需要继承类 ContentProvider类来创建一个内容提供者类。
其次,你需要定义用于访问内容的你的内容提供者URI地址。
接下来,你需要创建数据库来保存内容。通常,Android 使用 SQLite 数据库,并在框架中重写 onCreate() 方法来使用 SQLiteOpenHelper 的方法创建或者打开提供者的数据库。当你的应用程序被启动,它的每个内容提供者的 onCreate() 方法将在应用程序主线程中被调用。
最后,使用<provider.../>标签在 AndroidManifest.xml 中注册内容提供者。
需要在类 ContentProvider 中重写的一些方法:
onCreate():当提供者被启动时调用。
query():该方法从客户端接受请求。结果是返回指针(Cursor)对象。
insert():该方法向内容提供者插入新的记录。
delete():该方法从内容提供者中删除已存在的记录。
update():该方法更新内容提供者中已存在的记录。
getType():该方法为给定的URI返回元数据类型。

ContentProvider初探
4.4.1 ContentProvider初探 | 菜鸟教程
Android - 内容提供者(Content Provider)
Android 内容提供者(Content Provider) | 菜鸟教程
Android进阶知识树——ContentProvider使用和工作过程详解
Android进阶知识树——ContentProvider使用和工作过程详解 - 掘金
Android:关于ContentProvider的知识都在这里了!
Carson带你学Android:关于ContentProvider的知识都在这里了!_Carson带你学Android的博客-CSDN博客_contentprovider
Android 总结:ContentProvider 的使用
https://blog.csdn.net/u014136472/article/details/49907713

7.startservice 和 bindService 生命周期区别,如果bind的A Activity销毁了 服务怎么样

startService:
作用:启动服务
生命周期:onCreate() → onStartCommand() → onDestory()
bindService:
作用:启动服务
生命周期:onCreate() → onBind() → onUnbind() → onDestory()
区别:

  1. 从通讯角度看,使用startService()方法启动的服务不能与Activity进行通讯,而使用bindService()方法启动的服务可以与Activity进行通讯。

  2. 从生命周期看,startService()方法启动服务是通过startCommand()方法,而bindService()方法是通过onBind()方法。

  3. 通过startService()方法启动的服务,当调用者退出后,服务仍然可以运行,而使用bindService()方法启动的服务则停止运行。

  • onCreate()方法在生命周期中只调用一次,若在服务已经启动的前提下,多次调用startService()方法或者调用bindService()方法,都不会再执行onCreate()方法,在使用starService()方法启动服务的情况下,会多次调用onStart()方法。

谷歌官方绑定服务概览
绑定服务概览  |  Android 开发者  |  Android Developers
深入理解Android的startservice和bindservice
深入理解Android的startservice和bindservice - Andye - 博客园
startService和bindService 区别
startService bindService 区别_runrun117的博客-CSDN博客_bindservice startservice
Android中bindService的使用及Service生命周期
Android中bindService的使用及Service生命周期_孙群的博客-CSDN博客_android service生命周期
Android Service启动(一) startService()启动过程以及原理解析
Android Service启动(一) startService()启动过程以及原理解析_生死看淡_不服就干的博客-CSDN博客_startservice
Android Service启动(二) bindService()启动过程以及原理解析
Android Service启动(二) bindService()启动过程以及原理解析_生死看淡_不服就干的博客-CSDN博客

8.java类加载: 静态成员 ,静态代码块,构造方法。非静态成员 方法顺序

 一般顺序:静态块(静态变量)——>成员变量——>构造方法——>静态方法
 如果类还没有被加载:
1、先执行父类的静态代码块和静态变量初始化,并且静态代码块和静态变量的执行顺序只跟代码中出现的顺序有关。
2、执行子类的静态代码块和静态变量初始化。
3、执行父类的实例变量初始化
4、执行父类的构造函数
5、执行子类的实例变量初始化
6、执行子类的构造函数
如果类已经被加载:
则静态代码块和静态变量就不用重复执行,再创建类对象时,只执行与实例相关的变量初始化和构造方法。

java中静态变量,静态代码块,静态方法,实例变量,匿名代码块的加载顺序
java中静态变量,静态代码块,静态方法,实例变量,匿名代码块的加载顺序_lilong117194的博客-CSDN博客
一看你就懂,超详细java中的ClassLoader详解
一看你就懂,超详细java中的ClassLoader详解_frank909的博客-CSDN博客_classloader
ClassLoader
https://blog.csdn.net/mingyunxiaohai/article/details/86677509
深入理解Java类加载器(ClassLoader)
https://blog.csdn.net/javazejian/article/details/73413292
java中类的加载顺序介绍(ClassLoader)
https://blog.csdn.net/eff666/article/details/52203406

9.事件分发后如何中断处理。

 Android事件分发流程 = Activity -> ViewGroup -> View 即:1个点击事件发生后,事件先传到Activity、再传到ViewGroup、最终再传到 View
 Activity事件分发机制
 点击事件发生-》Activity.dispatchTouchEvent()-》getWindow().superDispatchTouchEvent()->mDecor.superDispatchTouchEvent()
 即ViewGroup的dispatchTouchEvent()
 返回值true:Activity.dispatchTouchEvent()为true事件分发结束。
 返回值false:Activity.onTouchEvent()无论返回什么事件分发结束,事件范围内默认返回为false.
 ViewGroup事件分发机制
 当点击了某个控件时-》ViewGroup.dispatchTouchEvent()-》ViewGroup.onInterceptTouchEvent()
 返回值true:事件停止往下传递,调用ViewGroup父类的dispatchTouchEvent()-》调用自身的onTouch()->onTouchEvent()->performClick->>onClick()
 返回值false:允许事件继续向子View传递->遍历ViewGroup的所有子View-》调用子View的dispatchTouchEvent() 实现事件从ViewGroup到View的传递。
 View事件分发机制
 每当控件被点击时-》View.dispatchTouchEvent()-》View.onTouch()
 返回值true:事件不再向下传递结束
 返回值false:事件无消费继续传递-》View.onTouchEvent()->performClick->>onClick()->手动回调setOnClickListener()为控件View注册点击事件

安卓的事件传递,中断,分发机制
安卓的事件传递,中断,分发机制_qq_15949077的博客-CSDN博客
Android事件分发机制 详解攻略,您值得拥有
Carson带你学Android:手把手带你深入分析事件分发机制!_Carson带你学Android的博客-CSDN博客_android事件分发流程
android事件分发机制解析(配流程图)
android事件分发机制解析(配流程图)_scholarSu的博客-CSDN博客_事件分发机制流程图

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值