Android性能优化(高级)
-
简述Android的系统架构?
- android的系统架构从下往上分为Linux内核层,运行库,应用程序框架层和应用程序层。
-
如何对Android应用进行性能分析;
- 使用ddms工具中的traceview,heap,allocation tracker工具。traceview是Android平台特有的数据采集和分析工具,可以用来分析耗时操作等。heap视图工具可以帮助我们检查代码中是否存在有造成内存泄漏的地方,allocation tracker工具是内存分配跟踪工具。
-
什么情况下会导致内存泄露;
- Android的虚拟机是基于寄存器的Dalvik,它的最大堆内存是16M,部分机器是24M,因此能利用的内存空间是有限的,一旦超出就会造成OutOfMemory异常。
- 内存泄漏的几点原因:1)资源释放问题,程序代码的问题,长期保持某些资源,如context,cursor,io流的引用,资源得不到释放就造成内存泄漏。2)对象内存过大问题,保存了多个耗用内存过大的对象(如bitmap,xml文件),造成内存超出限制。3)static关键字的使用,static在Java中,当用来修饰成员变量的时候,那么该变量就属于该类,而不是该类的实例,因此用static修饰的变量,生命周期很长。针对static的解决方案:尽量避免static成员变量引用资源耗费过多的实例,比如context,context尽量使用ApplicationContext,因为Application的context的生命周期比较长,引用他不会出现内存泄漏的问题。4)线程导致内存泄漏,线程的生命周期不可控。
-
OOM是什么?应如何避免?
内存溢出,要避免OOM异常需要先知道是什么原因导致的异常,
-
图片过大导致OOM,在Android中用bitmap很容易导致内存溢出,比如报如下错误:
Java.lang.OutOfMemoryError : bitmap size exceeds VM budget。-
解决方法:
//1)等比例缩小图片 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)对图片采用弱引用,及时的进行recycle()操作。 SoftReference
bitmap = new SoftReference (pBitmap); if(bitmap != null){ if(bitmap.get() != null && !bitmap.get().isRecycled()){ bitmap.get().recycle(); bitmap = null; } } 3)使用图片加载框架处理图片。
-
-
界面切换的时候导致OOM,一般情况下,开发中都会禁止横屏,一旦横竖屏切换的多了,activity的生命周期就会重新销毁然后创建。切换的次数多了,就会导致OOM。这种问题没有固定的解决方案,可以从一下几方面着手分析,
-
1)看页面布局中有没有大的图片,如背景图之类的,去除xml文件中相关设置,改在代码中设置(放在onCreate()方法中)
Drawable drawable = getResources().getDrawable(R.drawable.id); ImageView imageView = new ImageView(this); imageView.setBackgroundDrawable(drawable); //在activity destory的时候注意,要drawable.setCallback(null),防止得不到及时的释放。
-
查新数据库时没有关闭游标。
构造adapter时,没有使用缓存convertView,使用convertView的好处:
当convertView 为空时,用setTag()方法为每个View 绑定一个存放控件的ViewHolder 对象。当convertView 不为空,重复利用已经创建的view 的时候,使用getTag()方法获取绑定的ViewHolder 对象,这样就避免了findViewById 对控件的层层查询,而是快速定位到控件。bitmap对象不再使用的时候调用recycle()方法释放内存。
使用广播没有注销的时候也会产生OOM。
-
ANR是什么? 应如何避免和解决?
- application not responding 程序无响应。ANR一般有三种类型: activity 5秒,broadcastReceiver10秒,services20秒。超时的原因一般有两种:1)当前的事件没有机会得到处理(UI线程正在处理前一个事件没有及时完成或者looper被某种原因阻塞住)2)当前的事件正在处理,但没有及时完成,UI线程尽量只做UI相关的工作,耗时操作(数据库操作,io流,连接网络或者其他可能阻碍UI线程的操作)放入单独的线程处理,尽量用handler来处理UI thread和thread之间的交互。
-
Android中线程间通信有那几种方式?
- 共享内存(变量),文件,数据库,handler,Java中wait(),notify(),notifyAll()。
-
线程和进程之间的区别?
- 一个应用程序至少有一个进程,一个进程至少有一条线程,一个线程可以创建和撤销另一个线程,同一个进程中的多个线程可以并发执行。从逻辑角度讲,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行,但操作系统并没有将多个线程看成多个独立的应用,来实现进程的调度,管理和资源分配。
android的屏幕适配 :
-
屏幕适配的方式有哪些?
- 适配方式有:权重weight适配,layout适配,代码使配,dp适配(layout中定义布局设置的参数使用dp),dimens适配。
- 在values-1280x720目录中的dimens。xml文件中定义同样的尺寸名称,但是使用不同的尺寸,当在布局文件中使用长或宽单位时,用@dimens/width来定义。如果在values-1280x720中放置了dimens常量,一定要记得将该常量的对应值在values目录中的dimens.xml中也放一份,因为该文件是默认配置,当用户的手机不是1280*720的时候,系统应用用的是默认values目录中的dimens.xml。
-
屏幕适配的处理技巧?
- 在分辨率大小不同的问题上,推荐使用权重weight适配,一般应用于线性布局中。
- 尽量使用线性布局,相对布局,如果屏幕放不下数据,可以使用ScrollView来拖动。
- 尽量使用9-patch图片,可以自动的根据图片上面显示的内容被拉伸和收缩。
AIDL
-
什么是ALDI?应如何使用?
- AIDL是android interface definition language 意思是Android接口定义语言。使用aidl可以帮助我们发布以及调用远程服务,实现跨进程通信。
-
AIDL如何工作,能处理哪些类型数据?
- 编译器可以通过aidl文件生成一段代码,通过预先定义的接口达到两个进程内部通信进程跨进程对象访问的目的,需要完成两件事:一是引入aidl的相关类;二是调用aidl产生的class文件,理论上,参数可以传递基本数据类型和String,还有就是Bundle的派生类。
android 的事件处理 :
handler消息机制:
Android中主线程主要是用来创建,更新UI的,而耗时操作,网络请求等则需要在子线程中操作。而handler主要接受子线程发送的数据,并用该数据来配合主线程更新UI,handler运行在主线程中,他和子线程通过message对象来传递数据(子线程通过sendMessage()方法来发送消息,数据),将子线程发送来的这些消息放入到主线程中,配合主线程来更新UI。
handleThread,Looper,MessageQueue和Message的关系:handlerThread负责将需要传递的信息封装成Message对象,通过handler对象的sendMesage()方法将消息传递给looper,在有looper将message放入messageQueue消息队列中,当looper对象看见MessageQueue中含有Message时,就将其广播出去,该handler对象手到该消息之后,调用相应的handler对象的handlerMessage()方法对其进行处理。
-
handler消息机制的底层简单分析:
- 首先是messageQueue和Looper的创建:当系统启动的时候,先加载activityThread这个类,在这个类中的main方法会调用Looper.prepareMainLooper()这个方法,来创建一个Looper对象,Looper对象就会通过ThreadLocal把他和当前线程绑定在一起,创建Looper的时候就创建了一个MessageQueue,MessageQueue是个final类型的成员变量。这样就保证了一个Looper对应一个MessageQueue,所有的MessageQueue创建好了之后,就会一个线程对应唯一的messageQueue。然后MessageQueue在创建的时候通过JNI创建了一个NativeMessageQueue,他又创建了一个c++Looper,所以说Handler的底层是通过C++来实现的。MessageQueue中保存了一个int类型的成员变量mptr,他保存了NativeMessageQueue的指针,这样就是了MessageQueue和NativeMessageQueue的对应。
- 接下来是从消息队列中取消息:之后就会调用Looper.loop()来取消息,这里面有一个死循环,在这个死循环里存在着queue.next();的方法他是阻塞主线程的,它实际上就是调用了NativeMessageQueue去C++层取消息,如果取出来这个loop就去执行,没取出来就会阻塞在这里不去执行,这样就能是界面能够停留而不至于代码执行完界面就跳出;取出消息后调用handler.dispatchMessage()来分发消息,message这里面有一个回调,handler在创建的时候有一个回调,如果两个回调都为空的那么直接调用handleMessage()来处理消息,这就是取消息。
- 最后就是存消息:也就是向消息队列中发送消息,不管是sendMessage还是sendMessageDelaty等他们调用都是sendMessageAtTime(),在这个方法里面他实际调用的是messqueue.enqueuemessage()这里面实际上就是拿着当前消息要执行的时间进行排序,然后就会通过消息队列保存起来,这里面有一个message.next()他们都可以通过这个方法一条指向下一条,这样一条一条连起来,当有消息需要马上执行就会调用nativewake(),这个方法就会把Looper.looper()的queue.next()唤醒就能取出消息,他就可以开始工作.
事件分发机制
-
OnTouchEvent的事件传递机制:
- 当手指触摸到屏幕时,系统会调用相应的view的onTouchEvent,传入一系列的action,那么首先触发的是activity的dispatchTouchEvent,然后触发activity的onUserInteraction,在触Layout的dispatchTouchEvent,然后触发Layout的onInterceptTouchEvent.
如果dispatchTouchEvent:
事件由此分发,通常会调用super. DispatchTouchEvent.
如果onInterceptTouchEvent(拦截事件):
返回true:表示事件被拦截会传递给自己的onTouchEvent处理,
返回false:不进行拦截,会将事件传递给下一个view的dispatchTouchEvent()进行判断.
如果onTouchEvent:
返回结果为true:表示事件由自己处理,消费.
返回结果为false:表示当前事件自己不做处理,交给父view的onTouchEvent处理.
view的绘制流程:
参考博文:http://www.jianshu.com/p/5a71014e7b1b
-
子线程发消息到主线程更新UI,处理handler,asyncTask还有什么;
-
用activity对象的runOnUiThread()在线程中更新UI。
new Thread() { @Override public void run() { super.run(); //这儿是耗时操作,完成之后更新ui. runOnUiThread(new Runnable() { @Override public void run() { //更新ui ImageView.setImageBitmap(bitmap); } }); } }.start(); //如果是非上下文类中(activity),可以通过传递上下文实现调用; //Activity activity = (Activity) imageView.getContext() ; activity.runOnUiThread(new Runable() { @Override public void run() { imageView.setImageBitmap(bitmap); } });
-
-
子线程中能不能new handler,为什么?
- 不能,如果在子线程中直接new Handler() 会抛异常。
Android 中的动画 :
-
动画类型有几种,特点和区别是什么?
- 属性动画,补间动画;补间动画只是显示的位置变动,view的实际位置未改变,表现为view移动到其他地方,点击事件仍在原处才能相应,而属性动画在控件移动后时间响应就在控件移动后本身进行处理。
-
如何修改Activity进入和退出的动画;
- 通过两种方式:1)通过定义activity的主题,2)通过复写activity的overridePendingTransition(R.anim.fade,R.anim.hold)方法。
LRUCache的底层原理:
- 通过获取内存的最大可用值,lrucache通过构造函数传入缓存值,它是kb为单位,在不超出可使用内存的最大值的情况下,超出就会OOM,通过使用最大缓存值的1/8作为缓存的大小,重写sizeOf()方法来返回每一张图片的大小,这就是lurcache的是使用方法,那么lruchche为何这样强大,原因是它的主要算法原理是将最近最少使用的对象用强引用存储在LinkedHashMap中,并且将最近最少使用的对象在缓存值达到预设值之前从内存中移除,在过去经常使用一种非常流行的缓存技术实现,就是软引用和弱引用,但现在不再使用了,因为在Android2.3之后。垃圾回收机制更容易倾向于回收持有软引用和弱引用的对象,这就让软引用和弱引用显得不可靠,另外在Android3,0之后,图片的数据会存储在本地内存中,因而无法用一种可预见的方式将其释放,这就会潜在的造成应用程序内存溢出或者崩溃,而再有了lruCache这个功能之后,解决这些问题就不在难了。
JNI的调用过程:
- 安装和下载cygwin,下载Android NDK.
- ndk项目中JNI接口的设计,
- 使用C/C++实现本地方法
- JNI生成动态链接库.so文件
- 将动态链接库复制到Java工程,在Java工程中调用,运行Java工程即可。
图片加载框架picasso,glide,imageLoader,Fresco之间的区别:
-
共同的优点:
- 使用简单--都可以通过一句代码实现图片的获取和显示。
- 可配置度高,自适应行性高--可根据系统性能初始化缓存配置,给句cpu核数确定最大并发数,根据网络状态变化来调整最大并发数,根据可用内存确定最大缓存大小,
- 多级缓存--每种框架至少有两级缓存,可以提高图片的加载速度。
- 支持多种数据源--网络,本地,资源文件。
- 支持多种displayer--不仅支持imageView,同时支持多种view以及一些虚拟的imageview。
- 支持动画。
imageLoader的设计模式:
* 一个强大的图片加载框架,很好的处理了图片加载的多线程,缓存,内存溢出等问题。多线程异步加载和显示图片(图片来源于网络,sd卡,assets文件夹,drawable文件夹,不能加载9patch图片,可以加载视频缩略图)
* ImageLoader 收到加载及显示图片的任务,并将它交给 ImageLoaderEngine,ImageLoaderEngine 分发任务到具体线程池去执行,任务通过 Cache 及 ImageDownloader 获取图片,中间可能经过 BitmapProcessor 和 ImageDecoder 处理,最终转换为Bitmap 交给 BitmapDisplayer 在 ImageAware 中显示。
* imageloader的优点:
*
* 支持下载进度条监听,支持在view的滚动中暂停图片的加载,实现了多种内存缓存算法,支持本地缓存文件名规则定义。
* 支持监视加载的过程,可以暂停加载图片,在经常使用的listview,GridView,可以设置滑动时暂停加载,停止滑动的时候加载图片。
* 高度可定制化,可以根据自己的需求进行各种配置,(线程池,图片下载器,内存缓存策略等)
* 支持图片内存缓存,文件缓存
* 在网络速度较慢的时候,还可以对图片进行加载并设置下载监听。
* 避免同一个uri加载过程中重复开启任务加载
* 减少加载大图片出现oom的情况。
* 不足:
*
* 不支持加载GIF图片。
Picasso的设计模式:
* Picasso 收到加载及显示图片的任务,创建 Request 并将它交给 Dispatcher,Dispatcher 分发任务到具体 RequestHandler,任务通过 MemoryCache 及 Handler(数据获取接口) 获取图片,图片获取成功后通过 PicassoDrawable 显示到 Target 中。
* Picasso的优点:
*
* 自带统计监控功能--支持图片缓存使用的监控,包括已使用内存大小,节省的流量。
* 支持优先级处理。
* 支持延迟到图片尺寸计算完成加载,
* 支持飞行模式,并发线程数根据网络类型而变。
* 无本地缓存--不是说没有本地缓存,而是Picasso自身没有定义本地缓存的接口,默认使用http的本地缓存。
* 不足:
*
* 不支持加载GIF图片,缓存之后的图片没有进行缩放
glide的设计模式:
* Glide 收到加载及显示资源的任务,创建 Request 并将它交给RequestManager,Request 启动 Engine 去数据源获取资源(通过 Fetcher ),获取到后 Transformation 处理后交给 Target。Glide 依赖于 DiskLRUCache、GifDecoder 等开源库去完成本地缓存和 Gif 图片解码工作。
* glide的优点:
* 支持多种图片缓存,GIF,webP,缩略图,甚至是video。
* 支持优先级处理
* 支持velloy,okHttp.实际上imageloader。Picasso也支持velloy,OKHttp。
* 内存缓存的是处理之后的图片,而不是原始图片,节省内存大小。
fresco的优点:
* 支持流式,支持类似于网页上的从模糊到清晰加载图片,渐进式加载JPEG图片。
* 图片可以从任意的中心点显示在imageView,而不仅仅是图片的中心。
* 支持多帧动画,GIF,webP.
* 缺点: 框架较大,影响apk的体积,使用较繁琐。
网络请求框架Volley 和Ok-http,android-async-http,retrofit的区别:
- 参考博文:http://www.cnblogs.com/changyaohua/p/4992987.html
volley框架:
* google推出的异步网络请求框架和图片加载框架,适合数据量小,通信频繁的网络操作。
* 能够使网络通信更快,更简单,拓展性更好一点,
* get,post网络请求以及网络图像的高效率异步处理请求。
* 可以对网络请求进行排序优先级管理。
* 支持网络请求的缓存
* 多级别取消请求
* 使用volley可以简化一些网络通信的开发,不适合大数据和流媒体的网络请求,例如上百兆文件,视频上传。
* volley在Android2.2以下使用httpClient,2.2以上使用的是httpUriConnection.
* Volley的使用:http://blog.csdn.net/sinyu890807/article/details/17482095
基本的使用方法: http://www.kwstu.com/ArticleView/kwstu_20144118313429
直接返回Object的话,用Gson/FastJson与Volley的结合:http://www.cnblogs.com/freexiaoyu/p/3955137.html
* Volley问题收录:Volley的request默认回调到主线程中,如果有需求是要加载到sqlite等等仍需要在子线程中进行的操作 解决方案 :https://www.zhihu.com/question/36672622/answer/76003423
async-Http框架:
* 清晰地网络请求回调
* 网络请求使用线程池ThreadPool,限制并发资源使用情况
* get/post基于参数构建使用(RequestParams)
* 支持Multipart文件上传,大数据上传下载。
* 内置响应解析成json.
* 持久化cookie存储,保存cookie到应用程序的sharedPreferences.
* 支持二进制文件,图片的下载
* 使用的是httpClient
okhttp框架:
* OKhttp和retrofit都出自于Square公司,是高性能的http库。
* OKhttp使用okio进行数据传输, 包含一般的get,post请求;基于http的文件,图片上传,加载;支持请求回调,直接返回对象,对象集合;支持session的保持。
* 支持spdy,http2.0,websocket,支持同步,异步,
* 封装了线程池,数据转换,参数使用,错误处理等;
* OKhttp使用教程:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0106/2275.html
retrofit框架:
* 出自于Square公司,是对OKhttp做了一层封装。
* 支持Gson解析,retrofit已经在内部封装了json解析
异步加载框架handler,asyncTask,RXJava :
-
异步加载框架之asyncTask:
- 需要重写的四个方法:doInBackground(Params params)运行在后台线程中;onPreExecute(),onProgressUpdate(progress),onPostExecute(result)这三个方法是运行在UI线程中。
- 使用aysncTack需要遵守的原则:task实例必须在UI线程中创建;execute方法必须在UI线程中调用;不要手动的调用那四个方法。
- asyncTask内部也是handler机制完成的,只不过Android提供了执行框架来提供线程池来执相应的任务,因为线程池的大小的问题,所以asyncTask值应该用来执行耗时较短的任务,比如HTTP请求,大规模的下载和数据库的更改不适用于asyncTask,因为会导致线程池堵塞,没有线程来执行其他任务,导致asyncTask根本执行不了。
-
rxjava框架:
- rxjava是一个异步操作库,在JavaVM上使用可观测的序列来组成异步的,基于事件的程序的库,rxjava的优势在于简洁,随着程序逻辑变得越来越复杂,rxJava确一直简洁,能够很大程度的提升代码的阅读性。他通过拓展的观察者模式来实现的。rxjava有四个基本的概念:被观察者Observable,观察者Observer,订阅事件subscribe,Observable和observer通过subscribe()来实现订阅关系,从而Observable可以在需要的时候发出事件来通知Observer。
MVC和MVP的区别;
- MVC是model (模型) view (视图) controller(控制器),view强依赖于model是MVC的主要问题,使用MVC,将业务逻辑抽离到controller中,让view层专注于UI.
- MVC指的是model view controller,model着的是数据层,主要负责相关的业务逻辑,包括数据库,网络本地缓存等,view是指界面,主要负责UI相关的业务逻辑,包括布局界面,相关控件的显示,controller是控制层,主要负责处理在对象状态下调用model和view的方法实现数据和UI展示的相关逻辑.以MVC的设计模式的开发流程在一定程度上提高了代码的可维护性,比如要修改UI界面的相关数据,就可以找到对应的处理层来处理代码,但由于model和view的相关逻辑都写在avtivity控制层了,这样后期维护起来比较麻烦,因此就产生了一种新的设计模式,MVP,MVP指的是model view presenter,是在MVC的基础上,将controller层该为了presenter层,它成为了model和view层之间交互的桥梁,他本身不具有任何的逻辑代码,只是在对应的状态下调用model和view层的方法来实现数据的相关逻辑和UI展示.
- 两者的区别在于MVP中view不能直接的使用model层,它们之间的通信是通过Presenter(MVC中的Controller)来进行的,所有的交互都发生在Presenter内部,而在MVC中View会直接从Model中读取数据而不是通过 Controller。
网络请求get和post的区别;
- get是从服务器上获取数据,post是向服务器上传输数据。
- get是把参数数据队列加到提交表单的ACTION属性所指的URL中,值和表单内各个字段一一对应,在URL中可以看到。post是通过HTTP post机制,将表单内各个字段与其内容放置在HTML HEADER内一起传送到ACTION属性所指的URL地址。用户看不到这个过程。
- 对于get方式,服务器端用Request.QueryString获取变量的值,对于post方式,服务器端用Request.Form获取提交的数据。
- get传送的数据量较小,不能大于2KB。post传送的数据量较大,一般被默认为不受限制。但理论上,IIS4中最大量为80KB,IIS5中为100KB。
- get安全性非常低,post安全性较高。但是执行效率却比Post方法好。
- get可以被浏览器缓存,post不可以。
- get请求是安全的,post不安全。
Android中常见的解决冲突的方案:
- 多个滑动必然会产生冲突,比如最常见的scrollView中嵌套listview,一般做法是计算出listview的总高度,这样就不用去滑动listview了。再比如viewpager嵌套fragment,fragment中又有listview,这原本是由滑动冲突的,但是viewpager内部已经帮我们解决了这种冲突,如果需要自己解决的话,可以通过下面两种方式解决:一是外部拦截法,外部拦截法是指在有点击事件时都要经过父容器,那么在父容器时如果需要拦截就拦截自己处理,不需要则传递给下一层进行处理,主要的拦截是需要重写onInterceptTouchEvent()方法。二是内部拦截法,是指父容器不拦截任何事件,所有事件都传递给子view,如果需要就直接消费掉,不需要在传给父容器处理。需要重写dispatchTouchEvent()方法。
- 参考博文:http://blog.csdn.net/lylodyf/article/details/52438997
简述对Binder机制的理解。
- binder机制是用来实现不同进程之间的通信的。binder属于一个驱动,工作在linux层,运行在内核态,它的操作完成是基于一段内存,因此我们开发的程序中对binder的调用都是通过系统的调用来完成的,binder的架构由服务端,binder驱动,客户端三部分组成。处于对程序的安全性,可靠性,传输性能的考虑,Android选用binder来实现不同进程之间的通信。
谈谈对context的理解。
- 从字面意思理解是“上下文"的意思,从类的继承关系来看,context是一个抽象的基类,它的实现子类有三种:application,activity和service。我们通过它来访问当前包的资源(getResources,getAssets),启动其他组件(activity,service,broadCastReceiver)以及得到各种服务(getSystemService)。换句话说,context提供了一个应用运行的环境,在context的大环境中,应用才可以访问资源,才能完成和其他组件,服务交互。
如何缩减apk的大小,apk的打包;
- Android打包本身会对png图片进行无损压缩,但是纯粹的进行无损压缩并不会对apk的减小有任何的效果,因此可以可以通过tinypng进行有损压缩。
- png换成jpg,如果图片还大,在不降低画质的情况下,可以在将jpg换成webp.
- 大图缩小
- 覆盖arr中一些默认的大图,典型的是support-v4包中包含的一些可能用到的图片,在实际中app是用不到的。并不是吧所有用不到的图片替换掉,而是把几张较大的图片用1x1的图片替换。
- 删除armable-v7包中的so文件, armable-v7和armable文件夹可以只保留armable。
- 微信资源压缩打包。采用微信压缩方案。
- proground深度混淆代码
- 深度清理代码和资源,引入的无用图片,相同的图片,图片可用着色方案替换,用shape替换等。
- 去除重复库
- 表情包在线化
谈谈对Android NDK的理解。
- Android NDK是一系列工具的集合,可以快速的帮助开发者开发C/C++的动态库,并能自动将so文件和Java应用一起打包成apk。
- ndk 提供了一份稳定的,功能有限的API头文件声明;
- NDK是Android平台支持C开发的开端。
Android中进程间通讯的实现方式。
Bundle/Intent传递数据:可传递基本类型,String,实现了Serializable或Parcellable接口的数据结构。Serializable是Java的序列化方法,Parcellable是Android的序列化方法,前者代码量少(仅一句),但I/O开销较大,一般用于输出到磁盘或网卡;后者实现代码多,效率高,一般用户内存间序列化和反序列化传输。
文件共享:对同一个文件先后写读,从而实现传输,Linux机制下,可以对文件并发写,所以要注意同步。顺便一提,Windows下不支持并发读或写。
Messenger:Messenger是基于AIDL实现的,服务端(被动方)提供一个Service来处理客户端(主动方)连接,维护一个Handler来创建Messenger,在onBind时返回Messenger的binder。
双方用Messenger来发送数据,用Handler来处理数据。Messenger处理数据依靠Handler,所以是串行的,也就是说,Handler接到多个message时,就要排队依次处理。AIDL:AIDL通过定义服务端暴露的接口,以提供给客户端来调用,AIDL使服务器可以并行处理,而Messenger封装了AIDL之后只能串行运行,所以Messenger一般用作消息传递。
通过编写aidl文件来设计想要暴露的接口,编译后会自动生成响应的java文件,服务器将接口的具体实现写在Stub中,用iBinder对象传递给客户端,客户端bindService的时候,用asInterface的形式将iBinder还原成接口,再调用其中的方法。ContentProvider:系统四大组件之一,底层也是Binder实现,主要用来为其他APP提供数据,可以说天生就是为进程通信而生的。自己实现一个ContentProvider需要实现6个方法,其中onCreate是主线程中回调的,其他方法是运行在Binder之中的。自定义的ContentProvider注册时要提供authorities属性,应用需要访问的时候将属性包装成Uri.parse("content://authorities")。还可以设置permission,readPermission,writePermission来设置权限。 ContentProvider有query,delete,insert等方法,看起来貌似是一个数据库管理类,但其实可以用文件,内存数据等等一切来充当数据源,query返回的是一个Cursor,可以自定义继承AbstractCursor的类来实现。
Socket:学过计算机网络的对Socket不陌生,所以不需要详细讲述。只需要注意,Android不允许在主线程中请求网络,而且请求网络必须要注意声明相应的permission。然后,在服务器中定义ServerSocket来监听端口,客户端使用Socket来请求端口,连通后就可以进行通信。
实现一个自定义view的基本流程;
- 自定义view的属性:在res/values/下建立一个attrs.xml文件,在里面定义我们的属性和声明我们的整个样式。在xml文件中一定要引入命名空间。
- 在view的构造方法中获取到我们的自定义属性
- 重写onMersure()方法
- 重写onDraw()方法
- 参考博文:http://blog.csdn.net/lmj623565791/article/details/24252901/
几种常见的设计模式;
单例模式:
-
单例模式是确保某个类只有一个实例,有懒汉式和饿汉式,
* 懒汉式,首先得私有化构造函数,防止类在外部被实例化,接下来就是书写Singleton类型的getInstance()静态的方法,避免线程不安全可以再静态方法上加锁synchronized.以下是线程安全的懒汉式的设计模式. public class Singleton { private static Singleton instance; private Singleton (){} public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } * 饿汉式是在类一加载的时候就直接进行了实例化,本身就是线程安全的. //饿汉式单例类.在类初始化时,已经自行实例化 public class Singleton1 { private Singleton1() {} private static final Singleton1 single = new Singleton1(); //静态工厂方法 public static Singleton1 getInstance() { return single; } }
什么时候使用单例模式?
* 当这个类的对象在多个地方创建的时候,使得内部的方法多次调用,但是我们希望只要一个对象操作这个方法,或者不希望多个地方同时调用这个方法,我们需要保持这个方法的单一性质,我们就用单利模式。
简单的工厂类:
适配器模式:
谈谈你在实际项目中怎样解决一个BUG;
- 异常附近多打印log信息
- 分析log日志,进行断点调试,
- 调试不出来,上stack Overflow贴上异常信息,请加大牛
- 多看看代码,从源码中分析,查找相关信息。
- 网上搜索相关问题,解决方案,或寻求师傅帮忙。
怎样对Android进行优化
- listview优化,图片优化,内存优化,尽量不使用过多的静态类static,数据库使用完成之后,要记得关闭cursor。广播使用完之后要注销。
谈谈你对bitmap的理解,什么时候应该手动调用bitmap.recycle().
- bitmap是Android中经常使用的一个类,它代表一个图片资源,bitmap消耗内存很严重,如果不注意优化代码,经常会出现oom,优化方式通常有:使用缓存;压缩图片;及时回收。
- 至于什么时候使用是手动调用bitmap.recycle()方法,这个需要看具体场景了,原则是在不使用bitmap时,就要回收掉,需要注意的是,在Android2.3之前bitmap对象与像素数据是分开存放的,bitmap对象存在Java heap中而像素数据存储在Native Memory中,这时黑有必要调用recycle回收内存,但是在2.3之后,bitmap对象和像素数据都是存在Heap中,通过GC就可以回收内存。
你一般在开发项目中都是用什么设计模式,如何来重构,优化你的代码?
- 较为常用的是单例设计模式和工厂设计模式以及观察者模式,一般需要保证对象在内存中的唯一性时需要使用到单例模式,例如对数据库操作的sqliteOpenHelpter的对象,工厂模式主要是为创建对象提供过渡接口,以便将创建的对象的具体过程屏蔽隔离起来,达到提高灵活性的目的,观察者模式定义对象间的一种一对多的依赖关系,当一个对象的装填发生变化时,所有依赖于它的对象都得到通知并自动更新。
Android中第二次登陆实现自动登录
- 这是一个实际的案例流程,前提条件是所有用户相关的接口都走https,非用户相关的列表类数据走http。
- 第一次登录的时候getUserInfo的时候带一个长效的token,该长效的token用来判断用户是否登录和换取短的token;
- 把长效token保存到sharedPreferences中,
- 接口请求用长效的token换取短token,短token服务端可以根据你的接口最后一次请求作为标识,超时时间为一天
- 所有接口都用短效token
- 如果返回短效token失效,执行3步骤在直接标识当前接口,
- 如果长效token失效(用户换设备或者超过两周),提示用户重新登录。