安卓常见的一些原理性问题

1,Handler机制和底层实现

答:处理程序机制的原理 

       Android的Handler机制(也有人叫消息机制)目的是为了跨线程通信,也就是多线程通信。之所以需要跨线程通信是因为在Android中主线程通常只负责UI的创建和修改,子线程负责网络访问和耗时操作,因此,主线程和子线程需要经常配合使用才能完成整个Android功能。Handler机制可以近似用图1展示.MainThread代表主线程,newThread代表子线程。

        MainThread 是Android系统创建并维护的,创建的时候系统执行了Looper.prepare();方法,该方法内部创建了MessageQueue消息队列(也叫消息池),该消息队列是消息的消息的容器,用于存储通过处理程序发送过来的Message.MessageQueue是Looper对象的成员变量,Looper对象通过ThreadLocal绑定在MainThread中。因此我们可以简单的这么认为:MainThread拥有唯一的一个Looper对象,该Looper对象有用唯一的MessageQueue对象,MessageQueue 对象可以存储多个

       Message。MainThread中需要程序员手动创建Handler对象,并覆写Handler中的handleMessage(Message msg)方法,该方法将来会在主线程中被调用,在该方法里一般会写与UI修改相关的代码。

       MainThread创建好之后,系统自动执行了Looper.loop();方法,该方法内部开启了一个“死循环”不断的去之前创建好的MessageQueue中取消息。如果一有消息进入MessageQueue,那么马上会被Looper.loop();取出来,取出来之后就会调用之前创建好的句柄对象的handleMessage(Message)方法.newThread线程是我们程序员自定新出来的子线程。在该子线程中处理完我们的“耗时”或者网络访问任务后,调用主线程中的处理程序对象的sendMessage(msg)方法,该方法一被执行,内部将就绪msg添加到了主线程的  MessageQueue队列中,这样就成为了Looper。 loop()的盘中餐了,等待着被消费。这是一个很复杂的过程,但是Android显然已经将这种模式给封装起来了,就叫Handler机制。我们使用时只需要在主线程中创建Handler ,并覆写handler中的handleMessage方法,然后在子线程中调用处理程序的se ndMessage(MSG)方法即可



2,Handler、Thread和HandlerThread的区别


3,handler发消息给子线程,looper怎么启动?

4,关于Handler,在任何地方new Handler都是什么线程下?

答:Looper.prepare()方法是将当前线程绑定一个looper实例,并存储在TreadLocal中,一个线程只有一个looper对象。

looper中,有一个messagequeue,即消息队列;
handler可以通过sendmessage 等方法向队列中发送消息。
一个线程中可以有多个handler,handler 绑定当前线程的looper对象,以及其中的消息队列;
looper.loop()方法即循环调用MQ中的message,并且调用方送它的handler来处理它。

5,ThreadLocal原理,实现及如何保证Local属性?

6,请解释下在单线程模型中Message、Handler、Message Queue、Looper之间的关系

答:简单的说,Handler获取当前线程中的looper对象,looper用来从存放Message的MessageQueue中取出Message,再由Handler进行Message的分发和处理.

Message Queue(消息队列):

用来存放通过Handler发布的消息,通常附属于某一个创建它的线程,可以通过Looper.myQueue()得到当前线程的消息队列Handler:

可以发布或者处理一个消息或者操作一个Runnable,通过Handler发布消息,消息将只会发送到与它关联的消息队列,然后处理该消息队列中的消息

Looper:

是Handler和消息队列之间通讯桥梁,程序组件首先通过Handler把消息传递给Looper,Looper把消息放入队列。Looper也把消息队列里的消息广播给所有的 
Handler:Handler接受到消息后调用handleMessage进行处理

Message:

消息的类型,在Handler类中的handleMessage方法中得到单个的消息进行处理。

在单线程模型下,为了线程通信问题,Android设计了一个Message Queue(消息队列), 线程间可以通过该Message Queue并结合Handler和Looper组件进行信息交换。

7,请描述一下View事件传递分发机制

8,Touch事件传递流程

9,事件分发中的onTouch 和 onTouchEvent有什么区别,又该如何使用?

10,View和ViewGroup分别有哪些事件分发相关的回调方法

11,View刷新机制

答:Android 的布局体系中,父查看负责刷新,布局显示子查看;而当子查看需要刷新时,则是通知父查看来完成。这种处理逻辑在查看的代码中明确的表现出来:

12,View绘制流程

答:View的绘制基本分为onMeasure,onLayout,onDraw过程

13,自定义控件原理

答:如何把自定义控件的原理落实到代码上
步骤
1)自定义属性的声明与获取
分析需要的自定义属性;
在res/values/attrs.xml中进行声明;
在Layout.xml文件中进行使用
在View的构造方法中获得我们自定义的属性/声明的属性;

TypedArray 获取所有的自定义属性的集合,然后进行遍历;

2)测量onMeasure()
测量模式和测量值,有三种测量模式
EXACTLY:一般是设置了明确的值或者是MATCH_PARENT
AT_MOST:表示子布局限制在一个最大值内,一般为WARP_CONTENT

UNSPECIFIED:表示子布局想要多大就多大,ListView  ScrollView

MeasureSPEC

主要用于计算视图的大小,即视图的宽度和长度;
子类可以覆写onMeasure()方法实现自己的计算视图大小的方式,并通过setMeasuredDimension(width, height)保存计算结果;

3)布局onLayout() 

自定义ViewGroup才有的方法,继承自VierGroup,自定义View没有的;编写继承自View/VierGroup的子类(TextView、LinearLayout等)

用于控制控件在屏幕中显示的位置;

4)绘制onDraw()  
调用Canvas里面的api进行绘制操作;
draw操作利用前两部得到的参数,将视图显示在屏幕上;
5)onTouchEvent()
触摸事件的实现,响应用户操作。
6)onInterceptTouchEvent()

事件分发和拦截,只有自定义ViewGroup才有的方法;

在布局文件中使用时,使用全类名;

14,自定义View如何提供获取View属性的接口?

答:

15,Android代码中实现WAP方式联网

16,AsyncTask机制

17,AsyncTask原理及不足

18,如何取消AsyncTask?

19,为什么不能在子线程更新UI?

答:1、在子线程中是不能进行UI更新的,而可以更新的结果只是一个幻像:因为子线程代码执行完毕了,又自动进入到了主线程,执行了子线程中的UI更新的函数栈,这中间的时间非常的短,就让大家误以为分线程可以更新UI。如果子线程一直在运行,则子线程中的UI更新的函数栈 主线程无法获知,即无法更新

2、只有极少数的UI能,因为开辟线程时会获取当前环境,如点击某个按钮,这个按钮响应的方法是开辟一个子线程,在子线程中对该按钮进行UI 更新是能及时的,如换标题,换背景图,但这没有任何意义

PS:个人认为其他的原因。

3.UI是非线程安全的,主线程和子线程同时更新UI的话会导致错误,如UI错乱之类的。

4.UI更新是很耗性能的,更别说为了线程安全加锁了,最简单的方法就是更新UI的操作放到一个线程中,即主线程;

20,ANR产生的原因是什么?

答:1.在5秒内没有响应输入的事件(例如,按键按下,屏幕触摸)

2.BroadcastReceiver在10秒内没有执行完毕

3.service是20秒

造成以上两点的原因有很多,比如在主线程中做了非常耗时的操作,比如说是下载,io异常等。

 潜在的耗时操作,例如 网络或数据库操作,或者高耗时的计算如改变位图尺寸 ,应该在子线程里(或者以数据库操作为例,通过异步请求的方式)来完成。然而,不是说你的主线程阻塞在那里等待子线程的完成——也不是调用 Thread.wait()或是Thread.sleep()。替代的方法是,主线程应该为子线程提供一个Handler,以便完成时能够提交给主线程。以这种方式设计你的应用程序,将能保证你的主线程保持对输入的响应性并能避免由于5秒输入事件的超时引发的ANR对话框。

第二:如何避免ANR?

1、运行在主线程里的任何方法都尽可能少做事情。特别是,Activity应该在它的关键生命周期方法(如onCreate()和onResume())里尽可能少的去做创建操作。(可以采用重新开启子线程的方式,然后使用Handler+Message的方式做一些操作,比如更新主线程中的ui等)

2、应用程序应该避免在BroadcastReceiver里做耗时的操作或计算。但不再是在子线程里做这些任务(因为 BroadcastReceiver的生命周期短),替代的是,如果响应Intent广播需要执行一个耗时的动作的话,应用程序应该启动一个 Service。(此处需要注意的是可以在广播接受者中启动Service,但是却不可以在Service中启动broadcasereciver,关于原因后续会有介绍,此处不是本文重点)

3、避免在Intent Receiver里启动一个Activity,因为它会创建一个新的画面,并从当前用户正在运行的程序上抢夺焦点。如果你的应用程序在响应Intent广 播时需要向用户展示什么,你应该使用Notification Manager来实现。

21,ANR定位和修正

答:根本原因是:主线程被卡了,导致应用在5秒时间未响应用户的输入事件。

很多种ANR错误出现的场景:

1) 主线程当中执行IO/网络操作,容易阻塞。

2) 主线程当中执行了耗时的计算。----自定义控件的时候onDraw方法里面经常这么做。

(同时聊一聊自定义控件的性能优化:在onDraw里面创建对象容易导致内存抖动---绘制动作会大量不断调用,产生大量垃圾对象导致GC很频繁就造成了内存抖动。)内存抖动就容易造成UI出现掉帧卡顿的问题

3) BroadCastReceiver没有在10秒内完成处理。

4) BroadCastReceiver的onReceived代码中也要尽量减少耗时的操作,建议使用IntentService处理。

5) Service执行了耗时的操作,因为service也是在主线程当中执行的,所以耗时操作应该在service里面开启子线程来做。

6) 使用AsyncTask处理耗时的IO等操作。

7) 使用Thread或者HandlerThread时,使用Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)或者java.lang.Thread.setPriorityint priority设置优先级为后台优先级,这样可以让其他的多线程并发消耗CPU的时间会减少,有利于主线程的处理。

8) Activity的onCreate和onResume回调中尽量耗时的操作。

22,oom是什么?

答:程序申请内存过大,虚拟机无法满足我们,然后自杀了。这个现象通常出现在大图片的APP开发,或者需要用到很多图片的时候。通俗来讲就是我们的APP需要申请一块内存来存放图片的时候,系统认为我们的程序需要的内存过大,及时系统有充分的内存,比如1G,但是系统也不会分配给我们的APP,故而抛出OOM异常,程序没有捕捉异常,故而弹窗崩溃了

23,什么情况导致oom?

答:一、 Acitivity没有对栈进行管理,如果开启过多,就容易造成内存溢出

二、加载大的图片或者同时数量过多的图片的时候

三、数据库或者资源没有关闭

四、静态成员变量持有类的应用

五、非静态内部类持有外部类的引用,使用非静态内部类创建静态变量

六、单例引起内存泄露

七、Handler造成内存泄露

八、线程周期不可控

九、无线循环的属性动画引起内存泄露

十、listView等没有getView方法没有复用

十一、递归次数过多,也会导致内存溢出.

十二、频繁的内存抖动,也会造成OOM异常的发生,大量小的对象被平凡的创建,导致内存碎片,从而当需要分配内存的时候,虽然总体上还有内存分配,但是由于这些内存不是连续的,导致无法分配,系统就直接返回OOM

24,有什么解决方法可以避免OOM?

答:

25,Oom是否可以try catch?为什么?

答:只有在一种情况下,这样做是可行的:

在try语句中声明了很大的对象,导致OOM,并且可以确认OOM是由try语句中的对象声明导致的,那么在catch语句中,可以释放掉这些对象,解决OOM的问题,继续执行剩余语句。

但是这通常不是合适的做法。

Java中管理内存除了显式地catch OOM之外还有更多有效的方法:比如SoftReference, WeakReference, 硬盘缓存等。

在JVM用光内存之前,会多次触发GC,这些GC会降低程序运行的效率。
如果OOM的原因不是try语句中的对象(比如内存泄漏),那么在catch语句中会继续抛出OOM

26,内存泄漏是什么?

答:内存泄漏的定义: 对象不再被应用程序使用,但是垃圾回收器却不能移除它们,因为它们正在被引用。

27,什么情况导致内存泄漏?

答:1.对象内存过大

保存了多个好用内存过大的对象,造成内存超出限制。

2.资源释放

程序代码的问题,长期保持某些资源,如Context,Cursor,IO流的引用,资源得不到释放造成内存泄露。

3.static关键字的使用

static 是Java中的一个关键字,当用它来修饰成员变量时,那么该变量就属于该类,而不是该类的实例。所以用static修饰的变量,它的生命周期是很长的,如果用它来引用一些资源耗费过多的实例,就可能会造成内存的泄露。

针对static的解决方案:

应该尽量避免static成员变量引用资源耗费过多的实例,比如Context.

Context尽量使用ApplicationContext的生命周期比较长,引用它不会出现内存泄露。

使用WeakReference代替强引用。比如可以使用WeakReference<Context> mContext;

4.线程导致内存溢出

线程产生内存泄露的主要原因在于线程生命周期的不可控。如当我们切换横竖屏的时候,一般会重新创建Activity,老的Activity应该被销毁。但是此时我们在子线程中正在进行耗时的操作,老的Activity不会被销毁,这个时候就会出现内存泄露。

28,如何防止线程的内存泄漏?

29,内存泄漏场的解决方法

30,内存泄漏和内存溢出区别?

答:

内存泄漏

内存泄漏(memory leak): 是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。 
一般我们所说的内存泄漏是指堆内存的泄漏,堆内存是指程序从堆中分配的,大小任意的(内存块的大小可以在程序运行期决定),使用完成之后必须显示释放内存。应用程序一般使用malloc、realoc、new等函数从堆中分配到一块内存块,使用完成后,程序必须负责相应的释放。在C中使用free(),C++中delete、delete[]、free()。而Java中由于垃圾回收机制不用手动释放。 
如果内存不能释放,这块内存就不能再次使用,我们就说这块内存泄漏了。

内存溢出

内存溢出(out of memory):程序要求的内存,超出了系统所能分配的范围。如:我们用一个int型4字节的数据来装一个float型8字节的数据,就会产生内存溢出。不过我们在编程是可以强制类型转换int XX = (int)float;只取float 4字节数据给int.


以发生的方式来分类,内存泄漏可以分为4类: 
1. 常发性内存泄漏。发生内存泄漏的代码会被多次执行到,每次被执行的时候都会导致一块内存泄漏。 
2. 偶发性内存泄漏。发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。常发性和偶发性是相对的。对于特定的环境,偶发性的也许就变成了常发性的。所以测试环境和测试方法对检测内存泄漏至关重要。 
3. 一次性内存泄漏。发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块仅且一块内存发生泄漏。比如,在类的构造函数中分配内存,在析构函数中却没有释放该内存,所以内存泄漏只会发生一次。 
4. 隐式内存泄漏。程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。严格的说这里并没有发生内存泄漏,因为最终程序释放了所有申请的内存。但是对于一个服务器程序,需要运行几天,几周甚至几个月,不及时释放内存也可能导致最终耗尽系统的所有内存。所以,我们称这类内存泄漏为隐式内存泄漏。

从用户使用程序的角度来看,内存泄漏本身不会产生什么危害,作为一般的用户,根本感觉不到内存泄漏的存在。真正有危害的是内存泄漏的堆积,这会最终消耗尽系统所有的内存。从这个角度来说,一次性内存泄漏并没有什么危害,因为它不会堆积,而隐式内存泄漏危害性则非常大,因为较之于常发性和偶发性内存泄漏它更难被检测到

31,LruCache默认缓存大小

32,ContentProvider的权限管理(解答:读写分离,权限控制-精确到表级,URL控制)

33,如何通过广播拦截和abort一条短信?

34,广播是否可以请求网络?


35,广播引起anr的时间限制是多少?

36,计算一个View的嵌套层级

答:

37,activity栈

38,android线程有没有上限?

答:没有上限的,因为资源都限制在这个进程里,你开多少线程都最多用这些资源。至于开多少最好,完全取决你的需求,合理开线程,不卡,高效是最终目标。

39,线程池有没有上限?

答:

40,ListView重用的是什么?

41,android为什么引入Parcelable?

42,有没有尝试简化Parcelable的使用?


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值