什么情况下会导致内存泄露
如何对Android 应用进行性能分析以及优化;
说一款你认为当前比较火的应用并设计(直播APP);
OOM的避免异常及解决方法;
1.什么是ANR 如何避免它?
Android App优化之ANR详解
ANR 应用程序无响应 根本原因不要在主线程(UI线程做繁重的工作)
使用主线程的地方
1.Activity的所有生命周期回调都是执行在主线城的
2.Service默认是执行在主线城的
3.BroadCastReceiver的onReceive回调是在主线程的
4.没有使用子线程的looper的Handler的handlerMessage,post(Runnable) 是在主线城的
5.AsyncTask的回调中除了doInBackground()方法,其他回调都是在主线城的
6.View的post(Runnable)是执行在主线城的
使用子线程的地方
1.使用Thread 方式
1)继承Thread 2)实现Runnable接口
2.使用AsyncTask方式
3.使用HandlerThread方式
Android中结合Handler和Thread的一种方式. 默认情况下Handler的handleMessage是执行在主线程的, 但是如果我给这个Handler传入了子线程的looper, handleMessage就会执行在这个子线程中的. HandlerThread正是这样的一个结合体:
// 启动一个名为new_thread的子线程
HandlerThread thread = new HandlerThread("new_thread");
thread.start();
// 取new_thread赋值给ServiceHandler
private ServiceHandler mServiceHandler;
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
// 此时handleMessage是运行在new_thread这个子线程中了.
}
}
4.IntentService
Service是运行在主线程的, 然而IntentService是运行在子线程的.
实际上IntentService就是实现了一个HandlerThread + ServiceHandler的模式.
以上HandlerThread的使用代码示例也就来自于IntentService源码
5.Loader
Android 3.0引入的数据加载器, 可以在Activity/Fragment中使用. 支持异步加载数据, 并可监控数据源在数据发生变化时传递新结果. 常用的有CursorLoader, 用来加载数据库数据.
// Prepare the loader. Either re-connect with an existing one,
// or start a new one.
// 使用LoaderManager来初始化Loader
getLoaderManager().initLoader(0, null, this);
//如果 ID 指定的加载器已存在,则将重复使用上次创建的加载器。
//如果 ID 指定的加载器不存在,则 initLoader() 将触发 LoaderManager.LoaderCallbacks 方法 //onCreateLoader()。在此方法中,您可以实现代码以实例化并返回新加载器
// 创建一个Loader
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// This is called when a new Loader needs to be created. This
// sample only has one Loader, so we don't care about the ID.
// First, pick the base URI to use depending on whether we are
// currently filtering.
Uri baseUri;
if (mCurFilter != null) {
baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
Uri.encode(mCurFilter));
} else {
baseUri = Contacts.CONTENT_URI;
}
// Now create and return a CursorLoader that will take care of
// creating a Cursor for the data being displayed.
String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
+ Contacts.HAS_PHONE_NUMBER + "=1) AND ("
+ Contacts.DISPLAY_NAME + " != '' ))";
return new CursorLoader(getActivity(), baseUri,
CONTACTS_SUMMARY_PROJECTION, select, null,
Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
}
// 加载完成
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
mAdapter.swapCursor(data);
}
View的绘制流程
View的绘制是从ViewRoot的performTraversals()方法开始的,经过measure,layout,draw这个3大步骤。
measure的过程:
measure过程是对整个view树的所有控件计算宽高
measure是冲ViewRoot类中的host.measure开始的,内部调用的是View的measure(int widthMeasureSpec,int heightMeasureSpec)方法,measure方法里面调用了onMeasure(int widthMeasureSpec,int heightMeasureSpec)方法,方法中的两个参数都是是MeasureSpec类型(指父控件对子控件宽高的期望值,它是一个32位的int类型数,前两位表示测量模式,后30位表示测量大小)
测量模式一共有3种:
1)EXACTLY 精确测量模式,xml文件中写200dp,march_parent等代表使用该模式,
2)AT_MOST 最大模式,xml文件中写wrap_content表示使用该模式。
3)UNSPECIFIED 无限大测量模式,只有在绘制特定自定义View时才用的到这个模式。
真正代表测量结束的方法是setMeasuredDimension方法,该方法传入的两个参数是宽高的SpecSize。测量结束后我们可以通过getMeasureHeight和getMeasureWidth来获取测量宽高。
自定义ViewGroup一定要重写onMeasure方法,用于测量子View的宽高,不重写的话子View没有宽高。
自定义View如果在xml中使用了wrap_content属性,就需要重写onMeasure方法来设置wrap_content的默认大小,不然会显示出match_parent的效果。
layout的过程:
ViewGroup用来将子View放在合适的位置上。
layout是从ViewRoot类中的host.layout开始的,内部调用的是ViewGroup的layout方法。在ViewGroup的layout方法中,先调用setFrame来确定自己的左上右下的位置,再调用onLayout来确定子View的位置。
自定义ViewGroup一定要重写layout方法来确定子View的位置,自定义View一般不需要重写该方法,它的位置是右父控件确定的。
draw过程:
此过程是真正将内容展示在屏幕上让我们能够看到的过程。
draw是从ViewRoot类中的host.draw开始的,内部调用的是View的draw方法。
draw的步骤:
1)绘制背景。
2)绘制内容,也就是调用onDraw方法。
3)绘制子View,调用的是dispatchDraw方法。
4)绘制装饰,如listview的滚动条等。
对于View的绘制过程,既可以说是简单的,也可以说是复杂的,简单的在于Google已经帮我们将draw框架写好了,我们在自定义ViewGroup时不用管draw过程,只需要实现measure和layout过程。复杂在于,我们写继承View的自定义控件的时候需要重写onDraw方法,这样才能绘制出你自定义的View的内容,onDraw(Canvas canvas)方法中最重要的两个东西是Paint和Canvas,这个使用起来算是比较复杂的。
Android View的绘制流程
自定义View如何考虑机型适配
- 合理使用warp_content,match_parent.
- 尽可能的是使用RelativeLayout
- 针对不同的机型,使用不同的布局文件放在对应的目录下,android会自动匹配。
- 尽量使用点9图片。
- 使用与密度无关的像素单位dp,sp
- 引入android的百分比布局。
- 切图的时候切大分辨率的图,应用到布局当中。在小分辨率的手机上也会有很好的显示效果。
自定义View的事件分发机制
安卓自定义View进阶-事件分发机制详解
安卓自定义View进阶-事件分发机制原理
View和ViewGroup分别有哪些事件分发相关的回调方法
自定义View如何提供获取View属性的接口
Art和Dalvik对比
在程序运行过程中Dalvik虚拟机不断的进行将字节码转换为机器码的工作。
而Art引入了AOT这种预编译技术,在应用程序的安装过程中已经将所有的字节码编译为了机器码,在运行的时候直接调用。Art极大的提高了程序的运行效率,同时减少了手机的耗电量,在垃圾回收机制上也有很大的优化,但是Art模式下应用程序的安装需要消耗更多的时间,同时也需要跟多的安装空间。
Dalvik 是Android4.4及以下平台的虚拟机。
Art 是在Android4.4以上平台使用的虚拟机。
虚拟机原理,如何自己设计一个虚拟机(内存管理,类加载,双亲委派);
JVM内存模型及类加载机制
内存对象的循环引用及避免
ddms 和 traceView的区别
1, ddms:是android开发环境中的dalvik虚拟机调试监控服务;
ddms能够提供,测试设备截屏,针对特定的进程查看正在运行的线程以及堆信息,Logcat,广播状态信息,模拟电话呼叫,接收sms,虚拟地理坐标等。
2,traceView是android平台配备的性能分析的工具;它可以通过图形化让我们了解要跟踪的程序的性能,并且能具体到方法。
区别:ddms是一个程序执行查看器,在里面可以看见线程和堆栈等信息,traceView是程序性能分析器。
内存回收机制与GC算法(各种算法的优缺点以及应用场景)
gc是java的垃圾回收机制
引用计数法(Reference Counting Collector):使用计数器来区分存活对象和不再使用的对象。一般来说,堆中的每个对象对应一个引用计数器。当每一次创建一个对象并赋给一个变量时,引用计数器置为1。当对象被赋给任意变量时,引用计数器每次加1当对象出了作用域后(该对象丢弃不再使用),引用计数器减1,一旦引用计数器为0,对象就满足了垃圾收集的条件。
标记算法(Tracing Collector):使用了根集的概念,基于tracing算法的垃圾收集器从根集开始扫描,识别出哪些对象可达,哪些对象不可达,并用某种方式标记可达对象。
整理算法(Compacting Collecotr):该算法会将所有的对象移到堆的一端。能解决堆碎片的问题。
复制算法:将内存分为两个区域(from space 和 to space)。所有的对象都分配到from space。清理时先将所有标为活动对象copy到to space,然后清除from space空间。然后互换from space和to apce的身份,每次清理都重复上述过程。
gc收集器:serial收集器:单线程,工作时必须暂停其他工作线程,多用于client机器上,使用复制算法。
ParNew收集器:serial的多线程版本,server模式下jvm首选的新生代收集器。复制算法。
Parallel Scavenge收集器:可控制吞吐量的收集器,吞吐量指有效运行时间。复制算法。
Serial Old收集器:serial的老年代版本,使用整理算法。
Parallel Old收集器:Parallel Scavenge收集器的老版本,多线程,标记整理。
CMS收集器:整理算法。最短回收停顿时间,缺点是产生碎片。
GI收集器:基本思想是化整为零,将堆分为多个Region,优先回收价值最大的Region。并行并发,分代收集,空间整合。整理算法。
GC原理时机以及GC对象
内存泄露场景及解决方法
屏幕适配的处理技巧都有哪些
四大组件及生命周期
Activity
Service
Service 官方资料 需翻墙
Service 资料
ContentProvider
BroadcastReceiver
ContentProvider的权限管理(读写分离,权限控制-精确到表级,URL控制);
Activity的四种启动模式对比
Android 深入解析 Activity 的 launchMode 启动模式,Intent Flag,taskAffinity
Activity状态保存于恢复
什么是AIDL 以及如何使用
AIDL:android interface definition language的缩写。
AIDL是用来实现进程间通信的,可以帮我们实现发布以及调用远程服务。
使用:
1)服务端:创建一个Service用来监听客户端的连接请求,然后创建一个AIDL文件,将服务端暴露给客户端的接口在这个文件中声明,最后在Service中实现这个AIDL接口。
2)客户端:首先绑定服务端的Service,绑定成功后将服务端返回的Binder对象转成AIDL接口所属的类型,接着就可以调用AIDL中的方法。
请解释下在单线程模型中Message、Handler、Message Queue、Looper之间的关系
Android--异步消息处理机制(Handler、Looper、Message、MessageQueue)
Fragment生命周期
onAttach(),onCreate(),onCreateView(),onActivityCreated(),onViewStateRestore(),onStart(),onResume(),onPause(),onStop(),onDestroy(),onDestroyView(),onDetach(),
Fragment状态保存
startActivityForResult是哪个类的方法,在什么情况下使用,如果在Adapter中使用应该如何解耦
AsyncTask原理及不足
intentService原理
HandlerThread+ServiceHandler
Activity 怎么和Service 绑定,怎么在Activity 中启动自己对应的Service
请描述一下Service 的生命周期;
启动服务的生命周期 onCreate->onStartCommand()->onDestroy()
绑定服务的生命周期 onCreate()->onBind()->onUnBind()->onDestroy()
AsyncTask+HttpClient与AsyncHttpClient有什么区别;
如何保证一个后台服务不被杀死,比较省电的方式是什么
Android中通过Service实现后台任务。
方法一:
通过将Service绑定到Notification,成为一个前提服务,可以提高存活率
在Service中创建一个Notification,再调用Service.startForeground(int id,Notification notification)方法运行在前台即可。这个方式使用360等如阿健管家可以杀死。
方法二:
通过定时警报来不断启动Service,这样就算Service被杀死,也能再启动。同时也可以监听网络切换,开锁屏等广播启动Service。
参考:
Intent intent = new Intent(mContext,MyService.class);
PendingIntent sender = PendingIntent.getService(mContext,0,intent,0);
AlarmManager alarm = (AlarmManager)getSystemService(ALARM_SERVICE);
alarm.setRepeating(AlarmManager.RTC_WAKEUP,System.currentTimeMillis(),5*10000,sender);
这种方式不断启动的逻辑处理起来很麻烦。
方法三:
通过jni调用c,在c语音中启动一个进程fork()。 可以保证360等手机管家不会清理。但是带来了jni交互,稍微有点麻烦。
如何通过广播拦截和abort一条短信;
广播是否可以请求网络
广播引起anr的时间限制
进程间通信,AIDL
事件分发中的onTouch 和onTouchEvent 有什么区别,又该如何使用?
说说ContentProvider、ContentResolver、ContentObserver 之间的关系;
请介绍下ContentProvider 是如何实现数据共享的
Android提供了ContentProvider,一个程序可以通过实现一个ContentProvider的抽象接口将自己的数据完全暴露出去,而且ContentProvicer是以类似数据库中表的方式将数据暴露。也就是说ContentProvider就像一个“数据库”。那么外界获取其提供的数据,也就应该与从数据库中获取数据的操作基本一样,只不过是采用URI来表示外界需要访问的“数据库”。外部访问通过ContentResolver去访问并操作这些被暴露的数据。
Handler机制及底层实现
Handler包括四个角色:
Handler:负责发送消息处理消息。
Message:消息实体对象,handler通过sendMessage将实体放到消息队列中。
MessageQueQue:存放消息的队列。
Looper:消息轮询器,不停的从消息队列中取出消息交给handler处理。
在主线程创建Handler,在需要发送消息的地方创建一个Message,通过handler发送。这个消息回到MessageQueQue中,然后Looper会将这个消息取出交给handler处理。
Handler可以有多个,但是在同一线程中Looper和MessageQueQue只能有一个。
Binder机制及底层实现
Binder包含四个角色:
Server 服务器
Client 客户终端 ,获得实名Binder的引用。Server向ServiceManger注册了Binder实体及名字后,Client就可以通过名字获得该Binder的引用。例如我们申请获得名字叫张三的Binder的引用,ServiceManager收到这个连接请求,从请求数据包里获得Binder的名字。再找到该名字对应的条目,从条目中取出Binder的引用。将该引用作为回复发送给发起请求的Client。
ServiceManager 域名服务器(DNS),负责将字符形式的Binder名字转化成Client中对该Binder的应用,使得Client能通过Binder名字获得Server中Binder实体的引用。
Binder驱动 可以理解为路由器。Binder驱动负责进程之间Binder通信的建立,Binder在进程间的传递。
Binder使用Client-Server通信方式,安全性好,简单高效。再加上其面向对象的设计思想,独特的接收缓存管理和线程池管理方式,成为Android进程间通信的中流砥柱。
ListView 中图片错位的问题是如何产生的
错位原理: 如果我们只是简单的显示数据,没有convertView的复用和异步操作,就不会产生图片错位。重用convertView但没有异步操作也不会有错位现象。例如我们的listView中刚好显示7个item,当向下滑动时,显示出item8,而item8是重用的item1,如果此时异步网络请求item8的图片,比item1的图片慢,那么item8就会显示item1的图片。当item8下载完成,此时用户向上滑显示item1时,又复用的item8的image。这样就导致的图片错位。
解决方法: 对imageview设置tag,并预设一张图片。向下滑动后,item8显示,item1隐藏。但由于item1是第一次进来就显示所以一般情况下,item1都会比item8先下载完,此时可见的item8的tag和隐藏了的item1的url不匹配,所以就算item1的图片下载完也不会显示到item8中,因为tag标识的永远是可见图片中的url
holder.img.setTag(imgUrl);
holder.img.setImageResource(R.drawable.ic_launcher);
if(imageView.getTag() != null && imageView.getTag().equals(imageUrl)){ imageView.setImageBitmap(result);}
在manifest 和代码中如何注册和使用BroadcastReceiver
<receiver android:name="包名.自己扩展的广播接收者名">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
</receiver>
说说Activity、Intent、Service 是什么关系
ApplicationContext和ActivityContext的区别
这是两种不同的context,也是最常见的两种.第一种中context的生命周期与Application的生命周期相关的,context随着Application的销毁而销毁,伴随application的一生,与activity的生命周期无关.第二种中的context跟Activity的生命周期是相关的,但是对一个Application来说,Activity可以销毁几次,那么属于Activity的context就会销毁多次.至于用哪种context,得看应用场景,个人感觉用Activity的context好一点,不过也有的时候必须使用Application的context.application context
一张Bitmap所占内存以及内存占用的计算
Serializable 和Parcelable 的区别
在Android上应该尽量采用Parcelable,它效率更高。
Parcelabe代码比Serializable多一些。
Parcelabe比Serializable速度高十倍以上。
Serializable只需要对某个类以及它的属性实现Serializable接口即可,无需实现方法。缺点是使用的反射,序列化的过程较慢,这种机制会在序列化的时候创建许多的临时对象。容易触发GC。
Parcable方法实现的原理是将一根完整的对象进行分解,而分解后的每一部分都是Intent所支持的数据类型,这样也就实现传递对象的功能。
请描述一下BroadcastReceiver
请描述一下Android 的事件分发机制
请介绍一下NDK
一 : NDK是一系列工具的集合
NDK提供了一系列的工具,帮助开发者快速开发C或C++的动态库,并能自动将so和java应用一起打包成apk。这些工具对开发者的帮助是巨大的。
NDK集成了交叉编译器,并提供了相应的mk文件隔离CPU,平台,ABI等差异。开发人员只需要简单修改mk文件(指出哪些文件需要编译,编译特性要求等)就可以创建出so。
NDK可以自动将so和java应用一起打包,极大的减轻了开发人员的打包工作。
二 : NDK提供了一份稳定,功能有限的API头文件声明。
Google明确声明该API是稳定的,在后续所有版本中都稳定支持当前发布的API。从该版本的NDK中看出,这些API支持的功能非常有限,包含:c标准库,标准数学库,压缩库,Log库。
什么是NDK库,如何在jni中注册native函数,有几种注册方式
AsyncTask 如何使用;
对于应用更新这块是如何做的?(灰度,强制更新,分区域更新);
混合开发,RN,weex,H5,小程序(做Android的了解一些前端js等还是很有好处的)
混合开发就是在一个App中内嵌一个轻量级的浏览器,一部分源生的功能改为Html5来开发,这部分功能不仅能够在不升级App的情况下动态更新,也可以在Android或者iOS的App上同时运行,让用户的体验更好又可以节省开发的资源。
混合开发最主要的是Html5和Native的交互
在Android中WebView本来就支持js和java相互调用,只需要开启WebView的脚本执行,然后通过mWebView.addJavascriptInterface(new JsBridge(),"bxbxbai");向Html5页面注入一股Java对象,然后就可以在Html5页面中调用Native的功能了。
Android4.2以后的系统规定允许被js调用的Java方法必须以@JavascriptInterface进行注解。
两个Activity 之间跳转时必然会执行的是哪几个方法?
答:一般情况下比如说有两个activity,分别叫A,B,当在A 里面激活B 组件的时候, A 会调用onPause()方法,然后B 调用onCreate() ,onStart(), onResume()。
这个时候B 覆盖了窗体, A 会调用onStop()方法. 如果B 是个透明的,或者是对话框的样式, 就不会调用A 的onStop()方法。