技术面试 android部分

v:* {behavior:url(#default#VML);} o:* {behavior:url(#default#VML);} w:* {behavior:url(#default#VML);} .shape {behavior:url(#default#VML);}

Normal

0

false

7.8 磅

0

2

false

false

false

EN-US

ZH-CN

X-NONE

/* Style Definitions */ table.MsoNormalTable {mso-style-name:普通表格; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-priority:99; mso-style-parent:""; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-para-margin:0cm; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.5pt; mso-bidi-font-size:11.0pt; font-family:等线; mso-ascii-font-family:等线; mso-ascii-theme-font:minor-latin; mso-fareast-font-family:等线; mso-fareast-theme-font:minor-fareast; mso-hansi-font-family:等线; mso-hansi-theme-font:minor-latin; mso-font-kerning:1.0pt;} table.MsoTableGrid {mso-style-name:网格型; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-priority:39; mso-style-unhide:no; border:solid windowtext 1.0pt; mso-border-alt:solid windowtext .5pt; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-border-insideh:.5pt solid windowtext; mso-border-insidev:.5pt solid windowtext; mso-para-margin:0cm; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.5pt; mso-bidi-font-size:11.0pt; font-family:等线; mso-ascii-font-family:等线; mso-ascii-theme-font:minor-latin; mso-fareast-font-family:等线; mso-fareast-theme-font:minor-fareast; mso-hansi-font-family:等线; mso-hansi-theme-font:minor-latin; mso-font-kerning:1.0pt;}

Android技术面(注意时间的把握与控制) **

1**、自我介绍一下吧(一两分钟内,简短介绍学习、项目经历) **

我是来自广东工业大学信息工程学院的吴焕楠,焕然一新的焕,木字边加东南西北的楠,我来应聘的岗位是android开发工程师。大二开始自学android开发,看的是淘宝网买的一套黑马程序员的视频教程,后来又在菜鸟窝官网学习了一套叫做《菜鸟商城》的实战视频教程,后期主要是靠实习和看书,看博客来提升自己的技术水平。

2**、在什么地方实习过?实习多久?平时主要做什么?离职了吗?为什么离职? **

是这样的,我是在今年3月份开始在悦乎网络科技有限公司实习的,一直到九月份才离职,一共工作了6个月。这家公司是一家创业型公司,规模比较小,但是基本所有功能模块都是自己独立完成的,这样可以有更多锻炼自己的机会。但是毕竟是实习,薪水只能满足于生活费用,现在觉得自己的水平已经有一定提升,因此想得到贵公司的认可。

平时主要的任务就是负责开发《师兄帮帮》APP的各种功能模块的调研,开发以及日常的升级与维护。

3**、能不能简单介绍一下你做过的APP?开发周期?你做的项目,说一下优缺点,有什么想改进的地方? **

《师兄帮帮》是一款垂直社交应用平台,这款产品主打是大学生,里面有20万份资料供用户阅读查看和下载。用户可以在这个平台上发布问题或者需要,也可以进行抢单,解决问题以后就有相应的报酬。

**开发周期:**一个星期一个小版本,一个月一个大版本

优点是:技术方面:在架构设计方面比较混乱,没有用到一些比较新的架构。细节上面:还没有适配android6.0的动态权限。

非技术方面:界面比较清晰易懂,但就是功能过于单一。限制太多,很多功能需要花钱,而且用户之间的互动比较少。至于app的盈利模式,公司规定不能向外透露。

4**、你在开发这个APP的时候你遇到过的技术难点有什么?你是怎么解决的?(重点问题,一定要准备) **

*******注意点: **

1**、说到实现一个功能的时候,先说自己想办法,调研。实在找不到解决方案的时候,就说网上已经有成熟的解决方案。最好能说说找到了什么方案,最好能说一些外国的SDK或者库,给人一种比较牛逼的感觉。 **

2**、最好在说的时候突出一下团队合作,突出自己有提前调研过,计划好才开始写代码,实在是按时完成不了的就说自己是另外找时间加班完成的。 **

** **

0、内存泄漏问题

解决:

先介绍内存泄漏:用例子说明,我的手代表app所能使用的内存,手里面能够放很多东西。比如我手里有钥匙,洋娃娃。而洋娃娃需要扬声器才能工作,那么洋娃娃和扬声器之间就有了引用的关系。一旦我扔掉了洋娃娃,就会有垃圾回收器来回收。但是意外的是,我的钥匙和洋娃娃黏在了一起,阻止了我扔掉洋娃娃。导致洋娃娃不会被回收掉。内存泄漏就是外部的引用指向了本来应该被回收的对象,造成对象不能被释放,导致了内存泄漏。

项目中,为了app的特效,打算做水波纹效果,但是这是5.0以上原有的,以下版本需要自己手动实现ViewGroup,后来发现了github上面的水波纹效果控件。但是导致app变卡,初步断定是内存泄漏导致的。使用LeakCanary来进行检测内存泄漏(结合Demo),最终分析出是BitMap对象没有recycle导致。Demo分析,静态变量比较特殊,是GC root的一种,回收器只会回收非GC root的对象。解决办法:手动设置为null,或者使用弱引用。

LeakCanary检测内存泄漏的原理是利用虚引用(虚引用不会影响回收,即虚引用不会对象的生命周期,回收取决于对象是否还存在强引用)对被测对象打一个标记,当对象回调销毁(onDestory)之后,一旦判断到这个弱引用还存在的话,就说明对象不能被正确回收,造成了内存泄漏。然后利用API把堆内存的信息dump出来,并且弹出通知提醒用户,最终把信息显示到界面上面。

1、
APP内部查阅资料的问题:资料有word、excel、ppt、PDF、图片等格式。由于PDF和大图的查看网上有很多成熟的解决方案。但是谷歌官方并没有在android原生平台查看office文档的功能,相对来说IOS的浏览器内核Safari是支持直接打开office文档的。

解决:本来打算自己实现,但是发现工作量很大,因此只能找第三方解决方案。当时考虑过使用Apache的POI,但是发现这个库主要是java方向的,对于android的兼容性做得不是很好,引入的时候出现过很多问题。也考虑过使用韩国的一款office SDK,叫做北极星Office,但是后来考虑到这个SDK收费太贵所以没有使用。最后是决定采用使用Web Office服务,有服务动态生成office文档的url,用android的WebView来加载和浏览。

2、
第二个要说的就是沉浸式状态栏的实现。这个问题比较尴尬,因为我们的标题栏是白色的,因此如果直接设置状态栏的颜色为白色的话,那么原有的状态栏上面的文字是白色的话就看不见了。

解决:目前来说只有小米、魅族以及android6.0以上才提供了设置状态栏字体颜色的API。考虑到兼容性问题,只能参考市面上的一些app,把状态栏设置为半透明的黑色。使用了github上面的一个开源库system bar tint,并且在app主题中开启了透明状态栏。

3、
短信验证码的自动填写。

解决:使用Content Observer去观察短信数据库的收件箱,当有新的短信进来的时候,就通过正则表达式去获取短信中的验证码,并且通过handler把结果传递到UI中。四个连续数字(\d{4})

4、
动态库文件(so文件)冲突:由于当时想引入Fresco图片缓存框架,但是后来发现由于Fresco框架对不同架构的平台的支持得非常好,提供了所有平台的so包,但是这样会导致app不能正确加载其他库(如短信验证SDK)的so库,从而引发crash。

解决:当时是看到Stack Overflow上的一篇博客介绍,通过配置APP的gradle脚本文件,使得app只输出armabi架构的so包,成功解决问题。

5、
实现防止多终端登录同一个账号的时候,服务器需要知道用户是否正在登录app。

解决:通过后台开一个Service,每隔一段时间去访问服务端的一个心跳接口,从而告诉服务端app的某个账号正在使用。当时也考虑过其他的解决方案,但是这个唯有客户端去轮询才能实现。

6、
使用第三方SDK时,例如融云即使聊天、Ping++的时候,遇到问题一般都是通过提交工单的方式去寻求解决。

5**、你说你看过好多书,能不能简单介绍一下你看过什么书?平时看什么博客?自己写过博客吗? **

《android源码设计模式解析与实战》,这是一本介绍了设计原则、设计模式以及android源码的一本书。

《android群英传》,《android开发艺术探索》,《android开发从小工到专家》,这些都是android方面的技术进阶书籍,从各个方面介绍android开发中的一些常见的难点。

《android神兵利器》,主要介绍了git、android studio等工具的使用,还没看完。

平时看的博客主要来源于CSDN,微信公众号等,自己有在简书上面写技术博客以及笔记。

6**、你会不会使用反射技术?你是怎么理解这个技术的?在什么地方用过? **

我对反射技术的理解就是,反射就是把Java类中的各种成分通过java的反射API映射成相应的Java类,得到这些类以后就可以对其进行使用。比如方法,构造方法,成员变量,类型,包等。

直接用到反射技术的不多,一般都是间接用到比较多。比如:

1、android中的通过读取布局文件的信息,然后利用反射来构造各种控件的对象。

2、使用开源ORM数据库框架的时候,是通过反射的方式来进行java Bean和表的映射的。

3、Gson在解析JSON并且生成Bean对象的时候利用了反射技术。

7**、什么是设计模式?你知道哪些设计模式?简单介绍一下吧 **

设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。

单例模式,主要用在一些资源消耗比较多的类身上,比较典型的实现由DCL方式、枚举、静态内部类来实现。例子有

  1. SystemService
  2. 操作数据库的类,DataBaseOpenHelper
  3. 操作SharePreference的类
  4. 各种管理类,比如网络,图片加载器等等

单例模式的DCL失效问题:虚拟机允许指令的乱序执行,当单例实例化的时候,实际上会执行了三个步骤:

给单例实例分配内存空间

调用单例实例的构造函数,初始化一些字段

将单例实例对象指向分配的内存空间(sInstance不为null)

2、3两步的执行顺序是不确定的,因此如果在多线程的状态下,线程A执行了3以后,线程B马上来取走实例,就是报错(因为第二部的初始化还没有完成)

Builder模式,主要是用作一些复杂类的构建,并且一般支持连点操作。例子:

  1. AlertDialog的构建
  2. ImageLoader等图片加载框架类的构建

Adapter模式:这种模式主要是解决接口不兼容的问题。例子:

我们常用的ListView、RecycleView等,就使用了adapter模式设计,这样做分离了ListView显示视图,Adapter提供数据以及操作的功能,负责单一职责原则。

观察者模式:这种模式是主要有被观察者、观察者组成,当被观察者的观察行为发上变化的时候,观察者可以马上做出相应处理。例子:

Content Observer

RXJava

ListView、RecycleView在设置Adapter的时候也注册了数据观察者,当用户调用notifyDateSetChange等方法的时候,这些观察者就会通知这些控件去重新刷新视图。

工厂模式:主要是定义了一个对象创建的接口,具体产生什么类由用户去实现。例子:

  1. Activity中的onCreate方法,就是构造视图View的工厂方法
  2. Collection中的iterator方法
  3. Bitmap的创建也可以通过工厂方法

装饰者模式:动态地给一个对象添加一些额外的功能。例子:应用:IO流,ContextWrapper等。

8**、Java与JS的交互**

1、Java调用JS:直接利用WebView加载URL即可:webview.loadUrl("javascript:method("+参数+")");

2、WebView设置使能JS,创建JS Interface,设置到WebView当中。JS通过中window.appInterface.buy(id);调用Java方法。

9**、简述一下android的消息传递机制吧。 **

Android中的消息机制主要是指Handler的运行机制。Handler是进行线程切换的关键,在主线程和子线程之间切换只是一种比较特殊的使用情景而已。其中消息传递机制需要了解的东西有Message、Handler、Looper、Looper里面的MessageQueue对象。我们可以把整个消息机制看作是一条流水线。其中:

  1. MessageQueue是传送带,负责Message队列的传送与管理
  2. Looper是流水线的发动机,不断地把消息从消息队列里面取出来,交给Handler来处理
  3. Message是每一件产品
  4. Handler就是工人。但是这么比喻不太恰当,因为发送以及最终处理Message的都是Handler

app在启动的时候,也就是执行ActivityThread的main方法的时候,会为主线程创建Looper,并且启动整个app的消息循环,同时保证app不会立马退出。

另外,子线程之间也可以利用Handler进行通信,甚至是子线程可以通过Handler发消息给主线程。前提条件是子线程没有关联Looper,因此必须手动创建Looper并且启动消息循环。

10**、简述一下android的事件传递机制吧。遇到过(滑动)事件冲突吗?你是怎么解决的? **

事件总是从上往下进行分发,即先到达Activity,再到达ViewGroup,再到达子View,如果没有任何成员消耗事件的话,事件会顺着路径往回传递。

  1. dispatchTouchEvent是事件的分发方法,如果事件能够到达该视图的话,就首先一定会调用,一般我们不会去修改这个方法。
  2. onInterceptTouchEvent是事件分发的核心方法,表示ViewGroup是否拦截事件,如果返回true表示拦截,在这之后ViewGroup的onTouchEvent会被调用,事件就不会往下传递。
  3. onTouchEvent是最低级的,在事件分发中最后被调用。
  4. 子View可以通过requestDisallowInterceptTouchEvent方法去请求父元素不要拦截。

遇到过事件冲突:就是ViewPager其中一个Pager上面的Banner,当Banner还没滑到最后一页的时候,应该请求ViewPager不要拦截自己的滑动事件;当Banner滑动到最后一页的时候,要请求ViewPager去拦截自己的滑动事件,从而使得ViewPager能够跟Banner配合工作。

结合自己的Demo说说,举例子:爷父子之间传递苹果

11**、简要说说View的工作机制。有自定义过View吗?简要说说。 **

View的工作机制(View的绘制流程)主要包括测量(Measure)和绘制(Draw),而对于ViewGroup来说还包括布局(Layout)。

1、测量主要是根据用户指定的测量大小以及模式通过setMeasuredDimension方法去设置View的大小。精确模式下需要返回用户指定的大小。最大值模式以及不明确指定的模式下,需要提供默认大小,在最大值模式下需要返回两者的最小值。

2、绘制主要是通过一些画图API比如画布画笔,在View区域上面动态画一些东西。

3、布局主要是自定义ViewGroup的时候实现的,主要是控制各个子View在ViewGroup的位置以及他们之间的位置关系。

把《师兄帮帮》那个录音按钮给面试官看

12**、有没有(参与)写过(开源)框架? **

虽然我没有在GitHub上面参与或者发表过开源项目,感觉自己想传上去,但是发现已经有实现了,但是我经常关注GitHub上面看一些不错的开源项目和控件,比如说BGARefreshLayout、BaseRecyclerViewAdapterHelper、代码家以及各种自定义控件等。平常也有关注一些android开发相关的微信公众号,比如徐宜生、郭霖、android开发技术前线等,以及简书、CSDN等一些大牛的博客,平时自己也有在简书上面用MD来写一些技术文章。

13**、有没有关注一些新技术? **

RXJava:

一个异步请求库,核心就是异步。利用的是一种扩展的观察模式,被观察者发生某种变化的时候,可以通过事件(onNext、onError、onComplete)等方式通过观察者。RXJava同时支持线程的调度和切换,用户可以指定订阅发生的线程以及观察者触发的线程。

Retrofit 2.0:

通过注解的方式来指定URL、请求方法,实质上底层是通过OKHttp来实现的。

Dagger 2:一个依赖注入框架

HotFit:淘宝的Dexposed(兼容性不好,利用AOP面向切面编程实现)、阿里巴巴的AndFix(Dexposed从Method入手,但是AndFix从Field入手,不需要重启),QQ空间的ClassLoader(虚拟机加载类之前,通过反射去修复有问题的Dex文件),微信的Tinker(没有了解过,因为之前的手机用不了)、Nuwa(需要重启)

插件式开发:核心是动态加载技术。当app越来越大的时候,通过插件化来减轻应用的内存以及CPU,还可以实现热插拔,实现动态修复和更新。

大概了解,需要用到的技术:反射、动态代理、类加载器原理等。插件开发是开发者通过将插件代码封装成Jar或者Apk文件放在网络或者本地。在宿主app中加载并且调用插件中的方法。

14**、看过什么框架?源码?原理?有没有自己写过框架? **

**网络请求框架的基本流程: **

1、首先build request参数

2、因为不能在主线程请求HTTP,所以你得有个Executor或者线程

3、enqueue后,通过线程去执行请求,把Request对象通过字节流输出

4、得到服务器数据后,获取返回的输入字节流,通过反序列化转换成需

要的实体对象,最后通过callback回调给客户端,同时还要实现线程切换

**Retrofit****的一些优点: **

1、类型安全:通过指定泛型来实现

2、可扩展性强,可以适应不同的业务需求

3、简洁高效:通过注解的方式来实现网络请求,大大减少了代码量

4、高度解耦,内部通过Adapter模式适配不同的框架,比如RXJava等

**Retrofit****源码流程概述: **

1、先通过Builder模式去构造Retrofit对象,其中Retrofit只是外观模式包装的一个对象,所有基本功能都通过Retrofit来对外提供。构造Retrofit的内部构造了OkHttpClient对象,并且构建了CallbackExecutor对象,用来处理请求。同时也为Retrofit指定了Converter。

2、为我们的请求接口创建动态代理对象,当这个对象的方法被调用的时候,会被拦截,并且通过ServiceMethod类去通过反射的方式去获取这个对象上面的注解,通过这些注解获取网络请求方法、请求体等,最后构造出OKHttpCall对象、RequestConverter以及ResponseConverter,这两个Converter分别是代表把请求对象、返回对象进行转换,例如转换成JSON、XML等。(注意:由于通过反射获取对象的注解是耗时并且消耗性能的,ServiceMethod内部有做缓存,解析过就直接复用)

3、当我们调用Call对象进行enqueue或者execute的时候,内部是把OKHttpCall转换成CallAdapter,分别适配:RxJava, Java8, Guava还有一个Retrofit默认的回调。

4、把请求分发给CallbackExecutor对象执行,CallbackExecutor内部其实是调用OkHttp的一些相关方法进行请求。请求执行的时候通过RequestConverter进行转换。

5、请求返回的时候,判断返回码的类型,通过ResponseConverter对象把返回的字节流转换成实体对象,最后通过Handler机制post一个Runnable把Callback回调给主线程,刷新界面等操作。如果是使用RXJava的话,还可以很方便地先去指定子线程进行数据转换再返回主线程进行界面刷新。

**Retrofit****的一些功能扩展: **

1、通过自定义RequestConverter以及ResponseConverter进行数据网络传输的加密、解密。

2、可以通过自定义线程池来维护请求队列,适应具体的业务需求。比如先进先出、后进先出等。

3、通过Adapter模式(CallbackAdapter)可以利用不同的框架实现不同的回调代码,比如RXJava、Java8、Guava等。

4、通过不同Converter实现Request以及Resource适应不同的协议,比如JSON、XML等。

主流的网络请求框架

1**、**HttpURLConnection:在android4.4中已经替换成OKHttp了。

2**、**HttpClient:API数量比较多,使得我们很难在不破坏兼容性的

情况下对它进行升级和扩展维护成本较高,android5.0废弃,6.0删除。

3**、**Volley:已经停止更新。Android2.2以下版本使用HttpClient、

android2.2以上版本使用HttpURLConnection实现。但是相对来说扩

展性比Okhttp较低。

4**、**OkHttp:支持自定义请求头、支持一般的请求方法、文件上传

下载、图片加载、IP自动重连、Gzip、SPDY、连接池、HTTP缓存等。

Retrofit:和Volley框架的请求方式很相似,底层网络请求采用okhttp

(效率高,android4.4底层采用okhttp),采用注解方式来指定请求方

式和url地址,减少了代码量。

主流的图片加载框架

1.、Picasso:和Square的网络库一起能发挥最大作用,因为Picasso可以选择将网络请求的缓存部分交给了okhttp实现。 2.、Glide:模仿了Picasso的API,而且在他的基础上加了很多的扩展(比如gif等支持),支持图片流,因此在做爱拍之类的视频应用用得比较多一些。 3.、Fresco:Fresco中设计有一个叫做image pipeline的模块。它负责从网络,从本地文件系统,本地资源加载图片。 为了最大限度节省空间和CPU时间,它含有3级缓存设计(2级内存,1级文件)。Fresco中设计有一个叫做Drawees模块, 方便地显示loading图,当图片不再显示在屏幕上时,及时地释放内存和空间占用。

Fresco是把图片缓存放在了Ashmem(系统匿名内存共享区)

  1. Heap-堆内存:Android中每个App的 Java堆内存大小都是被严格的限制的。每个对象都是使用Java的new在堆内存实例化,这是内存中相对安全的一块区域。内存有垃圾回收机制,所以当App不在使用内存的时候,系统就会自动把这块内存回收。不幸的是,内存进行垃圾回收的过程正是问题所在。当内存进行垃圾回收时,内存不仅仅进行了垃圾回收,还把Android 应用完全终止了。这也是用户在使用 App 时最常见的卡顿或短暂假死的原因之一。
  2. Ashmem:Android 在操作 Ashmem 堆时,会把该堆中存有数据的内存区域从 Ashmem 堆中抽取出来,而不是把它释放掉,这是一种弱内存释放模式;被抽取出来的这部分内存只有当系统真正需要更多的内存时(系统内存不够用)才会被释放。当Android 把被抽取出来的这部分内存放回 Ashmem堆,只要被抽取的内存空间没有被释放,之前的数据就会恢复到相应的位置。

不管发生什么,垃圾回收器都不会自动回收这些 Bitmap。当 Android 绘制系统在渲染这些图片,Android 的系统库就会把这些Bitmap 从 Ashmem 堆中抽取出来,而当渲染结束后,这些Bitmap 又会被放回到原来的位置。如果一个被抽取的图片需要再绘制一次,系统仅仅需要把它再解码一次,这个操作非常迅速。

图片的三级缓存

当app需要使用图片的时候,先到内存缓存去取,因为内存缓存速度最快。如果内存缓存没有,再去本地缓存取,如果都没有,就到网络获取。获取以后分别写入内存缓存以及本地缓存。

自己写过图片加载框架:

概述:利用组合设计模式,图片加载功能在ImageLoader集中对外公开,ImageLoader是统筹了图片下载器,图片缓存等功能。

ImageLoader

ImageDownloaderer

ImageCache

ImageLoader

ImageLoader

遇到的问题:

1、
使用LruCache代替弱(软)引用存储,因为API9以后GC会更倾向于回收持有弱引用的对象。LruCache是android提供的一个缓存工具类,其算法是最近最少使用算法。它把最近使用的对象用“强引用”存储在LinkedHashMap中,并且把最近最少使用的对象在缓存值达到预设定值之前就从内存中移除。其在API12被引进,低版本可以用support包中的类。

2、
图片的错位问题。通过给ImageView打一个url标记,每次设置显示图片的时候先判断Bitmap是否对应某一个ImageView。

3、
url作为键值对中的键问题,使用MD5算法把url转为MD5字符串。

15**、网络优化、网络安全问题。**

为什么要做缓存,或者说有什么好处?

1、减少服务器负荷,降低延迟提升用户体验。

2、复杂的缓存策略会根据用户当前的网络情况采取不同的缓存策略,比如在2g网络很差的情况下,提高缓存使用的时间;

3、不用的应用、业务需求、接口所需要的缓存策略也会不一样,有的要保证数据的实时性,所以不能有缓存,有的你可以缓存5分钟,等等。你要根据具体情况所需数据的时效性情况给出不同的方案。当然你也可以全部都一样的缓存策略,看你自己。

网络优化建议点:

1、连接复用节省连接建立时间,如开启keep-alive

2、不用域名,用IP直连省去DNS解析过程,根据域名得到IP地址

3、能够缓存的尽量缓存起来,减少网络请求,减轻服务器负担

网络请求的安全性问题: 这块了解的不多。我给你说说我的思路吧,利用对称加密。服务器给我们的数据可以通过密钥和其他参数做个MD5加密,得到一个key;在客户端按照相同的办法生成key然后与服务端给的做个对比。

对称加密: 文件加密和解密使用相同的密钥,即加密密钥也可以用作解密密钥。DES、IDEA

非对称加密: 非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey)。公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。RSA,RCC

16**、进程、线程通信、Linux相关 **

如何开启多进程?

1、通过在清单文件对四大组件指定android:process属性。除此之外android是没有办法指定线程或者实体类运行所在的进程。

2、非常规方法:通过JNI在native层去fork一个新的进程。

多进程所带来的问题: 1、静态成员和单例模式完全失效。

2、线程同步机制会完全失效。

3、SharePreferences的可靠性会下降。 4、Application会多次被创建,例如在推送或者即时通信SDK中,需要单独开一个进程来接收消息,那么我们在Application中初始化SDK的时候,需要判断当前进程是否为当前APP的主进程,是就初始化,否则不做任何操作。

进程间通信方式对比:

方式

优点

缺点

适用场景

Bundle

简单易用

只支持Bundle支持的数据类型

四大组件之间的跨进程通信

文件共享

简单易用

不适合高并发场景,并且无法做到即时通信

无并发的情形,实时性不高的场景

AIDL

功能强大,支持一对多并发实时通信

适用稍微复杂,需要处理线程同步问题

一对多通信,实时性高

Messenger

功能一般,支持一对多串行实时通信

不能很好处理高并发,只能传输Bundle支持的类型

低并发的一对多实时通信,无需返回结果

ContentProvider

数据源访问方面功能强大,支持一对多并发数据共享,可以通过call方法进行扩展

可以理解为受约束的AIDL,主要是数据源提供CRUD操纵

一对多的进程间数据共享

Socket

功能强大,通过网络传输字节流,支持一对多并发实时通信

实现麻烦,不支持直接的RPC

网络数据交换

**Linux****中的进程间通信方式: **

1、命名管道  2、共享内存  3、信号量

**Android****中进程的级别: **

前台进程(正在与用户交互)、可见进程(有可见的界面)、服务进程、后台进程、空进程

常用的Linux命令:mkdir 创建文件夹、rmdir 删除文件夹、rm 删除文件、mv 移动文件、cp 拷贝文件、cat 查看文件

tail 查看文件尾部、more 分页查看文件、cd 切换当前目录、ls 列出文件清单、reboot 重启、date 显示日期

cal 显示日历、ps 查看系统进程相当于windows 的任务管理器、ifconfig 配置网络

** **

**Linux****进程和DVM的进程是否为同一个概念? **

每一个Android 应用程序都拥有一个独立的虚拟机实例,应用程序都在它自己的进程中运行。而每一个dvm都是在Linux中的一个进程,所以说可以近似认为是同一个概念。

独立的进程可以防止在虚拟机崩溃的时候所有程序都被关闭。

**Android****中的安全机制 **

1、Android 是基于Linux 内核的,因此Linux 对文件权限的控制同样适用于Android。在Android 中每个应用都有自己的/data/data/包名文件夹,该文件夹只能该应用访问,而其他应用则无权访问。

2、Android 的权限机制保护了用户的合法权益。如我们的代码想拨打电话、发送短信、访问通信录、定位、访问sdcard等所有可能侵犯用于权益的行为都是必须要在AndroidManifest.xml 中进行声明的,这样就给了用户一个知情权。并且在android6.0中引入了动态权限管理

3、Android 的代码混淆以及apk的签名保护了开发者的权益。

**AIDL****主要思路(步骤)就是: **

1、服务端创建一个Service来监听客户端的连接请求。创建AIDL文件,将接口暴露给客户端,并在Service中实现这个接口。

2、客户端绑定Service,并定成功之后将服务端返回的Binder对象转换成AIDL中的接口类型,然后就可以调用接口的方法,并且获取返回值。

17**、Android新特性相关**

  1. 5.0:Material Design、多种设备的支持、支持64位ART虚拟机、Project Volta电池续航改进计划等
  2. 6.0:动态权限管理、过度动画、支付、指纹等
  3. 7.0:分屏、通知消息快捷回复、夜间模式、流量保护模式等

18、在技术上的发展有什么规划?(离职原因)(实现自己的价值,学习牛逼的技术,公司的发展前景) **

之前的公司就是直接实现业务逻辑的,功能的话只要给时间一定可以实现的。然后在上一家公司就是接触到了一些思想层次上的,比如设计模式啊MVC之类的,然后买了一些书来看,现在在往这方面发展。以后的就要等我再学到一定层次再考虑了。

19、屏幕适配 **

1、**对应不同屏幕创建不同的dimen的文件夹,放置不同的大小 ,根据屏幕分辨率创建不同layout文件夹,使用权重来适配

2、**使用九切图和SVG图片、xml来代替直接使用png

3长度单位用dp代替px   ** 4**、使用wrap_content, match_parent, weight 5、**使用相对布局,在开发中禁用绝对布局

**上和左控制拉伸,下和右负责内容区域 5、使用线性布局,放不下的时候用ScrollView包裹 **

20**、android分包、插件化、热修复等相关知识: **

Dex分包 **

**引入原因:**在apk代码量越来越多的时候,往往会出现方法数量过多(超过了65536),而一个dex文件最多只支持65536个方法;apk过大不能在一些旧版本的android手机上面安装,因为android2.3以前的版本分配 用来执行dex优化的 内存只有5M。

android的加载器体系与java类加载器体系对比: **

1、PathClassLoader:加载安卓应用,只能加载

已安装到系统中(即/data/app目录下)的

apk文件。

2、 DexClass:可以加载apk,jar,及dex文件。

加载一个类的时候,ClassLoader先遍历一个装在dex文件(每个dex文件实际上是一个DexFile对象)的数组(Element数组,Element是一个内部类),然后依次去加载所需要的class文件,直到找到为止。

Dex注入的解决方案:假如我们将第二个dex文件放入Element数组中,在加载第二个dex包中的类时,应该可以直接找到。 **

Dex分包的注意要点: **

  1. 由于第二个dex包是在Application的onCreate中动态注入的,如果dex包过大,会使app(Activity)的启动速度变慢,因此,在dex分包过程中一定要注意,第二个dex包不宜过大。

  2. 由于上述第一点的限制,假如我们的app越来越臃肿和庞大,往往会采取dex分包方案和插件化方案配合使用,将一些非核心独立功能做成插件加载,核心功能再分包加载。

** **

**插件化 **

思路:通过在宿主app里面注册添加一些activity作为插件app的容器,加载插件app,通过动态代理的方法去处理被

加载(被代理)的activity、fragment的生命周期。由此引出Activity与Fragment之间的传值问题**

第三方服务:apkplug,还没有仔细研究就辞职了,由此引出久邦数码举办的app创新设计大赛。

插件化第三方框架:360的DroidPlugin、携程的DynamicAPK、今年初开源的Small

21**、android插件化是否有其他更好的代替方法? **

**插件化引入: **

1、APK方法数量过多2、低版本手机安装出错

3、移动应用中的2/8定律,80%的用户访问20%的页面,那么剩下80%的页面是没必要用户去下载的,只有在用到的时候去下载。通过这样可以大大减轻app对CPU、内存的负担,同时也实现动态加载的目的。

**代替方案: **

1、移动应用从C/S架构向B/S架构的转变。最近比较火的一个框架React Native。基于页面级别的动态加载。2、买付费的Proguard

3、最笨的方法是删除无用的代码以及模块

**热修复 **

app上线之后需要紧急去修改一些有问题的代码,常规的方法是重新测试、打包、混淆加密加固、上线。这样太麻烦。

**第三方方案: **

**阿里巴巴的AndFix:**通过加载器读取一个dex文件中的类,并找到被注解标记到的要修复的方法,再通过jni在C层替换掉原本出BUG的方法的指针,从而达到热修复的目的。

QQ空间的热修复方案:**MultiDex的思路,在应用启动的时候,往Classloader的PathList里面插入一个Dex,通过覆盖掉出BUG的类来做到热修复。QQ空间只出了理论方案,而这套方案的开源代码实现,则是由 贾吉鑫 写了一个 nuwa 托管在 Github 上。

** **

22**、怎么定位 **

**anr****问题代码位置? **

** **

ANR:**Application Not Response

导致ANR的原因有:主线程(Activity、Service)是5 秒,BroadCastReceiver 是10 秒。(自己项目:在主线程去读取SD卡)

**解决方案:**将所有耗时操作,比如访问网络,Socket 通信,查询大量SQL 语句,复杂逻辑计算等都放在子线程中去,然后通过handler.sendMessage、runonUITread去刷新UI;显示Loading!!!;onReceive启动Service

**定位:**发生ANR以后,系统会在/data/anr目录下面生成一个traces.txt文件

第一步,先pull出/data/anr/traces.txt文件。 第二步,查看app被杀死时的线程id。 第三步,查看traces.txt里关于该线程id的信息。

典型场景,调用关系比较复杂的时候,比如说主线程的方法里面开了一个子线程,子线程获得了同步锁,主线程的方法被阻塞掉。此时应该先分析主线程,查看什么锁被什么线程hold住了,然后再对应去看那个线程。

23**、避免OOM的一些常见方法:**

1、App资源中尽量少用大图。使用Bitmap的时候要注意等比例缩小图片,并且注意Bitmap的回收。 BitmapFactory.Options options = new BitmapFactory.Option(); options.inSampleSize = 2; //Options 只保存图片尺寸大小,不保存图片到内存 BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inSampleSize = 2; Bitmap bmp = null; bmp = BitmapFactory.decodeResource(getResources(), mImageIds[position],opts); //回收 bmp.recycle(); 2、结合组件的生命周期,释放资源 3、IO流,数据库查询的游标等应该在使用完之后及时关闭。 4、ListView中应该使用ViewHolder模式缓存ConverView 5、页面切换的时候尽量去传递(复用)一些对象,通过Intent传递过去

24**、Android中如何捕获未捕获的异常 **

1、自定义Application,实现Thread.UncaughtExceptionHandler

2、实现uncaughtException(Thread t, Throwable e)方法,在方法中处理异常。如果不能处理的话,一般android.os.Process.killProcess(android.os.Process.myPid());来结束自己,避免把异常信息报给用户。

3、在Application初始化的时候设置Thread.setDefaultUncaughtExceptionHandler(this);

** **

25**、Android6.0新增的动态权限 **

1、对于6.0以下的权限及在安装的时候,根据权限声明产生一个权限列表,用户只有在同意之后才能完成app的安装,也就是说权限在安装的时候已经授予了。

2、对于6.0或以上的版本,谷歌把权限分为了两类,

一种是Normal Permission,例如访问网络、网络状态获取、震动等;

另外一种是Dangerous Permission,例如发送、读取短信、打电话、读取通信录、录音等。并且引入了权限组的概念,如果你申请某个危险的权限,假设你的app早已被用户授权了同一组的某个危险权限,那么系统会立即授权,而不需要用户去点击授权

**权限申请方式: **

1、在清单文件里面配置权限,跟以前一样。

2、检查权限ContextCompat.checkSelfPermission,方法返回值为PackageManager.PERMISSION_DENIED或者PackageManager.PERMISSION_GRANTED。

3、在Activity中使用ActivityCompat的requestPermissions,或者在Fragment里面直接使用Fragment提供的requestPermissions方法去申请某一个危险权限,然后在onRequestPermissionsResult回调里面进行业务逻辑。

**最简单的解决方式:在gradle配置文件中把targetSDK改为小于23。 **

26**、布局优化 **

1、使用includea标签去复用相同的布局

2、使用merge标签去除同类的视图

3、使用ViewStub来延迟加载一些不常用的布局

27**、内存优化 **

1、珍惜Service资源,尽量使用IntentService IntentService在内部其实是通过线程以及Handler实现的,当有新的Intent到来的时候,会创建线程并且处理这个Intent,处理完毕以后就自动销毁自身。因此使用IntentService能够节省系统资源。

2、实现一些在内存紧张的时候会回调的一些方法,比如Activity中的onLowMemory、onTrimMemory。比如停止动画特效等。

3、通过Manifest中对Application配置更大的内存,但是一般不推荐。 android:largeHeap="true" 4、避免Bitmap的浪费,应该尽量去适配屏幕设备。尽量使用成熟的图片加载框架,Picasso,Fresco,Glide等。 5、使用优化的容器,SparseArray等 6、其他建议:尽量少用枚举变量,尽量少用抽象,尽量少增加类,避免使用依赖注入框架,谨慎使用library,使用代码混淆,时当场合考虑使用多进程等。 7、避免内存泄漏(本来应该被回收的对象没有被回收)。一旦APP的内存短时间内快速增长或者GC非常频繁的时候,就应该考虑是否是内存泄漏导致的。

28**、性能优化 **

1、防止过度绘制,通过打开手机的“显示过度绘制区域”即可查看过度绘制的情况。 2、最小化渲染时间,使用视图树查看节点,对节点进行性能分析。 3通过TraceView进行数据的采集以及分析。在有大概定位的时候,使用Android官方提供的Debug类进行采集。最后通过DDMS即可打开这个.trace文件,分析函数的调用情况(包括在指定情况下执行时间,调用次数) //开启数据采集 Debug.startMethodTracing("test.trace"); //关闭 Debug.stopMethodTracing();

29**、SharedPreference **

SharedPreference.Editor的apply和commit方法异同: ** 1. apply没有返回值而commit返回boolean表明修改是否提交成功 2. apply是将修改数据原子提交到内存, 而后异步真正提交到硬件磁盘, 而commit是同步的提交到硬件磁盘,因此,在多个并发的提交commit的时候,他们会等待正在处理的commit保存到磁盘后在操作,从而降低了效率。而apply只是原子的提交到内存,后面有调用apply的函数的将会直接覆盖前面的内存数据,这样从一定程度上提高了很多效率。 3. apply方法不会提示任何失败的提示。

**注意点: **

0、每次调用commit()、apply()都会将整个settings全部写到文件中,即使你只改动了一个setting。因为它是基于全局的,而不是增量的,所以你的客户端代码中一定不要出现一个putXXX就紧跟着一个commit/apply,而是put完所有你要的改动,最后调用一次commit/apply。 1、由于在一个进程中,SharedPreference是单实例,一般不会出现并发冲突,如果对提交的结果不关心的话,建议使用apply。2、当然需要确保提交成功且有后续操作的话,还是需要用commit(利用返回值)的。

3、**源码中多线程同步问题:**SharedPreference通过一个标志来控制多线程并发读写的,数据提交的时候,会把标志加一,数据提交完毕的时候会减一,因此如果判断到标志大于0的话就证明还有数据没有提交完成,那么就把数据集(Map)重新拷贝一份再进行后续提交操作。

4、**多进程中:**SharePreferences的可靠性会下降。

SharedPreference的xml文件什么时候加载到内存中? **

app启动Context初始化的时候,通过SharedPreferencesImpl的startLoadFromDisk中会起一个线程调用loadFromDiskLocked从磁盘上加载数据。

如果一个应用中有好几个sharedPreferences时,每个sp对应的SharedPreferencesImpl都会各自起一个线程去把xml中的数据加载到map中,后面所有的getXXX方法实际上都是从内存中取数据,不会再有读磁盘的动作了。

**注意:**SharedPreference的序列化和解析的操作都是在内存中完成,由于每一个DVM分配的堆内存一般最大只有16M,因此不建议在SharedPreference中保存太大的数据。

30**、Activity与Fragment传值问题 **

1、findFragmentByTag或者getActivity获得对方的引用(强转)之后,再相互调用对方的public方法,耦合度高。

2、通过Bundle的方法进行传值,fragment.setArguments()

3、通过EventBus或者RxBus,耦合度低。

31**、Activity相关 **

Activity生命周期 **

Activity 从创建到销毁有多种状态,从一种状态到另一种状态时会激发相应的回调方法,这些回调方法包括:

onCreate onStart onResume onPause onStop onDestroy

其实这些方法都是两两对应的,onCreate 创建与onDestroy 销毁;onStart 可见与onStop 不可见;onResume 可编辑(即焦点)与onPause;在Activity 被onStop 后,但是没有被onDestroy,在再次启动此Activity 时就调用onRestart(而不再调用onCreate)方法;如果被onDestroy 了,则是调用onCreate 方法。

** **

Activity的启动原理(大概把过程说出来) **

1.无论是通过Launcher来启动Activity,还是通过Activity内部调用startActivity接口来启动新的Activity,都通过Binder进程间通信进入到ActivityManagerService进程中,并且调用ActivityManagerService.startActivity接口;

2.ActivityManagerService调用ActivityStack.startActivityMayWait来做准备要启动的Activity的相关信息;

3.ActivityStack通知ApplicationThread要进行Activity启动调度了,这里的ApplicationThread代表的是调用ActivityManagerService.startActivity接口的进程,对于通过点击应用程序图标的情景来说,这个进程就是Launcher了,而对于通过在Activity内部调用startActivity的情景来说,这个进程就是这个Activity所在的进程了;

4.ApplicationThread不执行真正的启动操作,它通过调用.activityPaused接口进入到ActivityManagerService进程中,看看是否需要创建新的进程来启动Activity;

5.对于通过点击应用程序图标来启动Activity的情景来说,ActivityManagerService在这一步中,会调用startProcessLocked来创建一个新的进程,而对于通过在Activity内部调用startActivity来启动新的Activity来说,这一步是不需要执行的,因为新的Activity就在原来的Activity所在的进程中进行启动;

6.ActivityManagerServic调用ApplicationThread.scheduleLaunchActivity接口,通知相应的进程执行启动Activity的操作;

7.ApplicationThread把这个启动Activity的操作转发给ActivityThread,ActivityThread通过ClassLoader导入相应的Activity类,然后把它启动起来。

**两个Activity 之间跳转时必然会执行的是哪几个方法? **

一般情况下比如说有两个activity,分别叫A,B,当在A 里面激活B 组件的时候, A 会调用onPause()方法,然后B 调用onCreate() ,onStart(), onResume()。

这个时候B 覆盖了窗体, A 会调用onStop()方法. 如果B 是个透明的,或者是对话框的样式, 就不会调用A 的onStop()方法。

**异常状态下的Activity生命周期 **

资源相关的系统配置发生改变或者资源不足:例如屏幕旋转,当前Activity会销毁,并且在onStop之前回调onSaveInstanceState保存数据,在重新创建Activity的时候在onStart之后回调onRestoreInstanceState。其中Bundle数据会传到onCreate(不一定有数据)和onRestoreInstanceState(一定有数据)。 防止屏幕旋转的时候重建,在清单文件中添加配置: android:configChanges="orientation"。屏幕旋转的时候会回调onConfigurationChanged方法。

Activity的启动模式** 1、standard:每次激活Activity时(startActivity),都创建Activity实例,并放入任务栈; 2、singleTop:如果某个Activity自己激活自己,即任务栈栈顶就是该Activity,则不需要创建,其余情况都要创建Activity实例; 3、singleTask:如果要激活的那个Activity在任务栈中存在该实例,则不需要创建,只需要把此Activity放入栈顶,即把该Activity以上的Activity实例都pop,并调用其onNewIntent;但是getIntent方法返回的还是旧的Intent。 4、singleInstance:启用新的任务栈创建Activity实例,如果应用2也要激活Activity,则不需要创建,两应用共享该Activity实例。

32**、Service相关,Service与Activity之间的交互-利用进程间通信的方式 **

Service运行所在的进程**:不配置的情况下默认在当前app进程中的主线程中运行,因此不能执行耗时操作。

Service的两种启动方式 **

Activity 通过bindService(Intent service, ServiceConnection conn, int flags)跟Service 进行绑定,当绑定成功的时候Service 会将代理对象通过回调的形式传给conn,这样我们就拿到了Service 提供的服务代理对象。在Activity 中可以通过startService 和bindService 方法启动Service。一般情况下如果想获取Service 的服务对象那么肯定需要通过bindService()方法,比如音乐播放器,第三方支付等。如果仅仅只是为了开启一个后台任务那么可以使用startService()方法。

**非绑定模式:**当第一次调用startService 的时候执行的方法依次为onCreate()、onStartCommand(),(onStart())。当Service 关闭的时候调用onDestory 方法。

**绑定模式:**第一次bindService()的时候,执行的方法为onCreate()、onBind()解除绑定的时候会执行onUnbind()、onDestory()。

**注意 **

1、Service 实例只会有一个,也就是说如果当前要启动的Service 已经存在了那么就不会再次创建该Service 当然也不会调用onCreate()方法。

2、一个Service 可以被多个客户进行绑定,只有所有的绑定对象都执行了onBind()方法后该Service 才会销毁,不过如果在混合模式下,有一个客户执行了onStart()方法,那么这个时候如果所有的bind 客户都执行了unBind()该Service 也不会销毁。

**IntentService **

Service 本身存在两个问题:

1、Service 不会专门启动一条单独的进程,Service 与它所在应用位于同一个进程中;

2、Service 也不是专门一条新线程,因此不应该在Service 中直接处理耗时的任务;

IntentService的优点: **

会创建独立的worker 线程来处理所有的Intent 请求;

会创建独立的worker 线程来处理onHandleIntent()方法实现的代码,无需处理多线程问题;

所有请求处理完成后,IntentService 会自动停止,无需调用stopSelf()方法停止Service;

为Service 的onBind()提供默认实现,返回null;

为Service 的onStartCommand 提供默认实现,将请求Intent 添加到队列中;

IntentService的原理: **

IntentService在内部其实是通过线程以及Handler实现的,当有新的Intent到来的时候,会在onCreate中创建线程,Looper以及Handler。在onStart中通过Handler通知子线程去回调onHandleIntent方法去处理这个Intent,处理完毕以后就通过stopSelf方法销毁自身。因此使用IntentService能够节省系统资源。

**如何让Service成为前置进程? **

在onStartCommand中发一个Notification通知。

**Service **的onStartCommand 方法有几种返回值?各代表什么意思? **

有四种返回值,不同值代表的意思如下:

1、START_STICKY:如果service进程被kill掉,保留service 的状态为开始状态,但不保留递送的intent对象。随后系统会尝试重新创建service,由于服务状态为开始状态,所以创建服务后一定会调用onStartCommand方法。如果在此期间没有任何启动命令被传递到service,那么参数Intent 将为null。

2、START_NOT_STICKY:“非粘性的”。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统不会自动重启该服务。

3、START_REDELIVER_INTENT:重传Intent。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill 掉,系统会自动重启该服务,并将Intent 的值传入。

4、START_STICKY_COMPATIBILITY:START_STICKY 的兼容版本,但不保证服务被kill 后一定能重启。

**Service **的onRebind(Intent)方法在什么情况下会执行? **

如果在onUnbind()方法返回true 的情况下会执行,否则不执行。

33**、广播 **

**内部通信实现机制:**通过Android 系统的Binder 机制实现通信。

**广播的两种分类: **

无序广播:完全异步,逻辑上可以被任何广播接收者接收到。优点是效率较高。缺点是一个接收者不能将处理结

果传递给下一个接收者,并无法终止广播intent 的传播。(优先级对无序广播也会生效)

有序广播:按照被接收者的优先级顺序,在被接收者中依次传播。每个接收者有权终止广播,但是如果设置了最终接收者除外。如果传播过程中没有广播被终止,最终接收者会回调两次onReceive,第一次是按照正常顺序触发的,第二次是最终接受触发。

**判断广播类型:**在onReceive方法中通过isOrderedBroadcast()方法

**注册:**广播接收者可以在清单文件中设置,也可以在代码中设置,但是要注意注销,否则会引发内存泄漏。

**动态注册的优先级是通过注册先后来确定的,先注册的接收者优先级高。 **

**广播的生命周期 **

BroadCastReceiver 的生命周期

1、广播接收者的生命周期非常短暂的,在接收到广播的时候创建,onReceive()方法结束之后销毁;

2、广播接收者中不要做一些耗时的工作,否则会弹出Application No Response 错误对话框;(不能超过10秒)

3、最好也不要在广播接收者中创建子线程做耗时的工作,因为广播接收者被销毁后进程就成为了空进程,很容易

被系统杀掉;最好新开一个Service**来实现;

**如何让广播在指定app中才能接收?即广播自定义权限 **

1、在清单文件中通过permission标签定义自定义权限,然后uses-permission声明这个权限

2、在指定app中也同样做以上操作,定义广播接收者时候也要添加permission属性。

34**、ContentProvider相关 **

**简要介绍ContentProvider如何实现了应用程序之间的数据共享? **

1、APP如果需要把数据共享出去,先要自定义ContentProvider,实现CURD方法,在清单文件中注册。

2、第三方APP就可以通过ContentResolver进行数据的操作。

3、用户可以通过注册ContentObserver对数据提供者的数据变化进行监听,例如监听短信数据库来实现短信验证码自动填写。

35**、SurfaceView与View的区别 **

别:surfaceView是在一个新起的单独线程中可以重新绘制画面,而View必须在UI的主线程中更新画面。那么在UI的主线程中更新画面 可能会引发问题,比如你更新画面的时间过长,那么你的主UI线程会被你正在画的函数阻塞。那么将无法响应按键,触屏等消息。当使用surfaceView 由于是在新的线程中更新画面所以不会阻塞你的UI主线程。但这也带来了另外一个问题,就是事件同步。比如你触屏了一下,你需要surfaceView中 thread处理,一般就需要有一个event queue的设计来保存touch event,这会稍稍复杂一点,因为涉及到线程同步。

**所以基于以上,根据游戏特点,一般分成两类。 **

1 被动更新画面的。比如棋类,这种用view就好了。因为画面的更新是依赖于onTouch 来更新,可以直接使用 invalidate。 因为这种情况下,这一次Touch和下一次的Touch需要的时间比较长些,不会产生影响。

2 主动更新。比如一个人在一直跑动。这就需要一个单独的thread不停的重绘人的状态,避免阻塞main UI thread。所以显然view不合适,需要surfaceView来控制。

Scroller的原理

public classMyView extendsView {

private Context mContex

Scroller mScroller =****newScroller**(mContext);**

private void smoothScrollTo**(intdestX,intdestY)****{**

int scrollX = getScrollX**();**

//计算滑动距离,startScroll保存参数

int delta = destX - scrollX**;**

mScroller**.startScroll(scrollX,0,destX,0);**

}

@Override

public void computeScroll**()****{**

if **(mScroller.computeScrollOffset())****{// **

//滑动并且刷新

scrollTo**(mScroller.getCurrX(),mScroller.getCurrY());**

postInvalidate**();**

}

}

}** **

** **

**Scroller****执行流程里面的三个核心方法以及滑动的原理: **

mScroller.startScroll()

mScroller.computeScrollOffset()

view.computeScroll()

1、在mScroller.startScroll()中为滑动做了一些初始化准备。

比如:起始坐标,滑动的距离和方向以及持续时间(有默认值),动画开始时间等

2、mScroller.computeScrollOffset()方法主要是根据当前已经消逝的时间来计算当前的坐标点。

因为在mScroller.startScroll()中设置了动画时间,那么在computeScrollOffset()方法中依据已经消逝的时间就很容易得到当前时刻应该所处的位置并将其保存在变量mCurrX和mCurrY中。除此之外该方法还可判断动画是否已经结束。

3、postInvalidate方法会导致View的重绘,重绘的时候又会回调computeScroll方法去进行View的滑动,如此往复。

36**、工具相关 **

**请介绍下adb、ddms、aapt 的作用 **

**adb **Android Debug Bridge ,Android调试桥的意思。一个命令行工具,Android的主要调试工具。

ddms Dalvik Debug Monitor Service,dalvik 调试监视服务。ddms 是一个在adb 基础上的一个图形化工具。ddms是程序执行的查看器,可以查线程和堆栈信息。而traceView是ddms中的一部分,用于程序性能分析。

aapt 即Android Asset Packaging Tool,在SDK 的build-tools 目录下。该工具可以查看,创建,更新ZIP格式的文档附件(zip, jar, apk)。也可将资源文件编译成二进制文件,尽管我们没有直接使用过该工具,但是开发工具会使用这个工具打包apk 文件构成一个Android 应用程序。

37**、ListView等列表控件 **

**优化问题: **

1、使用ViewHolder模式,创建静态ViewHolder类,复用ConverView   2、使用分页加载     3、使用弱引用去引用一些控件。因为控件上有Context对象,这样做防止了内存泄漏。

**ViewHolder ****为什么要声明为静态类? **

非静态内部类拥有外部类对象的强引用,因此为了避免对外部类(外部类很可能是Activity)对象的引用,那么最

好将内部类声明为static 的。

使用Handler的时候:自定义的Handler内部类应该声明为static,并且使用弱引用去引用Context等,防止Context不能正常销毁而导致内存泄漏。

ListView****中的设计模式**:adapter模式、观察者模式、享元模式(ItemView的复用)

ScrollView****中嵌套ListView问题: **

问题:**例如在滑动页面中嵌套一个显示物流信息的ListView,这样会导致ListView显示不全,只显示一个条目。

解决办法:在ScrollView里面加一层LinearLayout,通过代码动态设置ListView的高度。

38****、视屏播放 **

1、MediaPlayer   2、VideoView(内部还是使用了MediaPlayer)  3、第三方开源万能播放器VitamioPlayer

39****、Intent **

Intent ****传递数据时,可以传递哪些类型数据? **

Intent 可以传递的数据类型非常的丰富,java 的基本数据类型和String 以及他们的数组形式都可以,除此之

外还可以传递实现了Serializable 和Parcelable 接口的对象。

Serializable ****(Java)和Parcelable (Android)的区别 **

1.在使用内存的时候,Parcelable 类比Serializable 性能高,所以推荐使用Parcelable 类。

2.Serializable 在序列化的时候会产生大量的临时变量,从而引起频繁的GC。

3.Parcelable 不能使用在要将数据存储在磁盘上的情况。尽管Serializable 效率低点,但在这种情况下,还是建议用Serializable 。

实现:

1 Serializable 的实现,只需要继承Serializable ,指定序列化ID即可。这只是给对象打了一个标记,系统会自动将其序列化。

2 Parcelabel 的实现,需要在类中添加一个静态成员变量CREATOR,这个变量需要继承Parcelable.Creator 接口。

40****、Fragment相关**

FragmentManager****的add方法与replace方法的异同: **

1、add 的时候是把所有的Fragment 一层一层的叠加到了FrameLayout 上

2、replace 的话首先将该容器中的其他Fragment 去除掉然后将当前Fragment 添加到容器中,因此使用replace方法的时候要注意会触发onDestroyView。**

3、一般思路是先add进来,然后通过hide以及show来进行Fragment的切换,此时Fragment的生命周期没有变化**。

Fragment****实现Activity压栈和出栈的效果**:通过addToBackStack方法实现,内部维护了一个双向链表。

Activity和Fragment之间你是怎么传值的? **

  1. 通过findFragmentByTag或者getActivity获得对方的引用(强转)之后,再相互调用对方的public方法,但是这样做一是引入了“强转”的丑陋代码,另外两个类之间各自持有对方的强引用,耦合较大,容易造成内存泄漏。
  2. 通过Bundle的方法进行传值,并且在创建Fragment的时候通过setArguments方法把Bundle设置进去。
  3. 利用eventbus进行通信,这种方法实时性高,而且Activity与Fragment之间可以完全解耦。

41****、Android中的动画 **

动画分类:帧动画,补间动画,属性动画(实际View的移动,点击事件会跟随)

如何为Activity或者Fragment添加动画? **

1、自定义主题,复写相应的属性,如下图所示   2、在finish或者startActivity之后调用overridePendingTransition

42****、签名、多渠道打包,上线流程 **

签名简介:**在Android 系统中,所有安装到系统的应用程序都必有一个数字证书,此数字证书用于标识应用程序的作者和在 **

应用程序之间建立信任关系。Android 系统要求每一个安装进系统的应用程序都是经过数字证书签名的。

签名好处:**(1)有利于程序升级,当新版程序和旧版程序的数字证书相同时,Android 系统才会认为这两个程序是同一个程序

的不同版本。如果新版程序和旧版程序的数字证书不相同,则Android 系统认为他们是不同的程序,并产生冲突,会

要求新程序更改包名。

(2)有利于程序的模块化设计和开发。Android 系统允许拥有同一个数字签名的程序运行在一个进程中,Android程序会将他们视为同一个程序。所以开发者可以将自己的程序分模块开发,而用户只需要在需要的时候下载适当的模块。

在签名时,需要考虑数字证书的有效期: **

(1)数字证书的有效期要包含程序的预计生命周期,一旦数字证书失效,持有改数字证书的程序将不能正常升级。

(2)如果多个程序使用同一个数字证书,则该数字证书的有效期要包含所有程序的预计生命周期。

(3)Android Market 强制要求所有应用程序数字证书的有效期要持续到2033 年10 月22 日以后。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值