Android面试

[toc]

Android面试题

Android面试题除了Android基础之外,更多的问的是一些源码级别的、原理这些等。所以想去大公司面试,一定要多看看源码和实现方式,常用框架可以试试自己能不能手写实现一下,锻炼一下自己。

一、Android基础知识点

四大组件是什么

  1. Activity
    其中Activity是我们最常使用的组件,也是我们最熟悉的组件,一般用于呈现页面内容,处理用户交互等等

  2. Service
    Service是一个可以运行于后台的组件,我们一般用于处理一些不需要用户知道,但是又必须比较长时间存在的操作,比如下载

  3. ContentProvider
    主要用于进程间通信,比如暴露某个APP的信息内存给予另外一个APP获取使用,比如获取联系人等等

  4. BroadcastReceiver
    广播接受者主要用于接受广播信息,像我们日常使用中可能用于两种情况:

    • APP内:界面间通信,例如退出app,可以发送自杀广播
    • APP间:可以收到第三方发出的广播,进而进行对应的响应操作。

四大组件的生命周期和简单用法

Activity
  1. 生命周期

oncreate()->onstart()->onresume()->onpause()->onstop()->ondestory()

  1. 简单用法
        /**创建App*/
        class someClass :Activity() 
        /**跳转app*/
        startActivity(Intent(this@someActivity,targetClass::class.java))
Service
  1. 生命周期
  • startService()的生命周期:
    oncreate()->onstartComment()->onstart()->onDestory()
  • bindService()的生命周期:
    oncreate()->onbind()->onUnbind()->onDestroy()
  1. 简单用法
    • startService()
      通过简单的startService()进行service启动,此后启动该Service的组件无法把控Service的生命周期,理论上此后该Service可以在后台无期限运行(实际根据情况该Service可能会在任意一个时刻被杀死,这里牵连到了另外一个知识点:Service防杀
      我们可以在onStartCommand()里面做我们要做的操作,注意Service跟Activity一样不可以做耗时操作,虽然运行anr时间比Activity多了近一倍。
    • bindService()
      通过绑定的方式启动Service
      绑定后,该Service与启动绑定操作的组件形成绑定,当组件销毁时,该Service也随着销毁。
      其中组件与Service形成一个典型的BC体系,Service相当于服务器,组件可以通过IBinder像Service 发送请求并获取回应。
  public boolean bindService(Intent service, ServiceConnection conn,
                int flags)
BroadcastReceiver(分为2种):
  1. 简单用法
    • 静态注册(常驻广播)
      在AndroidManifest.xml中进行注册,App启动的时候自动注册到系统中,不受任何组件生命周期影响,(即便应用程序已经关闭),但是 耗电占内存
    • 动态注册(非常驻广播)
      在代码中进行注册,通过IntentFilter意图过滤器筛选需要监听的广播,
      记得注销(推荐在onResume()注册,在onPause()注销),使用灵活,生命周期随组件变化
全局广播
  1. 普通广播(最常用的那种)
  2. 系统广播
    系统广播无须开发者进行发送,我们只需做好广播接收器进行接收即可,常用的几个系统广播为:
    android.net.conn.CONNECTIVITY_CHANGE 监听网络变化
    Intent.ACTION_PACKAGE_ADDED 成功安装apk
    ... ...
  3. 有序广播
    有序广播是指广播按照一定的优先级被广播接受者依次接收,代码实例
    发送广播
    定义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也可以收到这个广播信息,存在问题为:

  1. 其他app可以发送相对应的 intent-filter到我方App,导致我方错误处理
  2. 其他app可以设置对应的 intent-filter接收我方App发出的广播,导致安全性问题
    为解决这个问题,我们可以使用本地广播:
    1.方式一:
    1. 设置exported为false(默认为true),使该广播只在app内传递
    2. 广播传递和接收时,添加响应的permission
    3. 通过intent.setPackage(com.xxx.xxx)
  3. 方式二 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
  1. contentProvider 内容提供者
  2. contentresolver 内容解析器

Activity之间的通信方式

  1. 通过广播的方式进行通信
  2. 通过Intent的方式通信(startActivityForResult)
  3. 借助类的静态成员/方法进行通信
  4. 使用外部工具(sqlite/sharePreference/file/剪切板...)
  5. 全局变量(静态配置类/Application)
  6. Service(IPC模型),也就是上文的bingService()
  7. 通过EventBus/rxBus 进行通信

Activity各种情况下的生命周期

  1. 正常情况下是:onCreate()->onStart()->onResume()->onPause()->onStop()->onDestory()
  2. 被dialog遮罩情况下:
    1. 如果是自身的dialog那么不会走自己的生命周期
    2. 如果是其他组件传递的dialog,那么会走onpause()
  3. 可以看到我们前面有一个onRestart()的生命周期,这个生命周期我们不常用,具体是什么时机会被调用呢:
    1. home键,然后切换回来
    2. 跳转到另外一个activity,然后back键
    3. 从本应用跳转到另外一个应用,然后回来

... 其他

横竖屏切换的时候,Activity 各种情况下的生命周期

2种情况:

  1. 不做任何配置,生命周期重新走一遍:完整流程为:onCreate()->onStart()->onResume()->onPause()->onSaveInstanceState()->onStop()->Ondestory()->onCreate()->onStart()->onRestoreInstanceState()->onResume()。
  2. 做配置:android:configChanges="orientation|screenSize"
    横竖屏不走其他生命周期

其中onSaveInstanceState()可以保存用户数据,对应的onRestoreInstanceState()可以读取之前保存的用户数据

Activity与Fragment之间生命周期比较

activity有7个生命周期,fragment有十一个生命周期


fragment以及activity生命周期

其中他们的关系是:

  1. 创建的时候:Activity 带动 Fragment 走生命周期,表格关系为:
所属 Activity Fragment
创建操作 onCreate onAttach/onCreate/onCreateView/onActivityCreated
创建操作 onStart onStart()
  1. 销毁的时候: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,生命值周期回调方法

  1. 前台切换到后台:onPause()->onStop()
    如果正常切换回来,那么会走onRestart()->onStart()->onResume()
    如果很久没切回来,系统内存紧急被回收了,那么回来会重新走一次生命周期
  2. 弹出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的滑动?

  1. 把Fragment放到ViewPager里面去
  2. 把Fragment放到RecyclerView/ListView/GridView
    ... ...

fragment之间传递数据的方式?

  1. Intent传值
  2. 广播传值
  3. 静态调用
  4. 本地化存储传值
  5. 暴露接口/方法调用
  6. eventBus等之类的

Activity 怎么和Service 绑定?

通过BindService绑定,具体步骤
  1. 新建Activity,Service。Activity里面绑定Service。
  2. 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 对象的区别

  1. Application是全局的Context,可以用于需要长期持久的contenxt操作
  2. 而Activity的Context只是属于该Activity,如果被非法持有的话,很容易导致该Activity无法被回收,造成内存泄露

Android属性动画特性

如何导入外部数据库?

  1. 把外部建好的数据库文件放在raw中,
  2. 读入该数据库文件
  3. 写入到data/data/batabases文件夹中
  4. 正常读取

介绍下SurfaceView

SurfaceView是View的子类,与普通的View的最大区别就是内部实现了双缓冲机制:就是使用主线程来负责UI的显示和渲染线程做UI的渲染,这样2个线程交替进行,这样做使得页面显示内容的速度非常快,但是同样的,也加大了对内存和cpu的开销。

序列化的作用,以及Android两种序列化的区别

  1. Serializable接口
    是一个空接口,为对象提供标准的序列化和反序列化操作。
    其中有一个long类型的值为serialVersionUID,我们开发人员很少用到这个,但是官方推荐是最好可以声明这个属性,主要用于校验序列化和反序列化。

serialVersionUID的详细工作过程是这样的:序列化的时候系统会把当前类的serialVersionUID写入序列化的二进制文件中,当反序列化的时候系统会检测文件中的serialVersionUID是否和当前类的serialVersionUID一致,如果一致就说明序列化的类的版本和当前类的版本是相同的,这个时候可以成功反序列化;否则说明当前类和反序列化的类相比发生了某些变化,比如成员变量的数量、类型发生了变化,这个时候是无法正常反序列化的。

  1. 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
  1. 两者区别
区别 Serializable Parcelable
所属API JAVA API Android SDK API
原理 序列化和反序列化过程需要大量的I/O操作 序列化和反序列化过程不需要大量的I/O操作
开销 开销大 开销小
效率 很高
使用场景 序列化到本地或者通过网络传输 内存序列化

二、Android源码相关分析

Android动画框架实现原理

Android各个版本API的区别

其实只需要知道比较著名的几个就好了

  1. android 5.0引入material design风格
  2. android 6.0引入权限动态管理
  3. android 7.0引入多窗口支持等
  4. android 8.0引入画中画等

Requestlayout,onlayout,onDraw,DrawChild区别与联系(个人看法,不一定正确)

  1. RequestLayout():
    子View调用requestLayout方法,会标记当前View及父容器,同时逐层向上提交,直到ViewRootImpl处理该事件,ViewRootImpl会调用三大流程,从measure开始,对于每一个含有标记位的view及其子View都会进行测量、布局、绘制。
  2. onLayout()只是一个调度方法,实质调用的是layout(int l, int t, int r, int b)方法,其中的l,t,r,b分别表示该参数相对于父参数对应参数的位置,当需要重新布局时候,会依次上传,直至rootView
  3. onDraw同onLayout()
  4. DrawChild(Canvas canvas, View child, long drawingTime)也是个调度方法,调用了child.draw()方法
  5. 联系:Requestlayout会调用View树的重绘机制,会分别调用需要重绘View的测量,布局,绘制3个过程,而且其中onlayout(),onDraw()会被分别依次调用,调用Requestlayout()方法的view会依次上报父View分别进行测量,布局,绘制。而绘制的时候,子view会被父View通过DrawChild()启动目标View的绘制。

invalidate和postInvalidate,以及他们和RequestLayout的区别及使用

  1. invalidate 该方法的调用会引发View树的重绘,一般用于View内部调用或者是需要刷新界面的时候,该方法有多个重载方法,但是最终都会调用到invalidateInternal方法,在该方法内部,会经过一系列的判断之类的,判断该View是否需要重绘,然后给View设置标识位,然后把需要重绘的区域传递给父控件,调用父控件的invalidateChild方法,计算出自身需要绘制的参数,经过一系列传递后,最终会传递到ViewRootImpl,最终会触发performTraversals方法,开始进行View树的重绘(只绘制需要重绘的部分)
  2. postInvalidate 的方法与invalidate类似,不同的是postInvalidate可以在异步线程中调用,其内部有一个Handler,发送了一个消息到主线程中,通知主线程进行UI刷新。
  3. RequestLayout 子View调用requestLayout方法,会标记当前View及父容器,同时逐层向上提交,直到ViewRootImpl处理该事件,ViewRootImpl会调用三大流程,从measure开始,对于每一个含有标记位的view及其子View都会进行测量、布局、绘制
  4. 区别:


    Android面试_第1张图片
    3者区别

    一般来说,如果确定你的View的位置属性已经不再满足于现状,需要改变,也就是LayoutParams已经发生了改变了,也就是需要父布局对它进行测量,布局,绘制 三个步骤的时候,调用requestLayout;
    而invalidate/postInvalidate,则是在确定目标View(往往是自身)不需要测量,布局,只需要重新绘制自身的时候调用,所以这种情况下invalidate/postInvalidate会比requestLayout更高效。

Activity-Window-View三者的差别

  1. 通用比喻:Acitivty像一个工匠(控制单元),Window像窗户(承载模式),View像窗花(显示视图),LayoutInflater像剪刀,xml配置像窗花图纸
  2. 三者关系
    1. 在Activity中调用attach,创建了一个Window
    2. 创建的window是其子类PhoneWindow,在attach中创建了PhoneWindow
    3. 在Activity中调用setContentView(R.layout.xxxxx)
    4. 其实实质上是调用getWindow().setContentView()
    5. 调用phoneWindow中的setContentView方法
    6. 创建ParentView:作为ViewGroup的子类,实际上创建的是DecorView(作为Fraglayout的子类)
    7. 将制定的R.layout.xxxx进行填充,通过布局填充器进行填充(其中的parent就是DecorView)
    8. 调用到ViewGroup
    9. 调用viewGroup的remoAllView(),先将所有View移除,
    10. 添加新的View addView()

如何优化自定义View

  1. 降低刷新频率
    1. 减少不必要的调用invalidate()方法,避免频繁刷新,最好可以调用四个参数的invalidate()方法,刷新指定View而不是刷新整个
    2. 减少不必要的layout()调用,因为这个方法需要遍历整个View树来获取你真实的layout位置。如果真的必须经常性地调用,那么你可以考虑写一个特殊的ViewGroup
  2. 使用硬件加速
  3. 一些参数的初始化千万不要在onDraw里面初始化,最好的位置是构造函数里面

低版本SDK如何实现高版本api?

  1. 使用support包的方法
  2. @TargetApi

描述一次网络请求的流程

  1. 建立TCP连接
    在HTTP工作开始之前,Web浏览器首先要通过网络与Web服务器建立连接,该连接是通过TCP来完成的,该协议与IP协议共同构建Internet,即著名的TCP/IP协议族,因此Internet又被称作是TCP/IP网络。HTTP是比TCP更高层次的应用层协议,根据规则,只有低层协议建立之后才能进行更高层协议的连接,因此,首先要建立TCP连接,一般TCP连接的端口号是80
  2. Web浏览器向Web服务器发送请求命令
    一旦建立了TCP连接,Web浏览器就会向Web服务器发送请求命令。例如:GET/sample/hello.jsp HTTP/1.1。
  3. Web浏览器发送请求头信息
    浏览器发送其请求命令之后,还要以头信息的形式向Web服务器发送一些别的信息,之后浏览器发送了一空白行来通知服务器,它已经结束了该头信息的发送。
  4. Web服务器应答
    客户机向服务器发出请求后,服务器会客户机回送应答, HTTP/1.1 200 OK ,应答的第一部分是协议的版本号和应答状态码。
  5. Web服务器发送应答头信息
    正如客户端会随同请求发送关于自身的信息一样,服务器也会随同应答向用户发送关于它自己的数据及被请求的文档。
  6. Web服务器向浏览器发送数据
    Web服务器向浏览器发送头信息后,它会发送一个空白行来表示头信息的发送到此为结束,接着,它就以Content-Type应答头信息所描述的格式发送用户所请求的实际数据。
  7. Web服务器关闭TCP连接
    一般情况下,一旦Web服务器向浏览器发送了请求数据,它就要关闭TCP连接,然后如果浏览器或者服务器在其头信息加入了这行代码:Connection:keep-alive
    TCP连接在发送后将仍然保持打开状态,于是,浏览器可以继续通过相同的连接发送请求。保持连接节省了为每个请求建立新连接所需的时间,还节约了网络带宽。

HttpUrlConnection 和 okhttp关系

4.4之后的的 HTTP URL connection 基于 OK HTTP实现

Bitmap对象的理解(未完成)

  1. 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机制和底层实现

  1. 机制:


    Android面试_第2张图片
    时序图
主线程-->主线程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简介)

你可能感兴趣的:(Android面试)