1) 整个的生命周期,从onCreate(Bundle)开始到onDestroy()结束。
2) 可见的生命周期,从onStart()开始到onStop()结束。在这段时间,可以看到Activity在屏幕上,尽管有可能不在前台,不能和用户交互。在这两个接口之间,需要保持显示给用户的UI数据和资源等,例如:可以在onStart中注册一个IntentReceiver来监听数据变化导致UI的变动,当不再需要显示时候,可以在onStop()中注销它。
3) 前台的生命周期,从onResume()开始到onPause()结束。在这段时间里,该Activity处于所有 Activity的最前面,和用户进行交互。Activity可以经常性地在resumed和paused状态之间切换。
4) 从界面A跳转到界面B,生命周期变化情况: 当用户点击A中按钮来到B时,假设B全部遮挡住了A,将依次执行A:onPause -> B:onCreate -> B:onStart -> B:onResume -> A:onStop。 此时如果点击Back键,将依次执行B:onPause -> A:onRestart -> A:onStart -> A:onResume -> B:onStop -> B:onDestroy。
5) 对于栈最顶上的界面A,按Back键和按Home键的区别: 如果按下Back键,系统返回到桌面,并依次执行A:onPause -> A:onStop -> A:onDestroy。 此时如果按下Home键(非长按),系统返回到桌面,并依次执行A:onPause -> A:onStop。由此可见,Back键和Home键主要区别在于是否会执行onDestroy。
1)standard:默认的标准启动模式,不管有没有已存在的实例,都生成新的实例。即使是A startActivity A,也会重新生成一个新的实例,再回退时,A也会出现两次; 2)singleTop:如果发现有对应的Activity实例正位于栈顶,则重复利用,不再生成新的实例,如A启动A,不会生成新的实例,会走A的onNewIntent方法,而不是onCreate方法,回退时,也只会回退一次; 3)singleTask:所在Activity栈中有对应的Activity实例,则使此Activity实例之上的其他Activity实例统统出栈,使此Activity实例成为栈顶对象,显示到幕前,这一般用在程序的主界面上; 4)singleInstance:当被启动时,系统会首先判断系统其他栈中是否已经存在此Activity实例,有则直接使用,并且其所在的Activity栈理论上只有它一个Activity元素。 singleInstance表示该Activity在系统范围内“实例唯一”。ingInstance和singleTask主要区别在与系统范围内的“实例唯一”还是当前Activity栈“实例唯一”。
Activity的状态保存onSaveInstanceState()、onRestoreInstanceState() Activity的 onSaveInstanceState() 和onRestoreInstanceState()并不是生命周期方法,当应用遇到意外情况(如:内存不足、用户直接按Home键)由系统销毁一个Activity时,onSaveInstanceState()会被调用。但是当用户主动去销毁一个Activity时,例如在应用中按返回键,onSaveInstanceState()就不会被调用。通常onSaveInstanceState()只适合用于保存一些临时性的状态,而onPause()适合用于数据的持久化保存。当某个activity变得“容易”被系统销毁时,该activity的onSaveInstanceState就会被执行,开发者可以覆写onSaveInstanceState()方法。onSaveInstanceState()方法接受一个Bundle类型的参数, 开发者可以将状态数据存储到这个Bundle对象中, 这样即使activity被系统摧毁, 当用户重新启动这个activity而调用它的onCreate()方法时, 上述的Bundle对象会作为实参传递给onCreate()方法, 开发者可以从Bundle对象中取出保存的数据, 然后利用这些数据将activity恢复到被摧毁之前的状态。如在横竖屏切换时,应用的Activity可能要保存edittext中的值,需要调用onSaveInstanceState()来何存输入值。
startActivityForResult两个注意事项: 1) 这里需要注意的是,如果被启动的与Activity在AndroidManifest.xml文件中配置的launchMode设为"singleTask"的时候,startActivityForResult执行后,onActivityResult立即会被执行到。 原因:startActivityForResult(Intent,int,Bundle)有说明:if the activity you are launching uses thesingleTask launch mode, it will not run in your task and thus you willimmediately receive a cancel result.因为Activity1用startActivityForResult启动Activity2, 如果一个Activity2的启动方式是singleTask时,若Activity2之前在任务栈中存在,再次启动它时,它会把它上面所有的Activity实例都pop出栈,这样再调setResult时,返回的是Activity2任务栈下面的界面,而不是Activity1,所以,Activity1收到的一个cancel的Result。
2) 另外startActivityForResult的requestCode值必须要大于等于0,不然,startActivityForResult就变成了 startactivity。
activity的finish()方法有两个层面含义,1.将此Activity从Activity栈中移除,2.调用了此Activity的onDestroy方法。 1) finish()方法用于结束一个Activity的生命周期,但是并没有释放他的资源。 2) onDestory()方法则是Activity的一个生命周期方法,其作用是在一个Activity对象被销毁之前,Android系统会调用该方法,用于释放此Activity之前所占用的资源。 有可能程序在运行了finish()方法而没有运行onDestory()方法,因为Activity生命周期里的方法,不确定什么时候会被调用,皆是由系统确定; 3) isFinishing方法来判断Activity是否处于销毁状态。
一个fragment必须总是嵌入在一个activity中,同时fragment的生命周期受activity而影响,一个Activity可以运行多个 Fragment,一个fragment也可以在多个activity中作为一个模块,fragment有自己的生命周期,接收自己的输入事件,可以从运行中的activity添加或移除。 Activity 的后退栈由系统管理,而 Fragment 的后退栈由所在的Activity 管理。
广播接收器仅在它执行这个方法时处于活跃状态。当 onReceive() 返回后,它即为失活状态。
1) 静态注册,在AndroidManifest.xml中用<receiver>标签声明注册,并在标签内用<intent- filter>标签设置过滤器。
2) 动态注册:动态地在代码中先定义并设置好一个 IntentFilter对象,然后在需要注册的地方调 Context.registerReceiver()方法,如果取消时就调用Context.unregisterReceiver()方法。如果用动 态方式注册的BroadcastReceiver的Context对象被销毁时,BroadcastReceiver也就自动取消注册了。 注意事项:若在使用sendBroadcast()的方法是指定了接收权限,则只有在AndroidManifest.xml中用<uses- permission>标签声明了拥有此权限的BroascastReceiver才会有可能接收到发送来的Broadcast。
(1) 定义:一个专门在后台处理时间任务,没有UI界面; (2) Service的生命周期
Service生命周期.jpg① 通过bindService启动的Service生命周期:onCreate()->onBind()->onUnbind()->onDestory() ② 通过startService启动的Service生命周期:onCreate()->onStartCommand()->onDestory() (3) Service两种启动方式和区别:bindService, startService ① startService只是启动Service,与启动的组件没有关联,当Service调用stopSelf或是相关组件调用stopService服务才会终止; ② bindService,和组件绑定,其他组件可通过回调获取Service代理来和Service交互,当启动方销毁,Service也会调用unBinder进行解绑,只有所有绑定该Service的组件都解绑了,Service才会销毁。 (4) Service和Activity怎么进行通信 (5) Service与IntentService ① Service与它所在应用位于同一个进程中; Service还是在主线程中跑,所以不应该在Service中直接处理耗时的任务; ② IntentService:IntentService是Service的子类,比普通的Service增加了额外的功能。 它会创建独立的worker线程来处理所有的Intent请求; 会创建独立的worker线程来处理onHandleIntent()方法实现的代码,无需处理多线程问题; 所有请求处理完成后,IntentService会自动停止,无需调用stopSelf()方法停止Service; 为Service的onBind()提供默认实现,返回null,故适合用startService来启动 为Service的onStartCommand提供默认实现,将请求Intent添加到队列中;
(1)Fragment的生命周期
fragment生命周期.png(2)Fragment之间数据传递方式
(3)Fragment与Activity之间数据传递方式
(1)Intent通过bundle发送数据 (2)文件共享 (3)content Provider (4)AIDL (5)Messager (6)Socket
(1)由synchronized关键字修饰方法、代码块,如果synchronized修饰的是静态方法,此时若调用该静态方法,将会锁住整修类。 使用方法,如饿汉式的线程安全写法(修饰方法): //公有的同步静态方法 public static synchronized Singleton getInstance() { //锁住获取实例方法 if (instance == null) { instance = new Singleton(); } return instance; }
(2)用特殊域变量(volatile)实现线程同步 volatile修饰的变量每次都是从内存中读取,而不是从缓存中读取,保证每个线程访问到的变量值都是一样的;适应用于需要读同步的线程同步中,常结合synchronized一起使用。 如单例的双重锁写法 public class Singleton { private volatile static Singleton singleton; //使用volatile 关键字 private Singleton (){} //私有构造函数 public static Singleton getInstance() { if (singleton == null) { //第一层校验 synchronized (Singleton.class) { if (singleton == null) { //第二层校验 singleton = new Singleton(); } } } return singleton; } } 这里需要使用volatile来修饰单例的实例,因为在执行singleton = new Singleton();这句里需要三步: 1) 给Singleton的实例分配内存; 2) 调用Singleton()的构造函数,需要初始化成员字段; 3) 将singleton对象指向分配的内存空间(即singleton不为空了); 由于步骤2和步骤3的运行顺序是不确定的,如果某线程创建实例步骤是:1-3-2,在执行3还没有执行2时,另外一个线程就来使用该实例,由于执行了3就直接把实例拿走了,这样在使用过程中就会因为singleton中某些成员没有初始化而报错。 具体的单例写法可参考:Android设计模式之单例模式的七种写法
(3)使用重入锁ReentrantLock类实现线程同步 用法:先声明一个 ReentrantLock实例 Lock lock = new ReentrantLock(); 在需要同步的代码块前后添加上lock.lock();和lock.unlock(); (4)使用ThreadLocal管理变量 1) 用ThreadLocal修饰的变量有什么不同? 使用ThreadLocal创建的变量叫线程局部变量,只可以被当前线程访问并修改,其他线程无法访问或修改。若使用ThreadLocal来管理变量,每个线程使用的都是该变量的副本,副本之间是相互独立的,这样每个线程都可以随意修改自己变量的副本,而不会对其他线程产生影响。
2) ThreadLocal内部实现原理,怎么保证数据仅被当前线程持有? ThreadLocal在进行放值时的代码如下: public void set(T value) { Thread currentThread = Thread.currentThread();//获取当前线程 Values values = values(currentThread);//利用当前线程作为句柄获取一个Values对象 if (values == null) { values = initializeValues(currentThread);//values为空则创建一个values对象 } values.put(this, value);//将值放入values对象中 } ThreadLocal的值是放入了当前线程的一个Values实例中,所以只能在本线程访问,其他线程无法访问。
3) ThreadLocal修饰的变量一定不能被其他线程访问么? 答案是不是:对于子线程是可以访问父线程中的threadlocal修饰的变量的。 如果在主线程中创建一个InheritableThreadLocal实例,那么在子线程中就可以得到InheritableThreadLocal实例,并获取相应的值。 在ThreadLocal中# inheritValues(Values fromParent) 方法来获取父线程中的值。
4)ThreadLocal的对象存放在哪? 答案是堆上:在Java中,第个线程都会有一个栈内存,栈内存属于单个线程,其存储的变量只能在其所属线程中可见。但是TheadLocal的值是被线程实例所有,而线程实例是由其创建的类所持有,所以ThreadLocal实例实际上也是被其创建的类所持有,故它们都存在于堆上。
5)ThreadLocal会导致内存泄漏么? 答案是不会:虽然ThreadLocal实例被线程中的Values实例所持有,也可以被看成是线程所持有,若使用线程池,之前的线程实例处理完后出于复用的目的依然存在,但Values在选择key时,并不是直接选择ThreadLocal实例,而是ThreadLocal实例的弱引用: Reference<ThreadLocal<?>> reference = (Reference<ThreadLocal<?>>) k; ThreadLocal<?> key = reference.get(); 在get方法中也是采用弱引用: private final Reference<ThreadLocal<T>> reference = new WeakReference<ThreadLocal<T>>(this); if (this.reference == table[index]) { return (T) table[index + 1]; } 这样能保证如查当前thread被销毁时,ThreadLocal也会随着销毁被GC回收。
6)在android中的应用 android中的looper类就是利用了ThreadLocal特性,保证每个线程只存在一个looper对象。 static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }
7) 应用场景: 在实现单个线程上下文信息存储,比如交易id等,
AsyncTask是对Thread和Handler的组合包装,一些使用规择: 类的定义、实例化、调用execute必须在主线程中调用,一个task只能执行一次;定义了一个线程池THREAD_POOL_EXECUTOR
5、,它实现的原理是什么?
(1) 如何加载大图,如几百M甚至1G的大图如何加载? ①在内存中压缩图片,如通过设置压缩比options.inSampleSize,再进行读图 ②除低要显示的图片色彩质量 如:alpah_8:每个像素占1byte的内存 RGB_565:每个像素占2byte的内存 ARBG_444:每个像素占2byte的内存(渐变色效果图片,会使渐变色呈现色彩条状) ARGB_888:每个像素占4byte的内存(默认显示方式) 如果对图片要求不高,可以RGB_565或是ARBG_444格式显示图片 ③若只查询图片信息,可通过设置options.inJustDecodeBounds=true;设置不把图片加载到内存中,但可以获取到图片的宽、高和大小等参数 (2) 100*100像素图片,需要占用手机多少内存?
屏幕快照 2017-03-05 下午3.55.50.png参考资料:[ 一张图片占多大内存的计算-android]http://blog.csdn.net/shareus/article/details/50947185
(1)采用convertview进行布局复用,通过setTag, getTag,获取布局 (2)运用ViewHolder,避免重复调用findViewById (3)item的布局层级越少越好 (4)滑动时不加载一些耗资源如图片等,停止时再加载,使用异步加载 (5)在adapter中的getView方法中尽量少使用逻辑 (6)将ListView的scrollingCache和animateCache设置为false (7)在listview滑动时,判断不进行图片等的加载 参考资料:[提高ListView性能的技巧]http://www.androidchina.net/3189.html
(1)确定是完全自定义还是继承View的派生子类 (2) 定义自定义属性,在资源元素<declare-styleable>中为您的view定义自定义属性。 (3) 获取自定义属性,当view从XML布局中创建了之后,XML标签中所有的属性都从资源包中读取出来并作为一个AttributeSet传递给view的构造函数。 (4) 添加属性和事件 (5) 自定义绘制(实施),重写onDraw()\onMesure()方法,如果是viewGroup,还需要重写onLayout()方法
(1)dispatchTouchEvent (2)OnInterceptTouchEvent (3)TouchEvent
1) OkHttp 是 Square 公司开源的针对 Java 和 Android 程序,封装的一个高性能 http 请求库,所以它的职责跟 HttpUrlConnection 是一样的,支持 spdy、http 2.0、websocket ,支持同步、异步, OkHttp 还封装了线程池、数据转换、参数使用、错误处理等,api 使用起来更加方便。可以把它理解成是一个封装之后的类似 HttpUrlConnection 的一个东西,但在使用的时候仍然需要自己再做一层封装,这样才能像使用一个框架一样更加顺手。OkHttp 基于 NIO 和 Okio ,所以性能上要比 Volley更快。
2) Volley 是 Google 官方出的一套小而巧的异步请求库,该框架封装的扩展性很强,支持 HttpClient、HttpUrlConnection,甚至支持 OkHttp,但不支持 post 大数据,所以不适合上传文件。Volley 设计的初衷本身也就是为频繁的、数据量小的网络请求而生。
3) Retrofit 是 Square 公司出品的默认基于 OkHttp 封装的一套 RESTful 网络请求框架,通过注解直接配置请求,可以使用不同的 http 客户端,默认是用 http ,可以使用不同 Json Converter 来序列化数据,同时提供对 RxJava 的支持。
4) OKHttp的优点: HTTP/2 以及 SPDY的支持多路复用 连接池会降低并发连接数 透明GZIP加密减少下载体积 响应缓存避免大量重复请求 同时支持同步的阻塞式调用与异步回调式调用
它使用动态代理技术,实现在接口中声明的方法,采用注解方式 参考资料:1)[Android开源项目推荐之「网络请求哪家强」]https://zhuanlan.zhihu.com/p/21879931 2)[[Android] Retrofit 2.0实现原理 (http://blog.qiji.tech/archives/category/android)
总体来说,http协议是从http1.0->http1.1->https-> SPDY->http2.0发展过来的; (1)HTTP1.1相对于HTTP1.0的改进:添加了更多的缓存头策略;宽带优化;优化了错误通知处理;添加了Host头部处理;支持长连接; (2)https相对于http1.x的改进: HTTPS协议需要到CA申请证书; HTTP协议运行在TCP之上,所有传输的内容都是明文,HTTPS运行在SSL/TLS之上,SSL/TLS运行在TCP之上,所有传输的内容都经过加密的; HTTP和HTTPS使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443; HTTPS可以有效的防止运营商劫持,解决了防劫持的一个大问题。 (3)SPDY相对于https的改进: 采取了多路复用(multiplexing),降低延迟,还可设置请求的优先级; 请求头部header压缩;服务端推送(server push); (4)http2.0相对于SPDY的改进: HTTP2.0的协议解析采用二进制格式,更健壮; 优化http请求头部压缩。 参考资料:(HTTP,HTTP2.0,SPDY,HTTPS你应该知道的一些事)
(1) Glide 默认的 Bitmap 格式是 RGB_565 格式,而 Picasso 默认的是 ARGB_8888 格式,这个内存开销要小一半。 (2) 在磁盘缓存方面,Picasso 只会缓存原始尺寸的图片,而 Glide 缓存的是多种规格,也就意味着 Glide 会根据你 ImageView 的大小来缓存相应大小的图片尺寸,比如你 ImageView 大小是200 x 200,原图是 400 x 400 ,而使用 Glide 就会缓存 200 x 200 规格的图,而 Picasso 只会缓存 400 x 400 规格的。这个改进就会导致 Glide 比 Picasso 加载的速度要快,毕竟少了每次裁剪重新渲染的过程。 (3) Glide 支持加载 Gif 动态图,而 Picasso 不支持该特性。 (4) Fresco 将图片放到一个特别的内存区域叫 Ashmem 区,就是属于 Native 堆,图片将不再占用 App 的内存。 (5)各网络请求库使用线程池情况: 1) Picasso在Wifi下线程数为4,而4G下线程数为3, 3G下为2, 2G下为1,默认状况为3. 2) UniversalImageLoader的加载任务线程池和缓存处理线程池的默认大小都为3,同时默认线程优先级是Thread.NORM_PRIORITY-2,线程池的任务处理类型都是FIFO。 3) Glide加载缓存未命中的线程池会根据根据CPU的数量和Java虚拟机中可用的处理器数量来选择合适的线程数,但是最多不超过4;而加载缓存命中的图片的线程池默认大小为1. 参考资料:1)[[ Android ] Fresco 与 Picasso 、Glide 的比较]http://blog.qiji.tech/archives/6344 2) [Android的App中线程池的使用,具体使用多少个线程池?]https://www.zhihu.com/question/37804956 3)[Android开源项目推荐之「图片加载到底哪家强」]https://zhuanlan.zhihu.com/p/21397115
一般只需要对一种图片加载库进行研究即可,如以Glide为例,图片加载流程图:
流程图.png它主要采用的技术有: 1) 图片池:(BitmapPool,实现类是LruBitmapPool,是一个基于LRU方式的Bitmap缓存池,用于Bitmap的复用) 2) 两级内存缓存:LruResourceCache, activeResources(先从LruResourceCache中寻找资源,若找到则将其从cache中移除并放入activeResources中,否则从activeResources中寻找。比一般内存缓存额外多一级缓存的意义在于,当内存不足时清理cache中的资源时,不会对使用中的Bitmap造成影响)。 3) 磁盘缓存(若内存中没有,则从磁盘中查找,根据不同的磁盘缓存策略,源数据可首先被写入到磁盘,对获取的Bitmap将其转换然后从缓存文件中加载,而不是直接返回) 4)网络获取(利用线程池策略,采用okHttp进行请求,并将文件流写入磁盘缓存,再对文件流进行编码) 参考资料:1) [Glide源码分析]http://www.jianshu.com/p/96fc561eada1
参考资料:1)[Android MultiDex实现原理解析]http://www.jianshu.com/p/79a14d340cb0
快速排序、归并排序、两个有序数组合并
(1)用两个队列实现一个栈,用两个栈实现一个队列 (2)写一个栈的实现代码(用数组实现)
(1) 单例模式(饿汉式、懒汉式、如何保证线程安全、双重锁机制) (2) 观察者模式(Rxjava中的Observer和Observable、List中的adapter#notifyDataSetChanged()) (3) Builder模式(一般的链式调用,如Glide.with)
参考资料: 从Android代码中来记忆23种设计模式
上图是一般的apk包包含的文件,如果加入混淆等还会有proguard.cfg、project.properties等文件,从图中可以看出AndroidManifest.xml、META-INF这些本身就很小没有必要做进一步压缩的文件,而其它文件或者文件夹都可以考虑进行优化,从而减小APK的体积。下面具体说说android apk (1) 使用lint去除一些无用的代码、资源减小resources.arsc体积 可以借助Android Studio→Inspect Code...对工程做静态代码检查,删掉无用的代码; (2) 代码混淆减少classes.dex大小 代码混淆可以减小该文件的大小,因为混淆后的代码将较长的文件名、实例、变量、方法名等等做了简化,从而实现字节长度上的优化; (3)压缩资源: 1) 使用一些小图片代替大图,有些适配图片可能只需要保留一套如xxhdpi,有些背景图片用代码实现,用.9.png; 2) 使用tinypng压缩图片; 3) 对图片质量要求不是很严格,可以考虑不带alpha值的jpg图片、同等质量下文件更小的webP图片格式; 4) 借助微信提供的AndResGuard资源文件混淆工具对资源文件做混淆,进一步压缩资源文件所占用的空间; (4) 去掉一些不用的适配 1) 如果只需要支持中文,可在build.grade中添加resConfigs "zh"去除无用的语言资源; 2) 去掉一些so包支持 一种CPU架构 = 一种ABI = 一种对应的SO库; 现在手机市场总共支持以下七种不同的CPU架构:ARMv5,ARMv7,x86,MIPS,ARMv8,MIPS64和x86_64, 这7种CPU类型对应的SO库的文件夹名是:armeabi,armeabi-v7a,x86,mips,arm64-v8a,mips64,x86_64。所有的x86/x86_64/armeabi-v7a/arm64-v8a设备都支持armeabi架构的.so文件,64位设备(arm64-v8a, x86_64, mips64)能够运行32位的函数库,所以应用不需要支持很多设备,建议实际工作的配置是只保留armable、armable-x86下的so文件,当然so包是向下兼容的,如果提供了其他cpu类型的文件夹,也需要在相应的文件夹里补全所有的so包,要不手机到时适配找不到合适的so包导致crash。 (5) 减小或甚用第三方依赖库 如果只使用第三方依赖库的少部分功能,可以考虑只提取少部分代码,而不是直接把第三库全部引入; 用较少的库替换大库; (6) 插件化和动态加载 上面提到的so包,可以只提供一套,在应用运行时,需要用到so包的地方,可以从服务器下载so包,再动态加载;
参考资料: 1) 如何做到将apk大小减少6M 2)[Android动态加载]http://www.jianshu.com/p/e9da13f647a9
(1) 多语言适配 不同国家语方适配、相同语言不同国家 (2) 多系统版本适配 (3) 不同屏幕分辨率适配 除了h、xh、xxh多图,还包括一些特殊屏幕分辨率的适配 (4)不同手机版本适配
MVC 、MVP、 MVVM框架的区别,实现
Ant打包和Gradle打包流程、步骤、区别 JNI、NDK相关