面霸养成记;50万字2022最新Android11位大厂面试专题

Posted 初一十五啊

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面霸养成记;50万字2022最新Android11位大厂面试专题相关的知识,希望对你有一定的参考价值。

前言

“抗寒”,2022专有词…任正非大佬都说了要把寒气传给每一个人,确实22年过得很艰辛,摸爬滚打,总算是安定下来了。

回头想想,抽空吧面试过程中被问到的问题和平时看的文档整理分享出来,希望能给面试的小伙伴一点借鉴。

一共50W字的文档,面试专题12W字只是一小部分,字数限制,分几篇更。

关注公众号:初一十五a提前解锁

总共囊括

1.腾讯Android开发笔记(33W字)

2.2022最新Android十一位大厂面试专题(12W字)

3.音视频经典面试题(6W字)

承接上文的继续更新,上文更新了字节,京东篇腾。今天继续更BIlibili和网易篇。

六丶Bilibili

1.Activity的启动流程

  1. 从Launcher到AMS

2.从AMS到ApplicationThread

3.从ApplicationThread到Activity。

2.intent可以传递的数据

①Intent传输数据

②Intent接受数据

③Intent传输Bundle对象


④无论Intent怎么封装,多少个数据,接收时已经拆包,直接获取就可以了

3.Activity和Fragment的区别

参考回答:

相似点: 都可包含布局、可有自己的生命周期

不同点:

  • Fragment 相比较于 Activity 多出 4 个回调周期,
    在控制操作上更灵活;
  • Fragment 可以在 XML 文件中直接进行写入,也可以
    在 Activity 中动态添加;
  • Fragment 可以使用 show()/hide()或者 replace()
    随时对 Fragment 进行切换,并且切换的时候不会出
    现明显的效果,用户体验会好;Activity 虽然也可
    以进行切换,但是 Activity 之间切换会有明显的翻
    页或者其他的效果,在小部分内容的切换上给用户
    的感觉不是很好;

4.项目用到了ViewModel,说说看他的原理

先来看看ViewModel是什么?

ViewModel类旨在以注重生命周期的方式存储和管理界面相关的数据。ViewModel类让数据可在发生屏幕旋转等配置更改后继续留存。

架构组件为界面控制器提供了 ViewModel辅助程序类,该类负责为界面准备数据。在配置更改期间会自动保留 ViewModel对象,以便它们存储的数据立即可供下一个 activity 或 fragment 实例使用。

这两个介绍摘自于官网文档,其中说明ViewModel的作用,主要是保存UI数据的。我们来看看它的生命周期:

可以看到ViewModel在UI整个生命周期范围内都是同一个状态内的,当UI销毁的时候才会执行onCleard()操作,清除数据。

①使用

接下来,我们看下ViewModel 的简单使用

首先我们创建一个ViewModel

class MainViewModel : ViewModel() 
    val liveData = MutableLiveData<String>("这是LiveData的数据")

然后在UI中进行获取到MainViewModel,并进行数据订阅

class MainActivity3 : AppCompatActivity() 
    private lateinit var viewModel: MainViewModel
    private lateinit var binding:MainFragmentBinding
    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main_fragment)
      
        viewModel = ViewModelProvider(this).get(MainViewModel::class.java)

        viewModel.liveData.observe(this)
            binding.message.text = it
        
    

先实例化ViewModelProvider,通过ViewModelProvider::get 方法来获取MainViewModel 的实例对象,通过这种创建形式,不难看出,ViewModel 的创建是通过反射来创建的。

那么 ViewModle 是如何保存数据的呢?今天就由浅入深的带大家来领略一下 ViewModel 的奥妙之处,保证人人都能看得懂!

②原理

首先我们从ViewModelProvider 看起,先了解一下它的面目。

 public constructor(
        owner: ViewModelStoreOwner
    ) : this(owner.viewModelStore, defaultFactory(owner))

在这里,这个构造方法 新建了一个默认的工厂,然后调用了自己默认的构造方法。其中这个工厂就是用来创建ViewModel的。

接下来看看get方法做了点什么?

  @MainThread
    public open operator fun <T : ViewModel> get(modelClass: Class<T>): T 
        val canonicalName = modelClass.canonicalName
            ?: throw IllegalArgumentException("Local and anonymous classes can not be ViewModels")
        return get("$DEFAULT_KEY:$canonicalName", modelClass)
    

这里就是增加了一个默认的Key值,然后调用另外一个get方法,这个默认Key

internal const val DEFAULT_KEY = "androidx.lifecycle.ViewModelProvider.DefaultKey"
  @MainThread
    public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T 
        var viewModel = store[key]
        if (modelClass.isInstance(viewModel)) 
            (factory as? OnRequeryFactory)?.onRequery(viewModel)
            return viewModel as T
         else 
           ........
        
        viewModel = if (factory is KeyedFactory) 
            factory.create(key, modelClass)
         else 
            factory.create(modelClass)
        
        store.put(key, viewModel)
        return viewModel
    

在这个方法中,可以看到,根据key值从store中获取ViewModel 对象,如果 类型正确 ,当即返回当前对象, 如果不正确的话,通过工厂创建新的ViewModel对象,存储到store中并返回。

我们先看看工厂是如何创建VIewModel

   public open class NewInstanceFactory : Factory       
        override fun <T : ViewModel> create(modelClass: Class<T>): T 
            return try 
                modelClass.newInstance()
             catch (e: InstantiationException) 
                throw RuntimeException("Cannot create an instance of $modelClass", e)
             catch (e: IllegalAccessException) 
                throw RuntimeException("Cannot create an instance of $modelClass", e)
            
        
    

这是前面 ViewModelProvider 创建的默认工厂,最后通过 modelClass.newInstance() 创建了 ViewModel的实例化对象。

接下来我们看看看 store 是什么?

store的类型是ViewModelStore

public class ViewModelStore 
    private final HashMap<String, ViewModel> mMap = new HashMap<>();
    final void put(String key, ViewModel viewModel) 
        ViewModel oldViewModel = mMap.put(key, viewModel);
        if (oldViewModel != null) 
            oldViewModel.onCleared();
        
    

    final ViewModel get(String key) 
        return mMap.get(key);
    

    Set<String> keys() 
        return new HashSet<>(mMap.keySet());
    
  
    public final void clear() 
        for (ViewModel vm : mMap.values()) 
            vm.clear();
        
        mMap.clear();
    

可以看到它里面维护了一个HashMap,根据Key 值 来对ViewModel 进行存储。同时提供了clear()方法,将所有ViewModel 清除。

这个ViewModelStore 是在什么时候创建的呢?

在上文提到的创建ViewModelProvider的时候,可以看到,ViewModelStoreOwner 是由Activity 创建ViewModelProvider 的时候 传入的,然后调用owner中的 getViewModelStore() 方法,获取ViewModelStore ,并传到构造方法里面的。

 public constructor(
        owner: ViewModelStoreOwner
    ) : this(owner.viewModelStore, defaultFactory(owner))

这是因为 AppCompatActivity 的父类 ComponentActivity 实现了 ViewModelStoreOwner 接口。

public interface ViewModelStoreOwner 
    ViewModelStore getViewModelStore();

ViewModelStoreOwner 接口就很简单,只提供了一个 getViewModelStore() 方法 来获取ViewModelStore ,我们来看看它的实现。

   public ViewModelStore getViewModelStore() 
        if (getApplication() == null) 
            throw new IllegalStateException("Your activity is not yet attached to the "
                    + "Application instance. You can't request ViewModel before onCreate call.");
        
        ensureViewModelStore();
        return mViewModelStore;
    

这里面只是简单返回了 mViewModelStore 对象 ,重要的是ensureViewModelStore()方法。

    void ensureViewModelStore() 
        if (mViewModelStore == null) 
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) 
                mViewModelStore = nc.viewModelStore;
            
            if (mViewModelStore == null) 
                mViewModelStore = new ViewModelStore();
            
        
    
    
    static final class NonConfigurationInstances 
        Object custom;
        ViewModelStore viewModelStore;
    

这里主要是获取 NonConfigurationInstances 对象,然后从中获取到 viewModelStore ,如果NonConfigurationInstances为空的话,就新建一个 ViewModelStore 对象。

接下来我们主要看下 getLastNonConfigurationInstance(); 方法。

   public Object getLastNonConfigurationInstance() 
        return mLastNonConfigurationInstances != null
                ? mLastNonConfigurationInstances.activity : null;
    

我将这个方法的注释做了翻译:

检索之前由onRetainNonConfigurationInstance返回的非配置实例数据。可以从对新实例的初始@link #onCreate和@link #onStart调用中获得,允许你从先前的实例中提取任何有用的动态状态。

简单来说,这个方法就是用来获取 onRetainNonConfigurationInstance () 方法中存储的内容。

   public Object onRetainNonConfigurationInstance() 
        return null;
    

由系统调用,作为由于配置更改而销毁活动的一部分,当已知将立即为新配置创建新实例时。您可以在此处返回您喜欢的任何对象,包括活动实例本身,稍后可以通过调用 getLastNonConfigurationInstance()新的活动实例来检索这些对象。

这样对比来看不难看出:
onRetainNonConfigurationInstance() 是在Activity 销毁的时候 进行存储信息。
getLastNonConfigurationInstance() 的作用是获取 存储的信息的。
当屏幕发生旋转的时候 ,会先调用 onRetainNonConfigurationInstance先将数据进行保存,然后再通过 getLastNonConfigurationInstance 将保存的数据获取到。

我们来看看ComponentActivityonRetainNonConfigurationInstance 的实现:

  public final Object onRetainNonConfigurationInstance() 
        Object custom = onRetainCustomNonConfigurationInstance();
        ViewModelStore viewModelStore = mViewModelStore;
        if (viewModelStore == null)           
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) 
                viewModelStore = nc.viewModelStore;
            
        

        if (viewModelStore == null && custom == null) 
            return null;
        

        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.custom = custom;
        nci.viewModelStore = viewModelStore;
        return nci;
    

mViewModelStore 是在ensureViewModelStore 方法中获取的对象( 可能是通过 getLastNonConfigurationInstance()获取,可能是重新创建 ) ,如果这个 mViewModelStore 是空的话,就会尝试从NonConfigurationInstances 中获取,如果仍然是空,直接返回null,如果不是空的话, 重新创建nci 进行存储。

那这些数据什么时候才会清除呢?

ComponentActivity 的无参构造中,对生命周期做了一个监听,当页面进行销毁的时候,并且没有配置更改的时候,会执行
mViewModelStoreclear() 方法,进行数据的释放操作。

  public ComponentActivity() 
        Lifecycle lifecycle = getLifecycle();
        ..........
        getLifecycle().addObserver(new LifecycleEventObserver() 
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source,
                    @NonNull Lifecycle.Event event) 
                if (event == Lifecycle.Event.ON_DESTROY) 
                    // Clear out the available context
                    mContextAwareHelper.clearAvailableContext();
                    // And clear the ViewModelStore
                    if (!isChangingConfigurations()) 
                        getViewModelStore().clear();
                    
                
            
        );
        ..........
    

整个流程,大概就是这样

③总结

分析了这么多,我们捋一下:

  • 我们的Activity 的父类 ComponentActivity 实现了 ViewModelStoreOwner 接口,通过 ViewModelProvider 使用默认工厂 创建了 viewModel ,并通过唯一Key值 进行标识,存储到了 ViewModelStore 中。等下次需要的时候即可通过唯一Key值进行获取。

  • 由于ComponentActivity 实现了ViewModelStoreOwner 接口,实现了 getViewModelStore 方法,当屏幕旋转的时候,会先调用

onRetainNonConfigurationInstance() 方法将 viewModelStore 保存起来,然后再调用 getLastNonConfigurationInstance 方法将数据恢复,如果为空的话,会重新创建 viewModelStore ,并存储在全局中,以便以下次发生变化的时候,能够通过onRetainNonConfigurationInstance 保存起来。

最后当页面销毁并且没有配置更改的时候,会将viewModelStore 中的数据 进行清除操作。

5.你说到了内存泄露,有用过别的内存检查的工具吗,什么时候会发生内存泄露

参考第二章第8题

6.说说进程和线程的理解

类似“**进程是资源分配的最小单位,线程是CPU调度的最小单位”**这样的回答感觉太抽象,都不太容易让人理解。

做个简单的比喻:进程=火车,线程=车厢

  • 线程在进程下行进(单纯的车厢无法运行)
  • 一个进程可以包含多个线程(一辆火车可以有多个车厢)
  • 不同进程间数据很难共享(一辆火车上的乘客很难换到另外一辆火车,比如站点换乘)
  • 同一进程下不同线程间数据很易共享(A车厢换到B车厢很容易)
  • 进程要比线程消耗更多的计算机资源(采用多列火车相比多个车厢更耗资源)
  • 进程间不会相互影响,一个线程挂掉将导致整个进程挂掉(一列火车不会影响到另外一列火车,但是如果一列火车上中间的一节车厢与前一节产生断裂,将影响后面的所有车厢)
  • 进程可以拓展到多机,进程最适合多核(不同火车可以开在多个轨道上,同一火车的车厢不能在行进的不同的轨道上)
  • 进程使用的内存地址可以上锁,即一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。(比如火车上的洗手间)-“互斥锁”
  • 进程使用的内存地址可以限定使用量(比如火车上的餐厅,最多只允许多少人进入,如果满了需要在门口等,等有人出来了才能进去)-“信号量”

7.请说说看mvp和mvvm模式有什么区别吗

(第三章第1题)

9.App的启动流程

①点击桌面App图标,Launcher进程采用Binder IPC向AMS进程发起startActivity请求;

②AMS接收到请求后,向zygote进程发送创建进程的请求;

③Zygote进程fork出新的子进程,即App进程;

④App进程,通过Binder IPC向AMS进程发起attachApplication请求;

⑤AMS进程在收到请求后,进行一系列准备工作后,再通过binder IPC向App进程发送scheduleLaunchActivity请求;

⑥App进程的binder线程(ApplicationThread)在收到请求后,通过handler向主线程发送LAUNCH_ACTIVITY消息;

⑦主线程在收到Message后,通过反射机制创建目标Activity,并回调Activity.onCreate()等方法。

⑧到此,App便正式启动,开始进入Activity生命周期,执行完onCreate/onStart/onResume方法,UI渲染结束后便可以看到App的主界面。

10.对Handler的理解,handler的内存泄露了解吗

第二章第3题

七丶网易篇

1.线程和进程区别

1.进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1–n个线程。

⒉线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。

3.线程和进程—样分为五个阶段:创建、就绪、运行、阻塞、终止

4.多进程是指操作系统能同时运行多个任务(程序)

5.多线程是指在同一程序中有多个顺序流在执行。

6.在java中要想实现多线程,有两种手段,一种是继续Thread类,另外一种是实现Runable接口。

2.HashMap

第三章第10题

3.创建线程几种方式

①继承Thread类,重写run方法

②实现Runnable接口

③实现Callable接口,重写call方法

4.锁、volatile (第三章第8题和第二章12题)

5.动态代理

动态代理 :在运行时再创建代理类和其实例。

举个栗子

//抽象角色 
interface Biz 
    void print(String s);

//真实角色 
public class BizImpl implements Biz
 
    @Override
    public void print(String s) 
        System.out.println("真实实现:" + s);
    

 
public class Test 
 
    public static void main(String[] args)
 
        BizImpl bizImpl = new BizImpl();
 
        //动态代理
        //第一个参数:类加载器
        //第二个参数:想代理的接口类
        //第三个参数:InvocationHandler是一个接口,会监听到Object o调用了print()方法,并把                   
        //print()方法传递到invoke中,所以调用print()方法就会执行invoke()方法,args是print()对 
        //应的参数
        Object o = Proxy.newProxyInstance(Test.class.getClassLoader(), new Class[]Biz.class,
 
                new InvocationHandler() 
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
                        return method.invoke(bizImpl, args);
                    
                );
 
        Biz biz = (Biz)o;
        biz.print("through proxy");
    
 

运行后得到打印:

真实实现:through proxy

调用o.print会调用invoke方法是因为newProxyInstance()方法会在内存中创建一个Class对象(具体方法在Proxy内部类的ProxyClassFactory里面),我们可以仿照它创建一个Class对象方便我们分析。我们在工程中新建一个module,类型选择java library(因为ProxyGenerator在sun.misc包下),命名为lib, 代码如下:

public class Test2 
 
    public static void main(String[] args)
        String name = Biz.class.getName() + "$Proxy0";
        //生成代理指定接口的Class数据
        byte[] bytes = ProxyGenerator.generateProxyClass(name, new Class[]Biz.class);
        FileOutputStream fos;
 
        try 
            fos = new FileOutputStream("lib/" + name + ".class");
            fos.write(bytes);
            fos.close();
         catch (FileNotFoundException e) 
            e.printStackTrace();
         catch (IOException e) 
            e.printStackTrace();
        
    

运行后会在lib模块下生成类文件:Biz$Proxy0.class,代码如下:

public final class Biz$Proxy0 extends Proxy implements Biz 
    private static Method m1;
    private static Method m2;
    private static Method m0;
    private static Method m3;
 
    public Biz$Proxy0(InvocationHandler var1) throws  
        super(var1);
    
 
    public final boolean equals(Object var1) throws  
        try 
            return (Boolean)super.h.invoke(this, m1, new Object[]var1);
         catch (RuntimeException | Error var3) 
            throw var3;
         catch (Throwable var4) 
            throw new UndeclaredThrowableException(var4);
        
    
 
    public final String toString() throws  
        try 
            return (String)super.h.invoke(this, m2, (Object[])null);
         catch (RuntimeException | Error var2) 
            throw var2;
         catch (Throwable var3) 
            throw new UndeclaredThrowableException(var3);
        
    
 
    public final int hashCode() throws  
        try 
            return (Integer)super.h.invoke(this, m0, (Object[])null);
         catch (RuntimeException | Error var2) 
            throw var2;
         catch (Throwable var3) 
            throw new UndeclaredThrowableException(var3);
        
    
 
    public final void print(String var1) throws  
        try 
            super.h.invoke(this, m3, new Object[]var1);
         catch (RuntimeException | Error var3) 
            throw var3;
         catch (Throwable var4) 
            throw new UndeclaredThrowableException(var4);
        
    
 
    static 
        try 
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            m3 = Class.forName("com.enzo.lib.Biz").getMethod("print", Class.forName("java.lang.String"));
         catch (NoSuchMethodException var2) 
            throw new NoSuchMethodError(var2.getMessage());
         catch (ClassNotFoundException var3) 
            throw new NoClassDefFoundError(var3.getMessage());
        
    

通过41-49行代码我们看到:调用print()方法会调用 super.h.invoke(this, m3, new Object[]var1),这里的super.h就是InvocationHandler接口,实际就是一个监听回调,这就是调用o.print会调用invoke方法的原因。

6.Handler

(第二章第3题)

7.Binder

(第二章第5题)

8.view绘制

(第一章第2题)

关注公众号:初一十五a提前解锁

总共囊括

1.腾讯Android开发笔记(33W字)

2.2022最新Android十一位大厂面试专题(12W字)

3.音视频经典面试题(6W字)

以上是关于面霸养成记;50万字2022最新Android11位大厂面试专题的主要内容,如果未能解决你的问题,请参考以下文章

面霸养成记;50W字Android面试文档致敬2022摸爬滚打的一年

含泪刷128道面试题,50万字2022最新Android11位大厂面试专题

软件测试面霸是如何养成的,他的路子真的野

年薪50万的大数据分析师养成记

年薪50万的大数据分析师养成记摘抄

年薪50万的大数据分析师养成记摘抄