[toc]
Android面试题
Android面试题除了Android基础之外,更多的问的是一些源码级别的、原理这些等。所以想去大公司面试,一定要多看看源码和实现方式,常用框架可以试试自己能不能手写实现一下,锻炼一下自己。
一、Android基础知识点
四大组件是什么
Activity
其中Activity是我们最常使用的组件,也是我们最熟悉的组件,一般用于呈现页面内容,处理用户交互等等Service
Service是一个可以运行于后台的组件,我们一般用于处理一些不需要用户知道,但是又必须比较长时间存在的操作,比如下载ContentProvider
主要用于进程间通信,比如暴露某个APP的信息内存给予另外一个APP获取使用,比如获取联系人等等-
BroadcastReceiver
广播接受者主要用于接受广播信息,像我们日常使用中可能用于两种情况:- APP内:界面间通信,例如退出app,可以发送自杀广播
- APP间:可以收到第三方发出的广播,进而进行对应的响应操作。
四大组件的生命周期和简单用法
Activity
- 生命周期
oncreate()->onstart()->onresume()->onpause()->onstop()->ondestory()
- 简单用法
/**创建App*/
class someClass :Activity()
/**跳转app*/
startActivity(Intent(this@someActivity,targetClass::class.java))
Service
- 生命周期
- startService()的生命周期:
oncreate()->onstartComment()->onstart()->onDestory() - bindService()的生命周期:
oncreate()->onbind()->onUnbind()->onDestroy()
- 简单用法
- startService()
通过简单的startService()进行service启动,此后启动该Service的组件无法把控Service的生命周期,理论上此后该Service可以在后台无期限运行(实际根据情况该Service可能会在任意一个时刻被杀死,这里牵连到了另外一个知识点:Service防杀)
我们可以在onStartCommand()里面做我们要做的操作,注意Service跟Activity一样不可以做耗时操作,虽然运行anr时间比Activity多了近一倍。 - bindService()
通过绑定的方式启动Service
绑定后,该Service与启动绑定操作的组件形成绑定,当组件销毁时,该Service也随着销毁。
其中组件与Service形成一个典型的BC体系,Service相当于服务器,组件可以通过IBinder像Service 发送请求并获取回应。
- startService()
public boolean bindService(Intent service, ServiceConnection conn,
int flags)
BroadcastReceiver(分为2种):
- 简单用法
- 静态注册(常驻广播)
在AndroidManifest.xml中进行注册,App启动的时候自动注册到系统中,不受任何组件生命周期影响,(即便应用程序已经关闭),但是 耗电,占内存 - 动态注册(非常驻广播)
在代码中进行注册,通过IntentFilter意图过滤器筛选需要监听的广播,
记得注销(推荐在onResume()注册,在onPause()注销),使用灵活,生命周期随组件变化
- 静态注册(常驻广播)
全局广播
- 普通广播(最常用的那种)
- 系统广播
系统广播无须开发者进行发送,我们只需做好广播接收器进行接收即可,常用的几个系统广播为:
android.net.conn.CONNECTIVITY_CHANGE 监听网络变化
Intent.ACTION_PACKAGE_ADDED 成功安装apk
... ... - 有序广播
有序广播是指广播按照一定的优先级被广播接受者依次接收,代码实例
发送广播
定义2个广播接受者
动态注册2个广播接受者,设置不同的优先级
高优先级的广播接受者接受信息,篡改信息,塞回篡改后的信息
低优先级广播接收源信息,接收上一个广播信息,分别打印出来
class MainActivity : AppCompatActivity() {
companion object {
fun sayLog(message: String) = Log.e("THIS IS THE TAG", message)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
tvMainSendBroadcast.setOnClickListener {
sendBroadcast()
}
// register 高等级的广播 等级为100
val firstReceiver = BroadcastReceiverOrderFirst()
val intentFilter = IntentFilter()
intentFilter.addAction("lY_ACTION")
intentFilter.priority = 100
registerReceiver(firstReceiver, intentFilter)
// register 低等级的广播 等级为50
val lowReceiver = BroadcastReceiverOrderLow()
val intentFilterLow = IntentFilter()
intentFilterLow.addAction("lY_ACTION")
intentFilterLow.priority = 50
registerReceiver(lowReceiver, intentFilterLow)
}
/**发送普通广播*/
private fun sendBroadcast() {
val targetIntent = Intent()
val bundle = Bundle()
bundle.putString("message", "this is the def message")
targetIntent.action = "lY_ACTION"
targetIntent.putExtra("extra", bundle)
sendOrderedBroadcast(targetIntent, null)
}
/**
* 优先级比较高的广播接受者
*/
class BroadcastReceiverOrderFirst : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val bundle = intent?.getBundleExtra("extra")
sayLog("BroadcastReceiverOrderFirst has the message,the message is ${bundle?.getString("message")}")
// 修改携带的信息
bundle?.putString("message", " this is not the def message")
// 把修改好的信息put进结果集
setResultExtras(bundle)
}
}
/**
* 优先级比较低的广播接受者
*/
class BroadcastReceiverOrderLow : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
// 获取源数据
val bundle = intent?.getBundleExtra("extra")
sayLog("BroadcastReceiverOrderLow has the message,the message is ${bundle?.getString("message")}")
// 获取上一个广播传递过来的数据
val bundleForPre=getResultExtras(true)
sayLog("BroadcastReceiverOrderLow has the message,the modify is ${bundleForPre?.getString("message")}")
}
}
}
}
本地广播
上面所讲的广播都是全局广播,全局广播指的是,我在甲App发送了一条广播,其中已App也可以收到这个广播信息,存在问题为:
- 其他app可以发送相对应的 intent-filter到我方App,导致我方错误处理
- 其他app可以设置对应的 intent-filter接收我方App发出的广播,导致安全性问题
为解决这个问题,我们可以使用本地广播:
1.方式一:- 设置exported为false(默认为true),使该广播只在app内传递
- 广播传递和接收时,添加响应的permission
- 通过intent.setPackage(com.xxx.xxx)
- 方式二 LocalBroadcastManager:
/**发送普通广播*/
private fun sendBroadcast() {
val targetIntent = Intent()
val bundle = Bundle()
bundle.putString("message", "this is the def message")
targetIntent.action = "lY_ACTION"
targetIntent.putExtra("extra", bundle)
val localBroadcastReceiver =LocalBroadcastManager.getInstance(this@MainActivity)
localBroadcastReceiver.sendBroadcast(targetIntent)
// register
val lowReceiver = BroadcastReceiverOrderLow()
val intentFilterLow = IntentFilter()
intentFilterLow.addAction("lY_ACTION")
intentFilterLow.priority = 50
localBroadcastReceiver.registerReceiver(lowReceiver,intentFilterLow)
}
ContentProvider
- contentProvider 内容提供者
- contentresolver 内容解析器
Activity之间的通信方式
- 通过广播的方式进行通信
- 通过Intent的方式通信(startActivityForResult)
- 借助类的静态成员/方法进行通信
- 使用外部工具(sqlite/sharePreference/file/剪切板...)
- 全局变量(静态配置类/Application)
- Service(IPC模型),也就是上文的bingService()
- 通过EventBus/rxBus 进行通信
Activity各种情况下的生命周期
- 正常情况下是:onCreate()->onStart()->onResume()->onPause()->onStop()->onDestory()
- 被dialog遮罩情况下:
- 如果是自身的dialog那么不会走自己的生命周期
- 如果是其他组件传递的dialog,那么会走onpause()
- 可以看到我们前面有一个onRestart()的生命周期,这个生命周期我们不常用,具体是什么时机会被调用呢:
- home键,然后切换回来
- 跳转到另外一个activity,然后back键
- 从本应用跳转到另外一个应用,然后回来
... 其他
横竖屏切换的时候,Activity 各种情况下的生命周期
2种情况:
- 不做任何配置,生命周期重新走一遍:完整流程为:onCreate()->onStart()->onResume()->onPause()->onSaveInstanceState()->onStop()->Ondestory()->onCreate()->onStart()->onRestoreInstanceState()->onResume()。
- 做配置:android:configChanges="orientation|screenSize"
横竖屏不走其他生命周期
其中onSaveInstanceState()可以保存用户数据,对应的onRestoreInstanceState()可以读取之前保存的用户数据
Activity与Fragment之间生命周期比较
activity有7个生命周期,fragment有十一个生命周期
其中他们的关系是:
- 创建的时候:Activity 带动 Fragment 走生命周期,表格关系为:
所属 | Activity | Fragment |
---|---|---|
创建操作 | onCreate | onAttach/onCreate/onCreateView/onActivityCreated |
创建操作 | onStart | onStart() |
- 销毁的时候:Fragment 带动 Activity 走生命周期,表格关系为:
所属 | Fragment | Activity |
---|---|---|
创建操作 | onPause() | onPause() |
创建操作 | onStop() | onStop() |
创建操作 | onDestroyView()/onDestroy()/onDetach() | onDestroy() |
Activity上有Dialog的时候按Home键时的生命周期
两个Activity 之间跳转时必然会执行的是哪几个方法?
假设有2个Activity A,B,在A里面激活B
A:调用onPause()
B:调用onCreate(),onStart(),onResume()
如果B是个正常的Activity,那么B会覆盖A,那么A会走onStop()
如果B不能覆盖A,那么A不会走onStop()
前台切换到后台,然后再回到前台,Activity生命周期回调方法。弹出Dialog,生命值周期回调方法
- 前台切换到后台:onPause()->onStop()
如果正常切换回来,那么会走onRestart()->onStart()->onResume()
如果很久没切回来,系统内存紧急被回收了,那么回来会重新走一次生命周期 - 弹出dialog
如果dialog是自身Activity弹出来的,则不会走生命周期
如果不是自身Activity弹出来的,则走onPause(),退出Dialog后走onRestart()->onResume()
Activity的四种启动模式对比
standard
这个是Activity的默认启动方式,我们不需要额外的配置
在该配置下,启动一个Activity就会在该应用的Activity栈中压入一个Activity,返回的时候就直接把该Activity弹出栈。
singleTop
这个是栈顶复用模式
在该配置下,如果在Activity栈,栈顶是该Activity,那么会走onNewIntent()->onResume()
如果不是,那么就走正常的生命周期
singleInstance
这个是栈内复用模式
在该配置下,如果在该Activity栈,栈内存在该Activity(没有要求是栈顶),那么会走onNewIntent()->onResume(),并且把位于该Activity上方的Activity全部出栈,使该Activity位于栈顶
singleTask
这个配置下,Activity独享一个Activity栈。
Activity状态保存与恢复
override fun onSaveInstanceState(outState: Bundle?){} 保存状态
override fun onRestoreInstanceState(savedInstanceState: Bundle?){} 恢复状态
Fragment状态保存startActivityForResult是哪个类的方法,在什么情况下使用?
startActivityForResult是FragmentActivity的方法。
其实和Activity的startActivityForResult是一样的,只不过需要注意在Fragment里面调用的话,直接使用:
val targetIntent = Intent([email protected], DialogActivity::class.java)
startActivityForResult(targetIntent, 1)
而不需要使用getActivity()/activity:
val targetIntent = Intent([email protected], DialogActivity::class.java)
activity.startActivityForResult(targetIntent, 1)
如何实现Fragment的滑动?
- 把Fragment放到ViewPager里面去
- 把Fragment放到RecyclerView/ListView/GridView
... ...
fragment之间传递数据的方式?
- Intent传值
- 广播传值
- 静态调用
- 本地化存储传值
- 暴露接口/方法调用
- eventBus等之类的
Activity 怎么和Service 绑定?
通过BindService绑定,具体步骤
- 新建Activity,Service。Activity里面绑定Service。
- Service新建绑定类继承Binder。
具体代码如下:
Service
public class MyService extends Service {
private IBinder iBinder=new MyBinder();
@Nullable
@Override
public IBinder onBind(Intent intent) {
return iBinder;
}
class MyBinder extends Binder {
public MyService get() {
return MyService.this;
}
}
}
Activity
public class ServiceActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyConnection myConnection = new MyConnection();
Intent intent = new Intent(this, MyService.class);
bindService(intent, myConnection, Service.BIND_AUTO_CREATE);
}
private class MyConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
MyService myService = ((MyService.MyBinder) service).get();
Log.e("lht", "onServiceConnected: " + myService.getPackageName());
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onBindingDied(ComponentName name) {
}
}
}
谈谈你对ContentProvider的理解
说说ContentProvider、ContentResolver、ContentObserver 之间的关系
AlertDialog,popupWindow区别
AlertDialog是非阻塞式对话框,当弹出来的时候,后台还是可以继续做事情的
popupWindow则是阻塞式的对话框
Application 和 Activity 的 Context 对象的区别
- Application是全局的Context,可以用于需要长期持久的contenxt操作
- 而Activity的Context只是属于该Activity,如果被非法持有的话,很容易导致该Activity无法被回收,造成内存泄露
Android属性动画特性
如何导入外部数据库?
- 把外部建好的数据库文件放在raw中,
- 读入该数据库文件
- 写入到data/data/batabases文件夹中
- 正常读取
介绍下SurfaceView
SurfaceView是View的子类,与普通的View的最大区别就是内部实现了双缓冲机制:就是使用主线程来负责UI的显示和渲染线程做UI的渲染,这样2个线程交替进行,这样做使得页面显示内容的速度非常快,但是同样的,也加大了对内存和cpu的开销。
序列化的作用,以及Android两种序列化的区别
- Serializable接口
是一个空接口,为对象提供标准的序列化和反序列化操作。
其中有一个long类型的值为serialVersionUID,我们开发人员很少用到这个,但是官方推荐是最好可以声明这个属性,主要用于校验序列化和反序列化。
serialVersionUID的详细工作过程是这样的:序列化的时候系统会把当前类的serialVersionUID写入序列化的二进制文件中,当反序列化的时候系统会检测文件中的serialVersionUID是否和当前类的serialVersionUID一致,如果一致就说明序列化的类的版本和当前类的版本是相同的,这个时候可以成功反序列化;否则说明当前类和反序列化的类相比发生了某些变化,比如成员变量的数量、类型发生了变化,这个时候是无法正常反序列化的。
- Parcelable接口
是androidSdk特意为序列化和反序列化开发出来的一个接口,相当于Serializable具有更好的效率。
但是Parcelable接口使用起来比较麻烦。具有如此的属性:
方法 | 功能 | 标记位 |
---|---|---|
createFromParcel(Parcel in) | 从序列化后的对象中创建原始对象 | |
newArray(int size) | 创建指定长度的原始对象数组 | |
User(Parcel in) | 从序列化后的对象中创建原始对象 | |
writeToParcel(Parcel out,int flags) | 将当前对象写入序列化结构中 | PARCALABLE_WRITE_RETURN_VALUE |
describeContents | 返回当前对象的内容描述,几乎所有情况都返回0,仅在当前对象中存在文件描述符时返回1 | CONTENTS_FILE_DESCRIPTOR |
- 两者区别
区别 | Serializable | Parcelable |
---|---|---|
所属API | JAVA API | Android SDK API |
原理 | 序列化和反序列化过程需要大量的I/O操作 | 序列化和反序列化过程不需要大量的I/O操作 |
开销 | 开销大 | 开销小 |
效率 | 低 | 很高 |
使用场景 | 序列化到本地或者通过网络传输 | 内存序列化 |
二、Android源码相关分析
Android动画框架实现原理
Android各个版本API的区别
其实只需要知道比较著名的几个就好了
- android 5.0引入material design风格
- android 6.0引入权限动态管理
- android 7.0引入多窗口支持等
- android 8.0引入画中画等
Requestlayout,onlayout,onDraw,DrawChild区别与联系(个人看法,不一定正确)
- RequestLayout():
子View调用requestLayout方法,会标记当前View及父容器,同时逐层向上提交,直到ViewRootImpl处理该事件,ViewRootImpl会调用三大流程,从measure开始,对于每一个含有标记位的view及其子View都会进行测量、布局、绘制。 - onLayout()只是一个调度方法,实质调用的是layout(int l, int t, int r, int b)方法,其中的l,t,r,b分别表示该参数相对于父参数对应参数的位置,当需要重新布局时候,会依次上传,直至rootView
- onDraw同onLayout()
- DrawChild(Canvas canvas, View child, long drawingTime)也是个调度方法,调用了child.draw()方法
- 联系:Requestlayout会调用View树的重绘机制,会分别调用需要重绘View的测量,布局,绘制3个过程,而且其中onlayout(),onDraw()会被分别依次调用,调用Requestlayout()方法的view会依次上报父View分别进行测量,布局,绘制。而绘制的时候,子view会被父View通过DrawChild()启动目标View的绘制。
invalidate和postInvalidate,以及他们和RequestLayout的区别及使用
- invalidate 该方法的调用会引发View树的重绘,一般用于View内部调用或者是需要刷新界面的时候,该方法有多个重载方法,但是最终都会调用到invalidateInternal方法,在该方法内部,会经过一系列的判断之类的,判断该View是否需要重绘,然后给View设置标识位,然后把需要重绘的区域传递给父控件,调用父控件的invalidateChild方法,计算出自身需要绘制的参数,经过一系列传递后,最终会传递到ViewRootImpl,最终会触发performTraversals方法,开始进行View树的重绘(只绘制需要重绘的部分)
- postInvalidate 的方法与invalidate类似,不同的是postInvalidate可以在异步线程中调用,其内部有一个Handler,发送了一个消息到主线程中,通知主线程进行UI刷新。
- RequestLayout 子View调用requestLayout方法,会标记当前View及父容器,同时逐层向上提交,直到ViewRootImpl处理该事件,ViewRootImpl会调用三大流程,从measure开始,对于每一个含有标记位的view及其子View都会进行测量、布局、绘制
-
区别:
一般来说,如果确定你的View的位置属性已经不再满足于现状,需要改变,也就是LayoutParams已经发生了改变了,也就是需要父布局对它进行测量,布局,绘制 三个步骤的时候,调用requestLayout;
而invalidate/postInvalidate,则是在确定目标View(往往是自身)不需要测量,布局,只需要重新绘制自身的时候调用,所以这种情况下invalidate/postInvalidate会比requestLayout更高效。
Activity-Window-View三者的差别
- 通用比喻:Acitivty像一个工匠(控制单元),Window像窗户(承载模式),View像窗花(显示视图),LayoutInflater像剪刀,xml配置像窗花图纸
- 三者关系
- 在Activity中调用attach,创建了一个Window
- 创建的window是其子类PhoneWindow,在attach中创建了PhoneWindow
- 在Activity中调用setContentView(R.layout.xxxxx)
- 其实实质上是调用getWindow().setContentView()
- 调用phoneWindow中的setContentView方法
- 创建ParentView:作为ViewGroup的子类,实际上创建的是DecorView(作为Fraglayout的子类)
- 将制定的R.layout.xxxx进行填充,通过布局填充器进行填充(其中的parent就是DecorView)
- 调用到ViewGroup
- 调用viewGroup的remoAllView(),先将所有View移除,
- 添加新的View addView()
如何优化自定义View
- 降低刷新频率
- 减少不必要的调用invalidate()方法,避免频繁刷新,最好可以调用四个参数的invalidate()方法,刷新指定View而不是刷新整个
- 减少不必要的layout()调用,因为这个方法需要遍历整个View树来获取你真实的layout位置。如果真的必须经常性地调用,那么你可以考虑写一个特殊的ViewGroup
- 使用硬件加速
- 一些参数的初始化千万不要在onDraw里面初始化,最好的位置是构造函数里面
低版本SDK如何实现高版本api?
- 使用support包的方法
- @TargetApi
描述一次网络请求的流程
- 建立TCP连接
在HTTP工作开始之前,Web浏览器首先要通过网络与Web服务器建立连接,该连接是通过TCP来完成的,该协议与IP协议共同构建Internet,即著名的TCP/IP协议族,因此Internet又被称作是TCP/IP网络。HTTP是比TCP更高层次的应用层协议,根据规则,只有低层协议建立之后才能进行更高层协议的连接,因此,首先要建立TCP连接,一般TCP连接的端口号是80 - Web浏览器向Web服务器发送请求命令
一旦建立了TCP连接,Web浏览器就会向Web服务器发送请求命令。例如:GET/sample/hello.jsp HTTP/1.1。 - Web浏览器发送请求头信息
浏览器发送其请求命令之后,还要以头信息的形式向Web服务器发送一些别的信息,之后浏览器发送了一空白行来通知服务器,它已经结束了该头信息的发送。 - Web服务器应答
客户机向服务器发出请求后,服务器会客户机回送应答, HTTP/1.1 200 OK ,应答的第一部分是协议的版本号和应答状态码。 - Web服务器发送应答头信息
正如客户端会随同请求发送关于自身的信息一样,服务器也会随同应答向用户发送关于它自己的数据及被请求的文档。 - Web服务器向浏览器发送数据
Web服务器向浏览器发送头信息后,它会发送一个空白行来表示头信息的发送到此为结束,接着,它就以Content-Type应答头信息所描述的格式发送用户所请求的实际数据。 - Web服务器关闭TCP连接
一般情况下,一旦Web服务器向浏览器发送了请求数据,它就要关闭TCP连接,然后如果浏览器或者服务器在其头信息加入了这行代码:Connection:keep-alive
TCP连接在发送后将仍然保持打开状态,于是,浏览器可以继续通过相同的连接发送请求。保持连接节省了为每个请求建立新连接所需的时间,还节约了网络带宽。
HttpUrlConnection 和 okhttp关系
4.4之后的的 HTTP URL connection 基于 OK HTTP实现
Bitmap对象的理解(未完成)
- Bitmap像素数据
在Android 2.3.3(API 10)之前,Bitmap的像素数据的内存时分配在Native堆上的,而Bitmap对象的内存则分配在Dalvik堆上的;
由于Native堆上的内存时不受DVM管理的,如果想要回收Bitmap的所占用内存的话,那么需要调用Bitmap.recyle()方法。
而API 10之后呢,谷歌将像素数据的内存分配也移到DVM堆上,由DVM管理,因此在dvm回收前;
只需要保证Bitmap对象不被任何GC Roots强引用就可以回收这部分内存。
- looper架构
- ActivityThread,AMS,WMS的工作原理
- 自定义View如何考虑机型适配
- 自定义View的事件
- AstncTask+HttpClient 与 AsyncHttpClient有什么区别?
- LaunchMode应用场景
- AsyncTask 如何使用?
- SpareArray原理
- 请介绍下ContentProvider 是如何实现数据共享的?
- AndroidService与Activity之间通信的几种方式
- IntentService原理及作用是什么?
- 说说Activity、Intent、Service 是什么关系
- ApplicationContext和ActivityContext的区别
- SP是进程同步的吗?有什么方法做到同步?
- 谈谈多线程在Android中的使用
- 进程和 Application 的生命周期
- 封装View的时候怎么知道view的大小
- RecycleView原理
- AndroidManifest的作用与理解
三、常见的一些原理性问题
Handler机制和底层实现
-
机制:
主线程-->主线程Looper对象: 创建的时候自动在创建了属于自己的Looper对象
Note right of 主线程: 每个线程只能拥有一个looper对象
Note right of 主线程Looper对象: 自动进入循环状态
Note right of 主线程Looper对象: 每个looper可以对应很多个handler(多线程并发的条件)
主线程-->主线程MessageQueue:创建的时候自动创建了属于主线程的MessageQueue
主线程-->主线程Handler对象:创建的时候自动绑定了主线程的looper对象
子线程-->主线程MessageQueue:把自己的消息通过Handler发送到MessageQueue中
主线程MessageQueue-->主线程Looper对象:无限循环,没有message则阻塞,有就拿出来处理
主线程Looper对象-->主线程:通过looper循环把消息拿出来,进行处理
- Handler、Thread和HandlerThread的差别
- handler发消息给子线程,looper怎么启动?
- 关于Handler,在任何地方new Handler 都是什么线程下?
- ThreadLocal原理,实现及如何保证Local属性?
- 请解释下在单线程模型中Message、Handler、Message Queue、Looper之间的关系
- 请描述一下View事件传递分发机制
- Touch事件传递流程
- 事件分发中的onTouch 和onTouchEvent 有什么区别,又该如何使用?
- View和ViewGroup分别有哪些事件分发相关的回调方法
- View刷新机制
- View绘制流程
- 自定义控件原理
- 自定义View如何提供获取View属性的接口?
- Android代码中实现WAP方式联网
- AsyncTask机制
- AsyncTask原理及不足
- 如何取消AsyncTask?
- 为什么不能在子线程更新UI?
- ANR产生的原因是什么?
- ANR定位和修正
- oom是什么?
- 什么情况导致oom?
- 有什么解决方法可以避免OOM?
- Oom 是否可以try catch?为什么?
- 内存泄漏是什么?
- 什么情况导致内存泄漏?
- 如何防止线程的内存泄漏?
- 内存泄露场的解决方法
- 内存泄漏和内存溢出区别?
- LruCache默认缓存大小
- ContentProvider的权限管理(解答:读写分离,权限控制-精确到表级,URL控制)
- 如何通过广播拦截和abort一条短信?
- 广播是否可以请求网络?
- 广播引起anr的时间限制是多少?
- 计算一个view的嵌套层级
- Activity栈
- Android线程有没有上限?
- 线程池有没有上限?
- ListView重用的是什么?
- Android为什么引入Parcelable?
- 有没有尝试简化Parcelable的使用?
四、开发中常见的一些问题
- ListView 中图片错位的问题是如何产生的?
- 混合开发有了解吗?
- 知道哪些混合开发的方式?说出它们的优缺点和各自使用场景?(解答:比如:RN,weex,H5,小程序,WPA等。做Android的了解一些前端js等还是很有好处的);
- 屏幕适配的处理技巧都有哪些?
- 服务器只提供数据接收接口,在多线程或多进程条件下,如何保证数据的有序到达?
- 动态布局的理解
- 怎么去除重复代码?
- 画出 Android 的大体架构图
- Recycleview和ListView的区别
- ListView图片加载错乱的原理和解决方案
- 动态权限适配方案,权限组的概念
- Android系统为什么会设计ContentProvider?
- 下拉状态栏是不是影响activity的生命周期
- 如果在onStop的时候做了网络请求,onResume的时候怎么恢复?
- Bitmap 使用时候注意什么?
- Bitmap的recycler()
- Android中开启摄像头的主要步骤
- ViewPager使用细节,如何设置成每次只初始化当前的Fragment,其他的不初始化?
- 点击事件被拦截,但是想传到下面的View,如何操作?
- 微信主页面的实现方式
- 微信上消息小红点的原理
- CAS介绍(这是阿里巴巴的面试题,我不是很了解,可以参考博客: CAS简介)