男人与女人的最大区别:男人注重脸面,女人注重脸面
《智言》
腾哥记得自己还是中级的时候去一些名企去面试,面试官说的一些专业技术名词自己听都没听说过,感觉自己特别low……一次次面试失败,一次次记笔记,腾哥相信再困难的问题只要认真地写出来、分享出来已经解决了一多半!
以下是我总结的面试的时候面试官经常使用的技术词汇来“吓唬人”:
1.依赖注入
什么是依赖注入
依赖是指一个对象持有其他对象的引用。依赖注入则是将这些依赖对象传递给被依赖对象,而不是被依赖对象自己创建这些对象。
上面的话比较官方,看不懂也不要紧,下面我们使用ButterKnife框架写个demo,相信对大家有深刻的帮助。
ButterKnife举例说明
官方链接
Annotate fields with @BindView and a view ID for Butter Knife to find and automatically cast the corresponding view in your layout.
大致的意思是用@bindview和butterKnife的视图ID对字段进行注释,以便在布局中查找并自动转换相应的视图。
首先,在Project的 build.gradle 中添加如下代码:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
classpath 'com.jakewharton:butterknife-gradle-plugin:9.0.0' //控制好版本号和Gradle版本号不然会有意想不到的报错
}
}
第二步,在App的 build.gradle 中添加代码:
apply plugin: 'com.jakewharton.butterknife'
最后,引入ButterKnife:
implementation 'com.jakewharton:butterknife:9.0.0'
annotationProcessor 'com.jakewharton:butterknife-compiler:9.0.0'
如果中途不出意外的话,配置环境还是挺简单的……
下面用代码实现:
引入控件对象
首先,在Activity的onCreate生命周期里,在引入布局文件后编写ButterKnife.bind(this);
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
activity_main.xml布局很简单:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="@+id/close"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="点击我消失" />
<android.support.v7.widget.RecyclerView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent"></android.support.v7.widget.RecyclerView>
</LinearLayout>
然后获取控件的引入在Activity中实现:
@BindView(R.id.listView)
RecyclerView listView;
@BindView(R.id.close)
Button close;
注意:使用ButterKnife修饰的方法和控件不能用private or static 修饰,否则会报错。错误: @BindView fields must not be private or static.
在adapter使用
public static class VH extends RecyclerView.ViewHolder {
@BindView(R.id.text)
TextView text;
public VH(View view) {
super(view);
ButterKnife.bind(this, view);
}
}
注意:是ButterKnife.bind(this, view);区别Activity里的ButterKnife.bind(this);
另外,Fragment的生命周期不同于activity。在onCreateView中绑定一个Fragment时,在onDestroyView中将视图设置为null。当你调用bind来为你绑定一个Fragment时,Butter Knife会返回一个Unbinder的实例。在适当的生命周期(onDestroyView)回调中调用它的unbind方法进行Fragment解绑。使用ButterKnife.bind(this, view)进行绑定。
onCreateView代码如是:
Unbinder unbinder = ButterKnife.bind(this,view);
onDestroyView的生命周期里需要解绑。
/**
* onDestroyView中进行解绑操作
*/
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
onClick绑定
刚才布局文件的button id为"close" ,它的绑定为:
@OnClick(R.id.close)
public void close() {
close.setVisibility(View.GONE);
}
资源绑定
String资源:
@BindString(R.string.f1)
String f1;
@BindArray(R.array.main_function)
String[] functionArrry;
图片资源:
@BindBitmap(R.mipmap.ic_launcher)
Bitmap bitmap;
@BindDrawable(R.drawable.ic_launcher_background)
Drawable bg;
ButterKnife能绑定的对象很多,这里就不一一列举了……
常用的依赖注入的框架还有Dagger,Dagger2这里暂不细讲,总的来说是引入对象并绑定对象使用。
关于依赖注入,鄙人觉着废话有点多了,接下来只取精华列出……
2.进程间通信
Bundle/Intent传递数据:
可传递基本类型,String,实现了Serializable或Parcellable接口的数据结构。Serializable是Java的序列化方法,Parcellable是Android的序列化方法,前者代码量少(仅一句),但I/O开销较大,一般用于输出到磁盘或网卡;后者实现代码多,效率高,一般用户内存间序列化和反序列化传输。关于序列化和反序列化,下面会有合理的说明知识。
ContentProvider:
四大组件之一,底层也是Binder实现,主要用来为其他APP提供数据,可以说天生就是为进程通信而生的。自己实现一个ContentProvider需要实现6个方法,其中onCreate是主线程中回调的,其他方法是运行在Binder之中的。自定义的ContentProvider注册时要提供authorities属性,应用需要访问的时候将属性包装成Uri.parse("content://authorities")。还可以设置permission,readPermission,writePermission来设置权限。 ContentProvider有query,delete,insert等方法,看起来貌似是一个数据库管理类,但其实可以用文件,内存数据等等一切来充当数据源,query返回的是一个Cursor,可以自定义继承AbstractCursor的类来实现。关于四大组件,以后的文章会详细说明。
AIDL:
AIDL通过定义服务端暴露的接口,以提供给客户端来调用,AIDL使服务器可以并行处理,而Messenger封装了AIDL之后只能串行运行,所以Messenger一般用作消息传递。
通过编写aidl文件来设计想要暴露的接口,编译后会自动生成响应的java文件,服务器将接口的具体实现写在Stub中,用iBinder对象传递给客户端,客户端bindService的时候,用asInterface的形式将iBinder还原成接口,再调用其中的方法。
文件共享:
对同一个文件先后写读,从而实现传输,Linux机制下,可以对文件并发写,所以要注意同步。顺便一提,Windows下不支持并发读或写。
Messenger:
Messenger是基于AIDL实现的,服务端(被动方)提供一个Service来处理客户端(主动方)连接,维护一个Handler来创建Messenger,在onBind时返回Messenger的binder。
双方用Messenger来发送数据,用Handler来处理数据。Messenger处理数据依靠Handler,所以是串行的,也就是说,Handler接到多个message时,就要排队依次处理。
3.多线程
腾哥觉着网上关于多线程的文章一大堆,这里就不便多说了。
推荐一篇文章,鄙人感觉总结的已经很到位了:android 多线程 — 线程的面试题和答案
4.事件分发消费机制
Android:View的事件分发与消费机制
case1:ViewFather的dispatchTouchEvent返回false的时候
2019-06-16 12:35:54.480 32382-32382/com.example.jianshu E/====>: TouchEventActivity===>dispatchTouchEventACTION_DOWN
2019-06-16 12:35:54.482 32382-32382/com.example.jianshu E/====>: ViewFather===>dispatchTouchEventACTION_DOWN
2019-06-16 12:35:54.483 32382-32382/com.example.jianshu E/====>: TouchEventActivity===>onTouchEventACTION_DOWN
2019-06-16 12:35:54.496 32382-32382/com.example.jianshu E/====>: TouchEventActivity===>dispatchTouchEventACTION_MOVE
2019-06-16 12:35:54.499 32382-32382/com.example.jianshu E/====>: TouchEventActivity===>onTouchEventACTION_MOVE
2019-06-16 12:35:54.513 32382-32382/com.example.jianshu E/====>: TouchEventActivity===>dispatchTouchEventACTION_MOVE
2019-06-16 12:35:54.518 32382-32382/com.example.jianshu E/====>: TouchEventActivity===>onTouchEventACTION_MOVE
2019-06-16 12:35:54.529 32382-32382/com.example.jianshu E/====>: TouchEventActivity===>dispatchTouchEventACTION_MOVE
2019-06-16 12:35:54.531 32382-32382/com.example.jianshu E/====>: TouchEventActivity===>onTouchEventACTION_MOVE
2019-06-16 12:35:54.547 32382-32382/com.example.jianshu E/====>: TouchEventActivity===>dispatchTouchEventACTION_MOVE
2019-06-16 12:35:54.550 32382-32382/com.example.jianshu E/====>: TouchEventActivity===>onTouchEventACTION_MOVE
2019-06-16 12:35:54.580 32382-32382/com.example.jianshu E/====>: TouchEventActivity===>dispatchTouchEventACTION_MOVE
2019-06-16 12:35:54.584 32382-32382/com.example.jianshu E/====>: TouchEventActivity===>onTouchEventACTION_MOVE
case2:ViewFather的dispatchTouchEvent返回true的时候
2019-06-16 12:30:13.480 29728-29728/com.example.jianshu E/====>: TouchEventActivity===>dispatchTouchEventACTION_DOWN
2019-06-16 12:30:13.481 29728-29728/com.example.jianshu E/====>: ViewFather===>dispatchTouchEventACTION_DOWN
2019-06-16 12:30:13.555 29728-29728/com.example.jianshu E/====>: TouchEventActivity===>dispatchTouchEventACTION_MOVE
2019-06-16 12:30:13.556 29728-29728/com.example.jianshu E/====>: ViewFather===>dispatchTouchEventACTION_MOVE
2019-06-16 12:30:13.575 29728-29728/com.example.jianshu E/====>: TouchEventActivity===>dispatchTouchEventACTION_MOVE
2019-06-16 12:30:13.576 29728-29728/com.example.jianshu E/====>: ViewFather===>dispatchTouchEventACTION_MOVE
2019-06-16 12:30:13.588 29728-29728/com.example.jianshu E/====>: TouchEventActivity===>dispatchTouchEventACTION_MOVE
2019-06-16 12:30:13.588 29728-29728/com.example.jianshu E/====>: ViewFather===>dispatchTouchEventACTION_MOVE
2019-06-16 12:30:13.597 29728-29728/com.example.jianshu E/====>: TouchEventActivity===>dispatchTouchEventACTION_MOVE
2019-06-16 12:30:13.598 29728-29728/com.example.jianshu E/====>: ViewFather===>dispatchTouchEventACTION_MOVE
2019-06-16 12:30:13.598 29728-29728/com.example.jianshu E/====>: TouchEventActivity===>dispatchTouchEventACTION_UP
2019-06-16 12:30:13.599 29728-29728/com.example.jianshu E/====>: ViewFather===>dispatchTouchEventACTION_UP
case3:ViewFather的dispatchTouchEvent返回super.dispatchTouchEvent(event)且在其onInterceptTouchEvent返回true的时候
2019-06-16 12:42:24.298 6582-6582/com.example.jianshu E/====>: TouchEventActivity===>dispatchTouchEventACTION_DOWN
2019-06-16 12:42:24.299 6582-6582/com.example.jianshu E/====>: ViewFather===>dispatchTouchEventACTION_DOWN
2019-06-16 12:42:24.299 6582-6582/com.example.jianshu E/====>: ViewFather===>onInterceptTouchEventACTION_DOWN
2019-06-16 12:42:24.299 6582-6582/com.example.jianshu E/====>: ViewFather===>onTouchEventACTION_DOWN
2019-06-16 12:42:24.299 6582-6582/com.example.jianshu E/====>: TouchEventActivity===>onTouchEventACTION_DOWN
2019-06-16 12:42:24.316 6582-6582/com.example.jianshu E/====>: TouchEventActivity===>dispatchTouchEventACTION_MOVE
2019-06-16 12:42:24.316 6582-6582/com.example.jianshu E/====>: TouchEventActivity===>onTouchEventACTION_MOVE
2019-06-16 12:42:24.332 6582-6582/com.example.jianshu E/====>: TouchEventActivity===>dispatchTouchEventACTION_MOVE
2019-06-16 12:42:24.332 6582-6582/com.example.jianshu E/====>: TouchEventActivity===>onTouchEventACTION_MOVE
case4:ViewFather的dispatchTouchEvent返回super.dispatchTouchEvent(event)且在其onInterceptTouchEvent返回false的时候
2019-06-16 12:43:54.322 8412-8412/com.example.jianshu E/====>: TouchEventActivity===>dispatchTouchEventACTION_DOWN
2019-06-16 12:43:54.323 8412-8412/com.example.jianshu E/====>: ViewFather===>dispatchTouchEventACTION_DOWN
2019-06-16 12:43:54.323 8412-8412/com.example.jianshu E/====>: ViewFather===>onInterceptTouchEventACTION_DOWN
2019-06-16 12:43:54.323 8412-8412/com.example.jianshu E/====>: ViewChild===>dispatchTouchEventACTION_DOWN
2019-06-16 12:43:54.323 8412-8412/com.example.jianshu E/====>: ViewChild===>onInterceptTouchEventACTION_DOWN
2019-06-16 12:43:54.323 8412-8412/com.example.jianshu E/====>: ViewChild===>onTouchEventACTION_DOWN
2019-06-16 12:43:54.323 8412-8412/com.example.jianshu E/====>: ViewFather===>onTouchEventACTION_DOWN
2019-06-16 12:43:54.324 8412-8412/com.example.jianshu E/====>: TouchEventActivity===>onTouchEventACTION_DOWN
2019-06-16 12:43:54.354 8412-8412/com.example.jianshu E/====>: TouchEventActivity===>dispatchTouchEventACTION_MOVE
2019-06-16 12:43:54.355 8412-8412/com.example.jianshu E/====>: TouchEventActivity===>onTouchEventACTION_MOVE
case5:正常情况下即ViewFather的三个方法都是返回super.xxxTouchEvent()方法
2019-06-16 15:29:49.651 11555-11555/com.example.jianshu E/====>: TouchEventActivity===>dispatchTouchEventACTION_DOWN
2019-06-16 15:29:49.652 11555-11555/com.example.jianshu E/====>: ViewFather===>dispatchTouchEventACTION_DOWN
2019-06-16 15:29:49.652 11555-11555/com.example.jianshu E/====>: ViewFather===>onInterceptTouchEventACTION_DOWN
2019-06-16 15:29:49.652 11555-11555/com.example.jianshu E/====>: ViewChild===>dispatchTouchEventACTION_DOWN
2019-06-16 15:29:49.653 11555-11555/com.example.jianshu E/====>: ViewChild===>onInterceptTouchEventACTION_DOWN
2019-06-16 15:29:49.653 11555-11555/com.example.jianshu E/====>: ViewChild===>onTouchEventACTION_DOWN
2019-06-16 15:29:49.653 11555-11555/com.example.jianshu E/====>: ViewFather===>onTouchEventACTION_DOWN
2019-06-16 15:29:49.654 11555-11555/com.example.jianshu E/====>: TouchEventActivity===>onTouchEventACTION_DOWN
2019-06-16 15:29:49.671 11555-11555/com.example.jianshu E/====>: TouchEventActivity===>dispatchTouchEventACTION_MOVE
2019-06-16 15:29:49.672 11555-11555/com.example.jianshu E/====>: TouchEventActivity===>onTouchEventACTION_MOVE
5.Android 四大引用
6.无敌service
Android Service(服务)详解-附异步下载Demo
7.广播机制(几种方式)
https://www.jianshu.com/p/a2e482af70be
Broadcast广播,注册方式主要有两种.
第一种是静态注册,也可成为常驻型广播,这种广播需要在Androidmanifest.xml中进行注册,这中方式注册的广播,不受页面生命周期的影响,即使退出了页面,也可以收到广播这种广播一般用于想开机自启动啊等等,由于这种注册的方式的广播是常驻型广播,所以会占用CPU的资源。
第二种是动态注册,而动态注册的话,是在代码中注册的,这种注册方式也叫非常驻型广播,收到生命周期的影响,退出页面后,就不会收到广播,我们通常运用在更新UI方面。这种注册方式优先级较高。最后需要解绑,否会会内存泄露
广播是分为有序广播和无序广播。
8.AIDL
9.动画机制(逐帧、补间和SurfaceView动画)
10.设计模式
11.Java序列化的区别
12.Camera1和Camera2的区别
13.Android启动流程 、app安装和启动原理
https://www.jianshu.com/p/12de32b31836
https://www.jianshu.com/p/657336b545bd
14.自定义Android混淆规则
自定义Android混淆规则--proguard-rules.pro
15.hook
16.进程保活(不死进程)
此处延伸:进程的优先级是什么
当前业界的Android进程保活手段主要分为** 黑、白、灰 **三种,其大致的实现思路如下:
黑色保活:不同的app进程,用广播相互唤醒(包括利用系统提供的广播进行唤醒)
白色保活:启动前台Service
灰色保活:利用系统的漏洞启动前台Service
黑色保活
所谓黑色保活,就是利用不同的app进程使用广播来进行相互唤醒。举个3个比较常见的场景:
场景1:开机,网络切换、拍照、拍视频时候,利用系统产生的广播唤醒app
场景2:接入第三方SDK也会唤醒相应的app进程,如微信sdk会唤醒微信,支付宝sdk会唤醒支付宝。由此发散开去,就会直接触发了下面的 场景3
场景3:假如你手机里装了支付宝、淘宝、天猫、UC等阿里系的app,那么你打开任意一个阿里系的app后,有可能就顺便把其他阿里系的app给唤醒了。(只是拿阿里打个比方,其实BAT系都差不多)
白色保活
白色保活手段非常简单,就是调用系统api启动一个前台的Service进程,这样会在系统的通知栏生成一个Notification,用来让用户知道有这样一个app在运行着,哪怕当前的app退到了后台。如下方的LBE和QQ音乐这样:
灰色保活
灰色保活,这种保活手段是应用范围最广泛。它是利用系统的漏洞来启动一个前台的Service进程,与普通的启动方式区别在于,它不会在系统通知栏处出现一个Notification,看起来就如同运行着一个后台Service进程一样。这样做带来的好处就是,用户无法察觉到你运行着一个前台进程(因为看不到Notification),但你的进程优先级又是高于普通后台进程的。那么如何利用系统的漏洞呢,大致的实现思路和代码如下:
思路一:API < 18,启动前台Service时直接传入new Notification();
思路二:API >= 18,同时启动两个id相同的前台Service,然后再将后启动的Service做stop处理
熟悉Android系统的童鞋都知道,系统出于体验和性能上的考虑,app在退到后台时系统并不会真正的kill掉这个进程,而是将其缓存起来。打开的应用越多,后台缓存的进程也越多。在系统内存不足的情况下,系统开始依据自身的一套进程回收机制来判断要kill掉哪些进程,以腾出内存来供给需要的app。这套杀进程回收内存的机制就叫 Low Memory Killer ,它是基于Linux内核的 OOM Killer(Out-Of-Memory killer)机制诞生。
进程的重要性,划分5级:
前台进程 (Foreground process)
可见进程 (Visible process)
服务进程 (Service process)
后台进程 (Background process)
空进程 (Empty process)
了解完 Low Memory Killer,再科普一下oom_adj。什么是oom_adj?它是linux内核分配给每个系统进程的一个值,代表进程的优先级,进程回收机制就是根据这个优先级来决定是否进行回收。对于oom_adj的作用,你只需要记住以下几点即可:
进程的oom_adj越大,表示此进程优先级越低,越容易被杀回收;越小,表示进程优先级越高,越不容易被杀回收
普通app进程的oom_adj>=0,系统进程的oom_adj才可能<0
有些手机厂商把这些知名的app放入了自己的白名单中,保证了进程不死来提高用户体验(如微信、QQ、陌陌都在小米的白名单中)。如果从白名单中移除,他们终究还是和普通app一样躲避不了被杀的命运,为了尽量避免被杀,还是老老实实去做好优化工作吧。
所以,进程保活的根本方案终究还是回到了性能优化上,进程永生不死终究是个彻头彻尾的伪命题!
17.Dalvik VM
18.Binder机制原理
19.Https机制
20.卡顿分析(原因、几种、工具分析)
分享一下:面试知识大全https://www.jianshu.com/p/7bb56481063d