面试随记

1、抽象类与接口的区别
抽象类:是子类的通用特性的抽象。它可以有具体的实现方法,子类使用extends关键字继承于它,实现抽象类中抽象方法的实现,可以有自己的构造器,但是不能够实例化。抽象方法的修饰符可以是public,protected和default。
接口:是抽象方法的集合。它的方法都是抽象的,没有实现,实现接口用implements关键字,不能有构造器。接口方法的修饰符是public。

什么时候使用抽象类和接口?
1、如果你拥有一些方法要有默认的实现,那就用抽象类;
2、如果想实现多继承的话,用接口,因为JAVA是单继承的;
3、如果基本功能在不断改变,用抽象类,不然用接口的话,就得修改所有实现了该接口的类。

类的加载过程,以User user = new User()为例。
第一步,使用new创建User对象,会先去【查找User.class文件】,加载到内存中;
第二步,执行该类中的static代码块;
第三步,在堆中开辟空间分配内存地址;
第四步,在堆内存中建立属性,并进行初始化;
第五步,对对象进行构造函数初始化;
第六步,将内存地址赋给栈内存中的user变量。

List、Set、Map区别
List和Set都继承于Collection,Map不是;
List有序,元素可以重复,支持下标查询和迭代器,插入或删除会引起元素位置变化 ,效率低,但是查找速度快
Set无序,元素不可重复,元素重复的会被覆盖,只能用迭代器循环,插入和删除效率高,查询速度慢。
Map存储键值对的数据。HashMap非线安全,HashTable线程安全。
LinkedList、ArrayList、HashSet是非线程安全的,Vector线程安全。

ArrayList与LinkedList的区别与场景
ArrayList:基于动态数组的数据结构,地址连续,所以【查找效率高】,插入和删除慢;
LinkedList:基于链表的数据结构,地址任意,所以【插入和删除快】,查找要移动指针,效率低。重点内容

2、泛型的通配符
上界通配符:? extends A 表示它的参数类型可以为A或A的子类。get方法可以获取到A的子类,所以可以转成成具体对象:父类A。
缺点:set方法不可用,因为不知道参数的具体类型就无法传递特定的类型。
总结:只能读取数据,不能写入数据。

下界通配符:? super A 表示它的参数类型为A或A的父类。set方法可以使用,因为我们参数传入小于等于A的就可以。
缺点:get方法只能赋值给Object,我们知道返回的是A或A的父类,但是不知道具体类型。
总结:只能写入数据,取数据只能赋值给Object。

3、锁类型、死锁的概念,sychronized的应用,用在方法与静态方法、代码块上的区别
锁类型:公平/非公平锁,可重入锁(Synchronized),独享/共享锁,互斥/读写锁,悲观/乐观锁等等。
死锁的概念:多个进程循环等待他方占用的资源而无限期的僵持下去的局面。
避免死锁通用经验:当几个线程都要访问共享资源A、B、C时,保证使每个线程都按照同样的顺序去访问它们,比如都先访问A,在访问B和C。
synchronized应用:
(1)修饰实例方法:作用于当前实例加锁,进入同步代码前要获得当前实例的锁;
(2)修饰静态方法:作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁;
(3)修饰代码块:指定加锁对象,对给定对象加锁,进入同步代码前要获得给定对象的锁。

4、内存泄漏情况有几种?怎么看-LeakCanary
http://www.cnblogs.com/hacjy/p/6900169.html

5、异步线程AsynTask、HandlerThread、new Thread、IntentService
http://www.cnblogs.com/hacjy/p/6911853.html

6、为什么离开公司

7、职业规划

8、有阅读什么书籍、代码
Android开发艺术探索,Android第一行代码,大话设计模式等。

基础知识 – 四大组件(生命周期,使用场景,如何启动)
java基础 – 数据结构,线程,mvc框架
通信 – 网络连接(HttpClient,HttpUrlConnetion),Socket
数据持久化 – SQLite,SharedPreferences,ContentProvider
性能优化 – 布局优化,内存优化,电量优化
安全 – 数据加密,代码混淆,WebView/Js调用,https
UI– 动画
其他 – JNI,AIDL,Handler,Intent等
开源框架 – Volley,Gilde,RxJava等(简历上写你会的,用过的)
拓展 – Android6.0/7.0/8.0特性,kotlin语言,I/O大会

书面离职原因:
非常感谢这3年多来公司、领导、同事对我的信任和关照,使我在这里得到了很多机会和挑战,学到了很多知识,积累了一定的经验,对此我深怀感激!
我因为诸多个人原因,经过深刻的思考后,郑重的向公司提出辞职要求。我会认真做好交接工作的,对此为公司造成的不便还请谅解。
衷心祝愿公司发展越来越好!领导和同事们工作顺利!

1、Activity启动过程
-Launcher(继承Activity)启动,调用的是StartActivitySafely方法,启动模式为NEW_TASK;该方法中调用了Activity.startActivity方法;
-Activity.startActivity中只调用了startActivityForResult方法;
-Activity.startActivityForResult方法中调用了Instrumentation(监控应用程序与系统的交互)的execStartActivity方法;
-Instrumentation.execStartActivity方法中,调用了ActivityManagerNative.getDefault,返回的是ActivityManagerService的远程接口,即ActivityManagerProxy接口,然后调用其方法startActivity;
-ActivityManagerProxy(实现IActivityManager接口).startActivity中进行一些参数的赋值,调用mRemote的transact方法,action为START_ACTIVITY_TRANSACTION,进而调用了ActivityManagerService的startActivity方法;
-ActivityManagerService.startActivity中将操作转给ActivityStack的startActivityMayWait方法;
-ActivityStack.startActivityMayWait,获取ActivityInfo所需的值,然后调用startActivityLocked方法;
-ActivityStack.startActivityLocked,创建即将启动的Activity相关信息ActivityRecord,接着调用startActivityUncheckedLocked方法进行下一步操作;
-ActivityStack.startActivityUncheckedLoacked,创建一个新的Task来启动Activity,添加到ActivityManagerService中,最后进入startActivityLocked中;
-ActivityStack.startActivityLocked,调用resumeTopActivityLocked方法;
-ActivityStack.resumeTopActivityLocked,调用startPausingLocked方法;
-ActivityStack.startPausingLocked,调用ApplicationThreadProxy的schedulePauseActivity方法;
-ApplicationThreadProxy.schedulePauseActivity,通过Binder进入到ApplicationThread.schedulePauseActivity函数中;
-ApplicationThread.schedulePauseActivity,调用queueOrSendMessage方法;
-ActivityThread.queueOrSendMessage中,将信息组装成message,然后由H(继承于Handler,是ActivityThread的内部类)发送sendMessage;
-H.handlerMeaasge,接收发送过来的数据,调用ActivityThread的handlePauseActivity方法;
-ActivityThread.handlePauseActivity,调用Launcher的onPause方法,然后通知ActivityManageNative.getDefault即ActivityManagerService执行activityPaused方法;
-ActivityManagerProxy.ActivityPaused,通过binder机制进入ActivityManagerService的activityPaused;
-ActivityManagerService.activityPaused,调用ActivityStack.actiivtyPaused;
-ActivityStack.actvivityPaused,执行completePauseLocked;
-ActivityStack.completePauseLocked,执行resumeTopActivityLocked;
ActivityStack.resumeTopActivityLocked中,执行StartSpecificActivityLocked,在该方法调用ActivityManagerService.startProcessLocked方法;
-ActivityManagerService.startProcessLocked方法中,执行Process.start方法,创建ActivityThread,执行它的main方法;
-ActivityThread.main,创建了一个ActivityThread的实例,调用它的attach函数,接着进入消息循环,直至最后进程退出;
-ActivityManagerProxy.attachApplication,最后进入ActivityManagerService的attachApplication,这里将操作转给attachApplicationLocked;
-ActivityManagerService.attachApplicationLocked,最终调用的是ActivityStack的realStartActivityLocked方法,执行真正的Activity启动操作;
-ActivityStack.realStartActivityLocked,通过app.thread调用ApplicationThreadProxy.scheduleLaunchActivity方法;
-ApplicationThread.scheduleLaunchActivity,调用ActivityThread的queueOrSendMessage方法,该方法中通过H发送消息;
-H.handleMessage,最后调用ActivityThread的handlerLaunchActivity函数;
-ActivityThread.handleLaunchActivity,首先调用performLaunchActivity来加载这个Activity类,然后调用它的onCreate方法,最后调用handleResumeActivity方法使Activity进入Resumed状态,即会调用Activity的onResume函数;
总结:
(1)阶段一、Launcher通过Binder进程间通信机制通知ActivityManagerService,它要启动一个Activity;
(2)阶段二、ActivityManagerService通过Binder进程机制通知Launcher进入Paused状态;
(3)阶段三、Launcher通过Binder进程间通信机制通知ActivityManagerService,它准备就绪进入Paused状态,于是AMS就创建一个新的进程,启动一个ActivityThread实例;
(4)阶段四、ActivityThread通过Binder机制将一个为ApplicationThread类型的Binder对象传递给AMS,以便它们后续的通信;
(5)阶段五、AMS通过Binder进程间通信机制通知ActivityThread,现在一切准备就绪,可以真正执行Activity的启动操作了。

冒泡算法
两两比较(从后向前相邻的两个数),较大的下沉,较小的数冒泡;

int temp;//临时变量
for(int i = 0;i小于arr.length-1;i++){//趟数,比较几趟
for(int j = arr.length-1;j>i;j–){
if(arr[j]小于arr[j-1]){
temp=arr[j];
arr[j]=arr[j-1];
arr[j-1]=temp;
}
}

}

选择排序算法
在无序数组中,第一次遍历n-1个数,找出最小的数与第一个元素交换,直至第n-1次,找出最小的数与第n-1个元素交换。

for(int i=0;i < arr.length;i++){
int minIndex = i;//第几个元素,假定是最小值
for(int j=i+1;j小于arr.length;j++){
if(arr[j]小于arr[minIndex]){
minIndex = j;
}
}
if(minIndex != i){
int temp = arr[i];
arr[i]=arr[minIndex];
arr[minIndex]=temp;
}

}

Dagger2
基于java注解的在编译阶段(apt)完成依赖注入的框架。
用于模块解耦,提高代码健壮性和可维护性。

@Component
用于标注接口,是依赖需求方与依赖提供方之间的桥梁。

@inject
标记需要依赖的变量;
标记构造函数,在需要类实例的话就可以找到构造函数,创建实例给被@inject的变量提供依赖;
标志普通方法,做一些初始化工作。

@Module 优先于@inject执行
用于标志提供依赖的类。
解决第三方库的构造函数没有带inject,以及给构造函数传参的问题。

@Provides
用于标注Module所标注的类中的方法。

@Scope
用于自定义注解,限定作用域。

@ActivityScope
局部单例

@Singleton
用于标记全局单例

多渠道gradle配置
productFlavors:定义产品特性

不同渠道包
productFlavors{
productA{
manifestPlaceholders = [UMC:”product-a”]
}
}
然后在Androidmanifest中设置

控制是否自动更新
不同包名
不同代码,资源,
不同的第三方sdk

EventBus3.0源码(观察者模式)
解耦,业务与视图分离;
滥用的话,逻辑分散,维护困难,大量使用,代码可读性会变差。

关键点:EventType,订阅者,订阅事件

1.构造函数
使用getDefault获取EventBus实例,这是一个单例模式,使用了双重校验模式;
接下来看下构造函数做了什么?构造函数中使用EventBusBuild创建EventBus;

2、注册
使用subscriber的findSubscriberMethods方法查找订阅的内容;
-findSubscriberMethods:保存订阅方法的Method对象、线程模式、事件类型、优先级、是否是粘性事件等
先通过反射获取订阅者中所有的方法,然后根据方法的类型,参数和注解来找到订阅方法。
-找到所有方法后,开始对所有订阅方法进行注册;
构建订阅事件subscribersByEventType(根据EventType找订阅事件,分发处理)和typesBySubscriber(解绑的时候,根据订阅者找订阅事件,进而对订阅者解绑)
粘性事件,立马执行

3、发送
取出事件队列,然后将当前事件加入队列,按照顺序依次进行处理,并移除。

4、取消注册
typesBySubscriber,解绑的时候,根据订阅者找到EventType,然后根据EventType和订阅者得到订阅事件,进而对订阅者解绑。

热修复原理
1、下发补丁(dex)到手机,即让app从服务器下载;
2、APP通过某种方式,使补丁中的class被app调用

关键点:PathClassLoader、DexClassLoader、DexPathList

使用场景:
PCL:加载已安装在系统上的apk,默认加载器;
DPL:加载任意目录下的dex/jar/zip/apk(程序文件)文件。

两者都继承于BaseClassLoader,调用父类的构造方法。
父类构造方法:初始化DexPathList;
然后获取class,是通过父类的findClass查找,它调用的是DexPathList的findClass方法;
我们先了解DexPathList的构造方法做了什么?初始化了Element[]数组,调用makeDexElement方法,它主要是将一个个程序文件封装成Element对象,添加到Element数组中;
再来看看DexPathList.findClass:就是对Element数组遍历,找到同名的类,然后返回这个class。

原理
就是遍历Element数组,将打包好的dex放在Element数组的第一个元素,保证获取到的class是最新修复好的。

如何得到dex补丁
build-tools dx指令:dx -dex -output=输出路径 class文件路径(完整包名路径)

AndFix原理:方法的替换,是在native层使用指针替换bug方法,以达到修复bug的目的。

Hotfix原理:类的替换,就是把多个dex塞入classloader中,当android加载的时候,如果dex中有相同的类,就会加载最前面的类。
难点:类会被打上CLASS_ISPREVERIFIED标志,进而报错。

Robust原理:对每个函数在编译打包阶段插入一段代码。如果存在patch.jar,就执行其中的方法而跳过原理的代码,达到修复的目的。

Glide原理
1、对象池 Object Pool
核心是为bitmap维护一个对象池。对象池的目的是减少大对象内存分配达到重用来提供性能。
2、生命周期绑定
跟Activity与Fragment的生命周期绑定。
3、预览图的使用
为加快加载速度,优先加载预览图。
4、ABSListView内图片的预加载。

优势:缓存方面,支持gif和video,跟Activity与Fragment的生命周期绑定。

源码分析:如何加载,工作原理怎样?
-Glide.with(context) ,得到RequestManager(实现了LifecycleListener接口)
首先得到RequestManageRetriever(主要是将请求与Activity、Fragment的生命周期绑定(在RequestManager实现)而自动执行请求、暂停等操作),调用它的get方法得到RequestManager。
-RequestManager.get方法,传入了context,Lifecycle对象等参数。这里的context以传入Activity为例说明。
首先,判断activity是否已经是销毁状态(assertNotDestoryed),获取FragmentManager对象后,调用fragmentGet(activity,fm)方法;
fragmentGet方法:返回的是RequestManager。主要是利用Fragment进行生命周期管理的。首先获取RequestManagerFragment对象(getRequestManagerFragment),再获取RequestManager对象,如果为空,就创建RequestManager对象,并设置为当前的RequestManager;
getRequstManagerFragment:通过tag标签查找RequestManagerFragment对象,如果没有找到的话,就创建一个。
RequestManagerFragment继承Fragement,有个成员变量ActivityFragementLifecycle实现了Lifecycle接口,所以在其onStart、onStop、onDestory中调用了Lifecycle相应的方法。
回过头看RequestManager的构造方法:lifecycle赋值、requestTracker赋值、glide赋值(通过单例获取Glide.get(context))等,添加了监听器(addListener)。

总结:
RequestManagerFragment连接(调用)生命周期方法,RequestManager实现了生命周期中的请求方法,而RequestManagerRetriever则是绑定了RequestManager。

-GlideModule的实现
从上面得知,Glide对象是通过单例获取的Glide.get(context)
我们看看Glide.get中的实现:
1、使用了双重检验法,保证同步;
2、通过解析manifest清单(方式:创建一个ManifestParser),返回GlideModule集合;
3、循环集合,执行其中的applyOptions方法和注册组件方法。在此之中,要创建下Glide对象。

再来看看ManifestParse的parse方法(解析metadata的具体实现),返回一个List:
通过PackageManager获取metadata的所有信息,循环比较是否等于GlideModule字符串,是就封装metadata信息成一个GlideModule对象(parseModule)添加到GlideModule集合中。
parseModule方法:封装metadata信息,返回一个GlideModule对象,是通过反射获取的。

Glide对象创建:GlideBuilder.createGlide()
这里主要是做一些初始化操作,如初始化线程池,初始化BitmapPool对象池,内部磁盘缓存、引擎类初始化等,然后调用Glide的构造方法创建Glide对象。

总结:
现在就明白为什么自定义GlideModule只需要实现GlideModule接口,然后在androidmanifest中设置metaData信息,key为自定义的GlideModule的全路径,name为固定字符串GlideModule了。

-Glide.load
实质调用的就是RequestManager的load方法。我们这里已String类型参数分析。
RequestManager.load(String):返回的是DrawableTypeRequest对象(它继承于DrawableTypeBuilder)。对于常用的placeholder,error,transform等设置都是在这个建造者模式DrawableTypeBulider中设置。

-into方法:返回的是Target对象
1、通过断言判断是否在主线程;
2、通过设置的scaleType,调用对应的方法实现图片操作;
3、最终调用into(glide.buildImageViewTarget(view,突然是从的Class))方法
into(Y target)方法返回泛型Y:
1、通过断言判断是否在主线程;
2、获取上一次的Request对象和RequestTracker(追踪请求)对象,清除上一次请求的追踪并释放上一次请求;
3、创建当前请求(最终调用GenericRequest.obtain方法创建),为target设置请求setRequest(request),添加Lifecycle监听,然后执行请求requestTracker.runRequest(request)。
4、执行请求:requestTracker.runRequest
-将请求加到集合中,非暂停状态就发送请求begin(最终调用GenericRequest.begin),暂停的就添加到挂起的集合中;
-GenericRequest.begin:发送请求,并且在未加载完成,未失败且可更改状态的情况下,会在onLoadStarted中先执行加载占位图操作;
而发送请求onSizeReady中,会将状态置为运行状态,然后调用引擎engine的load方法;engine.load:断言是否在主线程,然后先从缓存中取出数据,没有的话就从活动的Resource中获取,它表示的是当前正在使用的resource ,与内存缓存不同的是clear缓存时不会清除它;(换句话说,正在使用中的图片使用弱引用来进行缓存,不在使用中的图片使用LruCache来进行缓存)
最后判断任务是否已经存在了,如果没有就创建一个EngineJob对象,将一个EngineRunnable交给job,调用engine.start方法开始请求。start中,提交任务到diskCacheService线程池。EngineRunnable的润方法:先从DiskLruCache获取数据,如果没有,就从其他地方获取。
数据获取:DataFetcher.loadData,可以自定义请求网络的方式(默认HttpUrlConnection),比如使用Retrofit或Volley。

Activity 的onStart与onResume的区别?
onStart是在界面被显示出来的时候执行,不能与之交互;
onResume是在Activity可以与用户交互的时候被执行,用户可以获取到Activity的焦点,能够与之交互。

Activity什么情况下执行onCreate,不执行onStart?
在onCreate中执行finish的时候会立即执行onDestory,其他的生命周期都不执行。
简单理解,onCreate-onDestory,onStart-onStop,onResume-onPause,比如执行了onCreate,最后肯定会执行onDestory的。

ThreadLocal的实现原理?
在Thread中有一个hash结构的ThreadLocal.ThreadLocalMap对象的成员变量threadLocals,threadLocals中以ThreadLocal对象为key,以要隔离的值为value(即调用threadLocal.set(value)中的value)。
ThreadLocal负责管理threadLocals中保存的变量。重点内容

设计模式

1、工厂模式
比如动物,有共同行为:移动move。这里有猫和狗。我们建立一个工厂,去生产猫和狗。
interface A{
public void move();
}
class AFactory{
public static Cat createCat(){
return new Cat();
}

public static Dog createDog(){
return new Dog();
}

}
概念:定义一个创建对象的接口,统一管理,让子类决定实例化哪个类。只有一个工厂类哦。如果项目扩展越来越多,每加一种动物都要修改工厂,违反开闭原则

2、抽象工厂
把生产抽象成一个接口,每个实体类对应一个工厂,同时所有工厂都继承这个接口。扩展性更好。
还是上面的例子。现在要添加生产熊猫。
interface Provider{
void produce();
}

class CatP implements Provider{
public void produce(){
return new Cat();
}
}

class DogP implements Provider{
public void produce(){
return new Dog();
}
}

class PandaP implements Provider{
public void produce(){
return new Panda();
}
}

3、适配器模式
将一个类的接口转成目标需要的接口。让已有功能能够更好的复用,但是过多使用会变得很零乱,不易进行一个整体把控。
比如ListView的adapter适配器:将泛型类型T转换成具体的数据类型结构。

4、建造者模式
将一个复杂的对象构建与它的表示分离,不同的构建过程会产生不同的表示。

1、android事件分发机制
Activity-dispatchTouchEvent
||
PhoneWindow-super dispatchTouchEvent
||
DecorView-super dispatchTouchEvent
||
ViewGroup-dispatchTouchEvent
||
onInterceptTouchEvent true拦截 false不拦截
拦截的话,调用父类的onTouchListener-onTouchEvent
不拦截,遍历子view,调用dispatchTouchEvent,之后步骤从步骤4开始。
||
如果子view的oTouchEvent返回true,表示消费了事件,否则往上传递。
这里写图片描述

举例:程序出现了bug
从上到下:总经理经任务分配给了技术经理,技术经理分配给技术主管,技术主管分配给了部门成员A。
从下到上:成员A解决不了,给主管,主管解决不了给经理,经理解决不来给总经理,最后总经理只能自己处理了。

2、android view绘制过程与加载过程
ViewRootImpl会调用performTraversals,其内部会调用performMeasure、performLayout、performDraw方法
||
performMeasure会调用最外层的viewGroup的measure-onMeasure,onMeasure会调用measureChildren,遍历子view,循环调用measureChild方法,
其内部会用getChildMeasureSpec+父view的MeasureSpec+子view的LayoutParams一起获取本view的MeasureSpec,然后调用子view的measure到onMeasure-setMeasureDimension,
默认返回measureSpec的测试数值,所以继承view进行自定义的wrap_content需要重写。
||
performLayout会调用最外层的viewGroup的layout方法,在该方法中使用setFrame设置view的四个顶点位置。在onLayout方法中确定子view的位置。
如LinearLayout会遍历子view,循环调用setChildFrame-子view.layout。
||
performDraw会调用最外层的viewGroup的draw,其中会调用drawBackground(绘制背景)、onDraw(绘制自己)、diaptachDraw(绘制子view)、onDrawScrollBars(绘制装饰)。

MeasureSpec 2位SpecMode+30位SpecSize组成的32位int。
三种方式获取measure后的宽高:
-Activity的onWindowFocusChange;
-view.post;
-ViewTreeObservable.

3、Activity的加载过程
Activity最终调用startActivityForResult方法-》Instrumentation.execStartActivity和checkStartActivityResult,检查Activity是否启动成功;
最终ActivityThread.performLaunchActivity实现Activity的启动:
-获取启动的Activity的组件信息ActivityRecord;
-创建Activity对象;
-创建Application对象,单例的;
-创建CotextImpl,与context建立联系;
-创建和关联window,创建过程会调用ViewRootImpl的performTraversals初始化view;
-最后,Instrumentation.callActivityOnCreate-performCreate-Activity.onCreate,在其中会调用PhoneWindow的setContentView设置内容视图。

4、Service两种方式
(1)context.startService:onCreate-onStart-running-(如果调用stopService)-onDestory-shut down
-未运行,先onCreate再onStart;
-已运行,只调用onStart;
-调用stopService,直接onDestory;
-只能手动调用stopService关闭服务,不然会一直在后台运行。
(2)context.bindService:onCreate-onBind-running-onUnbind-onDestory-stop
-onBind返回一个IBind接口实例;
-绑定方式,context退出了,就会调用onUnbind-onDestory对应退出;
-onBind,只能绑定一次。

Android LruCache原理

LruCache原理:
维护一个缓存对象列表,按照访问顺序进行对象排列,就是将一直没有访问的对象放到队尾,即将淘汰。而最近访问的对象放在队头,最后淘汰。而这个队列主要是通过LinkedHashMap实现。

LinkedHashMap:(默认为插入排序,即输入与输出顺序一致)
继承于HashMap,使用双向链表来存储Map中的Entry顺序关系。顺序有2,一是访问顺序,一是插入顺序,可以由其构造方法LinkedHashMap(int initialCapacity,float loadFactor,boolean accessOrder:true表示顺序访问,false是插入排序)指定。所以,get,put,remove等操作,它除了做HashMap的操作,还需要调整Entry顺序列表的工作。

Android中的LruCache将LinkedHashMap中的默认顺序设置为访问顺序,每次调用get,则将该对象移到链表的头部,调用put插入新的对象到链表头部。当内存缓存达到最大值时,就将链表尾部的对象移除。每次put或者remove,都需要判断缓存大小是否足够trimToSize。
注:链表头部-最近使用或者插入的;链表尾部-最近很少使用的。

maven与gradle 分包的区别

weex是一套跨平台的框架,使用vue作为表现层框架,实现页面UI。

它的原理:通过编写vue文件,然后编译成一段javascript代码,在weex中成为js bundle;将这个js bundle部署到服务器上,然后通过网络传递到客户端,接着由客户端的weex sdk中的js引擎进行执行js bundle,并且通知native进行渲染。它有自己的生命周期:初始化,创建,渲染,销毁。

因为weex是单页面模式,为了让其与原生相似有个页面栈的概念,好管理页面路径,引入了vue-router路由框架;而且为了代码的可维护性,以及解决共享数据的唯一性和复用性,采用vuex状态树模式进行状态的统一管理。它的模式类似MVVM,数据有变动就会通知UI进行更新,是单向绑定。而且,原生与weex也可以进行交互,主要是通过module方式,实现js方法声明,注册,回调流程,在weex中就可以调用原生方法,并且能够将结果返回给原生。

UI性能分析工具

HV 分析UI布局的复杂度和冗余
Lint 检查UI布局和资源文件的冗余性
traceview 分析方法调运一次的耗费时间和频繁调用累计的时间
anr 分析data/anr/traces.txt

内存泄漏分析

Studio下的memory窗口,看内存波动
DDMS-heap内存检测工具
使用LeakCanary检测:获取RefWatcher观察器,在onDestory中监听当前页面的内存泄漏情况,它会在通知栏中通知,在正式版本要去掉这个检测。

UI性能优化

布局优化:嵌套布局的话,使用RelatviveLayout;使用include复用布局;使用merge减少层次;使用viewstub按需加载;使用weigth的话,宽高设置为0减少运算;存在特别复杂的布局时可以考虑使用自定义View,减少measure和layout的次数。
列表优化:尽量复用getview中的相关view,在滑动中不进行UI刷新;使用viewholder;不做大量的耗时操作,因为getview会被调用多次;数据多的话最好分页加载。
图片尽量压缩,使用bitmapFactory.Options采样;尽量减少不必要的背景设置;
尽量避免在draw中进行耗时操作或者频繁创建临时对象,因为draw会被多次调用的;
不要在UI线程中做耗时操作,避免产生anr。

Handler、Looper、MessageQueue、Message

Handler发送消息,接受并处理消息
Looper:循环获取消息,调用Looper.prepare方法创建了Looper对象,使用ThreadLoacl存储,达到对象复用的效果
MessageQueue:消息队列,在Looper的构造方法中创建的
Message:消息

Handler发送消息,实际上是调用sendMessageAtTime方法。
||
在该方法中,会调用MQ的入列方法enqueueMessage方法:它不是以集合的方式去存储消息,而是通过一个变量mMessages,为每一个待处理的消息指定下一个待处理消息(message.next),是按照时间顺序将消息入列,出列的。
||
接着Looper调用loop方法,进行循环取出消息。有待处理的消息就取出,否则就进入堵塞,直到新消息入列。在该方法中,消息出列,是通过target属性查找到对应的handler,然后调用其dispatchMessage方法,让handler接收到消息,处理消息,这样子就消费了一个message。

HandlerThread

1、继承Thread,是一个线程类;
2、有自己的内部Looper对象,可以进行Loop循环;
3、通过获取Looper对象,传递给Handler,可以在handleMessage方法中执行异步任务;
4、在run方法中,创建了Looper对象(调用Looper.prepare())。

构造方法:线程名称和线程优先级
成员变量:Looper对象
run方法:调用了Looper.prepare方法,该方法中创建了Looper对象,然后获取looper对象(Looper.myLooper()),赋值给mLooper,通过等待唤醒机制保证同步,调用loop方法,实现循环取消息操作。

getLooper方法:这时会先判断Looper对象是否创建完成了,因为Looper创建是在子线程中,而getLooper是在UI线程调用的,为了解决同步问题,所以使用等待唤醒机制来保证只有当线程创建成功且Looper对象也创建成功才能获取mLooper对象。

quit方法:调用Looper的quit方法,表示立马清空消息队列中的消息,不管是延迟消息还是非延迟消息。
quitSafely方法:调用Looper的quitSafely方法,该方法会清空延迟消息,非延迟的消息会派发出去让Handler处理完成了才停止loop循环。

IntentService

1、继承于Service;
2、可以在后台执行耗时的异步任务,当任务完成了就会自动停止;
3、有较高的优先级,不易被系统杀死;
4、内部通过HandlerThread和Handler实现异步操作;
5、重写onHandleIntent方法,它是个异步方法,可以执行耗时操作。

构造方法:线程名称
onCreate:创建HandlerThread对象,并启动该线程。创建Handler(ServiceHandler),将HandlerThread的Looper对象赋值给它,这样ServiceHandler就变成可处理异步任务了。

onStartCommand方法:调用onStart方法。
onStart方法:构造message,由serviceHandler.sendmessage发送消息。

ServiceHandler的handleMessage方法:调用了抽像方法onHandleIntent,和stopSelf(msg.arg1)。
这样就可以说明onHandleIntent是个异步处理方法。stopSelf(msg.arg1)会在消息都处理完成后自动停止服务。

Binder进程间通信流程
首先,我们需要明确的是,谁发消息谁是client,接收消息的则是server。
Binder的组成结构有client,server,Binder驱动和ServiceManager(管理server)。举个例子:电话局就相当于SM,存储着每个住宅的电话。张三(client)打电话给李四(server),由电话局的接线员(Binder驱动)查找这个住宅电话,所以首先李四的电话需要在SM注册过,就能拨通,否则提示该号码不存在。

Client:要调用Server的call方法,但是它们不是在同一个进程的,所以通过Binder来帮忙。
首先,Server在SM注册,然后Client发起请求调用Server的call方法,就需要获取Server对象,但是SM不会把真正的Server返回给Client,而是返回它的代理对象proxy给Client。接着,Client调用Proxy的call方法,SM帮它去调用Server的call方法,并将结果返回给Client。
这里写图片描述

Android中的进程
1、前台进程,用户当前正在做的事需要这个进程。
符合以下条件之一都算前台进程:
这个进程拥有一个正在与用户交互的Activity(就是onResume被调用);
这个进程拥有一个正在与用户交互的Activity上的Service;
这个进程拥有一个正在前台运行的Service(调用了startForeground);
这个进程拥有一个正在执行任何一个生命周期回调方法(onCreate,onStart等)的Service;
这个进程拥有一个正在执行其onReceive方法的BroadcastReceiver。

2、可见进程,拥有一个影响用户所见的,不在前台运行。
这个进程拥有一个不在前台但是可见的Activity(onPause被调用)。比如Activity启动一个对话框,就出现这种情况。

3、服务进程
一个进程不直接影响用户所见,但是通常做着一些用户关系的事。比如播放音乐或者下载数据。

4、后台进程
一个进程拥有一个当前不可见的Activity(onStop被调用)。

5、空进程
一个进程不拥有任何active的组件。主要是为了高速缓存,下次启动同一组件的时候提高启动速度。

Android中常见的线程池
1、FixThreadPool,只有核心线程,并且数量固定,不会被回收,所有线程都活动时,因为队列没有限制大小,新任务会等待执行。
优点:可以更快的响应外界请求。
2、SingleThreadPool,只有一个核心线程,确保所有任务都在同一线程按照顺序执行。
3、CachedThreadPool,只有非核心线程,最大线程数很大,所有线程都活动时,会为新任务创建新线程,否则会利用空闲线程处理任务。注:空闲时间为60s,过了会被回收,所有线程池中存在0个线程的可能。
优点:任务都会被立即执行;比较适合执行大量的耗时较少的任务。
4、ScheduledThreadPool,核心线程固定,非核心线程数(闲着没事干立即被回收)没有限制。
优点:执行定时任务和固定周期的重复任务。

计算一张图片的大小
size = 图高x图宽x一个像素占用的内存大小。要考虑图片所在目录和设备密度,会影响图片的宽高,从而导致图片被拉伸或者压缩。

加载图片避免OOM:使用BitmapFactory.Options进行配置
BitmapFactory.Options相关参数详解
(1).Options.inPreferredConfig值来降低内存消耗。
比如:默认值ARGB_8888改为RGB_565,节约一半内存。
(2).设置Options.inSampleSize 缩放比例,对大图片进行压缩 。
(3).设置Options.inPurgeable和inInputShareable:让系统能及时回 收内存。
A:inPurgeable:设置为True时,表示系统内存不足时可以被回 收,设置为False时,表示不能被回收。
B:inInputShareable:设置是否深拷贝,与inPurgeable结合使用,inPurgeable为false时,该参数无意义。
(4).使用decodeStream代替其他方法。

OkHttp3源码解析

1、基本使用
-创建一个OkHttpClient对象;
-创建一个请求对象Request,使用的是建造者模式;
-创建一个Call对象(client.newCall(request)),执行同步或异步请求,返回一个Response对象。

2、解析
(1)Request:每一个Http请求,包括url,请求方法,http头等内容。
(2)Response:响应是对请求的回复,包括状态码,Http头和主体部分等。
(3)Call:执行请求的容器模型。执行方式有两种:同步、异步。

第一步:使用OkHttpClient创建一个实例对象:
它的构造方法,默认创建一个Builder对象,并进行初始化操作。如设置dispatch,proxy,protocols,interceptors等一系列的属性。

第二步,使用建造者模式创建请求对象Request request = new Request.Builder().url(url).build();
Builder中默认method是Get请求方式。

第三步,抵用client的newCall方法,返回一个Call对象:
在该方法中创建一个RealCall对象(它是Call接口的实现)。

第四步,执行异步请求enqueue:
使用synchronized加锁
检查call是否被执行了,每个call只能被执行一次;如果要再次执行,就克隆一个执行。
最终调用client的dispatcher的enqueue方法真正执行请求,传入AsyncCall对象作为参数。AsyncCall是RealCall的内部类,继续NamedRunnable,而NamedRunnable中有个抽象方法execute,在AsyncCall中进行实现。在execute中,调用getResponseWithInterceptorChain方法,获取响应。成功的话回调onResponse方法,失败就回调onFailure方法。最后调用Dispatcher的finish方法结束请求。

3、重点核心类Dispatcher线程池介绍
该类中,有两个ArrayDeque存储同步和异步请求,有一个单例线程池(核心线程数0,最大线程数不限,线程空闲时间60s)。采用Deque作为缓存,保证先进先出的顺序执行。任务最后都会调用finished方法。

4、重点核心方法getResponseWithInterceptorChain方法
责任链方法:将请求和处理分开,并且可以动态添加中的处理方法对请求进行短路处理等操作。
在配置OkHttpClient时设置的interceptors;
负责失败重试以及重定向的RetryAndFollowUpInterceptor;
负责转换请求和转换响应结果的桥梁BridgeInterceptor;
负责读取缓存,更新缓存的cacheInterceptor;
负责和服务器建立连接的ConnectInterceptors;
设置OkHttpClient时的networkInterceptors;
负责向服务器发送请求数据,从服务器读取数据的CallServerInterceptor。
最后调用RealInterceptorChain的proceed方法。

总结
OkHttp底层实现是通过Scoket发送Http请求和接受请求的,它实现了连接池,即对于同一主机的多个请求,可以共用一个Scoket连接,而不是每次发完Http、请求就关闭底层的Scoket。使用的是OkIo对Scoket进行读写操作。
这里写图片描述
参考:http://blog.csdn.net/mwq384807683/article/details/71173442?locationNum=8&fps=1

NDK开发流程
(一)Java调用C方法

1、安装ndk
在Android Studio的project structure目录,有个Android NDK location设置。未下载ndk的可以点击下方的download下载。
2、配置path路径
在环境变量path中配置ndk的路径。
3、项目中关联ndk
在项目的local.properties中添加ndk路径:

ndk.dir=C:\AndroidDevelop\sdk\ndk-bundle;

在gradle.properties中添加配置,兼容老的ndk:

android.useDeprecatedNdk=true。

4、编写native方法

private native String sayHello();

5、定义对应的jni文件(.h,.c)
使用javah命令。
-进入main目录;
–执行命令:

javah -d jni -classpath
C:\Android\sdk\platforms\android-25\android.jar;E:\Github\TestNdk\app\build\intermediates\classes\debug
com.hacjy.demo.textndk.JniTest

说明:-d jni,生成jni目录;第2个参数是本地sdk的android.jar路径;第3个参数是.class路径,绝对路径,包括包名。
6、生成.h后,创建对应的.c文件
如果遇到编译失败的话,需要创建一个empty.c文件。这个是AS2.2.2bug。
7、指定编译不同的CPU
在app的build.gradle的defaultConfig配置:

ndk{ moduleNae “Hello”//.so文件的名称组成:lib+moduleName+.so abiFilters
“armeabi”,”armeabi-v7a”,”x86”//cpu类型 }

8、编译生成动态链接文件
执行rebuild,生成so文件,存放目录:build\intermediates\ndk\debug\lib
9、调用native方法
先加载so文件:

System.loadLibrary(“Hello”);

再调用方法。

(二)C调用java方法

步骤与java调用c一样,它是使用反射的原理,找到jclass对象,进而找到对应的方法method对象,然后创建类对象调用java方法。
重要的是需要使用javap -s命令查看类中所有方法的签名信息,因为找方法需要用到这个参数。
-查看签名
-进入main目录,执行命令:

javap -s -classpath
C:\Android\sdk\platforms\android-25\android.jar;E:\Github\TestNdk\app\build\intermediates\classes\debug
com.hacjy.demo.textndk.JniTest Compiled from “JniTest.java”

步骤如下:
1、加载类得到jclass对象:

jclass jc = (*env)->FindClass(env,”com/hacjy/demo/JniTest”);

2、得到对应的方法Method对象:

jmethodId method = (*env)->GetMethodID(env,jc,”sayHello”,”()V”);

3、创建类对象:

jobject obj2 = (*env)->AllowObject(env,jc);

4、调用方法:

(*env)->CallVoidMethod(env,obj2,method);

参考自:http://blog.csdn.net/ccg_201216323/article/details/54563825

遇到的技术难点
(怎么产生,如何分析,怎么解决)

1、一般在做设置头像图片功能,我们会对图片进行剪裁,减小图片的大小。 我用真机测试的时候,剪裁后提示”无法保存经过剪裁的图片”。
我的第一反应是不是代码报错了,但是校对了几遍代码,没有问题。
接下来,我又在手机测试了几遍,还是一样的问题。我想是不是手机的问题。所以我换了台手机测试,突然它又好了,没有提示。不同设备导致结果不一样,我想应该是系统导致的。
我的手机是7.0系统,测试机子是6.0系统。
因为7.0对文件读取有设限的问题,我代码中使用的是FileProvider去获取图片的uri的,设置剪裁的时候之前我们都是用Uri.fromFile方法,没有问题,所以我就想着把它替换回去看看,结果问题没有了。


2、做试题展示的时候,遇到一个问题:试题的内容有文本和图片,原生的textview默认是只显示文本的,这样子就导致图片在页面上没有显示出来了。
Textview是可以显示出文本了,我先查了下api,看textview是否支持图片的展示。TextView支持显示Html文本,而Html提供了图片下载的类ImageGetter。我们开启一个线程去下载网络图片,然后使用主线程的handler.post通知UI线程去更新文本,就能解决图文混合的html文本的显示,而且也避免了在主线程访问网络的问题。

kotlin认识

1、简洁。开发相同的功能比起java可以少写很多的代码;
2、安全。在编译期就处理了各种null的情况;
3、函数式编程概念,代码更直接明了(lambdas)
4、高度兼容java,可以继续使用java和kotlin混合开发。

缺点:

1、会导致方法数量增加,因为它会帮忙自动生成getter和setter方法,2、导致编译速度变慢;
3、没有静态修饰语,所以java与kotlin转换的时候,需要自己手动给字段添加JvmField,方法添加JvmStatic,不然会有问题。

sdk封装经验

1、第三方SDK的二次封装
偏向于功能性的封装,比如版本更新组件,使用的第三方平台:蒲公英;推送组件,权限管理等。这样子有利于我们后期更替平台,或者是一些功能性完善之类的,对于调用方来说,它不用管具体的实现方式如何。
2、业务组件的封装
对于一些通用性业务的抽离,独立成子工程,方便管理与复用。
在这个封装过程中,使用了部分的设计模式。比如子工程会向外暴露接口,会需要一些参数数据。传统的做法我们是直接就一个方法参数传递,但是一旦方法参数多了就不明朗了,也不易于扩展,不够灵活。所以我们采用了建造者模式,参数可以进行灵活配置(比如会员与非会员的功能开发不一样等),后期也容易扩展,在不影响已有功能的基础上。

tcp/udp http协议

网络七层:物理数据链路网络传输会话表示应用

ip是位于网络层,tcp是位于传输层。http是位于应用层。
tcp协议是基于ip协议的,两者互补。

tcp协议的三次握手:
【第一次握手】:客户端尝试连接服务器,向服务器发送syn(同步序列编号)包,syn=j,客户端进入SYN_SEND状态等待服务器确认;
【第二次握手】:服务器收到客户端的syn包并确认(ack=j+1),同时向客户端发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
【第三次握手】:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(已建立的)状态,完成三次握手。

scoket是对tcp/ip协议的一种封装,是一个执行单元,提供一个针对tcp或udp编程的接口。

http:基于tcp/ip协议封装的一种协议,它的特点:跟服务器建立连接后,你请求服务器,服务器会给你响应。

tcp与udp区别:打电话与发短信的区别。都是基于ip协议。
打电话:必须对方接通才可以聊天,可信度高;
发短信:只管发不管收不收的到,可信度较低。

http与scoket:http是轿车,scoket是发动机,所以http是把scoket包含进去的一种协议。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值