Android 最全面试题汇总(问题+答案+详解链接)

大厂的Android面试一般不简单,基本都会涉及Java、算法,当然也移不开Android的题目。

本文主要涉及Android相关的问题,之后我会陆续公布Java,以及算法的相关面试题总结。


目录

一、基本问题

Android的四大组件以及作用

描述下Activity的生命周期?

屏幕旋转时的Activity生命周期:

Activity的启动模式

onNewIntent的调用时机

Service的两种启动方式 

注册广播有几种方式,有何优缺点?

本地广播和全局广播有什么差别?

View的绘制流程

事件传递机制

Android Handler的机制和原理

Android中三种动画,特点和区别是什么?

ListView优化有哪几种方式?

RecyclerView相比ListView有哪些优势

二、进阶问题

ANR出现的场景及解决方案

一个线程可以有几个Handler、Looper、MessageQueue,不同的Handler如何通信?

为啥要用Handler,子线程中如何使用Handler

自定义Handler时如何避免内存泄漏

产生内存泄露的情况

查找内存泄漏

简述项目中对于内存优化的几个细节点

Android动画框架实现原理

进程保活

 进程的优先级

进程间通信的方式 :AIDL、 广播、Messenger 、Binder

IntentService作用是什么,AIDL解决了什么问题

Binder机制

如何导入外部数据库?

sqlite可以执行多线程操作吗?如何保证多线程操作数据库的安全性

谈一谈Proguard混淆技术

三、其他问题

OOM异常是否可以被try...catch捕获(或者可否用try-catch捕获Out Of Memory Error以避免其发生?)

try catch 中在catch返回,finally还会执行吗

抽象类接口区别

字节流和字符流的区别

Java中字节码是什么,Java中如何获取Class对象,在一个方法中传入 '类名.class'这是什么技术?

Java中一个静态方法可以复写吗,为什么?

Java中的内部类有哪些?有什么区别?

TCP和UPD的区别以及使用场景

简述一个设计模式的概念,并简要谈谈framework层哪些地方用到了什么设计模式


Android面试首先问问一些Android和Java的基础知识,其次可能会问一些算法问题,最后便会问你的项目经验,以及个人性格等方面的东西,以下是我个人整理的资料,欢迎阅读


一、基本问题

Android的四大组件以及作用

详细说明请点击此处

  • Activity:Activity是Android程序与用户交互的窗口,是Android构造块中最基本的一种,它需要为保持各界面的状态,做很多持久化的事情,妥善管理生命周期以及一些跳转逻辑。
  • service:后台服务于Activity,封装有一个完整的功能逻辑实现,接受上层指令,完成相关的动作,定义好需要接受的Intent提供同步和异步的接口。
  • Content Provider:是Android提供的第三方应用数据的访问方案,对外提供数据,屏蔽内部数据的存储细节,向外提供统一的借口模型,大大简化上层应用对数据的整合提供了更方便的途径。
  • BroadCast Receiver:接受一种或者多种Intent作触发事件,接受相关消息,做一些简单处理,转换成一条Notification,统一了Android的事件广播模型。

描述下Activity的生命周期?

详细说明请点击此处

Activity的生命周期方法有:onCreate()、onStart()、onReStart()、onResume()、onPause()、onStop()、onDestory();

Android 最全面试题汇总(问题+答案+详解链接)_第1张图片

屏幕旋转时的Activity生命周期:

  • 不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次;
  • 设置Activity的android:configChanges="orientation"时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次;
  • 设置Activity的android:configChanges="orientation|keyboardHidden"时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法。

Activity的启动模式

 Android:图解四种启动模式 及 实际应用场景解说

1.Standard 标准模式:

说明:每次启动一个Activity都会又一次创建一个新的实例入栈,无论这个实例是否存在。

生命周期:每次被创建的实例Activity 的生命周期符合典型情况,它的onCreate、onStart、onResume都会被调用。

2.SingleTop 栈顶复用模式:

说明:(1)要创建的Activity已经处于栈顶时,此时会直接复用栈顶的Activity。不会再创建新的Activity;

            (2)若须要创建的Activity不处于栈顶,此时会又一次创建一个新的Activity入栈,同Standard模式一样。

生命周期:情况一中栈顶的Activity被直接复用时,它的onCreate、onStart不会被系统调用,由于它并没有发生改变。可是一个新的方法 onNewIntent会被回调(Activity被正常创建时不会回调此方法)。

3.SingleTask 栈内复用模式:

说明:若须要创建的Activity已经处于栈中时,此时不会创建新的Activity,而是将存在栈中的Activity上面的其他Activity所有销毁,使它成为栈顶。

生命周期:同SingleTop 模式中的情况一同样。仅仅会又一次回调Activity中的 onNewIntent方法。

4.SingleInstance 单实例模式:

说明: 全局单例模式,系统会为它创建一个单独的任务栈,有此模式的Activity仅仅能单独位于一个任务栈中,是一种加强的SingleTask模式。这个经常使用于系统中的应用,比如Launch、锁屏键的应用等等,整个系统中仅仅有一个!所以在我们的应用中一般不会用到。了解就可以。

生命周期:如果已经ActivityA已经在堆栈中,那么此时会调用onNewIntent方法

问题:如果有三个Activity分别是A、B、C,其中B的启动模式为SingleInstance ,A、C的启动模式为Standard ,然后依次启动A、B、C三个Activity,问在C界面按返回键,回到哪个界面,再返回回到哪个界面?

答:C返回 回到A,再按返回键 回到B 之后再返回退出。

问题:依次启动活动A,B,C,D,其中BD的启动模式为SingleInstance ,AC的启动模式为Standard ,问按四次返回键,依次的退出顺序,如果BC的启动模式是SingleInstance ,AD的启动模式是Standard ,这时的退出顺序是怎样的?

答:第一种情况依次退出的顺序是D,C,A,B;第二种情况的退出顺序是D,A,C,B。

onNewIntent的调用时机

Android:图解四种启动模式 及 实际应用场景解说

当launchMode为singleTask的时候,如果创建的Activity已经处于栈中时,此时不会创建新的Activity,而是将存在栈中的Activity上面的其他Activity所有销毁,使它成为栈顶,这时会调用onNewIntent方法

当ActivityA已经启动过,处于当前应用的Activity堆栈中;当ActivityA的LaunchMode为SingleTop时,如果ActivityA在栈顶,且现在要再启动ActivityA,这时会调用onNewIntent方法

当ActivityA的LaunchMode为SingleInstance,SingleTask时,如果已经ActivityA已经在堆栈中,那么此时会调用onNewIntent方法

当ActivityA的LaunchMode为Standard时,由于每次启动ActivityA都是启动新的实例,和原来启动的没关系,所以不会调用原来ActivityA的onNewIntent方法,仍然调用的是onCreate方法

Service的两种启动方式 

https://blog.csdn.net/jinmie0193/article/details/81875178

startService:(启动服务):是由其他组件调用startService()方法启动的,其生命周期与启动它的组件无关,并且可以在后台无限期运行,即使启动服务的组件已经被销毁。因此,服务需要在完成任务后调用stopSelf()方法停止,或者由其他组件调用stopService()方法停止。注意,多次调用starService不会被嵌套,所以无论同一个服务被启动多少次,一旦调用stopService或者StopSelf,都会被停止。

bindService:使用bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止。
 

问:startService和bindService能不能混合使用?

答:

https://blog.csdn.net/ican87/article/details/82945867

https://blog.csdn.net/dfskhgalshgkajghljgh/article/details/51471108
当一个Service在被启动(startService)的同时又被绑定(bindService),该Service将 会一直在后台运行。

并且不管调用几次,onCreate方法始终只会调用一次, onStartCommand的调用次数与startService调用的次数一致(使用bindService方法 不会调用onStartCommand)。

Service的终止,需要unbindService和stopService同时调用才行(不管startService与bindService的调用顺序)。

如果先调用unbindService,此时服务不会自动终止,再调用stopService之后,服务才会终止;

如果先调用stopService,此时服务也不会终止,而再调用unbindService或者之前调用bindService的Context不存在了(如Activity被finish的时候)之后,服务才会自动停止

注册广播有几种方式,有何优缺点?

详细说明请点击此处

静态注册:在AndroidManifest文件中声明

  
            
            
              
              
  

动态注册:使用代码进行注册:

//new出定义好的BroadcastReceiver
MyBroadCastReceiver yBroadCastReceiver = new MyBroadCastReceiver();
//实例化过滤器并设置要过滤的广播  
IntentFilter intentFilter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
//注册广播   
myContext.registerReceiver(smsBroadCastReceiver,intentFilter,"android.permission.RECEIVE_SMS", null);  

两种注册类型的区别是:

  1. 静态注册无需担忧广播接收器是否被关闭,只要设备是开启状态,广播接收器也是打开着的。也就是说哪怕app本身未启动,该app订阅的广播在触发时也会对它起作用。
  2. 动态注册广播接收器特点是当用来注册的Activity关掉后,广播也就失效了。

本地广播和全局广播有什么差别?

https://www.jianshu.com/p/bfbb6ebc1c04

BroadcastReceiver是针对应用间、应用与系统间、应用内部进行通信的一种方式
LocalBroadcastReceiver仅在自己的应用内发送接收广播,也就是只有自己的应用能收到,数据更加安全广播只在这个程序里,而且效率更高。

BroadcastReceiver 使用

1.制作intent(可以携带参数)
2.使用sendBroadcast()传入intent;
3.制作广播接收器类继承BroadcastReceiver重写onReceive方法(或者可以匿名内部类啥的)
4.在java中(动态注册)或者直接在Manifest中注册广播接收器(静态注册)使用registerReceiver()传入接收器和intentFilter
5.取消注册可以在OnDestroy()函数中,unregisterReceiver()传入接收器

LocalBroadcastReceiver 使用

LocalBroadcastReceiver不能静态注册,只能采用动态注册的方式。
在发送和注册的时候采用,LocalBroadcastManager的sendBroadcast方法和registerReceiver方法

View的绘制流程

Android 自定义View(一):对现有控件的扩展

Android 自定义View(二):创建复合控件

Android 自定义View(三):重写View实现全新控件

Android自定义View设计之自己需要的控件

View的绘制流程:OnMeasure()——>OnLayout()——>OnDraw()

各步骤的主要工作:

OnMeasure():

测量视图大小。从顶层父View到子View递归调用measure方法,measure方法又回调OnMeasure。

OnLayout():

确定View位置,进行页面布局。从顶层父View向子View的递归调用view.layout方法的过程,即父View根据上一步measure子View所得到的布局大小和布局参数,将子View放在合适的位置上。

OnDraw():

绘制视图:ViewRoot创建一个Canvas对象,然后调用OnDraw()。六个步骤:①、绘制视图的背景;②、保存画布的图层(Layer);③、绘制View的内容;④、绘制View子视图,如果没有就不用;⑤、还原图层(Layer);⑥、绘制滚动条。

事件传递机制

这个有点啰嗦,请看我的另一篇博客:Android 事件拦截/分发机制 (图解+代码)

  1. Android事件分发机制的本质是要解决:点击事件由哪个对象发出,经过哪些对象,最终达到哪个对象并最终得到处理。这里的对象是指Activity、ViewGroup、View.
  2. Android中事件分发顺序:Activity(Window) -> ViewGroup -> View.
  3. 事件分发过程由dispatchTouchEvent() 、onInterceptTouchEvent()和onTouchEvent()三个方法协助完成
  •     事件传递顺序:        ViewGroupA    ->    ViewGroupB    ->    ViewC
  •     事件传递返回值:    true:拦截,不继续传递    false:不拦截,继续传递
  •     事件处理顺序:        ViewC    ->    ViewGroupB    ->    ViewGroupA 
  •     事件处理返回值:    true:处理了,不上报       false:处理了,但上报

    布局如下:

   Android 最全面试题汇总(问题+答案+详解链接)_第2张图片

Android Handler的机制和原理

Message,理解为线程间交流的信息

Handler,是Message的主要处理者,负责Message的发送和执行处理

Message Queue,用来存放通过Handler发布的消息,按照先进先出执行

Looper ,是每条线程里的Message Queue的管家

Handler一般在主线程中创建,子线程通过处理器对象的sendMessage发消息到

 

首先在主线程创建一个Handler对象 ,并重写handleMessage方法。

然后当在子线程中需要进行更新UI的操作,我们就创建一个Message对象,并通过handler发送这条消息出去。

之后这条消息被加入到MessageQueue队列中等待被处理,通过Looper对象会一直尝试从Message Queue中取出待处理的

消息,最后分发会Handler的handler Message方法中。

主线程中包含一个Looper(轮循器,死循环),会一直轮询消息队列,看是否有Message(消息) ,如果有,轮询器会把消息对象传给Handler(消息处理器),然后调用handlerMessage处理该消息,进而更新UI。


问题:Handler能不能在子线程使用

答:可以,首先要先要Looper.prepare()进行初始化,不然直接new Handler()会报错,提示Looper为Null,因为在主线程中会默认调用一个Looper.prepareMainLooper()方法,它会调用ooper.prepare(),完成Looper对象的创建。

 

Android中三种动画,特点和区别是什么?

Android 中的动画有帧动画,补间动画,属性动画。

  • 帧动画:一张张图片不断的切换,形成动画效果,类似小时候的电影。很多应用的loading是采用这种方式。
  • 补间动画:对某个View进行一系列的动画的操作,如淡入淡出(Alpha),缩放(Scale),平移(Translate),旋转(Rotate)等四种模式
  • 属性动画:

作用对象进行了扩展:不只是View对象,甚至没对象也可以,不再局限于 视图View对象

实现的动画效果:可自定义各种动画效果,不再局限于4种基本变换:平移、旋转、缩放 & 透明度

工作原理:在一定时间间隔内,通过不断对值进行改变,并不断将该值赋给对象的属性,从而实现该对象在该属性上的动画效果

属性动画详细介绍

ListView优化有哪几种方式?

1、ListView中item的布局至关重要,必须尽可能的减少使用的控件,布局。

2、同时要尽可能的复用控件,这样可以减少ListView的内存使用,减少滑动时GC次数。

3、ListView的背景色与cacheColorHint设置相同颜色,可以提高滑动时的渲染性能。

4、getView方法中不能做复杂的逻辑计算,特别是数据库操作,否则会严重影响滑动时的性能。

5、contentView的setTag(),传入一个viewHolder对象,用于缓存要显示的数据,可以达到图像数据异步加载的效果。

6、如果listview需要显示的item很多,就要考虑分页加载。比如一共要显示100条或者更多的时候,我们可以考虑先加载20条,等用户拉到列表底部的时候再去加载接下来的20条。

7、如果自定义适配器,那么在getView方法中要考虑方法传进来的参数contentView是否为null,如果为null就创建contentView并返回,如果不为null则直接使用。在这个方法中尽可能少创建view。

RecyclerView相比ListView有哪些优势

解析:

        从RecyclerView类名上看,RecyclerView只管回收与复用View,其他的你可以自己去设置。可以看出其高度的解耦,给予你充分的定制自由(所以你才可以轻松的通过这个控件实现ListView,GirdView,瀑布流等效果)

        其次RecyclerView提供了添加、删除item的动画 效果,而且可以自定义

        RecyclerView相比ListView优势在于可以轻松实现:ListView的功能、GridView的功能、横向ListView的功能、横向ScrollView的功能、瀑布流效果

便于添加Item增加和移除动画

        不过RecyclerView没有提供ClickListener和LongClickListener,但是我们也可以自己去添加,只是会多了些代码而已,可以通过mRecyclerView.addOnItemTouchListener去监听然后去判断手势,也可以通过adapter中自己去提供回调

 

二、进阶问题

ANR出现的场景及解决方案

ANR:Application Not Responding。

在Android中,应用的响应性被活动管理器(Activity Manager)和窗口管理器(Window Manager)这两个系统服务所监视,当用户操作的在5s内应用程序没能做出反应,BroadcastReceiver在10秒内没有执行完毕,就会出现应用程序无响应对话框,就是ANR。

比如当用户触发了输入事件(如键盘输入,点击按钮等),如果应用5秒内没有响应用户的输入事件,那么,Android会认为该应用无响应,便弹出ANR对话框。而弹出ANR异常,也主要是为了提升用户体验。

解决方案是对于耗时的操作,比如访问网络、访问数据库等操作,需要开辟子线程,在子线程处理耗时的操作,主线程主要实现UI的操作

响应时间限制:

  1. 应用在5秒内未响应用户的输入事件(如按键或者触摸)会发生ANR
  2. BroadcastReceiver未在10秒内完成相关的处理会发生ANR
  3. 前台Service在20秒内无法处理完成会发生ANR
  4. 后台Service在200s内没有处理完成会发生ANR

如果开发机器上出现问题,我们可以通过查看/data/anr/traces.txt即可,最新的ANR信息在最开始部分。

Android系统会监控程序的响应状况,一旦出现下面情况,则弹出ANR对话框:

1.应用在5秒内未响应用户的输入事件(如按键或者触摸)

2.BroadcastReceiver未在10秒内完成相关的处理

3.前台Service在20秒内无法处理完成会发生ANR

4.后台Service在200s内没有处理完成会发生ANR

5.使用Thread或者HandlerThread时,调用Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)设置优先级,否则仍然会降低程序响应,因为默认Thread的优先级和主线程相同。

6.使用Handler处理工作线程结果,而不是使用Thread.wait()或者Thread.sleep()来阻塞主线程。

7.Activity的onCreate和onResume回调中尽量避免耗时的代码

8.BroadcastReceiver中onReceive代码也要尽量减少耗时,建议使用IntentService处理。

9.主线程被IO操作(从4.0之后网络IO不允许在主线程中)阻塞。

10.主线程中存在耗时的计算

11.主线程中错误的操作,比如Thread.wait或者Thread.sleep等

一个线程可以有几个Handler、Looper、MessageQueue,不同的Handler如何通信?

个数:

handler:可以有多个

looper:一个线程只有一个

messageQuese:一个线程只有一个

不同的Handler如何与他的msg绑定的:

https://www.cnblogs.com/txlbupt/p/4169076.html

由于一个线程中可能有多个handler,为了区分这些不同的hanlder所需要处理的消息,每个Message对象都维护有一个hanlder实例即target,Message通过target属性标识handler。在loop方法中通过调用msg.target.dispathMessage(msg)方法将消息分发到与Message绑定handler.handleMessage()方法中。 所以一个handler发送的消息只会被自己接收。

为啥要用Handler,子线程中如何使用Handler

为啥要用Handler:

因为Android  UI线程不安全。因为Android中UI线程操作并没有加锁,也就是说可能在非UI线程中刷新界面的时候,UI线程(或者其他非UI线程)也在刷新界面.这样就导致多个界面刷新的操作不能同步,导致线程不安全。如果,我们直接更新UI,但你不能做过多的耗时操作,否则你会使UI卡顿。

子线程中如何使用Handler:

https://blog.csdn.net/haoyuegongzi/article/details/82315866

https://blog.csdn.net/qq_27070117/article/details/81183118

主线程可以直接使用handler,因为主线程默认通过Looper.prepareMainLooper()调用了Looper的prepare()方法完成Looper对象的创建,而子线程中并没有创建lopper对象,所以需要先调用Looper.prepare(),之后再创建Handler对象,最后调用Looper.looper(),不断的读出消息。(注意: Looper.loop()之后的位置代码在Looper退出之前不会执行,并非永远不执行)

注意:Android中一个线程最多只能有一个Looper,若在已有Looper的线程中调用Looper.prepare()会抛出looper已经创建的异常。

自定义Handler时如何避免内存泄漏

原因:一般是非静态内部类持有外部类的引用的情况下,造成外部类在使用完成后不能被系统回收内存,从而导致内存泄漏。

解决:自定义Handler,声明为静态内部类,然后通过弱引用的方式,让Handler之后外部类的弱引用,从而避免内存泄漏。

java 强、软、弱、虚引用讲解

代码举例:

方式一:

public class HandlerDemo {

    public static final int HANDLER_DMEO = 1;

    private MyHandler mMyHandler = null;

    public HandlerDemo() {
        mMyHandler = new MyHandler(this);
        mMyHandler.sendEmptyMessage(HANDLER_DMEO);
    }

    public void updateData() {

    }

    // 静态内部类
    public static class MyHandler extends Handler {

        //弱引用
        WeakReference mWeakReference;

        MyHandler(HandlerDemo handlerDemo) {
            mWeakReference = new WeakReference<>(handlerDemo);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            HandlerDemo handlerDemo = mWeakReference.get();
            if (handlerDemo == null) {
                return;
            }
            switch (msg.what) {
                case HANDLER_DMEO:
                    // 通过弱引用的方式对外部类操作
                    handlerDemo.updateData();
                    break;
                default:
            }

        }
    }
}

方式二

public class DemoActivity extends Activity {

    public static final int HANDLER_DEMO = 1;
    private WeakReference mWeakReference;
    private MyHandler                   myHandler;
    private TextView                    mTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextView = findViewById(R.id.mvMainTestBtn);
        
        mWeakReference = new WeakReference<>(this);
        myHandler = new MyHandler(mWeakReference);
        myHandler.sendEmptyMessage(HANDLER_DEMO);
    }

    static class MyHandler extends Handler {
        private DemoActivity activity;

        MyHandler(WeakReference ref) {
            this.activity = ref.get();
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 1:
                    //需要做判空操作
                    if (activity != null) {
                        activity.mTextView.setText("new Value");
                    }
                    break;
                default:
            }
        }
    }
}

产生内存泄露的情况

内存泄漏详细介绍文章

  • 非静态内部类导致内存泄漏:内部类中隐式的持有了外部类的引用,当外部类被销毁后,由于内部类仍旧强引用持有了外部类,因此外部类不能被及时回收,造成内存泄漏问题。
  • 生命周期不统一导致内存泄漏:如ObjB 引用了 ObjA,但是 ObjB 比 ObjA 生命更长,当 ObjA 自己销毁时,由于 ObjB 还在活跃,导致 ObjA 无法被回收。
  1. 建议对于生命周期不可控的内部类,采用静态声明,结合虚引用来将内部类和外部类隔离

  • 静态引用导致内存泄漏:静态引用的变量不依赖于某个类实例,所以他不会随着某个实例的销毁而随之销毁,类似生命周期不统一造成的问题。
  • 资源回收不及时:指的是一些对象我们使用完后要及时关闭、回收或者解除注册,来保证内存可以被及时回收,

如:Cursor、IO 流、Bitmap、Animation、BroadcastReceiver

// Cursor
Cursor cursor = ...;
cursor.close();

// Stream
FileOutputStream outputStream = ...
outputStream.close();

// Bitmap
Bitmap bitmap = ...;
bitmap.recycle();

// Animation
Animation animation = ...;
animation.cancel();
animation.setAnimationListener(null);

// BroadcastReceiver
BroadcastReceiver broadcastReceiver = ...
context.unregisterReceiver(broadcastReceiver);

// byte
byte[] bytes = new byte[1024 * 8];
bytes = null;
  • 重复创建对象:在代码中常常有一些循环操作和发生频率比较高的操作,在这类操作中应该尽量避免创建对象,虽然不会导致内存泄漏但是频繁创建和销毁对象会占用大量内存和引起显著的内存抖动
  1. 1)循环,开发过程中,循环次数往往不可控,应该避免在循环中创建新的对象。
    
    2)字符串拼接操作,每次字符串拼接都会产生一个新的字符串,因此如果有频繁的拼接操作,请使用 StringBuilder。
    
    3)方法名的定义导致使用的不当,在 getXXX() 方法中不应该进行创建对象的操作,
    如果有,则要加入仅创建一次的判断,因为对使用者来说,这只是一个获取操作
        private ViewModel mViewModel;
    
        // 不应该在 get 方法中不加判断的创建对象
        public ViewModel getViewModel() {
            return new ViewModel();
        }
    
        // 如果仅执行创建新对象的操作应该命名为 newXXX()
        public ViewModel newViewModel() {
            return new ViewModel();
        }
    
        // 如果一定要使用 get() 方法,需要增加创建一次的判读
        public ViewModel getViewModel() {
           if(mViewModel == null){
               mViewModel = new ViewModel();
           }
           return mViewModel;
        }
  •  
  • 静态引用 Context 导致内存泄漏:Android 中静态引用 Context 也是内存泄漏重灾区,
  • 如下列举常见的几种可能静态引用 Context 的场景如:

    1)某个对象没有静态引用 Context,但是这个对象在其他位置被静态引用了,导致 Context 间接的静态引用。

    2)单例,单例其实也是静态的引用,不能在单例中引用 Context。

    3)Toast,为了管理 Toast,比如避免大量 Toast 排队通常会写一个 ToastUtils,里面就会静态持有 Toast 对象,而 Toast 中是引用了 Context 的。

    4)View,由于 View 中引用了 Context,静态的 View 就很危险。

    5)Animator,属性动画通常绑定到一个 View 上面,静态引用 Animator 相当于静态引用了 View。

    6)Animation,补间动画中并没有显式的引用 View 或者 Context,但是他有一个 mListenerHandler,这个监听在 View.draw() 方法中,如果当前 View 的 Animation 不为空,会给他一个 mAttachInfo.mHandler,而这里面引用了 ViewRootImpl.mContext。

    7)特别注意静态引用的集合数据类型,如 List 和 Map,里面通常会存储大量的对象,如果这些对象中有某些对象引用了 Context,同样会造成内存泄漏。

  1. 1)当不可避免的需要静态引用 Context 时,使用虚引用代替,

  2. WeakReference mContextWeakRef;
    mContextWeakRef = new WeakReference<>(getContext());
    

     2)试着使用关于application的context来替代和activity相关的context

  3. Context appContext = context.getApplicationContext();
    Application application = (Application) context.getApplicationContext();

查找内存泄漏

  • Android Profiler

使用 AndroidStudio,通过 View -> ToolWindows -> Android Profiler,可以查看内存、网络、CPU 变化情况,还可以 Dump 内存记录,用于内存分析。

  • Memory View

使用 AndroidStudio,通过 View -> ToolWindows -> Memory View,可以结合断点调试 dump 指定断点处的内存使用情况,进行内存分析。

  • 内存监控

当应用内存不足时,会调用 Application 的 onTrimMemory() 方法,我们可以在这里做一些清理内存的操作,避免内存过大造成 OOM

public class MyApplication extends BaseApplication {

    @Override
    public void onTrimMemory(int level) {
        super.onTrimMemory(level);
    }   
}
  • LeakCanary-GitHub : A memory leak detection library for Android and Java.

LeakCanary 是 squre 开源的一个用于在 Android 和 Java 平台下检测内存泄漏的工具,提供了两种依赖方式,在 release 版本下不会进行内存泄漏的检测,避免性能问题。

// 依赖
compile "com.squareup.leakcanary:leakcanary-android-no-op:1.5.1"
debugCompile "com.squareup.leakcanary:leakcanary-android:1.5.1"

在 Application 中初始化  

public class MyApplication extends BaseApplication {

    @Override
    public void onCreate() {
        super.onCreate();
        initLeakCanary();
    }

    private RefWatcher mRefWatcher;

    public void initLeakCanary() {
        if (LeakCanary.isInAnalyzerProcess(this)) {
            return;
        }
        mRefWatcher = LeakCanary.install(this);
    }

    public static RefWatcher getRefWatcher(Context context) {
        MyApplication application = (MyApplication) context.getApplicationContext();
        return application.mRefWatcher;
    }

}

初始化之后就可以自动检测 Activity 的内存泄露问题,如果需要检测 Fragment 等其他对象的内存问题,需要在希望对象被回收的时候注册检测监听 

public class MyFragment extends BaseFragment {
    @Override
    public void onDestroy() {
        super.onDestroy();
        MyApplication.getRefWatcher(getContext()).watch(this,"fragment");
    }
}

 

简述项目中对于内存优化的几个细节点

内存优化文章请点击此处

1.当查询完数据库之后,及时关闭Cursor对象。

2.记得在Activity的onPause方法中调用unregisterReceiver方法,反注册广播

3.尽量不要在Activity中使用非静态内部类,因为非静态内部类会隐式持有外部类实例的引用,当非静态内部类的引用的声明周期长于Activity的声明周期时,会导致Activity无法被GC正常回收掉。

4.使用Handler时,要么是放在单独的类文件中,要么就是使用静态内部类。因为静态的内部类不会持有外部类的引用,所以不会导致外部类实例的内存泄露

5.谨慎使用线程Thread!!当使用线程时,一定要考虑在Activity退出时,及时将线程也停止并释放掉。因为: Java中的Thread都是直接被GC Root所引用,也就是说Dalvik虚拟机对所有被激活状态的线程都是持有强引用,导致GC永远都无法回收掉这些线程对象,除非线程被手动停止并置为或者用户直接kill进程操作。

Android动画框架实现原理

Animation框架定义了透明度,旋转,缩放和位移几种常见的动画,而且控制的是整个View。

实现原理

实现原理是每次绘制视图时,View所在的ViewGroup中的drawChild函数获取该View的Animation的Transformation值,

然后调用canvas.concat(transformToApply.getMatrix()),通过矩阵运算完成动画帧,

如果动画没有完成,继续调用invalidate()函数,启动下次绘制来驱动动画,

动画过程中的帧之间间隙时间是绘制函数所消耗的时间,可能会导致动画消耗比较多的CPU资源

进程保活

https://www.jianshu.com/p/63aafe3c12af

当前业界的Android进程保活手段主要分为** 黑、白、灰 **三种,其大致的实现思路如下:

黑色保活:不同的app进程,用广播相互唤醒(包括利用系统提供的广播进行唤醒)

场景1:开机,网络切换、拍照、拍视频时候,利用系统产生的广播唤醒app

场景2:接入第三方SDK也会唤醒相应的app进程,如微信sdk会唤醒微信,支付宝sdk会唤醒支付宝。由此发散开去,就会直接触发了下面的 场景3

场景3:假如你手机里装了支付宝、淘宝、天猫、UC等阿里系的app,那么你打开任意一个阿里系的app后,有可能就顺便把其他阿里系的app给唤醒了。(只是拿阿里打个比方,其实BAT系都差不多)

白色保活:启动前台Service,类似网易云音乐播放时,通知栏会有显示。

调用系统api启动一个前台的Service进程,这样会在系统的通知栏生成一个Notification,用来让用户知道有这样一个app在运行着,哪怕当前的app退到了后台。

灰色保活:利用系统的漏洞启动前台Service,这种保活手段是应用范围最广泛。

它是利用系统的漏洞来启动一个前台的Service进程,与普通的启动方式区别在于,它不会在系统通知栏处出现一个Notification,看起来就如同运行着一个后台Service进程一样。这样做带来的好处就是,用户无法察觉到你运行着一个前台进程(因为看不到Notification),但你的进程优先级又是高于普通后台进程的

  • 思路一:API < 18,启动前台Service时直接传入new Notification();
  • 思路二:API >= 18,同时启动两个id相同的前台Service,然后再将后启动的Service做stop处理;

 进程的优先级

https://segmentfault.com/a/1190000006251859

划分5级:(前天进程优先级最高,之后依次降低)

前台进程 (Foreground process)

可见进程 (Visible process)

服务进程 (Service process)

后台进程 (Background process)

空进程 (Empty process)

进程间通信的方式 :AIDL、 广播、Messenger 、Binder

AIDL (Android Interface Definition Language): 一种实现进程间通信的语言,用AIDL书写的文件的后缀是 .aidl,而不是 .java。

https://www.jianshu.com/p/a8e43ad5d7d2 ,https://www.jianshu.com/p/0cca211df63c 
Messenger :实现了基于消息的进程间通信的方式,支持回调,不需要编写aidl文件,可以实现一对多的通信

https://blog.csdn.net/lmj623565791/article/details/47017485
Binder :Android系统的Binder机制,由Client、Server、Service Manager和Binder驱动程序组成。

https://blog.csdn.net/luoshengyang/article/details/6618363/

IntentService作用是什么,AIDL解决了什么问题

1、IntentService是什么?

  • 一个封装了HandlerThread和Handler的异步框架。
  • 是一种特殊Service,继承自Service,是抽象类,必须创建子类才可以使用。
  • 可用于执行后台耗时的任务,任务执行后会自动停止
  • 具有高优先级(服务的原因),优先级比单纯的线程高很多,适合高优先级的后台任务,且不容易被系统杀死。
  • 启动方式和Service一样。
  • 可以多次启动,每个耗时操作都会以工作队列的方式在IntentService的onHandleIntent回调方法中执行。
  • 串行执行。
  • 该服务提供了一个onBind()方法的默认实现,它返回null

2、IntentService可以执行大量的耗时操作?

  • 如果只有一个任务,是可以进行耗时操作的。
  • 如果有很多任务,由于内部的HandlerThread是串行执行任务,会导致耗时操作阻塞了后续任务的执行。

3、IntentService和Service的区别

  • 继承自Service
  • IntentService任务执行完后会自动停止
  • IntentService和Service优先级一致,比Thread高。
  • Service处于主线程不能直接进行耗时操作; IntentService内部有HandlerThread,可以进行耗时操作。

4、工作流程

  • IntentService默认生成一个与主线程互相独立的工作者线程来执行所有传送至onStartCommand() 方法的Intetnt,
  • 它将Intent先传送至工作队列,然后从工作队列中每次取出一个传送至onHandleIntent()方法,在该方法中对Intent对相应的处理,同一时刻只传送一个Intent对象,这样一来,你就不必担心多线程的问题。
  • 在所有的请求(Intent)都被执行完以后会自动停止服务,所以,你不需要自己去调用stopSelf()方法来停止。

AIDL (Android Interface Definition Language) 是一种IDL 语言,用于生成可以在Android设备上两个进程之间进行进程间通信(interprocess communication, IPC)的代码。如果在一个进程中(例如Activity)要调用另一个进程中(例如Service)对象的操作,就可以使用AIDL生成可序列化的参数。

AIDL IPC机制是面向接口的,像COM或Corba一样,但是更加轻量级。它是使用代理类在客户端和实现端传递数据。

Binder机制

1.了解Binder

在Android系统中,每一个应用程序都运行在独立的进程中,这也保证了当其中一个程序出现异常而不会影响另一个应用程序的正常运转。在许多情况下,activity都会与系统的各种service打交道,而程序中activity与系统service肯定不是同一个进程,实现进程间通信(IPC)的方式之一:Binder

1).首先,Binder分为Client和Server两个进程。

注意,Client和Server是相对的。谁发消息,谁就是Client,谁接收消息,谁就是Server。

2).其次,下图Binder的组成解构

Android 最全面试题汇总(问题+答案+详解链接)_第3张图片

图中的IPC就是进程间通信的意思。

图中的ServiceManager,负责把Binder Server注册到一个容器中。

比喻:小明给小亮打电话,那么小明便是Binder Client小亮便是Binder Server,拨打电话号码,会先转接到电话局,那么电话局便是ServiceManager,如果电话局的接线员便能查到这个电话号码的地址(接线员便是Binder驱动),即小亮的电话号在电话局注册过(ServiceManager把Binder Server注册到一个容器过)则能拨通,即可以实现进程通信,反之无法实现进程通信

 3).Binder通信的过程

Android 最全面试题汇总(问题+答案+详解链接)_第4张图片

 

注:图中的SM也就是ServiceManager。

我们看到,Client想要直接调用Server的add方法,是不可以的,因为它们在不同的进程中,这时候就需要Binder来帮忙了。

首先是Server在SM这个容器中注册。

其次,Client想要调用Server的add方法,就需要先获取Server对象, 但是SM不会把真正的Server对象返回给Client,而是把Server的一个代理对象返回给Client,也就是Proxy。

然后,Client调用Proxy的add方法,SM会帮他去调用Server的add方法,并把结果返回给Client。

Binder驱动的底层是C++实现,一般不需要了解。

2. Binder的优点

 

1).可靠性。在移动设备上,通常采用基于Client-Server的通信方式来实现互联网与设备间的内部通信。目前linux支持IPC包括传统的管道,System V IPC,即消息队列/共享内存/信号量,以及socket中只有socket支持Client-Server的通信方式。Android系统为开发者提供了丰富进程间通信的功能接口,媒体播放,传感器,无线传输。

这些功能都由不同的server来管理。开发都只关心将自己应用程序的client与server的通信建立起来便可以使用这个服务。毫无疑问,如若在底层架设一套协议来实现Client-Server通信,增加了系统的复杂性。在资源有限的手机 上来实现这种复杂的环境,可靠性难以保证。

2).传输性能。socket主要用于跨网络的进程间通信和本机上进程间的通信,但传输效率低,开销大。消息队列和管道采用存储-转发方式,即数据先从发送方缓存区拷贝到内核开辟的一块缓存区中,然后从内核缓存区拷贝到接收方缓存区,其过程至少有两次拷贝。虽然共享内存无需拷贝,但控制复杂。比较各种IPC方式的数据拷贝次数。共享内存:0次。Binder:1次。Socket/管道/消息队列:2次。

3).安全性。Android是一个开放式的平台,所以确保应用程序安全是很重要的。Android对每一个安装应用都分配了UID/PID,其中进程的UID是可用来鉴别进程身份。传统的只能由用户在数据包里填写UID/PID,这样不可靠,容易被恶意程序利用。而我们要求由内核来添加可靠的UID。

所以,出于可靠性、传输性、安全性。android建立了一套新的进程间通信方式。

如何导入外部数据库?

  1. 把原数据库包括在项目源码的 res/raw下,用FileInputStream读取原数据库,再用FileOutputStream把读取到的东西写入到那个目录.
  2. android系统下数据库应该存放在 /data/data/com..(package name)/ 目录下,所以我们需要做的是把已有的数据库传入那个目录下.

sqlite可以执行多线程操作吗?如何保证多线程操作数据库的安全性

  • 每当你需要使用数据库时,需要使用DatabaseManager的openDatabase方法来取得数据库,这个方法里面使用了单例模式,保证了数据库对象的唯一性,也就是每次操作数据库时所使用的sqlite对象都是一致得到。
  • 其次,我们会使用一个引用计数来判断是否要创建数据库对象。如果引用计数为1,则需要创建一个数据库,如果不为1,说明我们已经创建过了。
  • 在closeDatabase方法中我们同样通过判断引用计数的值,如果引用计数降为0,则说明我们需要close数据库。
  • 在多线程访问的情况下需要自己来封装一个DatabaseManager来管理Sqlite数据库的读写,需要同步的同步,需要异步的异步,不要直接操作数据库,这样很容易出现因为锁的问题导致加锁后的操作失败。

谈一谈Proguard混淆技术

Proguard技术有如下功能:

  • 压缩 --检查并移除代码中无用的类
  • 优化--对字节码的优化,移除无用的字节码
  • 混淆--混淆定义的名称,避免反编译
  • 预监测--在java平台对处理后的代码再次进行检测

代码混淆只在上线时才会用到,debug模式下会关闭,是一种可选的技术。

那么为什么要使用代码混淆呢?

因为Java是一种跨平台的解释性开发语言,而java的源代码会被编译成字节码文件,存储在.class文件中,由于跨平台的需要,java的字节码中包含了很多源代码信息,诸如变量名、方法名等等。并且通过这些名称来访问变量和方法,这些变量很多是无意义的,但是又很容易反编译成java源代码,为了防止这种现象,我们就需要通过proguard来对java的字节码进行混淆,混淆就是对发布的程序进行重新组织和处理,使得处理后的代码与处理前的代码有相同的功能,和不同的代码展示,即使被反编译也很难读懂代码的含义,哪些混淆过的代码仍能按照之前的逻辑执行得到一样的结果。

但是,某些java类是不能被混淆的,比如实现了序列化的java类是不能被混淆的,否则反序列化时会出问题。

下面这类代码混淆的时候要注意保留,不能混淆。

  • Android系统组件,系统组件有固定的方法被系统调用。
  • 被Android Resource 文件引用到的。名字已经固定,也不能混淆,比如自定义的View 。
  • Android Parcelable ,需要使用android 序列化的。

其他Anroid 官方建议 不混淆的,如

  • android.app.backup.BackupAgentHelper
  • android.preference.Preference
  • com.android.vending.licensing.ILicensingService
  • Java序列化方法,系统序列化需要固定的方法。
  • 枚举 ,系统需要处理枚举的固定方法。
  • 本地方法,不能修改本地方法名
  • annotations 注释
  • 数据库驱动
  • 有些resource 文件

三、其他问题

OOM异常是否可以被try...catch捕获(或者可否用try-catch捕获Out Of Memory Error以避免其发生?)

补充:

error 表示恢复很困难的一种严重问题。如内存溢出。不可能指望程序能处理这样的情况。

exception 表示一种设计或实现问题。它表示如果程序运行正常,从不会发生的情况。

程序只会catch住异常,如某个对象为null,你调用了对象里面的方法,这种属于异常,而对于错误是无法catch住的。

答:当然是不可以的,Error都不能被捕获,OOM这种更是不会被捕获的,不能阻止这个过程。

android 可以全局捕获异常,但是捕获到了也意味着崩溃了,不能阻止这个过程,OOM 这种是不会被捕获的,直接就闪退了,全局捕获的也是异常 exception,不是error.

只有在一种情况下,可以捕获:在try语句中声明了很大的对象,导致OOM,并且可以确认OOM是由try语句中的对象声明导致的,那么在catch语句中,可以释放掉这些对象,解决OOM的问题,继续执行剩余语句。

但是这通常不是合适的做法。Java中管理内存除了显式地catch OOM之外还有更多有效的方法:比如SoftReference, WeakReference, 硬盘缓存等。在JVM用光内存之前,会多次触发GC,这些GC会降低程序运行的效率。如果OOM的原因不是try语句中的对象(比如内存泄漏),那么在catch语句中会继续抛出OOM

try catch 中在catch返回,finally还会执行吗

https://www.cnblogs.com/fery/p/4709841.html

https://blog.csdn.net/qq_36347817/article/details/80148270

1、不管有没有出现异常,finally块中代码都会执行;

2、当try和catch中有return时,finally也仍然会执行;

3、try或catch中遇到return时,会准备返回,但不会立刻返回,此时程序会去执行finally语句,如果finally中没有return,则程序会跳转到try或catch中return的地方继续执行。

抽象类接口区别

https://blog.csdn.net/jinmie0193/article/details/82084424

速度:

抽象类比接口速度要快,接口是稍微有点慢的,因为它需要时间去寻找在类中实现的方法。

与正常Java类的区别:

除了你不能实例化抽象类之外,它和普通Java类没有任何区

接口是完全不同的类型

构造函数:

抽象类可以有构造方法,接口中不能有构造方法。

继承/实现:

一个类可以实现多个接口,但只能继承一个抽象类。

子类使用extends关键字来继承抽象类。如果子类不是抽象类的话,它需要提供抽象类中所有声明的方法的实现。

子类使用关键字implements来实现接口。它需要提供接口中所有声明的方法的实现

成员变量:

抽象类中可以有普通成员变量,接口中没有普通成员变量。

抽象类和接口中都可以包含静态成员变量

抽象类中的静态成员变量的访问类型可以任意

接口中定义的变量只能是public static final类型,并且默认即为public static final类型。

方法:

抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的

抽象类中可以包含静态方法,接口中不能包含静态方法

添加新方法:

 

如果你往抽象类中添加新的方法,你可以给它提供默认的实现。因此你不需要改变你现在的代码。

如果你往接口中添加方法,那么你必须改变实现该接口的类。

方法访问修饰符:

抽象方法可以有public、protected和default这些修饰符

接口方法默认修饰符是public。你不可以使用其它修饰符。

 

字节流和字符流的区别

  • 字节流操作的基本单元为字节;字符流操作的基本单元为Unicode码元(2个字节)。
  • 字节流默认不使用缓冲区;字符流使用缓冲区。
  • 字节流通常用于处理二进制数据,实际上它可以处理任意类型的数据,但它不支持直接写入或读取Unicode码元;字符流通常处理文本数据,它支持写入及读取Unicode码元。

Java中字节码是什么,Java中如何获取Class对象,在一个方法中传入 '类名.class'这是什么技术?

字节码:

我们写的java文件,通过编译器编译成java字节码文件,即.class文件。java虚拟机执行的就是字节码文件。不论该字节码文件来自何方,由哪种编译器编译,甚至是手写字节码文件,只要符合java虚拟机的规范,那么它就能够执行该字节码文件,所以这样使得java可以一次编译到处运行”。

获取class对象:

https://www.cnblogs.com/hunt/p/7067141.html

第一种:通过类名获得:Class class = ClassName.class; // 不做类的初始化工作.返回Class的对象。

第二种:通过类名全路径获得:Class class = Class.forName("类名全路径"); // (注:类名字符串是包名+类名)  说明:装入类,并做类的静态初始化,返回Class的对象。

第三种:通过实例对象获得:Class class = object.getClass(); // 对类进行静态初始化、动态初始化;返回引用运行时真正所指的对象。

技术:java反射(反射就是把java类中的各种成分映射成一个个的Java对象),通过反射机制强制转换类型用的。

Java中一个静态方法可以复写吗,为什么?

静态方法是类在加载时就被加载到内存中的方法,在整个运行过程中保持不变,因而不能重写。但非静态方法是在对象实例化时才单独申请内存空间,为每一个实例分配独立的运行内存,因而可以重写

Java中的内部类有哪些?有什么区别?

https://www.cnblogs.com/aademeng/articles/6192954.html

一.常规内部类:常规内部类没有用static修饰且定义在在外部类类体中。

1.常规内部类中的方法可以直接使用外部类的实例变量和实例方法。

2.在常规内部类中可以直接用内部类创建对象

二.静态内部类:与类的其他成员相似,可以用static修饰内部类,这样的类称为静态内部类。

1.静态内部类与静态内部方法相似,只能访问外部类的static成员,不能直接访问外部类的实例变量,与实例方法,只有通过对象引用才能访问。

2.由于static内部类不具有任何对外部类实例的引用,因此static内部类中不能使用this关键字来访问外部类中的实例成员,但是可以访问外部类中的static成员。这与一般类的static方法想通

三.局部内部类:在方法体或语句块(包括方法、构造方法、局部块或静态初始化块)内部定义的类成为局部内部类。局部内部类不能加任何访问修饰符,因为它只对局部块有效。

1.局部内部类只在方法体中有效,就想定义的局部变量一样,在定义的方法体外不能创建局部内部类的对象

2.在方法内部定义类时,应注意以下问题:
   1.方法定义局部内部类同方法定义局部变量一样,不能使用private、protected、public等访问修饰说明符修饰,也不能使用static修饰,但可以使用final和   abstract修饰
   2.方法中的内部类可以访问外部类成员。对于方法的参数和局部变量,必须有final修饰才可以访问。
   3.static方法中定义的内部类可以访问外部类定义的static成员

四.匿名内部类:定义类的最终目的是创建一个类的实例,但是如果某个类的实例只是用一次,则可以将类的定义与类的创建,放到与一起完成,或者说在定义类的同时就创建一个类,以这种方法定义的没有名字的类成为匿名内部类。
   声明和构造匿名内部类的一般格式如下:

new ClassOrInterfaceName(){
    /*类体*/ 
}

   1.匿名内部类可以继承一个类或实现一个接口,这里的ClassOrInterfaceName是匿名内部类所继承的类名或实现的接口名。但匿名内部类不能同时实现一个接口和继承一个类,也不能实现多个接口。如果实现了一个接口,该类是Object类的直接子类,匿名类继承一个类或实现一个接口,不需要extends和implements关键字。
  
   2.由于匿名内部类没有名称,所以类体中不能定义构造方法,由于不知道类名也不能使用关键字来创建该类的实例。实际上匿名内部类的定义、构造、和第一次使用都发生在同样一个地方。此外,上式是一个表达式,返回的是一个对象的引用,所以可以直接使用或将其复制给一个对象变量。

TCP和UPD的区别以及使用场景

 

TCP

UDP

是否面向连接

是否可靠

是否广播

效率

TCP与UDP基本区别

1.基于连接与无连接

2.TCP要求系统资源较多,UDP较少;

3.UDP程序结构较简单

4.流模式(TCP)与数据报模式(UDP);

5.TCP保证数据正确性,UDP可能丢包

6.TCP保证数据顺序,UDP不保证

UDP应用场景:

1.面向数据报方式

2.网络数据大多为短消息

3.拥有大量Client

4.对数据安全性无特殊要求

5.网络负担非常重,但对响应速度要求高

简述一个设计模式的概念,并简要谈谈framework层哪些地方用到了什么设计模式

单例模式: 单例模式是一种对象创建模式,它用于产生一个对象的具体实例,它可以确保系统中一个类只产生一个实例。

单例模式详细介绍

适配器模式: 将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,又称包装器(Wrapper)

装饰模式:动态地给一个对象增加一些额外的职责,就增加对象功能来说,装饰模式比生成子类实现更为灵活。装饰模式是一种对象结构型模式。

使用场景:

1.在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。

2.当不能采用继承的方式对系统进行扩展或者采用继承不利于系统扩展和维护时可以使用装饰模式。

优点:

1.对于扩展一个对象的功能,装饰模式比继承更加灵活,不会导致类的个数急剧增加。

2.可以通过一种动态地方式来扩展一个对象的功能。

3.可以对一个对象进行多次装饰,通过使用不同的具体装饰类以及这些装饰类的排列组合。

实际运用:

Android中Context类的实现

外观模式:主要目的在于让外部减少与子系统内部多个模块的交互,从而让外部能够更简单得使用子系统。它负责把客户端的请求转发给子系统内部的各个模块进行处理。

使用场景:

1.当你要为一个复杂的子系统提供一个简单的接口时

2.客户程序与抽象类的实现部分之前存在着很大的依赖性

3.当你需要构建一个层次结构的子系统时

组合模式:将对象以树形结构组织起来,以达成”部分--整体”的层次结构,使得客户端对单个对象和组合对象的使用具有一致性。

使用场景:

1.需要表示一个对象整体或部分 层次

2.让客户端能够忽略不同对象层次的变化

优点:

1.高层模块调用简单

2.节点自由增加

模板模式:是通过一个算法骨架,而将算法中的步骤延迟到子类,这样子类就可以复写这些步骤的实现来实现特定的算法。它的使用场景:

1.多个子类有公有的方法,并且逻辑基本相同2.重要、复杂的算法,可以把核心算法设计为模板方法

3.重构时,模板方法模式是一个经常使用的模式

观察者模式:定义对象之间一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。其使用场景:

1.一个抽象模型有两个方面,其中一个方面依赖于另一个方面2.一个对象的改变将导致一个或多个其他对象也 发生改变

3.需要在 系统中创建一个 触发链

具体应用:

比如回调模式中,实现了抽象类/接口的实例实现了父类提供的抽象方法后,将该方法交还给父类来处理

Listview中的notifyDataSetChanged

RxJava中的观察者模式

责任链模式: 是一个请求有多个对象来处理,这些对象是一条链,但具体由哪个对象来处理,根据条件判断来确定,如果不能处理会传递给该链条中的下一个对象,直到有对象处理它为止。

使用场景:

1.有多个对象可以处理同一个请求,具体哪个对象处理该请求待运行时再确定

2.在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。

实际运用:

Try...catch语句

OrderedBroadcast

MotionEvent:actionDwon actionMove actionUp

事件分发机制三个重要方法:dispatchTouchEvent. onInterceptTouchEvent. onTouchEvent

策略模式:定义一系列的算法,把它们一个个封装起来,并且使他们可互相替换。本模式使得算法可独立于使用它的客户而变化。策略模式的使用场景:一个类定义了多种行为,并且这些行为在这个类的方法中以多个条件语句的形式出现,那么可以使用策略模式避免在类中使用大量的条件语句。

使用场景:一个类定义了多种行为,并且这些行为在这个类的方法中以多个条件语句的形式出现,那么可以使用策略模式避免在类中使用大量的条件语句。

优点:

1.上下文Context和具体策略ConcreateStrategy是松耦合关系

2.策略模式满足 开-闭原则

具体应用:

HttpStack

Volley框架

 


参考链接:

月薪20+的Android面试都问些什么?(含答案)

Android 面试常问七道题

Android面试的准备与经历分享 | 百度offer(上)

Android面试的准备与经历分享 | 百度offer(下)

Android面试必备26题(阿里腾讯总结)

多家一线互联网公司Android面试题:小米+百度+360+美团

Android高级开发面试题目,再也不用担心不能升职加薪了

2018 Android面试心得,已拿到offer

Android2017-2018最新面试题(3-5年经验个人面试经历)

Android(2017-2018)BAT面试题整理(java篇,含答案) 

Android(2017-2018)BAT面试题整理(Android篇,含答案) 

小米Android面试总结

小米面试题

小米的面试过程及面试题~


Android中经常会问的一些重点

  • 基础知识 – 四大组件(生命周期,使用场景,如何启动)
  • java基础 – 数据结构,线程,mvc,mvp,mvvm框架
  • 通信 – 网络连接(HttpClient,HttpUrlConnetion),Socket
  • 数据持久化 – SQLite,SharedPreferences,ContentProvider
  • 性能优化 – 布局优化,内存优化,电量优化
  • 安全 – 数据加密,代码混淆,WebView/Js调用,https
  • UI– 动画,触摸事件传递机制
  • 其他 – JNI,AIDL,Handler,Intent等
  • 开源框架 – Volley,Gilde,RxJava等(简历上写你会的,用过的)
  • 拓展 – Android6.0/7.0/8.0特性,kotlin语言,I/O大会

你可能感兴趣的:(android,进阶)