Android面试题总结
- Android四大组件
- Activity(活动)
- 概念
- Service(服务)
- 概念
- 定义与作用
- Content Provider(内容提供器)
- 介绍
- 作用
- 系统的Content Provider
- 自定义Content Provider
- Broadcast Receiver(广播)
- 概述
- 广播的作用
- 广播接收者的创建
- 广播接收者的类型
- 注册广播的两种方式
- 静态注册和动态注册的区别
- 有序广播和无序广播的区别
- 有序广播接收者们的优先级
- 有序广播的拦截和篡改
- Activity(活动)
- Android类加载器
- Android的系统架构
- Android应用程序结构
- Android中的几种动画
- Android内存溢出内存泄露
- 跨进程通讯的几种方式
- Android中为什么子线程不能更新UI
- 如果不做这个校验,是不是我也可以正常在子线程更新UI
- 但是google为什么要这样去设计呢
- ViewRootImp是在onActivityCreated方法后面创建的吗
- 为什么一定需要checkThread呢
- 那为什么不加锁呢
- 为什么一开始在Activity的onCreate方法中创建一个子线程访问UI,程序还是正常能跑起来呢
- Android中子线程真的不能更新UI吗
- 保证上述条件1成立不就可以避免checkThread时候抛出异常了吗?为什么还需要开启消息循坏
- 使用子线程更新UI有实际应用场景吗
- Android程序运行时权限与文件系统权限的区别
- Android进程与线程
- 进程
- 前台进程
- 可见进程
- 服务进程(service进程)
- 后台进程
- 空进程
- Android线程间通信有哪几种方式
- Devik进程和Linux进程的区别
- 进程保活(不死进程)
- 当前Android进程保活手段主要分为 黑、白、灰 三种
- 黑色保活
- 白色保活
- 灰色保活
- 当前Android进程保活手段主要分为 黑、白、灰 三种
- 进程
- Android的数据存储
- 使用SharedPreferences存储数据
- 文件存储数据
- SQLite数据库存储数据
- 使用ContentProvider存储数据
- 网络存储数据
- Android六大布局
- Activity(持续更新中~~~)
Android四大组件
简单介绍:Android四大核心组件指的是 Activity、Service、Content Provider、BroadCast Receiver,核心组件都是由 Android系统进行管理和维护的,一般都要在清单文件中进行注册或者在代码中动态注册。
活动(activity):用于表现功能; 服务(service):后台运行服务,不提供界面呈现; 内容提供者(Content Provider):支持多个应用中存储和读取数据; 广播接受者(Broadcast Receive):用于接收广播。
Activity(活动)
概念
概念:在android中,Activity相当于一个页面,可以在 Activity中添加 Button、CheckBox 等控件,一个android程序有多个Activity组成。 // Activity 之间通过 Intent 进行通信;直接通过 Bundle 对象来传递
- 一个 Activity 通常就是一个单独的屏幕(窗口)
- Activity 之间通过 Intent 进行通信。
- Android 应用中每一个 Activity 都必须要在 AndroidManifest.xml 配置文件中声明,否则系统将不识别也不执行该Activity。在 android stdio会自动生成,但 eclipse 需要自己手动添加 定义与作用: Activity 的中文意思是 活动,代表手机屏幕的一屏,或是平板电脑中的一个窗口,提供了和用户交互的可视化界面。一个活动开始,代表 Activity 组件启动,活动 结束,代表一个 Activity 的生命周期结束。一个 Android 应用必须通过 Activity 来 运行 和 启动,Activity 的生命周期交给系统统一管理。Activity 是用于处理 UI 相关业务的,比如加载界面、监听用户操作事件。
Service(服务)
概念
概念:Service(服务)是安卓中的四大组件之一,它通常用作在后台处理耗时的逻辑,与 Activity 一样,它存在自己的生命周期,也需要在 AndroidManifest.xml 配置相关信息。开发人员需要在应用程序配置文件中声明全部的 service,使用 < service>< /service> 标签。 Service 通常位于后台运行,它一般不需要与用户交互,因此 Service 组件没有图形用户界面。Service 组件需要继承Service 基类。Service 组件通常用于为其他组件提供后台服务或监控其他组件的运行状态。
定义与作用
定义与作用:Service(服务)是一个没有用户界面的专门在后台处理耗时任务的 Android 组件,它没有UI。它有两种启动方式,startService和bindService。其他应用组件能够启动 Service,并且当用户切换到另外的应用场景,Service将持续在后台运行。另外,一个组件能够绑定到一个service与之交互(IPC机制),例如,一个service可能会处理网络操作,播放音乐,操作文件I/O或者与内容提供者(content provider)交互,所有这些活动都是在后台进行。 Service 还有一个作用就是提升进程(每个应用都是一个进程)的优先级,进程的优先级指的是在 Android 系统中,会把正在运行的应用确定一个优先级,当内存空间不足时,系统会根据进程的优先级清理掉一部分进程占用的内存空间,以获得足够的内存空间以供新启用的应用运行。 详细的进程优先级划分如下:
- 前台进程:应用程序存在Activity正位于前台,可见并可控
- 可见进程:应用程序存在Activity处于局部可见状态,即局部可见却不可控
- 服务进程:应用程序存在正在运行的Service
- 后台进程:应用程序的所有Activity均被置于后台,没有任何Activity可见
- 空进程:已经退出的应用程序
Content Provider(内容提供器)
介绍
介绍:Content Provider是 android 四大组件之一的内容提供器,它主要的作用就是将程序的内部的数据和外部进行共享,为数据提供外部访问接口,被访问的数据主要以数据库的形式存在,而且还可以选择共享哪一部分的数据。这样一来,对于程序当中的隐私数据可以不共享,从而更加安全。Content Provider 是 android中 一种跨程序共享数据的重要组件
- android 平台提供了 ContentProvider 把一个应用程序的指定数据集提供给其他应用程序。其他应用可以通过ContentResolver类 从该内容提供者中获取或存入数据。
- 只有需要在多个应用程序间共享数据是才需要 内容提供者。例如,通讯录数据被多个应用程序使用,且必须存储在一个内容提供者中。它的好处是统一数据访问方式。
- ContentProvider 实现数据共享。ContentProvider 用于保存和获取数据,并使其对所有应用程序可见。这是不同应用程序间共享数据的唯一方式,因为 android 没有提供所有应用共同访问的公共存储区。
- 开发人员不会直接使用 ContentProvider 类的对象,大多数是通过 ContentResolver 对象实现对 Content Provider 的操作。
- Content Provider 使用 URI 来唯一标识其数据集,这里的 URI 以 content:// 作为前缀,表示该数据由 Content Provider来管理。
四大基本组件都需要注册才能使用,每个 Activity、service、Content Provider 都需要在 AndroidManifest 文件中进行配置。AndroidManifest 文件中未进行声明的 activity、服务 以及 内容提供者 将不为系统所见,从而也就不可用。而 broadcast receiver 广播接收者的注册分静态注册(在AndroidManifest文件中进行配置)和通过代码动态创建并以调用Context.registerReceiver()的方式注册至系统。需要注意的是在AndroidManifest文件中进行配置的广播接收者会随系统的启动而一直处于活跃状态,只要接收到感兴趣的广播就会触发(即使程序未运行)。
作用
作用:Content Provider中文意思是内容提供者,Content Provider可以将应用程序自身的数据对外(对其它应用程序)共享,使得其它应用可以对自身的数据进行增、删、改、查操作。
系统的Content Provider
// 使用系统的 Content Provider: Android 系统使用了许多 Content Provider,将系统中的绝大部分常规数据进行对外共享,系统的 ContentProvider 有很多,例如:通讯录、通话记录、短信、相册、歌曲、视频、日历等等,一般这些数据都存放于一个个的数据库中。同时这些数据一般都需要和第三方的 app 进行共享数据。既然是使用系统的,那么 Content Provider的具体实现就不需要我们担心了,使用内容提供者的步骤如下: 1、获取 Content Resolver 实例 2、确定 Uri 的内容,并解析为具体的 Uri 实例 3、通过 Content Resolver 实例来调用相应的方法,传递相应的参数,但是第一个参数总是 Uri,它制定了我们要操作的数据的具体地址
自定义Content Provider
// 自定义 ContentProvider: 系统的 Content Provider在与我们交互的时候,只接受了一个 Uri 的参数,然后根据我们的操作返回给我们结果。系统到底是如何根据一个 Uri 就能够提供给我们准确的结果呢?只有自己亲自实现一个看看了。 和之前提到的一样,想重新自定义自己程序中的四大组件,就必须重新实现一个类,重写这个类中的抽象方法,在清单文件中注册,最后才能够正常使用。
Broadcast Receiver(广播)
概述
概述: Broadcast Receiver( 广播接收者 )顾名思义就是用来接收来自系统和应用中的广播 的 系统组件。广播是一种一对多的通信方式,即存在1个发送方,若干个接收方。在 Android 系统中,广播体现在方方面面,例如:当开机完成后系统会产生一条广播,接收到这条广播就能实现开机启动服务的功能;当网络状态改变时系统会产生一条广播,接收到这条广播就能及时地做出提示和保存数据等操作;当电池电量改变时,系统会产生一条广播,接收到这条广播就能在电量低时告知用户及时保存进度,等等。把这种数据的传递方式的机制称之为 “广播” 。Android 系统会在特定的情景下发出各种广播,例如开机、锁屏了、电量不足了、正在充电了、呼出电话了、被呼叫了…… // android广播分为两个角色:发送者和接收者
广播的作用
- 用于不同组件间的通信(含:应用内/不同应用之间)
- 用于多线程通信
- 与 android 系统的通信
广播接收者的创建
- 构建 Intent,使用 sendBroadcast 方法发出广播。
- 自定义一个类,该 类 继承 BroadcastReceive 基类
- 重写抽象方法 onReceive() 方法
- 注册该广播接收者,我们可以在代码中注册,也可以在 manifest.xml 中注册。
广播接收者的类型
- Normal broadcasts:默认广播。发送一个默认广播使用 Content.sendBroadcast() 方法,普通广播对于接收者来说是完全异步的,通常每个接收者都无需等待即可以接收到广播,接收者相互之间不会有影响。对于这种广播,接收者无法终止广播,即无法阻止其他接收者的接收动作。
- Ordered broadcasts:有序广播。发送一个有序广播使用 Content.sendOrderedBroadcast() 方法,有序广播比较特殊,它每次只发送到优先级较高的接收者那里,然后由优先级高的接收者再传播到优先级低的接收者那里,优先级高的接收者有能力终止这个广播
- Sticky Broadcast:粘性广播。当处理完之后的 Intent,依然存在,直到你把它去掉。
注册广播的两种方式
广播接收者的注册有两种方法,分别是AndroidManifest文件中进行静态注册和程序动态注册。
- 第一种是静态注册,也可成为常驻型广播,这种广播需要在Androidmanifest.xml中进行注册,这中方式注册的广播,不受页面生命周期的影响,即使退出了页面,也可以收到广播这种广播一般用于想开机自启动啊等等,由于这种注册的方式的广播是常驻型广播,所以会占用CPU的资源。
- 第二种是动态注册,而动态注册的话,是在代码中注册的,这种注册方式也叫非常驻型广播,收到生命周期的影响,退出页面后,就不会收到广播,我们通常运用在更新UI方面。这种注册方式优先级较高。最后需要解绑,否会会内存泄露。
静态注册和动态注册的区别
- 动态注册广播接收者特点是当用来注册的 Activity 关掉后,广播也就失效了。( 动态注册广播不是常驻型广播,也就是说广播跟随 activity 的生命周期。注意:在 activity 结束前,移除广播接收器。 )
- 静态注册时无需担忧广播接收者是否被关闭,只要设备是开启状态,广播接收者也是打开着的。也就是说哪怕 app 本身未启动,该 app 订阅的广播在触发时也会对它起作用。( 静态注册是常驻型,也就是说当应用程序关闭后,如果有信息广播来,程序也会被系统调用自动运行。 )
有序广播和无序广播的区别
- 普通广播: 即为 无序广播,谁都可以接收,并不会相互打扰。普通广播是完全异步的,可以在同一时刻(逻辑上)被所有接收者接收到,消息传递的效率比较高,但缺点是:接收者不能将处理结果传递给下一个接收者,并且无法终止广播 Intent 的传播;
- 有序广播:调用 sendOrderedBroadcast(Intent, String permission)方法发送的广播,各广播接收者在接收广播时,会存在一定的先后顺序,即某接收者会先收到广播,其他接收者后收到广播,广播会在各接收者之间按照一定的先后顺序进行传递。在广播的传递过程中,先接收到广播的接收者可以对广播进行拦截或篡改。( 有序广播是按照接收者声明的优先级别(声明在 intent-filter 元素的 android:priority 属性中,数越大优先级别越高,取值范围:-1000 到 1000。也可以调用IntentFilter 对象的 setPriority() 进行设置),被接收者依次接收广播。如:A 的级别高于 B,B 的级别高于 C,那么,广播先传给A,再传给B,最后传给C。A 得到广播后,可以往广播里存入数据,当广播传给 B 时,B可以从广播中得到 A 存入的数据
总结:
- 当广播为有序广播时:
- 优先级高的先接收
- 同优先级的广播接收器,动态优先于静态
- 同优先级的同类广播接收器,静态:先扫描的优先于后扫描的,动态:先注册的优先于后注册的。
- 当广播为普通广播时:
- 无视优先级,动态广播接收器优先于静态广播接收器
- 同优先级的同类广播接收器,静态:先扫描的优先于后扫描的,动态:先注册的优先于后注册的。
有序广播接收者们的优先级
有序广播的接收者们的优先级用于确定接收的先后顺序,优先级越高的接收者,将更优先接收到广播,反之,则更靠后接收到广播。
- 注册广播时,在广播对应的 IntentFilter 中的 priority 属性直接决定优先级,该属性值为 int 类型的数值,取值越大,则优先级越高!
- 如果存在多个广播接收者配置的 priority 属性值相同,则动态注册的广播接收者的优先级高于静态注册的广播接收者。
- 如果根据以上两条规则都无法确定优先级,则根据注册的先后顺序确定各接收者们的优先级。
有序广播的拦截和篡改
- 拦截:在广播接收者中,使用abortBroadcast()方法,可以终止有序广播向后继续传递,即后续的接收者们将无法接收到该广播。注意:该方法只能在接收有序广播时调用!
- 篡改:在广播接收者中,调用setResult()方法,可以向广播中添加数据,并在后续的接收者中,可以通过getResult()获取这些数据,同时,后续的接收者也可以再次调用setResult()方法重新向广播中写入数据,即覆盖原有的数据,以实现篡改。
Android类加载器
在Android开发中,不管是插件化还是组件化,都是基于Android系统的类加载器ClassLoader来设计的。只不过Android平台上虚拟机运行的是Dex字节码,一种对class文件优化的产物,传统Class文件是一个Java源码文件会生成一个.class文件,而Android是把所有Class文件进行合并、优化,然后再生成一个最终的class.dex,目的是把不同class文件重复的东西只需保留一份,在早期的Android应用开发中,如果不对Android应用进行分dex处理,那么最后一个应用的apk只会有一个dex文件。
Android中常用的类加载器有两种,DexClassLoader和PathClassLoader,它们都继承于BaseDexClassLoader。区别在于调用父类构造器时,DexClassLoader多传了一个optimizedDirectory参数,这个目录必须是内部存储路径,用来缓存系统创建的Dex文件。而PathClassLoader该参数为null,只能加载内部存储目录的Dex文件。所以我们可以用DexClassLoader去加载外部的apk文件,这也是很多插件化技术的基础。
Android的系统架构
从小到上就是:linux,kernel,lib,dalvik vm,application,framework, app
Android应用程序结构
- main code
- unit test
- manifest
- res -> drawable,drawable-xxhdpi,layout,value,mipmap mipmap 是一种很早就有的技术了,翻译过来就是纹理映射技术. google建议只把启动图片放入。
- lib
- color
Android中的几种动画
帧动画:指通过指定每一帧的图片和播放时间,有序的进行播放而形成动画效果,比如想听的律动条。
视图动画(补间动画):指通过指定View的初始状态、变化时间、方式,通过一系列的算法去进行图形变换,从而形成动画效果,主要有Alpha、Scale、Translate、Rotate四种效果。注意:只是在视图层实现了动画效果,并没有真正改变View的属性,比如滑动列表,改变标题栏的透明度。
属性动画:在Android3.0的时候才支持,通过不断的改变View的属性,不断的重绘而形成动画效果。相比于视图动画,View的属性是真正改变了。比如view的旋转,放大,缩小。 通过变化属性来达到动画的效果,性能略差,支持点击等事件。android 3.0
Gif动画:原理和帧动画差不多,是canvas画出来。
Android内存溢出内存泄露
// 内存溢出: out of memory:是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。内存溢出通俗的讲就是内存不够用。 // 内存泄露: memory leak:是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光 // 内存泄露检测工具 → LeakCanary
跨进程通讯的几种方式
// Android 跨进程通信,像intent,contentProvider,广播,service都可以跨进程通信。 1、Files 文件系统(包括内存映射) 2、Sockets 3、Pipes 管道 4、共享内存 5、Intents, ContentProviders, Messenger 6、Binder
intent:这种跨进程方式并不是访问内存的形式,它需要传递一个uri,比如说打电话。 contentProvider:这种形式,是使用数据共享的形式进行数据共享。 service:远程服务,aidl 广播
Android中为什么子线程不能更新UI
// Android中为什么子线程不能更新UI? viewRootImpl对象是在Activity中的onResume方法执行完成之后,View变得可见时才创建的,之前的操作是没有进行线程检查的,所以没有报错。但是ViewRootImpl创建之后,由于进行了checkThread操作,所以就不能在子线程更改UI了 当访问 UI 时,ViewRootImpl 会调用 checkThread方法去检查当前访问 UI 的线程是否为创建 UI 的那个线程,如果不是。则会抛出异常
如果不做这个校验,是不是我也可以正常在子线程更新UI
// 如果不做这个校验,是不是我也可以正常在子线程更新UI? 按理来说,这样是可以的
但是google为什么要这样去设计呢
// 但是google为什么要这样去设计呢 1.如果在不同的线程去控制用一个控件,由于网络延时或者大量耗时操作,会使UI绘制错乱,出了问题也很难去排查到底是哪个线程更新时出了问题; 2.主线程负责更新,子线程负责耗时操作,能够大大提高响应效率 3.UI线程非安全线程,多线程进行并发访问有可能会导致内存溢出,降低硬件使用寿命;且非线程安全不能加Lock线程锁,否则会阻塞其他线程对View的访问,效率就会变得低下!
ViewRootImp是在onActivityCreated方法后面创建的吗
// ViewRootImp是在onActivityCreated方法后面创建的吗? 1、ViewRootImpl是在Activity的onResume()方法后面创建出来的,所以在onResume之前的UI更新可以在子线程操作而不报错,因为这个时候ViewRootImpl还没有创建,没有执行checkThread()方法。 2、安卓系统中,操作viwe对象没有加锁,所以如果在子线程中更新UI,会出现多线程并发的问题,导致页面展示异常。
为什么一定需要checkThread呢
// 为什么一定需要checkThread呢? 因为UI控件不是线程安全的
那为什么不加锁呢
// 那为什么不加锁呢? 一是加锁会让UI访问变得复杂; 二是加锁会降低UI访问效率,会阻塞一些线程访问UI。 所以干脆使用单线程模型处理UI操作,使用时用Handler切换即可
为什么一开始在Activity的onCreate方法中创建一个子线程访问UI,程序还是正常能跑起来呢
// 为什么一开始在Activity的onCreate方法中创建一个子线程访问UI,程序还是正常能跑起来呢? 因为ViewRootImpl 的创建在 onResume 方法回调之后,而我们一开篇是在 onCreate 方法中创建了子线程并访问 UI,在那个时刻,ViewRootImpl 还没有创建,我们在因此 ViewRootImpl#checkThread 没有被调用到,也就是说,检测当前线程是否是创建的 UI 那个线程 的逻辑没有执行到,所以程序没有崩溃一样能跑起来。而之后修改了程序,让线程休眠了 3000 毫秒后,程序就崩了。很明显 3000 毫秒后 ViewRootImpl 已经创建了,可以执行 checkThread 方法检查当前线程
Android中子线程真的不能更新UI吗
// Android中子线程真的不能更新UI吗? 任何线程都可以更新自己创建的UI,但是需要满足各自对应的条件
- ViewRootImpl 还没创建出来之前。UI 修改的操作没有线程限制。因为 checkThread 方法不会被执行到。
- 在 ViewRootImpl 创建完成之后,保证「创建 ViewRootImpl 的操作」和「执行修改 UI 的操作」在同一个线程即可。也就是说,要在同一个线程调用 ViewManager的addView 和 ViewManager的updateViewLayout 方法。 // 注:ViewManager 是一个接口,WindowManger 接口继承了这个接口,我们通常都是通过 WindowManger(具体实现为 WindowMangerImpl) 进行 view 的 add remove update 操作的。 对应的线程需要创建 Looper 并且调用 Looper的loop 方法,开启消息循环。
保证上述条件1成立不就可以避免checkThread时候抛出异常了吗?为什么还需要开启消息循坏
// 保证上述条件1成立,不就可以避免checkThread时候抛出异常了吗?为什么还需要开启消息循坏? 条件 1 可以避免检查异常,但是无法保证 UI 可以被绘制出来。 条件 2 可以让更新的 UI 效果呈现出来。 注:WindowManger的addView 最终会调用WindowManageGlobal的addView方法,进而触发ViewRootImpl的setView 方法,该方法内部会调用ViewRootImpl的requestLayout 方法。根据 UI 绘制原理,下一步就是 scheduleTraversals了,该方法会往消息队列中插入一条消息屏障,然后调用 Choreographer的postCallback 方法,往 looper 中插入一条异步的 MSG_DO_SCHEDULE_CALLBACK 消息。等待垂直同步信号回来之后执行
使用子线程更新UI有实际应用场景吗
// 使用子线程更新 UI 有实际应用场景吗? Android 中的 SurfaceView 通常会通过一个子线程来进行页面的刷新。如果我们的自定义 View 需要频繁刷新,或者刷新时数据处理量比较大,那么可以考虑使用 SurfaceView 来取代 View
Android程序运行时权限与文件系统权限的区别
文件的系统权限是由linux系统规定的,只读,读写等。 运行时权限是对于某个系统上的app的访问权限,允许,拒绝,询问。这个可以防止非法的程序访问敏感的信息。
Android进程与线程
进程
- 前台进程
- 可见进程
- 服务进程
- 后台进程
- 空进程
前台进程
// 前台进程
- 当前进程activity正在与用户进行交互。
- 当前进程service正在与activity进行交互或者当前service调用了startForground()属于前台进程或者当前service正在执行生命周期(onCreate(),onStart(),onDestory())
- 进程持有一个BroadcostReceiver,这个BroadcostReceiver正在执行onReceive()方法
可见进程
// 可见进程:
- 进程持有一个activity,这个activity不再前台,处于onPouse()状态下,当前覆盖的activity是以dialog形式存在的。
- 进程有一个service,这个service和一个可见的Activity进行绑定。
服务进程(service进程)
// service进程: 当前开启startSerice()启动一个service服务就可以认为进程是一个服务进程。
后台进程
// 后台进程 activity的onStop()被调用,但是onDestroy()没有调用的状态。该进程属于后台进程。
空进程
// 空进程: 改进程没有任何运行的数据了,且保留在内存空间,并没有被系统killed,属于空进程。该进程很容易被杀死。
Android线程间通信有哪几种方式
- 共享变量(内存)
- 管道
- handler机制
- runOnUiThread(Runnable)
- view.post(Runnable)
Devik进程和Linux进程的区别
// Davik 进程:
- Dalvik 虚拟机运行在 Linux 系统之上。
- Davik 进程就是 Linux 操作系统中的一个进程,属于 Linux 进程。
- 每一个 Android 应用程序进程都有一个 Dalvik 虚拟机实例。这样做的好处是 Android 应用程序进程之间不会相互影响,也就是说,一个 Android 应用程序进程的意外终止,不会影响到其他的应用程序进程的正常运行。 注:每个 Android 应用程序都会对应一个独立的 Dalvik 虚拟机
// Linux 进程:
- 有独立的内核堆栈和独立的存储空间,它是操作系统中资源分配和调度的最小单位。
- 以进程为单位,分配系统资源,给程序进行调度。
- 在执行一个程序时,它会创建一个进程,来执行应用程序,并且伴随着资源的分配和释放。
进程保活(不死进程)
当前Android进程保活手段主要分为 黑、白、灰 三种
黑色保活:不同的app进程,用广播相互唤醒(包括利用系统提供的广播进行唤醒) 白色保活:启动前台Service 灰色保活:利用系统的漏洞启动前台Service
黑色保活
所谓黑色保活,就是利用不同的app进程使用广播来进行相互唤醒
- 场景1 :开机,网络切换、拍照、拍视频时候,利用系统产生的广播唤醒app
- 场景2 :接入第三方SDK也会唤醒相应的app进程,如微信sdk会唤醒微信,支付宝sdk会唤醒支付宝。由此发散开去,就会直接触发了下面的 场景3
- 场景3 :假如你手机里装了支付宝、淘宝、天猫等阿里系的app,那么你打开任意一个阿里系的app后,有可能就顺便把其他阿里系的app给唤醒了。(只是拿阿里打个比方,其实BAT系都差不多)
白色保活
// 白色保活 白色保活手段非常简单,就是调用系统api启动一个前台的Service进程,这样会在系统的通知栏生成一个Notification,用来让用户知道有这样一个app在运行着
灰色保活
// 灰色保活 灰色保活,这种保活手段是应用范围最广泛。它是利用系统的漏洞来启动一个前台的Service进程,与普通的启动方式区别在于,它不会在系统通知栏处出现一个Notification,看起来就如同运行着一个后台Service进程一样。这样做带来的好处就是,用户无法察觉到你运行着一个前台进程(因为看不到Notification),但你的进程优先级又是高于普通后台进程的。那么如何利用系统的漏洞呢,大致的实现思路和代码如下: 思路一:API < 18,启动前台Service时直接传入new Notification(); 思路二:API >= 18,同时启动两个id相同的前台Service,然后再将后启动的Service做stop处理
Android的数据存储
使用SharedPreferences存储数据
它是Android提供的用来存储一些简单配置信息的一种机制,采用了XML格式将数据存储到设备中。只能在同一个包内使用,不能在不同的包之间使用。
文件存储数据
文件存储方式是一种较常用的方法,在Android中读取/写入文件的方法,与Java中实现I/O的程序是完全一样的,提供了openFileInput()和openFileOutput()方法来读取设备上的文件。
SQLite数据库存储数据
SQLite是Android所带的一个标准的数据库,它支持SQL语句,它是一个轻量级的嵌入式数据库
使用ContentProvider存储数据
主要用于应用程序之间进行数据交换,从而能够让其他的应用保存或读取此Content Provider的各种数据类型
网络存储数据
通过网络上提供给我们的存储空间来上传(存储)和下载(获取)我们存储在网络空间中的数据信息