杭州Android培训
达内杭州Android培训中心

13732203138

热门课程

Android面试总结,助你就业一臂之力!

  • 时间:2018-11-13 16:37
  • 发布:转载
  • 来源:网络

本文为大家分享的是一份Android面试总结,如果你也从事Android相关的工作,希望对你成为Android工程师有所帮助!大家一起来看看吧。

接口的意义

规范、扩展、回调

在Activity中如何保存/恢复状态?

分别调用onSaveInstanceState和onRestoreInstanceState 2个方法保存和恢复状态。

launchMode之singleTask与taskAffinity

taskAffinity是用来指示Activity属于哪一个Task的。taskAffinity能够决定以下两件事情(前提是Activity的launchMode为singleTask或者设置了FLAG_ACTIVITY_NEW_TASK),默认情况下,在一个app中的所有Activity都有一样的taskAffinity,但是我们可以设置不同的taskAffinity,为这些Activity分Task。甚至可以在不同的app之中,设置相同的taskAffinity,以达到不同app的activity公用同一个Task的目的。

res/raw和asserts的区别

这两个目录下的文件都会被打包进APK,并且不经过任何的压缩处理。

assets与res/raw不同点在于,assets支持任意深度的子目录,这些文件不会生成任何资源ID,只能使用AssetManager按相对的路径读取文件。如需访问原始文件名和文件层次结构,则可以考虑将某些资源保存在assets目录下。

图片放错目录会产生的问题吗?

高密度(density)的系统去使用低密度目录下的图片资源时,会将图片长宽自动放大以去适应高密度的精度,当然图片占用的内存会更大。所以如果能提各种dpi的对应资源那是最好,可以达到较好内存使用效果。如果提供的图片资源有限,那么图片资源应该尽量放在高密度文件夹下,这样可以节省图片的内存开支。

drawable-nodpi文件夹

这个文件夹是一个密度无关的文件夹,放在这里的图片系统就不会对它进行自动缩放,原图片是多大就会实际展示多大。但是要注意一个加载的顺序,drawable-nodpi文件夹是在匹配密度文件夹和更高密度文件夹都找不到的情况下才会去这里查找图片的,因此放在drawable-nodpi文件夹里的图片通常情况下不建议再放到别的文件夹里面。

Bitmap和Drawable

Bitmap是Android系统中的图像处理的最重要类。可以简单地说,Bitmap代表的是图片资源在内存中的数据结构,如它的像素数据,长宽等属性都存放在Bitmap对象中。Bitmap类的构造函数是私有的,只能是通过JNI实例化,系统提供BitmapFactory工厂类给我们从从File、Stream和byte[]创建Bitmap的方式。

Drawable官文文档说明为可绘制物件的一般抽象。View也是可以绘制的,但Drawable与View不同,Drawable不接受事件,无法与用户进行交互。我们知道很多UI控件都提供设置Drawable的接口,如ImageView可以通过setImageDrawable(Drawable drawable)设置它的显示,这个drawable可以是来自Bitmap的BitmapDrawable,也可以是其他的如ShapeDrawable。

也就是Drawable是一种抽像,最终实现的方式可以是绘制Bitmap的数据或者图形、Color数据等。理解了这些,你很容易明白为什么我们有时候需要进行两者之间的转换。

要加载很大的图片怎么办?

如果图片很大,比如他们的占用内存算下来就直接OOM了,那么我们肯定不能直接加载它。解决主法还是有很多的,系统也给我们提供了一个类BitmapRegionDecoder,可以用来分块加载图片。

图片圆角(或称矩形圆角)或者圆形头像的实现方式

除了把原图直接做成圆角外,常见有三种方式实现:

使用Xfermode混合图层;

使用BitmapShader;

通过裁剪画布区域实现指定形状的图形(ClipPath)

Context

ContextWrapper是上下文功能的封装类,而ContextImpl则是上下文功能的实现类,Context的具体能力是由ContextImpl类去实现。那么Context到底可以实现哪些功能呢?这个就实在是太多了,弹出Toast、启动Activity、启动Service、发送广播、操作数据库等等等等都需要用到Context。

getApplicationContext()比getApplicatio()作用域会更广一些,但实际上获取的对象是同一个。

除了上面两个方法之外,其实还有一个getBaseContext()方法,这个方法实际上获得的是一个ContextImpl对象。ContextWrapper的attachBaseContext()方法其实是由系统来调用的,它会把ContextImpl对象作为参数传递到attachBaseContext()方法当中,从而赋值给mBase对象,之后ContextWrapper中的所有方法其实都是通过这种委托的机制交由ContextImpl去具体实现的,所以说ContextImpl是上下文功能的实现类是非常准确的。

内存泄漏的原因:

导致内存泄漏有很多原因,最常见的有内部类的使用,因为内部类持有外部引用。

还有例如:1.资源对象没关闭造成的内存泄漏(源性对象比如(Cursor,File文件等)往往都用了一些缓冲,我们在不使用的时候,应该及时关闭它们,以便它们的缓冲及时回收内存) 2.构造Adapter时,没有使用缓存的convertView 3.Bitmap对象不在使用时调用recycle()释放内存(视情况而定) 4.使用Application context。这个context的生存周期和你的应用的生存周期一样长,而不是取决于activity的生存周期。如果你想保持一个长期生存的对象,并且这个对象需要一个context,记得使用application对象。你可以通过调用 Context.getApplicationContext() or Activity.getApplication()来获得 5.注册没取消造成的内存泄漏(一些Android程序可能引用我们的Anroid程序的对象(比如注册机制)。即使我们的Android程序已经结束了,但是别的引用程序仍然还有对我们的Android程序的某个对象的引用,泄漏的内存依然不能被垃圾回收。调用registerReceiver后未调用unregisterReceiver。) 6.集合中对象没清理造成的内存泄漏(我们通常把一些对象的引用加入到了集合中,当我们不需要该对象时,并没有把它的引用从集合中清理掉,这样这个集合就会越来越大。如果这个集合是static的话,那情况就更严重了。)

oom和内存泄漏

内存泄漏:就是存在一些被分配的对象,这些对象有下面两个特点,首先,这些对象是可达的,及有引用指向,存在通路可以与其相连;其次,这些对象是无用的,即程序以后不会再使用这些对象。如果对象满足这两个条件,这些对象就可以判定为Java中的内存泄漏,这些对象不会被GC所回收,然而它却占用内存。

内存泄露的原因:

1、资源对象没关闭造成的内存泄漏,如查询数据库后没有关闭游标cursor

2、构造Adapter时,没有使用 convertView 重用

3、Bitmap对象不在使用时调用recycle()释放内存

4、对象被生命周期长的对象引用,如activity被静态集合引用导致activity不能释放

内存泄漏示例:非静态的内部类和匿名内部类都会隐式地持有其外部类的引用。静态的内部类不会持有外部类的引用

不会导致内存泄露的代码如下:

当这个Activity退出时消息队列中还有未处理的消息或者正在处理消息,而消息队列中的Message持有mHandler实例的引用,mHandler又持有Activity的引用,所以导致该Activity的内存资源无法及时回收,引发内存泄漏

四种引用的区别

1、强引用

通常我们编写的代码都是强引用,eg :Object obj = new Object();当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。比如你创建一个很长的数组Object[] objArr = new Object[10000];当运行至这句时,如果内存不足,JVM会抛出OOM错误也不会回收数组中的object对象。不过要注意的是,当方法运行完之后,数组和数组中的对象都已经不存在了,所以它们指向的对象都会被JVM回收。

2、软引用

只要有足够的内存,就一直保持对象。一般可用来实现缓存,通过java.lang.ref.SoftReference类实现。内存非常紧张的时候会被回收,其他时候不会被回收,所以在使用之前需要判空,从而判断当前时候已经被回收了。

3、弱引用

通过java.lang.ref.WeakReference或java.util.WeakHashMap类实现,eg : WeakReference p = new WeakReference(new Person(“Rain”));不管内存是否足够,系统垃圾回收时必定会回收。

4、虚引用

不能单独使用,主要是用于追踪对象被垃圾回收的状态。通过java.lang.ref.PhantomReference类和引用队列ReferenceQueue类联合使用实现。

如何打多渠道包

在AndroidMainfest.xml配置相应的渠道

<meta-data android:value="UMENG_CHANNEL"

android:name="${UMENG_CHANNEL_VALUE}"/> <!--动态更改渠道号-->

在build.gradle中配置渠道信息和自动替换脚本

//多渠道打包

productFlavors {

xiaomi {}

huawei {}

yingyongbao {}

wandoujia {}

}
//自动替换清单文件中的渠道号

productFlavors.all {

flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]

比如会问在一个工作线程中创建自己的消息队例应该怎么做

Looper.prepare(在每个线程只允许执行一次)

handler

handler通过post(Runnable)和sendMessage(Message)传递Runnable和Message,最后调用的是sendMessageAtTime

MessageQueue核心方法:enqueueMessage和next(实现原理:synchronized 和 for(;;))

Looper核心方法:Looper.prepare()和Looper.loop(),其中loop()方法里面是一个for(;;)死循环

msg.target = this

Android中为什么主线程不会因为Looper.loop()里的死循环卡死?

事实上,会在进入死循环之前便创建了新binder线程,在代码ActivityThread.main()中:thread.attach(false);便会创建一个Binder线程(具体是指ApplicationThread,Binder的服务端,用于接收系统服务AMS发送来的事件),该Binder线程通过Handler将Message发送给主线程。对于主线程,我们是绝不希望会被运行一段时间,自己就退出,那么如何保证能一直存活呢?简单做法就是可执行代码是能一直执行下去的,死循环便能保证不会被退出,例如,binder线程也是采用死循环的方法,通过循环方式不同与Binder驱动进行读写操作,当然并非简单地死循环,无消息时会休眠。

主线程的死循环一直运行是不是特别消耗CPU资源呢? 其实不然,这里就涉及到Linux pipe/epoll机制**,简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。

链式存储结构和顺序存储结构

顺序表适宜于做查找这样的静态操作;链表宜于做插入、删除这样的动态操作。

若线性表的长度变化不大,且其主要操作是查找,则采用顺序表;

若线性表的长度变化较大,且其主要操作是插入、删除操作,则采用链表。

顺序表平均需要移动近一半元素

链表不需要移动元素,只需要修改指针

MVP(要特别熟悉,能说出优点)

任何事务都存在两面性,MVP当然也不列外,我们来看看MVP的优缺点。

优点:

1.降低耦合度,实现了Model和View真正的完全分离,可以修改View而不影响Modle

2.模块职责划分明显,层次清晰

3.Presenter可以复用,一个Presenter可以用于多个View,而不需要更改Presenter的逻辑(当然是在View的改动不影响业务逻辑的前提下)

4.利于测试驱动开发。以前的Android开发是难以进行单元测试的(虽然很多Android开发者都没有写过测试用例,但是随着项目变得越来越复杂,没有测试是很难保证软件质量的;而且近几年来Android上的测试框架已经有了长足的发展——开始写测试用例吧),在使用MVP的项目中Presenter对View是通过接口进行,在对Presenter进行不依赖UI环境的单元测试的时候。可以通过Mock一个View对象,这个对象只需要实现了View的接口即可。然后依赖注入到Presenter中,单元测试的时候就可以完整的测试Presenter应用逻辑的正确性。

5.View可以进行组件化。在MVP当中,View不依赖Model。这样就可以让View从特定的业务场景中脱离出来,可以说View可以做到对业务完全无知。它只需要提供一系列接口提供给上层操作。这样就可以做到高度可复用的View组件。

6.代码灵活性

缺点:

1.Presenter中除了应用逻辑以外,还有大量的View->Model,Model->View的手动同步逻辑,造成Presenter比较笨重,维护起来会比较困难。

2.由于对视图的渲染放在了Presenter中,所以视图和Presenter的交互会过于频繁。

3.如果Presenter过多地渲染了视图,往往会使得它与特定的视图的联系过于紧密。一旦视图需要变更,那么Presenter也需要变更了。

4.额外的代码复杂度及学习成本。

组件化方案和思想

组件化开发就是将一个app分成多个模块,每个模块都是一个组件(Module),开发的过程中我们可以让这些组件相互依赖或者单独调试部分组件等,但是最终发布的时候是将这些组件合并统一成一个apk,这就是组件化开发。

组件中提供common库:

1、我们将给给整个工程提供统一的依赖第三方库的入口,前面介绍的Common库的作用之一就是统一依赖开源库,因为其他业务组件都依赖了Common库,所以这些业务组件也就间接依赖了Common所依赖的开源库。

2、在Common组件中我们封装了项目中用到的各种Base类,这些基类中就有BaseApplication 类。

Gradle构建工具在组件的build.gradle中有如下用法:

//设置了resourcePrefix值后,所有的资源名必须以指定的字符串做前缀,否则会报错。

//但是resourcePrefix这个值只能限定xml里面的资源,并不能限定图片资源,所有图片资源仍然需要手动去修改资源名。

resourcePrefix "girls_"

做SDK类项目的要求

1.暴露的接口尽可能少,最大程度减少 SDK 接入方需要了解的细节。

2.license

3.调用尽可能简单

4.library,aar两种

aar和jar的区别

aar:.aar文件中包含所有资源,class以及res资源文件。

jar:只包含了class文件与清单文件 ,不包含资源文件,如图片等所有res中的文件。

如果你只是一个简单的类库那么使用生成的.jar文件即可;如果你的是一个UI库,包含一些自己写的控件布局文件以及字体等资源文件那么就只能使用*.aar文件。

性能优化(分类优化:UI,内存,cpu,电量,网络)

ui优化:约束布局

内存优化:上下文,handler,线程,单例的,图片,native,java(不保留活动 java堆内存)

cpu优化:看电话热不热

电量优化: 大的东西一直不放

网络优化:gip压缩,图片webp,策略

上一篇:2018Android面试通关题,速收
下一篇:实用:Android开源项目地址分享

人工智能时代来了?我们应该如何应对?

android权限代码集合

andriod环境搭建教程

分享:嵌入式跨平台UI开发工具汇总

选择城市和中心
贵州省

广西省

海南省