1- Davik进程、linux进程、线程之间的区别?
Linux进程:
1. Linux进程,它有独立的内核堆栈和独立的存储空间,它是操作系统中资源分配和调度的最小单位。
2. Linux操作系统会以进程为单位,分配系统资源,给程序进行调度。
3. Linux操作系统在执行一个程序时,它会创建一个进程,来执行应用程序,并且伴随着资源的分配和释放。
Davik进程():
1. Dalvik虚拟机运行在Linux操作系统之上。
2. Davik进程就是Linux操作系统中的一个进程,属于linux进程
3. 每个Android应用程序进程都有一个Dalvik虚拟机实例。这样做得好处是Android应用程序进程之间不会互相影响,也就是说,一个Android应用程序进程的意外终止,不会影响到其他的应用程序进程的正常运行。
线程:
1. 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。
2. 线程自己基本上不拥有系统资源,在运行时,只需要必不可少的资源(如程序计数器,一组寄存器和栈)。
3. 线程与同属一个进程的其他的线程共享进程所拥有的全部资源。
三者之间的联系:
1. Davik进程就是Linux操作系统的一个进程。
2. 线程就是进程的一个实体,线程是进程的一部分。一个进程中可以提供多个线程执行控制。
进程和线程的区别:
1) 一个程序至少有一个进程,一个进程至少有一个线程.
2) 线程的划分尺度小于进程,使得多线程程序的并发性高。
3) 进程在执行过程中拥有独立的内存单元,而多个线程共享内存(同属一个进程),从而极大地提高了程序的运行效率。
4) 每个独立的进程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
5) 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。
2-Android 中进程与进程之间如何通信?
aidl机制进程间通信
AIDL: (Android Interface definition language的缩写)它是一种android内部进程通信接口的描述语言,通过它我们可以定义进程间的通信接口
AIDL进程间通讯的原理:
通过编写aidl文件来定义进程间通信接口。
编译后会自动生成响应的java文件
服务器将接口的具体实现写在Stub中,用iBinder对象传递给客户端,
客户端bindService的时候,用asInterface的形式将iBinder还原成接口,再调用其接口中的方法来实现通信。
请查看实例代码:
服务端代码
服务器代码
使用Messenger实现进程间通信
Messenger是基于AIDL实现的。
AIDL使服务器可以并行处理,而Messenger封装了AIDL之后只能串行运行,所以Messenger一般用作消息传递。
需要大家注意:
区别Messenger和Message。
Message是消息,承载了要传递的数据。
Messenger是信使,可以发送消息。并且Messenger对象可以通过getBinder方法获取一个Ibinder对象。
Messenger实现原理:
服务端(被动方)提供一个Service来处理客户端(主动方)连接,维护一个Handler来创建Messenger,在onBind时返回Messenger的binder。
双方用Messenger来发送数据,用Handler来处理数据。Messenger处理数据依靠Handler,所以是串行的,也就是说,Handler接到多个message时,就要排队依次处理。
使用Messenger实现进程间通信方法如下:
首先A应用提供一个Service,创建一个Messenger对象,在onBinder方法里返回messenger.getBinder()生成的IBinder对象;
然后在B应用绑定该Service,在ServiceConnection的onServiceConnected方法获取到IBinder对象;
最后在B应用使用获取到的binder对象构造出一个新的Messenger对象,使用该Messenger对象的send方法发送的Message数据,都将被Service里的Messenger对象handlerMessage方法接收到。
参考案例:
内容提供者ContentProvider实现进程间通信
系统四大组件之一,底层也是Binder实现,主要用来为其他APP提供数据。
自定义的ContentProvider为外界进程访问的时候,
需要在注册时要提供authorities属性,应用需要访问的时候将属性包装成Uri.parse("content://authorities")。
然后实现:
ContentProvider的中query,delete,insert等相关方法,进行数据的操作。
实例代码查看:
与及时通讯相关试题
3-简单阐述一下对XMPP协议理解以及优缺点?
定义:
(可扩展消息处理现场协议)是基于可扩展标记语言(XML)的协议,它用于即时消息(IM)以及在线现场探测。它在促进服务器之间的准即时操作。这个协议可能最终允许因特网用户向因特网上的其他任何人发送即时消息,即使其操作系统和浏览器不同。
数据格式:
在XMPP中,各项工作都是通过在一个XMPP流上发送和接收XMPP来完成的。
核心XMPP工具集由三种基本节组成,这三种节分别为<presence>、出席<message>、<iq>。
XMPP流由两份XML文档组成,通信的每个方向均有一份文档。
这份文档有一个根元素<stream:stream>
<?xmlversion='1.0'?>
<stream:stream
to='example_com'
xmlns='jabber:client'
xmlns:stream='http_etherx_jabber_org/streams'
version='1.0'>
<messagefrom='juliet_example_com' to='romeo_example_net' xml:lang='zh-cn'>
<body>Art thou not Romeo, and a Montague?</body>
</message>
</stream:stream>
XMPP通信原理?
先建立一个stream,然后协商一堆安全之类的东西,中间通信过程就是客户端发送XML Stanza,一个接一个的。
服务器根据客户端发送的信息以及程序的逻辑,发送XML Stanza给客户端。
但是这个过程并不是一问一答的,任何时候都有可能从一方发信给另外一方。
通信的最后阶段是</stream>关闭流,关闭TCP/IP连接。
优势和缺陷
优势:
与HTTP相比,XMPP具有如下的优势:
(1)能够“推送”数据。
(2)自由、开放、公开的,并且易于了解。而且在客户端、服务器、组件、源码库等方面,都已经各自有多种实现
(3)牢固的身份验证和安全机制(内部使用SASL(Simple Authentication and Security Layer)技术,是通信更加可靠和安全性,已内置于核心XMPP技术规格中)
SASL(Simple Authentication and Security Layer): 是一种用来扩充C/S模式验证能力的机制, 判断用户是否有权使用转发服务
(4)为许多不同的问题提供大量即开即用的工具(smack asmack 开源库)
缺陷:
每种协议都有各自的优缺点,在许多场合中XMPP并不是完成任务的最佳工具或受到某种限制。
(1)有状态协议
指下一次传输数据的时候能考虑到这次传输信息的状态。
也有是说有状态协议以前的请求导致协议所处的状态会影响后续的状态,协议会根据上一个状态创建上下文
(2)社区和部署不及HTTP广泛
(3)对于简单的请求,其开销比HTTP大
(4)仍然需要专门的实现
4-简单阐述一下及时推送原理?
a) 传统获取服务器数据使用的是pull模式,是客户端向服务器请求数据。从客户端发起连接请求,获取到服务器数据后就关闭连接。当连接断开后,服务器就会失去客户端的地址,因此无法主动向客户端发送消息。
b) 推送(push)是服务主动向客户端发送数据。
它的原理是保持一个长连接,当客户端和服务器建立连接后不再断开,这样服务器随时有新消息都可以发送给客户端。
c) 至于如何获取推送消息。由于服务端传来推送消息的时间是不确定的,这里只能等待推送SDK的回调,比如通过注册监听或者广播接收者。不同的厂商的推送SDK可能会有不同的处理方案,以百度推送SDK来说,是通过广播接收者获取推送数据。
5-简要说明一下openfire、smack、spark
openfire是基于XMPP协议的即时通信的服务器端的一个实现,你不需要编写一行服务端的代码,实现一个简单的点对点通信或是简单的群聊.
smack 是XMPP传输协议的Java实现,提供了一套API接口 可以连接服务端。一般用于快速开发手机客户端。
spark是基于smack实现的一个XMPP即时通信客户端(PC端的)。
6-列出几种常见的解决消息即时获取方案
1. 轮询(Pull)方式:客户端定时向服务器发送询问消息,一旦服务器有变化则立即同步消息。
2. SMS(短信消息)(Push)方式:通过拦截SMS消息并且解析消息内容来了解服务器的命令,但这种方式一般用户在经济上很难承受。
3. 持久连接(Push)方式:客户端和服务器之间建立长久连接,这样就可以实现消息的及时行和实时性。
7-Timer比AlarmManager实现心跳时更耗电原因
一. Timer
Android 的 Timer 类可以用来计划需要循环执行的任务。
Timer 的问题是它需要用 WakeLock 让 CPU 保持唤醒状态,这样会大量消耗手机电量,
大大减短手机待机时间。这种方式不能满足我们的需求。
二. AlarmManager
AlarmManager 是 Android 系统封装的用于管理 RTC 的模块,RTC (Real Time Clock) 是一个独立的硬件时钟,可以在 CPU 休眠时正常运行,
在预设的时间到达时,通过中断唤醒 CPU。
这意味着,如果我们用 AlarmManager 来定时执行任务,CPU 可以正常的休眠,只有在需要运行任务时醒来一段很短的时间。
与性能优化相关试题
8-对于Android 的安全问题,你知道多少
1. 日志信息的输出
2. 参数校验不严
3. WebView引入各种安全问题,webview中的js注入
4. 不混淆、不防二次打包
5. 明文存储关键信息
6. 错误使用HTTPS
7. 山寨加密方法
8. 滥用权限、内存泄露、使用debug签名
9-内存泄漏和内存溢出分别是什么?它们有什么关系?
1. 内存泄露是指保存了不可能再被访问的变量引用,导致垃圾回收器无法回收内存。
也就是说:
在Java中有些对象的生命周期是有限的,当它们完成了特定的逻辑后将会被垃圾回收;但是,如果在对象的生命周期本来该被垃圾回收时这个对象还被别的对象所持有引用,那就会导致内存泄漏
2. 内存溢出是指虚拟机内存耗尽,无法为新对象分配内存,导致引用崩溃。典型的情况为加载多张大图,导致内存耗尽。
3. 当某个界面存在内存泄露,反复进入该界面,将导致一直有新对象创建但是无法回收,最终内存耗尽,产生内存溢出。
10-什么情况下会导致内存泄漏
1. 资源释放问题
程序代码的问题,长期保持某些资源,如Context、Cursor、IO流的引用,资源得不到释放造成内存泄露。
2. 对象内存过大问题
保存了多个耗用内存过大的对象(如Bitmap、XML文件),造成内存超出限制。
3. static关键字的使用问题
static是Java中的一个关键字,当用它来修饰成员变量时,那么该变量就属于该类,而不是该类的实例。所以用static修饰的变量,它的生命周期是很长的,如果用它来引用一些资源耗费过多的实例(Context的情况最多),这时就要谨慎对待了。
针对static的解决方案:
1) 应该尽量避免static成员变量引用资源耗费过多的实例,比如Context。
2) Context尽量使用ApplicationContext,因为Application的Context的生命周期比较长,引用它不会出现内存泄露的问题。
3) 使用WeakReference代替强引用。比如可以使用WeakReference<Context> mContextRef;
4. 线程导致内存溢出
线程产生内存泄露的主要原因在于线程生命周期的不可控。
针对这种线程导致的内存泄露问题的解决方案:
l 将线程的内部类,改为静态内部类(因为非静态内部类拥有外部类对象的强引用,而静态类则不拥有)。
l 在线程内部采用弱引用保存Context引用。
5. 查询数据库没有关闭cursor
程序中经常会进行查询数据库的操作,但是经常会有使用完毕Cursor后没有关闭的情况。如果我们的查询结果集比较小,对内存的消耗不容易被发现,只有在常时间大量操作的情况下才会出现内存问题,这样就会给以后的测试和问题排查带来困难和风险。
6. 构造Adapter没有复用convertview
在使用ListView的时候通常会使用Adapter,那么我们应该尽可能的使用ConvertView。
为什么要复用convertView?
当convertView为空时,用setTag()方法为每个View绑定一个存放控件的ViewHolder对象。当convertView不为空,重复利用已经创建的view的时候,使用getTag()方法获取绑定的ViewHolder对象,这样就避免了findViewById对控件的层层查询,而是快速定位到控件。
7. Bitmap不再使用时没有调用recycle()释放内存
有时我们会手工的操作Bitmap对象,如果一个Bitmap对象比较占内存,当它不再被使用的时候,可以调用Bitmap.recycle()方法回收此对象的像素所占用的内存,但这不是必须的,视情况而定。
11-说一下如何对Android内存优化?
Bitmap OOM(图片优化)
1. 图片处理
a. 等比缩放
以上代码可以优化内存溢出,但它只是改变图片大小,并不能彻底解决内存溢出。
b. 对图片采用软引用,及时地进行recyle()操作
2. 图片的缓存
a) 网络缓存
b) 内存缓存
c) 磁盘缓存
3. 使用加载图片框架处理图片,如专业处理加载图片的ImageLoader,glide,picasso等图片加载框架。
ListView优化
1. Item布局,层级越少越好,使用hierarchyview工具查看优化。
2. 复用convertView
3. 使用ViewHolder
4. item中有图片时,异步加载
5. 快速滑动时,不加载图片
6. item中有图片时,应对图片进行适当压缩
7. 实现数据的分页加载
UI Review(视图检查)
减少视图层级
减少视图层级可以有效的减少内存消耗,因为视图是一个树形结构,每次刷新和渲染都会遍历一次。
ViewStub标签
此标签可以使UI在特殊情况下,直观效果类似于设置View的不可见性,但是其更大的意义在于被这个标签所包裹的Views在默认状态下不会占用任何内存空间。
include标签
可以通过这个标签直接加载外部的xml到当前结构中,是复用UI资源的常用标签。
merge标签
它在优化UI结构时起到很重要的作用。目的是通过删减多余或者额外的层级,从而优化整个Android Layout的结构。
使用ThreadPool而不是每次new Thread
减少不必要的全局变量
尽量避免static成员变量引用资源耗费过多的实例,比如Context。
因为Context的引用超过它本身的生命周期,会导致Context泄漏。所以尽量使用Application这种Context类型。
你可以通过调用Context.getApplicationContext()或 Activity.getApplication()轻松得到Application对象。
Cursor(游标)回收
Cursor是Android查询数据后得到的一个管理数据集合的类,在使用结束以后。应该保证Cursor占用的内存被及时的释放掉,而不是等待GC来处理。并且Android明显是倾向于编程者手动的将Cursor close掉,因为在源代码中我们发现,如果等到垃圾回收器来回收时,会给用户以错误提示。
Receiver(接收器)回收
调用registerReceiver()后未调用unregisterReceiver().
当我们Activity中使用了registerReceiver()方法注册了BroadcastReceiver,一定要在Activity的生命周期内调用unregisterReceiver()方法取消注册
也就是说registerReceiver()和unregisterReceiver()方法一定要成对出现,通常我们可以重写Activity的onDestory()方法,在onDestory里进行unregisterReceiver操作
Stream/File(流/文件)回收
主要针对各种流,文件资源等等如:
InputStream/OutputStream,SQLiteOpenHelper,SQLiteDatabase,Cursor,文件,I/O,Bitmap图片等操作等都应该记得显示关闭。
避免创建不必要的对象
最常见的例子就是当你要频繁操作一个字符串时,使用StringBuffer代替String。
还比如:使用int数组而不是Integer数组。
避免创建短命的临时对象,减少对象的创建就能减少垃圾收集,进而减少对用户体验的影响。
避免内部Getters/Setters
在Android中,虚方法调用的代价比直接字段访问高昂许多。通常根据面向对象语言的实践,在公共接口中使用Getters和Setters是有道理的,但在一个字段经常被访问的类中宜采用直接访问。
for循环
访问成员变量比访问本地变量慢得多,如下面一段代码:
for(int i =0; i < this.mCount; i++) {}
永远不要在for的第二个条件中调用任何方法,如下面一段代码:
for(int i =0; i < this.getCount(); i++) {}
对上面两个例子最好改为:
int count = this.mCount; / int count = this.getCount();
for(int i =0; i < count; i++) {}
12-如何对android应用进行内存性能分析
Android应用UI性能分析
在使用App时会发现有些界面启动卡顿、动画不流畅、列表等滑动时也会卡顿出现这种情况,可以考虑对UI性能分析。
首先要清楚卡顿的原因,有以下几种情况:
l 人为在UI线程中做轻微耗时操作,导致UI线程卡顿;
l 布局Layout过于复杂,无法在16ms内完成渲染;
l 同一时间动画执行的次数过多,导致CPU或GPU负载过重;
l View过度绘制,导致某些像素在同一帧时间内被绘制多次,从而使CPU或GPU负载过重;
l View频繁的触发measure、layout,导致measure、layout累计耗时过多及整个View频繁的重新渲染;
l 内存频繁触发GC过多(同一帧中频繁创建内存),导致暂时阻塞渲染操作;
l 冗余资源及逻辑等导致加载和执行缓慢;
l 臭名昭著的ANR;
如何分析?
分析UI卡顿我们一般都借助工具,通过工具一般都可以直观的分析出问题原因,从而反推寻求优化方案,具体如下细说各种强大的工具
使用HierarchyViewer分析UI性能
我们可以通过SDK提供的工具HierarchyViewer来进行UI布局复杂程度及冗余等分析
通过命令启动HierarchyViewer
接下来Hierarchy window窗口打开:
一个Activity的View树,通过这个树可以分析出View嵌套的冗余层级,以及每个View在绘制的使用时长也有表示。
使用Lint进行资源及冗余UI布局等优化
冗余资源及逻辑等也可能会导致加载和执行缓慢,这可以使用Link工具,来发现优化这些问题的
在Android Studio 1.4版本中使用Lint最简单的办法:
就是将鼠标放在代码区点击右键->Analyze->Inspect Code–>界面选择你要检测的模块->点击确认开始检测,等待一下后会发现如下结果:
如果存在冗余的UI层级嵌套,会进行高亮显示, 我们根据提示可以点击跳进去进行优化处理掉的。
使用Memory监测及GC打印与Allocation Tracker进行UI卡顿分析
由于Android系统会依据内存中不同的内存数据类型分别执行不同的GC操作,常见应用开发中导致GC频繁执行的原因主要可能是因为短时间内有大量频繁的对象创建与释放操作,也就是俗称的内存抖动现象,或者短时间内已经存在大量内存暂用介于阈值边缘,接着每当有新对象创建时都会导致超越阈值触发GC操作
如何查看?
Android Studio 工具提供内存查看器:
根据内存抖动现象,查看log日志进行分析:
如何看到,这种不停的大面积打印GC导致所有线程暂停的操作必定会导致UI视觉的卡顿,所以我们要避免此类问题的出现,具体的常见优化方式如下:
l 检查代码,尽量避免有些频繁触发的逻辑方法中存在大量对象分配;
l 尽量避免在多次for循环中频繁分配对象;
l 避免在自定义View的onDraw()方法中执行复杂的操作及创建对象(譬如Paint的实例化操作不要写在onDraw()方法中等);
l 对于并发下载等类似逻辑的实现尽量避免多次创建线程对象,而是交给线程池处理。
有了上面说明GC导致的性能后我们就该定位分析问题了,
我们可以通过运行DDMS->Allocation Tracker标签打开一个新窗口,然后点击Start Tracing按钮,接着运行你想分析的代码,运行完毕后点击Get Allocations按钮就能够看见一个已分配对象的列表,如下:
根据内存分配情况,进行优化。
Android应用内存性能分析
如何查看项目中内存泄漏?
AS的Memory窗口如下,详细的说明这里就不解释了,很简单很直观(使用频率高):
DDMS-Heap内存监测工具窗口如下,详细的说明这里就不解释了,很简单(使用频率不高):
dumpsys meminfo命令如下(使用频率非常高,非常高效,注意:adb shell dumpsys meminfo不跟参数直接展示系统所有内存状态):
Android应用内存泄露leakcanary工具定位分析
leakcanary是一个开源项目,一个内存泄露自动检测工具,是著名的GitHub开源组织Square贡献的,它的主要优势就在于自动化过早的发觉内存泄露。
关于leakcanary工具的配置使用方式这里不再详细介绍,可以参考官方说明使用:
https://github.com/square/leakcanary
Android应用内存泄露MAT工具定位分析
Eclipse Memory Analysis Tools是一个专门分析Java堆数据内存引用的工具,我们可以使用它方便的定位内存泄露原因,核心任务就是找到GC ROOT位置即可
Android应用代码逻辑性能分析
在我们开发中除过常规的那些经典UI、内存性能问题外其实还存在很多潜在的性能优化、这种优化不是十分明显,但是在某些场景下却是非常有必要的,所以我们简单列举一些常见的其他潜在性能优化技巧,具体如下探讨
Android应用String/StringBuilder/StringBuffer优化建议
Android应用HashMap与ArrayMap及SparseArray优化建议
在Android开发中涉及到数据逻辑部分大部分用的都是Java的API(譬如HashMap),但是对于Android设备来说有些Java的API并不适合,可能会导致系统性能下降,好在Google团队已经意识到这些问题,所以他们针对Android设备对Java的一些API进行了优化,优化最多就是使用了ArrayMap及SparseArray替代HashMap来获得性能提升。
HashMap:
HashMap内部使用一个默认容量为16的数组来存储数据,数组中每一个元素存放一个链表的头结点,其实整个HashMap内部结构就是一个哈希表的拉链结构。HashMap默认实现的扩容是以2倍增加,且获取一个节点采用了遍历法,所以相对来说无论从内存消耗还是节点查找上都是十分昂贵的。
SparseArray:
SparseArray比HashMap省内存是因为它避免了对Key进行自动装箱(int转Integer),它内部是用两个数组来进行数据存储的(一个存Key,一个存Value),它内部对数据采用了压缩方式来表示稀疏数组数据,从而节约内存空间,而且其查找节点的实现采用了二分法,很明显可以看见性能的提升。
ArrayMap:
ArrayMap内部使用两个数组进行数据存储,一个记录Key的Hash值,一个记录Value值,它和SparseArray类似,也会在查找时对Key采用二分法。
有了上面的基本了解我们可以得出结论供开发时参考,当数据量不大(千位级内)且Key为int类型时使用SparseArray替换HashMap效率高;
当数据量不大(千位级内)且数据类型为Map类型时使用ArrayMap替换HashMap效率高;其他情况下HashMap效率相对高于二者。
Android应用ContentProviderOperation优化建议
ContentProvider是Android应用开发的核心组件之一,有时候在开发中需要使用ContentProvider对多行数据进行操作,我们的做法一般是多次调运相关操作方法,殊不知这种实现方式是非常低性能的,取而代之的做法应该是使用批量操作,具体为了使批量更新、插入、删除数据操作更加方便官方提供了ContentProviderOperation工具类。所以在我们开发中遇到类似情景时请务必使用批量操作,具体的优势如下:
l 所有的操作都在一个事务中执行,可以保证数据的完整性。
l 批量操作在一个事务中执行,所以只用打开、关闭一个事务。
l 减轻应用程序与ContentProvider间的多次频繁交互,提升性能。
除了以上建议,还有一些其他建议:
避免在Android中使用Java的枚举类型,因为编译后不但占空间,加载也费时,完全没有static final的变量好用、高效。
Handler发送消息时尽量使用obtain去获取已经存在的Message对象进行复用,而不是新new Message对象,这样可以减轻内存压力。
在使用后台Service时尽量将能够替换为IntentService的地方替换为此,这样可以减轻系统压力、省电、省内存、省CPU占用率。
在当前类内部尽量不要通过自己的getXXX、setXXX对自己内部成员进行操作,而是直接使用,这样可以提高代码执行效率。
尽量减少锁个数、减小锁范围,避免造成性能问题。
合理的选择使用for循环与增强型for循环,譬如不要在ArrayList上使用增强型for循环等。
与开源框架相关的试题
13-开发中常用的一些开源框架和平台有哪些?
EventBus(事件处理)
JPush(推送平台)
友盟(统计平台)
有米(优米)(广告平台)
百度地图
bmob(服务器平台、短信验证、邮箱验证、第三方支付)
ShareSDK(分享平台、第三方登录)
Gson(解析json数据框架)
imageLoader (图片处理框架)
zxing (二维码扫描)
OKHttp
Retrofit
GreedDao
Volley
EventBus
14-说一下Picasso ImageLoader Fresco Glide优劣
Fresco:
优点:
1. 图片存储在安卓系统的匿名共享内存, 而不是虚拟机的堆内存中, 图片的中间缓冲数据也存放在本地堆内存, 所以, 应用程序有更多的内存使用, 不会因为图片加载而导致oom, 同时也减少垃圾回收器频繁调用回收Bitmap导致的界面卡顿, 性能更高.
2. 渐进式加载JPEG图片, 支持图片从模糊到清晰加载
3. 图片可以以任意的中心点显示在ImageView, 而不仅仅是图片的中心.
4. JPEG图片改变大小也是在native进行的, 不是在虚拟机的堆内存, 同样减少OOM
5. 很好的支持GIF图片的显示
缺点:
1. 框架较大, 影响Apk体积
2. 使用较繁琐
ImageLoader, Picasso, Glide: 这三者实现机制都差不多
ImageLoader:
比较老的框架, 稳定, 加载速度适中, 缺点在于不支持GIF图片加载, 使用稍微繁琐, 并且缓存机制没有和http的缓存很好的结合, 完全是自己的一套缓存机制.
Picasso:
使用方便, 一行代码完成加载图片并显示, 框架体积小,
缺点在于不支持GIF, 并且它可能是想让服务器去处理图片的缩放, 它缓存的图片是未缩放的, 并且默认使用ARGB_8888格式缓存图片, 缓存体积大.
Glide:
可以说是Picasso的升级版, 有Picasso的优点, 并且支持GIF图片加载显示, 图片缓存也会自动缩放, 默认使用RGB_565格式缓存图片, 是Picasso缓存体积的一半.
15-volley、okHttp之间的区别和优缺点
Volley是在2013年Google I/O大会上推出了一个新的网络通信框架。
它支持并发网络连接,支持标准的HTTP缓存协议,同时也支持取消单个或多个请求。
并且易于定制,扩展性比较强。在提供强大的网络请求能力下,还可以轻松地发送异步请求来填充UI数据。
Volley框架拥有请求任务队列管理,适合小而频繁的请求。对于请求大数据,比如下载文件,Volley不太适合。
Okhttp:
在6.0版本中,google公司移除了HttpClient的API,使用okhttp来代替。
Okhttp是一个高效的HTTP网络框架,它支持SPDY ,可以合并多个到同一个主机的请求,使用连接池技术减少请求的延迟
无缝的支持GZIP来减少数据流量,缓存响应数据来减少重复的网络请求。
除了以上功能:
OKHttp还弥补了Volley一些不足,比如,Volley默认不支持文件的上传。
而okhttp提供了多文件上传功能。
缺点:消息回来需要切到主线程,主线程要自己去写,第二传入调用比较复杂
16-说一下Volley框架的实现原理以及优缺点?
首先说一下Volley的特点:
l 自动调度网络请求
l 支持并发网络连接
l 支持标准的HTTP缓存协议(由服务器来决定是否缓存数据)
l 支持请求优先级设置(4级)
l 支持取消单个或多个请求
l 易于定制,扩展性强。比如Retry&Backoff机制
l 强大的网络请求能力让你轻松的发送异步请求来填充UI数据
l 提供调试和跟踪工具
Volley的原理:
当添加Request到RequestQueue的时候,请求先被缓存线程处理和鉴别:
如果请求在缓存中有,则将缓存的response响应在缓存线程中进行解析并将解析后的响应分发给主线程。
如果缓存中没有这个Request请求,则将这个Request放到网络队列中。
第一个可用的网络线程将Request请求从队列中取出来,执行HTTP传输,并在工作线程中解析响应,将响应写进缓存,并将解析好的响应内容传给主线程进行分发。
Volley将像IO、解析等耗时操作都放到了工作线程(子线程)来执行。你可以在任何线程去添加请求,Volley会将响应直接在主线程去进行分发。
Volley的优缺点:
优点:
Volley的优点在于请求队列的管理, 适合小而频繁的请求, 如果app比较小, 网络请求要求不高的情况下可以使用volley, 通常情况下是要结合其他框架一起来使用, 比如volley+okhttp.
缺点: 下载大文件性能不佳, 不支持大文件上传
与应用发布相关的试题
17-根据自己的理解描述下Android数字签名。
1. 所有的应用程序都必须有数字证书,Android系统不会安装一个没有数字证书的应用程序
2. Android程序包使用的数字证书可以是自签名的,不需要一个权威的数字证书机构签名认证
3. 如果要正式发布一个Android ,必须使用一个合适的私钥生成的数字证书来给程序签名,而不能使用adt插件或者ant工具生成的调试证书来发布。
4. 数字证书都是有有效期的,Android只是在应用程序安装的时候才会检查证书的有效期。如果程序已经安装在系统中,即使证书过期也不会影响程序的正常功能
18-如何缩减APK包大小
开启minifyEnabled
开启混淆,删除没用的java文件
开启shrinkResources
去除无用资源
resConfigs "zh"
删除无用的语言资源
使用tinypng有损压缩
TinyPNG 使用一种智能有损压缩技术(通过降低图片中的颜色数量,来减少存储图片所需要的数据)来降低 PNG 图片的大小
TinyPNG 将 PNG 图片压缩成 8 位的 PNG(而不是24位),所以它的压缩比例非常高
对于非透明的大图,png转换为jpg格式
格式工厂进行转换
使用webp格式
定义
一种支持有损压缩和无损压缩的图片文件格式
兼容和使用
从Android 4.0+开始原生支持,但是不支持包含透明度,直到Android 4.2.1+才支持显示含透明度的webp
特点
根据 Google 的测试,无损压缩后的 WebP 比 PNG 文件少了 45% 的文件大小,即使这些 PNG 文件经过其他压缩工具压缩之后,WebP 还是可以减少 28% 的文件大小
优势
PNG 转 WebP 的压缩率要高于 PNG 原图压缩率,同样支持有损与无损压缩
转换后的 WebP 体积大幅减少,图片质量也得到保障(同时肉眼几乎无法看出差异)
转换后的 WebP 支持 Alpha 透明和 24-bit 颜色数,不存在 PNG8 色彩不够丰富和在浏览器中可能会出现毛边的问题
使用shape文件替换图片
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"
>
<corners android:radius="10dp"></corners>
<solid android:color="#e6d5d5"></solid>
</shape>
切图使用一套图即可
适配主流的分辨率1280x720
19-如何实现多渠道打包
由于apk程序需要发布到不同市场,针对不同市场,做下载数量的统计,或者针对不同的市场的apk,推送消息和广告。
这时候,需要给每个apk,做一个标记,而这个标记就是区分不同市场。
因此:多渠道打包就是给发布到不同市场的apk,打个标记(也就是渠道号),用于统计或者针对性的推送消息和广告
如何实现多渠道打包?
这里我们使用android studio提供Gradle构建工具实现多渠道打包:
1. 配置项目目录下的build.gradle文件
a. 配置签名文件:
1. 签名文件的生产: build – Generate Sigend APK
2. 在build.gradle文件中,添加签名文件配置
通过图形化界面配置完成以后,会在build.gradle文件生成配置信息:
b. 在build.gradle文件中的buildTypes节点下 – realease节点下 - 引用签名的配置
c. 添加productFlavors,声明渠道号
d. 配置 替换清单文件中meta-data节点中CHANNEL_VALUE占位符
2.在清单文件里配置meta-data标签
3.执行命令,生成多个不同渠道的apk
4.执行结果:
20-Apk混淆时,需要注意哪些问题?
* 类型转换错误,使用Gson之类的需要注意
-keepattributes Signature
* 忽略警告
-ignorewarnings
* webview中js和本地代码有交互,保持`桥梁类`不被混淆
-keepclassmembers class com.xxx.xxxx.xxxx.HuodongBeBindedToJS
{
public *;
}
* 保持注解内容,不被混淆
-keepattributes *Annotation*
* 保持反射调用的java类与方法,不要混淆
-dontwarn com.alibaba.fastjson.**
-keep class com.alibaba.fastjson.** { *;}
* 第三方包不被混淆
-libraryjars /libs/core.jar
-dontwarn com.google.zxing.**
-keep class com.google.zxing.** { *;}
* 常见第三方包
#zxing
-libraryjars /libs/core.jar
-dontwarn com.google.zxing.**
-keep class com.google.zxing.** { *;}
#支付宝
-libraryjars /libs/alipay_sdk.jar
-dontwarn com.alipay.android.app.**
-keep class com.alipay.android.app.** {*;}
#友盟
-libraryjars /libs/umeng_sdk.jar
-dontwarn com.umeng.**
-keep class com.umeng.** { *;}
-keepclassmembers class * { public <init>(org.json.JSONObject); }
-keep public class com.hyx.maizuo.main.R$*{ public static final int *; }
-keep public class com.umeng.fb.ui.ThreadView { }
#百度地图
-libraryjars /libs/baidumapapi.jar
-dontwarn com.baidu.mapapi.**
-keep class com.baidu.mapapi.** { *;}
#fastJson
-libraryjars /libs/fastjson-1.1.20.jar
-dontwarn com.alibaba.fastjson.**
-keep class com.alibaba.fastjson.** { *;}
#微信
-libraryjars /libs/libammsdk.jar
-dontwarn com.tencent.mm.sdk.**
-keep class com.tencent.mm.sdk.** {*;}
与工作中开发有关的试题
21-工作中如何解决bug的?
l 异常附近多打印log信息
l 分析log日志,断点调试
l 调试不出结果,上Stack Overflow贴上异常信息,进行解决
l 根据异常信息,从源代码中查找问题
22-介绍一下项目的整体架构
1. 从层次设计结构角度说明:
我们使用mvp这种层级结构进行划分:
Mvp的介绍:
随着UI创建技术的功能日益增强,UI层也履行着越来越多的职责。为了更好地细分视图(View)与模型(Model)的功能,让View专注于处理数据的可视化以及与用户的交互,同时让Model只关系数据的处理,基于MVC概念的MVP(Model-View-Presenter)模式应运而生。
在MVP模式里通常包含4个要素:
(1)View:负责绘制UI元素、与用户进行交互(在Android中体现为Activity);
(2)View interface:需要View实现的接口,View通过View interface与Presenter进行交互,降低耦合,方便进行单元测试;
(3)Model:负责存储、检索、操纵数据(有时也实现一个Model interface用来降低耦合);
(4)Presenter:作为View与Model交互的中间纽带,处理与用户交互的负责逻辑。
Mvp这种模式使模块与模块之间降低耦合度,模块职责划分明显,实现了代码复用机制以及结构更灵活
2. 从代码设计结构角度说明:
代码结构我们使用常见的模板方式设计模式,抽取一些通用的基类:
比如:BaseActivity, BaseFragment, BaseAdapter,BaseDao,BasePresenter等
通过这种方式抽取,使我们的代码更加简洁,便于维和。
除了以上的设计,我们这里使用了:
单例模式:减少对象的创建,避免了内存的不必要的开销
工厂模式:用于隐藏多个Fragment的创建过程,使代码更加灵活和简洁
建造者模式:封装了一些辅助工具类。将复杂的创建,放在内部,外部只关注业务逻辑的实现。
3. 从整体UI结构角度说明:
UI结构我们采用了市面比较流程的设计结构
就是底部导航 + ViewPager + Fragments 来实现。
这里面我们并没有使用大量的Activity,而是使用Fragment来代替多个Activity。
因为当业务逻辑或者功能模块比较多时,如果使用大量的Activity,会带来内存泄漏,
从而导致内存开销比较大。这里就使用了ViewPager + 多个Fragments来代替Actiivty。
Fragment在加载数据时,可以对Fragment的数据进行缓存,这样就避免的内存的开销,
同时,界面加载速度很快,从而提高了用户体验。
4. 从第三方开源框架角度说明:
首先我们这里为什么选择比较好的开源框架。
良好的一个开源框架,它能够帮助我们快速实现某些功能,这样不但提高我们的开发效率,从而使我们的
应用程序更加健壮。我们都采取哪些开源框架呢?
网络请求框架:我们使用了okhttp
图片加载框架:我们采用了glide
数据库框架我们采用了Greendao
同时我们也引入了Dragger,通过依赖注入的形式,降低代码之间的耦合度
以及EventBus,来简化了线程模型,是线程间通信更加简单和直接。
23-在android开发中,用户登录时,客户端会接收到token值,请描述一下对token的理解?
Token的引入:
Token是在客户端频繁向服务端请求数据,服务端频繁的去数据库查询用户名和密码并进行对比,判断用户名和密码正确与否,并作出相应提示,在这样的背景下,Token便应运而生。
Token的定义:
Token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。
使用Token的目的:
Token的目的是为了验证用户登录情况以及减轻服务器的压力,减少频繁的查询数据库,使服务器更加健壮。
Token的应用:
1. 当用户首次登录成功之后, 服务器端就会生成一个 token 值,这个值,会在服务器保存token值(保存在数据库中),再将这个token值返回给客户端.
2. 客户端拿到 token 值之后,使用sp进行保存。
3. 以后客户端再次发送网络请求(一般不是登录请求)的时候,就会将这个 token 值附带到参数中发送给服务器.
4. 服务器接收到客户端的请求之后,会取出token值与保存在本地(数据库)中的token值做对比!
5. 如果两个 token 值相同, 说明用户登录成功过!当前用户处于登录状态!
6. 如果没有这个 token 值, 没有登录成功.
7. 如果 token 值不同: 说明原来的登录信息已经失效,让用户重新登录.
24-说一下第三方支付流程以及集成流程?
集成流程:
签约流程:
注册支付宝账号
进行实名认证
提交审核资料
审核通过
备注:申请通过后会获得:合作者身份ID(PID),该ID在项目配置中需要用到
开发流程:
第一步:
下载API开发文档后,即可获取官方Demo,该Demo中需要将审核通过后获取的PID替换,并且输入支付宝收款账户即可。这里非常简单,就不过多叙述。
第二步:
官方Api开发文档中,存在一个openssl的文件夹,该文件夹主要是用于生成支付宝所需要用到的公钥以及私钥。打开该文件夹可以看到详细的生成方式,根据提示生成公钥及私钥,请注意,密钥需要经过pkcs8二次加密。
第三步:
将生成的公钥和私钥配置到Demo中。
第四步(可省略):
为了方便后期维护,建议将支付宝相关的方法及配置项抽取出来做为单独的一个类,后期需要使用直接调用即可
25-说一下第三方登录的实现?
一般大家经常讲的第三方登录只是一个概念,就是获得第三方平台的授权,而不是讲应用使用这种授权来注册用户完成登录的流程。
下面的是具体流程:
1、需要支持用户注册
2、需要在应用登录的时候提供第三方平台的图标
3、用户点击第三方平台图标以后,你们尝试判断用户是否已经授权
4、如果用户授权,获取他的唯一识别符,比方说WeiboDb里面的weiboId这个字段
5、如果用户没有授权,引导用户授权,授权成功后也可以获取weibo Id
6、然后用这个唯一识别符登录你们的系统,如果用户已经注册,则应该让用户登录到你们的系统,流程结束
7、如果你们的系统发现用户没有注册,引导用户进入你们应用的注册页面,并通过share sdk的showuser方法获取用户资料,自动帮助用户完成注册资料的填写,然后等待用户确认
8、如果用户确认了注册信息,你们的应用就根据他的信息完成这注册操作,如果操作成功,则应该让用户登录到你们的系统,流程结束