Android重点面试题

这篇博客详细探讨了Android面试中的关键知识点,包括视频播放、性能优化、常见架构模式如MVC和MVP,以及网络框架Okhttp和Retrofit的使用。还深入讲解了事件分发机制、屏幕适配、Android不同版本的特性,以及View的绘制流程和自定义View的实现。此外,还涵盖了Glide图片加载库的运用和 Handler 的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

视频播放
  • 视频播放的实现方式?
1、使用其自带的播放器。指定Action为ACTION_VIEW,Data为Uri,Type为其MIME类型。

2、使用VideoView来播放。在布局文件中使用VideoView结合MediaController来实现对其控制。

3、使用MediaPlayer类和SurfaceView来实现,这种方式很灵活。

4、还有一些三方的(节操视频,饺子视频)的使用不在这详细说了(基本的会的话,三方的简单的没啥问题)
  • 视频播放怎么用?
  1.      Viderview的使用方法 
    videoView.start();  //开始播放
    videoView.pause();  //暂停
    videoView.stopPlayback();   //停止播放
    videoView.isPlaying(); //获取是否在播放状态
    videoView.setVideoURI(Uri uri); //设置视频播放uri
    videoView.setVideoPath(String path); //设置视频播放路径
    videoView.seekTo(int msec);  //跳转到设置时间
    videoView.getCurrentPosition();  //获取当前播放时间
    videoView.getDuration();  //获取该视频播放时长
    videoView.setMediaController(MediaController controller); //设置播放控制器
    videoView.setOnPreparedListener(MediaPlayer.OnPreparedListener listener); //加载完成监听
    videoView.setOnCompletionListener(MediaPlayer.OnCompletionListener listener); //设置播放完成监听
    videoView.setOnErrorListener(MediaPlayer.OnErrorListener listener);  //播放失败监听
    注:VideoView其实就是继承的SurfaceView、实现了MediaController.MediaPlayerController接口的组建
    
  2.       MediaPlayer类和SurfaceView的使用
    1,给SurfaceView添加CallBack监听(为了可以播放视频或者使用Camera预览,我们需要指定其Buffer类型)
    2,开始实例化MediaPlayer对象
    3,然后指定需要播放文件的路径,初始化MediaPlayer
    4,取得当前Display对象(getWindowManager().getDefaultDisplay())
    5,重写(SurfaceView几个方法) eg:
     @Override  
     publicvoidsurfaceChanged(SurfaceHolderarg0,intarg1,intarg2,intarg3){    
      //当Surface尺寸等参数改变时触发    
     } 
      @Override    
        publicvoidsurfaceCreated(SurfaceHolderholder){    
            //当SurfaceView中的Surface被创建的时候被调用    
            //在这里我们指定MediaPlayer在当前的Surface中进行播放    
            //在指定了MediaPlayer播放的容器后,我们就可以使用prepare或者prepareAsync来准备播放了    
        }  
        
        @Override    
        publicvoidonVideoSizeChanged(MediaPlayerarg0,intarg1,intarg2){    
            //当video大小改变时触发    
            //这个方法在设置player的source后至少触发一次    
        }    
    
        @Override    
        publicvoidonSeekComplete(MediaPlayerarg0){    
           //seek操作完成时触发    
        }    
    
        @Override    
        publicvoidonPrepared(MediaPlayerplayer){    
    		//当prepare完成后,该方法触发,在这里我们播放视频    
    	}
    6,然后取得video的宽和高
      注意:如果video的宽或者高超出了当前屏幕的大小,则要进行缩放(选择大的一个进行缩放)
    7,设置surfaceView的布局参数()
     	方法.setLayoutParams()
    8,然后开始播放视频 
    	方法.start()
    9,当MediaPlayer播放完成后触发 
     publicvoidonCompletion(MediaPlayerplayer){ 
     	this.finish();
     }
    
    
  • 视频播放使用的好处?

    对于VideoView
       VideoView其实就是继承的SurfaceView、实现了MediaController.MediaPlayerController接口的组建。其中,SurfaceView用来显示视频内容,MediaPlayerController用来控制媒体播放。它能够实现包括快进、快退、播放、暂停按钮以及一个进度条的功能,它的优点就是简单易用,基本功能已经都帮我们封装好了,我们只要调用即可;缺点就是可扩展性差,无法实现一些自定义效果。
    
    对于MediaPlayer+SurfaceView
    	使用MediaPlayer+SurfaceView实现播放器可以实现自定义效果,缺点就是实现比较麻烦;使用MediaPlayer来控制媒体的播放,暂停,进度等;SufaceView则用来显示视频内容。
    
    视频录制
  • 视频录制的实现方式?

    1,调用自带的视频功能
    
    2,使用MediaRecorder
    
    其他使用看 http://www.cnblogs.com/Free-Thinker/p/4486588.html
    
  • 视频录制的使用方法?

    自带的视频功能(主要是在监听中写)
    	 @Override  
     public void onClick(View v) {  
                Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);  
    //在这里的QUALITY参数,值为两个,一个是0,一个是1,代表录制视频的清晰程度,0最不清楚,1最清楚  
    //没有0-1的中间值,另外,使用1也是比较占内存的,测试了一下,录制1分钟,大概内存是43M多  
    //使用0,录制1分钟大概内存是几兆  
                intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);  
                // 限制时长 ,参数61代表61秒,可以根据需求自己调,最高应该是2个小时。  
    //当在这里设置时长之后,录制到达时间,系统会自动保存视频,停止录制  
                intent.putExtra(MediaStore.EXTRA_DURATION_LIMIT, 61);  
                // 限制大小 限制视频的大小,这里是100兆。当大小到达的时候,系统会自动停止录制  
                intent.putExtra(MediaStore.EXTRA_SIZE_LIMIT, 1024 * 1024 * 100);  
    //在这里有录制完成之后的操作,系统会默认把视频放到照片的文件夹中  
                startActivityForResult(intent, 11);  
     }
    
    MediaRecorder使用(显示的时候需要用SurfaceView来预览视频)
    1,创建保存录制视频的视频文件,视频结果会保存在sd卡的根目录
    	new Feil(Environment.getExternalStorageDirectory()  
                            .getCanonicalFile() + "名字");
    2,创建MediaPlayer对象(并设置一系列参数)
     	mRecorder = new MediaRecorder();  
     	mRecorder.reset();
    设置从麦克风采集声音(或来自录像机的声音AudioSource.CAMCORDER)
    	mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
    设置从摄像头采集图像
    	mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
    设置视频文件的输出格式  
    必须在设置声音编码格式、图像编码格式之前设置  
        mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
    设置声音编码的格式  
        mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
    设置图像编码的格式  
        mRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
    我一开始将这里的分辨率调成了640,480调完之后,发现录出的视频会花屏,改成1280,720则没事  
        mRecorder.setVideoSize(1280, 720);  
           // 每秒 4帧  
        mRecorder.setVideoFrameRate(20);  
    	//数值越大,越清晰,占用内存越大,一开始我使用的是5*1024*1024,画面的质量和第一种方法质量为0的时候差不多,  
    //后来改成7*1024*1024,清晰度立马提升,后来写成了8*1024*1024  
         mRecorder.setVideoEncodingBitRate(8*1024*1024);  
         mRecorder.setOutputFile(videoFile.getAbsolutePath());
    // 指定使用SurfaceView来预览视频  
         mRecorder.setPreviewDisplay(sView.getHolder().getSurface()); // ①  
         mRecorder.prepare();
    3,在相应的按钮中设置 开始录制 结束录制  释放资源
    	mRecorder.start();开始录制
    	mRecorder.stop();结束录制
    	mRecorder.release();结束时并释放资源
    	
    
  • 两种视频录制的区别?

    一:使用第一种方法,方便快捷,不需要太多的代码量;使用第二种方法,代码量稍大;
    二:第一种方法视频清晰度只有两种,一个是最不清楚,一个是最清楚;第二种方法视频清晰度可根据数值自动往上调 ;
    三:第一种方法,清晰度为1的时候,会占用大内存;第二种方法 :清晰度越高的时候,内存也会增大;
    经测试:第一种方法清晰度为1,录制一分钟,大小在43M左右;第二种方法当为8*1024*1024时,录制一分钟,大小在57M;
    四:第一种方法在录制过程中,操作方便,有自己的暂停、录制、播放按钮 ;第二种只能自己去写暂停、录制或播放按钮;
    综上,如果内存足够大的时候,推荐使用第一种方法,方便快捷。
    
    
    Handler机制
  • Handler机制是什么?

    Handler 是 Android 给我们提供来更新 UI 的一套机制,也是一套消息处理的机制,我们可以发送消息,也可以通过它来处理消息,Handler 在我们的 framework 中是非常常见的。
    Android 在设计的时候,就封装了一套消息创建、传递、处理机制,如果不遵循这样的机制就没有办法更新 UI 信息,就会抛出异常信息。
    	来源:https://blog.youkuaiyun.com/wangning13ji/article/details/52289452
    
  • Handler机制中常用的类的概念?

    * Message 
    	消息,理解为线程间通讯的数据单元。例如后台线程在处理数据完毕后需要更新UI,则可发送一条包含更新信息的Message给UI线程。
    * Message Queue 
    	消息队列,用来存放通过Handler发布的消息,按照先进先出执行。
    * Handler 
    	Handler是Message的主要处理者,负责将Message添加到消息队列以及对消息队列中的Message进行处理。
    * Looper 
    	循环器,扮演Message Queue和Handler之间桥梁的角色,循环取出Message Queue里面的Message,并交付给相应的Handler进行处理。
    * 线程 
    	UI thread 通常就是main thread,而Android启动程序时会替它建立一个Message Queue。每一个线程里可含有一个Looper对象以及一个MessageQueue数据结构。在你的应用程序里,可以定义Handler的子类别来接收Looper所送出的消息。
    
  • Handler机制怎么用?

    1* 传递message(用于接受子线程发送的数据,并用此数据配合主线程更新 UI)
    	post(Ruannable);
    	postAtTime(Runnable, long);
    	postDelayed(Runnable long);
    注意:post类方法允许你排列一个 Runnable 对象到主线程队列中。
    
    2* 传递Runnable对象(用于通过 Handler 绑定的消息队列,安排不同操作的执行顺序)
    	sendEmptyMessage(int);
    	sendMessage(Message);
    	sendMessageAtTime(Message, long);
    	sendMessageDelayed(Message, long);
    注意:sendMessage 类方法,允许你安排一个带数据的 Message 对象到队列中,等待更新。
        1,使用 Handler 在子线程中向 UI 线程发送一个消息进行 UI 的更新
        2,创建一个 Message, Message msg = new Message(); msg.arg1 = 88;
        3,handler.sendMessage(msg); msg.obj = xxx; 可以传递一个对象
        4,当然不一定要用 new 一个 Message,也可以复用系统的 message 对象 Message msg = 			 handler.obtainMessage();
    
     3* 传递Callback对象(Callback 用于截获 handler 发送的消息,如果返回 true 就截获成功不会向下传递了)
     
    public Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            // TODO Auto-generated method stub
            Toast.makeText(getApplicationContext(), "HandleMessage 1", 	Toast.LENGTH_SHORT).show();
            return true;
        }
    }) {
        public void handleMessage(Message msg) {
            // TODO Auto-generated method stub
            Toast.makeText(getApplicationContext(), "handleMessage 1", Toast.LENGTH_SHORT).show();
        };
        注意:上面的示例中,第一个有返回值的 handlerMessage 方法是 Callback 的回调,如果返回true,则不执行下面的 handlerMessage 方法,从而达到拦截 handler 发送的消息的目的,如果返回 false,则会继续执行 handlerMessage 方法。
    
  • Handler原理

    1* Android为什么设计成只能通过Handler机制更新UI呢?
    	最根本的目的就是解决多线程并发的问题,假设在一个 Activity 当中,有多个线程去更新 UI,并且对更新的 UI 的操作进行枷锁处理的话又会产生什么样的问题呢? 那就是性能下降,Handler 通过消息队列,保证了消息处理的先后有序。
    
    鉴于以上问题的考虑,Android 给我们提供了一套更新 UI 的机制,我们只要使用一套机制就好,所有的更新 UI 的操作都是在主线程中轮询处理。
    
    2*Handler的原理是什么?
    	①、Handler 封装了消息的发送(主要包括消息发送给谁) Looper:
    		a,内部包含一个消息队列也就是 MessageQueue,所有 Handler 发送的消息都走向这个队列。
    		b,Looper.loop()方法,就是一个 for 死循环,不断的从 MessageQueue 取消息,如果有消息就处理消息,没有消息就阻塞。
    	②、MessageQueue,就是一个消息队列,可以添加消息,处理消息。
    	③、Handler 也不难,比较简单,在构造 Handler 时候内部会跟 Looper 进行关联,通过 	   	Looper.myLooper() 获取到 Looper,找到 Looper 也就找到了 MessageQueue。在 Handler 中发送消息,其实是向 MessageQueue 队列中发送消息。
    
    3* handler与Looper、MessageQueue的关系?
    	handler 负责发送消息,Looper 负责接收 Handler 发送消息,并直接把消息回传给 handler 自己,MessageQueue 就是一个存储消息的容器。
    	如下图:
    		图片来源:https://my.oschina.net/Draymond/blog/1015181
    

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cJdgqUH2-1599705435159)(D:\学习成功之路\Android中级文件夹\图库\24163244_tSjI.png)]

4* HandlerThread是什么?
	HandlerThread 继承于 Thread,所以它本质就是个 Thread。与普通的 Thread 的差别就在于,它有个 Looper 成员变量。这个 Looper 其实就是对消息队列以及队列处理逻辑的封装,简单来说就是消息队列+消息循环。
	
当我们需要一个工作线程,而不是把它当作一次性消耗品,用过即废的话,就可以使用它。
eg:
private Handler mHandler = null;
private HandlerThread mHandlerThread = null;
 
private void sendRunnableToWorker(Ruannable run) {
    if (null == mHandlerThread) {
        mHandlerThread = new HandlerThread("WorkerThread");
        // 给工作者线程低优先级
        mHandlerThread.setPriority(Thread.MIN_PRIORITY);
        mHandlerThread.start();
    }
    if (null == mHandler) {
        mHandler = new Handler(mHandlerThread.getLooper());
    }
    mHandler.post(run);
}
5* 主线程与子线程之间的信息交互
	//创建主线程的Handler
private Handler mHandler = new Handler() {
    public void handleMessage(Message msg) {
        Message mssage = new Message();
        System.out.println("main Handler");
        //向子线程发送消息
        threadHandler.sendMessageDelayed(message, 1000);
    };
};
//创建子线程的 handler
private Handler threadHandler;
 
@Override
protected void onCreate(Bundle saveInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
 
    HandlerThread thread = new HandlerThread("handlerThread");
    //创建子线程的 handler
    threadHandler = new Handler(thread.getLooper()) {
        public void handlerMessage(Message msg) {
            Message message = new Message();
            //向主线程发送消息
            mHandler.sendMessageDelayed(message, 1000);
        };
    };
}
6* Android中更新UI的几种方式
	Android 中更新 UI 的 4 种方式:
        1,runOnUiThread
        2,handler 的 post
        3,handler 的 sendMessage
        4,View 自身的 post
性能优化
  • 概述
性能调优是开发中少不了的一个过程,同时也是一名优秀的程序员需要掌握的基本技能
	当应用内存不足时,将发生内存溢出。内存溢出会直接导致应用 crash。尽量减少应用的内存使用将很好的避免发生内存溢出
	并从一下三个方面进行优化
		内存泄露,布局优化和UI卡顿优化,还有其他优化
  • 内存优化
1* 内存泄漏
	首先我们应该确保我们的应用不存在内存泄漏的情况。内存泄漏是指长生命周期对象持有短生命周期对象的引用,而导致短生命周期对象无法及时释放的问题,例如一个静态变量持有某个 Activity 的引用而导致该 Activity 退出时无法被回收。

Android 中常见的内存泄漏场景有:
1,Handler
    我们可以使用静态内部类的 Handler,并让 Handler 只持有外层 Activity 的弱引用;在 Activity 不再需要时,可以手动清空对应 Handler 中的所有消息。
2,打开的资源未关闭
    例如文件、数据库等,打开后都是需要关闭的。这类问题通常比较好排查,使用一些代码检查工具(如 lint 等)都可以帮助你找到未关闭的资源。
3,注册的回调没有注销
    一些回调方法,通常命名如 registerCallback 对应有 unregisterCallback,addCallback 对应有 removeCallback。这些回调都是需要我们成对使用的,否则很可能产生内存泄漏。
4,单例模式持有的引用
	通常单例对象都是 static 的,其生命周期都很长。当单例对象持有某个短生命周期对象的引用时,如某个 Activity,将导致该 Activity 无法被系统回收。
我们应该手动清除 Activity 的引用,或者是当单例中需要上下文时,直接使用 Application 作为其上下文。
5,静态成员持有的引用
	该引用对象如果不再需要使用,应该手动将引用置为 null。
6,非静态内部类的静态实例
	非静态内部类对象会持有外部类的引用,如果该非静态内部类对象是静态的,也将导致外部类对象无法被回收。
2* 图片
	- 图片压缩:对于大图片,可以压缩后加载(图片压缩方法可自行百度)。
    - 图片使用完后,及时释放。
    - 图片素材放在合适的目录下,如 xxhdpi、xxxhdpi 等,系统加载不同目录下的图片资源时,会根据手机 dpi 对其进行一定的缩放。一张较大的图片放在较低的 dpi 目录下容易导致系统加载时又进行放大而带来很大的内存消耗。
    - 图片缓存:当图片众多时,可以使用 池 来管理,并设置恰当的池大小(通常图片框架中都有相似实现了)。
    - 善用开源的图片框架吧。
3* 使用合适和数据结构
	- 选用合适的数据结构,往往不仅可以减少内存的使用,也可以加快运算速度。你需要了解 栈、队列、数组、链表、树、哈希表 等常见数据结构,并根据实际场景选用合适的数据结构。

	- 这里介绍两个 Android 推荐使用的,类似 键值对 功能的类:SparseArray 和 ArrayMap。相比 HashMap,它们占用更少的内存,且功能相似,虽然性能上略逊一筹,但在 1000 以下的数量级上,性能上的差异基本可以忽略,但内存占用将少很多。
4* 其它优化
  - 使用 int 代替枚举
	枚举本质还是对象,比 int 多使用两倍左右的内存。不过枚举也有其优势,我们可以考虑使用 int 来代替枚举而减少内存。
  - 使用对象池
	对于需要频繁创建和释放的对象,我们可以考虑使用对象池来管理,通过重用对象来避免反复的创建释放。
  - ListView 的复用
	这个老生常谈了,通过 ViewHolder 重用布局。或者使用 RecyclerView。
  • 布局优化
1* 尽量选择使用简单的布局
	简单的控件加载起来更快,当简单的控件可以满足需求时,应尽量考虑使用简单的控件。
常用布局控件复杂度:FrameLayout < LinearLayout < RelativeLayout。
2* 减少布局的嵌套层级
	当布局的嵌套层级增大时,将大大减慢 xml 的解析速度,而影响到页面的显示。所以我们应当保证布局的嵌套层级尽可能的低。
3* 其它优化
	- 使用 include 标签重用布局。
	- 合理的使用 merge 标签,可优化布局的嵌套层级。
	- 使用 style 标签,抽离公共的风格,可减少代码量,也更易维护。
	- 使用 ViewStub,对于某些不常用的控件,可在需要的时候再进行加载。
  • UI卡顿优化
1* ANR
	ANR 即页面无响应,产生原因有:Activity 中超过 5s 无响应;BroadcastReceiver 中,前台广播超过 10s,后台广播超过 60s;Service 20s 未完成启动;ContentProvider 的 publish 在 10s 没进行完等。

解决方法:避免在 UI 线程中进行耗时的操作,注意四大组件的大多数回调均在主线程中。
2* View 绘制慢
	Android 的渲染需要在 16ms 内完成,否则会产生卡顿的现象。
避免在 onDraw() 方法中进行任何耗时操作,包括频繁创建局部对象(最好不要在该方法中创建局部对象)等。
避免频繁触发 view 的 layout 方法,因为会重新测量和绘制。
3* 动画
	避免在同一时间执行过多的动画,导致 CPU/GPU 负载过大。
实现动画时,可以考虑一些开源的优秀的动画框架。
尽量使用硬件加速来完成动画。
4* GC(垃圾回收)
	由于 GC 时将挂起其它所有线程,所以频繁的 GC 将带来卡顿的现象。
避免频繁创建释放对象,避免内存负荷大将减少 GC 的频率。
  • 其他优化
1* 线程池
	开启一个新的线程花销是很大的,如果应用需要经常创建新的线程,就考虑使用线程池吧。通过重用旧的线程对象减少创建新线程的开销。
2*  网络
	一个页面的数据尽可能的放在同一个接口里,从而减少网络访问的次数。
大量的数据可以使用分页加载、缓存等。
3* IO流
	尽量减少 IO 的访问次数,例如读取一个文件时,一个字节一个字节的读取的话将频繁的访问 IO,我们可以一次性读取更多的字节而减少 IO 访问次数。
注意:同理,数据库也是这样。
4*  代码量
	尽量精简我们的代码,移除无用的资源和代码,选择使用更轻量级的依赖库。这也将大大减小我们 apk 的体积。
5* 运算
	例如进行乘 2、除 2 等操作时,使用位运算(左移、右移)比乘除运算效率将好很多。尤其是这个运算发生在某个较大的循环体内时。
对于一些操作,我们可以选择使用 预处理 和 延时计算 的策略。
6* 异常、锁
	try - catch 和加锁的操作都是较重量级的,我们可以尽量不使用它们。例如一些线程同步的场景中选择使用 原子类 或 volatile 关键字代替锁。
如果需要使用的场景下,我们也应该尽量保证其粒度足够小,即其包含的语句尽量少。
MVC框架
  • MVC的概述
M是指逻辑模型,
V是指视图模型,
C则是控制器。
	一个逻辑模型可以对于多种视图模型,比如一批统计数据你可以分别用柱状图、饼图来表示。一种视图模型也可以对于多种逻辑模型。使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可以使用不同的表现形式,而C存在的目的则是确保M和V的同步,一旦M改变,V应该同步更新,这与《设计模式》中的观察者模式是完全一样。
	1) 视图层(View):一般采用XML文件进行界面的描述,使用的时候可以非常方便的引入。当然,如何你对Android了解的比较的多了话,就一定可以想到在Android中也可以使用JavaScript+HTML等的方式作为View层,当然这里需要进行Java和JavaScript之间的通信,幸运的是,Android提供了它们之间非常方便的通信实现。    
    
  2) 控制层(Controller):Android的控制层的重任通常落在了众多的Acitvity的肩上,这句话也就暗含了不要在Acitivity中写代码,要通过Activity交割Model业务逻辑层处理,这样做的另外一个原因是Android中的Acitivity的响应时间是5s,如果耗时的操作放在这里,程序就很容易被回收掉。

  3) 模型层(Model):对数据库的操作、对网络等的操作都应该在Model里面处理,当然对业务计算等操作也是必须放在的该层的。就是应用程序中二进制的数据。
  • MVC的好处
	从用户的角度出发,用户可以根据自己的需求,选择自己合适的浏览数据的方式。比如说,对于一篇在线文档,用户可以选择以HTML网页的方式阅读,也可以选择以pdf的方式阅读。从开发者的角度,MVC把应用程序的逻辑层与界面是完全分开的,最大的好处是:界面设计人员可以直接参与到界面开发,程序员就可以把精力放在逻辑层上。而不是像以前那样,设计人员把所有的材料交给开发人员,由开发人员来实现界面。在Eclipes工具中开发Android采用了更加简单的方法,设计人员在DroidDraw中设计界面,以XML方式保存,在Eclipes中直接打开就可以看到设计人员设计的界面。
  • MVC对应每层明确的分工
Model 模型层
包括实体模型层,存放程序中调用的实体。
业务模型层,存放程序中调用的业务逻辑。
 
View 显示层
 Android很好的将显示层抽离,并放入res/目录的中以XML形式体现。 包括
main.xml 布局文件。 
string.xml 存放常量。
drawable 存放使用的图片文件。
 
Control 控制层
Control层是Activity的职责。你只有告诉Activity做什么。怎么做那是模型层的事。
MVP框架
  • MVP是什么?
所谓的mvp,即是(model-处理业务逻辑(主要是数据读写,或者与后台通信(其实也是读写数据)),view-处理ui控件,presenter-主导器,操作model和view)

View 层主要是用于展示数据并对用户行为做出反馈。在Android平台上,他可以对应为Activity, Fragment,View或者对话框。
Model 是数据访问层,往往是数据库接口或者服务器的API。
Presenter 层可以想View层提供来自数据访问层的数据,除此以外,他也会处理一些后台事务。
  • 为什么要使用MVP?
- 将数据处理逻辑和UI显示逻辑隔离,解耦合,职责分明,符合SRP原则。
- 数据逻辑和UI显示逻辑的修改互不影响,减少维护成本,可以很好的防止修改UI显示逻辑时不小心影响到数据逻辑导致bug的出现,多人配合时更好的分清楚各自的职责。
- 降低耦合度、
- 模块职责划分明显
- 利于测试驱动开发
- 代码复用
- 隐藏数据
- 代码灵活性
  • 使用MVP有什么缺点呢?
	由于对视图的渲染放在了Presenter中,所以视图和Presenter的交互会过于频繁。还有一点需要明白,如果Presenter过多地渲染了视图,往往会使得它与特定的视图的联系过于紧密。一旦视图需要变更,那么Presenter也需要变更了
  • MVC和MVP的使用有什么区别?
MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负责显示。作为一种新的模式,MVP与MVC有着一个重大的区别:在MVP中View并不直接使用Model,它们之间的通信是通过Presenter (MVC中的Controller)来进行的,所有的交互都发生在Presenter内部,而在MVC中View会从直接Model中读取数据而不是通过 Controller。

在MVC里,View是可以直接访问Model的!从而,View里会包含Model信息,不可避免的还要包括一些业务逻辑。 在MVC模型里,更关注的Model的不变,而同时有多个对Model的不同显示,及View。所以,在MVC模型里,Model不依赖于View,但是View是依赖于Model的。不仅如此,因为有一些业务逻辑在View里实现了,导致要更改View也是比较困难的,至少那些业务逻辑是无法重用的。
Okhttp网络框架

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tAUnwvfc-1599705435160)(D:\学习成功之路\Android中级文件夹\图库\16598307-c1a2aa7fa4e8242f.png)]

  • 什么是okhttp?
一般在Java平台上,我们会使用Apache HttpClient作为Http客户端,用于发送 HTTP 请求,并对响应进行处理。比如可以使用http客户端与第三方服务(如SSO服务)进行集成,当然还可以爬取网上的数据等。OKHttp与HttpClient类似,也是一个Http客户端,提供了对 HTTP/2 和 SPDY 的支持,并提供了连接池,GZIP 压缩和 HTTP 响应缓存功能。
注意:来源http://www.cnblogs.com/WUXIAOCHANG/p/10646545.html
  • okhttp的优点有哪些?
(1)支持HTTP2/SPDY(SPDY是Google开发的基于TCP的传输层协议,用以最小化网络延迟,提升网络速度,优化用户的网络使用体验)
(2)socket自动选择最好路线,并支持自动重连,拥有自动维护的socket连接池,减少握手次数,减少了请求延迟,共享Socket,减少对服务器的请求次数
(3)基于Headers的缓存策略减少重复的网络请求
(4)拥有Interceptors轻松处理请求与响应(自动处理GZip压缩)
  • okhttp的使用场景有哪些?
(1)一般的get请求
(2)一般的post请求
(3)基于Http的文件上传
(4)文件下载
(5)上传下载的进度回调
(6)加载图片
(7)支持请求回调,直接返回对象、对象集合
(8)支持session的保持
(9)支持自签名网站https的访问,提供方法设置下证书就行
(10)支持取消某个请求
  • okhttp怎么用?(使用步骤)
(1)get请求的步骤,首先构造一个Request对象,参数最起码有个url,当然你可以通过Request.Builder设置更多的参数比如:header、method等。
(2)然后通过request的对象去构造得到一个Call对象,类似于将你的请求封装成了任务,既然是任务,就会有execute()和cancel()等方法。
(3)最后,我们希望以异步的方式去执行请求,所以我们调用的是call.enqueue,将call加入调度队列,然后等待任务执行完成,我们在Callback中即可得到结果。
(4)onResponse回调的参数是response,一般情况下,比如我们希望获得返回的字符串,
可以通过response.body().string()获取;如果希望获得返回的二进制字节数组,则调用response.body().bytes();如果你想拿到返回的inputStream,则调用response.body().byteStream()
(5)看到这,你可能会奇怪,竟然还能拿到返回的inputStream,看到这个最起码能意识到一点,这里支持大文件下载,有inputStream我们就可以通过IO的方式写文件。不过也说明一个问题,这个onResponse执行的线程并不是UI线程。的确是的,如果你希望操作控件,还是需要使用handler等
(6)okHttp还支持GJson的处理方式
(7)okhttp支持同步请求和异步请求,Call call = client.newCall(request);为同步请求,发送请求后,就会进入阻塞状态,知道收到响应call.enqueue(new Callback()为异步请求
(8)在okhttp3.Callback的回调方法里面有个参数是Call 这个call可以单独取消相应的请求,随便在onFailure或者onResponse方法内部执行call.cancel()都可以。如果想取消所有的请求,则可以okhttpclient.dispatcher().cancelAll();
  • okhttp拦截器的使用
什么是拦截器
  首先我们需要了解什么事拦截器。打个比方,镖局押着一箱元宝在行走在一个山间小路上,突然从山上下来一群山贼拦住了镖局的去路,将镖局身上值钱的东西搜刮干净后将其放行。其中山贼相当于拦截器,镖局相当于一个正在执行任务的网络请求,请求中的参数就是镖局携带的元宝。拦截器可以将网络请求携带的参数进行修改验证,然后放行。这里面其实设计了AOP编程的思想(面向切面编程)。
  在介绍拦截器的作用和好处之前,我们还是要回到山贼这个角色上,如果让你做一次山贼,你会在什么地方埋伏?肯定是在镖局必经之路上埋伏。也就是说,拦截器就是在所有的网络请求的必经之地上进行拦截。
(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值