面试所问到的知识点
- jvm内存结构
- JVM虚拟机了解吗
- 说一下强引用 软引用 弱引用 虚引用
- 虚引用与软引用和弱引用的一个区别
- 线程的几种状态
- 线程同步
- java排序
- Thread类中的start()和run()方法有什么区别?
- wait和notify的区别
- 抽象类和接口的区别
- java的常用集合
- 对线程池的理解
- activity启动过程
- Activity的四种启动模式、应用场景?了解哪些Activity常用的标记位Flags吗?
- Fragment最多嵌套多少层,生命周期又是怎样去控制,子Fragment怎样去管理他的状态
- Fragment懒加载
- Android的事件分发,然后会举个例子让你解决问题。
- 滑动冲突
- binder机制
- 简历上的源码:rxJava源码、retrofit源码、okhttp源码、glide源码、热修复源码、Leakcanary原理、Butterknife的原理等等
- DexClassLoader和PathClassLoader的区别
- 热修复框架的区别(回答完热修复会问)
- rxjava的操作符
- Rxjava的背压技术
- rxJava怎样处理异步线程的
- dagger2的父子adapter怎么样去实例化
- Handler原理。消息队列的类型是什么
- 主线程中的Looper.loop()一直无限循环为什么不会造成ANR?
- ANR定位工具
- 怎么避免ANR
- 怎么精致化APK
- webview的优化
- 子线程怎么初始化一个looper handler
- 子线程与子线程的通信
- view绘制流程,刷新逻辑
- 自定义view的调用流程
- Android获取view的宽高;Activity在哪个生命周期可以拿到宽高
- View.post和handler.post的区别
- requestlayout调用流程,和invalide区别
- MMKV和sharepreference
- sharepreference里面的apply方法和commit有什么区别?
- 对MVP、mvvm自己的理解
- ANdroid进程间通信
- Android打包不可混淆哪些资源
- Tcp三次握手连接和四次挥手断开过程详解
- http和https的区别
- webview加载https注意什么
- 了解websocket吗
- 说一下tcp/ip协议
- 说一下kotlin的Coroutine协程
- 说一下kolin的backing field
- 准备五六个设计模式
- 安卓各个版本的不同
JVM虚拟机-java虚拟机
blog.csdn.net/qq_41701956… github.com/LixyAndroid…
jvm内存结构
程序计数器:虚拟字节码指令的地址
- 内存空间小,它可以看成当前线程所执行的字节码的行号指示器。字节码解释器工作时通过程序计数器来选取下一个执行的字节码。分支、循环、跳转、异常处理等都需要依赖程序计数器来完成。
- 为了确保线程切换后能恢复到正确的执行位置,每个线程都需要有独立的程序计数器,各条线程之间计数互不影响,即线程私有内存。
- 线程如果正在执行一个java方法,那么这个计数器记录的是正在执行的虚拟机的字节码指令的地址;如果执行的是native方法,则值为Undefined。
- 此区域是唯一一个在java虚拟机规范中没有规定何OutOfMemoryError情况的区域。
Java虚拟机栈:java方法(局部变量表、操作数栈、动态链接、方法出口)
- 线程私有,生命周期和线程一致。
- 描述的是 Java 方法执行的内存模型:每个方法在执行时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行结束,就对应着一个栈帧从虚拟机栈中入栈到出栈的过程。
- 当线程请求的栈深度大于虚拟机所允许的深度,会抛出StackOverflowError;当虚拟机栈可以动态扩展时,如果扩展时无法申请到足够的内存,则会抛出OutOfMemoryError异常。
本地方法栈:native方法
- 区别于 Java 虚拟机栈的是,Java 虚拟机栈为虚拟机执行 Java 方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。
- 也会有StackOverflowError 和 OutOfMemoryError 异常。
方法区:类信息、常量、静态变量、即时编译器编译后的代码
- 各个线程共享的内存区域,用来存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。
- 当方法区无法满足内存分配需要的时候,将抛出OutOfMemoryError异常。运行时常量池属于方法区的一部分,用于存放编译期间生成的各种字面量的符号引用,这部分内容将在类加载后进入方法区的运行时常量池存放。在运行期间也可能有新的常量放入常量池中,例如通过String.intern()方法。
Java堆:对象实例、数组
- 这块区域是 JVM 所管理的内存中最大的一块。线程共享,主要是存放对象实例和数组。内部会划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer, TLAB)。可以位于物理上不连续的空间,但是逻辑上要连续。
java内存结构
java是在JVM所虚拟出的内存环境中运行的,内存分为三个区:堆、栈和方法区。
- 栈(stack):是简单的数据结构,程序运行时系统自动分配,使用完毕后自动释放。优点:速度快。
- 堆(heap):用于存放由new创建的对象和数组。在堆中分配的内存,一方面由java虚拟机自动垃圾回收器来管理,另一方面还需要程序员提供修养,防止内存泄露问题。
- 方法区(method):又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。
引用计数器算法
堆中每个对象都有一个计数器,当对象被被其他对象引用时,计数器+1,当引用失效时-1,如果引用计数器为0时,该对象就会被回收。 缺点:当两个对象互相引用,而他们两个没有被用到时,这两个对象不会被回收。
可达性分析算法
以GCRoot作为起点开始搜索,在引用链上的对象不可被回收,不在引用链上的对象可以被回收。
哪些可以作为GCRoot对象呢?
- 活的线程
- 方法区中的静态变量和常量引用的对象
- 虚拟机栈(栈帧中的本地变量表)中引用的对象
- 本地方法栈中JNI(即一般说的native方法)引用的对象
retrofit源码
- retrofit使用时创建retrofit对象,然后创建一个网络请求接口。
- 创建对象的时候,使用构造者模式,增加网络请求适配器工厂(addCallAdapterFactoty),把对象进行平台适配,比如如果用rxjava的话,就会传RXjavaCallAdapterFactoty.creat(),这个对象其实就是创建了一个带参数的RXjavaCallAdapterFactoty对象。
- 然后,增加数据转换器工厂(addConverterFactory),为retrofit添加合适的转化器,默认是用BuiltInConverters转化器,如果我们用gson的话就必须添加gsonConverterFactory.create(),这个对象其实是创建了一个带有gson对象的gsonConverterFactory实例。
- 创建retrofit实例之后,retrofit会使用代理模式,通过Proxy.newProxyinterface创建代理,当网络接口方法被调用的时候,会调用newProxyinterface里面的invocationHandler接口的invoke方法。 在invoke方法里面,会将接口方法、方法注解、类型等信息封装到servicemethod对象里面,然后根据servicemethod和接口方法参数创建retrofit的call对象:okhttcall,最后,servicemethod将okhttcall转化成各个平台的call。
- 整个过程,请求网络的对象还是okhttp的call来完成,retrofit负责请求前将参数注解方法解析,请求后将网络数据转化成我们所需要的java对象。
热修复源码
Handler原理,消息队列的类型
- 主线程main()在创建的时候,会默认调用looper.prepareMainLooper()方法,他的目的是创建一个属于主线程的looper对象,和MessageQueue对象。
- 在handler创建实例的时候,指定了looper对象,因为looper对象绑定了线程,所以handler也绑定了looper所在的线程,然后复写handleMessage方法以便于处理消息的回调。
- 在子线程中创建Message对象的时候,有两种方法,一种是new Message(),另一种的话是Message.obtin(),Message维护了一个消息池,通过obtain获取,是从消息池里面获取,获取不到再创建,建议obtin,避免每次new出来都重新分配内存
- 子线程调用handler.sendMessage(message),获取到消息队列,内部最终会获取到handler所在线程的消息队列,然后调用MessageQueue.enqueueMessage()方法,把handler对象赋值给Message消息的target属性,然后将handler发送的消息加入到消息队列。
- 主线程会调用looper.loop()方法,他无限循环,取出从消息队列中取消消息msg,并且调用一个handler的实例target的dispatchMessage()方法( msg.target.dispatchMessage(msg);),将消息通过handleMessage方法或者handleCallback()里面的run方法分发出去。
- 消息队列的类型:单链表类型,以时间顺序存储,取的时候从表头取。
Thread避免ANR
- 非静态内部类或者匿名内部类默认持有外部类的引用。
- thread实现是匿名内部类或者内部类。 工作线程在处理任务,这时候外部类销毁的时候,由于工作线程的实例持有外部类引用,将使外部类无法GC回收,从而导致ANR。
避免ANR:
- 方法1:因为静态内部类不默认持有外部类的引用,所以可以将thread设置成静态内部类。
- 方法2:当外部类结束生命周期时,强制结束线程。在onDestroy里面调用Thread.stop();
handler避免ANR
- 原因:主线程的Looper对象的生命周期 = 该应用程序的生命周期 在Java中,非静态内部类 & 匿名内部类都默认持有外部类的引用
- 在handler中如果消息没有处理完,handleer中的message对象持有handler引用,非静态内部类/匿名内部类handler持有activity的引用,当activity destroy的时候,将无法回收。
避免ANR:
- 方法一:将非静态内部类换成静态的
- 方法二:在activity销毁的时候把消息清空,调用mHandler.removeCallbacksAndMessages(null);
子线程怎么初始化一个looper handler
- 方法一 :在子线程中调用looper.prepare(); looper.loop(); 在子线程获取当前的looper对象:Looper.myLooper()
private void createHandler(){
new Thread(new Runnable() {
@Override public void run() {
Looper.prepare();
Handler andler = new Handler(Looper.myLooper());
Looper.loop();//传入子线程Looper
}
}).start();
}
//推送任务到消息队列
private void postRun(){
mHandler.post(new Runnable() {
@Override public void run() {
//执行耗时操作
SystemClock.sleep(5000);
//更新UI
MainActivity.this.runOnUiThread(new Runnable() {
@Override public void run() {
actTextReslt.setText("完成操作");
}
});
}
});
}
- 方法二:HandlerThread(内部创建消息队列,外部通过handler通知HandlerThread执行)
private Handler mHandler;
//创建子线程handler
private void createHandler(){
HandlerThread handlerThread = new HandlerThread("myHandlerThread", Process.THREAD_PRIORITY_BACKGROUND);
handlerThread.start();//必须开启
mHandler = new Handler(handlerThread.getLooper());
}
//推送任务到消息队列
private void postRun(){
mHandler.post(new Runnable() {
@Override public void run() {
//执行耗时操作
SystemClock.sleep(5000);
//更新UI
MainActivity.this.runOnUiThread(new Runnable() {
@Override public void run() {
actTextReslt.setText("完成操作");
}
});
}
});
}
子线程与子线程的通信
接受信息的子线程声明handler,记得写looper,传递信息的子线程发送消息就好。 blog.csdn.net/yh186681971…
ANR定位工具
- MAT(Memory Analysis Tools)
- Heap Viewer
- Allocation Tracker
- Android Profiler as3.0以后代替Memory Monitor
- LeakCanary
获取view的宽高
- 通过onWindowFocusChanged方法
- 通过View.post()来实现
- 通过ViewTreeObserver的OnGlobalLayoutListener回调
通过onWindowFocusChanged方法
Activity的窗口得到焦点时,View已经初始化完成,此时获取到的View的宽高是准确的
public class GetHeightSampleActivity extends AppCompatActivity {
TextView textView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_get_height);
textView = findViewById(R.id.tv);
}
//通过onWindowFocusChanged方法
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
Log.w("tv_width", "" + textView.getWidth());
Log.w("tv_height", "" + textView.getHeight());
}
}
}
通过ViewTreeObserver的OnGlobalLayoutListener回调
viewtree有任何变化都会调用此方法
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_get_height);
textView = findViewById(R.id.tv);
final ViewTreeObserver viewTreeObserver = textView.getViewTreeObserver();
viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
viewTreeObserver.removeOnGlobalLayoutListener(this);
Log.w("tv_width", "" + textView.getWidth());
Log.w("tv_height", "" + textView.getHeight());
}
});
}
通过View.post()来实现
View.post()会等到view绘制完才会回调回来
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_get_height);
textView = findViewById(R.id.tv);
textView.post(new Runnable() {
@Override
public void run() {
Log.w("tv_width", "" + textView.getWidth());
Log.w("tv_height", "" + textView.getHeight());
}
});
}
线程的几种状态
1.新建(NEW)
新建状态,线程创建且没有执行start方法时的状态
2.可运行(RUNNABLE)
可运行状态,线程已经启动,但是等待相应的资源(比如IO或者时间片切换)才能开始执行
3.运行(RUNNING)
可运行状态的线程获取了CPU,执行程序代码。
4.阻塞(BLOCKED)
阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。
阻塞的情况分三种:
1.等待阻塞:
运行(running)的线程执行o.wait()方法,JVM会把该线程放入等待队列(waitting queue)中。
2.同步阻塞:
运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中。
3.其他阻塞:
运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行(runnable)状态。
5.死亡(DEAD)
线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
Thread类中的start()和run()方法有什么区别?
start()方法被用来启动新创建的线程,而且start()内部调用了run()方法,这和直接调用run()方法的效果不一样。当你调用run()方法的时候,只会是在原来的线程中调用,没有新的线程启动,start()方法才会启动新线程。
线程同步
用synchronized关键字。因为java为每一个对象都设置了内置锁,当使用synchronized关键字修饰方法时,内置锁就会保护这个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。
- 锁非静态方法:锁住的是当前对象
- 锁静态方法,此时如果调用该静态方法,将会锁住整个类。
- 锁代码块:同步是一种高开销的操作,因此应该尽量减少同步的内容。通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。
//锁代码块的几种情况
public void test(){
//传this,是对象锁
synchronized (this) {
// TODO
}
}
public void method2(){
//传类,类锁。作用:这个类的所有对象
synchronized (ObjectLock.class) {
try {
System.out.println("do method2..");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private Object lock = new Object();
public void method3(){ //任何对象锁
synchronized (lock) {
try {
System.out.println("do method3..");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
wait和notify
- wait():使一个线程处于等待状态,并且释放所持有的对象的lock。wait()使用notify或者notifyAlll或者指定睡眠时间来唤醒当前等待池中的线程。
- sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。
- notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。
- Allnotity():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。
事件分发
upload-images.jianshu.io/upload_imag…
APK精致化
- svg矢量图:如果有大量套图问题的话可以使用svg定义xml图形,他是可缩放的矢量图可以配置兼容哪些套图(vectorDrawables.generatedDensities('xhdpi','xxhdpi')),在安卓中是通过vectory来对svg进行支持的,所以使用的时候先将svg转换成android的vectory
- 图片转化成webp格式:webp格式的体积更小,质量损失小到可以忽略不计。
- tint着色器:只有颜色不同的图片,可以用tint着色器减少对图片的需求量,减少apk体积。
- build里面配置resConfigs('zh-rCN','ko') ,可以只保留两国语言:apk里面有resources.arsc文件,string里面包含很多国家的语言,可以设置resConfigs,只保留自己所需要的语言。
- so库配置:当引入so库的时候 modle的build.gradle会引入jniLibs.srcDirs=['libs'],这样打出来的apk特别大,可以在defaultConfig里面配置ndk {abiFilters('armeabi','armeabi-v7a)},不适用于定制化开发(eg:PDA设备)、内部开发要求版本性兼容性高的。
- 移除无用资源:利用AS: Analyze,Run inspection by Name,unused resource
- 代码混淆
- 启用shrinkResources资源缩减:(设置shrinkResources true)它不需要物理上的压缩,可以自定义要保留的资源,在路径res---raw---keep.xml。
- 微信资源混淆工具AndResGuard+7zip压缩:微信资源混淆主要为了混淆资源ID长度(例如将res/drawable/welcome.png混淆为r/s/a.png),同时用7zip压缩可以减少apk提交,提升反破解的难度
requestlayout调用流程,和invalide区别
- requestlayout()调用父容器的requestlayout(),层层向上,直到Decorview也就是根view,根view传递给ViewRootImpl接受处理,ViewRootlImpl会调用measure()、layout()、draw()三大流程,对每一个有标记位的view及他的子view都进行measure()、layout()、draw()。
- invalidate()最终会调用view的invalideInternal(),他会判断是否重新绘制,为view设置标记位,把重新绘制的区域传递给父容器,父容器计算得出自身需要绘制的区域,直到传到ViewRootImpl中国,最终触发performTravserals()对view进行重绘制。
区别
invalidate():
- invalidate()不会导致measure()和layout()被调用,父view不会执行draw()
- viewgroup调用invalidate()会使子view调用draw()
- invalidate()必须在ui线程中调用,如果非ui线程调用则用postinvalidate()
- 如果viewgroup它的子view变化,调用incalidate(),因为对viewgroup而言,他的属性没变化
requestlayout()
- 子view布局发生变化时,父布局会变化。这个方法不能在正在布局的时候调用。
- 调用这个方法导致布局重绘,调用measure()、layout()、draw()
view绘制流程,刷新逻辑
blog.csdn.net/weixin_4309… www.jianshu.com/p/a5ea8174d…
滑动冲突
sharepreference里面的apply方法和commit有什么区别?
SharedPreference 相关修改使用 apply 方法进行提交会先写入内存,然后异步写入磁盘,commit 方法是直接写入磁盘。如果频繁操作的话 apply 的性能会优于 commit,apply会将最后修改内容写入磁盘。 但是如果希望立刻获取存储操作的结果,并据此做相应的其他操作,应当使用 commit。
MVP自己的理解
用法:www.jianshu.com/p/ae0b21d32… blog.csdn.net/vector_yi/a… 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交互的中间纽带(Model对象调用其方法),处理与用户交互的负责逻辑。
优点
- 降低耦合度,实现了Model和View真正的完全分离,可以修改View而不影响Model。
- 模块职责划分明显,层次清晰。
- 隐藏数据。
- Presenter可以复用,一个Presenter可以用于多个View,而不需要更改Presenter的逻辑(当然是在View的改动不影响业务逻辑的前提下)。
- 利于测试驱动开发。以前的Android开发是难以进行单元测试的(虽然很多Android开发者都没有写过测试用例,但是随着项目变得越来越复杂,没有测试是很难保证软件质量的;而且近几年来Android上的测试框架已经有了长足的发展——开始写测试用例吧),在使用MVP的项目中Presenter对View是通过接口进行,在对Presenter进行不依赖UI环境的单元测试的时候。可以通过Mock一个View对象,这个对象只需要实现了View的接口即可。然后依赖注入到Presenter中,单元测试的时候就可以完整的测试Presenter应用逻辑的正确性。
- View可以进行组件化。在MVP当中,View不依赖Model。这样就可以让View从特定的业务场景中脱离出来,可以说View可以做到对业务完全无知。它只需要提供一系列接口提供给上层操作。这样就可以做到高度可复用的View组件。 代码灵活性
缺点
- Presenter中除了应用逻辑以外,还有大量的View->Model,Model->View的手动同步逻辑,造成Presenter比较笨重,维护起来会比较困难。 由于对视图的渲染放在了Presenter中,所以视图和Presenter的交互会过于频繁。
- 如果Presenter过多地渲染了视图,往往会使得它与特定的视图的联系过于紧密。一旦视图需要变更,那么Presenter也需要变更了。 额外的代码复杂度及学习成本。
- MVP在实现代码简洁的同时,额外增加了大量的接口、类,不方便进行管理,于是Contract就登场了
public interface MainContract {
interface Model {
Flowable<BaseObjectBean<LoginBean>> login(String username, String password);
}
interface View extends BaseView {
@Override
void showLoading();
@Override
void hideLoading();
@Override
void onError(Throwable throwable);
void onSuccess(BaseObjectBean<LoginBean> bean);
}
interface Presenter {
/**
* 登陆
*
* @param username
* @param password
*/
void login(String username, String password);
}
}
单例模式
饿汉式 (可用)
就是在类定义的时候就实例化了(因为饿,主观能动性强 - . -)。
public class Singleton {
private final static Singleton INSTANCE = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return INSTANCE;
}
}
普通的懒汉式 (线程不安全,不可用)
这种写法有个致命的问题,就是多线程的安全问题。假设对象还没被实例化,然后有两个线程同时访问,那么就可能出现多次实例化的结果,所以这种写法不可采用。
public class Singleton {
private static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
同步方法的懒汉式 (可用)
这种写法是对getInstance()加了锁的处理,保证了同一时刻只能有一个线程访问并获得实例,但是缺点也很明显,因为synchronized是修饰整个方法,每个线程访问都要进行同步,而其实这个方法只执行一次实例化代码就够了,每次都同步方法显然效率低下,为了改进这种写法,就有了下面的双重检查懒汉式。
public class Singleton {
private static Singleton instance = null;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
双重检查懒汉式 (可用,推荐)
这种写法用了两个if判断,也就是Double-Check,并且同步的不是方法,而是代码块,效率较高,是对第三种写法的改进。为什么要做两次判断呢?这是为了线程安全考虑,还是那个场景,对象还没实例化,两个线程A和B同时访问静态方法并同时运行到第一个if判断语句,这时线程A先进入同步代码块中实例化对象,结束之后线程B也进入同步代码块,如果没有第二个if判断语句,那么线程B也同样会执行实例化对象的操作了。
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
静态内部类 (可用,推荐)
因为类的静态属性只会在第一次加载类的时候初始化,也就保证了SingletonInstance中的对象只会被实例化一次,并且这个过程也是线程安全的。
public class Singleton {
private Singleton() {}
private static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
Activity的四种启动模式、应用场景?了解哪些Activity常用的标记位Flags?
-
standard:
-
singleTop:说明:分两种处理情况:须要创建的Activity已经处于栈顶时,此时会直接复用栈顶的Activity。不会再创建新的Activity;若须要创建的Activity不处于栈顶,此时会又一次创建一个新的Activity入栈,同Standard模式一样。 应用:假设你在当前的Activity中又要启动同类型的Activity,此时建议将此类型Activity的启动模式指定为SingleTop,能够降低Activity的创建,节省内存!
-
singleTask: 若须要创建的Activity已经处于栈中时,此时不会创建新的Activity,而是将存在栈中的Activity上面的其他Activity所有销毁,使它成为栈顶。 应用:主页
-
singleInstance
注意:复用Activity时的生命周期回调 这里还须要考虑一个Activity跳转时携带页面參数的问题。
由于当一个Activity设置了SingleTop或者SingleTask模式后,跳转此Activity出现复用原有Activity的情况时,此Activity的onCreate方法将不会再次运行。onCreate方法仅仅会在第一次创建Activity时被运行。
而一般onCreate方法中会进行该页面的数据初始化、UI初始化,假设页面的展示数据无关页面跳转传递的參数,则不必操心此问题,若页面展示的数据就是通过getInten() 方法来获取,那么问题就会出现:getInten()获取的一直都是老数据,根本无法接收跳转时传送的新数据!这时我们须要另外一个回调 onNewIntent(Intent intent)方法。此方法会传入最新的intent,这样我们就能够解决上述问题。这里建议的方法是又一次去setIntent。然后又一次去初始化数据和UI。
启动模式的两种使用方式
1.在 Manifest.xml中指定Activity启动模式
2.启动Activity时。在Intent中指定启动模式去创建Activity
Intent intent = new Intent();
intent.setClass(context, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
注意:以上两种方式都能够为Activity指定启动模式,可是二者还是有差别的。
(1)优先级:动态指定方式即另外一种比第一种优先级要高,若两者同一时候存在,以另外一种方式为准。
(2)限定范围:第一种方式无法为Activity直接指定 FLAG_ACTIVITY_CLEAR_TOP 标识,另外一种方式无法为Activity指定 singleInstance 模式。
Activity 的 Flags
- FLAG_ACTIVITY_SINGLE_TOP: 作用是为Activity指定 “SingleTop”启动模式,跟在AndroidMainfest.xml指定效果同样。
- FLAG_ACTIVITY_NEW_TASK: 作用是为Activity指定 “SingleTask”启动模式。跟在AndroidMainfest.xml指定效果同样。
- FLAG_ACTIVITY_CLEAN_TOP: 具有此标记位的Activity,启动时会将与该Activity在同一任务栈的其他Activity出栈。一般与SingleTask启动模式一起出现。它会完毕SingleTask的作用。但事实上SingleTask启动模式默认具有此标记位的作用.
- FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS: 具有此标记位的Activity不会出如今历史Activity的列表中,使用场景:当某些情况下我们不希望用户通过历史列表回到Activity时,此标记位便体现了它的效果。它等同于在xml中指定Activity的属性:android:excludeFromRecents="trure"
Android打包不可混淆哪些资源
1、反射中使用的元素;
2、GSON的序列化与反序列化(本质还是用到了反射)
3、枚举也不要混淆(用到反射)
4、四大组件不要混淆(会导致Manifest名称与混淆后名称不一致)
5、其他:
①jni调用的java方法
②java的native方法
③js调用的java方法
④第三方库不建议混淆
⑤其他和反射相关的一些情况
Tcp三次握手连接和四次挥手断开过程详解
baijiahao.baidu.com/s?id=159601…
TCP的连接建立是一个三次握手过程,目的是为了通信双方确认开始序号,以便后续通信的有序进行。主要步骤如下:
- 连接开始时,连接建立方(Client)发送SYN包,并包含了自己的初始序号a;
- 连接接受方(Server)收到SYN包以后会回复一个SYN包,其中包含了对上一个a包的回应信息ACK,回应的序号为下一个希望收到包的序号,即a+1,然后还包含了自己的初始序号b;
- 连接建立方(Client)收到回应的SYN包以后,回复一个ACK包做响应,其中包含了下一个希望收到包的序号即b+1。
TCP终止连接的四次握手过程如下:
- 首先进行关闭的一方(即发送第一个FIN)将执行主动关闭,而另一方(收到这个FIN)执行被动关闭。
- 当服务器收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号。
- 同时TCP服务器还向应用程序(即丢弃服务器)传送一个文件结束符。接着这个服务器程序就关闭它的连接,导致它的TCP端发送一个FIN。
- 客户必须发回一个确认,并将确认序号设置为收到序号加1。
热修复框架的区别(回答完热修复会问)
Android应用程序启动过程:
blog.csdn.net/shareus/art… 1.Launcher通过Binder进程间通信机制通知ActivityManagerService,它要启动一个Activity; 2.ActivityManagerService通过Binder进程间通信机制通知Launcher进入Paused状态; 3.Launcher通过Binder进程间通信机制通知ActivityManagerService,它已经准备就绪进入Paused状态,于是ActivityManagerService就创建一个新的进程,用来启动一个ActivityThread实例,即将要启动的Activity就是在这个ActivityThread实例中运行; 4.ActivityThread通过Binder进程间通信机制将一个ApplicationThread类型的Binder对象传递给ActivityManagerService,以便以后ActivityManagerService能够通过这个Binder对象和它进行通信; 5.ActivityManagerService通过Binder进程间通信机制通知ActivityThread,现在一切准备就绪,它可以真正执行Activity的启动操作了。
抽象类和接口的区别
抽象类 抽象方法必须用abstract关键字进行修饰。如果一个类含有抽象方法,则称这个类为抽象类,抽象类必须在类前用abstract关键字修饰。 抽象类就是为了继承而存在的 如果一个类继承于一个抽象类,则子类必须实现父类的抽象方法。如果子类没有实现父类的抽象方法,则必须将子类也定义为为abstract类。
接口
称作interface 接口中的变量会被隐式地指定为public static final变量(并且只能是public static final变量,用private修饰会报编译错误),而方法会被隐式地指定为public abstract方法且只能是public abstract方法(用其他关键字,比如private、protected、static、 final等修饰会报编译错误),并且接口中所有的方法不能有具体的实现,也就是说,接口中的方法必须都是抽象方法。
区别
抽象类可以提供成员方法的实现细节,而接口中只能存在public abstract 方法; 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型; 接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法; 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
webview的优化
www.jianshu.com/p/6179d5128… 为什么拥有Webview的H5页面打开这么慢,是因为它通常会经历以下几个阶段:
1)Webview初始化。 2)到达新的页面,网络连接,从服务器下载html,css,js,页面白屏。 3)页面基本框架出现,js请求页面数据,页面处于loading状态。 4)出现所需的数据,完成整个页面的渲染,用户可交互。
- WebView在Application中提前初始化
- WebView有一个setBlockNetworkImage(boolean)方法,该方法的作用是是否屏蔽图片的加载
- 另开WebView进程
- DNS解析优化(接口与网页主域名一致)
- WebView创建与网络请求并行
http和https的区别
blog.csdn.net/cao12619710… www.jianshu.com/p/7a40e874f…
webview加载https注意什么
tcp/ip协议
强引用 软引用 弱引用 虚引用
blog.csdn.net/baidu_22254… Java中4种引用的级别和强度由高到低依次为:强引用 -> 软引用 -> 弱引用 -> 虚引用
- 强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。
- 如果一个对象只具有软引用,则内存空间充足时,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。
- 软引用可以和一个引用队列(ReferenceQueue)联合使用。如果软引用所引用对象被垃圾回收,JAVA虚拟机就会把这个软引用加入到与之关联的引用队列中。
- 弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。
- 同样,弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。可见WeakReference对象的生命周期基本由垃圾回收器决定,一旦垃圾回收线程发现了弱引用对象,在下一次GC过程中就会对其进行回收
- 虚引用顾名思义,就是形同虚设。与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。虚引用主要用来跟踪对象被垃圾回收器回收的活动。
虚引用与软引用和弱引用的一个区别
虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。
安卓各个版本的不同
集合 hashmap
DexClassLoader和PathClassLoader的区别
都是BaseDexClassLoader的子类 1、DexClassLoader可以加载jar/apk/dex,可以从SD卡中加载未安装的apk 2、PathClassLoader只能加载系统中已经安装过的apk