2017/9/8 17:17:03
 

前言

    最近接到个需要优化Android原生系统设置APK的任务。这个任务里面有一个更换应用背景图片的需求。我手里的这个设备是一个平板设备,使用了一下这个原生设置APK,感觉它有点像是一个主Activity,通过更换Fragment的方式来切换不同的展示内容。这样一来就好办了,想着直接找到这个Activity,看看它是 set 了哪一个 layout 进去,然后再直接在这个 layout 中添加个背景图片就好了。但后来跟踪了一下源码,发现并没有这么简单。这个主Activity是继承自Android打包好的 PreferenceActivity 来实现的,而设置布局的工作,已经由这个父Activity完成了(如下图所示)。完全没有子Activity什么事,并且PreferenceActivity还没有暴露任何接口能让子类取得布局。而且像这种设置方式,想反射都不好反射。
./frameworks/base/core/java/android/preference/PreferenceActivity.java
 
    那这样可如何是好呢?如果能有一个 getContentView() 方法就好了。既然Android官方不提供,那我们干脆跟踪跟踪源码,看看能不能自己造一个出来。
 

开发环境

    操作系统:Android4.4.2
    硬件设备:智能音箱中的平板设备。
    编译需求:有完整源代码,能够正常编译大包。
 
 

追本溯源-跟踪源代码

    首先来看看Activity.java中的 setContentView() 是如何处理的。
./frameworks/base/core/java/android/app/Activity.java
 

原来与 Window 有关啊。再看看 getWindow() 中是如何提供 Window 对象的。

到这里,我们就知道了,Activity中使用的 Window 就是 PhoneWindow 类的对象了。那么,我们直接去 PhoneWindow 类中看看它的 setContentView() 作了什么操作。
 
./frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java
 

由上图所示代码第290行,我们知道了原来 set 进去的 layout 就是被加载到了这个 mContentParent 容器中去了啊。这个 mContentParent 是一个 ViewGroup 类的对象。那这个 mContentParent 又是怎么来的呢?
注意到上图第285行的条件判断语句里,似乎这个 mContentParent 对象还与 installDecor() 方法有关呢!去看看。
 

看来刚才猜想的没错,这个 mContentParent 确实和这个 Decor 有着很密切的关系。先去 generateDecor() 方法里看看这个 mDecor 是个什么来头。

 简单粗暴,我喜欢!!!DecorView是一个定义在 PhoneWindow 内部的内部类,它是 FrameLayout 的子类,如下图所示。

 
然后我们再去看看 mContentParent 是如何由 mDecor 生成的。追踪 generateLayout() 方法。
generateLayout() 方法代码量较大,我们不需要看懂每一行代码的含义,只看我们需要的就好。
protected ViewGroup generateLayout(DecorView decor) {
// 为后续的加载作前期准备工作,读取属性值,选择布局文件等。
// ...
mDecor.startChanging(); View in = mLayoutInflater.inflate(layoutResource, null);
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);// ID_ANDROID_CONTENT = com.android.internal.R.id.content;
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
} if (getContainer() == null) {
Drawable drawable = mBackgroundDrawable;
if (mBackgroundResource != 0) {
drawable = getContext().getResources().getDrawable(mBackgroundResource);
}
mDecor.setWindowBackground(drawable);
drawable = null;
if (mFrameResource != 0) {
drawable = getContext().getResources().getDrawable(mFrameResource);
}
mDecor.setWindowFrame(drawable);
} mDecor.finishChanging(); return contentParent;
}
 
由上述代码第7行可知,mDecor在这里添加了一个View,准确说,它添加的肯定是一个 ViewGroup 类对象。这次添加也是 mDecor 首次添加,即在 mDecor 的 child 中,它是第0个。
然后第9行的 ViewGroup contentParent 就是最终要返回的容器对象。这行代码后面的 findViewById() 方法是定义在 Window.java 中的方法。它其实就是通过 mDecor 来根据 ID 查找 View 。
./frameworks/base/core/java/android/view/Window.java
 

 
到这里,整个加载布局的流程就很清楚了。我们把它总结一下。

 
那么,借此设置流程图,创造 getContentView() 方法的步骤也清晰了。如下图所示:

这里有一个很重要的、也是关键的一点,就是Activity提供了 getWindow() 方法用于取得 Window 对象,且这个 Window 对象又提供了取得 mDecorView 对象的接口。
 
 

代码实操

更改Android原生系统设置APK中的背景图片。
./packages/apps/Settings/src/.../Settings.java
 

 
至此,我们不仅完成了更换不是由自己设置布局的Activity的背景图片,还了解到了系统设置布局的流程的知识。
 

<wiz_tmp_tag id="wiz-table-range-border" contenteditable="false" style="display: none;">

 
 
 
 

在 Activity 中实现 getContentView 操作的更多相关文章

  1. Android 实现在Activity中操作刷新另外一个Activity数据列表

    做android项目中遇到这样一个问题:有两个acticity,一个显示好友列表,另外一个显示会话列表,现在问题是在会话界面增加一个添加好友功能,添加好友后要求实时的刷新好友列表. 想了想,找了两种方 ...

  2. 我的Android六章:Android中SQLite数据库操作

    今天学习的内容是Android中的SQLite数据库操作,在讲解这个内容之前小编在前面有一篇博客也是讲解了SQLite数据库的操作,而那篇博客的讲解是讲述了 如何在Window中通过DOM来操作数据库 ...

  3. android开发之在activity中控制另一个activity的UI更新

    转自:http://blog.csdn.net/jason0539/article/details/18075293 第一种方法: 遇到一个问题,需要在一个activity中控制另一个acitivit ...

  4. Android 管理Activity中的fragments

    为了管理Activity中的fragments,需要使用FragmentManager,为了得到它,需要调用Activity中的getFragmentManager()方法,接下来详细介绍,感兴趣的朋 ...

  5. Activity中的四种启动模式

    在Android中每个界面都是一个Activity,切换界面操作其实是多个不同Activity之间的实例化操作.在Android中Activity的启动模式决定了Activity的启动运行方式. An ...

  6. android中的数据库操作(转)

    android中的数据库操作 android中的应用开发很难避免不去使用数据库,这次就和大家聊聊android中的数据库操作. 一.android内的数据库的基础知识介绍 1.用了什么数据库   an ...

  7. android中的文件操作详解以及内部存储和外部存储(转载)

    原文链接:http://m.blog.csdn.net/article/details?id=17725989 摘要 其实安卓文件的操作和java在pc环境下的操作并无二致,之所以需要单独讲解是因为安 ...

  8. 在Activity中响应ListView内部按钮的点击事件的两种方法!!!

    在Activity中响应ListView内部按钮的点击事件的两种方法 转载:http://www.cnblogs.com/ivan-xu/p/4124967.html 最近交流群里面有人问到一个问题: ...

  9. Fragment在Activity中的应用 (转载)

    原文链接 http://www.cnblogs.com/nanxin/archive/2013/01/24/2875341.html 在本小节中介绍在Activity中创建Fragment. 官网有很 ...

随机推荐

  1. Linux Namespace : Mount

    Mount namespace 为进程提供独立的文件系统视图.简单点说就是,mount namespace 用来隔离文件系统的挂载点,这样进程就只能看到自己的 mount namespace 中的文件 ...

  2. sublime插件不能使用,提示plugin_host has exited unexpectedly

    sublime Text3一打开软件就提示plugin_host has exited unexpectedly,插件不能使用 解决方法很简单: 1.首先,ctrl + shift + p  --&g ...

  3. 十二、存token获取token刷新token发送header头

    //测试token //获取token function setToken(data){ var storage = window.localStorage; if(!storage){ alert( ...

  4. 百度软件开发实习生c++方向面经(一面)

    百度2017实习生软件开发(cpp方向) 首先说一下岗位.分为软件开发,开发测试,前端,机器学习数据挖掘,移动开发,据我观察,报的人数来看,软件开发最多,移动开发和开发测试较少.百度前台还准备了吃的喝 ...

  5. 小程序wxRequest封装

    //const host = 'http://114.215.00.00:8005';// 测试地址 const host = 'https://xx.xxxxxxxx.net'; // 正式地址 c ...

  6. K 班1-7,alpha,beta 作业成绩汇总

    K 班1-7,alpha,beta 作业成绩汇总 千帆竞发 详细得分 短学号 名 1 2 3 4 5 6 7 alpha beta TOTAL 505 基智 4.55 1 -2 0 0 -10 4.3 ...

  7. 使用mysql,sql语言删除冗余信息

    这是表,我们需要操作的就是删除除了学号不同,其它信息都相同的冗余信息 思路:删除表格class3中的冗余的stu_id信息,那么接下来我们应该去筛选哪些stu_id信息是冗余的, 此时我们想到的就是利 ...

  8. 蒲公英App开发之检测新版本

    https://www.jianshu.com/p/2d3f048511d7 2017.04.17 16:22* 字数 62 阅读 422评论 0喜欢 1 可以在App内部实现检测版本更新并实现安装. ...

  9. 05Hadoop 概论

    Hadoop的思想之源:Google Google搜索引擎,Gmail,安卓,AppspotGoogle Maps,Google earth,Google 学术,Google翻译,Google+,下一 ...

  10. mysql cpu 100% 满 优化方案

    解决MySQL CPU占用100%的经验总结 - karl_han的专栏 - CSDN博客 https://blog.csdn.net/karl_han/article/details/5630782 ...