前言
最近一段瞎xx百度,无意中发现一个帖,看帖子的质量肯定是被转载不知道多少次了,来出处都已经不见了,但是博主的质量比较的高。甚至把这篇文章的内容当做面试前的查缺补漏也是非常合适的。
总之是一篇质量非常给常高的文章。所以忍不住在这里发了出来。如果原作者看到这篇文章,觉得不妥。私聊我就会删掉。
一、应用组件
应用没有唯一的入口,没有 main() 函数,因为应用是由多个组件拼凑在一起的,每个组件都是系统或者用户进入应用的入口,组件之间既可以是相互独立的,也可以是相互依赖的。系统和其它应用在被允许的情况下可以启动/激活一个应用的任意一个组件
组件有四种类型: Activity,Service,BroadcastReceiver 和 ContentProvider
1.1、Activity
Activity 表示一个新的用户界面,只能由系统进行创建和销毁,应用只能监听到一些生命周期回调,这些回调通常也被叫作生命周期方法
Activity 的名字一旦确定好就不要再更改了,否则可能会引发一系列问题
1.2、Service
Service 表示一个后台服务,Service 可以是独立的,可以在应用退出后继续运行。也可以绑定到其他进程或 Activity,表示其他进程想使用这个 Service,像输入法、动态壁纸、屏保等系统功能都是以 Service 的形式存在的,在需要运行的时候进行绑定
大部分情况下,更建议使用 JobScheduler,因为 JobScheduler 和 Doze API 配合下一般会比简单使用 Service 更省电
1.3、BroadcastReceiver
BroadcastReceiver 是一个事件传递的组件,通过它应用可以响应系统范围的广播通知。系统的包管理器会在安装应用时将应用中的静态广播接收器注册好,所以即使应用没在运行,系统也能把事件传递到该组件。
通过 BroadcastReceiver 可以实现进程间通信
1.4、ContentProvider
ContentProvider 是在多个应用间共享数据的组件,如果应用的一些数据想要被其它应用使用,必须通过 ContentPrivider 进行管理,不过应用的私有数据也可以通过 ContentProvider 进行管理,主要还是因为 ContentProvider 提供了共享数据的抽象,使用者不需要知道数据究竟是以文件形式还是数据库等其他形式存储的,只需要通过 ContentProvider 提供的 统一的 API 进行数据的增删改查即可。同时 ContentProvider 还提供了 安全 环境,可以根据需要方便地控制数据的访问权限,不需要手动控制文件权限或数据库权限
为了安全,也为了方便,一般需要通过 ContentResolver 操作 ContentProvider
通过 ContentProvider 可以实现进程间通信
二、激活组件
- 应用不能也不应该直接激活其它应用的任意一个组件,但是系统可以,所以要想激活一个组件,需要给系统发一个消息详细说明你的意图( Intent ),之后系统就会为你激活这个组件
- Activity,Service,BroadcastReceiver 都需要通过被称为 Intent 的异步消息激活
- 被激活组件返回的结果也是 Intent 形式的
- ContentProvider 只有在收到 ContentResolver 的请求时才会被激活
- 只有 BroadcastReceiver 可以不在 manifest 文件中注册,因为有些 BroadcastReceiver 需要在程序运行时动态地注册和注销。而其它组件必须在manifest文件中注册,否则无法被系统记录,也就无法被激活
- 如果 Intent 通过组件类名显式指明了唯一的目标组件,那么这个 Intent 就是显式的,否则就是隐式的。隐式 Intent 一般只描述要执行动作的类型,必要时可以携带数据,系统会根据这个隐式 Intent 的描述决定激活哪个组件,如果有多个组件符合激活条件,系统一般会弹出选择框让用户选择到底激活哪个组件
- Service 必须使用显式 Intent 激活,不能声明 IntentFilter
- 启动指定的 Activity 使用显式 Intent,启动随便一个能完成指定工作的 Activity 使用隐式 Intent。能完成指定工作的那些想要被隐式 Intent 激活的 Activity 需要事先声明好 IntentFilter 表示自己有能力处理什么工作,IntentFilter 一般通过 能完成的动作 、意图类型 和 额外数据 来描述
- 要想被隐式 Intent 激活,意图类型至少要包含 android.intent.category.DEFAULT 的意图类型
- 在使用隐式 Intent 激活 Activity 之前一定要检查一下有没有 Activity 能处理这个 Intent :
if (sendIntent.resolveActivity(getPackageManager()) != null) { startActivity(sendIntent); } PackageManager packageManager = getPackageManager(); List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); boolean isIntentSafe = activities.size() > 0;
- 使用隐式 Intent 时每次都强制用户选择一个组件激活:
Intent intent = new Intent(Intent.ACTION_SEND); String title = getResources().getString(R.string.chooser_title); Intent chooser = Intent.createChooser(intent, title); if (intent.resolveActivity(getPackageManager()) != null) { startActivity(chooser); }
- 如果想要你的 Activity 能被隐式 Intent 激活,如果想要某个 链接 能直接跳转到你的 Activity,必须配置好 IntentFilter。这种链接分为两种: Deep links 和 Android App Links
- Deep links 对链接的 scheme 没有要求,对系统版本也没有要求,也不会验证链接的安全性,不过需要一个 android.intent.action.VIEW 的 action 以便 Google Search 能直接打开,需要 android.intent.category.DEFAULT 的 category 才能响应隐式 Intent,需要 android.intent.category.BROWSABLE 的 category 浏览器打开链接时才能跳转到应用,所以经典用例如下。一个 intent filter 最好只声明一个 data 描述,否则你得考虑和测试所有变体的情况。系统处理这个链接的流程为: 如果用户之前指定了打开这个链接的默认应用就直接打开这个应用 → 如果只有一个应用可以处理这个链接就直接打开这个应用 → 弹窗让用户选择用哪个应用打开
<activity android:name="com.example.android.GizmosActivity" android:label="@string/title_gizmos" > <intent-filter android:label="@string/filter_view_http_gizmos"> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <!-- Accepts URIs that begin with "http://www.example.com/gizmos” --> <data android:scheme="http" android:host="www.example.com" android:pathPrefix="/gizmos" /> <!-- note that the leading "/" is required for pathPrefix--> </intent-filter> <intent-filter android:label="@string/filter_view_example_gizmos"> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <!-- Accepts URIs that begin with "example://gizmos” --> <data android:scheme="example" android:host="gizmos" /> </intent-filter> </activity>
- Android App Links 是一种特殊的 Deep links,要求链接必须是你自己网站的 HTTP URL 链接,系统版本至少是 Android 6.0 (API level 23),优点是安全且具体,其他应用不能使用你的链接,不过你得先 验证你的链接,由于链接和网站链接一致所以可以无缝地在应用和网站间切换,可以支持 Instant App,可以通过浏览器、谷歌搜索 APP、系统屏幕搜索、甚至 Google Assistant 的链接直接跳转到应用。验证链接的流程为: 将 标签的 android:autoVerify 设置为 true 以告诉系统自动验证你的应用属于这个 HTTP URL 域名 → 填写好网站域名和应用 ID 并使用签名文件生成 Digital Asset Links JSON 文件 → 将文件上传到服务器,访问路径为 https://domain.name/.well-known/assetlinks.json ,响应格式为 application/json,子域名也需要存在对应的文件,一个域名可以关联多个应用,一个应用也可以关联多个域名,且可以使用相同的签名 → 利用编辑器插件完成关联并验证
- 使用 Intent Scheme URL 需要做过滤。如果浏览器支持 Intent Scheme Uri 语法,如果过滤不当,那么恶意用户可能通过浏览器 js 代码进行一些恶意行为,比如盗取 cookie 等。所以如果使用了 Intent#parseUri() 方法,获取的 intent 必须严格过滤,intent 至少包含 addCategory(“android.intent.category.BROWSABLE”),setComponent(null),setSelector(null) 3 个策略
- 开放的 Activity/Service/BroadcastReceiver 等需要对传入的 intent 做合法性校验
尾声
原文篇幅过长,一节一节的发布…唉,android好难,早知就不会android了。哈哈~
本文暂时没有评论,来添加一个吧(●'◡'●)