Android高级interview

一、Android基础知识

1、四大组件、六大布局、五大存储

四大组件:

activity、service、content provider、broadcast

六大布局(现在是 7 大了):

  • 线性布局(LinearLayout)
  • 相对布局(RelativeLayout)
  • 帧布局(FrameLayout)
  • 表格布局(TableLayout)
  • 网格布局(GridLayout)
  • 协调布局(ConstraintLayout)
  • 绝对布局(AbsoluteLayout)

五大存储

SharedPreferences、文件存储、SQLite数据库方式、内容提供器(Content provider)、网络

2、Activity生命周期

onCreate() -> onStart() -> onResume() -> onPause() -> onStop() -> onDetroy()

Fragment生命周期

onAttach() -> onCreate() -> onCreateView() -> onActivityCreate() -> onStart() -> onResume() -> onPause() -> onStop() -> onDestoryView() -> onDestory() -> onDetach()

3、启动模式

standard:默认启动模式

singleTop栈顶复用模式:当activity为此种模式时,开启这个activity会对当前栈顶activity进行检查,如果栈顶activity与待开启的activity一致(或者说为同一个activity),那么将复用栈顶acticity,而不会启动新的

singletask栈内复用模式

singleInstance模式:当activity设定为此种模式时,那么系统将会为这个activity单独开启一个栈,在这个栈中只能存在这一个activity,并且对于一个activity这种栈只能开启一个,重复开启页面时会复用之前的activity。

singleInstancePerTask:singleInstancePerTas是 android12 新增的,与singleTask几乎一样,不同的是它允许应用程序在一个新的任务中启动一个新的实例,而不是在现有任务中启动实例。

4、Service生命周期?

通过startService()这种方式启动的service,生命周期是这样:

调用startService() --> onCreate()–> onStartConmond()–> onDestroy()。

startService() 方法用于启动一个服务并在后台运行,它的生命周期如下:

  1. onCreate():当服务第一次被创建时调用,这是初始化代码的好地方。
  2. onStartCommand():每次调用 startService() 方法时都会调用此方法。在此方法中,您可以处理来自客户端的请求并执行任何必要的操作,例如启动新线程或播放音乐。
  3. onDestroy():当服务不再需要时,将调用此方法。在此方法中,您可以执行任何必要的清理操作,并释放所有资源。

通过bindService()方式进行绑定,这种方式绑定service,

生命周期走法:bindService–>onCreate()–>onBind()–>unBind()–>onDestroy()

bindService() 方法用于将客户端绑定到服务,并与服务进行通信。它的生命周期如下:

  1. onCreate():当服务第一次被创建时调用,这是初始化代码的好地方。
  2. onBind():每次调用 bindService() 方法时都会调用此方法。在此方法中,您可以返回一个 IBinder 接口的实现,以便客户端可以与服务进行通信。
  3. onUnbind():当客户端与服务解除绑定时,将调用此方法。在此方法中,您可以执行任何必要的清理操作。
  4. onRebind():当客户端重新绑定到服务时,将调用此方法。在此方法中,您可以执行任何必要的操作,例如重新初始化数据或恢复状态。
  5. onDestroy():当服务不再需要时,将调用此方法。在此方法中,您可以执行任何必要的清理操作,并释放所有资源。

5、Broadcast注册方式与区别

此处延伸:什么情况下用动态注册

Broadcast广播,注册方式主要有两种.

第一种是静态注册,也可成为常驻型广播,这种广播需要在Androidmanifest.xml中进行注册,这中方式注册的广播,不受页面生命周期的影响,即使退出了页面,也可以收到广播这种广播一般用于想开机自启动啊等等,由于这种注册的方式的广播是常驻型广播,所以会占用CPU的资源。

第二种是动态注册,而动态注册的话,是在代码中注册的,这种注册方式也叫非常驻型广播,收到生命周期的影响,退出页面后,就不会收到广播,我们通常运用在更新UI方面。这种注册方式优先级较高。最后需要解绑,否会会内存泄露

广播是分为有序广播和无序广播。

6、android各版本差异

Android 5.x(Lollipop)

  • 开始支持64位的处理器
  • ART(Android Runtime)取代了Dalvik虚拟机,提升应用性能(即时编译 ->提前编译)

Android 6.x(Marshmallow)

  • 动态权限管理系统
  • 取消支持 Apache HTTP 客户端,转而推荐使用HttpUrlConnection或者OkHttp库来进行网络请求。

Android 7.x(Nougat)

  • 分屏模式,支持同时运行两个应用程序
  • 系统权限的更改,分享私有文件内容的推荐方法是使用 FileProvider,禁止向你的应用外公开 file:// URI
  • 更便捷的通知栏,自动将多条通知合并。
  • 支持app应用签名v2的打包方式(在AS2.2后,在打包签名应用时,可勾选jar打包(v1)和全应用打包(v2)

Android 8.x(Oreo)

  • 通知渠道 — Notification Channels
  • 画中画模式,支持应用窗口悬浮播放视频
  • 安装未知来源的第三方开关被移出,变成了每次安装未知的第三方都要手动授权。

Android 9.x(Pie)

  • 异型屏适配

  • 使用前台服务,必须请求 FOREGROUND_SERVICE 权限

  • 不能直接非 Activity 环境中(比如Service,Application)启动 Activity,否则会崩溃报错。保活的说法从此之后越来越少

Android 10.x(Q)

Android 11.x(R)

  • 用户存储权限

Android 12.x

  • 启动模式新增singleInstancePerTask(与singleTask几乎一样,不同的是它允许应用程序在一个新的任务中启动一个新的实例,而不是在现有任务中启动实例。)

  • JDK版本强制JDK11

7、[重要]activity a 启动 activity b 在ams层上是怎么流转的

当 Activity A 启动 Activity B 时,在 AMS(Activity Manager Service)层面上的流转如下:

  1. Activity A 发起启动请求,并将该请求传递给 AMS。
  2. AMS 接收到启动请求后,会进行一系列的处理和判断,包括权限检查、任务栈管理等。
  3. 如果 Activity B 尚未创建,则 AMS 会创建一个新的实例,并将其添加到任务栈中。
  4. 如果 Activity B 已经存在于任务栈中,AMS 会将其调至前台,并执行相应的生命周期方法(如 onCreate()、onStart()、onResume())。
  5. 同时,AMS 会将 Activity A 转入后台,并执行相应的生命周期方法(如 onPause())。
  6. 当 Activity B 完成其生命周期后,AMS 可能会执行一些后续操作,如销毁 Activity B、返回到上一个 Activity 等。

总之,在 AMS 层面上,Activity A 启动 Activity B 的流转是通过任务栈的管理和生命周期方法的调用来实现的。

8、activity a跳转到activity b 都经过了哪些生命周期

1、B页面完全遮住A页面

a页面 -> b页面

A页面:onPause
B页面:onCreate -> onStart -> onResume
A页面:onStop

B页面 -> a页面

B页面:onPause
A页面:onRestart -> onStart -> onResume
B页面:onStop -> onDestroy

2、B是透明的或者完全没有遮挡A

a页面 -> b页面

A页面:onPause
B页面:onCreate -> onStart -> onResume

B页面 -> a页面

B页面:onPause
A页面:onResume //因为是透明的,所以A执行 onResume(不会执行onRestart->onStart)
B页面:onStop -> onDestroy

9、Android中的几种动画

  1. 属性动画(Property Animation):可以对任意对象的属性进行动画操作,如平移、缩放、旋转等。示例代码如下:
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "translationX", 0f, 200f);
animator.setDuration(1000);
animator.start();
  1. 补间动画(Tween Animation):通过定义起始状态和结束状态来实现动画效果,常用的补间动画包括透明度渐变、缩放、旋转等。示例代码如下:
Animation animation = AnimationUtils.loadAnimation(context, R.anim.fade_in);
view.startAnimation(animation);
  1. 帧动画(Frame Animation):通过连续播放一系列预先定义好的图片帧来实现动画效果。示例代码如下:
ImageView imageView = findViewById(R.id.image_view);
imageView.setBackgroundResource(R.drawable.frame_animation);
AnimationDrawable animationDrawable = (AnimationDrawable) imageView.getBackground();
animationDrawable.start();
  1. 转场动画(Transition Animation):用于在Activity或Fragment之间实现平滑的过渡效果,如淡入淡出、滑动等。示例代码如下:
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.setCustomAnimations(R.anim.slide_in, R.anim.slide_out);
transaction.replace(R.id.container, newFragment);
transaction.commit();

二、UI设计

1、mipmap、drawable的区别

mipmap文件夹主要用于应用程序图标,而drawable文件夹则用于其他类型的图像资源

mipmap、drawable都是安卓存放图片资源的文件夹目录

2、自定义控件

1、组合控件。这种自定义控件不需要我们自己绘制,而是使用原生控件组合成的新控件。如标题栏。

2、继承原有的控件。这种自定义控件在原生控件提供的方法外,可以自己添加一些方法。如制作圆角,圆形图片。

3、完全自定义控件:这个View上所展现的内容全部都是我们自己绘制出来的。比如说制作水波纹进度条。

3、View的绘制原理(绘制流程)

Android绘制原理涉及到Android的View体系和绘图流程。当一个View需要绘制时,Android会调用View的onDraw()方法,该方法中可以通过Canvas对象进行绘制操作。

绘制过程大致分为以下几个步骤:

  1. 测量(Measure):根据View的测量规则,确定View的尺寸大小。
  2. 布局(Layout):确定View在父容器中的位置和大小。
  3. 绘制(Draw):通过调用View的onDraw()方法,使用Canvas对象进行绘制操作,如绘制背景、文本、图形等。
  4. 重绘(Invalidate):如果View的内容发生改变或需要更新,可以调用invalidate()方法来触发重绘流程。

在绘制过程中,Android使用了双缓冲技术来提高绘制效率和避免闪烁。它使用了两个屏幕缓冲区,一个用于显示当前内容,另一个用于绘制下一帧的内容。当绘制完成后,交换两个缓冲区的内容,实现平滑的显示效果。

4、View、ViewGroup事件分发(绘制原理、UI事件传递)

区分:dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent

具体的触摸事件分发流程如下:

  1. 当触摸事件发生时,首先会调用顶层视图的 dispatchTouchEvent 方法。
  2. dispatchTouchEvent 方法中,会按照一定的规则将触摸事件传递给目标视图,并在传递过程中调用每个视图的 onTouchEvent 方法。
  3. 如果目标视图是一个 ViewGroup,并且该 ViewGroup 重写了 onInterceptTouchEvent 方法,并且在该方法中返回了 true,表示拦截事件,那么接下来触摸事件会直接传递给该 ViewGroup 的 onTouchEvent 方法处理,而不会再继续传递给子视图。
  4. 如果目标视图是一个普通的 View 或者 ViewGroup,并且没有重写 onInterceptTouchEvent 方法,或者在方法中返回了 false,那么触摸事件会继续传递给子视图进行处理。

先走的是 dispatchTouchEvent 方法,然后根据 onInterceptTouchEvent 的返回值来决定是否拦截事件。

事件分发L型链传递

img

L型链的特点是最终会有一个组件对事件进行消费(执行点击事件逻辑)。如果没有组件消费,最终事件会重回Activity,变成U型链,也就是第二种情况

事件分发U型链传递

img

三、通信机制

1、Handler机制

Handler机制由四个组件组成:
1、Message:消息体
2、MessageQueue:消息队列(链表实现)
3、Handler:消息处理者
4、Looper:消息循环器

工作原理:Android中主线程是不能进行耗时操作的,子线程是不能进行更新UI的。所以就有了handler,它的作用就是实现线程之间的通信。当应用创建的时候,就会在主线程中创建handler对象,我们通过要传送的消息保存到Message中,handler通过调用sendMessage方法将Message发送到MessageQueue中,Looper对象就会不断的调用loop()方法,不断的从MessageQueue中取出Message交给handler进行处理。从而实现线程之间的通信

理解:Message、Handler、Looper、MessageQueue 这四者就构成了一个生产者和消费者模式。Message 相当于产品,MessageQueue 相当于传输管道,Handler 相当于生产者,Looper 相当于消费者

备注:Handler 能用于更新 UI 包含了一个隐性的前提条件:Handler 与主线程 Looper 关联在了一起。在主线程中初始化的 Handler 会默认与主线程 Looper 关联在一起,所以其 handleMessage(Message msg) 方法就会由主线程来调用。在子线程初始化的 Handler 如果也想执行 UI 更新操作的话,则需要主动获取 mainLooper 来初始化 Handler

img

2、Android中跨进程通讯的几种方式

IPC全称为Inter-Process Communication,含义为进程间通信,指的是两个进程之间进行数据交换的过程。

Android 跨进程通信,像intent,contentProvider,广播,service都可以跨进程通信。

1、intent:这种跨进程方式并不是访问内存的形式,它需要传递一个uri,比如说打电话。

2、contentProvider:这种形式,是使用数据共享的形式进行数据共享。

3、service:远程服务,aidl

4、AIDL:功能强大,支持进程间一对多的实时并发通信,并可实现RPC(远程过程调用)
5、Broadcast Receiver:广播,但只能单向通信,接收者只能被动接受信息。
6、文件共享:在非高并发的情况下共享简单的数据。
7、Socket:通过网络传输数据。

3、AIDL理解

AIDL(Android Interface Definition Language):Android中用于定义进程间通信接口的一种语言(基于 Binder 的)。

饿了么HermesEventBus跨进程通信,本质也是AIDL通信

AIDL: 每一个进程都有自己的Dalvik VM实例,都有自己的一块独立的内存,都在自己的内存上存储自己的数据,执行着自己的操作,都在自己的那片狭小的空间里过完自己的一生。而aidl就类似与两个进程之间的桥梁,使得两个进程之间可以进行数据的传输,跨进程通信有多种选择,比如 BroadcastReceiver , Messenger 等,但是 BroadcastReceiver 占用的系统资源比较多,如果是频繁的跨进程通信的话显然是不可取的;Messenger 进行跨进程通信时请求队列是同步进行的,无法并发执行。

4、Binder机制原理

Binder是Android中进程间通信的核心机制,它通过驱动层和框架层的配合实现进程间通信。Binder的工作原理如下:

  1. 服务端注册Binder对象:服务端通过Binder机制创建一个Binder对象,并将其注册到ServiceManager中,以便客户端可以通过Binder对象的引用来访问服务端。
  2. 客户端获取Binder对象:客户端通过ServiceManager获取服务端注册的Binder对象的引用。
  3. 客户端与服务端通信:客户端通过Binder对象的引用调用服务端提供的方法,实现进程间通信。
  4. 跨进程通信:Binder在内核层通过驱动进行跨进程通信,具体实现是通过进程间共享内存和进程间信号量来完成的。当客户端调用服务端的方法时,Binder会将客户端的请求封装成一个数据结构,然后通过驱动将数据发送给服务端。服务端接收到请求后,再将处理结果返回给客户端。
  5. Binder对象的引用计数:Binder对象的引用计数机制可以确保在没有客户端使用时,服务端可以被回收。当没有客户端使用时,Binder对象会调用其onLastStrongRef()方法,然后被销毁。

IPC 机制:进程间通信机制

Binder机制是Android系统中的一个重要的进程间通信(IPC)机制。以下是一些与Binder机制相关的面试知识点:

  1. Binder的基本原理:Binder机制基于C/S架构,使用了驱动层、服务管理器、Binder驱动和Binder对象等组件来实现进程间通信。

  2. Binder的角色:在Binder机制中,有三种主要角色:客户端、服务端和Binder驱动。客户端通过代理对象与服务端进行通信,而Binder驱动负责处理进程间的数据传输和通信。

  3. Binder对象:在Binder机制中,进程间通信是通过Binder对象进行的。每个Binder对象都有一个唯一的标识符(Binder引用),用于在不同进程之间进行通信。

  4. 进程间通信的方式:Binder机制提供了多种进程间通信的方式,包括跨进程方法调用、跨进程数据传输和跨进程事件通知等。

  5. AIDL(Android Interface Definition Language):AIDL是一种用于定义客户端和服务端之间接口的语言,它可以帮助开发者生成跨进程通信所需的代码。

  6. 进程间通信的安全性:Binder机制通过权限验证和用户ID(UID)来确保进程间通信的安全性,防止未授权的访问和恶意行为。

  7. Binder的性能优化:在使用Binder机制时,可以通过减少跨进程调用次数、使用轻量级数据传输方式和优化服务端代码等方式来提高性能。

5、linux的共享内存有几次拷贝,为啥android不使用linux的IPC机制

Linux 中的共享内存在使用时通常涉及两次拷贝。首先,数据从用户空间被拷贝到内核空间,然后再从内核空间拷贝到另一个进程的用户空间。

Android 之所以不直接使用 Linux 的共享内存,是因为 Android 使用了基于 Binder 的进程间通信机制。Binder 通过将数据序列化为跨进程的消息进行传递,避免了额外的内核空间拷贝。这种设计可以提供更高效的进程间通信,并且具有更好的安全性和稳定性。

此外,Android 还提供了其他的共享内存机制,如 Ashmem(匿名共享内存),用于实现特定的共享内存需求,但它并不是 Android 应用程序常用的进程间通信方式。

四、架构设计

1、MVP,MVC,MVVM

参考:https://blog.csdn.net/zg0601/article/details/123587933

1、MVC(Model-View-Controller)

可以理解为:1、页面万能activity + xml +model 或者 2、adapter 中 adapter +xml +model

MVC是模型-视图-控制器,它是MVC、MVP、MVVM这三者中最早产生的框架,其他两个框架是以它为基础发展而来的。

img

优点:耦合性低:视图层和业务层分离

缺点:Controller层与View层之间是一一对应的,断绝了View层复用的可能,产生了很多冗余代码

2、MVP(Model-View-Presenter)

MVP是模型-视图-表示器,Model层提供数据,View层负责视图显示,Controller/Presenter层负责逻辑的处理。将Controller改名为Presenter的同时改变了通信方向。

Model:模型层,用于数据存储以及业务逻辑。View:视图层,用于展示与用户实现交互的页面,通常实现数据的输入和输出功能。Presenter:表示器,用于连接M层、V层,完成Model层与View层的交互,还可以进行业务逻辑的处理。
  • 数据模型 Model 是 M 层,负责处理数据的获取、存储和处理。
  • Activity 和 XML 是 V 层,负责展示界面和与用户交互。
  • 抽象类 HomePresenter 是 P 层,负责处理业务逻辑和协调 Model 和 View 的交互。

在 Android MVP 中,Contract 是用于定义 Model、View 和 Presenter 之间的接口规范的一个契约类。它通常包含了以下几个接口:

  • View 接口:定义了 View 层的操作方法,供 Presenter 层调用。
  • Presenter 接口:定义了 Presenter 层的操作方法,供 View 层调用。
  • Model 接口:定义了 Model 层的操作方法,供 Presenter 层调用。

Contract 的存在可以帮助实现解耦和模块化,使得各个层之间的交互更加清晰和可维护。

img

优点:所以View层可以抽离出来做成组件,view复用性强

缺点:

1、由于View层和Model层都需要经过Presenter层,导致Presenter层比较复杂,维护起来也会有一定的问题

2、没有绑定数据,所有数据都需要Presenter层进行“手动同步”

3、MVVM(Model-View-ViewModel)

MVVM是模型-视图-视图模型。MVVM与MVP框架区别在于:MVVM采用双向绑定:View的变动,自动反映在ViewModel,反之亦然。

Model:数据模型(数据处理业务),指的是后端传递的数据。View:视图(包含 xml),将Model的数据以某种方式展示出来。ViewModel:视图模型,数据的双向绑定(当Model中的数据发生改变时View就感知到,当View中的数据发生变化时Model也能感知到),是MVVM模式的核心。ViewModel 层把 Model 层和 View 层的数据同步自动化了,解决了 MVP 框架中数据同步比较麻烦的问题,不仅减轻了 ViewModel 层的压力,同时使得数据处理更加方便——只需告诉 View 层展示的数据是 Model 层中的哪一部分即可。

img

优点:

1.低耦合:视图(View)可以独立于Model变化和修改

2.可重用性:你可以把一些视图逻辑放在一个 ViewModel 里面,让很多 view 重用这段视图逻辑。

缺点:

1.对于简单的图形界面不实用

2.如果多个页面共用一个model(个人也不建议这么干),会导致model数据变多,内存开销变大,影响性能;

3.view和model的绑定,使页面异常追踪变得不方便。

2、热修复的原理

Android热修复(Hotfix)是一种在运行时修复应用程序的机制,它可以在不重新安装或升级应用的情况下,修复应用中的bug或添加新功能。Android热修复的原理通常涉及以下几个步骤:

1、补丁生成:开发者根据需要修复的问题或添加的功能,生成一个代码补丁(Patch),通常是一个包含修复或新增代码的.dex文件。

2、下载补丁:应用在启动时,通过网络或其他方式下载最新的补丁文件。

3、补丁加载:应用在下载完补丁后,将补丁文件加载到内存中。

4、类加载器替换:通过替换应用的类加载器,将新加载的补丁类加入到应用的类加载器中。

需要注意的是,Android热修复虽然可以在运行时修复问题,但也有一些限制。例如,无法修复资源文件、Manifest文件等静态资源的问题;同时,由于涉及到动态加载和替换代码,需要保证补丁的正确性和安全性,以避免潜在的风险。因此,在使用热修复技术时,开发者需要谨慎处理,并进行充分的测试和验证。我们知道Java虚拟机 —— JVM 是加载类的class文件的,而Android虚拟机——Dalvik/ART VM 是加载类的dex文件,而他们加载类的时候都需要ClassLoader,ClassLoader有一个子类BaseDexClassLoader,而BaseDexClassLoader下有一个数组——DexPathList,是用来存放dex文件,当BaseDexClassLoader通过调用findClass方法时,实际上就是遍历数组,找到相应的dex文件,找到,则直接将它return。而热修复的解决方法就是将新的dex添加到该集合中,并且是在旧的dex的前面,所以就会优先被取出来并且return返回。

5、修复生效:应用在执行时,会优先使用补丁中的修复或新增代码逻辑,从而达到修复bug或添加新功能的目的。

3、热修复单classloader和双classloader的区别

热修复单ClassLoader和双ClassLoader的区别在于:

单ClassLoader方案中,所有的补丁包和原始包都会被加载到同一个ClassLoader中,因此可能会出现类冲突的问题。而双ClassLoader方案中,补丁包和原始包会被加载到不同的ClassLoader中,从而避免了类冲突的问题。同时,双ClassLoader方案中,补丁包的生命周期也可以更好地控制,可以更加灵活地管理和升级补丁。但是双ClassLoader方案也会带来一些额外的开销和复杂度。

4、设计一个 日志系统模块 你都能想到哪些点

1.需要保证日志系统收集到的数据的有效性和完整性,在app崩溃时日志不丢失.

2.保证收集日志的过程不影响app的性能,不能占用过高cpu资源,频繁IO操作造成卡顿现象。

使用 MMAP就非常适用于解决这个问题,MMAP是一种内存映射文件的方法,将文件或者一些对象映射到进程的地址空间。

因为在app崩溃的时候,MMAP写入的映射内存数据也会被操作系统自动回写到文件磁盘空间。

Android中使用mmap,可以通过RandomAccessFile与MappedByteBuffer来配合

五、性能优化

1、内存优化

1、android 内存分类

Android每个应用程序可以分配的内存包括以下几部分,按照从大到小的顺序:

  1. Java堆内存(Java Heap Memory):用于存储Java对象的内存空间。这是应用程序最大的内存区域,其中包括了各种对象实例、数组等。Java堆内存的大小可以通过虚拟机参数进行调整,默认情况下通常在16MB到256MB之间。

  2. Native堆内存(Native Heap Memory):用于存储C/C++代码和本地库的内存空间。Native堆内存的大小一般比Java堆内存小得多,具体大小取决于设备和系统配置。

  3. 栈内存(Stack Memory):用于存储方法调用、局部变量、方法参数等数据。每个线程都会有一个独立的栈内存,用于维护方法调用的上下文。栈内存的大小通常较小,一般在几KB到几MB之间。

  4. 运行时常量池(Runtime Constant Pool):用于存储常量和符号引用。运行时常量池包含了类、方法、字段的符号引用以及字符串等常量。它位于方法区(Method Area)中,占用一部分堆内存空间。

  5. 方法区(Method Area):用于存储类的元数据信息、静态变量、常量池等数据。方法区也是堆内存的一部分,但与Java堆内存和Native堆内存有所区别。

备注:

1、其中 OOM 指的是 java 堆内存

2、图片库Fracsco 、ImageLoader图片加载库的缓存在Native 堆内存,Glide、Picago 缓存在 Java 堆内存。这是前者使用了自己的缓存机制,将缓存存储在Native堆内存中,以提高性能和效率,而后者使用了Android系统提供的LRU缓存机制。

2、OOM 优化

1、Manifest文件中设置android:largeHeap="true"来增加应用程序的堆内存大小

2、优化代码:通过优化应用程序的代码和算法,可以减少内存的使用,从而提高应用程序的性能和稳定性。

3、使用图片压缩:对于需要大量使用图片资源的应用程序,可以使用图片压缩技术来减小图片的尺寸和大小,从而降低内存的使用。

4、使用分页加载:对于需要加载大量数据的应用程序,可以使用分页加载技术来分批加载数据,减少一次性加载数据所需的内存空间。

5、使用内存缓存:对于需要频繁读写数据的应用程序,可以使用内存缓存技术来缓存数据,避免重复读写和占用过多的内存空间。

3、Handler 引起的内存泄漏。

原因:非静态内部类,或者匿名内部类。使得Handler默认持有外部类的引用

解决:将Handler声明为静态内部类,通过弱引用的方式调用,就不会持有Activity的引用,其生命周期就和外部类无关,

2、网络优化

减少网络请求次数:通过合并请求、缓存数据

使用压缩技术:gzip

使用CDN加速:通过使用CDN(内容分发网络)来加速数据传输

3、绘制优化

  1. 减少绘制操作:用invalidate()方法的局部刷新、判断onDraw()方法中的绘制条件来减少绘制操作。
  2. 使用硬件加速:设置android:hardwareAccelerated="true"来开启硬件加速,将部分绘制操作转移到GPU进行处理,提高绘制效率。
  3. 布局优化:减少布局层次的复杂性,避免过深的嵌套。
  4. 视图复用:根使用RecyclerView进行列表项的复用等。

4、App启动优化

延迟初始化和懒加载:将一些不必要立即初始化的组件或资源延迟到真正需要时再初始化,避免不必要的启动时间消耗。

减少主线程阻塞:在应用程序启动过程中,主线程的阻塞会导致界面卡顿,影响用户体验。可以将耗时的操作(如网络请求、数据库查询等)放在子线程中执行,避免阻塞主线程。

5、Leakcanery中referencequeue底层实现是什么

LeakCanary中ReferenceQueue底层实现是使用Java中的ReferenceQueue类。ReferenceQueue是一个队列,用于保存被垃圾回收器标记为可回收的对象的引用。当这些对象被垃圾回收器回收时,它们的引用会被添加到ReferenceQueue中。LeakCanary会检查ReferenceQueue中是否有引用,如果有,就说明该对象已经被回收,因此可以判断出是否存在内存泄漏。

在LeakCanary中,ReferenceQueue底层的实现是使用Java中的java.lang.ref.ReferenceQueue类。这个类是Java提供的用于处理弱引用、软引用和虚引用的队列。它的主要作用是收集被垃圾回收器回收的对象,并通知相应的引用对象。通过使用ReferenceQueue,LeakCanary能够跟踪并监测应用中的潜在内存泄漏问题。

六、第三方框架

1、讲一讲Glide的原理

内部是使用 LruCache、弱引用和硬盘缓存实现的。 先从缓存中读取,然后磁盘 最后网络

图片加载策略: 首先从ActivateResource获取,是个值为弱引用的Map

LruCache 最近最少使用算法。核心思想是当缓存满时,会优先淘汰那些近期最少使用的缓存对象。其内部采用的数据结构是LinkedHashMap,当缓存满时,把最近最少使用的对象从内存中移除,并提供了get和put方法来完成缓存的获取和添加操作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/387635.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

替换后端国外身份目录服务,宁盾身份域管接管FileNet助力国产化升级

IBM FileNet 是一款优秀的企业内容管理解决方案,为客户提供了领先的文档管理和流程管理集成环境,被大量企业所采用。FileNet 需要使用企业级的目录服务器(LDAP)作为其用户管理系统,满足其认证和授权的需求。对于 LDAP …

成为git砖家(4): git status 命令简介

1. untracked 和 tracked 状态 Remember that each file in your working directory can be in one of two states: tracked or untracked. Tracked files are files that were in the last snapshot, as well as any newly staged files; they can be unmodified, modified, o…

zabbix使用脚本自定义监控项

1. 在zabbix_agent的配置文件中配置自定义key和脚本位置 vim /etc/zabbix/zabbix_agentd.confUserParametermq_check_log,/etc/zabbix/zabbix_agentd.d/mqlog.shmq_check_log:是这个自定义参数的名称。在Zabbix的监控项(item)配置中&#xf…

点菜吧——随便点 C#生成套餐

前言 一到食堂发现有多种选择,但是有一个固定的套路,只能是一个荤,二个小荤,菜品数量也不少,任君选择,如果是一个选择困难症,就有点烦了,所以出品这个自动生成套餐软件。各位老板可…

代码随想录算法训练营Day 63| 图论 part03 | 417.太平洋大西洋水流问题、827.最大人工岛、127. 单词接龙

代码随想录算法训练营Day 63| 图论 part03 | 417.太平洋大西洋水流问题、827.最大人工岛、127. 单词接龙 文章目录 代码随想录算法训练营Day 63| 图论 part03 | 417.太平洋大西洋水流问题、827.最大人工岛、127. 单词接龙17.太平洋大西洋水流问题一、DFS二、BFS三、本题总结 82…

在手机查看笔记本电脑上的便签 笔记本电脑和手机共享便签方法

在这个信息时代,笔记本电脑已成为我们工作和学习中不可或缺的工具。我经常在笔记本上记录各种便签,无论是工作中的待办事项,还是生活中的小提醒,都依赖于这些小小的便签。它们轻便、灵活,可以随时随地提醒我接下来要做…

TongHttpServer 简介

1. 概述 随着网络技术的飞速发展,高并发大用户场景越来越普遍,单一应用服务节点已经不能满足并发需求,为了提高整个系统可靠性,扩展性,吞吐率,通常将多个应用服务器通过硬负载/软负载组成集群,负载均衡器根据不同负载算法将请求分发到各个应用服务器节点。 Tong…

花几千上万学习Java,真没必要!(三十六)

1、File类: 测试代码1: package filetest.com; import java.io.File; import java.io.IOException; public class FileOperations { public static void main(String[] args) { // 创建新文件File file new File("example.txt"); tr…

Prometheus+Grafana+Alertmanager监控告警

PrometheusGrafanaAlertmanager告警 Alertmanager开源地址:github.com/prometheus Prometheus是一款基于时序数据库的开源监控告警系统,它是SoundCloud公司开源的,SoundCloud的服务架构是微服务架构,他们开发了很多微服务&#xf…

TCP为什么需要四次挥手?

tcp为什么需要四次挥手? 答案有两个: 1.将发送fin包的权限交给被动断开方的应用层去处理,也就是让程序员处理 2.接第一个答案,应用层有了发送fin的权限,可以在发送fin前继续向对端发送消息 为了搞清楚这个问题&…

前端开发知识-vue

大括号里边放键值对,即是一个对象。 一、vue可以简化前端javascript的操作。 主要特点是可以实现视图、数据的双向绑定。 使用vue主要分为三个步骤: 1.javascript中引入vue.js 可以src中可以是vue的网址,也可以是本地下载。 2.在javasc…

网络爬虫必备工具:代理IP科普指南

文章目录 1. 网络爬虫简介1.1 什么是网络爬虫?1.2 网络爬虫的应用领域1.3 网络爬虫面临的主要挑战 2. 代理IP:爬虫的得力助手2.1 代理IP的定义和工作原理2.2 爬虫使用代理IP的必要性 3. 代理IP的类型及其在爬虫中的应用3.1 动态住宅代理3.2 动态数据中心…

数据挖掘-数据预处理

来自🥬🐶程序员 Truraly | 田园 的博客,最新文章首发于:田园幻想乡 | 原文链接 | github (欢迎关注) 文章目录 3.3.1 数据的中心趋势平均数和加权平均数众数,中位数和均值描述数据的离散程度 &a…

【MySQL】用户管理连接池原理{数据库权限/连接池/mysql访问逻辑}

文章目录 1.普通用户的引入用户创建用户删除用户修改用户密码 2.数据库的权限给用户授权回收权限实操过程 3.简略介绍mysql连接池3.一个用户注册/登录/使用网页版mysql逻辑 1.普通用户的引入 用户 MySQL中的用户,都存储在系统数据库mysql的user表中 mysql> use…

uniapp微信小程序本地和真机调试文件图片上传成功但体验版不成功

文章目录 导文是因为要添加服务器域名! 导文 uniapp微信小程序本地和真机调试文件图片上传成功但体验版不成功 uniapp微信小程序体验版上传图片不成功 微信小程序本地和真机调试文件图片上传成功但体验版不成功 是因为要添加服务器域名! 先看一下 你小程…

解决jenkins配置extendreport不展示样式

下载插件:Groovy 、 HTML Publisher plugin 配置: 1)Post Steps : 增加 Execute system Groovy script , 内容: System.setProperty("hudson.model.DirectoryBrowserSupport.CSP", "&qu…

C++ | string

前言 本篇博客讲解c中的string类的使用(常用接口) 💓 个人主页:普通young man-CSDN博客 ⏩ 文章专栏:C_普通young man的博客-CSDN博客 ⏩ 本人giee:普通小青年 (pu-tong-young-man) - Gitee.com 若有问题 评论区见📝 &#x1f389…

Magento2 常用命令以及技巧

1.Magento 命令行工具 Magento2 带有一个命令行工具,在windows下,用管理员权限打开MS-DOS命令提示符,然后cd到Magento根目录,运行下面命令,就可 以看到这个强大的命令行工具的命令清单: php bin/magentoU…

【C++】C++11中R字符串的作用

在 C11 中添加了定义原始字符串的字面量 1.定义和基本使用 定义方式为: R"xxx(原始字符串)xxx"其中 () 两边的字符串可以省略,R只会处理括号中的字符串。 原始字面量 R 可以直接表示字符串的实际含义,而不需要额外对字符串做转义…