Android _ 资源、知识总结链接目录 + 框架软技能集 面试题集Android零碎知识点 & 业界讯息

Android面试题整理

2018-09-18  本文已影响299人  gybguohao

Android方面:

1、View事件分发机制
2、资源加载原理
3、内存优化(内存泄漏、内存溢出),如何查找内存泄漏,如何优化
内存泄露:是指程序在申请内存后,无法释放本应该被回收的内存。内存泄露的堆积就会导致内存溢出。

内存溢出:是指程序在申请内存时,没有足够的内存空间供其使用。

查找内存泄露:
    1、使用AS自带的内存分析界面,可以查看内存使用情况(蓝色区域表示程序使用的内存,灰色区域表示空闲内存)

内存泄露的原因以及解决方法:
    1、静态的上下文,比如static Context context,应该尽量避免静态上下文的使用;
    2、未取消注册或者监听,比如BrocastReceiver和EventBus需要在Activity的onDestroy中取消注册;
    3、资源没有关闭或者释放,比如IO、File流或者Cursor在操作完成后没有及时关闭;
    4、耗时任务或者属性动画没有取消,比如属性动画需要在Activity的onDestroy中取消;
    5、WebView造成内存泄露,WebView也需要在在Activity的onDestroy中调用它的destroy()方法
4、Activity启动模式及区别
有以下四种启动模式:standard、singleTop、singleTask、singleInstance。

1、standard标准模式
也是系统默认模式。
    在该模式下,每次启动一个Activity都会创建一个新的实例,不管这个实例是否已经存在。并且谁启动了这个
    Activity,那么这个Activity就运行在启动它的那个Activity所在的栈中。比如OneActivity启动了
    TwoActivity(TwoActivity是标准模式),则TwoActivity就会进入到OnaActivity所在的栈中。

2、singleTop栈顶模式
具体分为以下两种情况:
    2.1、如果被启动的Activity已经存在且位于栈顶,那么该Activity就不会重建,而是调用其onNewIntent()
    方法重用该实例。
    2.2、如果被启动的Activity已经存在但不是位于栈顶,那么就会重新创建该Activity的实例。

3、singleTask栈内模式
具体分为以下三种情况:
    3.1、如果被启动的Activity所需的任务栈不存在,就会先创建该Activity所需的任务栈,然后再创建该
    Activity的实例并将它加入到任务栈中。
    3.2、如果被启动的Activity所需的任务栈存在且该任务栈中不存在该Activity的实例,就会创建该Activity
    的实例并将它加入到任务栈中。
    3.3、如果被启动的Activity所需的任务栈存在且该任务栈中存在该Activity的实例。则有以下两种情况:
        3.3.1、如果该Activity位于任务栈的栈顶,则该Activity不会重建,系统会调用其onNewIntent()方法
        重用该实例;
        3.3.2、如果该Activity不是位于任务栈的栈顶,那么该Activity不会重建,系统会调用其
        onNewIntent()方法重用该实例并且清除任务栈中该Activity之上所有的Activity实例。

4、singleInstance单例模式
该模式具备singleTask所有特性。
具体分为以下两种情况:
    4.1、如果被启动的Activity不存在,系统先会创建一个全新的任务栈,然后再创建该Activity实例并将它加入
    到新的任务栈栈顶。
    4.2、如果被启动的Activity已经存在,那么无论它位于哪个应用程序,哪个任务栈,系统都会把该Activity
    所在的任务栈切换前台,从而使该Activity显示出来。
注意:采用singleInstance加载模式的Activity总是位于任务栈栈顶,并且该任务栈中只包含该Activity实例。
5、进程保活方法
1、提高进程的优先级
2、利用广播拉活Activity
6、Activity启动过程
7、动画
1、Android的动画可以分为三种:View动画、帧动画、属性动画。
8、UI(同一张图片放在不同dpi中,加载内存的大小有何不同)
9、Handler的原理,手写两个子线程Handler通信,loop wait时如何唤醒的
Handler原理:
    Handler主要是由Looper和MessageQuene构建消息的处理。Looper负责循环从MessageQuene中读取消息,
    如果有就交给Handler处理,如果没有就一直等待。MessageQuene负责消息的插入和读取,里面维护了一个
    单向链表,根据消息的执行时间排列(即最先被执行的在链表前面)。
两个子线程Handler通信

public Handler mHandler;

new Thread(new Runnable(){
    @Override
    public void run(){
        Looper.prepare();
        mHandler = new Handler(){
            @Override
            public void handleMessage(Message msg){
                super.handleMessage(msg);
                Log.e("gybguohao","msg.obj = " + msg.obj);
            }
        }
        Looper.loop();
    }
}).start();

new Thread(new Runnable(){
    @Override
    public void run(){
        Message msg = mHandler.obtainMessage();
        msg.obj = "这是从第二个子线程过来的消息";
        mHandler.sendMessage(msg);
    }
}).start();
10、Context类
11、热更新
12、WebView优化,及H5的js调用
WebView的优化:
    1、第一次打开WebView时间过长,可以在合适的时候创建一个全局的WebView供所有页面使用;
    2、设置WebView的文件缓存,加快加载速度。
    
H5的js调用:
    1、首先设置允许与JS交互webSettings.setJavaScriptEnabled(true); 
    2、添加交互接口mWebView.addJavascriptInterface(new JsCommunicate(), "App");
    3、在JsCommunicate类中通过注解@JavascriptInterface定义具体的交互方法;
    4、js端通过window.App.方法名()调用对应的方法。
13、性能优化
1、应用启动速度优化,避免在自定义Application类中做过多耗时的操作;
2、减少布局多层嵌套,避免GPU过度绘制,使用merge减少布局嵌套;
3、尽量减少APK的大小,删除无用代码和资源,重复布局尽量采用include;
4、内存优化,及时释放内存,避免内存泄露;
5、避免无用的网络请求,给用户造成流量的浪费。
14、Binder机制
Android Binder是用来做进程通信的,Android的各个应用以及系统服务都运行在独立的进程中,它们的通信都依赖
于Binder。
15、65536是怎么回事?如何解决
表示方法数超过了65536个

解决方法:
    1、在项目的build.gradle文件下defaultConfig闭包中配置multiDexEnabled true
    2、在dependencies下添加依赖implementation 'com.android.support:multidex:1.0.0'
    3、在自定义的Application类中重写attachBaseContext方法,并且执行MultiDex.install(this)代码。
@Override
public void attachBaseContext(Context base) {
    super.attachBaseContext(base);
    MultiDex.install(this);
}
16、LruCache原理
LruCache的全称是Least Recently Used,也就是最近最少使用算法。
17、断点续传
18、主线程Lopper为什么不会ANR
因为Android是由事件驱动的,looper.loop() 不断地接收事件、处理事件,每一个点击触摸或者说Activity的
生命周期都是运行在Looper.loop()的控制之下,如果Looper停止了,应用也就停止了。只能是某一个消息或者
说对消息的处理阻塞了 Looper.loop(),而不是 Looper.loop()阻塞它。
19、AIDL

Java方面:

1、线程安全、多线程通信、同步方式
线程安全:
    在多线程环境下对共享资源的访问,保证此共享资源的一致性。
    线程安全问题多是由全局变量和静态变量引起的,
    当多个线程对共享数据只执行读操作,不执行写操作时,一般是线程安全的;
    当多个线程都执行写操作时,需要考虑线程同步来解决线程安全问题。
    
多线程通信:
    1、全局变量
    2、Handler
    3、AsyncTask
    4、EventBus
    
同步方式:
    1、使用synchronized关键字(同步方法和同步代码块)
2、线程池的原理
3、进程间通信方式
1、Binder
2、Intent
3、ContentProvider
4、AIDL
4、进程和线程区别
一个程序至少一个进程,一个进程至少一个线程。
进程是系统进行资源分配和调度的最小单位,比如分配内存。
线程是程序执行的最小单位,可以与同属一个进程的其他的线程共享该进程所拥有的全部资源。
5、sleep和wait区别
1、sleep是Thread类的方法,wait是object类的方法;
2、sleep方法没有释放锁所以不能调用里面的同步方法。而wait方法释放了锁,使得其他线程可以调用里面的同步方法。
4、sleep必须捕获异常,而wait不需要捕获异常
6、堆和栈的区别
1、堆中存储的是对象
2、栈中存储的是局部变量
7、final的作用
final可以修饰类、方法、变量(包括成员变量和局部变量)
1、final修饰类,表示这个类不能被继承,并且该类中所有的成员方法会被隐式的指定为final方法;
2、final修饰方法,该方法在子类中不能重写,只能继承;
3、final修饰变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,
则在对其初始化之后便不能再让其指向另一个对象
8、简单描述封装、继承、多态
1、封装:将事物的属性和实现细节隐藏起来,只对外提供访问和修改接口(例如set和get方法);
2、继承:当多个类具有相同的属性和方法时,可以将相同部分抽取出来放到一个类中作为父类,其他的类继承这个父类。
3、多态:方法的重写和重载是Java多态性的不同表现。
    重写是父类与子类之间多态性的一种表现,重载是一个类中多态性的一种表现。
9、==和equals的区别
==:
    1、对于基本数据类型比较的是它们的值是否相等;
    2、对于对象比较的是它们是否是同一个对象(即内存地址是否相同);

equals:
    1、equals比较的是值是否相等
10、java基本数据类型和区别
基本数据类型:
    1、byte:Java中最小的数据类型,1个字节,取值范围-128~127,默认值0
    2、short:短整型,2个字节,取值范围-32768~32767,默认值0
    3、int:整型,4个字节,取值范围-2147483648~2147483647,默认值0
    4、long:长整型,在内存中占64位,即8个字节-2^63~2^63-1,默认值0L
    5、float:浮点型,4个字节,与double的区别在于float类型有效小数点只有6~7位,默认值0
    6、double:双精度浮点型,8个字节,默认值0
    7、char:字符型,2个字节,取值范围0~65535,默认值为空
    8、boolean:布尔类型,占1个字节,用于判断真或假,默认值false

引用数据类型:
    1、类、接口类型、数组类型、枚举类型、注解类型。

区别:

    1、基本数据类型在被创建时,在栈上给其划分一块内存,将数值直接存储在栈上。
    2、引用数据类型在被创建时,首先要在栈上给其引用分配一块内存,而具体对象存储在堆内存上,
    然后由栈上面的引用指向堆中对象的地址。
11、如何实现一个死锁
12、synchronized的实现原理
13、volatile的实现原理
Java内存模型三大性质:可见性、原子性和有序性。

    1、可见性:是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的,volatile修饰的变量不允许
    线程内部缓存和重排序,即直接修改内存,所以对其他线程是可见的。但是这里需要注意一个问题,volatile
    只能让被它修饰内容具有可见性,但不能保证它具有原子性。
    2、原子性:原子是世界上的最小单位,具有不可分割性。
    3、有序性:Java 语言提供了 volatile 和 synchronized 两个关键字来保证线程之间操作的有序性,
    volatile 是因为其本身包含“禁止指令重排序”的语义,synchronized 是由“一个变量在同一个时刻
    只允许一条线程对其进行 lock 操作”这条规则获得的,此规则决定了持有同一个对象锁的两个同步块只能串行执行。
    
volatile变量用来确保将变量的更新操作通知到其他线程。当把变量声明为volatile类型后,编译器与运行时都会
注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。volatile变量不会被缓存在寄存器
或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。
在访问volatile变量时不会执行加锁操作,因此也就不会使执行线程阻塞,因此volatile变量是一种比sychronized
关键字更轻量级的同步机制。

声明变量是 volatile 的,JVM 保证了每次读变量都从内存中读,跳过 CPU cache 这一步。
14、子类重写父类方法,返回值和参数有什么要求

框架搭建:

1、MVC和MVP的区别
1、MVC中无法区分Activity到底是V还是C,因为它即承担了界面的显示,又负责一部分逻辑的处理;
2、MVP中V和M不存在直接联系,而是通过P来交互。P和V以及P和M之间通过接口来交互,从而降低了代码的耦合度。
2、手写MVP框架(用户登录)
public class Activity extends AppCompatActivity implements LoginView {
    
    private LoginPresenter mLoginPresenter;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        mLoginPresenter = new LoginPresenter(this);
        
        //  请求登录
        mLoginPresenter.login("18855586110", "123456");
    }
    
    @Override
    public void loginSuccess(LoginModel model) {
        
    }
    
    @Override
    public void loginFail(int code, int msg) {
        
    }
    
}

public interface LoginView() {
    
    void loginSuccess(LoginModel model);
    
    void loginFail(int code, int msg);
    
}

public class LoginPresenter {
    
    public LoginView mView;
    
    public LoginPresenter(LoginView view) {
        this.mView = view;
    }
    
    public void login(String phone, String password) {
        NetClient.getInstance().login(new BaseCallback<LoginModel>() {
            @Override
            public void onSuccess(LoginModel model) {
                mView.loginSuccess(model);
            }

            @Override
            public void onFail(int code, int msg) {
                mView.loginSuccess(code, msg);
            }
        })
    }
}
3、MVVM框架
Android的MVVM框架主要是通过DataBinding这个库实现

设计模式:

1、用到的设计模式都有哪些
常用的设计模式有:单例模式、Builder、工厂模式、策略模式、观察者模式、等等
2、实现单例模式都有哪几种方式
有以下几种方法:饿汉、懒汉、Double checked locking、静态内部类
饿汉模式
public class Singleton {

    private static final Singleton mInstance = new Singleton();

    private Singleton() {
        
    }
    
    public static Singleton getInstance() {
        return mInstacen;
    }
}

懒汉模式
public class Singleton {

    private static Singleton mInstance;
    
    private Singleton() {
        
    }
    
    public static synchronized Singleton getInstance() {
        
        if (mInstance == null) {
            mInstance = new Singleton();
        }
        
        return mInstacen;
    }
}
3、手写单例模式
双重加锁
public class Singleton{
    
    private volatile static Singleton mInstance = null;
    
    private Singleton(){
        
    }
    
    public static Singleton getInsatnce(){
        if (mInstance == null){
            synchronized(Singleton.class){
                if(mInstance == null){
                    mInstance = new Singleton();
                }
            }
        }
        return mInstance;
    }
}

静态内部类
public class Singleton{
    
    private Singleton(){
        
    }
    
    public static Singleton getInsatnce(){
        return SingletonHolder.mInstance;
    }
    
    private static class SingletonHolder{
        private static final Singleton mInstance = new Singleton();
    }
}
4、装饰模式

三方框架:

1、用到的三方框架都有哪些
1、Butterknife
2、EventBus
3、Retrofit
4、OkHttp
5、Glide
2、OkHttp的原理
3、Picasso缓存机制

算法方面:

1、冒泡排序
假设无序数组为[6, 1, 2, 7, 9, 3, 4, 5, 10, 8]

当i = 0时,进行第一次内部比较
1、第一次循环后的结果是[1, 6, 2, 7, 9, 3, 4, 5, 10, 8]
2、第二次循环后的结果是[1, 2, 6, 7, 9, 3, 4, 5, 10, 8]
3、第三次循环后的结果是[1, 2, 6, 7, 9, 3, 4, 5, 10, 8]
4、第四次循环后的结果是[1, 2, 6, 7, 9, 3, 4, 5, 10, 8]
5、第五次循环后的结果是[1, 2, 6, 7, 3, 9, 4, 5, 10, 8]
6、第六次循环后的结果是[1, 2, 6, 7, 3, 4, 9, 5, 10, 8]
7、第七次循环后的结果是[1, 2, 6, 7, 3, 4, 5, 9, 10, 8]
8、第八次循环后的结果是[1, 2, 6, 7, 3, 4, 5, 9, 10, 8]
9、第九次循环后的结果是[1, 2, 6, 7, 3, 4, 5, 9, 8, 10]

......
当i = 1时,进行第一次内部比较
int number[] = new int[10]{6, 1, 2, 7, 9, 3, 4, 5, 10, 8};
int temp = 0;

for (int i = 0; i < number.length - 1; i++) {
    for (int j = 0; j < number.length - i - 1; j++) {
        if (number[j] > number[j + 1]) {
            temp = number[j];
            number[j] = number[j + 1];
            number[j + 1] = temp;
        }
    }
}
2、快速排序
3、两个栈实现一个队列
思路:因为队列是先进先出,而栈是先进后出
Stack<Integer> stack1 = new Stack<Integer>();
Stack<Integer> stack2 = new Stack<Integer>();

//  入列  
public void push (int node) {
    stack1.push(node);
}

//  出列    
public int pop () {
    if (stack2.size() <= 0) {
        while (stack1.size() > 0) {
            stack2.push(stack1.pop());
        }
    }
        
    return stack2.pop();
}
4、斐波那契数列
斐波那契数列通项公式:
    f(1) = 1;
    f(2) = 1;
    f(n) = f(n - 1) + f(n - 2) ( n >= 3)
public int Fibonacci (int n) {
    if (n == 0) {
        return 0;
    }
        
    if (n == 1 || n == 2) {
        return 1;
    }
        
    // f(n)
    int a = 0;
    // f(n - 1)
    int b = 1;
    // f(n - 2)
    int c = 1;
    
    for (int i = 3; i <= n; i++) {
        a = b + c;
        c = b;
        b = a;
    }
        
    return a;
}
5、二叉树广度优先遍历
6、二叉树根节点到叶节点的和

网络编程:

1、Http、Https区别
Https是在Http的基础上加了SSL层,SSL层负责对传输的数据进行加密
1、Http是明文传输,Https对传输的数据进行了加密
2、Http使用80端口连接,Https使用443端口连接
3、Https需要申请证书
2、Https的原理
3、post、get方式的区别
4、三次握手过程

数据加密:

1、加密方式
1、MD5:MD5是不可逆的加密算法,也就是无法解密,主要用于客户端的用户密码加密。
2、SHA1
3、AES
4、RSA:RSA算法在客户端使用公钥加密,在服务端使用私钥解密。这样即使加密的公钥被泄露,没有私钥仍然无法解密。
2、对称加密和非对称加密
对称加密:加密解密用同一个密钥。
非对称加密:公钥加密,私钥解密。例子:RSA

数据结构:

1、ArrayList和LinkedList的区别及各自的使用场景
ArrayList内部采用数组的形式存储数据,所以更适合需要大量进行随机访问的场景(即改查操作);
LinkedList内部采用双向链表的形式存储数据,所以更适合需要对集合进行元素的增加和删除的场景(即增删操作)。
2、HashMap的底层数据结构,及原理
HashMap是由数组+链表+红黑树组成的。根据key的hash值决定元素所在数组中的位置,数组里面保存的是由每个
元素组成的单向链表,当链表的长度超过8时,会转换成红黑树的形式(提高元素的查找效率)。
3、String、StringBuffer、StringBuilder的区别
1、执行速度。执行速度的快慢为:StringBuilder > StringBuffer > String。
2、线程安全。StringBuilder是线程不安全的,而StringBuffer是线程安全的。因为StringBuffer中很多方法带有
synchronized关键字,可以保证是线程安全的。所以在多线程情况下建议使用StringBuffer,确保线程安全,但是在
单线程的情况下,建议使用StringBuilder,因为执行速度更快。
3、HashMap、HaseSet、HashTable、ConcurrentHashMap、LinkHashMap的区别
1、HaseMap储存是无序不重复的,允许key和value为null(如果key为null,则会在代码里面将null保存为0),是
线程不安全的。
2、HashSet内部其实是用的HashMap作为数据的保存,储存的只是map的key,默认value为null(所以HashSet也是
无序不重复的,也允许key为null的,也是线程不安全的)
3、HashTable储存无序不重复的,不允许key和value为null,通过synchronized关键字保证线程安全
4、ConcurrentHashMap储存无序不重复的,不允许key和value为null,通过volatile关键字保证线程安全
5、LinkHashMap继承至HashMap,通过一个双向链表保存了元素的储存顺序(所以LinkHashMap也允许key为null的,也是线程不安全的)。

Kotlin方面:

1、const和val的区别
const只能修饰val,不能修饰var。
const val的可见性是public final static,可以直接方法;
val的可见性是private final static,并且val会生成getNormalObject(),通过方法调用。
2、kotlin闭包
3、run、apply、let、also、with的用法和区别

Linux方面:

1、修改权限的命令
chmod 777

r表示read,读取权限,代表数字4
w表示write,写入权限,代表数字2
x表示execute,执行权限,代表数字1
2、Git使用
上一篇下一篇

猜你喜欢

热点阅读