多线程下指令重排会导致错误
单例为什么要双重验证(即使双重验证后为什么还会出现错误)
juejin.cn/post/684490… juejin.cn/post/684490…
内存泄漏 主要还是因为生命周期不同导致的。
-
判断一个数是奇数还是偶数的方法 答:1. %2;2.&1(转成二进制后如果末尾是1就是奇数,所以进行与运算后,结果是00001的话就是奇数);3.if(((x>>1)<<1) == x){ return 0; //是偶数 }
-
LinkList和ArrayList的区别 答:
3. 安卓的数据持久化方法有哪些
sharedPreferences、数据库、外部文件
- 哪些情况会内存泄漏 由于引用和生命周期不一致造成的,比如非静态匿名对象默认引用外部对象,比如由于非静态匿名对象handler的引用,导致外部对象无法回收,解决办法:静态对象+弱引用。因为当jvm垃圾回收时,判断一个对象只含有弱引用,仍会回收该对象。
Native
native关键字声明的方法是在c中定义的,本地方法栈会为执行native服务。 System.load(需要传入so库绝对路径)和System.loadlibrary(System.loadlibrary还会调用findlibrary在系统库和此app的库中寻找此名字的so库)可以导入so库。
nativeMethod()->JNI_Onload
实现native函数有两种方式:1.静态注册;2.动态注册。 静态注册:根据包名和函数名命名实现函数名称,若有重载,则在函数名称后面追加两个下划线和参数描述符。 动态注册:使用RegisterNatives(...)函数
- 序列化后能干什么,安卓序列化和java序列化有什么区别
tcp三次握手从第几次可以携带数据
第三次
jvm内存分区
如果要制造栈溢出该怎么做,为什么会栈溢出
可以无限递归实现栈溢出,因为每次递归都会压栈,栈容量有限,超过后就会栈溢出
制造堆溢出
申请一块非常大的数组空间,由于无法满足所申请的空间大小,导致堆溢出
Sql执行过程
hashcode与eauals
当两个对象使用“==”比较时,会先调用hashcode如果为true再调用eauals juejin.cn/post/684490…
double、float精度损失
dart中string是基本数据类型,可以直接使用“==”比较,而不用像java那样使用equals()
tcp握手挥手时ack为何要加1
加1能保证是对上一次的请求,而不是对上上次或者其他的请求的回应。
requiredz只能用在被{}包裹的参数列表中,对参数赋初始值也只能用在{}或[]参数列表中,配置参数时[]和{}不能同时存在
dart中,初始化列表只能存在于重定向构造函数和构造函数中,工厂函数或者带有factory修饰的工厂重定向构造函数不能有初始化列表
dart中加‘_’可修饰为private,但是dart中的private是同一包内可以访问,而不是只有class内部可以访问
如果类中存在构造函数无法被混入
在子类对象中,调用父类的函数,这个父类函数中又调用了子类覆盖的同名函数,则实际上还是会调用子类中的覆盖的函数。如果在该覆盖函数前加一个this那么就会不会调用子类的函数了。
如:
class test
{
void f()
{
print("test");
p();
如果这里是this.p(),那么就会输出test p。
}
void p()
{
print("test p");
}
}
class test2 extends test{
void f()
{
super.f();
}
@override
void p()
{
print("test2 p");
}
}
test2().f();
//输出:
test
test2 p
调用
Widget更新过程,如果是第一次创建而非更新的话是不会进入到update相关的函数
在performRebuild里面调用重新调用build(),并把_dirty置为false方便下次更新重新build,然后调用child的updateChild;在updateChild里面调用child的update,update完child之后会调用inflateWidget()用来重新创建element并调用新element的mount,这些步骤完成后回到了update,在update里面又调用了rebuild,在rebuild里面调用performRebuild;
在activity的onResume之前是可以在子线程中更新UI的,因为那时的ViewRootImpl还没有启动,启动后在每次的更新UI时都会检查是否在主线程。
String 为什么设计成不可变的?
扩展:可变字符串
StringBuilder类 StringBuilder:用于表示可变字符串的一个类,是非线程安全的,建议在单线程环境下使用,效率略高于StringBuffer。
StringBuffer类 StringBuffer:用于表示可变字符串的一个类,是线程安全的,建议在多线程环境下使用,效率略低于StringBuilder。因为其中方法使用synchronized修饰为同步方法,所以在多线程的情景下,使用StringBuffer。
list和map实现方式及存储方式、hashmap原理
为什么looper循环不会阻塞主线程
在Looper.loop()里面有一个死循环,一直在获取handler发来的消息,当获取消息时会执行MessageQuene里面的next(),如果此时没有消息需要处理,next()中的nativePollOnce函数会使循环停止在这里,并且不会占用cpu(如果有新消息进来就会唤醒该线程),所以主线程的looper循环并不会一直占用cpu。所以死循环并不是导致主线程卡多的真正原因, 真正的原因handleMessage里面执行耗时操作导致后面的事件没有得 到及时的处理。
一个looper有一个MessageQueen,所有使用相同looper的handler都会持有该looper的queen,也就是handler不会创建MessageQueen,只会使用looper的。
Looper.quit()
基于回收资源或避免内存泄漏的考虑,在 Thread 完成任务后应当手动 quit。主线程极为重要,承载着 ContentProvider、Activity、Service 等组件生命周期的管理,即便某个组件结束了,它仍有继续存在去调度其他组件的必要! 主线程 ActivityThread 创建 Looper 时指定了不允许 quit 的标志,即不可以手动调用 quit。
void quit(boolean safe) {
//mQuitAllowed在ActivityThread创建lopper时设置了false,所以当主线程使用quit()时会报错
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
.......
}
dart中mixin
如果混入了多个类,每一次super都会从后往前调用混入的类,实例对象是WidgetsFlutterBinding,他里面的instance()会调用super.initInstances(),首先调用的是WidgetsBinding里面的initInstances(),然后WidgetsBinding.initInstances()里面的super.initInstances()调用的RendererBinding.instance()里的initInstances()
在函数中,局部变量被基本数据类型赋值后并更改局部变量的值,原基本数据类型的值不变,但是如果是原类型是引用类型,会修改原类型的值
demo[] a= {new demo(1),new demo(2),new demo(3)};
int f[] = {1,2,3};
void fun()
{
demo b = a[1];
b.a = 6;
System.out.println(a[1].a);//输出6
demo b = a[1];
b = a[2];
System.out.println(a[1].a);//输出1,因为只是改变了b的引用地址值,而地址指向的对象内容没有改变
int d= f[1];
d = 6;
System.out.println(f[1]);输出2
int[] d = f;
d[1]=3;
System.out.println(f[1]);//输出3,因为直接d 和f在堆栈中的引用类型地址是一样的,因为数组不是基本类型,所以d[1]直接操作的是f[1]的真实地址。
}
HashMap
put过程
使用hash()函数计算key的hash值,和value一并传入putVal(),在putVal()中根据hash & (table.length-1)计算在table中的位置,如果为null,则新建node节点,如果不为null,判断key值是否相等,如果相等,则更新value值,如果key值不相等,则判断是否是treenode(红黑树),是的话遍历红黑树继续判断是否有相同的key进行更新或者插入,如果不是treenode,那么就是链表,遍历链表是否有相同的key,有的话更新,没有则在链表尾部插入新节点。
每次put完后都会判断占用大小是否超过阈值,超过的话就会执行扩容函数resize();
一开始遇到的问题:即使不同的key也可能能映射到table中的相同位置,这就是为什么存在链表或者红黑树的原因。
扩容
扩容调用的是resize函数,tanle数组的大小只会是2的整数幂,这也是为什么可以用hash & (table.length-1)来代替取余%提高计算速度。
如果链表的大小大于8则会检查是否扩容或者转成红黑数,如果table大小超过64则会转成红黑树,反之进行扩容。
扩容过程:超过最大长度2的30次方就不会扩容,如果数组长度扩大二倍后仍小于2的30次方,则把阈值也扩大到原来的二倍。
Android View事件分发
www.jianshu.com/p/068b57d79… 首先调用viewgroup的dispatchTouchEvent(),先判断是否是按下事件或者firstTouchTarget是否为null,如果是的话在判断是否被禁止拦截,如果没被禁止在调用是否拦拦截的方法;没有被拦截的话就调用子view的dispatchTouchEnent,如果子view为空的话,调用自己的ontouch,如果ontouch返回true则表示消耗,不调用ontouchEvent,如果ontouchEvent被调用返回true和ontouch返回true效果一样都表示事件不再向下传递;如果子view不为空,则调用子view的dispatchTouchEnent,过程和上面一样。在ontouchEvent中先是判断是否是longClick,然后是click。
每套事件有一个触控点id,触控点id不会改变, 从手指触摸到离开的时间段,id都不会改变。
Tcp握手和挥手
https默认端口是443,http是80。
握手
1.客户端请求连接SYN 生成一个随机seq=0(seq=0,其中0是相对的数字,原始的seq其实是一个10位的随机数字串),此时ack是0,而且原始的ack数字串也是0; 2.服务端收到客户端的SYN请求后,在服务端会生成一个随机seq数字串,此时seq也是相对是0,也会生成一个随机的ack数字串,ack的相对值是1; 3.客户端收到服务端的ack请求后,客户端会把SYN请求生成的seq值加一,也就是相对值变成了1,ack值是服务端发过来的seq加1,则ack的相对值是1 在握手完之后,如果是https的话,客户端还会发送一个TLS协议请求ClientHello的握手协议,Server收到后会用TLS回复ServerHello的握手协议。 tls加密过程:客户端clienthello里面包括自己支持的加密套件和一些tls的选项, 服务端返回serverhello里面包括自己的证书和对客户端要求的证书、自己的公钥和让客户端使用的加密套件, 客户端收到后,会使用证书和公钥通过加密套件加密随机值并发送出去 服务端使用私钥解密并把解密后的值发送出去让客户端验证。
挥手
1.客户端发送FIN 2.服务端收到后时间发送ACK 3.服务端继续发送还没有传完的数据后发送FIN+ACK 4.客户端返回ACK 存在三次挥手的问题:因为第二步等待一段时间后发送Ack可能会与第3步FIN同时接收FIN+ACK包,这个时候,在客户端看来就是三次挥手了。
AsyncTask
由线程池和Hnadler实现,当然也包括looper
线程创建方式
1.继承Thread,重写run方法 2.实现Runnable、Callable(这个可以获取返回结果) 3.AsyncTask 4.handler机制 5.HandlerThread 6.线程池:newFixedThreadPool(适用于密集型、执行时间长的任务、keepAliveTime为0)、newCachedThreadPool(适用于并发大、执行时间小的任务)、newSingleThreadExcutor(串行执行的任务)、newScheduledThreadPool(定期执行、限制线程数量、 keepAliveTime为0)
几个核心参数的作用:
- corePoolSize: 线程池核心线程数最大值
- maximumPoolSize: 线程池最大线程数大小
- keepAliveTime: 线程池中非核心线程空闲的存活时间大小
- unit: 线程空闲存活时间单位
- workQueue: 存放任务的阻塞队列
- threadFactory: 用于设置创建线程的工厂,可以给创建的线程设置有意义的名字,可方便排查问题。
- handler: 线城池的饱和策略事件,主要有四种类型。
核心线程和非核心线程的区别
1.如果不执行线程池的shutDown方法且没有调用设置核心线程空闲存活时间,那么只会回收非核心线程,不会回收核心线程,核心线程会一直阻塞 2.调用shutDown方法后,所有线程都会回收,这也是为什么大多数我们调用线程池的 execute方法后,会调用shutDown方法
switch如果不break的话,会出现向下穿透的情况。
进入主线程更新ui的几种方式
下面这几个都不是静态方法
- Activity.runOnUiThread(Runnable)
- View.post(Runnable)
- View.postDelayed(Runnable, long)
- handler机制
子线程更新UI
更新UI的过程:会调用requestLayout 由于ViewRootImpl是视图的父类,由于层级关系也会调用ViewRootImpl的requestLayout
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
//检查是否在同一个线程,也就是主线程
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
----------------------------------
void checkThread() {
//mThread是主线程的引用
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
在onResume之前,由于ViewRootImpl还没有创建,所以可以在子线程更新UI。 onResume执行完之后,紧接着就创建了ViewRootImpl(这一过程由ActivityThread的perfromResumeActivity执行)并把ViewRootImpl加入到了View层级关系中来,所以没创建ViewRootImpl时,由于事件机制并不会调用他的requestLayout,也就不会检测。
Instrumention Application创建时机
都是在ActivityThread的handleBindApplicion中创建的,且Instrumentation先创建的。 然后在创建好的Instrumentation的方法newApplication通过反射创建Application。紧接着又调用了该application的onCreate方法。
view中invalidate()方法会使viewrootimpl重新开始view的measure、layout、draw执行。
main函数执行完,打印线程也结束了,在main里面创建的子线程还没有结束,但是有打印行为也看不到了,因为打印线程结束了。
>https://www.cnblogs.com/Deng-23-binb/p/16379423.html
activity生命周期
当启动第二个activity时,会首先执行第二个activity的oncreate方法,然后执行第一个activity的onstop方法,也就是第一个activity仍然可见时,第二个activity的oncreate就执行了。activity不可见是在onstop执行完之后。