Android 11 Settings源码入门,食堂大妈看完都学会了

第一步

首先通过getMetaData()获取该Activity在manifest中配置的fragment, 并赋值给mFragmentClass

public static final String META_DATA_KEY_FRAGMENT_CLASS = “com.android.settings.FRAGMENT_CLASS”;

private void getMetaData() {
try {
ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
PackageManager.GET_META_DATA);
if (ai == null || ai.metaData == null) return;
mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
} catch (NameNotFoundException nnfe) {
// No recovery
Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
}
}

那么manifest中是怎么配置的呢?如下:

由此可知WifiInfoActivity这个Acitivity对应的fragment是:com.android.settings.wifi.WifiInfo

第二步

通过getIntent()构造包含EXTRA_SHOW_FRAGMENT的intent

public Intent getIntent() {
Intent superIntent = super.getIntent();
String startingFragment = getStartingFragmentClass(superIntent);
// This is called from super.onCreate, isMultiPane() is not yet reliable
// Do not use onIsHidingHeaders either, which relies itself on this method
if (startingFragment != null) {
Intent modIntent = new Intent(superIntent);
modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
Bundle args = superIntent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
if (args != null) {
args = new Bundle(args);
} else {
args = new Bundle();
}
args.putParcelable(“intent”, superIntent);
modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
return modIntent;
}
return superIntent;
}

/**

  • Checks if the component name in the intent is different from the Settings class and
  • returns the class name to load as a fragment.
    */
    private String getStartingFragmentClass(Intent intent) {
    // 存在mFragmentClass则直接返回
    if (mFragmentClass != null) return mFragmentClass;

String intentClass = intent.getComponent().getClassName();
if (intentClass.equals(getClass().getName())) return null;

if (“com.android.settings.RunningServices”.equals(intentClass)
|| “com.android.settings.applications.StorageUse”.equals(intentClass)) {
// Old names of manage apps.
intentClass = ManageApplications.class.getName();
}

return intentClass;
}

这里包含了mFragmentClass为空的情况,暂时先不管。

第三步

通过launchSettingFragment()启动对应Fragment,这里的initialFragmentName参数就是第二步Intent中包含的EXTRA_SHOW_FRAGMENT参数,mFragmentClass不为空的情况下传入的就是mFragmentClass

void launchSettingFragment(String initialFragmentName, Intent intent) {
if (initialFragmentName != null) {
setTitleFromIntent(intent);

Bundle initialArguments = intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
switchToFragment(initialFragmentName, initialArguments, true,
mInitialTitleResId, mInitialTitle);
} else {
// Show search icon as up affordance if we are displaying the main Dashboard
mInitialTitleResId = R.string.dashboard_title;
switchToFragment(TopLevelSettings.class.getName(), null /* args */, false,
mInitialTitleResId, mInitialTitle);
}
}

switchToFragment()中将fragment添加到activity中。

/**

  • Switch to a specific Fragment with taking care of validation, Title and BackStack
    */
    private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
    int titleResId, CharSequence title) {
    Log.d(LOG_TAG, "Switching to fragment " + fragmentName);
    if (validate && !isValidFragment(fragmentName)) {
    throw new IllegalArgumentException("Invalid fragment for this activity: "
  • fragmentName);
    }
    // 反射创建fragment
    Fragment f = Utils.getTargetFragment(this, fragmentName, args);
    FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
    transaction.replace(R.id.main_content, f);
    if (titleResId > 0) {
    transaction.setBreadCrumbTitle(titleResId);
    } else if (title != null) {
    transaction.setBreadCrumbTitle(title);
    }
    // 提交事务
    transaction.commitAllowingStateLoss();
    getSupportFragmentManager().executePendingTransactions();
    Log.d(LOG_TAG, “Executed frag manager pendingTransactions”);
    return f;
    }
三,数据控制

在首页-电池设置项中可以显示实时电量。那么它是如果实现的呢?

首先看下它是如何配置的:

配置项中配置了TopLevelBatteryPreferenceController控制器,它继承自AbstractPreferenceController,这个抽象类用于对所有菜单项进行统一管理(例如展示或隐藏,监听点击事件等)。

TopLevelBatteryPreferenceController代码如下:

public class TopLevelBatteryPreferenceController extends BasePreferenceController implements
LifecycleObserver, OnStart, OnStop {

// 电量改变广播
private final BatteryBroadcastReceiver mBatteryBroadcastReceiver;
// 当前配置项
private Preference mPreference;
// 电量信息
private BatteryInfo mBatteryInfo;

public TopLevelBatteryPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
mBatteryBroadcastReceiver = new BatteryBroadcastReceiver(mContext);
mBatteryBroadcastReceiver.setBatteryChangedListener(type -> {
BatteryInfo.getBatteryInfo(mContext, info -> {
mBatteryInfo = info;
updateState(mPreference);
}, true /* shortString */);
});
}

// 控制该项是否可用
@Override
public int getAvailabilityStatus() {
return mContext.getResources().getBoolean(R.bool.config_show_top_level_battery)
? AVAILABLE : UNSUPPORTED_ON_DEVICE;
}

@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
// 获取当前的配置项
mPreference = screen.findPreference(getPreferenceKey());
}

@Override
public void onStart() {
// 注册广播
mBatteryBroadcastReceiver.register();
}

@Override
public void onStop() {
// 取消注册广播
mBatteryBroadcastReceiver.unRegister();
}

@Override
public CharSequence getSummary() {
// 返回电量概览
return getDashboardLabel(mContext, mBatteryInfo);
}

// 获取电量信息
static CharSequence getDashboardLabel(Context context, BatteryInfo info) {
if (info == null || context == null) {
return null;
}
CharSequence label;
if (!info.discharging && info.chargeLabel != null) {
label = info.chargeLabel;
} else if (info.remainingLabel == null) {
label = info.batteryPercentString;
} else {
label = context.getString(R.string.power_remaining_settings_home_page,
info.batteryPercentString,
info.remainingLabel);
}
return label;
}
}
代码比较简单:

  1. 在构造方法中初始化电量改变广播
  2. onStart()onStop()中注册和取消注册广播
  3. 一旦收到电量改变广播,则把电量信息保存在mBatteryInfo
  4. 然后执行updateState(),该方法会调用getSummary()把信息设置给当前配置项
  5. getSummary()中将mBatteryInfo保存的电量信息解析出来
小结:

菜单项的展示、隐藏、监听点击事件等都是通过继承自AbstractPreferenceController的控制器完成,这个控制器有时候实在xml中配置,有些时候则是在fragment中动态添加。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

最后

下面是辛苦给大家整理的学习路线

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

的圈子,让我们一起学习成长!**](https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0)

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

  • 30
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值