java基础
安卓面试
fragment生命周期
项目介绍
主线程Looper.loop()进入无限循环,为什么不会造成ANR?
对于这个问题,首先我们来理解一下什么原因会造成ANR。
1.当前事件没有得到机会处理(有可能是因为主线程正常处理其他的事件,而不能及时处理当前的事件)。 2.当前事件正在处理了,但是没有及时处理完成。
looper.loop()不断的轮询接收事件,handler不断的处理事件。每一个点击触摸事件,或者Activity的生命周期的创建,都是运行在looper.loop()控制之下,简单的来说就是,这些都是属于一个事件消息,然后由looper.loop()轮询到,接着做相应的操作。也就是说,这些这些事件都在looper.loop()循环内执行的。这些事件消息的处理,可能会卡顿,造成了looper.loop()循环的阻塞,而不是looper.loop()阻塞了这些事件。
apk打包流程
- appt将资源文件打包为R.java
- aidl文件转为java文件
- javac对java文件进行编译生成.calss文件
- 将.class转化为dex文件
- 整合资源文件,打包为apk
- 签名
- 内存对齐处理,读取更有效率
事件分发
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提交,提升反破解的难度
性能优化
1.启动优化
- 冷启动:在app完全没有打开的情况下启动,此时此时没有为app分配进程,此时系统会创建新的进程分配给应用。
- 热启动:app 已经被分配进程,在app按home键进入后台、按back键退出应用但没有被杀死的情况下。此时app在任务列表是可以看到的。
- 优化:
- 更改第一个 Activity默认的主题,设置一个静态的图片(并不能优化,知识视觉上好看)
- 把一些初始化的东西放到子线程去处理,比如IntentService等
- 热启动可以拦截 Back 键的点击事件,不执行System.exit() 去执行Home键的功能,但是要做好事件的保存和恢复
2.布局优化
- 自定义 View 实现一些布局,
- 使用协调者布局,使布局偏平化
- 使用 include、merge、 ViewSub进行优化
- 使用自定义 VIew 优化布局结构
3.绘制优化
- 不在 View 的 onDraw方法进行大量的操作,比如频繁创建对象,会造成内存抖动
- 不在 View 的 onDraw 方法中做耗时任务,会造成 View绘制不流畅
4.内存优化
- 在 Activity 销毁的时候及时停止动画,比如帧动画,会持有 Activity 内部 View 的引用,在 onDestory 的时候,及时 cancel 掉。
- 不在单例模式里面持有非 Application 的 Context
- 使用静态内部类创建 Handler 对象,在Activity 销毁的时候移除 CallBack。
- 及时关闭不使用的资源
- 对于key为int的HashMap尽量使用SparceArray替代,大概可以省30%的内存,而对于其他类型
- 对于可以 延迟初始化的页面,使用viewstub
- 使用 LeakCanary - 进行内存泄漏检测,及时的发现问题,解决问题
5.屏幕适配
- 尽量是用 Match_Parent、Wrap_Content、权重
- 使用密度无关像素指定尺寸 sp、dp
- 使用相对布局或线性布局,不要使用绝对布局
- 使用新推出的约束布局
- 使用minWidth、minHeight、lines等属性
- 使用 singleLine、elliedze
- 使用dimens,不同屏幕大小使用不同尺寸
LeakCanary 原理
-
会注册对所有 Activirty的监听,主要可以检测到生命周期,在监听到 onDestroy 调用的时候,会把检测到 Activity 实例关联包装成一个自定义弱引用。
-
但是这里在使用时,还给指定了一个 ReferenceQuery 队列,该队列的作用就是当发生 GC 时,弱引用所持有的的对象如果被回收,就会进入该队列。
-
所以只要在 Activity onDestroy 发生时候,把 Activity 对象绑定到 弱引用中,然后手动执行一次 GC,然后观察引用集合 (ReferenceQuery)中是不是包含对应的 Activity 对象,如果不包含,说明内存泄漏。
-
接着 LeakCanary 会使用开源库 haha 做分析 Android 堆垃圾,并把结果通过通知的方式显示出来。
-
这就是 LeakCanary 工作的大致原理。
recyleview的复用机制
recyleview的缓存机制
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(msg)方法,把handler对象赋值给Message消息的target属性,然后将handler发送的消息加入到消息队列。
- 主线程会调用looper.loop()方法,他无限循环,取出从消息队列中取消消息msg,并且调用一个handler的实例target的dispatchMessage()方法( msg.target.dispatchMessage(msg);),将消息通过handleMessage方法或者handleCallback()里面的run方法分发出去。
- 消息队列的类型:单链表类型,以时间顺序存储,取的时候从表头取。
Thread避免内存泄露
- 非静态内部类或者匿名内部类默认持有外部类的引用。
- thread实现是匿名内部类或者内部类。 工作线程在处理任务,这时候外部类销毁的时候,由于工作线程的实例持有外部类引用,将使外部类无法GC回收,从而导致ANR。
避免内存泄露:
- 方法1:因为静态内部类不默认持有外部类的引用,所以可以将thread设置成静态内部类。
- 方法2:当外部类结束生命周期时,强制结束线程。在onDestroy里面调用Thread.stop();
handler避免内存泄露
- 原因:主线程的Looper对象的生命周期 = 该应用程序的生命周期 在Java中,非静态内部类 & 匿名内部类都默认持有外部类的引用
- 在handler中如果消息没有处理完,handleer中的message对象持有handler引用,非静态内部类/匿名内部类handler持有activity的引用,当activity destroy的时候,将无法回收。
避免内存泄露:
- 方法一:将非静态内部类换成静态的
- 方法二:在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…
定位工具
- MAT(Memory Analysis Tools)
- Heap Viewer
- Allocation Tracker
- Android Profiler as3.0以后代替Memory Monitor
- LeakCanary
onstart和onresume区别
- onStart()是activity界面被显示出来的时候执行的,用户可见,包括有一个activity在他上面,但没有将它完全覆盖,用户可以看到部分activity但不能与它交互
- onResume()是当该activity与用户能进行交互时被执行,用户可以获得activity的焦点,能够与用户交互。
onstop和onpause区别
- onPause 用于由一个Activity转到另一个Activity、设备进入休眠状态(屏幕锁住了)、或者有dialog弹出时
- onStop 用于不可见的Activity(有对话框弹出时,这时底下的activity仍然可见,所以此时onStop不会被调用)
注意:
- 当前Activity产生事件弹出Toast和AlertDialog的时候Activity的生命周期不会有改变
- Activity运行时按下HOME键(跟被完全覆盖是一样的):onSaveInstanceState --> onPause --> onStop onRestart -->onStart--->onResume
- Activity未被完全覆盖只是失去焦点:onPause--->onResume
横竖屏切换的时候,生命周期
1、不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次
2、设置Activity的android:configChanges="orientation"时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次
3、设置Activity的android:configChanges="orientation|keyboardHidden|screenSize"时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法
onSaveInstanceState() 与 onRestoreIntanceState()
onSaveInstanceState 和 onRestoreIntanceState 方法并不是 Activity 的生命周期方法,当遇到异常情况的时候会触发。比如内存不足 Activity 被杀死,或者用户主动按下 Home 键。onSaveInstanceState 会被调用,但是当用户主动去销毁 Activity 的时候,不会触发。
onSaveInstanceState什么时机被调用?
- 用户按下 Home 键时
- 调出多任务选择其他程序时
- 按下电源键时
- 屏幕方向切换时无论竖屏切横屏还是横屏切竖屏都会调用)
- 启动新的 Activity 时(因为不确定当前的Activity 会不会被销毁 关于 onSaveInstanceState 调用时机,有的地方说是在 onStop 之前,和 onPause 不确定调用顺序,但是我用三星 S8 和 AS 自带虚拟机测试一直都是在 onStop 之后才会被调用。
onRestoreInstanceState什么时机被调用?
- onRestoreInstanceState(Bundle savedInstanceState)只有在activity确实是被系统回收,重新创建activity的情况下才会被调用。比如屏幕旋转一定会执行 OnRestoreInstanceState 的。
- 但是如果是按了 onSaveInstanceState 调用的其他四种情况,如果 Activity 没有被回收只会重建,是不是调用 onRestoreInstanceState 方法的。如果被重建了,那么则会调用onRestoreInstanceState 方法
onSaveInstanceState() 与 onRestoreIntanceState() 使用场景
其实正常来说Android系统已经帮我们默认实现了很多保存和恢复的操作,但是个别场景还是需要我们自己来手动保存和恢复数据的,举个例子: 进入页面有个本地变量 localValue = 0; 然后操作赋值为2 ,如果这个时候发生了 onSaveInstanceState() 与 onRestoreIntanceState(),那么这个值还是会变成初始值的,会导致 赋值为2的操作无效。所以这个要根据开发中具体的业务需求来实现这两个方法。
以下两种情况下 都只会触发onPause而不会触发onStop:
- 一个透明的包含Dialog的Activity出现
- 按poweroff锁屏
View.post和handler.post的区别
获取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());
}
});
}
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);
}
}
mvp和mvvm的区别
mvvm www.jianshu.com/p/b5e13be17…
MVVM采用双向绑定(data-binding):View的变动,自动反映在ViewModel,反之亦然,这种模式实际上是框架替应用开发者做了一些工作(相当于ViewModel类是由库帮我们生成的),开发者只需要较少的代码就能实现比较复杂的交互。这一步对于我还是比较有吸引力了,可以少写很多模板代码。
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"
进程间通信
一、使用 Intent
二、使用文件共享
三、使用 Messenger
四、使用 AIDL
五、使用 ContentProvider
六、使用 Socket
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的启动操作了。
对自己接下来的计划
项目难点
最近有没有学其他的东西
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的区别
- HTTP 数据明文传输,HTTPS 数据加密传输
- HTTP 不需要证书,HTTPS 需要申请 CA 证书
- HTTP 默认80端口,HTTPS 默认 443 端口
- HTTPS 比 HTTP 安全,因为比 HTTP 多了 SSL 层
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
Android版本的新特性
安卓序列化有哪些
retrofit源码
- retrofit使用时创建retrofit对象,然后创建一个网络请求service接口。
- 创建对象的时候,使用构造者模式,增加网络请求适配器工厂(addCallAdapterFactoty),把对象进行平台适配,比如如果用rxjava的话,就会传RXjavaCallAdapterFactoty.creat(),这个对象其实就是创建了一个带参数的RXjavaCallAdapterFactoty对象。
- 然后,增加数据转换器工厂(addConverterFactory),为retrofit添加合适的转化器,默认是用BuiltInConverters转化器,如果我们用gson的话就必须添加gsonConverterFactory.create(),这个对象其实是创建了一个带有gson对象的gsonConverterFactory实例。
- 创建retrofit实例之后,retrofit会使用代理模式,将定义的网络请求接口生成对应的代理对象,然后在执行代理对象方法调用的时候,会通过反射把调用的方法的信息(方法注解、参数注解、参数类型)封装在 ServiceMethod 中。通过servicemethod对象拿到 OkHttpCall 的包装类,最后通过包装类里面调用 OkhttpCall 调用 OkHttp 的方式完成网络请求。