2024年Android最新Android开发之Theme、Style探索及源码浅析,头条android面试算法

结语

  • 现在随着短视频,抖音,快手的流行NDK模块开发也显得越发重要,需要这块人才的企业也越来越多,随之学习这块的人也变多了,音视频的开发,往往是比较难的,而这个比较难的技术就是NDK里面的技术。
  • 音视频/高清大图片/人工智能/直播/抖音等等这年与用户最紧密,与我们生活最相关的技术一直都在寻找最终的技术落地平台,以前是windows系统,而现在则是移动系统了,移动系统中又是以Android占比绝大部分为前提,所以AndroidNDK技术已经是我们必备技能了。
  • 要学习好NDK,其中的关于C/C++,jni,Linux基础都是需要学习的,除此之外,音视频的编解码技术,流媒体协议,ffmpeg这些都是音视频开发必备技能,而且
  • OpenCV/OpenGl/这些又是图像处理必备知识,下面这些我都是当年自己搜集的资料和做的一些图,因为当年我就感觉视频这块会是一个大的趋势。所以提前做了一些准备。现在拿出来分享给大家。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

在新版本的Android中添加了很多新的Theme,而老版本又不兼容这些Theme,所以很多时候我们可能需要处理一下这种兼容性问题,譬如我们在res/values/styles.xml文件中定义如下Theme:

当我们想在Android3.0(API 11)以上使用新的Theme则可以res/values-v11目录下定义如下Theme:

这样当我们编译的APK在不同的设备上运行时就能自己切换选择适合自己平台的Theme了。

2-4 Android系统预制的Theme与Style选择


话说Android应用层开发之所以简单的原因就在于系统已经帮我们实现了很多自由选择的功能,关于Style与Theme也不例外(应用层开发难就难在知识面很广),具体使用可以记住如下秘诀:

  • 当我们想要知道Theme具体有哪些属性可以有效使用时,可以查阅API的R.styleable进行选择。

  • 当我们想要知道Style具体有哪些属性可以有效使用时,可以查阅API的R.attr进行选择。

  • 系统为我们提供了很多实用的Theme与Style,我们还可以通过查阅API的R.style进行选择(要注意的是这里的文档查到的不一定全,最好的办法是去查FW下base的res或者appcompat的res),不过要注意,在API中譬如Theme_NoTitleBar主题样式在我们xml引用时要替换为@android:style/Theme.NoTitleBar的格式。

2-5 Android应用资源拓展语法


上面提到的都是Theme与Style相关的东西,其实这两个东西实质都属于res资源的处理,关于Android的res资源使用规则和不同平台软硬件系统匹配的策略不属于本文范围,不过也很简单,感兴趣的同学可以移步到API Guide的App Resources进行研读。这里我们主要简单说下资源的引用语法,因为Theme与Style中也会经常使用到,免得带来不必要的疑惑。

Android中资源在Java文件中引用的语法定义如下:

[<package_name>.]R.<resource_type>.<resource_name>

//注意:当资源在当前APP中则package_name可以省略,当为系统的资源则可换位譬如android.

Android中资源在XML文件中引用的语法定义如下:

@[<package_name>:]<resource_type>/<resource_name>

//注意:package_name的规则同上java中,不过在XML中引入不是本包资源时要注意格式,譬如引用系统的资源格式为android:textColor=“@android:color/secondary_text_dark”

Android系统预制资源在XML文件中引用的特殊语法定义如下:

//可以引用系统所有资源,public & private

@*android:type/name

//只能引用系统public的资源

@android:type/name

//注意:没在frameworks/base/core/res/res/values/public.xml(也就是<sdk_path>\platforms\android-X\data\res\values\public.xml)中申明的资源App时不推荐使用的。

Android在XML文件中引用当前主题属性的语法定义如下:

?[<package_name>:][<resource_type>/]<resource_name>

//资源值允许引用当前主题中的属性的值,这个属性值只能在style资源和XML中使用,随着当前主题的切换该值也在变换,该resource_name不需要自己定义,系统会自己在当前主题下寻找,常见的譬如动画中等。

Android在XML文件中创建或者引用资源语法定义如下:

//在R.java的type内部类中添加一条静态常量id资源标识符,如果标示符(包括系统资源)已经存在则表示引用该标示符。

@+type/name

//在R.java中寻找已经定义的标识符,如果找不到则提示失败错误,一般在xml中定义有先后关系。

@type/name

//所以一般推荐直接使用+号避免不必要的意外。

Android在XML文件中xmlns语法定义如下:

//xmlns(XML Namespaces)是XML的命名空间

//通用XML命名空间格式规则

xmlns:namespace-prefix=“namespaceURI”

在Android的XML中命名空间规则如下:

xmlns:namespace-prefix=http://schemas.android.com/apk/res/应用程序包路径

在使用时规则如下:

namespace-prefix:属性

切记,xmlns的定义必须放在最外层开始的的标记中,譬如我们Activity的xml文件的根布局中的android前缀、tools前缀、自定义View的前缀等。常见的例子如下:

//android即为frameworks/base/core/res/res/values/attrs.xml中的属性

xmlns:android=“http://schemas.android.com/apk/res/android”

//开发调试利器,不再过多说明

xmlns:tools=“http://schemas.android.com/tools”

//Email App中res/values/attrs.xml等自定义属性

xmlns:settings=“http://schemas.android.com/apk/res/com.android.email”

2-6 Android应用Theme、Style使用小结


到此关于Android应用中如何定义Theme、Style及使用和继承重写相信大家已经明白了,再出现诡异的现象就可以通过查询相关API及google结合就能完全理会其中的原因了,而不是停留在能搜到复制;下面一节我们将针对上面的这些使用进行粗略的源码分析说明。

【工匠若水 http://blog.csdn.net/yanbober 未经允许严禁转载,请尊重作者劳动成果。私信联系我

3 源码结构浅析

============

有了上面的应用使用基础,下面的源码简单浅析可能存在跳跃性和经验性,不会像之前博客那样系统性的从头到尾进行分析,而是分点点到为止,感兴趣的同学可以自行深入研读。

3-1 追根溯源Theme、Style等根源


在我们App开发中通常我们会在新建工程后的AndroidManifest.xml文件中看见工程默认引用了应用包下自定义的主题@style/AppTheme(用法完全符合上一大节的规则)。该主题在当前应用包的style.xml中定义如下:

看着木有,它活生生的继承了Theme.AppCompat.Light.DarkActionBar这个style,这玩意又在framework的support v7包下res的themes.xml文件中,具体如下:

哈哈,原来如此,这里的Theme.Light你应该十分熟悉了吧(这就是以前我们App用的不是Support包,而是默认的时候,theme默认就是这玩意哈),这玩意就在framework的base下的themes.xml中定义着呢(所以通过了android:进行引用,留意细节吧),具体如下:

到这里我们就很容易明白啦,Theme.Light的父类原来是Theme哇,也在这个文件中,如下:

看注释吧,这货有接近400多个item属性,这也就是我们Android关于Theme的开山鼻祖了,在我们自定义时其实来这看比去API查还方便呢(其实需要两个互相配合,一个查,一个看解释,哈哈),因为它里面定义了关于我们整个应用中文字样式、按钮样式、列表样式、窗体样式、对话框样式等,这些样式都是默认样式,它还有很多我们常用的扩展样式,譬如Theme.Light、Theme.NoTitleBar、Theme.NoTitleBar.Fullscreen等等,反正你要有需求来这里搞就行。当我们继承使用时只用在前加上android:即可,有些属性可能是找不到的。同理,我们所谓的style、attr等等也都是这么个框架,大致位置也类似主题Theme的,所以这里不再过多说明,自行脑补即可。

3-2 Theme、Style等res资源客户化流程


对于纯App开发来说这一个知识点可以忽略,因为本小节需要大致了解Android源码的结构和编译框架,对于固件等开发来说这个还是比较重要的,记得以前做TV盒子开发时很多系统资源需要替换及添加,也就是说会稍微涉及到修改System UI及FW的res,那时候好坑爹,虽然修改的地方不多,只是换几个图标和加几个资源,但是那时候自己还是蒙圈了一段时间才搞明白,所以说有必要啰嗦几句。

首先我们先要明白设备里系统目录下的这些常见jar与apk的来源,如下:

| 名字 | 解释 |

| — | — |

| am.jar | 执行am命令所需的java lib,对应FW的base/cmds/am目录,具体可以参考下面的Android.mk定义。 |

| framework-res.apk | Android系统资源库集合,对应FW的core/res目录,具体同理参见Android.mk定义。 |

| framework.jar | Android SDK核心代码,对应FW的base目录,具体可以参考目录下的Android.mk的MOUDLE定义。 |

| SystemUI.apk | 从Android2.2开始状态栏和下拉通知栏被分割出一个单独的SystemUI.apk,一般在system的app或者priv-app下(还有很多其他模块呢,譬如SettingProvider等,具体可以在设备下看看),对应的源码在FW的packages下的SystemUI中。 |

| Others | 其他的jar比较多,不做一一介绍,不同厂商可能还会不同定制,具体可在厂商设备的system下看看有哪些包,对应回去通过Android.mk文件寻找即可。 |

| android.jar | 切记这个特例,这货是make sdk生成的,多方整合,别以为也可以找到对应目录,木有的!还有就是这个jar很实用的,很多时候我们想用AS直接调运系统的hide API等,自己编译一个就能派上用场啦! |

有了上边这几个和我们本文相关的核心常识后我们简单说下怎么修改编译:

  1. 修改FW/base/XXX/下面需要修改的代码;

  2. 单独在XXX下mm编译生成XXX.jar(apk);

  3. 把编译的jar(apk)包(在out目录对应路径下)push到设备系统system的FW目录下;

  4. reboot重启设备验证;

不过这里有些坑大家要明白,我们在mm前最好每次都去清除对应out/obj目录下的中间文件,特别是资源文件更新时,否则容易被坑。还有就是切记添加系统API或者修改@hide的API或者添加资源(包含添加修改public.xml等)后,需要执行make update-api命令来同步base/api下的current.txt的修改,完事再make就行啦,这些编译文档都有介绍。

有了上面这些相信大家对于客户化资源也就有了一些认识啦,想想如果我们需要用到framework.jar的hide资源或者framework-res.apk中新加的资源时又不想用反射和源码下编译怎么办?当然是编译一个no hide的jar引入我们工程即可哇,要注意我们引入以后一定是Providered的模式,也就是该jar只编译不打包入该apk,还有就是依赖的先后优先级顺序,否则又用的是sdk默认的。还有就是万能的android.jar也是一种曲线救国的办法。当然啦,如果是SDK开发则完全可以复制一份自己搞,完事编译进系统即可,同时提供给App开发。

3-3 Theme、Style加载时机及加载源码浅析


前面我们介绍了Android的Theme、Style的定义及使用及Theme、Style等res的由来,这里我们来看看这些被使用的Theme的最终是何时、怎样被加载生效的。我们都知道对于Theme有两种方式来使用,具体如下(Style等attr在View的使用也比较同类,这里只分析Theme、其他的请在View等地自行分析脑补):

  • 在AndroidManifest.xml中<application>或者<activity>节点设置android:theme属性;

  • 在Java代码中调用setTheme()方法设置Activity的Theme(须在setContentView()前设置;

可以看见,这两种方式我们都比较常用,甚至有时候还会设置Window的一些属性标记,这些标记方法都在Window类中。我们平时在设置这些Theme时总是有很多疑惑,譬如为毛只能在setContentView()前设置等等,那么下面我们就来庖丁解牛一把。故事在开始之前可能还需要你自行脑补下《Android应用setContentView与LayoutInflater加载解析机制源码分析》《Android应用Activity、Dialog、PopWindow、Toast窗口添加机制及源码分析》两篇文章,完事再来继续下面的内容。

关于Activity通过setContentView方法设置View的来源这里就不多说了,参考前面两篇即可,我们直接跳到PhoneWindow的setContentView方法来看下,如下:

public void setContentView(int layoutResID) {

if (mContentParent == null) {

installDecor();//每个Activity第一次进来必走

} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {

mContentParent.removeAllViews();

}

}

我们接着来看下installDecor()方法,如下:

private void installDecor() {

if (mDecor == null) {

//仅仅new DecorView(getContext(), -1)而已,也就是FrameLayout

mDecor = generateDecor();

}

if (mContentParent == null) {

//生成我们布局的父布局

mContentParent = generateLayout(mDecor);

// Set up decor part of UI to ignore fitsSystemWindows if appropriate.

mDecor.makeOptionalFitsSystemWindows();

final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(

R.id.decor_content_parent);

}

}

接着我们继续看看generateLayout(mDecor);这个方法,如下:

protected ViewGroup generateLayout(DecorView decor) {

// Apply data from current theme.

//获取当前主题,重点!!!!!!!

TypedArray a = getWindowStyle();

//解析一堆主题属性,譬如下面的是否浮动window(dialog)等

mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);

// Inflate the window decor.

//依据属性获取不同的布局添加到Decor

int layoutResource;

int features = getLocalFeatures();

// System.out.println(“Features: 0x” + Integer.toHexString(features));

if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {

layoutResource = R.layout.screen_swipe_dismiss;

}

View in = mLayoutInflater.inflate(layoutResource, null);

decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));

mContentRoot = (ViewGroup) in;

ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);

return contentParent;

}

一样喽,继续先看下getWindowStyle()方法是神马鬼,这个方法在其基类Window中,如下:

/**

  • Return the {@link android.R.styleable#Window} attributes from this

  • window’s theme.

*/

public final TypedArray getWindowStyle() {

synchronized (this) {

if (mWindowStyle == null) {

mWindowStyle = mContext.obtainStyledAttributes(

com.android.internal.R.styleable.Window);

}

return mWindowStyle;

}

}

哎,没啥好看的,没有逻辑,就是流程,继续跟吧,去Context类看看obtainStyledAttributes(com.android.internal.R.styleable.Window)方法吧,如下:

/**

  • Return the Theme object associated with this Context.

*/

@ViewDebug.ExportedProperty(deepExport = true)

public abstract Resources.Theme getTheme();

/**

  • Retrieve styled attribute information in this Context’s theme. See

  • {@link android.content.res.Resources.Theme#obtainStyledAttributes(int[])}

  • for more information.

  • @see android.content.res.Resources.Theme#obtainStyledAttributes(int[])

*/

public final TypedArray obtainStyledAttributes(@StyleableRes int[] attrs) {

//获取当前Theme对应的TypedArray对象

return getTheme().obtainStyledAttributes(attrs);

}

哎呦我去,憋大招呢,急死人了!可以看见Context的getTheme()方法时一个抽象方法,那他的实现在哪呢,看过《Android应用Context详解及源码解析》一文的同学一定知道对于Activity来说他的实现类就是ContextThemeWapprer,那我们赶紧进去看看它到底搞了啥玩意,如下:

@Override

public Resources.Theme getTheme() {

//一旦设置有Theme则不再走后面逻辑,直接返回以前设置的Theme

if (mTheme != null) {

return mTheme;

}

//没有设置Theme则获取默认的selectDefaultTheme

mThemeResource = Resources.selectDefaultTheme(mThemeResource,

getApplicationInfo().targetSdkVersion);

//初始化选择的主题,mTheme就不为null了

initializeTheme();

return mTheme;

}

@Override

public void setTheme(int resid) {

//通过外部设置以后mTheme和mThemeResource就不为null了

if (mThemeResource != resid) {

mThemeResource = resid;

//初始化选择的主题,mTheme就不为null了

initializeTheme();

}

}

文末

那么对于想坚持程序员这行的真的就一点希望都没有吗?
其实不然,在互联网的大浪淘沙之下,留下的永远是最优秀的,我们考虑的不是哪个行业差哪个行业难,就逃避掉这些,无论哪个行业,都会有他的问题,但是无论哪个行业都会有站在最顶端的那群人。我们要做的就是努力提升自己,让自己站在最顶端,学历不够那就去读,知识不够那就去学。人之所以为人,不就是有解决问题的能力吗?挡住自己的由于只有自己。
Android希望=技能+面试

  • 技能
  • 面试技巧+面试题

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

//初始化选择的主题,mTheme就不为null了

initializeTheme();

return mTheme;

}

@Override

public void setTheme(int resid) {

//通过外部设置以后mTheme和mThemeResource就不为null了

if (mThemeResource != resid) {

mThemeResource = resid;

//初始化选择的主题,mTheme就不为null了

initializeTheme();

}

}

文末

那么对于想坚持程序员这行的真的就一点希望都没有吗?
其实不然,在互联网的大浪淘沙之下,留下的永远是最优秀的,我们考虑的不是哪个行业差哪个行业难,就逃避掉这些,无论哪个行业,都会有他的问题,但是无论哪个行业都会有站在最顶端的那群人。我们要做的就是努力提升自己,让自己站在最顶端,学历不够那就去读,知识不够那就去学。人之所以为人,不就是有解决问题的能力吗?挡住自己的由于只有自己。
Android希望=技能+面试

  • 技能
    [外链图片转存中…(img-bI50G7nH-1715658470302)]
  • 面试技巧+面试题
    [外链图片转存中…(img-FO7uqZ73-1715658470303)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值