AndroidManifest.xml深度解析
核心作用
复制代码
- AndroidManifest.xml是每个Android应用的全局配置文件,相当于应用的"身份证"和"使用说明",该文件向Android系统提供了关于应用的基本信息,是Android系统与应用程序之间进行通信的桥梁。
主要承担一下关键作用:
- 应用身份的标识:定义包名和应用ID
- 组件注册中心:声明四大组件
- 权限管理系统:声明所需权限和权限限制
- 设备兼容性:指定硬件/软件需求
- 应用特性配置:主题、图标等元数据
关键元素
复制代码
<manifest> 根元素
- package:应用的唯一包名
- versionCode:内部版本号,用于版本比较(整数)
- versionName:用户可见的版本号(字符串)
- installLocation:
- auto:由系统决定
- internalOnly:只安装到内部存储
- preferExternal:优先外部存储
<uses-sdk>: SDK版本要求
- android:minSdkVersion:最小版本
- android:targetSdkVersion:目标版本
- android:maxSdkVersion:最大版本
<uses-permission>: 权限声明
- android:name: 权限名称
<permission>:自定义权限 声明自定义权限,用于保护应用组件。必须在 <application> 外部声明。
<application>:应用配置
- icon:应用图标。
- label:应用名称。
- theme:应用主题。
- allowBackup:是否允许备份(默认为 true)
- supportsRtl:是否支持从右到左布局
- usesCleartextTraffic:是否允许明文传输(HTTP)
<activity>:活动组件
- android:name: 指定 Activity 对应的类名(相对、绝对路径),关联 XML 配置与具体的 Java/Kotlin 实现类
- 相对路径形式:.MainActivity(相对于 manifest 标签中的 package 属性)
- 绝对路径形式:com.bytedance.speech.speechdemo.MainActivity(完整的类路径)
- 两种写法功能相同,都会指向同一个 MainActivity 类
- android:theme: 指定 Activity 使用的主题样式,定义界面外观、动画等视觉效果
- android:label: 设置 Activity 的显示标签,定义在 Launcher 或任务管理器中显示的名称
- exported:是否允许其他应用启动(Android 12+ 重要安全特性)
-android:launchMode: 定义 Activity 的启动模式,控制 Activity 实例的创建和复用策略
- standard:标准模式(默认)
- singleTop:栈顶复用
- singleTask:栈内复用
- singleInstance:独立栈
- android:screenOrientation: 用于控制 Activity 的屏幕显示方向,常用取值包括:
- portrait: 竖屏模式
- landscape: 横屏模式
- unspecified: 默认值,由系统决定方向
- sensor: 根据设备传感器自动旋转
- nosensor: 不响应传感器变化,保持初始方向
- user: 用户当前首选的方向
- behind: 与前一个 Activity 方向保持一致
- reversePortrait: 反向竖屏(倒置)
- reverseLandscape: 反向横屏(倒置)
- sensorPortrait: 仅支持竖屏但可感应正反向
- sensorLandscape: 仅支持横屏但可感应正反向
- fullSensor: 支持所有四个方向的自由旋转
- android:windowSoftInputMode: 控制软键盘与窗口的交互模式,管理键盘弹出时的窗口调整行为
示例:stateHidden 表示默认隐藏输入法
- android:configChanges: 指定当设备配置发生改变时,Activity是否自己处理这些变化,而不是让系统重新创建Activity。
示例:orientation|screenSize 表示自行处理屏幕旋转
- <intent-filter>: 定义 Activity 能够响应的 Intent 类型
- android.intent.action.MAIN: 声明该 Activity 是应用程序的主入口点
- android.intent.category.LAUNCHER: 指定该 Activity在应用启动器中显示(即桌面图标)
<server>:服务组件
<receiver>:广播接收器
<provider>:内容提供者
<uses-feature>:硬件特性要求 声明应用需要的硬件特性(如摄像头、蓝牙),Google Play会根据此过滤设备。
<meat-data>: 元数据 常用于第三方库配置或跨平台框架集成。
Activity 组件
1. 介绍
复制代码
- Activity是一个应用程序组件,提供一个屏幕,用户可以用来交互为了完成某项任务
- Activity中所有操作都与用户密切相关,是一个负责与用户交互的组件,可以通过setContentView(View)来显示指定控件
- 在Android应用中,一个Activity通常就是一个独立的屏幕,它上面可以显示一些控件也可以监听并处理用户的事件做出响应。Activity之间通过Intent进行通信。
2. 生命周期
复制代码
- Active/Running
一个新的Activity启动入栈后,它显示在屏幕最前端,处于栈的最顶端(Activity栈顶), 此时他处于可见并可和用户交互的激活状态,叫做活动状态或者运行状态。
- Paused
当Activity失去焦点,被一个新的非全屏的Activity或者一个透明的Activity被放置在栈顶,此时的状态叫做暂停状态。可见单不可与用户进行交互。
- Stopped
当一个Activity被另外一个Activity完全覆盖,叫做停止状态。依然保持所有状态和成员信息,窗口不可见
- Killed
3. API接口操作
复制代码
- 无返回值
Intent intent = new Intent(MainActivity.this, MainActivity2.class);
startActivity(intent);
- 有返回值
1. 旧接口
- 启动Activity
Intent intent = new Intent(MainActivity.this, MainActivity2.class);
startActivityForResult(intent, 100);
- 处理返回数据(在onCreate 外)
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Log.i(LogTag, "旧的处理接口 onActivityResult: " + requestCode +resultCode + data.getStringExtra("key1"));
}
2. 新接口
- 创建一个ActivityResultLauncher 实例
private ActivityResultLauncher<Intent> resultLauncher;
- 注册回调(在 onCreate中注册) 并处理返回的数据
resultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<ActivityResult>() {
@Override
public void onActivityResult(ActivityResult result) {
Intent data = result.getData();
assert data != null;
Log.i(LogTag, "新的处理接口" + data.getStringExtra("key1"));
}
});
- 启动Activity
Intent intent = new Intent(MainActivity.this, MainActivity2.class);
resultLauncher.launch(intent);
Server 组件
1. 介绍
复制代码
是一种长周期的,没有可视化界面,运行与后台的一种服务程序。
2. 启动方式
复制代码
- Start Service被开启的service通过其他组件调用startService()被创建,这种service可以无限的运行下去,必须调用stopSelf()方法或者其他组件调用stopService()方法来停止,当service被停止时,系统会销毁它。
- Bounded Service被绑定的service是当其他组件(一个客户)调用bingService()来创建的,客户可以通过一个IBinder接口和service进行通信,客户可以通过unbindService()方法来关闭这种连接,一个service可以同时
和多个客户绑定,当多个客户都解除绑定之后,系统会销毁service
BroadcastReceiver
1. 介绍
复制代码
- 用于监听/接收应用发出的广播消息,并做出回应。
- 应用场景:不同组件之间通信(包括应用内/不同应用之间); 与Android系统特定情况下的通信(如当电话呼入时、网络可用时); 多线程通信
- 分为两个角色: 广播发送者、广播接收者
2. 实现原理
复制代码
- 使用了设计模式中的观察者模式:基于消息的发布/订阅事件模型
- 模型中有3个角色
- 消息订阅者(广播接收者)/消息发布者(广播发送者)/消息中心(AMS, 即Activity Manager Service)
- 广播接收者通过Binder 机制在AMS注册
- 广播发送者通过Binder 机制向AMS发送广播
- AMS根据广播发送者要求,在已注册列表中,寻找合适的广播接收者(寻找依据: IntentFilter/Permission)
- AMS将广播发送到合适的广播接收者相应的消息循环队列中
注册方式
复制代码
- 静态注册
- 在AndroidManifest.xml里通过标签声明,当此APP首次启动时,系统会自动实例化mBroadcastReceiver类,并注册到系统中,静态注册是常驻广播,不受任何组件声明周期的影响
- 动态注册
- 在代码中通过调用 Context的 registerReceiver()方法进行动态注册BroadcaseReceiver, 注册广播后,要在相应位置记得销毁广播,即在onPause()中unregisterReceiver(xxx)。
- 注意:
- 动态广播最好在Activity的onResume()注册、onPause()销毁。
- 原因:动态广播有注册就得有销毁,否则会导致内存泄漏。
- 动态注册是非常驻广播, 灵活,跟随组件的生命周期变化。
Content Provider (SqLite3)
1. 介绍
复制代码
- 作为应用程序之间唯一的共享数据的途径,Content Provider 为存储和读取数据提供统一的接口。
- Content Provider 主要功能就是存储并检索数据以及向其他应用程序提供访问数据接口。
- 让自己的数据和其他应用程序共享有两种方式
- 创建自己的Content Provider(即继承ContentProvider的子类)将自己的数据添加到已有的Content Provider中去。
- 后者需要保证现有的Content Provider 和自己的数据类型相同且具有该Content Provider的写入权限。
2. 核心三要素
复制代码
- URI(数据地址)
- 格式: content://<authority>/<path>/<id>
- prefix: 固定协议头 始终设置为content://
- authority: 唯一标识(通常是包名.provider)。
- path: 数据集合(表名)。
- id: (可选):单条记录 ID
- 示例
- 所有用户: content://com.demo.app.provider/users
- 指定用户:content://com.demo.app.provider/users/5
- ContentProvider(发送端)
- 继承ContentProvider类
- 重写5个核心方法
- 在Androidmanifest.xml 注册
<provider
android:name=".MyProvider"
android:authorities="com.demo.app.provider"
android:exported="true" <!-- 是否允许外部应用访问 -->
android:permission="com.demo.app.permission.USER" /> <!-- 自定义权限 -->
<!-- 可选:声明自定义权限 -->
<permission
android:name="com.demo.app.permission.USER"
android:protectionLevel="normal" />
- ContentResolver(接收端)
- 在Service里面用 getContetnResolver()
- 调用CURD操作
- 声明权限
<uses-permission android:name="com.demo.app.permission.USER" /> <!-- 访问服务端需要的权限 -->
- 整体设计思路(核心原则)
1. 职责绝对分开(最重要)
· SQLiteOpenHelper: 只管数据库创建,升级,表结构
· ContentProvider: 只做路由,权限,对外接口
· ContentResolver: 只做外部调用,不关心内部实现
· 常量统一管理: 不要到处写字符串,避免写错导致崩溃
2. 结构分层
· Constant类: 统一放URI,表名,字段名
· DBHelper: 数据库的创建,升级
· MyPorvider: 路由匹配 + 增删改查转发
· 调用: 通过ContentResolver操作
Handler(线程消息机制)
1.介绍
复制代码
- Handler是线程之间发送消息的工具
- 最核心作用:子线程(耗时)做完事后,通知主线程更新UI
- 主线程才能更新UI,子线程不能操作,否则崩溃
2. 核心组件
复制代码
- Handler: 发消息 + 处理消息
- sendMessage()
- handleMessage()
- Message: 要传递的消息(int/object)
- what: int类型,用来区分消息类型
- arg1,arg2: 传递简单数字
- obj: 传递任意对象
- MessageQueue: 消息队列(排队)
- Looper: 循环取消息
3. 子线程必须要创建Loop 否则new 的时候会崩溃
复制代码
- Looper.prepare(); //给当前线程创建 Looper + MessageQueue
- Handler = new Handler(Looper.mylooper); // 创建 Handler,并绑定当前子线程的 Looper
- Looper.loop(); //开启无限循环,从队列取消息、执行消息
4. 总结
Binder(进程消息机制)
1. 介绍
复制代码
- Binder 就是Android里专门用来跨进程通信的工具
- 作用只有一个:
- 让A应用(客户端) 能够调用B应用(服务端)里面的方法(数据)
2. 实现流程
复制代码
- 服务端:写Service类, 创建Binder子类,暴露方法
- 服务端:在onBinder()返回Binder
- 服务端:AndroidManifest.xml 添加service
<service
android:name=".MyService"
android:exported="true"
android:enabled="true"
android:process=":bindserver" >
<intent-filter>
<action android:name="android.intent.action.server.student" /> //字符串自定义
</intent-filter>
</service>
- 客户端:绑定服务
- Intent intent = new Intent(action_str);
- intent.setPackage(报名+service服务类名);
- bindService(xxx);
- 客户端:拿到binder,往服务端发送数据
- binder.transact(REQUEST_CODE, data, reply, 0);
NDK
1. 函数签名
复制代码
- 查看前面方法
javap -s 报名.类名
- 签名类型
Z --> boolean
B --> byte
C --> char
S --> short
I --> int
J --> long
F --> float
D --> double
[ type --> 类型的数组 type[]
L class; --> 对应类参数
2. 反射
复制代码
- java层
- 获取类
Class<?> clazz = Class.forName("com.example.MyClass"); // 1. 全类名加载
Class<?> clazz = MyClass.class; // 2. 直接获取
Class<?> clazz = 对象.getClass(); // 3. 通过对象实例获取
- 获取属性和方法
Method[] methods = clazz.getDeclaredMethods(); // 获取所有方法(含私有)
Constructor[] cons = clazz.getDeclaredConstructors(); // 获取所有构造方法
Method method = clazz.getMethod("方法名", 参数类型...); // 获取单个方法
Method method = clazz.getDeclaredMethod("方法名", 参数类型...);
Field field = clazz.getDeclaredField("字段名"); // 获取属性
Field[] fields = clazz.getDeclaredFields();
- 调用方法
method.invoke(实例对象, 参数...);
- ndk层
- 获取类
jclass clazz = env->GetObjectClass(实例对象); // 1. 通过对象实例获取类
jclass clazz = env->FindClass("com/example/MyClass"); // 2. 通过全类名直接查找类
- 获取方法ID
jmethodID mid = env->GetMethodID(jclass, "方法名", "方法签名"); // 普通方法
jmethodID mid = env->GetStaticMethodID(jclass, "方法名", "方法签名"); // 静态方法
jmethodID mid = env->GetMethodID(jclass, "<init>", "构造方法签名"); // 构造方法(固定方法名 <init>)
- 调用方法
env->CallVoidMethod(实例, mid, 参数...); // 无返回值
env->CallStaticVoidMethod(jclass, mid, 参数...);
// 返回对象
jobject obj = env->CallObjectMethod(实例, mid, 参数...); // 返回对象
jobject obj = env->CallStaticObjectMethod(jclass, mid, 参数...);
jint num = env->CallIntMethod(实例, mid, 参数...); // 返回 int/boolean 等
jboolean b = env->CallBooleanMethod(实例, mid, 参数...);
- 获取和设置成员变量
jfieldID fid = env->GetFieldID(jclass, "字段名", "字段签名"); // 1. 获取字段 ID
jfieldID fid = env->GetStaticFieldID(jclass, "字段名", "字段签名");
jobject obj = env->GetObjectField(实例, fid); // 2. 获取字段值
jint num = env->GetIntField(实例, fid);
jboolean b = env->GetBooleanField(实例, fid);
env->SetObjectField(实例, fid, 值); // 3. 设置字段值
env->SetIntField(实例, fid, 值);
env->SetBooleanField(实例, fid, 值);