Android面试题总结——持续更新
文章目录
-
Android
- 四个核心功能模块
-
活动(Activity)
- 生命cycle管理
-
在Activity场景中
- 启动另一个活动周期
-
四种启动方式
-
- 如何给Activity指定启动模式
-
- 四个核心功能模块
-
为何有必要调用onNewIntent()
-
调用onNewIntent()这一方法可能在哪些实际应用中出现
-
在何时调用savesaveStateState()以及在何时调用它的回调函数onRestoreStateState()
-
操作流程中的具体执行环节
- 当屏幕进行旋转操作时会触发哪些特定事件
-
请说明如何将一个Activity设置为窗口样式。
-
请描述退出应用程序的方法,并说明如何关闭所有的Activity。
-
通过Service进行数据或消息的交互与沟通。
* Service * * Service的两种形式-
- 本地服务
- 远程服务
-
Service 的两种状态
-
-
后台服务
-
- 如何保证Service不被杀死
-
前台服务
-
-
服务的三种启动方式:
* 启动服务函数
* 绑定服务
* 在 Foreground 环境中启动主进程
-
IntentService
-
-
IntentService 的特性
-
在创建过程中需注意哪些关键因素?
- 关闭服务
- 系统服务
-
-
ActivityManagerService
-
BroadcastReceive
-
- 广播类型
-
ContentProvider
-
- URI
-
-
-
Android进程的优先级设置
-
在Android系统中是否存在自定义Main方法的支持?
-
开发过程中如何利用Manifest.xml文件构建基本布局?
-
触控组件相关的属性及使用场景有哪些?
-
SurfaceView与普通View在功能上有哪些区别?
-
invalidated与postInvalidated这两个术语的主要作用有何不同?
-
在触控事件处理中,请详细说明onTouch、onTouchEvent以及onClick分别对应哪些操作及其相互之间的关联性。
DP(Dot Pitch)、DPI(Dot Per Inch)与PX(Pitch Per Unit)之间最大的区别在于它们所代表的单位定义不同以及具体的应用场景。DP用于表示屏幕像素之间的密度大小;DPI则衡量的是每英寸有多少个像素;而PX通常被用来描述UI元素的最小单位长度。
Android中的Android Application(Activity)、View与Window三者之间存在怎样的关联?Activity作为应用程序的主要执行单元,在应用逻辑执行层面起到核心作用;而View则主要用于显示特定的内容或界面;Window则是系统内部展示的应用程序实例窗口。这三者共同构成了Android应用程序运行的基础架构体系。
Android开发过程中内存泄漏通常由以下原因引起:一方面应用未能及时释放已经不再使用的资源;另一方面过度使用共享资源导致竞争排斥效应;此外内存泄漏还可能源于外部库函数的不兼容性或是开发者代码中的潜在错误操作等多方面因素的影响。
JNI全称为Java Native Interface是Java编程语言与操作系统之间实现通信的一种桥梁机制它允许Java应用程序调用底层操作系统提供的API功能从而在Java虚拟机层面上实现跨平台的代码复用优势显著地提升了软件开发效率并为移动设备上的性能优化提供了有力的技术支持。
NDK全称是NVIDIA Developer Kit它是用于构建高性能移动设备应用的关键组件其主要优势体现在以下几个方面:第一它能够高效地整合硬件加速功能如GPU计算能力提升应用性能表现;第二它提供了丰富的开发工具和框架简化了复杂的应用开发流程;第三支持跨平台特性使其能够在多种移动设备平台上实现统一的开发策略从而提高代码复用率和维护效率;第四通过优化系统资源管理使得应用运行更加流畅且能更好地应对复杂计算任务的需求等多方面的技术优势使其成为现代移动应用开发的重要基石之一。
* so文件
* JNI的命名规则
- 在Android开发中常会遇到内存泄漏的问题
- 通过Handle实现异步任务或Runnable时的操作时可能导致的内存泄漏
-
由于资源未被正确释放而导致的应用内存在 memory leaks
- Fragment
-
- 生命周期
- Activity 和 Fragment 的异同?
- Fragment的优点
-
Android应用中所采用的数据存储方式主要包括数据库技术与文件系统相结合的方案。
Android支持多线程通信的机制,并通过内核级协议实现各组件之间的信息交互。
在启动过程中,用户可以通过系统提供的命令行工具来执行相应的操作。
多线程带来的潜在问题是资源利用率的提升可能会引发性能瓶颈。
为了实现高效可靠的消息传递功能,在 Android系统中引入了专门的线程间通信框架Binder。
该框架的优势在于能够快速地建立并管理多个互相关联的任务实例。
在客户端/服务器架构模式下,默认配置通常会为各个服务提供独立的资源分配空间以确保系统的稳定性。
* Android中的应用
* 为什么不能在子线程访问UI
* * 为什么不对子线程上锁
* Handle
* * Looper
* Message创建
* Looper无限循环为什么没有阻塞ANR
* AsyncTask
* * 为什么要用AsyncTask
* 注意事项
- 线程间通信
-
事件处理器线程
-
位图
-
视图
-
视图绘制流程
- 测量大小操作
-
布局管理函数
-
绘图函数
-
ViewGroup
-
- View和ViewGroup
- ViewGroup绘制流程
-
margin和padding的区别
-
ListView和RecycleView
-
- ListView优化
-
ANR
-
Bundle
-
Android性能优化
-
Android
四大组件
Activity
Activity作为一个核心组件(也被称为Android四大组件之一),在移动应用开发中扮演着关键角色。它为开发者提供了构建复杂应用的基础架构模型,并允许用户提供交互并完成一系列操作(如拨打电话、拍照、发送电子邮件及查看地图)。每个Activity都包含一个独特的界面 window用于展示其用户界面。这些 window 通常占据整个屏幕区域,但可能尺寸较小,并位于其他 window 的顶端菜单栏中。
生命周期

Activity 应用场景
- 当Activity A启动时:执行A对象的启动流程。
- 按下home键返回桌面。
- 当重新启动桌面时:执行活动A的状态机重启流程。
- 将该活动退出后:完成活动A的状态机关闭流程。
- 当系统内存不足时:将该后台进程终止并将其重置到初始状态;若再次尝试唤醒该应用,则会执行完整的重启流程。
- 在锁定屏幕时只会执行一次唤醒操作(即调用
onPause()),而不会触发onStop()方法;只有在解锁屏幕后才会触发onorum()方法。
Activity 中启动一个新的 Activity 生命周期变化
- 当Activity A被启动时触发事件B
- Activity A执行暂停操作
- 当事件B发生时依次执行创建、启动和恢复操作
- 如果此时Activity A不可见,则执行停止操作
四种启动方式
standard
标准模式:创建一个Activity然后放入任务栈中。
单线程模式:首先进行判断当前栈顶Activity是否属于当前需求的Activity类型。如果是,则直接使用该栈顶Activity实例;如果不符合,则新建该Activity并将其插入到栈中。
基于单例的任务栈机制:该系统会检查任务栈内是否存在与当前Activity相同的项。如果存在,则会移除栈顶及其上方所有项;否则会启动新活动以重新创建该Activity的位置。
singleInstance
单任务栈模式:创建一个新的TaskStack用于该Activity,并设置其仅有一个实例以实现独享资源管理功能;从而使得该Activity独自拥有一个TaskStack;当已有此实例被激活后再次启动时将触发onNewIntent事件。
如何给Activity指定启动模式
- 在AndroidManifest文件中配置Activity的运行方式时,默认设置为单线程任务模式。
- 通过在Intent对象中添加标志位来明确指示Activity的新任务状态,则可实现其以单线程任务模式执行。
Intent.FLAG_ACTIVITY_NEW_TASK,是为Activity指定“singleTask”启动模式
Intent.FLAG_ACTIVITY_SINGLE_TOP,是为Activity指定“singleTop”启动模式
为什么需要onNewIntent()
因为Activity启动可能会通过Intent传递参数
- 如果首次初始化,则可在
.onCreate()方法内接收数据。 - 在单线程/单任务/单实例模式下(如果不是首次初始化),由于重新启动时可能不会调用
create方法,在.onNewIntent()阶段获取参数。
onNewIntent()方法应用场景
- Activity A从后台onStop()状态重新唤醒
- A->onStop()->onNewIntent()->onRestart()->onStart()->onResume();
- 当Activity A处于singleTop模式,此时要再次启动A:
- Activity A中启动Activity A
- A->onResume()->onPause()->onNewIntent()->onResume();
- 当Activity A处于singleTask或者singleInstance模式时:
- 在Activity A中触发Activity B的运行。
- 在Activity B中触发Activity A的运行。
- 在整个过程中:
- 首先触发停止事件
- 然后触发新intent事件
- 接着进行重启操作
- 再次进入启动流程
- 最后完成继续执行步骤。
onSaveInstanceState() 和 onRestoreInstanceState()
- 如果因系统强制手段而非正常应用程序行为导致Activity被终止,则即使该实例已退出应用环境但它仍会被系统保留状态信息以便后续恢复。
- 每次翻转屏幕方向都会导致当前Activity被终止并重新创建新的实例该操作会触发saveInstanceState()和restoreInstanceState()方法。
- 该逻辑会在onStop事件处理前执行但不会影响到onPause事件的发生。
- 系统启动后会自动执行Created事件但如果Bundle为空则需手动调用相关方法而无需单独检查Bundle是否存在 restoration仅在有保存状态时才会被触发且会在Start事件之后运行。
请注意,在用户自行按下了回车键或调用了exit()函数退出程序的情况下,不会触发onSaveInstanceState()和onRestoreInstanceState()。

执行场景
- 当用户按下Home键执行操作时
- 当用户长时间按下Home键并选择运行其他应用程序时
- 在锁屏状态下
- 当从Activity A启动新的Activity B时
- 当屏幕方向发生切换时
屏幕旋转时发生什么
- Activity启动
I/System.out: onCreate
I/System.out: onStart
I/System.out: onResume
代码解释
- 屏幕旋转
I/System.out: onConfigurationChanged
I/System.out: onPause
I/System.out: onSaveInstanceState
I/System.out: onStop
I/System.out: onDestroy
I/System.out: onCreate
I/System.out: onStart
I/System.out: onRestoreInstanceState
I/System.out: onResume
代码解释
在AndroidManifest.xml文件中为对应Activity设置android:configChanges属性值为'orientation|screenSize'。为了避免因配置改变导致Activity重建,在旋转屏幕时该Activity不会被系统终止或重建,而是会调用onConfigurationChanged方法以响应配置变化。因此,在需要配置程序根据环境变化做出相应调整时,应指定该参数并重写onConfigurationChanged方法即可实现所需功能。
android:configChanges="orientation|screenSize"
代码解释
屏幕旋转时
I/System.out: onConfigurationChanged
代码解释
如何将一个 Activity 设置成窗口的样式?
android:theme="@android:style/Theme.Dialog"
代码解释
如何退出APP并关闭所有Activity
- 发布确定的广播
- 当发生递归退出事件时,在启动新Activity的过程中执行doPost操作,并在此处设置标记;随后,在rgbond组件中进行处理;整个过程完成。
Activity 怎么和 Service 通信
采用广播接收机制
借助Binder对象,在Activity中调用bindService方法启动服务,并由此获得一个Service实例。此Service实例即可直接与相关的Service进行交互。
Service
服务是在Android系统中提供程序后台运行支持的一种解决方案,在Android系统中提供这种解决方案主要是为了支持那些无需与用户进行交互且需要长时间保持运行的任务。这种解决方案不是以单独线程为基础而是基于主线程的架构设计因此,在实际应用过程中应当尽量避免进行那些可能引发异常(ANR)的操作以确保系统的稳定性和可靠性
Service的两种形式
启动本地服务用的是显式启动; 远程服务的启动要用到隐式启动
本地服务
- 该服务依附于主进程上
- 节省资源
- 当主进程被kill,服务也随之停止
远程服务
- 运行在独立的线程中
- 在Application崩溃前(当Activity所在的进程被终止时),该服务依然保持正常运作
- 消耗一定资源,并常用于系统中的Service
- 按照 android:process 标签命名规则。
android:process=":service"
代码解释
Service 的两种状态
后台服务
当某个组件(如Activity)调用startService()方法时,该服务即被激活并处于"运行"状态。一旦被启动后,该服务将在后台持续运行,不受当前组件销毁的影响,除非手动干预才能终止其运行状态。已被启动的服务通常执行单一操作,并且不会将操作结果传递给调用者。
如何保证Service不被杀死
当系统内存不足时,应用程序或服务可能会容易地被终止;如何防止这种情况发生呢?
- 设定优先级范围(从1到1000)
- 将服务标记为远程服务
- 将其标记为前台服务
- 通过广播机制进行实时监控
- 在Service的onStartCommand方法中指定flages字段,并将其值设为START_STICKY字段名;这样做的好处是可以确保当Service被终止后系统会尝试重新启动该实例。
前台服务
手机状态栏上会有该服务的状态标记,并且这种展示方式与通知功能相似的表现形式。因此,在这个位置上显示一个状态标记能够确保该设备不会轻易被系统终止或挂起。
服务的三种启动方式
startService()
生命周期是这样:调用startService()–>onCreate()–>onStartConmon()–> onDestroy()。
- 执行startService之后,在后续的每次startService或(onCreate)方法被执行时, (onStartCommon)方法只会在stopService被调用时才会被执行, 这会导致在stopService后(onDestroy)会自动被触发以完成销毁操作。
- 在启动服务时, 在(onStartCommon)方法中获取相关参数时, 必须首先检查intent是否为null。
bindService()
生命周期走法:bindService()–>onCreate()–>onBind()–>unBind()–>onDestroy()
- 此时的Service处在一个绑定的状态
- 执行bindService()将返回一个Binder对象,使用该Binder对象可以让Activity完成与Service的交互
startForegroundService()
启动前台服务
IntentService
IntentService 可被视为由 Service 和 HandlerThread 组合而成,在实现了使命后会自动退出,并特别适用于在工作线程中执行与 UI 无关的任务。
- IntentService 是基于 Service 类并专门处理异步请求的一个类,在其内部运行一个作业线程来负责处理那些需要长时间运行的操作。
- 当任务完成时系统会自动关闭该服务无需我们手动终止。
- 每次启动 IntentService 时系统都会将所有的耗时操作按照工作队列的形式分配给 onHandleIntent 方法来进行处理这些操作会被依次执行并采用串行方式运行最终完成后系统会主动关闭该服务。
IntentService 的特点
- 本质上是一种服务,在系统中拥有更高的优先级,并且不容易被系统杀死;它能够承担具有高优先级的任务。
- 该组件能够自动启动子线程以执行任务,并确保在任务完成时自动终止这些子线程。
为何不用bindService方式创建IntentService?
IntentService的工作原理在于,在其onCreate()方法内生成一个HandlerThread,并通过其Looper实例化一个ServiceHandler对象;这一特定设计使得service handler能够负责处理消息的handleMessage()方法所调用的服务意图处理逻辑(即onHandleIntent()).这种安排也解释了为何能在该服务意图处理逻辑中实现后台任务处理功能.当有一个intent请求时将intent被封装进message中随后service handler负责将这些message发送出去这一过程由onStartCommand()执行然而只有通过调用startService()才能触发此生命周期阶段的方法.因此无法直接使用bindService来创建此类intent服务.
关闭服务
- 必须主动发起并实现停止stopService。
- intent service无需干预,在任务完成后会自然释放。
系统服务
该处为图片转存失败(链接无效),无法正常显示
ActivityManagerService
在Android系统中发挥核心作用的服务...能够有效地协调启动、切换以及管理应用进程的运行和调度工作,并且其功能类似于操作系统中的进程管理与调度机制。
BroadcastReceive
广播(Broadcast)是一种广泛运用的在应用程序之间传输信息的机制。
- 广播机制(Broadcast) - 一种分发信息的方式。
- 广播接收设备(BroadcastReceiver) - 设备能够接收广播信号;
- 数据存储模块(Intent) - 负责记录与分发相关的各种信息。
广播类型
传统的广播方式通过异步发送实现信息传播,在这一过程中具有较高的传输效率。所有接收到的广播内容均可传播出去。然而其主要缺陷在于一个接收到该信息后无法将处理结果传递给下一个收件人,并且无法停止该过程。
-
有序广播 按照广播接收者的优先级排序原则进行广播传递。有序广播信息可能会被丢弃以减少数据包的数量,从而导致后续接收者无法接收到该消息。
-
系统广播
系统广播是Android内置的广播,发送系统状态。 -
本地广播
广播只在应用程序内部传递。 -
动态注册广播机制
通过Context.registerReceiver进行动态注册可以在应用开发过程中实现灵活的管理功能该机制允许开发者根据需求实现对服务实例的动态连接与断开操作并且仅在程序运行后才开始工作以确保资源的安全性。
在AndroidManifest.xml文件中定义广播的注册方式被称为静态注册。这种机制属于一种持久性机制,在应用退出后若仍需接收广播信息时,系统会自动执行相应的操作以处理这些信息。
- 粘性消息传播机制
自发送之后便持续存在于系统消息容器中,并即当广播接收器注册后立即即可接收到广播。
ContentProvider
ContentProvider的主要职责是管理存储与共享功能。与文件存储、SharedPreferences存储以及SQLite数据库存储等常见方法相比,则其显著的优势在于能够实现跨应用程序的数据互通,并且支持按需限定哪些区域的数据被访问以防止敏感信息泄露的有效防止。
- 是一个抽象类
- 利用URI来访问资源
URI
URI(Uniform Resource Identifier)被定义为统一资源标识符,并表示为一个表示互联网资源名称的字符序列
Android 进程优先级
优先级从高到低
- 前台进程:负责与用户进行交互运行的进程(包括当前APP中的activity和服务)
- 可视进程:其界面或操作对用户可见但无法通过用户的交互来做出响应
- 服务进程中没有提供可视化界面但仍在持续执行中的一般由service组件处理
- 后台进程中包括进入onStop()方法时被触发的所有活动
- 空进程中指系统为了提高性能会缓存已经完成生命周期的应用程序以备下次启动使用,并非真正意义上的空闲状态
Android有main方法吗
有main方法,main方法在ActivityThread类中的第 6041行 main(String[] args)。
Manifest.xml文件中主要包括哪些信息?
- manifest:根节点,描述了package中所有的内容。
- uses-permission:请求你的package正常运作所需赋予的安全许可。
- permission: 声明了安全许可来限制哪些程序能你package中的组件和功能。
- instrumentation:声明了用来测试此package或其他package指令组件的代码。
- application:包含package中application级别组件声明的根节点。
- activity:Activity是用来与用户交互的主要工具。
- receiver:IntentReceiver能使的application获得数据的改变或者发生的操作,即使它当前不在运行。
- service:Service是能在后台运行任意时间的组件。
- provider:ContentProvider是用来管理持久化数据并发布给其他应用程序使用的组件。
总结:
- 四大组件
- 运行权限
- APP相关
View相关
SurfaceView和View的区别是什么?
SurfaceView采用的是双缓存技术,在独立运行的一条处理线上更新界面;该系统中的View处于UI处理线路上,并对该界面进行更新
- SurfaceView是基于View基类开发的一种显示类型。
- 在UI主线上更新画面的是一个典型的显示组件,在子线程上执行刷新操作。
- 在主动更新时通常选择普通显示组件如View,在被动更新场景下推荐使用SurfaceView。例如,在频繁刷新的需求下选择后者更为合适。
- 由于采用了双缓冲机制,SurfaceView能够在不中断数据处理的情况下持续输出。
invalidate()和postInvalidate()的区别?
inhibit()和postInhibit()均用于刷新View界面。其主要区别在于inhibit()必须在主进程中执行操作以确保数据一致性...相比之下, postInhibit()可以在子进程中直接发起操作以提高效率
onTouch()、onTouchEvent()和onClick()关系?
优先级上依次为on\ Touch(),\ on\ onTouchFace(),\ 然后是\ onClick(). 由于\ on\ Listener\ 的\ on\ Touch()\ 方法具有较高的优先级,在此情况下它会被优先执行;只有当\ on\ Touch()\ 返回\ false\ 时才会依次执行后续的方法。\ 类似地,\ 内置如\ onClick()\ 事件等的实现也都是基于\ on\ onTouchFace()\ 方法。\ 当\ on\ Touch()\ 返回\ true$\ 时,\ 这些事件就不会被触发。\
dp、dpi、px的区别
- 像素单位(px)
- 每英寸像素密度(PPI)
- 相对像素单位(RPU),便于在不同设备上实现良好的适应性
通过简单的数学计算即可将RPU转换为PX:PX = RPU × (PPI / 160)
Activity、View、Window三者之间的关系?
当Activity启动时,在其attach()方法中初始化了PhoneWindow,并且PhoneWindow作为唯一的实现类;接着Activity通过调用setContentView将指定的View设置在PhoneWindow上;最后, View借助于 WindowManager中的addView( )、removeView( )以及updateViewLayout( )来动态管理自身.
- 可以将Activity视为一个功能集合体
- Window类似于打开了一个展示界面
- View能够在窗体中呈现多样的数据内容
Android中内存泄漏的原因
- 广播没有unregisterrecevier
- 静态集合Vector、HashMap
后面再补充
JNI
JNI 全称是 Java Native Interface 即本地接口 。该特性使 Java 能够调用 C/C++ 等语言的代码 。通过 JNI 可以在编写 Java 代码时使用 C/C++ 库 在编写 C/C++ 代码时使用 Java 库 。由于 JNI 是 JVM 规范 的一部分 因此我们可以将任何实现了 JNI 规范 的 Java 虚拟机运行我们的 JNI 程序 。同时 这一特性允许我们复用以前用 C/C++ 编写的大量代码 。这是一个在 Java 虚拟机机制下执行的标准机制 。我们可以通过编写成汇编程序或者 C/C++ 程序 并组装为动态库 来实现这一机制 。通过非静态绑定实现跨平台调用 。这一机制允许我们在不同平台上互用资源
NDK
Android NDK是一系列开发工具包,支持开发者通过C/C++语言构建和优化应用程序的不同组件。
优点
- C/C++效率高
- 便于加密
- 有些优秀的C/C++第三方库
- 方便平台移植
so文件
因为C语言的不跨平台,使用NDK编译在Linux下能执行的函数库——so文件。
JNI的命名规则
*jEATN 对应于 Java 环境,在借助函数表的方式下操作 Java 数据或调用 Java 方法。
*jobject 指代被调用的对象
JNIExport 返回值 JNICALL 包名_类名_方法名(JNIEnv* env,jobject thiz,...参数)
代码解释
Android 中的内存泄漏
内存资源出现泄漏的原因在于长时间存活的对象持续不断地引用了短暂生命周期的对象,从而导致这些短暂生命周期的对象无法在预期的时间内被垃圾回收机制清理掉。
Handle(AsyncTask/Runnable)造成的内存泄漏
在创建Handler时采用内部类机制(包括匿名类),该Handler实例会间接保留外部父类对象的引用。当用户在处理网络请求时关闭了Activity,在通常情况下,在请求处理完毕后Activity将不再被直接使用并由系统回收以释放内存资源。然而由于此时线程尚未完成所有操作任务,并且该线程依然持有对Handler的引用而Handler又间接持有对Activity对象的引用这就可能导致Activity无法被及时回收从而出现内存泄漏的问题直到整个网络请求处理流程结束为止才能彻底释放相关的内存资源以避免内存泄漏问题最好的解决方案是将Handler声明为静态类型以避免其直接持有外部父类对象的引用通过这种方式即使活动生命周期结束后也能确保相关对象能够被安全回收从而消除潜在的内存泄漏风险
资源未关闭导致的内存泄漏
未能及时注销资源会造成内存泄漏问题,并包括其BroadcasterReceiver、File、Cursor、Stream和Bitmap等
Fragment
生命周期

Fragment在创建到销毁整个生命周期中所包含的方法依次为:
...(此处保留数学公式)。
其中与Android Activity有不少名称相同、作用相似的功能模块或方法有:
而不同的功能模块或方法则有各自的特点。
onAttach():当Fragment和Activity建立关联时调用
onCreateView():当Fragment创建视图时调用
onProcessing(): 在与其相关的Fragment的Activity完成后自动执行此方法.
onDestroyView():在Fragment中的布局被移除时调用
onDetach():当Fragment和Activity解除关联时调用
Activity 和 Fragment 的异同?
- Fragment与Activity在某些方面是相同的特性,它们都拥有布局以及右子节周期性结构,其中Fragment类似于迷你的Activity形态存在。
- Fragment基于Activity提供服务,增加了与宿主系统交互所需的一些方法,包括onAttach()、onProcessing()以及onDetach()等。
- Fragment的生命循环是由其宿主Activity发起,而非直接由操作系统处理。
- Fragment的设计灵感来源于其名字"碎片",主要目的是应对Android应用中的屏幕碎片化问题,它能够作为活动界面的重要组成部分,允许在同一时间动态地被添加、移除或互换位置。
- 每个Activity支持在同一时间在其生命周期内包含多个Fragment实例,并且一个Fragment单元也可以被多个不同的Activity所共享使用。
Fragment的优点
- 模块化(Modularity):我们需要将所有的代码分别放入对应的Fragment而不是统一在一个Activity中。
- 可重用性(Reusability):我们可以将同一个Fragment复用于多个Activity。
- 可适配性(Adaptability):通过基于硬件屏幕尺寸和方向的不同设置方案来灵活实现多样化的布局方案。这不仅提升了开发效率也显著改善了用户体验。
- 使用Fragment的方式能够方便地实现同一界面的多次复用从而加快开发速度
Android 中的数据存储技术
- File 文件存储:写入和读取文件的方法和 Java中实现I/O的程序一样
public void saveData(byte[] data,String path){
try {
File file = new File(path);
FileOutputStream fos = new FileOutputStream(file);
fos.write(data);
fos.close();
}catch (Exception e){
e.printStackTrace();
}
}
public byte[] readData(String path){
byte[] data = null;
try {
File file = new File(path);
FileInputStream fis = new FileInputStream (file);
data = new byte[fis.available()];
fis.read(data);
}catch (Exception e){
e.printStackTrace();
}
return data;
}
代码解释
SharedPreferences常用于存储:一种轻量级的数据存储方案,主要用于保存一些基础配置数据。其本质是通过XML文件实现键值对的数据存储。SharedPreferences的读/写操作通常会采用缓存策略,即在内存中保留一份该文件副本以提高效率。然而,在多进程中可能会导致读/写操作不可靠并可能导致数据丢失。
public static void saveSongData(Context context,String database,String key,String data){
SharedPreferences.Editor editor = context.getSharedPreferences(database,Context.MODE_PRIVATE)
.edit();
editor.putString(key,data);
editor.apply();
}
public static String getSongData(Context context,String database,String key){
SharedPreferences sharedPreferences = context.getSharedPreferences(database,Context.MODE_PRIVATE);
return sharedPreferences.getString(key,"");
}
代码解释
- SQLite数据库存储:一款轻量级的关系型数据库系统,在性能和资源消耗方面表现优异,并特别适合处理复杂关系型数据集。
- ContentProvider:作为四大核心组件之一,在数据存储与共享功能上具有重要地位,并能够实现不同应用程序间的共享机制;特别支持限定共享范围以确保程序中的隐私数据不会泄露风险。
Android 多进程通信(ICP)
如何开启多进程
- 请为Manifest文件中的(Activity、Service及Receiver)配置activity:service属性。
- 该属性值以':':起始的进程属于私有类型。
- 该属性值不以':':起始则归于全局进程。
- Android系统会给每个应用分配唯一的Identifier。
- 只当两个应用拥有相同的Identifier时才可以共享数据。
- 此外这两个应用还须具有相同的签名。
多进程造成的影响
- 静态变量和单例模式失效:由于使用了独立的虚拟机而导致
- 线程同步机制失效:因为系统采用的是独立的虚拟机架构
- SharedPreference的不可靠下降:导致共享偏好协议出现不可靠性,在多线程环境下无法实现并发读写功能,并存在数据一致性风险
- 当多个进程运行时:Android系统会对每个新进程分配独立虚拟机资源
Binder
优点
传输性能高效可靠:该系统的主要技术优势在于其依赖于内存复制频率作为核心计算资源。当内存复制频率降低时,系统的整体提升幅度会越大。
基于Android进程架构的角度进行分析可知:就消息队列、Socket以及管道而言数据首先会经历以下过程即会先从发送端设备的本地缓存复制至系统内核专用缓存区域随后又会通过系统内核专用缓存区域传递至接收端设备的本地缓存区域完成两次传输过程如图所示

在Binder中,在源端缓存区的数据被传输至内核缓存区时,在目标端缓存区与内核缓存区共享同一块物理内存区域以避免了一次数据传输的过程。

便于实现C/S架构
注
安全性能高:传统Linux内核中使用IPC(信息传递接口)机制时,在接收端无法可靠地获取被接收进程的UID和PID信息(Unique Identifier和Process ID),因此无法识别发送方的身份;而采用Binder机制后,在进行IPC通信时会基于接收方的UID和PID信息来进行有效性验证。
C/S架构
C/S架构的优点 :
该系统的C/S架构界面对话界面及其操作功能设计得非常灵活(支持客户端界面对话界面根据实际需求进行灵活布局),能够有效满足不同场景下的使用需求(能够适应客户需求)
安全性能可以很容易保证。(因为只有两层的传输,而不是中间有很多层。
因为仅存在一层交互关系而无需多级通信中转,在实际应用体验上能够显著提升响应速度(即操作效率)。在操作界面设计上实现了直接连接,在线用户之间无需经过任何间接节点或中转环节。
C/S架构的缺点:
适用面窄,通常用于局域网中。
用户的群体具有固定性。由于程序仅在安装后才能被使用,并且该方法不适用于那些信息不明确的用户
维护成本高,发生一次升级,则所有客户端的程序都需要改变。
Android中的应用
Service以bindService()方式启动。
为什么不能在子线程访问UI
System provides a warning to developers to avoid accessing UI components in sub-threads due to the fact that these components are typically non-blocking, and concurrent access from multiple threads may result in unpredictable interface states.
为什么不对子线程上锁
- 效率变低
- 上锁后容易造成ANR
Handle
消息(Message):待传输的信息中包含着标识信息、处理对象以及相关信息等,由MessageQueue统一管理后将这些信息发送至Handler进行接收并执行。
MessageQueue(消息队列):负责存储由Handler发送过来的消息,并采用单链表数据结构管理消息列表以实现供Looper进行抽取操作的功能。
Handler(处理者):负责Message的发送及处理。
- Handler.sendInfo():将不同类型的事件信息发送至消息池中。
- Handler.parseAndResponse(): 解析并响应相应的事件。
消息泵Looper:调用循环函数loop()持续地从message queue中提取message,并根据分布机制将信息传递至目标处理器。
消息泵Looper:调用循环函数loop()持续地从message queue中提取message,并根据分布机制将信息传递至目标处理器。


Looper
public void test() {
new Thread(){
@Override
public void run() {
super.run();
Looper.prepare();
Handler handler = new Handler();//默认为当前线程Looper
Looper.loop();
}
}.start();
}
代码解释
- 每个线程最多只有一个Looper实例。
- 当一个线程调用Looper.prepare()方法时,系统会自动生成一条消息队列,并立即启动Looper.loop()方法,并进入持续的消息处理循环(无限循环)。在此之后就可以发送消息、取消息并进行相应的消息处理。
- 调用Looper.prepare()方法后,原始Thread会被转换为对应的Looper实例。与普通Thread相比,在这种情况下它会通过MessageQueue来存储消息和事件信息,并由Loop-loop().method来进行轮询操作。
Message创建
- messagedata msg =取得实例;
- messagedata msg = MessageClass.obtain();
- messagedata msg = handler1.obtainMessage();
后两种模式能够有效避免从整体Message池中重复创建对象的情况出现。因此这种模式能够更有效地被采用。
Looper无限循环为什么没有阻塞ANR
在整个周期流程中涉及的手动处理、循环机制以及消息传递系统构成了Activity的核心功能模块。应用采用基于事件驱动的方式运行代码逻辑,在这种设计下系统运行更加高效可靠。
AsyncTask
底层集成了一个线程池和一个Handler组件,并且能够使得远程作业得以顺利执行的同时支持在子进程中进行界面交互
为什么要用AsyncTask
- 线程管理机制存在的问题:在多个任务并行运行时难以精准管理每个线程的执行流程。
- 引入AsyncTask的好处:简化了创建异步作业的过程;通过继承该类实现了后台异步作业的执行与进度反馈回控更新UI界面,并且无需手动编写代码块来处理作业调度与结果反馈的问题。
注意事项
- 应避免手动触发 onPreExecute 检查功能、doInBackground 开发模式监控、onProgressUpdate 进度更新通知、onComplete 完成事件触发以及 onCancelled 取消事件触发等回调函数
- 异步执行机制确保每次只能执行一次任务
线程通信
- Handle
- AsyncTask
- HandlerThread
HandlerThread
HandlerThread属于一种线程类,在其基础架构上承袭自Base.Thread类型。相较于普通LineConcurrent Thread而言,HandlerThread具备独特的消息循环机制。该线程由于其内部HandlerThread.run()方法中包含Looper组件的存在而得以实现这一特性:通过Looper.prepare()建立消息队列,并利用Looper.loop()从而实现消息循环的持续运转。
- 创建一个名为HanderThread的对象,并将其参数设置为该线程的名字。
- 调用start方法启动该线程。
- 创建一个Hander实例,并将其传递给HanderThread中的looper属性以实现与其绑定。
- 此onents可直接处理异步操作。
- 若无需使用HanderThread时,则可通过调用quit或quitSafely方法终止该线程。
Bitmap
- 在实际应用中发现直接加载大容量的高清Bitmap可能会导致显示不完整且常出现显示不完整的现象(OOM异常),因此建议采取按一定采样率缩放图片后再进行加载的方法来解决这一问题。
- 通过优化系统设计,在处理图像数据时采用缓存机制可以有效降低网络传输负担的同时,在资源紧张的情况下也可以通过采用软引用的方式避免因频繁访问而导致的高负载问题。
- 当需要远程获取图片时,请务必启动一个子线程队列来进行耗时的操作以确保整体系统的稳定性不受影响。
View
View的绘制流程
View的绘制流程:OnMeasure()——>OnLayout()——>OnDraw()
OnMeasure()
获取视图尺寸。该递归过程是从顶层父View到子View进行的,并且每次调用都会触发OnMeasure事件。
获取视图尺寸。该递归过程是从顶层父View到子View进行的,并且每次调用都会触发OnMeasure事件。
OnLayout()
设置各视口的位置并完成页面布局。按照自顶向下策略依次对各子视口执行view.layout方法的操作。此过程使父视口根据上一步骤测量所得的子视口尺寸及相关布局参数自动调整其在屏幕中的放置位置。
OnDraw()
通过ViewRoot生成一个Canvas对象,并执行其OnDraw()方法以显示界面。整个流程包含以下六步操作:
- 展示视图的基础部分;
- 保留绘图层的状态;
- 展示应用内容;
- 展示子视窗内容(若存在);
- 恢复绘图层;
- 显示滚动条
ViewGroup
View和ViewGroup
- ViewGroup基于 View。
- View 代表绘制在屏幕上的用户界面元素。另一方面, ViewGroup 则是一个用于存储其他 View (以及嵌套的 ViewGroup) 构成层级结构组织框架。

ViewGroup绘制流程
类似于一个专门用于放置子视图(childView)的空间布局器,在其设计过程中需要考虑并实现多个功能模块以确保子视图能够获得合理的布局资源。该组件的主要职责包括计算建议宽度与高度的任务,并根据需要确定childView的具体位置。在操作实现方面,则涉及多个关键方法的应用与开发。
- onMesure() —— 计算并设置childView的尺寸参数及模式参数。
- onLayout()——通过调用函数获取子视图数量和具体布局信息,并为每个子视图分配布局位置。
- onSizeChanged()——在onMeasure执行后进行操作。
- onDraw()——只有当需要绘制时才会被触发。
margin和padding的区别
- margin是控件到父控件之间的距离
- padding是控件到自身边界的距离
ListView和RecycleView
- 在使用Listview时可以选择是否启用}>{使用RecyclerView则必须启用} }}>
- 每次从Listview取出一个Item时都需要重新绘制对应的View图形。
- 列表视图无法单独更新一个Item的显示效果;而使用RecyclerView则提供了相应的接口来实现局部刷新。
- 支持多种视图布局方式包括线性布局、网格布局和瀑布流布局等,并且由LayoutManager进行管理。
ListView优化
注
-
ViewHolder的优化;
根本原因是由于使用\texttt{findViewById}方法存在较大的延迟问题,在组件数量较多时会导致系统运行迟缓。因此主要目的是为了节省获取视图所需的时间。通过设置和获取标签的方法可以直接访问视图。 -
图片加载优化 ;
为了避免列表视图在运行过程中因频繁加载网络图片而出现卡顿现象, 我们需要通过事件监听机制来检测列表视图的状态变化. 当列表视图发生触控下滑事件(SCROLL_STATE_TOUCH_SCROLL)或猛击下滑事件(SCROLL_STATE_FLING)时, 应立即停止图片的动态加载过程; 只有当列表视图处于静止状态时(SCROLL_STATE_IDLE), 才可以开启网络图片的自动下载与渲染.
