前言
“抗寒”,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的发动流程
- 从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
将保存的数据获取到。
咱们来看看ComponentActivity
对 onRetainNonConfigurationInstance
的完成:
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
的无参结构中,对生命周期做了一个监听,当页面进行毁掉的时分,而且没有装备更改的时分,会履行
mViewModelStore
的 clear()
办法,进行数据的开释操作。
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题)
总共50W字的文档,面试专题12W字仅仅一小部分,字数约束,分几篇更。
重视大众号:Android苦做舟
提前解锁 《整套50W字Android体系PDF》,让学习更靠近未来实战。
总共包括:
1.腾讯Android开发笔记(33W字)
2.2022最新Android十一位大厂面试专题(12W字)
3.音视频经典面试题(6W字)
4.Jetpack全家桶
5.Android 功能监控结构Matrix
6.JVM
7.车载运用开发