第30章:Android Framework实战项目
说实话,到了这个阶段,你已经把Framework的理论啃得差不多了。但光有理论,就像只会背菜谱的厨师——真让你炒个菜,可能连火候都掌握不好。这一章,我们就来点真格的。
我会带你走一遍完整的实战流程:从自定义系统服务开始,到系统级应用开发,再到Bug修复和日志分析,最后聊聊CTS兼容性测试。这些内容,都是我这些年踩坑踩出来的经验。
30.1 自定义系统服务开发
为什么要自己写系统服务?说白了,有些功能需要全局访问权限,或者需要在系统启动时就运行。比如,你想做一个系统级的屏幕时间管理功能,普通App根本做不到。
30.1.1 服务接口定义
先定义AIDL接口。这是系统服务的“门面”。
// IMyCustomService.aidl
package com.example.framework;
interface IMyCustomService {
String getServiceName();
void registerCallback(IMyCustomCallback callback);
void unregisterCallback(IMyCustomCallback callback);
int getServiceStatus();
}
我个人习惯把回调接口单独放一个文件:
// IMyCustomCallback.aidl
package com.example.framework;
oneway interface IMyCustomCallback {
void onStatusChanged(int newStatus);
}
30.1.2 服务实现类
接下来是核心实现。这里要注意线程安全,因为系统服务可能被多个进程同时调用。
public class MyCustomService extends IMyCustomService.Stub {
private static final String TAG = "MyCustomService";
private final Context mContext;
private final RemoteCallbackList<IMyCustomCallback> mCallbacks
= new RemoteCallbackList<>();
private int mServiceStatus = 0;
public MyCustomService(Context context) {
mContext = context;
}
@Override
public String getServiceName() {
return "MyCustomService";
}
@Override
public void registerCallback(IMyCustomCallback callback) {
mCallbacks.register(callback);
}
@Override
public void unregisterCallback(IMyCustomCallback callback) {
mCallbacks.unregister(callback);
}
@Override
public int getServiceStatus() {
return mServiceStatus;
}
private void notifyStatusChanged(int newStatus) {
mServiceStatus = newStatus;
int count = mCallbacks.beginBroadcast();
for (int i = 0; i < count; i++) {
try {
mCallbacks.getBroadcastItem(i).onStatusChanged(newStatus);
} catch (RemoteException e) {
Log.e(TAG, "Callback error", e);
}
}
mCallbacks.finishBroadcast();
}
}
30.1.3 注册到SystemServer
服务写好了,得让系统知道它的存在。在SystemServer.java中添加:
private void startMyCustomService() {
try {
ServiceManager.addService("my_custom_service", new MyCustomService(this));
Log.i(TAG, "MyCustomService registered");
} catch (Throwable e) {
Log.e(TAG, "Failed to register MyCustomService", e);
}
}
然后在startOtherServices()方法中调用它。嗯,这里要注意启动顺序——如果你的服务依赖其他服务,一定要等它们初始化完成。
30.2 系统级应用开发
系统级应用和普通App最大的区别是什么?权限。系统应用可以调用很多普通App碰不到的API。比如修改系统设置、控制状态栏、管理通知等。
30.2.1 Settings应用扩展
假设我们要在设置里加一个“开发者选项”的入口。先写一个Fragment:
public class CustomDeveloperSettingsFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_custom_developer, container, false);
// 初始化UI
return view;
}
private void initViews(View view) {
SwitchPreference enableFeature = view.findViewById(R.id.enable_feature);
enableFeature.setOnPreferenceChangeListener((preference, newValue) -> {
boolean enabled = (boolean) newValue;
// 调用系统服务
IBinder binder = ServiceManager.getService("my_custom_service");
IMyCustomService service = IMyCustomService.Stub.asInterface(binder);
try {
service.setFeatureEnabled(enabled);
} catch (RemoteException e) {
Log.e(TAG, "Failed to set feature", e);
}
return true;
});
}
}
30.2.2 SystemUI定制
SystemUI是Android的“脸面”。状态栏、通知栏、导航栏都在这里。我做过一个项目,需要在状态栏显示自定义图标,表示当前网络加密状态。
核心思路是:在SystemUI的PhoneStatusBarPolicy中监听状态变化,然后更新图标。
public class CustomStatusBarPolicy {
private final Context mContext;
private final StatusBarIconController mIconController;
public CustomStatusBarPolicy(Context context) {
mContext = context;
mIconController = LocalServices.getService(StatusBarIconController.class);
}
public void updateCustomIcon(boolean active) {
StatusBarIcon icon = new StatusBarIcon(
"com.android.systemui",
null,
active ? R.drawable.ic_custom_active : R.drawable.ic_custom_inactive,
0,
0,
"custom_icon"
);
mIconController.setIcon("custom_icon", icon);
}
}
30.3 Framework层Bug修复与日志分析
这部分是Framework工程师的“吃饭本领”。不会看日志,不会定位Bug,那就别谈什么系统开发了。
30.3.1 常用日志工具
| 日志类型 | 命令 | 用途 |
|---|---|---|
| Main日志 | logcat -b main | 应用层日志 |
| 系统日志 | logcat -b system | 系统服务日志 |
| 事件日志 | logcat -b events | 系统事件(如Activity生命周期) |
| Crash日志 | logcat -b crash | 应用崩溃信息 |
| Kernel日志 | dmesg | 内核日志 |
30.3.2 实战:定位ANR问题
有一次,用户反馈系统经常卡死。我拿到日志后,先看traces.txt:
adb shell cat /data/anr/traces.txt
找到关键信息:
----- pid 1234 at 2024-01-15 10:30:00 -----
Cmd line: system_server
...
"main" prio=5 tid=1 Blocked
at com.example.framework.MyCustomService.getServiceStatus(MyCustomService.java:45)
- waiting to lock <0x0c71f240> (a java.lang.Object)
at com.android.server.am.ActivityManagerService.handleMessage(ActivityManagerService.java:2345)
...
看到没?主线程在等一个锁。我顺着锁的持有者找下去:
"Binder:1234_5" prio=5 tid=15 Blocked
at com.example.framework.MyCustomService.notifyStatusChanged(MyCustomService.java:78)
- waiting to lock <0x0c71f240> (a java.lang.Object)
held by thread "Binder:1234_3"
真相大白了:两个Binder线程互相等待,形成了死锁。修复方案很简单,把notifyStatusChanged中的同步块缩小范围即可。
30.4 兼容性测试与CTS
CTS(Compatibility Test Suite)是Google用来保证Android兼容性的“大杀器”。你的设备要过CTS,才能预装Google服务。说白了,这是Android生态的“入场券”。
30.4.1 CTS测试流程
- 下载CTS测试包:从Google官方获取对应Android版本的CTS
- 准备测试环境:设备刷入user版本固件,恢复出厂设置
- 运行测试:
./cts-tradefed run cts --plan CTS - 分析结果:查看test_result.xml中的fail项
30.4.2 常见CTS失败原因
我遇到过最坑的一次,CTS测试WiFi相关用例全部失败。排查了三天,最后发现是WiFi驱动返回的MAC地址格式不对——Google要求大写字母,驱动返回了小写。
| 失败类型 | 常见原因 | 解决方案 |
|---|---|---|
| 硬件相关 | 驱动实现不符合规范 | 修改HAL层实现 |
| 权限相关 | 系统应用缺少权限 | 检查AndroidManifest.xml |
| API行为 | 自定义ROM修改了API行为 | 回退修改或添加兼容层 |
| 性能相关 | 响应时间超出阈值 | 优化代码或调整阈值 |
30.4.3 自定义服务的CTS兼容性
如果你添加了自定义系统服务,要注意两点:
- 不要修改Google定义的API行为。比如,你不能让startActivity()的行为和标准不一样。
- 自定义服务不要影响系统服务的正常运行。我见过有人把SystemServer的启动时间拖长了,导致CTS的启动时间测试失败。
30.5 本章知识体系
下面这张图,是我对本章内容的总结。你可以把它当作一个“实战地图”。
好了,这一章的内容就到这里。记住,Framework开发没有捷径,多写代码、多分析日志、多踩坑,你才能真正成长。下次遇到问题,别急着问别人,先自己动手查日志、看源码——你会发现,很多问题其实没那么难。
公众号:蓝海资料掘金营,微信deep3321