一些面试基本知识(Android篇一)

Android

Activity设计模式之MVC模式(介绍MVP之前的引子)

參考博客、文章 http://www.cnblogs.com/liqw/p/4175325.html

MVC即Model-View-Controller

M:逻辑模型。 负责建立数据结构和对应的行为操作处理。

V:视图模型

负责在屏幕上渲染出对应的图形信息展示给用户看。

C:控制器

负责截获用户的按键和屏幕触摸等事件,协调Model对象和View对象。

用户与视图交互。视图接受并反馈用户的动作;视图把用户的请求传给对应的控制器,由控制器决定调用哪个模型,然后由模型调用对应的业务逻辑对用户请求进行加工处理,假设须要返回数据,模型会把对应的数据返回给控制器。由控制器调用对应的视图,终于由视图格式化和渲染返回的数据,对于返回的数据全然能够添加用户体验效果展现给用户。

一个模型能够有多个视图,一个视图能够有多个控制器。一个控制器也能够有多个模型。

 
1. 模型(Model)

Model是一个应用系统的核心部分。代表了该系统实际要实现的全部功能处理。

比方:在视频播放器中。模型代表一个视频数据库及播放视频的程序函数代码。在拍照顾用中。模型代表一个照片数据库,及看图片时的程序函数代码。在一个电话应用中,Model代表一个电话号码簿。以及拨打电话和发送短信的程序函数代码。

Model在values文件夹下通过xml文件格式生成。也能够通过硬编码的方式直接Java代码生成。==View和Model是通过桥梁Adapter来连接起来。==

2. 视图 (View)

View是软件应用传送给用户的一个反馈结果。它代表软件应用中的图形展示、声音播放、触觉反馈等职责。

视图的根节点是应用程序的自身窗体。比方。视频播放器中可能包括当前播放的画面,这个画面就是一个视图。

还有一个视图组件可能是该视频的文字标题。

再一个就是一些播放按键,比方:Stop、Start、Pause等button。

View在layout文件夹下通过xml文件格式生成。用findViewById()获取。也能够通过硬编码的方式直接Java代码生成。

3. 控制器 (Controller)

Controller在软件应用负责对外部事件的响应,包括:键盘敲击、屏幕触摸、电话呼入等。Controller实现了一个事件队列,每一个外部事件均在事件队列中被唯一标识。框架依次将事件从队列中移出并派发出去。

Android中最典型MVC是ListView。要显示的数据是Model。界面中的ListView是View,控制数据怎样在ListView中显示是Controller。

Activity设计模式之MVP模式

Model-View-Presenter

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也是比較困难的,至少那些业务逻辑是无法重用的。

尽管 MVC 中的 View的确“能够”訪问Model,可是我们不建议在 View 中依赖Model。而是要求尽可能把全部业务逻辑都放在 Controller 中处理,而 View 仅仅和 Controller 交互。

MVP怎样解决MVC的问题?

在MVP里。Presenter全然把Model和View进行了分离,基本的程序逻辑在Presenter里实现。

并且,Presenter与详细的View是没有直接关联的。而是通过定义好的接口进行交互。从而使得在变更View时候能够保持Presenter的不变。即重用!

不仅如此。我们还能够编写測试用的View。模拟用户的各种操作,从而实现对Presenter的測试–而不须要使用自己主动化的測试工具。 我们甚至能够在Model和View都没有完毕时候,就能够通过编写MockObject(即实现了Model和View的接口,但没有详细的内容的)来測试Presenter的逻辑。

在MVP里,应用程序的逻辑主要在Presenter来实现。当中的View是非常薄的一层。因此就有人提出了Presenter First的设计模式,就是依据User Story来首先设计和开发Presenter。

在这个过程中。View是非常easy的,能够把信息显示清楚就能够了。

在后面,依据须要再随便更改View。而对Presenter没有不论什么的影响了。

假设要实现的UI比較复杂,并且相关的显示逻辑还跟Model有关系,就能够在View和Presenter之间放置一个Adapter。由这个 Adapter来訪问Model和View,避免两者之间的关联。

而同一时候,由于Adapter实现了View的接口,从而能够保证与Presenter之间接口的不变。这样就能够保证View和Presenter之间接口的简洁,又不失去UI的灵活性。 在MVP模式里,View仅仅应该有简单的Set/Get的方法,用户输入和设置界面显示的内容,除此就不应该有很多其它的内容。绝不容许直接訪问Model–这就是与MVC非常大的不同之处。

MVP的长处
1. 模型与视图全然分离,我们能够改动视图而不影响模型
2. 能够更搞笑的使用模型。由于全部的交互都发生在一个地方:Presenter内部
3. 我们能够将一个Presenter用于多个视图,而不须要改变Presenter的逻辑。这个特性非常的实用。由于视图的变化总是比模型的变化频繁。
4. 假设我们把逻辑放在Presenter中,那么我们就能够脱离用户接口来測试这些逻辑(单元測试)

Activity生命周期、启动模式

參考书:《第一行Android代码》
參考书:《Android开发艺术探索》

Android是使用任务栈(Task)来管理活动的,一个任务就是一组存放在栈里的活动的集合。这个栈也被称作返回栈(Back Stack)。

在默认情况下。每当我们启动一个新的活动,他就会在返回栈中入栈。并处于栈顶的位置。而每当我们按下Back键或调用finsh()方法去销毁一个活动时,处于栈顶的活动会出栈。

活动在其生命周期中最多会有四种状态:运行状态、暂停状态、停止状态、销毁状态。

Activity类中定义了七个回调方法。覆盖了活动生命周期的每一个环节:
1. ==onCreate()==:活动第一次创建时调用。完毕活动的初始化操作。比方载入布局、绑定事件等。


2. ==onStart()==:活动由不可见变为可见时调用。
3. ==onResume()==:活动准备好和用户进行交互时调用。此时活动一定位于返回栈的栈顶。并且处于运行状态。
4. ==onPause()== :系统准备去启动或者恢复还有一个活动时调用。一般会在这种方法中将一些消耗CPU的资源释放掉,以及保存一些重要数据。但这种方法运行速度一定要快,不然会影响到新的栈顶活动的使用。
5. ==onStop()==: 活动全然不可见的时候调用。


6. ==onDestroy()== :活动被销毁前调用,之后活动的状态将变为销毁状态。


7. ==onRestart()== :活动由停止状态变为运行状态之前调用。也就是活动被又一次启动了。

活动的启动模式分为四种:standard,singleTop,singleTask,singleInstance.

能够依据实际的需求为Activity设置对应的启动模式,从而能够避免创建大量反复的Activity等问题。

设置Activity的启动模式,仅仅须要在AndroidManifest.xml里对应的标签设置Android:launchMode属性。比如:

<activity  
    android:name=".A1"  
    android:launchMode="standard" />  

standard:标准模式

默认模式,不用写配置。

系统不会在乎这个活动是否已经在返回栈中存在,每次启动都会创建一个新的实例。因此,在standard模式下。能够有多个同样的实例,也同意多个同样Activity叠加。

退出时,按Back依次按栈顺序退出。

singleTop: 栈顶复用模式

在这样的模式下,假设新Activity已经位于任务栈的栈顶,那么此Activity不会被又一次创建,同一时候它的onNewIntent方法会回调,通过此方法的參数我们能够取出当前请求的信息。须要注意的是。此时这个Activity的onCreate、onStart不会被系统调用,由于它并没有发生改变。

假设新Activity的实例已经存在但不是位于栈顶,那么新Activity仍然会又一次重建。

SingleTask: 栈内复用模式

仅仅有一个实例。

在同一个应用程序中启动他的时候。若Activity不存在,则会在当前task创建一个新的实例;若存在,则会把task中在其之上的其它Activity destroy掉并调用他的onNewInstant方法。


详细一点,当一个具有singleTask模式的Activity请求启动后,比方Activity A。系统首先会寻找是否存在A的任务栈,假设不存在,就又一次创建一个任务栈。然后创建A的实例后把A放到栈中。

假设存在任务栈且有实例,就把A调到栈顶并调用onNewIntent方法,假设实例不存在。就创建A的实例并把A压入栈中。

举几个样例:

  • 比方眼下任务栈S1中的情况为ABC,这个时候ActivityD以singleTask模式请求启动。其所需的任务栈为S2,由于S2和D的实例都不存在,所以系统会先创建任务栈S2,然后再创建D的实例并将其入栈到S2.
  • 还有一种情况,假设D所需的任务栈为S1,其它情况如上面的样例所看到的,那么由于S1已经存在,所以系统会直接创建D的实例并将其入栈到S1。
  • 假设D所需的任务栈为S1,并且当前任务栈S1的情况为ADBC,依据栈内复用原则,此时D不会又一次创建。系统会把D切换到栈顶并调用其onNewIntent方法,同一时候由于singleTask默认具有clearTop的效果,会导致栈内全部在D上面的Activity全部出栈。于是终于S1中的情况为AD。

singleInstance:

单实例模式。

这是一种加强的singleTask模式,它除了具有singleTask模式的全部特性外,还加强了一点,那就是具有此种模式的Activity仅仅能单独的位于一个任务栈中,换句话说,比方Activity A是singleInstance模式。当A启动后,系统会为它创建一个新的任务栈,然后A独自在这个新的任务栈中。由于栈内复用的特性。兴许的请求均不会创建新的Activity,除非这个独特的任务栈被系统销毁了。

任务栈

从一个參数说起:TaskAffinity,能够翻译为任务相关性。这个參数标识了一个Activity所须要的任务栈的名字。默认情况下,全部Activity所需的任务栈的名字为应用的包名。当然,我们能够为每一个Activity都单独制定TaskAffinity属性。这个属性必须不能和包名同样。否则就相当于没有制定。TaskAffinity属性主要和singleTask启动模式配对使用。

指定Activity启动模式

一、通过AndroidManifest为Activity指定启动模式:

       <activity android:name=".MainActivity"
            android:launchMode="standard">

二、通过在Intent中设置标志位来为Activity指定启动模式:

        Intent intent = new Intent(MainActivity.this,MyActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent);

点击事件传递dispatch,intercept,onTouchEvent

參考文章、博客: http://blog.csdn.net/morgan_xww/article/details/9372285/
http://www.cnblogs.com/smyhvae/p/4802274.html
http://www.cnblogs.com/lwbqqyumidi/p/3500997.html

image

上图显示:

  在ViewGroup中能够通过onInterceptTouchEvent方法对事件传递进行拦截。onInterceptTouchEvent方法:

    返回true代表不同意事件继续向子View传递。将会触发当前View的onTouchEvent()。进行事件的消费;

    返回false代表不正确事件进行拦截,事件能够传递给孩子

    默认返回false

跟touch事件相关的3个方法:

public boolean dispatchTouchEvent(MotionEvent ev);    //用来分派event

public boolean onInterceptTouchEvent(MotionEvent ev); //用来拦截event

public boolean onTouchEvent(MotionEvent ev);          //用来处理event

事件分发:public boolean dispatchTouchEvent(MotionEvent ev)

当有监听到事件时。首先由Activity的捕获到。进入事件分发处理流程。

假设事件分发返回true,表示改事件在本层不再进行分发且已经在事件分发自身中被消费了。

至此,事件已经完结。

假设事件分发返回 false,表明事件在本层不再继续进行分发,并交由上层控件的onTouchEvent方法进行消费。

事件拦截:public boolean onInterceptTouchEvent(MotionEvent ev)

假设 onInterceptTouchEvent 返回 true,则表示将事件进行拦截,并将拦截到的事件交由本层控件 的 onTouchEvent 进行处理;

假设返回结果是false;则表示不正确事件进行拦截,事件得以成功分发到子View。并由子View的dispatchTouchEvent进行处理。

假设返回super.onInterceptTouchEvent(ev)。事件默认不会被拦截,交由子View的dispatchTouchEvent进行处理。

事件响应:public boolean onTouchEvent(MotionEvent ev)

假设onTouchEvent返回true。表示onTouchEvent处理完事件后消费了此次事件。

此时事件终结,将不会进行兴许的冒泡。

假设onTouchEvent返回false,事件在onTouchEvent中处理后继续向上层View冒泡,且有上层View的onTouchEvent进行处理。

假设返回super.onTouchEvent(ev),则默认处理的逻辑和返回false时同样。

service,两种启动方式及特点。怎样与activity通信

參考博客、文章:http://www.jianshu.com/p/2fb6eb14fdec

==採用start的方式开启服务==

步骤:

  1. 定义一个类继承Service
  2. 在Manifest.xml文件里配置该Service
  3. 使用Context的startService(Intent)方法启动
  4. 不再使用时,调用stopService(Intent)方法停止

用这样的start方式启动的Service的生命周期例如以下:

onCreate()—>onStartCommand()(onStart()方法已过时) —> onDestory()

说明: 假设服务已经开启,不会反复的运行onCreate(), 而是会调用onStart()和onStartCommand()。

服务停止的时候调用 onDestory()。

服务仅仅会被停止一次。

特点: 一旦服务开启跟调用者(开启者)就没有不论什么关系了。
开启者退出了。开启者挂了。服务还在后台长期的运行。


开启者不能调用服务里面的方法。

==採用bind的方式开启服务==

步骤:

  1. 定义一个类继承Service
  2. 在Manifest.xml文件里配置该Service
  3. 使用Context的bindService(Intent, ServiceConnection, int)方法启动
  4. 不再使用时,调用unbindService(ServiceConnection)方法停止

使用这样的start方式启动的Service的生命周期例如以下:
onCreate() —>onBind()—>onunbind()—>onDestory()

注意: 绑定服务不会调用onstart()或者onstartcommand()方法

特点: bind的方式开启服务。绑定服务。调用者挂了,服务也会跟着挂掉。
绑定者能够调用服务里面的方法。

Handler-Looper-MessageQueue,AsyncTask原理相关

參考:《第一行代码》郭霖

參考: http://www.cnblogs.com/devinzhang/archive/2012/02/13/2350070.html

由于Android不同意在子线程中进行UI操作,由于UI是线程不安全的,假设想要更新UI则必须在主线程中进行,否则就会异常。Android提供的一套异步消息处理机制,完美的解决在子线程中进行UI操作的问题。

Android中的异步消息处理主要由四个部分组成:Message、Handler、MessageQueue和Looper。

Message:
Message是在线程之间传递的消息。他能够在内部携带少量的信息。用于在不同线程之间交换数据。

比方what字段、arg1、arg2带一些整型数据,obj字段携带一个Object对象。

Handler
Handler顾名思义就是处理者的意思,它主要是用于发送和处理消息的。发送消息一般使用Handler的sendMessage()方法。而发出的消息经过一系列的辗转处理后。终于会传递到Handler的handleMessage()方法中。

MessageQueue
MessageQueue是消息队列的意思,它主要用于存放全部通过Handler发送的消息。

这部分消息会一直存在于消息队列中,等待被处理。每一个线程仅仅有一个MessageQueue对象。

Looper
Looper是每一个线程中的MessageQueue的管家,调用Looper的loop()方法后,就会进入到一个无限循环当中。然后每发现MessageQueue中存在一条消息,就会将它取出。并传递到Handler的handleMessage()方法中。每一个线程也仅仅有一个Looper对象。

==异步消息处理流程:==
1. 主线程中创建一个Handler对象,并重写handleMessage()方法
2. 当子线程须要进行UI操作时,就创建一个Message对象。并通过handler.sendMessage()将这条消息发送出去
3. 这条消息被加入到MessageQueue的队列中等待被处理
4. Looper一直尝试从MessageQueue中提出待处理消息。分发会Handler的handleMessage()方法中。

==AsyncTask:==

AsyncTask是Android提供的轻量级异步类,能够直接继承。事实上现原理也是基于异步消息处理机制的。仅仅是Android帮我们做了非常好的封装而已。

由于AsyncTask是一个抽象类,要使用必须要创建一个子类去继承他。并提供三个泛型參数,同一时候重载几个方法:

AsyncTask定义了三种泛型类型Params。Progress和Result

  • Params:在运行AsyncTask时须要传入的參数。可用于在后台任务中使用(doInBackground方法的參数类型)如HTTP请求的URL
  • Progress:后台任务运行时,假设须要在界面上显示当前的进度,则指定进度类型
  • Result:后台任务的返回结果类型

样例:一个最简单的自己定义AsyncTask:

class DownloadTask extends AsyncTask<void,Integer,Boolean>{
}

//Params为void,表示在运行AsyncTask的时候不须要传入參数给后台任务
//Progress为Integer,表示用整型数据来作为进度显示单位
//Result为Boolean,表示使用布尔型数据来反馈运行结果

眼下自己定义的DownloadTask还是一个空任务。还须要重写AsyncTask中的几个方法才干完毕对任务的定制。

使用过AsyncTask 的同学都知道一个异步载入数据最少要重写下面这两个方法:

  • doInBackground(Params…): 这种方法的全部代码都会在后台子线程中运行。比較耗时的操作都能够放在这里。任务一旦完毕就能够通过return语句来将任务的运行结果返回(这里返回类型就是当初给定的第三个泛型參数)。

    注意这里不能直接操作UI.在运行过程中能够调用publicProgress(Progress…)来更新任务的进度。

  • onPostExecute(Result):
    当后台任务运行完毕并通过return语句返回时,这种方法就会被调用。返回的数据会作为參数传递到此方法中,相当于Handler 处理UI的方式。能够利用返回的数据进行一些UI操作。此方法在主线程运行,任务运行的结果作为此方法的參数返回

有必要的话你还得重写下面这三个方法,但不是必须的:

  • onPreExecute() 这里是终于用户调用Excute时的接口,当任务运行之前開始调用此方法,能够在这里显示运行进度对话框。

  • onProgressUpdate(Progress…) 当后台任务中调用了publicProgress(Progress…)方法后,这种方法非常快就会被调用。

    能够使用进度条添加用户体验度。 此方法在主线程运行。用于显示任务运行的进度。

  • onCancelled() 用户调用取消时,要做的操作

AsyncTask的局限性

AsyncTask的长处在于运行完后台任务后能够非常方便的更新UI,然而使用它存在着诸多的限制。先抛开内存泄漏问题。使用AsyncTask主要存在下面局限性:

  • 在Android 4.1版本号之前。AsyncTask类必须在主线程中载入。这意味着对AsyncTask类的第一次訪问必须发生在主线程中。
  • 在Android 4.1以及以上版本号则不存在这一限制,由于ActivityThread(代表了主线程)的main方法中会自己主动载入AsyncTask
  • AsyncTask对象必须在主线程中创建
  • AsyncTask对象的execute方法必须在主线程中调用
  • 一个AsyncTask对象仅仅能调用一次execute方法

一个简单的实例见: http://www.cnblogs.com/absfree/p/5357678.html

动画相关

一、Drawable Animation

也就是所谓的帧动画,Frame动画。

指通过指定每一帧的图片和播放时间。有序的进行播放而形成动画效果。能够理解成多张图片播放。图片不能过大。

二、View Animation

视图动画。也就是所谓补间动画,Tween动画。指通过指定View的初始状态、变化时间、方式,通过一系列的算法去进行图形变换,从而形成动画效果,主要有Alpha、Scale、Translate、Rotate四种效果。

注意:仅仅是在视图层实现了动画效果,并没有真正改变View的属性。view的实际位置还是移动前的位置

三、Property Animation

属性动画,通过不断的改变View的属性,不断的重绘而形成动画效果。相比于视图动画,View的属性是真正改变了。

注意:Android 3.0(API 11)以上才支持。

作者:AudienL
链接:https://www.zhihu.com/question/19703349/answer/153065511
来源:知乎
著作权归作者全部。商业转载请联系作者获得授权,非商业转载请注明出处。

binder机制

这里仅仅对Binder机制进行一个简要的概括,详细binder的介绍在这里就不展开了。

http://www.linuxidc.com/Linux/2012-07/66195.htm

  • Binder是Android系统中的一种IPC进程间通信结构。


    在Android中,每一个应用程序都运行在独立的进程中。这也保证了当当中一个程序出现异常而不会影响还有一个应用程序的正常运转。在很多情况下,我们activity都会与各种系统的service打交道,非常显然,我们写的程序中activity与系统service肯定不是同一个进程,可是它们之间是怎样实现通信的呢?Binder是android中一种实现进程间通信(IPC)的方式之中的一个。

  • Binder属于一个驱动,工作在linux层面。运行在内核态,它的操作完毕是基于一段内存。所以我们开发的程序中对binder的使用都是通过系统的调用来完毕的。

  • Binder的整个设计是C/S结构,Binder架构由服务端,binder驱动。client三个部分构成。 client进程通过binder驱动获取服务端进程的代理,并通过向这个代理接口方法中读写数据来完毕进程间的数据通信。

为什么Android选用Binder来实现进程间通信?

1. 安全。Android是一个开放式的平台,所以确保应用程序安全是非常重要的。每一个进程都会被Android系统分配UID和PID,当中进程的UID是可用来鉴别进程身份。

不像传统的在数据包里加入UID,这就让那些恶意进程无法直接和其它进程通信,保证了进程间通信的安全性。

1. 高效

socket主要用于跨网络的进程间通信和本机上进程间的通信,但传输效率低,开销大。消息队列和管道採用存储-转发方式,即数据先从发送方缓存区复制到内核开辟的一块缓存区中,然后从内核缓存区复制到接收方缓存区,其过程至少有两次拷贝。尽管共享内存无需拷贝,但控制复杂。

比較各种IPC方式的数据拷贝次数。

共享内存:0次。Binder:1次。Socket/管道/消息队列:2次。在手机这样的资源紧张的情况下非常重要。

BroadCast动态、静态注冊

參考:http://blog.csdn.net/q908555281/article/details/48541939

在Android中。Broadcast是一种广泛运用的在应用程序之间传输信息的机制。而BroadcastReceiver是对发送出来的 Broadcast进行过滤接受并响应的一类组件。下面将详细的阐述怎样发送Broadcast和使用BroadcastReceiver过滤接收的过程:

首先在须要发送信息的地方,把要发送的信息和用于过滤的信息(如Action、Category)装入一个Intent对象。然后通过调用 Context.sendBroadcast()、sendOrderBroadcast()或sendStickyBroadcast()方法,把 Intent对象以广播方式发送出去。

当Intent发送以后。全部已经注冊的BroadcastReceiver会检查注冊时的IntentFilter是否与发送的Intent相匹配,若匹配则就会调用BroadcastReceiver的onReceive()方法。所以当我们定义一个BroadcastReceiver的时候,都须要实现onReceive()方法。

注冊BroadcastReceiver有两种方式:

1. 静态注冊
静态的在AndroidManifest.xml中用标签生命注冊,并在标签内用标签设置过滤器。

<receiver android:name=".Receiver" >  
            <intent-filter>  
                <action android:name="android.intent.action.BOOT_COMPLETED" />  
            </intent-filter>  
 </receiver>  

2. 动态注冊
动态的在代码中先定义并设置好一个 IntentFilter 对象,然后在须要注冊的地方调Context.registerReceiver()方法,假设取消时就调用 Context.unregisterReceiver()方法。

UpdateBroadcast  broadcast= new UpdateBroadcast();  
IntentFilter filter = new IntentFilter("com.unit.UPDATE");  
registerReceiver(broadcast, filter); 

1.动态注冊的广播 永远要快于 静态注冊的广播,无论静态注冊的优先级设置的多高,无论动态注冊的优先级有多低>\

2.动态注冊广播不是 常驻型广播 ,也就是说广播尾随activity的生命周期。

注意: 在activity结束前。移除广播接收器。

静态注冊是常驻型 ,也就是说当应用程序关闭后,假设有信息广播来。程序也会被系统调用自己主动运行。

posted @ 2017-08-11 15:15  yfceshi  阅读(244)  评论(0编辑  收藏  举报