文章目录
Android基础入门教程
安卓面试基础(如会必过)
1. Android的事件处理机制
1.1 基于监听的事件处理机制
由事件源、事件和事件监听器完成,有几种实现方式:
使用匿名内部类
使用内部类
使用外部类
1.2 基于回调的事件处理机制
2. 应用的pid uid 包名 进程名
pid
PID:process identifier,进程id,PID是各个进程的身份标识,具有唯一性,不同进程的PID互相不同。程序已启动,系统将会为进程分配一个独一无二的PID,进程终止后PID会被系统回收,可能会被继续分配给新的进程,但是在android系统中一般不会把已经kill掉的进程ID重新分配给新的进程,新产生的进程ID一般比产生之前的所有进程ID都要大。
安卓中,同一个应用中的不同组件也可以通过设置具有不同的PID,只需要如下方式声明即可:
但是此种方式会隐形增加应用内组件之间通信的复杂性,不建议使用。
获取pid:
int pid = android.os.Process.myPid();
int pid = getCallingPid();
uid
UID:user identifier,用户ID。linux系统属于多用户系统,UID在linux中就是用户的ID,表明是哪个用户运行了这个进程,主要用于权限管理。而android系统属于单用户系统,此时UID的功能主要是实现数据共享。为了实现数据共享,android为每个应用分配了不同的UID。开发者可以通过设置使不同应用共享一个UID,相同UID的应用程序之间可以实现数据共享。
uid用于标识一个应用程序,在应用安装时被分配,并且在应用存在于手机期间,都不会改变。
一个应用程序只能有一个uid,多个应用可以使用sharedUserId 方式共享同一个uid,前提是这些应用的签名要相同。
获取uid:
int uid = android.os.Process.myUid();
int uid = getCallingUid();
在android中通过UID实现数据共享需要在共享程序a,b中的AM文件配置如下参数即可:
android:sharedUserId=“com.xxx.xxx”
注意,为保护共享数据的安全性,防止被开发者通过设置共享UID的方式恶意访问,android还要求共享数据的应用之间需要具有相同的签名,每个公司或者开发者的签名是唯一的,不容易被暴露,因此保证了共享数据的安全。
uid如何分配和uid的价值
完全暴露:通过在AM的组件(活动、服务、内容提供器)声明中设置exported=true。
权限提示暴露:通过在AM的组件(活动、服务、内容提供器)声明中设置访问权限要求。
私有暴露:通过sharedUserId+同一套签名的方式实现。
包名 进程名
Android应用开发中的进程名,包名,applicationId
包名是安卓应用apk的唯一标识,安卓系统管理应用时,也是以包名索引。
包名在AndroidManifest.xml中定义。在一个安卓应用程序工程创建之初,包名就已经确定。
包名更多的是一个编译时概念。由android IDE自动生成的类,比如R、BuildConfig,都是以AndroidManifest.xml中定义的包名来构造类名,譬如,包名为“com.android.helloworld”,对应生成“com.android.helloworld.R”“com.android.helloworld.BuildConfig”。
在需要使用的类中,需要imort,import语句也将保留在源代码中。
android应用在被启动时,对应的进程的进程名一般就是包名。
android应用一般是运行在自己的进程中,除非通过AndroidManifest.xml中application定义中通过android:process字段指定运行在其他进程中(这种机制有特殊限定条件)。
applicationId是Android Studio提供的一种新的机制,可以在app/build.gradle中定义,用于设置应用的包名。
Android Studio默认生成的app/build.gradle中,applicationId与AndroidManifest.xml中定义的包名一致。
但显然,这两个值是可以独立设置的,可以不同。
注意,在修改applicationId后,前面提到的IDE自动生成的类(譬如R、BuildConfig),包名不会被更新,仍然是AndroidManifest.xml中定义的包名,使用他们的类中的import语句也不需要更改。
但在修改applicationId之后,查看运行时进程名,会变成新的applicationId。
在程序内运行时调用Context.getPackageName(),得到的仍然是新的applicationId。
怀疑实现方式是build过程中,擦除并更新了AndroidManifest.xml中的package字段。
使用apktool对编译生成的apk反编译,查看AndroidManifest.xml,发现确实是修改成了applicationId的值,验证了上述假设。
如上分析,对于applicationId的理解已经清晰。
对于一个大的工程,修改包名的成本过高,几乎不可取。
而经常会出现一个工程需要出不同版本,譬如收费版和免费版。
这样就可以通过简单修改app/build.gradle中的applicationId来改变最终apk的包名,这样对于安卓手机来说,就是不同的app;
同时,又不影响到开发编译时的代码一致性。
最后,说一个这种机制的坑。对于上文提到的自动生成的Java类(譬如R、BuildConfig),如果不是通过正常的import,而是通过在运行时调用Context.getPackageName()得到包名拼接成类名再用反射获取类,这样就会遇到ClassNotFoundException。需注意尽量不要用这种方式。反射虽然能让人体会到超越Java数据封装规则之上的快感,但是也有各种暗礁等着。
3. 权限
参考:Android 权限(一):权限大全
Android源码中所有权限定义的文件为:frameworks/base/core/res/AndroidManifest.xml
权限的作用
Android 中应用权限有助于保护对以下数据和操作的访问/执行权限,从而为保护用户隐私提供支持:
- 受限数据,例如系统状态和用户的联系信息
- 受限操作,例如连接到已配对的设备并录制音频
权限的类别
-
安装时权限(install权限):安卓系统在应用安装时就会根据应用申请的权限,决策授予或者拒绝授予该应用此权限。在某些低版本的安卓SDK(<23)上,在应用安装时会让用户选择是否授予应用安装时权限,如果不授予,应用无法安装。最新的安卓SDK取消了安装时权限对用户的提醒,自动决策是否授予某些安装时权限。normal和signature级别的权限都是安装时权限。不会给用户提示界面,系统自动决定权限的赋予或拒绝。
-
运行时权限(动态权限):运行时权限也称为危险(dangerous)权限,此类权限授予应用对受限数据的额外访问权限,或允许应用执行对系统和其他应用具有更严重影响的受限操作。因此,您需要先在应用中请求运行时权限然后才能访问受限数据或执行受限操作。保护级别为dangerous的权限为运行时权限,需要应用在运行过程中动态申请该权限,由用户在弹窗中决策是否授予应用这些权限。
-
特殊权限:特殊权限与特定的应用操作相对应。只有平台和原始设备制造商 (OEM) 可以定义特殊权限。此外,如果平台和 OEM 想要防止有人执行功能特别强大的操作(例如通过其他应用绘图),通常会定义特殊权限。系统设置中的特殊应用访问权限页面包含一组用户可切换的操作。其中的许多操作都是以特殊权限的形式实现的。特殊权限旨在限制访问尤其敏感或与用户隐私没有直接关系的系统资源。这些权限不同于安装时权限和运行时权限。声明特殊权限的应用会显示在系统设置中的特殊应用权限页面内(图 1)。如需向应用授予特殊权限,用户必须转到此页面:设置 > 应用 > 特殊应用权限。与运行时权限不同,用户必须从系统设置中的特殊应用权限页面授予特殊权限。应用可以使用 intent 将用户转到该页面,这会暂停应用,并启动相应的设置页面,以便用户授予指定的特殊权限。用户返回到应用后,应用可以在 onResume() 函数中检查是否已获得相应权限。
权限的保护级别
- normal普通权限:默认值。具有较低风险的权限,此类权限允许请求授权的应用访问隔离的应用级功能,对其他应用、系统或用户的风险非常小。 系统会自动向在安装时请求授权的应用授予此类权限,无需征得用户的明确许可。
- signature签名权限:只有在请求授权的应用使用与声明权限的应用相同的证书进行签名时系统才会授予的权限。如果证书匹配,则系统会在不通知用户或征得用户明确许可的情况下自动授予权限。只有当应用A 与 定义权限的应用B 或 OS 使用相同的证书签名时,系统才会向应用授予签名权限
- dangerous:具有较高风险的权限,此类权限允许请求授权的应用访问用户私人数据或获取可对用户造成不利影响的设备控制权。由于此类权限会带来潜在风险,因此系统可能不会自动向请求授权的应用授予此类权限。例如,应用请求的任何危险权限都可能会向用户显示并且获得确认才会继续执行操作,或者系统会采取一些其他方法来避免用户自动允许使用此类功能。
- systemOrSignature:系统仅向位于 Android 系统映像的专用文件夹中的应用 或 使用与声明权限的应用相同的证书进行签名的应用授予的权限。不要使用此选项,因为 signature 保护级别应足以满足大多数需求,无论应用安装在何处,该保护级别都能正常发挥作用。
权限的操作
权限的声明:在AM文件中通过如下标签定义权限:
<permission
android:name="XXXX"
android:label="@string/my_permission_label"
android:description="@string/my_permission_desc"
android:protectionLevel="normal" />
- 权限名称:权限的标识字符串。
- 权限包名:拥有这个权限的包名。
- 权限标签:权限的简介。
- 权限描述:权限的具体描述,通常描述该权限用于执行哪些动作。
- 保护级别:决定了该权限能否、将以何种方式授予申请该权限的应用。
权限的使用:在AM文件中通过 uses-permission android:name=“XXXXX” /> 声明权限使用
权限的查看: - 查看某应用的信息,其中包含了该应用的权限信息:dumpsys package pkgName
- 查看某应用是否被授予某权限:dumpsys package check-permission pkgName permissionName,0表示授予,-1表示未授予
- 代码通过pid和uid检查是否具有权限:context.checkPermission(MY_PERMISSION_NAME, pid, uid) == PackageManager.PERMISSION_GRANTED
4. 签名
5. queries标签
作用:管理软件包可见性。
参考:Manifest queries标签 / targetAPI动态获取 / 手机 /包可见性
参考:android queries属性
6. meta-data标签
参考:Android中AndroidManifests.xml 之meta-data
参考:android meta-data
作用
用于在安卓应用开发中,定义应用程序(application标签),或者应用的组件(活动、服务、广播接收器)的额外元数据信息,这些信息之后可以在程序中通过packageManager特定的接口获得。meta-data是以键值对的形式配置到应用的AM文件中的。
使用
所有的meta-data键值对在程序中将被包装成Bundle供组件使用,因此使用方式同Bundle。
在代码中获取元数据的接口有:metaData.getString()、getInt()、getBoolean()、getFloat()等。
metadata普通值由value属性给出,资源ID由resource属性给出。
meta-data标签包括的基本信息如下:
name:数据项的唯一名称。 为了确保名称的唯一性,可使用 Java 风格的命名规则 — 如“com.example.project.activity.fred”。
resource(可选):如果元数据的值是定义在资源文件中,则需要通过resourse属性给定,赋值为资源ID,之后在程序中通过name获得的元数据的value也是资源ID。
value(可选):普通类型的值可以通过value属性给定。