Android

443 阅读26分钟

java基础

juejin.cn/post/688416…

安卓面试

www.kancloud.cn/smartsean/a…

fragment生命周期

项目介绍

主线程Looper.loop()进入无限循环,为什么不会造成ANR?

对于这个问题,首先我们来理解一下什么原因会造成ANR。

1.当前事件没有得到机会处理(有可能是因为主线程正常处理其他的事件,而不能及时处理当前的事件)。 2.当前事件正在处理了,但是没有及时处理完成。

looper.loop()不断的轮询接收事件,handler不断的处理事件。每一个点击触摸事件,或者Activity的生命周期的创建,都是运行在looper.loop()控制之下,简单的来说就是,这些都是属于一个事件消息,然后由looper.loop()轮询到,接着做相应的操作。也就是说,这些这些事件都在looper.loop()循环内执行的。这些事件消息的处理,可能会卡顿,造成了looper.loop()循环的阻塞,而不是looper.loop()阻塞了这些事件。

apk打包流程

  1. appt将资源文件打包为R.java
  2. aidl文件转为java文件
  3. javac对java文件进行编译生成.calss文件
  4. 将.class转化为dex文件
  5. 整合资源文件,打包为apk
  6. 签名
  7. 内存对齐处理,读取更有效率

事件分发

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在任务列表是可以看到的。
  • 优化:
  1. 更改第一个 Activity默认的主题,设置一个静态的图片(并不能优化,知识视觉上好看)
  2. 把一些初始化的东西放到子线程去处理,比如IntentService等
  3. 热启动可以拦截 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 原理

  1. 会注册对所有 Activirty的监听,主要可以检测到生命周期,在监听到 onDestroy 调用的时候,会把检测到 Activity 实例关联包装成一个自定义弱引用。

  2. 但是这里在使用时,还给指定了一个 ReferenceQuery 队列,该队列的作用就是当发生 GC 时,弱引用所持有的的对象如果被回收,就会进入该队列。

  3. 所以只要在 Activity onDestroy 发生时候,把 Activity 对象绑定到 弱引用中,然后手动执行一次 GC,然后观察引用集合 (ReferenceQuery)中是不是包含对应的 Activity 对象,如果不包含,说明内存泄漏。

  4. 接着 LeakCanary 会使用开源库 haha 做分析 Android 堆垃圾,并把结果通过通知的方式显示出来。

  5. 这就是 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对象。

热修复源码

www.jianshu.com/p/0399f5e4c…

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…

定位工具

onstart和onresume区别

  1. onStart()是activity界面被显示出来的时候执行的,用户可见,包括有一个activity在他上面,但没有将它完全覆盖,用户可以看到部分activity但不能与它交互
  2. onResume()是当该activity与用户能进行交互时被执行,用户可以获得activity的焦点,能够与用户交互。

onstop和onpause区别

  1. onPause 用于由一个Activity转到另一个Activity、设备进入休眠状态(屏幕锁住了)、或者有dialog弹出时
  2. 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:
  1. 一个透明的包含Dialog的Activity出现
  2. 按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…

滑动冲突

www.jianshu.com/p/d82f426ba…

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的连接建立是一个三次握手过程,目的是为了通信双方确认开始序号,以便后续通信的有序进行。主要步骤如下:
  1. 连接开始时,连接建立方(Client)发送SYN包,并包含了自己的初始序号a;
  2. 连接接受方(Server)收到SYN包以后会回复一个SYN包,其中包含了对上一个a包的回应信息ACK,回应的序号为下一个希望收到包的序号,即a+1,然后还包含了自己的初始序号b;
  3. 连接建立方(Client)收到回应的SYN包以后,回复一个ACK包做响应,其中包含了下一个希望收到包的序号即b+1。
TCP终止连接的四次握手过程如下:
  1. 首先进行关闭的一方(即发送第一个FIN)将执行主动关闭,而另一方(收到这个FIN)执行被动关闭。
  2. 当服务器收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号。
  3. 同时TCP服务器还向应用程序(即丢弃服务器)传送一个文件结束符。接着这个服务器程序就关闭它的连接,导致它的TCP端发送一个FIN。
  4. 客户必须发回一个确认,并将确认序号设置为收到序号加1。

热修复框架的区别(回答完热修复会问)

www.jianshu.com/p/566aa853c…

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 层

blog.csdn.net/cao12619710…

www.jianshu.com/p/7a40e874f…

webview加载https注意什么

www.jianshu.com/p/fa290c28b…

tcp/ip协议

www.cnblogs.com/onepixel/p/…

强引用 软引用 弱引用 虚引用

blog.csdn.net/baidu_22254… Java中4种引用的级别和强度由高到低依次为:强引用 -> 软引用 -> 弱引用 -> 虚引用

  • 强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。
  • 如果一个对象只具有软引用,则内存空间充足时,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。
  • 软引用可以和一个引用队列(ReferenceQueue)联合使用。如果软引用所引用对象被垃圾回收,JAVA虚拟机就会把这个软引用加入到与之关联的引用队列中。
  • 弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。
  • 同样,弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。可见WeakReference对象的生命周期基本由垃圾回收器决定,一旦垃圾回收线程发现了弱引用对象,在下一次GC过程中就会对其进行回收
  • 虚引用顾名思义,就是形同虚设。与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。虚引用主要用来跟踪对象被垃圾回收器回收的活动。

虚引用与软引用和弱引用的一个区别

虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。

安卓各个版本的不同

www.jianshu.com/p/21bd742e3…

集合 hashmap

www.cnblogs.com/chenglc/p/8…

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 的方式完成网络请求。

kotlin协程