1-3年Android开发工程师面试心得分享,精选面试题40道

一、前言

小编从事Android开发不到两年时间,最近也是面试过很多家公司,不说什么大厂。咱只能分享一下自己在面试过程中问到的问题,总结一下,希望可以帮助到您!!答案有时间会补上去,有了问题可以先自己百度一下,网上还是有很多的答案的。

二、Java基础面试题

1.Java中提供了抽象类还有接口,开发中如何去选择呢?

抽象类的设计目的,是代码复用;接口的设计目的,是对类的行为进行约束。

  • 当需要表示is-a的关系,并且需要代码复用时用抽象类
  • 当需要表示has-a的关系,可以使用接口

比如狗具有睡觉和吃饭方法,我们可以使用接口定义:

public interface Dog {
	public void sleep();
	public void eat();
}

但是我们发现如果采用接口,就需要让每个派生类都实现一次sleep方法。此时为了完成对sleep方法的复用,我们可以选择采用抽象类来定义:

public abstract class Dog {
    public void sleep(){
        //......
    }
	public abstract void eat();
}

但是如果我们训练,让狗拥有一项技能——握手,怎么添加这个行为? 将握手方法写入抽象类中,但是这会导致所有的狗都能够握手。

握手是训练出来的,是对狗的一种扩展。所以这时候我们需要单独定义握手这种行为。这种行为是否可以采用抽象类来定义呢?

public abstract Handshake{
    abstract void doHandshake();
}

如果采用抽象类定义握手,那我们现在需要创建一类能够握手的狗怎么办?

public class HandShakeDog extends Dog //,Handshake

大家都知道在Java中不能多继承,这是因为多继承存在二义性问题。

二义性问题:一个类如果能够继承多个父类,那么如果其中父类A与父类B具有相同的方法,当调用这个方法时会调用哪个父类的方法呢?

所以此时,我们就需要使用接口来定义握手行为:

public interface Handshake{
    void doHandshake();
}
public class HandShakeDog extends Dog implements Handshake

不是所有的狗都会握手,也不止狗会握手。我们可以同样训练猫,让猫也具备握手的技能,那么猫Cat类,同样能够实现此接口,这就是"has-a"的关系。

抽象类强调从属关系,接口强调功能,除了使用场景的不同之外,在定义中的不同则是:

在这里插入图片描述

2. 重载和重写是什么意思,区别是什么?

重写(Override)

重写就是重新写的意思,当父类中的方法对于子类来说不适用或者需要扩展增强时,子类可以对从父类中继承来的方法进行重写。

比如Activity是Android开发中四大组件之一。在Activity中存在各种声明周期方法:onCreate、onStart …等等。而我们应用中需要使用Activity来展示UI,那么我们会需要编写自己的类继承自Activity。

public class MainActivity extends Activity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

在上述代码中,onCreate就是被我们重写的Activity中定义方法。我们在onCreate增加了setContentView

的调用,完成了对超类Activity中对应方法的修改与扩展。

重载(Overload)

重载则是在同一个类中,允许存在多个同名方法,只要它们的参数列表不同即可。比如在Android开发中,我们会使用LayoutInflater的inflate方法加载布局,inflate方法存在多个定义,其中包括两个参数的,与三个参数的:

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
	return inflate(resource, root, root != null);
}
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
    //......
}

可以看到在两个参数的inflate方法中,会为我们调起三个参数的inflate方法,而定义第一个inflate方法的目的就是为了提供默认的attachToRoot参数值。因为Java中无法定义方法参数默认值,所以我们经常使用重载达成此目的。

3.静态内部类是什么?和非静态内部类的区别是什么?

在定义内部类时,如果内部类被static声明,则该内部类为静态内部类。

public class OuterClass{
    static class InnerClass{
        
    }
}

当内部类被static声明,那么在内部类中就无法直接使用外部类的属性。比如编写普通内部类:

public class OuterClass{
    int i;
    public class InnerClass{
        public InnerClass(){
            i = 10;
        }
    }
}

此时对OuterClass.java 进行编译,会生成:OuterClass.class 与 OuterClass$InnerClass.class 两个文件。对后者反编译我们将看到:

public class OuterClass$InnerClass {
    public OuterClass$InnerClass(OuterClass var1) {
        this.this$0 = var1;
        var1.i = 10;
    }
}

可以看到,普通内部类构造方法中实际上会隐式的传递外部类实例对象给内部类。在内部类中使用外部类的属性:i。实际上是通过外部类的实例对象:var1获取的。同时如果我们需要构建出InnerClass的实例对象,非静态内部类也无法脱离外部类实体被创建。

下面我们将InnerClass定义为static静态内部类:

public class OuterClass{
    int i;
    public static class InnerClass{
        public InnerClass(){
            //i = 10; 
        }
    }
}

此时无法使用外部类的普通成员属性:i。其对应字节码为:

public class OuterClass$InnerClass {
    public OuterClass$InnerClass() {
       
    }
}

静态内部类中不再隐式的持有外部类的实例对象。但是如果我们将属性i定义为static,那么在静态内部类中也是可以直接使用外部类的静态成员属性的,此时字节码为:

public class OuterClass$InnerClass {
    public OuterClass$InnerClass() {
        OuterClass.i = 10;
    }
}

内部静态类不需要有指向外部类的引用。但非静态内部类需要持有对外部类的引用。但是静态内部类能够直接利用new OuterClass.InnerClass() 实例化。

因此静态内部类与非静态内部类的区别有:

  1. 非静态内部类能够访问外部类的静态和非静态成员,静态类只能访问外部类的静态成员。
  2. 非静态内部类不能脱离外部类被创建,静态内部类可以。

4.String s = new String(“xxx”);创建了几个String对象?

首先代码String s = new String("xxx")中包含关键字new, 我们都知道此关键字是创建类的实例对象。JVM在运行期执行new指令因此这会在堆中创建一个String对象。

其次,在String的构造方法中传递了"xxx"字符串,此处的"xxx"是一个字符串常量。JVM会首先从字符串常量池中尝试获取其对应的引用。如果不存在,则会在堆中创建"xxx"的字符串对象,并将其引用保存到字符串常量池然后返回。

因此,如果xxx这个字符串常量不存在,则创建两个String对象;而如果存在则只会创建一个String对象。

三、高级UI面试题

1.View的绘制流程是从Activity的哪个生命周期方法开始执行的

View的绘制流程是从Activity的 onResume 方法开始执行的。
首先我们找到 onResume 在哪儿执行的,代码如下:

// ActivityThread.java
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward, String reason) {
    // 1 执行 onResume 流程
    final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);

    // 2 执行 View 的流程
    wm.addView(decor, l);
}

由上面1代码进入,我们继续跟进:

public ActivityClientRecord performResumeActivity(IBinder token, boolean finalStateRequest,
            String reason) {
    r.activity.performResume(r.startsNotResumed, reason);
}
// Activity.java
final void performResume(boolean followedByPause, String reason) {
    mInstrumentation.callActivityOnResume(this);
}
public void callActivityOnResume(Activity activity) {
    activity.onResume();
}    

到这儿我们就找到了onResume方法的执行位置。而View的绘制就是由2代码进入:wm.addView 中的wm 就是 WindowManager,但是WindowManger是一个接口,实际调用的是 WindowManagerImpl 的 addView 方法

// WindowManagerImpl.java
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
                mContext.getUserId());
}

mGlobal 是 WindowManagerGlobal 对象

// WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow, int userId) {
    root = new ViewRootImpl(view.getContext(), display);
    root.setView(view, wparams, panelParentView, userId);
}
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
            int userId) {
    requestLayout();
}

到这儿我们可以看到,通过 requestLayout 开始绘制 View。
所以通过以上分析可以知道,在调用了 onResume 生命周期方法后,开始执行 View 的绘制。更多细节,请各位去查看7.6题相关的说明。

2.Activity,Window,View三者的联系和区别

  1. Activity,Window,View分别是什么?
  • Activity是安卓四大组件之一,负责界面、交互和业务逻辑处理;

  • Window对安卓而言,是窗体的一种抽象,是顶级Window的外观与行为策略。目前仅有的实现类是PhoneWindow;

  • View是放在Window容器的元素,Window是View的载体,View是Window的具体展示;

    通过上面名词的解释以及平时开发中的实际,大家可以发现window其实没有什么实质性的意义,因为平时开发中,我们往往是在activity里面直接处理View。那么为什么还需要window呢?主要是从面向对象的角度来设计代码的结构的,类的职责应该单一,那么activity view都有它们自己的职责,所以activity管理view就需要有它独立类来进行这就是通过window进行。

  1. Window和View的关系。
  • Window是一个界面的窗口,适用于存放View的容器,即Window是View的管理者。安卓中所有的视图都是通过Window来呈现的,比如Activity、Dialog、Toast;
  • Window的添加、删除和更改是通过WindowManager来实现的,而WindowManager又是继承ViewManager。
  • WindowManager也是一个接口,它的唯一实现类是WindowManagerImpl,所以往往在管理view的时候,系统是通过WindowManagerImpl来管理view。
  • WindowManagerImpl内部的实现极其简单,它会将所有的方法全部转交给WindowManagerGlobal中的方法来进行。
  • windowManagerGlobal 通过调用 ViewRootImpl 来完成对view的操作。
  • ViewRootImpl 是视图层次结构的顶部,它实现了View与WindowManager之间所需要的协议,并且大部分的WindowManagerGlobal的内部实现是通过ViewRootImpl来进行的。
public interface ViewManager
{
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}
  1. Activity与Window的关系。
  • Activity是向用户展示一个界面,并可以与用户进行交互。我们通过分析原理发现Activity内部持有一个Window对象,用户管理View。
final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);

        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(mWindowControllerCallback);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
}
  • 从Activity的attch函数中是可以发现,新建了一个PhoneWindow对象并赋值给了mWindow。Window相当于Activity的管家,用于管理View的相关事宜。事件的分发,其实也是先交予Window再向下分发。
 public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }
  • 从上面的代码中可以发现,事件被传递到了Window的superDispatchTouchEvent方法,再接着会传递给我们的DecorView。

3.ScrollView下嵌套一个RecycleView通常会出现什么问题

考察同学对ScrollView和RecyclerView嵌套的理解,这个问题在开发中非常常见,如果我们使用了这种展示列表的方案,基本上都会遇到一些需要解决的冲突问题,所以面试官是问我们的实践情况。所以,建议大家用过这个场景实现app UI的都可以好好的总结一下,实践一下。

由于文章篇幅限制,不可能将所有面试题以文字形式把大厂面试题展示出来,本篇为大家精选了一些面试题,如果你需要这份完整版的面试笔记,大厂面试真题!可以直接点击下方卡片领取

第一章 Java方面

  • 第一节 Java基础
  • 第二节 Java集合
  • 第三节 Java多线程
  • 第四节 Java虚拟机

在这里插入图片描述

第二章 Android 方面

  • 第一节 Android 四大组件相关
  • 第二节 Android 异步任务和消息机制
  • 第三节 Android UI 绘制相关
  • 第四节 Android 性能调优相关
  • 第五节 Android 中的 IPC
  • 第六节 Android 系统 SDK 相关
  • 第七节 第三方框架分析
  • 第八节 综合技术
  • 第九节 数据结构方面
  • 第十节 设计模式
  • 第十一节 计算机网络方面
  • 第十二节 Kotlin方面

在这里插入图片描述

第三章 音视频开发高频面试题

  • 为什么巨大的原始视频可以编码成很小的视频呢?这其中的技术是什么呢?
  • 怎么做到直播秒开优化?
  • 直方图在图像处理里面最重要的作用是什么?
  • 数字图像滤波有哪些方法?
  • 图像可以提取的特征有哪些?

在这里插入图片描述

第四章 Flutter高频面试题

  • 第一节 Dart部分

    • Dart 语言的特性?
    • Dart的一些重要概念?
    • dart是值传递还是引用传递?
    • Dart 多任务如何并行的?
    • 说一下 mixin?
  • 第二节 Flutter 部分

    • Flutter 特性有哪些?
    • Flutter 中的生命周期
    • Widget 和 element 和 RenderObject 之间的关系?
    • mixin extends implement 之间的关系?
    • Flutter 和 Dart的关系是什么?

在这里插入图片描述

第五章 算法高频面试题

  • 如何⾼效寻找素数
  • 如何运⽤⼆分查找算法
  • 如何⾼效解决接⾬⽔问题
  • 如何去除有序数组的重复元素
  • 如何⾼效进⾏模幂运算

在这里插入图片描述

第六章 Android Framework方面

  • 第一节 系统启动流程面试题解析
  • 第二节 Binder面试题解析
  • 第三节 Handler面试题解析
  • 第四节 AMS面试题解析

在这里插入图片描述

第七章 企业常见174道面试题

  • 1.SD卡
  • 2.android的数据存储方式
  • 3.BroadcastReceiver
  • 4.sp频繁操作会有什么后果?sp能存多少数据?
  • 5.dvm与jvm的区别
  • 6.ART
  • 7.Activity的生命周期
  • 8.Application能不能启动Activity
  • 9.Activity的状态都有哪些
  • 10.横竖屏切换时Activity的生命周期

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值