IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android面试指南(二),全网独家首发 -> 正文阅读

[移动开发]Android面试指南(二),全网独家首发

ActivityA跳转到ActivityB,再按back键返回ActivityA,生命周期情况?

ActivityA跳转到ActivityB:onPauseA()----->onCreateB()----->onStartB()----->onResumeB()----->onStopA()

ActivityB按back键返回ActivityA:

onPauseB()----->onRestartA()----->onStartA()----->onResumeA()----->onStopB()----->onDestoryB()

如果ActivityB是窗口Activity呢?

ActivityA跳转到ActivityB:onPauseA()----->onCreateB()----->onStartB()----->onResumeB()

ActivityB按back键返回ActivityA:onPauseB()----->onResumeA()----->onStopB()----->onDestoryB()

Activity的生命周期会受Dialog影响吗?

不会,Activity生命周期不会随Dialog的显示而变化

Activity的生命周期受AMS调用,而dialog不是Activity,所以不受AMS控制,所以不会触发Activity的生命周期

Service

Service有几种创建方式?有什么区别?

  1. startService(),在不需要的时候stopService()
  2. bindService(),与生命周期相绑定,在销毁的时候进行回收unbind()

生命周期

如何理解IntentService?生命周期是什么?HandlerThread 是什么?

intentService继承自Service,持有Service的功能,同时,他是一个处理异步操作的类,当异步执行结束后会自动关闭intentService,多次执行startService(),只是执行onStartCommand方法,将消息加入到消息队列中。

其本质就是启动了一个类似于主线程Handler的机制去维护异步操作。

生命周期:onStartCommand()中执行onStart()方法,在onstart()方法中添加handler.sendMessage()方法

HandlerThread:就是将Handler+looper进行封装,允许直接在子线程中使用handler的一套逻辑。

IntentService更像是一个后台线程,但是他又是一个服务,不容易被回收,这是他的优点

JobIntentService

是IntentService的子类,在android 8.0(26)以上,IntentService的所有后台执行任务都会受到限制约束,所以要使用JobIntentService。

service不能使用后台服务,需要使用ContextCompat.startForegroundService启动前台服务,这样就会启动一个notification,对用户来说体验不是很好,所以就要使用 JobIntentService启动一个后台服务

在使用JobIntentService的时候不需要startService,stopService,在需要的时候调用

DownLoadJobIntentService.enqueueWork(MainActivity.this,DownLoadJobIntentService.class,jobId(8),intent); 

而后会执行onHandleWork方法中的逻辑,执行完毕后自动销毁

onStartCommand中三个回调分别是什么?

  • START_NOT_STICKY:Service被回收后不做处理
  • START_STICKY:Service在被回收后,重新创建Service,但是不保存intent
  • START_REDELIVER_INTENT:Service在被回收后,重新创建Service,保存intent
  • START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,但不保证服务被kill后一定能重启。

Service保活

  1. 设置成前台服务,绑定Notification, startForeground(1, getNotification());
  2. 单独设置为服务进程
  3. onStartCommand返回START_STICKY,START_REDELIVER_INTENT,保证服务被干掉后重启
  4. 在onDestory发送一个广播,广播接收器接收广播后拉起服务
  5. 添加系统广播拉起
  6. 提升服务的优先级

Fragment

生命周期

FragmentPagerAdapter和FragmentStatePagerAdapter的区别?

FragmentPagerAdapter:切换页面只是将Fragment分离,适合Fragment较少的情况不影响内存

FragmentStatePagerAdapter:切换页面将Fragment回收,适合Fragment较多的情况,这样不会消耗太多内存资源

Fragment的3种切换方式

  1. add方法:只是重新回到fragment
  2. replace方法:每次都会重新构建fragment

为什么不能用Fragment的构造函数进行传参?有什么劣势,应该怎么办?为什么?

Fragment在异常崩溃后重建时,默认会调用Fragment无参构造,这样会导致Fragment中的有参构造的值不会被执行,这样数据就会异常

Fragment中调用setArguments来传递参数,在Activity构造Fragment时会通过反射午餐构造实例化一个新的Fragment,并且给mArguments初始化为原先的值

Fragment的重建在那个生命周期中?

在FragmentActivity的onSaveInstanceState中做存储,将Framgent通过序列化Parcelable进行存储,在Activity的onCreate中进行恢复

当配置发生变化时,Activity进入销毁过程,FragmentManager先销毁队列中Fragment的视图,然后检查每个Fragment的retainInstance属性。如果retainInstance为false,FragmentManager会销毁该Fragment实例;如果retainInstance为true,则不会销毁该Fragment实例,Activity重建后,新的FragmentManager会找到保留的Fragment并为其创建视图。

BroadCastReceiver

简述广播的启动方式和区别

  • 静态注册:在AndroidManifest中注册,常驻型广播
  • 动态注册:使用intentFilter过滤广播,registerReceiver注册广播,跟随生命周期

Android8.0以上部分广播不允许静态注册

无序广播和有序广播的区别

  • 无序广播:所有广播接收器都可以获得,不可以拦截,不可以修改
  • 有序广播:按照优先级向下传递,可拦截广播,修改广播

本地广播和全局广播

本地广播接收器只接收本地广播,减少应用外广播干扰,高效 androidx中1.1.0-alpha01中弃用本地广播,官方推荐该用LiveData或响应式流

IPC机制

简述android中的IPC机制

进程间通信

架构:Client/Server架构,Binder机制,之间通过代理接口通信

client,server,serverManager

AndroidManifest中指定Android:process属性

  • 包名:remote为应用私有进程,其他应用不可访问
  • 包名.remote为全局进程,其他应好通过ShareUID可以和他跑在同一个进程

多进程带来的问题:四大组件共享数据失败,每个进程会单独开辟内存空间存储信息

  1. 静态成员和单例模式完全失效
  2. 线程同步机制完全失效
  3. SharedPreferences可靠性下降,不支持多进程
  4. Application会多次创建

Serializable和parcelable区别

serializable:java自带,反射后产生大量临时变量,容易引起gc,主要用于持久化存储和网络传输的序列化

parcelable:android专用,性能强,将完整对象分解为部分对象,每一部分进行intent传输,可用于ipc,内部实现Bundle,主要用于内存的序列化

Android为什么引入Parcelable?

  1. serializable通过反射,性能不好,
  2. serializable反射产生大量临时变量,容易gc,导致内存抖动
  3. serializable使用了大量的IO操作,也影响了耗时

parcelable使用复杂,但高效,适用于内存序列化

Parcelable一定比Serializable快吗?

单论内存中的传输速度,Parcelable一定快于Serializable,但是Parcelable没有缓存的概念 Serializable存在缓存,会将解析过的内容放置在HandleTable,下次解析到同一类型的对象时就可以直接复用

为什么java使用Serializable序列化对象,而不是json或者xml?

因为历史遗留问题,在json和xml出来之前,java已经设计了Serializable,对于Java的庞大体系,并不容易修改这个问题。java官方文档也推荐使用json库,因为他简单、易读、高效

简析Binder机制

在Android通信中并不是所有的进程通信都使用Binder,当fork()进程时,使用的是Socket()通信,因为fork不允许多线程,Binder是多线程模式,所以不被允许

进程空间划分

一个进程空间分为用户空间内核空间

用户空间:数据独享,其他进程无法访问

内核空间:数据共享,其他进程可以访问

所有的进程共用1个内核空间

如何看待ServiceManager?

ServiceManager管理系统中所有的服务,服务需要使用时都要在ServiceManager中进行注册,他的存在类似于DNS,提供client访问某一个服务的查询。

Binder原理

binder驱动属于进程中的内核空间,即共享空间,在client发起请求时,需要将数据从用户空间拷贝到内核空间,binder通过传输内核空间中数据存储的引用映射给服务端,供服务端调用,服务端处理后,将返回值放在内核空间,通过binder传递引用映射给客户端进行处理

img

简述通信流程

总体通信流程就是:

  • 客户端通过代理对象向服务器发送请求。
  • 代理对象通过Binder驱动发送到服务器进程
  • 服务器进程处理请求,并通过Binder驱动返回处理结果给代理对象
  • 代理对象将结果返回给客户端。
详细的通信过程
  • 服务端跨进程的类都要继承Binder类,所以也就是服务端对应的Binder实体。这个类并不是实际真实的远程Binder对象,而是一个Binder引用(即服务端的类引用),会在Binder驱动里还要做一次映射。
  • 客户端要调用远程对象函数时,只需把数据写入到Parcel,在调用所持有的Binder引用的transact()函数
  • transact函数执行过程中会把参数、标识符(标记远程对象及其函数)等数据放入到Client的共享内存,Binder驱动从Client的共享内存中读取数据,根据这些数据找到对应的远程进程的共享内存。
  • 然后把数据拷贝到远程进程的共享内存中,并通知远程进程执行onTransact()函数,这个函数也是属于Binder类。
  • 远程进程Binder对象执行完成后,将得到的写入自己的共享内存中,Binder驱动再将远程进程的共享内存数据拷贝到客户端的共享内存,并唤醒客户端线程。

通过Binder将客户端,服务端的共享内存中的数据进行读写,放入对方的共享内存中,并通知。

Binder在Android中的应用?
  • 系统服务及四大组件的启动调用工作:系统服务是通过getSystemService获取的服务,内部也就是通过ServiceManager。例如四大组件的启动调度等工作,就是通过Binder机制传递给ActivityManagerService,再反馈给Zygote。而我们自己平时应用中获取服务也是通过getSystemService(getApplication().WINDOW_SERVICE)代码获取。
  • AIDL(Android Interface definition language)。例如我们定义一个IServer.aidl文件,aidl工具会自动生成一个IServer.java的java接口类(包含Stub,Proxy等内部类)。
  • 前台进程通过bindService绑定后台服务进程时,onServiceConnected(ComponentName name, IBinder service)传回IBinder对象,并且可以通过IServer.Stub.asInterface(service)获取IServer的内部类Proxy的对象,其实现了IServer接口。

为什么选择Binder机制?他的优势是什么?

  1. 性能高,效率高:
    传统的IPC(socket,管道,消息队列)需要拷贝两次内存,Binder只需要拷贝一次内存、共享内存不需要拷贝数据,只需要传递引用
  2. 安全性好:
    C/S通信添加UID/PID,方便验证,安全机制完善。
  3. 利用C/S架构,通过多线程控制一个服务端多个客户端的情况

Android中IPC的几种方式详细分析与优缺点分析

  1. Bundle
  2. 文件共享
  3. Messenger:内部实现AIDL机制,c/s架构,通过handler接收message对象
  4. AIDL
  5. ContentProvider
  6. Binder连接池

Handler

Android Handler机制之总目录

Android面试题:Handler

其实并不是每一次添加消息时,都会唤醒线程。当该消息插入到队列头时,会唤醒该线程;如果有延迟消息,插入到头部,也会唤醒线程后在休眠

一句话概括Handler,并简述其原理

android中用于主线程和子线程之间通信的工具

主要包含Handler,Looper,MessageQueue,ThreadLocal.

Handler:封装了消息的发送和接收looper分发过来的Message

Looper:协调Handler和MessageQueue之间的桥梁,Looper的作用是循环从MessageQueue中取出message,并分发

给相应的Handler,Handler则存储在Message中的target中

message:单节点,存储handler传输的数据

MessageQueue:内部结构为单链表,由Looper创建,具体代码为Looper.prepare();先进先出原则(队列),根据 Message.when进行插入队列(队列中是按时间执行顺序排序)

ThreadLocal:负责存储和获取本线程的Looper

handler.sendMessage(message)将message发送到MessageQueue,MessageQueue执行enqueueMessage()方法入队,Looper执行looper.loop()方法从MessageQueue中取出message,执行message.target.dispatchMessage(message)方法将消息发送到Handler中,在handleMessage()方法中拿到回调

Looper.loop()是在主线程的死循环,为什么没有造成线程阻塞?

真正的ANR是在生命周期的回调中等待的时间过长导致的,深层次的讲,就是Looper.loop()没有及时取出消息进行分发导致的。一旦没有消息,Linux的epoll机制则会通过管道写文件描述符的方式来对主线程进行唤醒与沉睡,Android里调用了linux层的代码实现在适当时会睡眠主线程。

MessageQueue包含jni调用,无消息时,通知epoll休眠,来消息时,线程启动

looper.loop()中循环,判空退出怎么理解?

 public static void loop() {
       ....
        for (;;) {
            Message msg = queue.next(); // might block(可能会阻塞)
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            } 

在queue.next中,会通过jni调用,通过Linux的epoll机制则会通过管道写文件描述符的方式来对主线程进行唤醒与沉睡,只有当应用程序退出时,才会执行if语句退出循环。

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

因为如果要在子线程中更新UI,势必要考虑线程安全,加锁机制,这样很耗时,不加锁又很容易发生错误,这些错误是致命的,所以在设计时只允许UI线程更新UI,避免这些错误。

真的不能在子线程更新UI吗?

ViewRootImpl中会进行通过checkThread()进行线程检测

public ViewRootImpl(Context context, Display display) {
        ......
        mThread = Thread.currentThread();
        ......
}
......

void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    } 

由此得出:viewRootImpl在那个线程被初始化,就会在那个线程更新UI,大部分情况下,ViewRootImpl都是在UI线程中初始化的,所以只能在UI线程更新UI,部分情况下可以在子线程更新UI(比如Dialog是在addView中初始化ViewRootImpl)

SurfaceView可以在子线程中更新

ViewRootImpl是什么时候被创建的?

在Acitivty中,ViewRootImpl是在onResume中创建的,所以在onCreate中进行子线程更新是可以绕过checkThread()检测的。

一个Thread中可以有几个Looper?几个Handler

一个Thread中只有一个Looper,可以存在无数个Handler,但是使用MessageQueue都是同一个,也就是一个Looper

可以在子线程直接new一个Handler吗?那该怎么做?

thread= new Thread(){
            @Override
            public void run() {
                super.run();
                //创建Looper,Looper再创建MessageQueue
                Looper.prepare();
                //新建Handler
                handler=new Handler();
                //循环取出消息并执行
                Looper.loop();
            }
        }.start(); 

需要创建Looper,Looper会创建MessageQueue,循环从MessageQueue中取消息。

Message可以如何创建?哪种效果更好,为什么?

享元模式

数据重复利用

  1. Message msg = new Message();
  2. Message msg2 = Message.obtain();
  3. Message msg1 = handler1.obtainMessage();

2,3从整个Messge池中返回一个新的Message实例,通过obtainMessage能避免重复Message创建对象。

所以2,3都可以避免重复创建Message对象,所以建议用第二种或者第三种任何一个创建Message对象。

messge就是一个节点,存在就是一条链表,链表中存储的都是可以复用的message,在handleMessage和callback

方法执行完成后执行message.recycle()方法,进行信息重置后加入闲置链表头部中,每次调用obtain方法会从闲置链表中取出头节点,如果闲置链表为空,则新建message。

Message缓存池大小为50

使用Hanlder的postDelay()后消息队列会发生什么变化?

postDelay()内部调用sendMessageDelayed()

public final boolean sendMessageDelayed(Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    } 

时间:SystemClock.uptimeMillis() + delayMillis

SystemClock.uptimeMills()是从开机到现在的时间,不使用currentMills,因为其是可变的,uptimeMills()期间不包括休眠的时间,是一个相对时间

  1. Handler.postDelayed(Runnable r, long delayMillis)
  2. Handler.sendMessageDelayed(getPostMessage?, delayMillis)
  3. Handler.sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)
  4. Handler.enqueueMessage(queue, msg, uptimeMillis)
  5. MessageQueue.enqueueMessage(msg, uptimeMillis)

消息入队的时候会根据when判断时间,最终按照时间大小排序,时间短的在链表头,时间长的在链表尾部。

案例如下:

  1. postDelay()一个10秒钟的Runnable A、消息进队,MessageQueue调用nativePollOnce()阻塞,Looper阻塞;
  2. 紧接着post()一个Runnable B、消息进队,判断现在A时间还没到、正在阻塞,把B插入消息队列的头部(A的前面),然后调用nativeWake()方法唤醒线程;
  3. MessageQueue.next()方法被唤醒后,重新开始读取消息链表,第一个消息B无延时,直接返回给Looper;
  4. Looper处理完这个消息再次调用next()方法,MessageQueue继续读取消息链表,第二个消息A还没到时间,计算一下剩余时间(假如还剩9秒)继续调用nativePollOnce()阻塞;
  5. 直到阻塞时间到或者下一次有Message进队;

同步消息、异步消息和同步屏障消息是什么?具体应用场景是什么?

同步消息:handler默认无参构造的形式是同步消息

异步消息:async传入true,则为异步消息

屏障消息:msg.target == null,使用postSyncBarrier()方法打开同步屏障,导致同步消息不执行,优先执行异步消息,规则同同步消息一样,当执行完毕后关闭同步屏障。

应用场景:在view的更新过程中,draw,requestLayout、invalidate中都用到这个方法,系统会优先处理这些异步消息,等处理结束后再处理同步消息。这样可以优先处理我们指定的系统消息。

postSyncBarrier()该方法为私有方法,所以api不允许我们在开发中调用,我们只要知道原理就好了

调用该方法,会直接发送一个屏障消息进入messageQueue,则队列头部为屏障消息

ThreadLocal,谈谈你的理解

跟HashMap功能类似,为什么不直接用HashMap呢? 原因:

  1. HashMap太大了,太臃肿了。ThreadLocal的key值只有Thread,value为looper,而HashMap的key值则可以 是string、int等数据类型,我们可以不用考虑这些数据类型;
  2. 线程隔离:我们的线程是系统中唯一的,用ThreadLocal来管理这些唯一的线程和其对应的value值会非常方便,
  3. ThreadLocal参照了HashMap,简化了HashMap,便于我们使用。
  4. HashMap线程不安全

ThreadLocal的理解

为什么子线程中不能使用Handler,而UI线程可以?

UI线程就是ActivityThread,他在初始化的时候创建了Looper,MessageQueue,所以可以直接使用Handler,而新创建的子线程没有创建Looper,所以创建了就可以使用了

Handler的构造方法中使用Looper.myLooper()获取了looper,但是在子线程中并没有looper

Handler如何引起内存泄露?怎么解决?

非静态内部类或匿名内部类默认持有外部类的引用,当外部类被回收时,因为内部类持有外部类的引用,导致外部类不能被回收,造成内存泄露。

  1. Activity销毁时及时清理消息队列;
  2. 自定义静态Handler类+弱引用。

MessageQueue.next()会因为发现了延迟消息,而进行阻塞。那么为什么后面加入的非延迟消息没有被阻塞呢?

首先非延时消息会入队,并且插入链表头,这时唤醒线程,进行循环取出message,非延时消息出队,到延迟消息后,如果事件未到,触发next的阻塞机制,如果时间到了,取出message,执行消息

Handler延时机制保时吗?

不保时

 chatIflyHandler.post(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        chatIflyHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                Log.e("handler","执行延时1s操作");
            }
        },1000); 

首先一个非延时消息入队,紧接着一个延时消息入队,执行第一个非延时消息,用时3s后执行延时消息,对比when,然后直接执行,测试总共耗时3420ms

Handler的入队机制是线程安全的(synchronized)

messageQueue.equeue(){
    synchronized(this){
        ......
    }
} 

如何精确计时?

  • 使用timer(子线程处理TimerThread)

  • 误差补偿算法(TextClock控件方法)

private final Runnable mTicker = new Runnable() {
        public void run() {
            onTimeChanged();

            long now = SystemClock.uptimeMillis();
            long next = now + (1000 - now % 1000);

            getHandler().postAtTime(mTicker, next);
        }
    }; 

整秒数执行,当上次执行累积到1200,在下次执行时,通过next的计算后保证下次执行的时间不被累加到2200,而是同样在2000

IdleHandler是什么?用处是什么?

messageQueue中有一个addIdleHandler()方法,添加IdleHandler接口

  • 添加时messageQueue不为空,则在线程休眠(没有消息,延时消息)时回掉方法
  • 添加时messageQueue为空,则当时不会触发回掉,当线程被唤醒时才会执行

就是在启用IdleHandler的时候,如果线程处于休眠状态,要等到下次休眠状态才会生效。如果不是休眠状态,则下一次休眠立即生效。

启用IdleHandler后,主线程下次休眠时会通知

 Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
            @Override
            public boolean queueIdle() {
               //do something
                return false;
            }
        }); 

如果return true,则表示这个IdleHandler可多次使用
如果return false,则表示这个IdleHandler只能使用一次

主线程的Looper何时退出?能否手动退出?

在app退出或者异常终止时,会退出Looper。在正常退出时,ActivityThread主线程中的mH(Handler)会接收到回调信息,调用quit()方法,强制退出

//ActivityThread.java
case EXIT_APPLICATION:
    if (mInitialApplication != null) {
        mInitialApplication.onTerminate();
    }
    Looper.myLooper().quit();
    break; 
  • Looper.quit():调用后直接终止Looper,不在处理任何Message,所有尝试把Message放进消息队列的操作都会失败,比如Handler.sendMessage()会返回 false,但是存在不安全性,因为有可能有Message还在消息队列中没来的及处理就终止Looper了。

  • Looper.quitSafely():调用后会在所有消息都处理后再终止Looper,所有尝试把Message放进消息队列的操作也都会失败。

当尝试在主线程手动退出looper时,会报错:

Caused by: java.lang.IllegalStateException: Main thread not allowed to quit.
### 面试复习路线,梳理知识,提升储备

自己的知识准备得怎么样,这直接决定了你能否顺利通过一面和二面,所以在面试前来一个知识梳理,看需不需要提升自己的知识储备是很有必要的。

关于知识梳理,这里再分享一下我面试这段时间的复习路线:(以下体系的复习资料是我从各路大佬收集整理好的)

**[CodeChina开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》](https://codechina.csdn.net/m0_60958482/android_p7)**

* **架构师筑基必备技能**
* **Android高级UI与FrameWork源码**
* **360°全方面性能调优**
* **解读开源框架设计思想**
* **NDK模块开发**
* **微信小程序**
* **Hybrid 开发与Flutter**

![](https://img-blog.csdnimg.cn/img_convert/1379668d6f47b8ccc148f457a7ec2804.png)

知识梳理完之后,就需要进行查漏补缺,所以针对这些知识点,我手头上也准备了不少的电子书和笔记,这些笔记将各个知识点进行了完美的总结:

![Android开发七大模块核心知识笔记](https://img-blog.csdnimg.cn/img_convert/78c0c9212054e56ecd5ff5d3b1a3ac0a.png)

**《960全网最全Android开发笔记》**

![](https://img-blog.csdnimg.cn/img_convert/998108ff340d5d5adf80571e3455d001.png)

**《379页Android开发面试宝典》**

历时半年,我们整理了这份市面上最全面的安卓面试题解析大全
包含了腾讯、百度、小米、阿里、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。熟悉本文中列出的知识点会大大增加通过前两轮技术面试的几率。

如何使用它?

1.可以通过目录索引直接翻看需要的知识点,查漏补缺。
2.五角星数表示面试问到的频率,代表重要推荐指数

![](https://img-blog.csdnimg.cn/img_convert/a96339d11bb41086d7b18aec2d68eb21.png)

**《507页Android开发相关源码解析》**

只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。

mg-zTEe6jI4-1630396339093)]

知识梳理完之后,就需要进行查漏补缺,所以针对这些知识点,我手头上也准备了不少的电子书和笔记,这些笔记将各个知识点进行了完美的总结:

[外链图片转存中...(img-sTClUfwk-1630396339093)]

**《960全网最全Android开发笔记》**

[外链图片转存中...(img-EYAi7MSY-1630396339094)]

**《379页Android开发面试宝典》**

历时半年,我们整理了这份市面上最全面的安卓面试题解析大全
包含了腾讯、百度、小米、阿里、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。熟悉本文中列出的知识点会大大增加通过前两轮技术面试的几率。

如何使用它?

1.可以通过目录索引直接翻看需要的知识点,查漏补缺。
2.五角星数表示面试问到的频率,代表重要推荐指数

[外链图片转存中...(img-PMvpZdk4-1630396339095)]

**《507页Android开发相关源码解析》**

只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。

真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-09-01 12:02:26  更:2021-09-01 12:04:41 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年10日历 -2024/10/24 16:23:08-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码