前言

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

回头想想,抽空吧面试进程中被问到的问题和平时看的文档收拾同享出来,希望能给面试的小伙伴一点学习。

总共50W字的文档,面试专题12W字仅仅一小部分,字数约束,分几篇更。

重视大众号:Android苦做舟

提前解锁 《整套50W字Android体系PDF》,让学习更靠近未来实战。

总共包括

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

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

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

4.Jetpack全家桶

5.Android 功能监控结构Matrix

6.JVM

7.车载运用开发

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

六丶Bilibili

1.Activity的发动流程

  1. 从Launcher到AMS

2.从AMS到ApplicationThread

3.从ApplicationThread到Activity。

2.intent能够传递的数据

①Intent传输数据

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

②Intent承受数据

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

③Intent传输Bundle方针

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

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

④不管Intent怎样封装,多少个数据,接收时现已拆包,直接获取就能够了

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

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数据的。咱们来看看它的生命周期:

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

能够看到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();
                    }
                }
            }
        });
        ..........
    }

整个流程,大约便是这样

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

③总结

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

  • 咱们的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的主界面。

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

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题)

总共50W字的文档,面试专题12W字仅仅一小部分,字数约束,分几篇更。

重视大众号:Android苦做舟

提前解锁 《整套50W字Android体系PDF》,让学习更靠近未来实战。

总共包括

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

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

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

4.Jetpack全家桶

5.Android 功能监控结构Matrix

6.JVM

7.车载运用开发