Android有深度(二):一些面试官“常用”的技术

男人与女人的最大区别:男人注重脸面,女人注重脸面
《智言》

腾哥记得自己还是中级的时候去一些名企去面试,面试官说的一些专业技术名词自己听都没听说过,感觉自己特别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 四大引用

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的区别

Camera1.0和Camear2.0的区别

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进程保活

此处延伸:进程的优先级是什么
当前业界的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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342