一、Activity A 启动另一个Activity B 会调用哪些方法?如果B是透明主题的又或则是个DialogActivity呢 ?
Activity A 启动另一个Activity B,回调如下 Activity A 的onPause() → Activity B的onCreate() → onStart() → onResume() → Activity A的onStop(); 如果B是透明主题的又或则是个DialogActivity时:则不会回调A的onStop
二、说下onSaveInstanceState()方法的作用 ? 何时会被调用?
发生条件:异常情况下,Activity被杀死并重新创建。 1、系统会调用onSaveInstanceState来保存当前Activity的状态,此方法调用在onStop之前; 2、当Activity被重建后,系统会调用onRestoreInstanceState,调用在onStart之后;
三、什么是反射机制?反射机制的应用场景有哪些?
Java 反射机制是在运行状态中,对于任意一个类,都能够知道这个类中的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性; 应用场景: 1. 逆向代码,例如反编译 2. 与注解相结合的框架,如 Retrofit 3. 单纯的反射机制应用框架,例如 EventBus(事件总线) 4. 动态生成类框架 例如Gson
四、谈一谈Java成员变量,局部变量和静态变量的创建和回收时机?
成员变量:生命周期伴随类对象,类对象回收时回收,存在堆里。 局部变量:方法调用时创建,方法结束时被标记为可回收,存在栈里 静态变量:不回收,在方法区随着类的加载而加载,随着类的消失而消失。
五、扩展:内部类都有哪些?
有四种:静态内部类、非静态内部类、局部内部类、匿名内部类
六、线程池的好处
1、线程池的重用: 线程的创建和销毁的开销是巨大的,而通过线程池的重用大大减少了这些不必要的开销,当然既然少了这么多消费内存的开销,其线程执行速度也是突飞猛进的提升。 2、控制线程池的并发数。并发数:系统同时能处理的请求数量。
七、什么是OOM?
OOM,全称“Out Of Memory”,翻译成中文就是“内存用完了”
八、android 应用对内存是如何限制的?我们应该如何合理使用内存
限制应用内存 为了维持多任务环境的正常运行,Android 会为每个应用的堆大小设置硬性上限。不同设备的确切堆大小上限取决于设备的总体可用 RAM 大小。如果应用在达到堆容量上限后尝试分配更多内存,则可能会收到 OutOfMemoryError。 合理使用内存 1、避免全局变量 2、避免在使用枚举(Enum) 3、使用弱引用 4、使用ArrayMap/SparseArray代替hashmap 5、使用分页加载数据 6、减少bitmap的内存占用 7、图片的内存管理:使用Glide等库来管理图片的加载和内存缓存 8、及时回收资源:例如Cursor、流等,使用后应及时关闭 9、使用LeakCanary检测内存泄漏
九、ANR 是什么?怎样避免和解决 ANR
ANR->Application Not Responding 也就是在规定的时间内,没有响应。 四种类型: (1)Service Timeout: Service 在特定的时间内无法处理完成 (2)BroadcastQueue Timeout:BroadcastReceiver 在特定时间内无法处理完成 (3)ContentProvider Timeout:内容提供者执行超时 (4)inputDispatching Timeout: 按键或触摸事件在特定时间内无响应 为什么会超时:事件没有机会处理 & 事件处理超时
十、怎么避免ANR
ANR的关键是处理超时,所以应该避免在UI线程,BroadcastReceiver 还有service主线程中,处理复杂的逻辑和计算而交给work thread操作。 1)避免在activity里面做耗时操作,oncreate & onresume 2)避免在onReceiver里面做过多操作 3)避免在Intent Receiver里启动一个Activity,因为它会创建一个新的画面,并从当前用户正在运行的程序上抢夺焦点。 4)尽量使用handler来处理UI thread & workthread的交互。
十一、android实现线程间通信的四种常见方式
1、通过Handler机制 2、runOnUiThread方法 3、View.post(Runnable r) 4、AsyncTask
十二、android 的系统架构
Android系统架构分为四层架构,从高到低分别是应用层,应用框架层,系统运行层和Linux内核层。
十三、请解释下 Android 程序运行时权限与文件系统权限的区别
文件的系统权限是由linux系统规定的,只读,读写等。 运行时权限,是对于某个系统上的app的访问权限,允许,拒绝,询问。该功能可以防止非法的程序访问敏感的信息。
十四、Android 中如何捕获未捕获的异常
要捕获未捕获的异常,可以使用try-catch语句块将可能抛出异常的代码包裹起来,然后在catch块中处理异常。
十五、Android 中的动画有哪几类,它们的特点和区别是什么
视图动画,或者说补间动画。只是视觉上的一个效果,实际view属性没有变化,性能好,但是支持方式少。 属性动画,通过变化属性来达到动画的效果,性能略差,支持点击等事件。android 3.0 帧动画,通过drawable一帧帧画出来。 Gif动画,原理同上,canvas画出来。
十六、SurfaceView & View 的区别
view的更新必须在UI thread中进行 surfaceview会单独有一个线程做ui的更新。 surfaceview 支持open GL绘制。
十七、 Android 开发的四大组件分别是
活动(activity):用于表现功能。 服务(service):后台运行服务,不提供界面呈现。 广播接受者(Broadcast Receive):用于接收广播。 内容提供者(Content Provider),支持多个应用中存储和读取数据,相当于数据库。
十八、Android生命周期
onCreate:当Activity第一次被运行时调用此方法,可用于加载布局视图,获取控件命名空间等一些初始化工作。 onRestart:当Activity被重新启动的时候,调用此方法 onStart :表示Activity正在被启动,已经从不可见到可见状态(不是指用户可见,指Activity在后台运行,没有出现在前台),但还是无法与用户进行交互。 onResume :表Activity已经变为可见状态了,并且出现在前台工作了,也就是指用户可见了 onPause :表示Activity正在暂停,但Activity依然可见,可以执行一些轻量级操作,但一般不会进行太多操作,因为这样会影响用户体验。 onStop :表示Activity即将暂停,此时Activity工作在后台,已经不可见了,可以与onPause方法一样做一些轻量级操作,但依然不能太耗时。 onDestroy :表示活动即将被销毁。
十九、MVC、MVP、MVVM
MVC(Model View Controller) 通过controller的控制去操作model层的数据,并且返回给view层展示。 缺点: 1.View与Model之间还存在依赖关系,Controller很重很复杂,开发,测试,维护都需要花大量的精力 2.Activity即是View又是Controller,维护起来更难。 3.适合单人开发。 MVP(Model View Presenter) view层和model层完全隔离和解耦,presenter层充当桥梁。presenter层去操作model层,并且将数据返回给view层,整个过程中view层和model层完全没有联系。 优点: 1、view层和model层完全隔离。 2、activity和fragment不再是controller层,而是纯粹的view层。 缺点: 虽然是MVC模式的演变,但Presenter依旧很‘重’很复杂。 MVVM(Model View ViewModel) view层和viewmodel层是相互绑定的关系,更新viewmodel层的数据的时候,view层会相应的变动ui。同步逻辑是交由Binder做的,Model变更View跟着变更,只需要保证Model的正确性,View就正确。 优点: 1.解决了MVP大量的手动View和Model同步的问题,提供双向绑定机制。提高了代码的可维护性。 2.ViewModle易于单元测试。
二十、说下 Activity的任务栈及四种启动模式、应用场景 ?
任务栈:具有先进后出的特性,只有在任务栈栈顶的activity才可以跟用户进行交互。 standard标准模式:每次启动一个Activity都会重新创建一个新的实例。 singleTop栈顶复用模式:如果新Activity已经位于任务栈的栈顶,那么此Activity不会被重新创建。如果新Activity实例已经存在但不在栈顶,那么Activity依然会被重新创建; singleTask栈内复用模式:只要Activity在一个任务栈中存在,那么多次启动此Activity都不会重新创建实例。如果不存在,就会重新创建一个任务栈,然后把创建好A的实例放到栈中; singleInstance单实例模式:此模式的Activity只能单独的位于一个任务栈中,且此任务栈中只有唯一一个实例;
二十一、Android Activity 横竖屏切换的生命周期
manifest文件中不配置android:configChanges属性时: Activity启动时: onCreate,onStart,onResume 屏幕切换的时候:onPause,onSaveInstanceState,onStop,onDestroy,onCreate,onStart,onRestoreInstanceState,onResume 配置android:configChanges属性的时: android:configChanges=”orientation|screenSize” 调用:onConfigurationChanged
二十二、跨App启动Activity的方式,注意事项
在Android开发中,跨App启动Activity需要使用Intent的特殊标志FLAG_ACTIVITY_NEW_TASK以及对应的权限声明。 解决方案: (1)在启动Activity的App中,设置Intent的FLAG_ACTIVITY_NEW_TASK标志。 (2在被启动的Activity的AndroidManifest.xml中,将<activity>标签的android:launchMode属性设置为"singleTask",以确保启动新任务。 (3)被启动App的Manifest文件中需要声明相应的权限,以允许其他App启动其组件。 注意事项: (1)确保被启动App已经安装在设备上。 (2)被启动App需要有一个能够匹配启动Intent的<intent-filter>。 (3)确保启动Activity的App有权限启动其他App的组件,这通常需要自定义权限并在被启动App中声明。 (4)如果被启动的App和Activity使用了更加严格的安全设置,可能需要额外的签名认证或者密码保护。
二十三、谈一谈Fragment的生命周期?
Fragment从创建到销毁整个生命周期中涉及到的方法依次为:onAttach()→onCreate()→onCreateView()→onActivityCreated()→onStart()→onResume() →onPause()→onStop()→onDestroyView()→onDestroy()→onDetach(),其中和Activity有不少名称相同作用相似的方法,而不同的方法有: onAttach():当Fragment和Activity建立关联时调用; onCreateView():当fragment创建视图调用,在onCreate之后; onActivityCreated():当与Fragment相关联的Activity完成onCreate()之后调用; onDestroyView():在Fragment中的布局被移除时调用; onDetach():当Fragment和Activity解除关联时调用;
二十四、谈谈Activity和Fragment的区别?
相似点:都可包含布局、可有自己的生命周期 不同点: 1、Fragment相比较于Activity多出5个生命周期,在控制操作上更灵活; 2、Fragment可以在XML文件中直接进行写入,也可以在Activity中动态添加; 3、Fragment可以使用show()/hide()或者replace()随时对Fragment进行切换,并且切换的时候不会出现明显的效果,用户体验会好;Activity虽然也可以进行切换,但是Activity之间切换会有明显的翻页或者其他的效果,在小部分内容的切换上给用户的感觉不是很好; 4、Activity代表了一个具有用户界面的屏幕。而Fragment是Activity内的一个可重用部件,可以在多个Activity中重复使用,有助于构建灵活且适应不同屏幕尺寸的用户界面
二十五、Fragment中add与replace的区别(Fragment重叠)
1、add不会重新初始化fragment,replace每次都会。所以如果在fragment生命周期内获取获取数据,使用replace会重复获取; 2、添加相同的fragment时,replace不会有任何变化,add会报IllegalStateException异常; replace先remove掉相同id的所有fragment,然后在add当前的这个fragment,而add是覆盖前一个fragment。所以如果使用add一般会伴随hide()和show(),避免布局重叠; 3、使用add,如果应用放在后台,或以其他方式被系统销毁,再打开时,hide()中引用的fragment会销毁,所以依然会出现布局重叠bug,可以使用replace或使用add时,添加一个tag参数;
二十六、getFragmentManager、getSupportFragmentManager 、getChildFragmentManager之间的区别?
getFragmentManager():所得到的是所在fragment 的父容器的管理器, getChildFragmentManager():所得到的是在fragment 里面子容器的管理器, 如果是fragment嵌套fragment,那么就需要利用getChildFragmentManager(); getSupportFragmentManager() :Android3.0以下则需要调用getSupportFragmentManager() 来间接获取;
二十七、FragmentPagerAdapter与FragmentStatePagerAdapter的区别与使用场景
相同点 :二者都继承PagerAdapter 不同点 : FragmentPagerAdapter的每个Fragment会持久的保存在FragmentManager中,只要用户可以返回到页面中,它都不会被销毁。因此适用于那些数据相对静态的页,Fragment数量也比较少的那种; FragmentStatePagerAdapter只保留当前页面,当页面不可见时,该Fragment就会被消除,释放其资源。因此适用于那些数据动态性较大、占用内存较多,多Fragment的情况;
二十八、谈一谈Service的生命周期?
onCreate():如果service没被创建过,调用startService()后会执行onCreate()回调;如果service已处于运行中,调用startService()不会执行onCreate()方法。也就是说,onCreate()只会在第一次创建service时候调用,多次执行startService()不会重复调用onCreate(),此方法适合完成一些初始化工作; onStartComand():服务启动时调用,此方法适合完成一些数据加载工作,比如会在此处创建一个线程用于下载数据或播放音乐; onBind():服务被绑定时调用; onUnBind():服务被解绑时调用; onDestroy():服务停止时调用;
二十九、Service的两种启动方式?区别在哪?
startService():通过这种方式调用startService,onCreate()只会被调用一次,多次调用startSercie会多次执行onStartCommand()和onStart()方法。如果外部没有调用stopService()或stopSelf()方法,service会一直运行。 bindService():如果该服务之前还没创建,系统回调顺序为onCreate()→onBind()。如果调用bindService()方法前服务已经被绑定,多次调用bindService()方法不会多次创建服务及绑定。如果调用者希望与正在绑定的服务解除绑定,可以调用unbindService()方法,回调顺序为onUnbind()→onDestroy();
三十、如何保证Service不被杀死 ?
1、onStartCommand方式中,返回START_STICKY或则START_REDELIVER_INTENT START_STICKY:如果返回START_STICKY,表示Service运行的进程被Android系统强制杀掉之后,Android系统会将该Service依然设置为started状态(即运行状态),但是不再保存onStartCommand方法传入的intent对象 START_NOT_STICKY:如果返回START_NOT_STICKY,表示当Service运行的进程被Android系统强制杀掉之后,不会重新创建该Service START_REDELIVER_INTENT:如果返回START_REDELIVER_INTENT,其返回情况与START_STICKY类似,但不同的是系统会保留最后一次传入onStartCommand方法中的Intent再次保留下来并再次传入到重新创建后的Service onStartCommand方法中 2、提高Service的优先级。 在AndroidManifest.xml文件中对于intent-filter可以通过android:priority ="1000"这个属性设置最高优先级,1000是最高值,如果数字越小则优先级越低,同时适用于广播; 3、在onDestroy方法里重启Service 当service走到onDestroy()时,发送一个自定义广播,当收到广播时,重新启动service; 4、提升Service进程的优先级。 进程优先级由高到低:前台进程 一 可视进程 一 服务进程 一 后台进程 一空进程。 可以使用startForeground将service放到前台状态,这样低内存时,被杀死的概率会低一些; 5、系统广播监听Service状态将APK安装到/system/app,变身为系统级应用
三十一、能否在Service开启耗时操作 ? 怎么做 ?
Service默认并不会运行在子线程中,也不运行在一个独立的进程中,它同样执行在主线程中(UI线程)。换句话说,不要在Service里执行耗时操作,除非手动打开一个子线程,否则有可能出现主线程被阻塞(ANR)的情况;
三十二、了解ActivityManagerService吗?发挥什么作用?
ActivityManagerService是Android中最核心的服务 , 主要负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作,其职责与操作系统中的进程管理和调度模块类似;
三十三、如何开启多进程 ? 应用是否可以开启N个进程 ?
在AndroidMenifest中给四大组件指定属性android:process开启多进程模式 在内存允许的条件下可以开启N个进程
三十四、Binder机制的作用和原理?
Binder是Android系统中用于实现跨进程通信的机制。它通过一种轻量级的进程间通信方式,允许一个进程向另一个进程发送消息。 Binder的工作原理主要包括以下几个步骤: Binder驱动程序:Binder机制依赖于Linux内核中的Binder驱动程序,它负责处理进程间通信的底层细节。 Binder通信的建立:在Android系统中,每个进程都有一个Binder通信的服务端和客户端。当一个进程需要向另一个进程发送消息时,它首先通过Binder驱动程序获取对方进程的Binder通信实体。 Binder通信的传输:消息通过Binder通信实体传输,可以是数据、函数调用等。Binder采用了共享内存的方式传输数据,效率较高。 Binder通信的接收:接收方进程接收到消息后,会通过Binder驱动程序将消息传递给相应的进程组件,例如服务、活动等。 通过以上步骤,Binder机制实现了Android系统中的跨进程通信,为开发者提供了一种方便、高效的通信方式。
三十五、Bundle传递对象为什么需要序列化?Serialzable和Parcelable的区别?
因为bundle传递数据时只支持基本数据类型,所以在传递对象时需要序列化转换成可存储或可传输的本质状态(字节流)。序列化后的对象可以在网络、IPC(比如启动另一个进程的Activity、Service和Reciver)之间进行传输,也可以存储到本地。 序列化实现的两种方式:实现Serializable/Parcelable接口。不同点如图:
三十六、什么是AIDL
1、AIDL(Android Interface Definition Language,Android接口定义语言):如果在一个进程中要调用另一个进程中对象的方法,可使用AIDL生成可序列化的参数,AIDL会生成一个服务端对象的代理类,通过它客户端实现间接调用服务端对象的方法。 2、AIDL的本质是系统提供了一套可快速实现Binder的工具。
三十七、Android里的Intent传递的数据有大小限制吗,什么原因?如何解决?
Intent传递数据大小的限制大概在1M左右,超过这个限制就会静默崩溃。 原因:Intent不仅仅用于传递数据,还负责组件的查找和调用,如果数据过大,可能会导致Intent体积过大,超出了系统的限制,会静默崩溃。 解决方案: 进程内:EventBus,文件缓存、磁盘缓存。 进程间:通过ContentProvider进行款进程数据共享和传递
三十八、Android的事件分发机制?
Android事件分发机制的本质:事件从哪个对象发出,经过哪些对象,最终由哪个对象处理了该事件。 Android事件的分发顺序:Activity(Window) -> ViewGroup -> View 事件分发dispatchTouchEvent: 如果事件分发返回true,表示改事件在本层不再进行分发且已经在事件分发自身中被消费了。至此,事件已经完结。 如果事件分发返回 false,表明事件在本层不再继续进行分发,并交由上层控件的onTouchEvent方法进行消费。 dispatchTouchEvent无论返回true还是false,事件都不再进行分发, 只有当其返回super.dispatchTouchEvent(ev),才表明其具有向下层分发的愿望, 事件拦截onInterceptTouchEvent: 如果 onInterceptTouchEvent 返回 true,则表示将事件进行拦截,并将拦截到的事件交由本层控件 的 onTouchEvent 进行处理; 如果返回结果是false;则表示不对事件进行拦截,事件得以成功分发到子View。并由子View的dispatchTouchEvent进行处理。 事件响应onTouchEvent: 如果onTouchEvent返回true,表示onTouchEvent处理完事件后消费了此次事件。此时事件终结,将不会进行后续的冒泡。 如果onTouchEvent返回false,事件在onTouchEvent中处理后继续向上层View冒泡,且有上层View的onTouchEvent进行处理。
三十九、Activity的通信方式有哪些?
startActivityForResult EventBus LocalBroadcastReceiver
四十、Framework 工作方式及原理,Activity 是如何生成一个 view 的,机制是什么?
所有的框架都是基于反射和配置文件(manifest)的。 普通的情况:Activity 创建一个 view 是通过 ondraw 画出来的, 画这个 view 之前呢,还会调用 onmeasure 方法来计算显示的大小.特殊情况:Surfaceview 是直接操作硬件的, 因为 或者视频播放对帧数有要求,onDraw 效率太低,不够使,Surfaceview 直接把数据写到显存
四十一、Android 屏幕适配
1、dp 原生方案 dp 的优势也主要体现在相同尺寸,不同分辨率的设备的适配效果。对于不同尺寸不同分辨率的设备,dp 的适配效果就差强人意了 2、sw 限定符适配方案 android 会识别屏幕可用高度和宽度的最小尺寸的 dp 值,然后根据识别到的结果去 资源文件 中寻找对应限定符的文件夹下的资源文件
四十二、 Handler机制
Handler机制是进程内部、线程间的一种通信机制,用于发送和处理消息。
四十三、解释一下Android中的Intent及其类型
Intent是Android应用间进行交互的消息对象,用于启动Activity、Service或发送Broadcast。主要类型有: •显式Intent:明确指定了目标组件的类名,确保系统直接启动指定组件。 •隐式Intent:只描述了要执行的操作和数据类型,系统根据这些信息找到合适的组件来响应。
四十四、Service和Thread的区别是什么?
Service和Thread都可用于后台处理任务,但用途和性质不同。 •Service:运行在主线程,用于执行长时间运行的操作,即使应用切换到后台也能继续运行。它不提供UI,且生命周期与启动它的组件独立。 •Thread:用于执行多线程任务,适合CPU密集型操作。它是进程内的执行单元,但本身没有生命周期管理,也不直接与Android组件生命周期关联
四十五、什么是Context?它有哪些作用?
Context是Android中所有应用组件的基础,提供了访问应用环境和系统服务的接口。其作用包括: •加载资源。 •启动Activity、Service、发送Broadcast。 •获取系统服务,如LocationManager、NotificationManager等。 •访问应用文件系统和偏好设置。
四十六、Android中的消息机制(Handler, Looper, MessageQueue)是如何工作的?
此机制允许在主线程中处理来自其他线程的消息。 •MessageQueue:维护一个消息列表,按顺序处理消息。 •Looper:在每个线程中创建,用于循环遍历MessageQueue,取出并分发消息给Handler处理。 •Handler:发送消息到MessageQueue,并在消息出队时处理消息。通过post(Runnable)或sendMessage(Message)方法实现。
四十七、如何在Android中实现数据持久化?
数据持久化有多种方式: •SharedPreferences:存储键值对,适合轻量级数据。 •SQLite数据库:关系型数据库,适合结构化数据存储。 •文件存储:使用File API在内部或外部存储空间保存文件。 •ContentProvider:用于在应用间共享数据。 •Room Persistence Library:Android架构组件之一,提供SQLite数据库的封装,简化数据库操作。
四十八、生命周期回调方法在以下几种情况被调用
•用户操作(如打开、关闭应用)。 •系统事件(如来电、低电量警告)。 •应用内部事件(如启动新Activity、旋转屏幕)。 •系统资源紧张导致的后台进程回收。
四十九、如何在AndroidManifest.xml中注册BroadcastReceiver?
通过在AndroidManifest.xml文件中添加receiver标签来注册BroadcastReceiver,例如: <receiver android:name=".MyBroadcastReceiver"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED"/> </intent-filter> </receiver>
五十、android中如何实现横竖屏切换时的界面保持?
•在AndroidManifest.xml中对应Activity的标签中设置android:configChanges=“orientation|screenSize”,然后重写Activity的onConfigurationChanged(Configuration newConfig)方法,处理配置变更,避免重新创建Activity。 •使用ViewModel存储UI相关的数据,ViewModel在配置变更时不会被销毁,可以跨配置变更保持数据。
五十一、Android中的图片加载库Glide和Picasso有什么区别?
•Glide:由Google开发,更注重性能和内存优化,支持动画、视频解码、占位图、错误占位图、自动资源管理和回收。Glide的缓存策略更灵活,支持磁盘和内存缓存。 •Picasso:由Square公司开发,易于集成和使用,提供简单的图片加载、缓存、错误处理功能。虽然也支持GIF加载,但在性能和内存管理方面略逊于Glide。
五十二、RxJava相关面试?
1、响应式编程,观察者设计模式,实现异步操作的库。 2、上游Observable和下游observer通过subscribe建立连接,总共就3步:创建上游,创建下游,建立连接。
五十三、TCP、UDP
TCP---传输控制协议,提供的是面向连接、可靠的字节流服务。连接过程包含三次握手四次挥手,目的是建立可靠的通信信道。当客户端和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。 UDP---用户数据报协议,是一个简单的面向数据报的运输层协议。UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地,在网络质量令人十分不满意的环境下,UDP协议数据包丢失会比较严重。由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快,如聊天,发送音频视频 TCP和UDP区别 TCP UDP 是否连接 面向连接 面向非连接 传输可靠性 可靠 会丢包,不可靠 应用场景 传输数据量大 传输量小 速度 慢 快
五十四、什么是Socket?
即通过Socket,我们才能在Andorid平台上通过 TCP/IP协议进行开发 Socket不是一种协议,而是一个编程调用接口(API),属于传输层(主要解决数据如何在网络中传输)
五十五、 Android多线程实现方式?
1、基础使用 继承Thread类,实现Runnable接口,Handler 2、复合使用 AsyncTask,HandlerThread,IntentService 3、高级使用 线程池(ThreadPool)
五十六、Android中,共有六种布局方式
LinearLayout (线性布局),RelativeLayout(相对布局),FrameLayout(帧布局), AbsoluteLayout(绝对布局),TableLayout(表格布局),GridLayout(网格布局)。 1.线性布局LinearLayout,可以指定子控件的排列方式,比如垂直方向或水平方向。 2.绝对布局Relavitelayout,可以指定子控件的相对位置,比如上下左右、居中等,也可以指定一个控件相对另 一个控件的相对位置。 3.帧布局FrameLayout,显示的View都是一层一层地往上加,显示的是最上面的一层。应用在动态显示碎片 Fragment也是比较多的。 上面三种布局相对来说应用是比较多的。下面的三种应用就少一点了。 4.绝对布局AbsoluteLayout,View的显示要定义具体的单位长度px。这个局限性比较多,不能匹配多种屏幕, 基本已经不使用了。 5.表格布局TableLayout,是线性布局的子类,显示据一般是一行一行的,一行可以有多列,列与列对齐是很方便的。 6.网格布局GridLayout,作为android 4.0 后新增的一个布局,与前面介绍过的其实有点大同小异;如果是显示类似网格效果的多个控件是非常方便的。
五十七、android垃圾回收机制的算法
1、标记清除算法 当成功区分出内存中存活对象和死亡对象后,GC接下来的任务就是执行垃圾回收,释放掉无用对象所占用的内存空间,以便有足够的可用内存空间为新对象分配内存。 2、标记整理算法 和标记清除一样,标记整理的第一个阶段也是对垃圾对象进行标记,区别主要在第二个步骤,即整理。所谓的整理就是避免之前标记清除时的内存碎片的问题,他就会在清除的过程中,会把可用的对象向前给他移动,这样的话让内存更为紧凑,这就是整理的过程。整理之后,就能发现内存变的更紧凑了,即连续的空间就更多了,这样就不会造成内存碎片。 3、复制算法 复制算法比较特殊,他把内存区域划分成了大小相等的两块儿区域,左边区域称之为FROM,右边区域称之为TO,其中TO这个区域始终空闲着,即里面一个对象都没有。 1.先扫谁是垃圾 2.把好的不是垃圾的放进空的to里面 3.交换to和form的位置 4.删除垃圾 4、分代垃圾回收 在新生代中,每次收集都会有大量对象死去,所以可以选择复制算法,只需要付出少量对象的复制成本就可以完成垃圾收集的过程。 而老年代的对象存活率是比较高的,且没有额外的空间对它进行分配,所以我们必须选择 “标记-清除” 或 “标记-整理” 算法进行垃圾收集。
五十八、Window和WindowManager
Android中所有的视图都是通过Window来呈现的,Window实际上是View的直接管理者,在实际使用中无法直接访问Window,对Window的访问必须通过Windowmanager。 WindowManager是一个接口,它的真正实现是WindowManagerImpl。而WindowmanagerImpl的增删更新View都交由WindowManagerGlobal来处理。
五十九、IPC通信机制
Inter-Process Communication的缩写,含义是进程间通信和跨进程通信,是指两个进程直接进行数据交换的过程。 - **Bundle**:在Bundle中附加数据并通过Intent传输 - **文件共享**:两个进程通过读写一个文件来交换数据 - **AIDL**:Android Interface Definition Language - **Messenger**:基于消息的进程间通信 - **ContentProvider**::专门用于不同应用间的数据共享 - **Socket**:使用TCP和UDP协议进行网络通信
六十、service和intentservice的区别
1、Service执行耗时操作会阻塞主线程,IntentService每个Intent都在单独的工作线程中执行,因此它不会阻塞主线程。 2、Service需要通过stopSelf()或stopService()停止服务,否则它可能会一直运行,IntentService任务执行完成后,会自动停止。 3、Service适用于需要高度自定义的长期运行的服务逻辑,以及与用户界面紧密交互的后台服务,IntentService适用于逐个处理传入的Intent请求的场景,不需要与用户界面交互。
六十一、线程同步的几种方法
Synchronized:给一个方法增加synchronized修饰符之后就可以使它成为同步方法 volatile:volatile关键字为域变量的访问提供了一种免锁机制 使用局部变量实现线程同步(ThreadLocal):ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。 使用阻塞队列实现线程同步(LinkedBlockingQueue): 队列是先进先出的顺序。
六十二、ArrayMap,HashMap,SparseArray,ConcurrentHashMap,HashTable
ArrayMap是Android专门针对内存优化而设计的,用于取代的HashMap数据结构。为了更进一步优化key是int类型的Map,Android再次提供效率更高的数据结构SparseArray。HashMap的查找速度快是牺牲大量的内存来实现的,而SparseArray和ArrayMap性能略逊于HashMap,但更节省内存。 HashTable:底层数组+链表实现,无论key还是value都不能为null,线程安全,实现线程安全的方式是在修改数据时锁住整个HashTable,效率低,ConcurrentHashMap做了相关优化 ConcurrentHashMap:底层采用分段的数组+链表实现,是使用了锁分段技术来保证线程安全的
六十三、HashMap和ArrayList如何扩容
HashMap的扩容是通过创建一个新的、更大的哈希表,并将旧哈希表中的所有元素重新插入到新哈希表中来实现的。 ArrayList是动态数组,它可以根据需要动态增长或缩小。当我们向ArrayList中添加元素时,如果当前元素个数已经达到了数组的容量上限,ArrayList就会进行扩容操作。
六十四、Looper中的Loop()方法不能导致主线程卡死?
死循环并不是导致主线程卡死的真正原因, 真正的原因是死循环后面的事件没有得到分发。 Looper 中的 loop()方法, 他的作用就是从消息队列MessageQueue 中不断地取消息, 然后将事件分发出去 , 所以不会造成卡顿或者ANR。
六十五、线程池的具体行为和几个参数有关
核心数 corePoolSize 线程池中核心线程的数量。 最大容量 maximumPoolSize 线程池最大允许保留多少线程。 超时时间 keepAliveTime 线程池中普通线程的存活时间。
六十六、Retrofit是如何将子线程切换到主线程?
defaultCallbackExecutor 内部其实调用的是 new MainThreadExecutor() ,很清楚的看到, handler.post(r) 内部使用Handler将响应抛到了主线程。这就是Retrofit将子线程切换到主线程的核心所在。
六十七、AMS、PMS和WMS
AMS的原理: AMS(Activity Manager Service)作为活动管理器服务,它主要负责管理和跟踪所有应用程序的活动任务和生命周期。当一个应用程序被打开时,AMS会启动该应用程序的进程,并给应用程序分配处理器资源和内存。当应用程序不再处于前台或后台,或者当系统内存不足时,AMS会终止或杀死这个应用程序的进程。 PMS的原理: PMS(PackageManager Service)作为包管理器服务,主要负责在Android设备上安装、管理和卸载应用程序。当一个新的应用程序被安装时,PMS将识别应用程序的所有组件(如Activity、Service和Broadcast Receiver等),并为这些组件分配相应的权限。同时,PMS还监控已安装应用程序的状态,确保应用程序的完整性和安全性。 WMS的原理: WMS(Window Manager Service)作为窗口管理器服务,它主要负责管理Android设备上的窗口视图,并控制应用程序的界面和正确的显示和输入。WMS负责管理应用程序窗口的位置、大小和布局,照顾多任务操作和应用程序之间的切换,从而确保用户界面稳定、流畅和一致。
六十八、AMS是如何启动的?
阶段1:SystemSerer 进程创建 Android 运行环境。AMS 运行在 SystemServer 进程中,它的许多工作依赖于该运行环境 createSystemContext() -> new ActvityThread()->attach ->getSystemContext ->createSystemContext 阶段2:启动 AMS,主要进行一些初始化工作 new ActivityManagerService() start() 阶段3:将 SystemServer 进程纳入到 AMS 的进程管理体系中 //将framework-res.apk的信息加入到SystemServer进程的LoadedApk中 //构建SystemServer进程的ProcessRecord,保存到AMS中,以便AMS进程统一管理 setSystemProcess() installSystemProviders() //安装SystemServer进程中的SettingsProvider.apk 阶段4:AMS 启动完成,通知服务或应用完成后续的工作,或直接启动一些进程
六十九、Context有哪两种类型 ?区别是什么?
application context 和 activity context。 1、application context取的是这个应用程序的Context,它的生命周期伴随应用程序的存在而存在;而activity context取的是当前Activity的Context,它的生命周期则只能存活于当前Activity 2、不要让生命周期长的对象引用activity context, 对于生命周期长的对象,可以使用application context
七十、请简述什么是 Kotlin?它有哪些特性?
kotlin和java一样也是一门jvm语言最后的编译结果都是.class文件,并且可以通过kotlin的.class文件反编译回去java代码,并且封装了许多语法糖。 特性 语法糖简化代码复杂度 lamdba表达式,函数式编程. lamdba表达式并不是kotlin 的专利,java中也有,但是有限制, 像setOnClickListener一样, 接口方法只有一个的情况才能调用, 而在kotlin中对接口的lambda也是如此,有这样的限制,但是他更推荐你使用闭包的方式而不是实现匿名接口的方式去实现这样的功能,闭包对lambda没有接口这么多的限制,另外就是函数式编程 在java8中提供了streamApi对集合进行mapsortreduce等等操作,但是对androidapi有限制,为了兼容低版本,几乎不可能使用streamApi 判空语法 省略了许多ifxxx==null的写法 也避免了空指针异常 aaa?.toString ?: "空空如也" 当aaa为空的时候 它的值被"空空 如也"替代 aaa?.let{ it. bbb } 当aaa不为空时 执行括号内的方法 省略了findViewById ,使用kotlin 就可以直接用xml中定义的id 作为变量获取到这个控件,有了这个 butterknife就可以淘汰了,使用databinding也能做到,但是,非常遗憾,databinding的支持非常不好,每次修改视图,都不能及时生成,经常要rebulid才能生成. 默认参数 减少方法重载 fun funName(a :Int ,b:Int =123) 通过如上写法 实际在java中要定义两个写法 funName(a)和funName(a,b)
七十一、ContentProvider 是如何实现数据共享?
ContentProvider是以Uri的形式对外提供数据,其他应用通过ContenrResolver来操作ContentProvider暴露的数据。
七十二、Android本身的API并未声明会抛出异常,则其在运行时有无可能抛出runtime异常,如何解决?
在Android中,即便API本身没有声明抛出异常,仍有可能在运行时抛出异常。这通常发生在API内部逻辑错误、资源问题或者是底层库的异常。例如,如果你使用的是Android的Bitmap类,即便decodeResource方法没有声明抛出异常,解码图片时可能会因为资源问题或内存不足而抛出OutOfMemoryError。 解决方法: 检查代码,确保传入API的参数是有效的,比如资源ID是有效且存在的。 使用try-catch块来捕获可能的异常,并在catch块中处理这些异常,比如通知用户、释放资源或者进行恢复操作。 对于那些可能抛出异常的API,查看官方文档以了解特定的错误情况,并做好相应的准备。 如果是资源问题,确保正确处理不同的屏幕分辨率和尺寸,避免内存溢出。 对于第三方库,确保正确处理其可能抛出的异常,并查看其文档以了解如何正确使用。
七十三、如何退出Activity?如何安全退出已调用多个Activity的Application?
退出Activity可以通过调用Activity的finish()方法来实现。 1.利用ActivityContainer来管理所有的Activity的引用 2.使用广播通知BaseActivity结束 3.直接杀死进程 4.采用SingleTask的特点,结束应用 5.双击返回键退出
七十四、AsyncTask使用在哪些场景?它的缺陷是什么?如何解决?
场景:AsyncTask 运用的场景就是我们需要进行一些耗时的操作,耗时操作完成后更新主线程,或者在操作过程中对主线程的UI进行更新。 缺陷:AsyncTask中维护着一个长度为128的线程池,同时可以执行5个工作线程,还有一个缓冲队列,当线程池中已有128个线程,缓冲队列已满时,如果 此时向线程提交任务,将会抛出RejectedExecutionException。 解决:由一个控制线程来处理AsyncTask的调用判断线程池是否满了,如果满了则线程睡眠否则请求AsyncTask继续处理。
七十五、如何在 ScrollView 中如何嵌入 ListView
通常情况下我们不会在 ScrollView 中嵌套 ListView。 在 ScrollView 添加一个 ListView 会导致 listview 控件显示不全,通常只会显示一条,这是因为两个控件的滚动事件冲突导致。所以需要通过 listview 中的 item 数量去计算 listview 的显示高度,从而使其完整展示。 现阶段最好的处理的方式是: 自定义 ListView,重载 onMeasure()方法,设置全部显示。
七十六、子线程中能不能 new handler?为什么?
不能 在没有调用 Looper.prepare()的时候不能创建 Handler,如果在子线程中直接 new Handler()会抛出异常 java.lang.RuntimeException: Can'tcreate handler inside thread that has not called
七十七、一条最长的短信息约占多少byte?
中文70(包括标点),英文160,160个字节。
七十八、什么是Force Close?什么情况会导致Force Close ?如何避免?能否捕获导致其的异常?
forceclose,意为强行关闭,当前应用程序发生了冲突 原因:抛出运行时异常时就会导致Force Close,比如空指针、数组越界、类型转换异常等等。 避免:编写程序时,要思维缜密,在可能出现异常的地方都作相应的处理,增强程序的健壮性。 捕获:可以通过logcat查看抛出异常的代码出现的位置,然后到程序对应代码中进行修改。
七十九、如何打开res/raw目录中的数据库文件?
不能直接打开res/raw目录中的数据库文件,而需要在程序第一次启动时将该文件复制到手机内存或SD卡的某个目录中,然后再打开该数据库文件。
八十、如何理解Activity,View,Window三者之间的关系?
Activity作为Android应用程序的一个组件,负责管理界面的生命周期和交互逻辑;View作为界面的基本构建块,代表屏幕上的可视元素;Window作为Activity的关联对象,负责承载和展示界面内容。它们之间的关系是:Activity通过关联的Window来展示界面内容,而界面内容则由多个View组成。
八十一、阐述后台和前台Service的概念 ?
Service可以根据在应用中的位置和运行方式分为前台Service和后台Service。 前台Service:用于需要与用户进行交互或展示通知等情况。 常见应用场景包括音乐播放器、即时通讯应用等。 后台Service:用于执行后台任务,不需要与用户直接交互。
八十二、如何在 Kotlin 中用值初始化一个数组?
使用 arrayOf() 函数声明和初始化数组 val list = arrayOf(1, 2, 3)
八十三、Kotlin 中的 var 和 val 有什么区别?
val相当于java中定义常量时加了一个final,而var就是正常定义变量
八十四、解释 Kotlin 中的 Null 安全性 ?
isNullOrEmpty: 为空指针或者字符串长度为0时返回true,非空字符串和可空字符串都可以调用。 isNullOrBlank: 为空指针、字符串长度为0或者全为空格时候返回true,非空字符串和可空字符串都可以调用。 isEmpty: 字符串长度为0时返回true,只有非空字符串的时候才可以调用。 isBlanK: 字符串长度为0或者全为空格时返回true,只有非空字符串才可以调用 isNotEmpty: 字符串长度大于0时返回true,只有非空字符串可以调用。 isNotBlank: 字符串长度大于0且不是全空格时返回true,只有非空字符串可以调用
八十五、ActivityA跳转ActivityB的过程中,ActivityB 按back键呢?
A跳转B:A.onPause -> B.onCreate -> B.onStart-> B.onResume-> A.onStop 按back键:B.onPause->A.onRestart->A.onStart->A.onResume->B.onStop->B.onDestory
八十六、 onStart 和 onResume、onPause 和 onStop 的区别
onStart表示Activity可见,但是还不能与用户进行交互,可以理解为Activity已经显示出来了,但是我们还看不见。 onResume表示此时Activity从后台切换到前台,可以与用户进行交互,于onstart相比,onStart和onResume都表示Activity可见,但onstart的时候Activity还在后台,而onResume时Activity从后台切换到前台。 onPause表示Activity正在停止,此时Activity切换到后台,不能与用户进行交互。不能再onPause中做重量级的操作。 onStop表示Activity即将停止,此时可以做一些稍微重量级的回收工作,但不能太耗时,此时Activity已经变得不可见。
八十七、scheme使用场景,协议格式,如何使用
scheme协议是可以跳转到app任意界面的协议。 使用场景: 1.服务器下发跳转路径,app跳转到相应界面 2.H5页面点击描点,根据描点具体跳转路径APP端跳转具体的页面 3.APP根据URL跳转到另外一个APP指定页面 4.组件化开发 协议格式:scheme://host:port/path?query_parameters#fragment_identifier scheme: 表示要使用的协议,例如http、https、ftp等。 host: 表示要访问的主机名或IP地址。 port(可选): 表示要使用的端口号。 path(可选): 表示要访问的资源路径。它指定了应用程序或资源的特定位置。对于应 用程序,路径可以是特定的操作或页面。对于资源,路径可以是文件或目录路径。 query_parameters(可选): 表示查询参数,用于向应用程序传递额外的数据。例如,?param1=value1¶m2=value2。 fragment_identifier(可选): 表示片段标识符,用于指定资源中的特定片段或位置。它通常在网页中使用,并以符号开头。 使用方法: (1).定义一个scheme cheu://tests:92/goods?goodsId=8897&name=* chen代表Scheme协议名称 test代表Scheme作用的地址域 8080代表改路径的端口号 /goods代表的是指定页面(路径) goodsId和name代表传递的两个参数 (2).在AndroidManifest.xml中对标签增加设置Scheme <activity android:name=".SecondActivity"> <intent-filter> <action android:name="android.intent.action.VIEW"/> <category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.BROWSABLE"/> <data android:scheme="zymobi" android:host="3g2win" android:port="9999" android:path="/macthDetail" /> </intent-filter> </activity> 注意:如果想让别的app调转到此app,必须要进行配置 (3).进行调用 跳转到抖音的相应的抖音号的主界面。uid为该抖音号的唯一识别号 try { var url ="snssdk1128://user/profile/uid" var intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) startActivity(intent) }catch (e:ActivityNotFoundException){ toast("检查到您手机没有安装抖音,请安装后使用该功能") } (4.) 常用第三方APP 的url scheme
八十八、onCreat()和onRestoreInstanceState()恢复数据区别
1、因为onSaveInstanceState 不一定会被调用,所以onCreate()里的Bundle参数可能为空,如果使用onCreate()来恢复数据,一定要做非空判断。而onRestoreInstanceState的Bundle参数一定不会是空值,因为它只有在上次activity被回收了才会调用。 2、onRestoreInstanceState是在onStart()之后被调用的。有时候我们需要onCreate()中做的一些初始化完成之后再恢复数据,用onRestoreInstanceState会比较方便。
八十九、Service与Activity怎么实现通信
1、使用Binder:如果Service需要与Activity进行频繁和复杂的交互,可以定义一个Binder类,在Service中创建该类的实例,并在onBind()方法中返回。Activity可以通过bindService()方法绑定到Service,并在ServiceConnection的onServiceConnected()回调中接收Binder实例,进而实现与Service的通信。 2、使用Intent和BroadcastReceiver:Activity可以通过startService()启动Service,并通过Intent传递数据。Service在处理完任务后,可以发送广播(Broadcast),Activity可以注册相应的BroadcastReceiver来接收这些广播,从而获取Service的状态或结果。
九十、子线程可不可以更新UI?
一般来说子线程是不可以更新UI的,但是非要用子线程更新UI,那也可以做到。
九十一、为什么Android系统不建议子线程访问UI
Android系统不建议子线程访问UI,因为Android的UI是由主线程来更新的,称为UI线程。如果在子线程中直接访问UI,可能会引起以下问题: 1.不可预测的结果:由于UI线程和子线程是并行执行的,因此如果在子线程中修改UI,可能会导致UI不可预测的结果,例如UI的状态在更新之前可能已经改变。 2.可能引起异常:如果子线程尝试访问在UI线程中被锁定的对象,可能会引起异常。 3.性能问题:频繁的UI更新将消耗大量的计算资源,如果在子线程中进行,可能会导致应用程序崩溃或运行变慢。 因此,Android系统不建议子线程直接访问UI,而是建议使用Handler或AsyncTask等机制来进行UI更新,并将它们与UI线程进行交互。这将确保UI的正确性和稳定性,同时还可以提高应用程序的性能。
九十二、MessageQueue#next 在没有消息的时候会阻塞,如何恢复?
当其他线程调用 MessageQueue#enqueueMessage 时会唤醒 MessageQueue,这个方法会被 Handler#sendMessage、Handler#post 等一系列发送消息的方法调用。
九十三、Handler消息机制中,一个looper是如何区分多个Handler的
因为在msg入队列时,会将msg.target设置一个handler,处理消息的时候,也会调用msg对象的target去处理消息
九十四、什么是序列化?什么是反序列化?为什么需要使用序列化和反序列化?
序列化:将对象转换为可传输的二进制流的过程。 反序列化:把字节序列恢复成对象的过程。 原因:方便数据的传输
九十五、 请详细说明Navigation组件的使用场景以及与传统Fragment事务的比较。(Jetpack Navigation)
导航图的可视化: 使用导航图直观展示应用中的导航流程,方便理解和修改。 类型安全: 利用Kotlin的类型安全特性,减少在导航时的错误。 生命周期感知: 自动处理Fragment的生命周期,避免了一些常见的生命周期相关问题
九十六、请解释Navigation组件的作用,并介绍Navigation组件的核心组件以及它们之间的关系。 (Jetpack Navigation)
作用: Navigation组件用于实现应用内的导航结构,使得从一个目的地(Destination)到另一个目的地的导航变得更加容易管理和统一。 核心组件: NavGraph(导航图): 包含应用中所有目的地和它们之间的导航关系。 NavController(导航控制器): 管理导航操作的控制器,负责管理与目的地的交互。 NavDestination(导航目的地): 表示导航图中的一个页面或操作,定义了目的地的属性和行为。 这三个核心组件共同构建了整个导航体系,使得在Android应用中实现复杂的导航结构变得更加简单和可维护
九十七、请详细说明ViewModel的作用,并介绍使用ViewModel的主要优势。 (Jetpack ViewModel)
ViewModel的作用在于解决Android应用中活动和碎片(Fragment)的生命周期问题。它允许数据在屏幕旋转等配置更改时存活,并确保数据在不同组件之间共享而不丢失。主要优势包括: 生命周期感知:ViewModel能够感知与UI相关的生命周期变化,确保数据存活时间比短暂的UI组件更长。 数据共享:通过ViewModel,可以在不同的UI组件之间共享和管理数据,避免重复加载或丢失数据。 状态保存:ViewModel在配置变更时保持其状态,例如屏幕旋转,避免重新加载数据和执行耗时操作。
九十八、详细说明LiveData和ViewModel的工作原理,并讨论在实际项目中如何解决常见的生命周期问题。 (Jetpack LiveData和ViewModel)
LiveData是一种可观察的数据持有者,ViewModel用于存储和管理与用户界面相关的数据。深入理解包括: LiveData的粘性事件: 了解postValue和setValue的区别,以及如何避免LiveData的粘性事件在特定场景中引发的问题。 ViewModel的存活周期: 使用ViewModel正确处理配置变化,保证数据在屏幕旋转等情况下不丢失。 LiveData和View绑定: 结合DataBinding,实现LiveData与View之间的绑定,确保数据的实时更新。
九十九、请对比LiveData和Observable,分析它们在Android应用中的应用场景,以及在何种情况下选择使用哪种。 (Jetpack LiveData和Observable)
LiveData和Observable都是用于实现响应式编程的工具,但有一些关键区别: 生命周期感知: LiveData是生命周期感知的,它会在观察者(通常是UI组件)的生命周期内自动启动和停止。这使得在处理UI数据时更加安全,避免了潜在的内存泄漏。 背压处理: Observable在RxJava中通常使用背压策略来处理数据流,而LiveData则通过生命周期感知来实现反应式响应,避免了背压问题。
一百、请解释Paging库的基本原理,并提出在处理大型数据集时如何进行性能优化。(Jetpack Paging)
Paging库通过在RecyclerView中异步加载数据,实现了对大型数据集的高效处理。性能优化的关键在于以下几个方面: DataSource的定制: 实现自定义的DataSource,根据实际需求定制加载规则,如预加载、缓存等。 数据缓存策略: 使用BoundaryCallback来实现边界回调,可以在数据边界到达时触发预加载,减少用户等待时间。 DiffUtil的合理使用: 配合PagedListAdapter,使用DiffUtil来计算并刷新列表的差异,减少不必要的数据刷新,提高界面流畅度。
一百零一、请解释WorkManager的工作原理,并讨论在需要复杂任务调度时如何设计和实现。(Jetpack WorkManager)
WorkManager是一种用于在后台执行任务的库,它建立在JobScheduler、AlarmManager和JobIntentService之上。在高级任务调度中,我们可以: 自定义Worker: 实现Worker类以执行具体任务,并通过Constraints来定义任务的触发条件,如网络状态、电量等。 任务链和顺序执行: 使用OneTimeWorkRequest和WorkContinuation来构建任务链,实现复杂任务的顺序执行。 灵活的重试机制: 结合BackoffPolicy,实现任务的灵活重试策略,应对不同类型的任务失败情况。
一百零二、请解释Hilt依赖注入框架的优势,以及在Android应用中的使用方式。(Jetpack Hilt)
Hilt作为依赖注入框架,具有以下优势: 简化依赖注入: Hilt通过标准化依赖注入的方式,大大简化了在Android应用中的依赖注入过程,减少了样板代码。 与Jetpack集成: Hilt与其他Jetpack组件无缝集成,使得在使用其他Jetpack库时能够更加方便地进行依赖注入。 通过在应用类上添加@HiltAndroidApp注解,以及使用@Inject注解来标记依赖关系,等多种注解,可以轻松地实现依赖注入。
一百零三、在使用Room数据库时,有哪些性能优化的手段可以提高数据库访问的效率?(Jetpack Room)
Room数据库的性能优化手段包括: 合理使用索引: 根据查询需求创建合适的索引,提高查询效率。 批量操作: 使用@Transaction注解将多个操作放在同一个事务中,减少数据库事务的开销。 异步查询: 在后台线程执行查询操作,避免在主线程中执行耗时的数据库操作,防止ANR。 适度使用Room的内存缓存: 使用@Query注解的LiveData返回值时,Room会自动在内存中维护缓存,但要注意不要过度依赖,以免造成内存浪费。
一百零四、MVP模式下,现在是一个弱网,网络请求回调需要5秒才行。 用户等了2秒,有点不太耐烦,直接把界面关了
造成:那么由于回调没有完成,view被强持有,无法释放,等到网络回调完成会发生 1.内存泄漏,没有及时释放view 2.空指针,如果下面的逻辑没有写好,数据回来view不已再。 解决: 第一步:从现象找本质 从上述内存泄漏的原因来看,其实就是Presenter没有跟上view的生命周期。我们其实在view层消失后,即使释放资源,剪断引用链条就可以解决内存泄漏问题了。 那么现在的问题点空就变成 1.view层什么时候资源释放? (找到剪断时机) 2.presenter层怎么释放?(找到剪断方法) 第二步:通过Lifecycle来传递生命周期 说到这里,我不得不说一下。很多人喜欢让Presenter层也拥有onCreate、onDestroy 等等方法,然后让view层在自己的生命周期方法时候去调用Presenter层的方法。且不说Presenter作为一个业务处理层拥有这些方法是否美观。 就仅仅主动调用这一点其实就非常的麻烦,可以主动调用,那么就得承担“被多次调用的风险”和“未被调用的风险”。 说到Lifecyle就是简单的原理就是让P层去观察V层的生命周期,当V层生命周期发送改变,便可以监听到对应的状态。这样就将显式调用变成了隐式监听。(这里我就不展开说了) 1.首先是让Presenter继承LifecycleObserver接口 2.view层提供getLifecycle方法,让Presenter去观察。 第三步:通过autodispose来进行网络反注册,剪断引用链 简单的讲下个组件的原理: AutoDispose在内部创建了ArchLifecycleObserver,采用Event.ON_ANY注解监听Lifecyc的生命周期 当Lifecycle发出ON_DESTROY事件时,ArchLifecycleObserver转发该事件给特定observer,该observer通过filter限定Event.ON_DESTROY事件通过 随后当Activity销毁时,Lifecycle发送事件给ArchLifecycleObserver,并调用onDispose方法取消对Lifecycle的监听。 最后回调至ObservableCreate在订阅时创建的CreateEmitter的dispose方法,将CreateEmitter本身赋值为DISPOSED,销毁observer实例.
一百零五、HashMap和TreeMap的区别
1、HashMap是基于哈希表+数组来实现的,而TreeMap是基于红黑树来实现的。 2、HashMap比TreeMap的性能更高。 3、HashMap和TreeMap都是非线程安全的。 如果在多线程并发情况下建议使用ConcurrentHashMap;如果既要保证线程安全又要保证顺序,可以使用Collections.synchronizedMap()方法转化为线程安全的集合。 4、 HashMap是无序的,而TreeMap是有序的。
一百零六、什么是Lifecycle
Android Lifecycle 是Jetpack组件库中的一个重要组件,它可以帮助我们更好地管理Activity、Fragment等组件的生命周期,从而避免内存泄漏和其他问题
一百零七、Lifecycle 的实现原理
Lifecycle的实现原理是基于观察者模式,主要依赖于三个类:LifecycleOwner、LifecycleRegistry和LifecycleObserver。 1、LifecycleOwner在创建时会创建一个Lifecycle实例。 2、Lifecycle实例本质就是LifecycleRegistry,它会将自己的状态变化通知给所有注册的观察者。 3、LifecycleObserver在收到状态变化通知后,可以根据状态变化执行相应的操作。
一百零八、LiveData的优点
1、确保界面符合数据状态 LiveData 遵循观察者模式。当底层数据发生变化时,LiveData 会通知 **Observer**对象。您可以整合代码以在这些 Observer 对象中更新界面。这样一来,您无需在每次应用数据发生变化时更新界面,因为观察者会替您完成更新。 2、不会发生内存泄漏 观察者会绑定到Lifecycle对象,并在其关联的生命周期遭到销毁后进行自我清理。 不会因 Activity 停止而导致崩溃 如果观察者的生命周期处于非活跃状态(如返回栈中的Activity),则它不好接受任何LiveData事件 3、不再需要手动处理生命周期 界面组件只是观察相关数据,不会停止或恢复观察。LiveData 将自动管理所有这些操作,因为它在观察时可以感知相关的生命周期状态变化。 4、数据始终保持最新状态 如果生命周期变为非活跃状态,它会在再次变为活跃状态时接收最新的数据。例如,曾经在后台的 Activity 会在返回前台后立即接收最新的数据。 5、适当的配置更改 如果由于配置更改(如设备旋转)而重新创建了 Activity 或 Fragment,它会立即接收最新的可用数据。 6、共享资源 您可以使用单例模式扩展 LiveData对象以封装系统服务,以便在应用中共享它们。LiveData 对象连接到系统服务一次,然后需要相应资源的任何观察者只需观察 LiveData 对象。
一百零九、 Rsa/MD5/Aes/Des加密算法
1、Rsa加密:非对称加密 RSA加密算法使用一对密钥进行加解密过程,分别称为公钥和私钥。公钥用于加密信息,而私钥用于解密信息。这种加密算法的原理基于对一极大整数做因数分解的困难性来保证安全性。公钥是公开的,可以广泛分发,而私钥则由个人或实体保存,确保了信息的安全性,避免了直接传递密钥所造成的被破解的风险 2、MD5加密:非对称加密 翻译为消息摘要算法5,是一种单向加密算法,是不可逆的一种的加密方式 3、Aes加密:对称加密 翻译为高级加密标准,AES加密算法支持三种密钥长度:128位、192位或256位,这使得它能够提供高强度的安全性。这种算法的特点包括高速度和易于实现,使其广泛用于需要保护电子数据的场景中。 4、Des加密:对称加密 它使用相同的密钥进行加密和解密操作,DES算法的速度较快,适用于加密大量数据的场合。DES算法的安全性不高,3DES算法被提出,它是对一块数据使用三个不同的密钥进行三次加密,以提高加密强度,3DES算法采用两个密钥进行三重加密。
一百一十、支付开发流程
Android支付开发的流程主要涉及几个关键步骤,包括应用创建与配置、SDK集成、订单处理、支付请求发起、支付结果处理等。以下是具体的流程说明: 1、应用创建与配置 首先,需要创建应用并获取APPID。这是应用能够正常使用支付宝或其他支付服务的必要步骤。APPID是应用的唯一标识,通过它,应用才能调用相应的支付接口。 2、SDK集成 接下来,需要下载并集成支付SDK到Android项目中。这包括在AndroidManifest.xml文件中添加必要的声明,以便应用能够正确地处理支付相关的活动和服务。 3、订单处理 在用户发起支付请求之前,商户后台需要生成订单并组织订单信息。这包括根据《手机控件支付产品接口规范》将订单信息推送给支付平台后台。 4、支付请求发起 用户通过客户端点击购买商品后,客户端会发起订单生成请求到商户后台。商户后台接收到请求后,会生成交易流水号(TN)并返回给客户端。客户端通过交易流水号调用支付控件,用户在支付控件中输入支付信息后,支付控件向银联或支付宝后台发起支付请求。 5、支付结果处理 支付成功后,支付平台后台会将支付结果通知给商户后台,同时也会通知支付控件。支付控件显示支付结果并返回给客户端。 6、安全性考虑 在整个流程中,安全性是一个重要的考虑因素。包括但不限于数据加密、签名验证等措施,以确保支付过程的安全
一百一十一、Rxjava map和flatMap区别
1、map变换后可以返回任意值,而flatMap则只能返回ObservableSource类型 2、map只能进行一对一的变换,而flatMap则可以进行一对一,一对多,多对多的变换
一百一十二、 冷流和热流是什么
冷流(Cold Flow):冷流的特点是在数据被使用方订阅后,即调用collect方法之后,提供方才开始执行发送数据流的代码,通常是调用emit方法。这意味着不进行消费,就不会进行生产。冷流的使用方和提供方之间是一对一的关系。即,每次消费都会触发一次生产过程。 热流(Hot Flow):热流则与冷流相反,无论有无使用方,提供方都可以执行发送数据流的操作。热流提供方和使用方之间是一对多的关系。这意味着,热流可以不管是否有消费行为,都会持续生产数据。SharedFlow就是热流的一种例子,任何流也可以通过stateIn和shareIn操作转化为热流,或者通过produceIn操作将流转化为一个热通道也能达到目的
一百一十三、冷流和热流可以做什么
冷流的应用:在Kotlin项目中,使用协程和冷流可以替换RxJava框架进行响应式编程,这比使用RxJava更有优势 热流的应用: 1、SharedFlow可以用来做事件总线,替换EventBus; 2、StateFlow可以用来做事件状态更新,替换LiveData,并结合MVI替换MVVM
一百一十四、MVI架构
MVI架构模式,其核心思想在于实现单向数据流和唯一可信数据源。在MVI架构中,应用程序的状态管理得到简化,并且用户界面与业务逻辑之间的交互更加清晰和规范。 Model:代表数据模型,负责存储应用程序的状态。它是唯一可信的数据源,意味着应用程序的所有状态都集中在这里管理,避免了状态不一致的问题。 View:即用户界面,负责展示Model中的状态,并响应用户的操作。当用户与界面进行交互时,会产生相应的Intent。 Intent:表示用户的意图或操作。它是View向Model传递信息的方式,告诉Model用户想要执行的操作或期望达到的状态。 特点: 1、在MVI架构中,数据流动是单向的:View产生Intent,Intent传递给Reducer(一个处理Intent并更新Model状态的函数),Reducer根据Intent生成新的Model状态,并将这个状态发送回View进行渲染。这种单向数据流有助于简化状态管理,并使得代码更加可预测和易于维护。 2、MVI架构还强调代码分层和清晰的责任划分。ViewModel无需关心View如何触发和更新,它只需要维护Intent和State。View与ViewModel的交互更加规范,使用Kotlin的密封类特性来封装Intent和State,使得代码更加规范、整洁和易读。
一百一十五、Android白屏流程优化方案
1、启动页过渡动画:启动页过渡动画是一种常见的白屏优化方案。通过在应用启动时显示一个动画,可以让用户感知到应用正在加载,并提供更好的交互体验。 2、延迟加载:即在应用启动后,先显示一个空白页面,然后在后台进行资源加载和初始化操作,等操作完成后再显示内容。 3、避免在App启动时做一些业务的耗时操作 4、异步去初始化一些第三方得依赖模块 5、在项目清单文件中给启动页配置相应主题 6、利用这个白屏时间加载一张广告图片
来源:
CSDN
本文观点不代表码客-全球程序员交流社区立场,不承担法律责任,文章及观点也不构成任何投资意见。
评论列表