全面认识Android手机(MIUI ROM适配之旅第四天——移植MIUI Framework)

结尾

最后,针对上面谈的内容,给大家推荐一个Android资料,应该对大家有用。

首先是一个知识清单:(对于现在的Android及移动互联网来说,我们需要掌握的技术)

泛型原理丶反射原理丶Java虚拟机原理丶线程池原理丶
注解原理丶注解原理丶序列化
Activity知识体系(Activity的生命周期丶Activity的任务栈丶Activity的启动模式丶View源码丶Fragment内核相关丶service原理等)
代码框架结构优化(数据结构丶排序算法丶设计模式)
APP性能优化(用户体验优化丶适配丶代码调优)
热修复丶热升级丶Hook技术丶IOC架构设计
NDK(c编程丶C++丶JNI丶LINUX)
如何提高开发效率?
MVC丶MVP丶MVVM
微信小程序
Hybrid
Flutter

接下来是资料清单:(敲黑板!!!


1.数据结构和算法

2.设计模式

3.全套体系化高级架构视频;七大主流技术模块,视频+源码+笔记

4.面试专题资料包(怎么能少了一份全面的面试题总结呢~)

不论遇到什么困难,都不应该成为我们放弃的理由!共勉~

如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。

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

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

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

大家看到这里可能有一个疑问,我们直接替换原厂ROM框架层和核心应用层这两层的代码不就得了。不行,因为各个层次之间是有管理的,框架层和下层代码的一些调用接口是各个厂家自己扩展的,简单的整个替换MIUI框架层和核心应用层的代码无法工作。

2. 方法概述

这一章介绍MIUI框架层的移植,其实主要是修改system/framework目录下的三个文件:framework.jar, android.policy.jar和services.jar。这3个文件是Android系统的核心:framework.jar提供了应用层调用的各种API的实现,android.policy.jar提供了锁屏的实现以及手机窗口管理策略的实现。services.jar是一些核心服务Java层的实现,比如ActivityManagerService, PackageManagerService等,这些服务大都运行在system_server进程中。

我们目前2.3的代码是基于google发布的android 2.3.7源代码开发的,大家下载附件中的压缩包打开后的目录结构为:

porting-miui/

|-----------------android

|------------framework.jar

|------------services.jar

|------------android.policy.jar

|------------------miui

|----------framework.jar

|----------services.jar

|----------android.policy.jar

|-----------framework-res/

|-----------framework-miui-res.apk

其中android目录中的这三个文件是从google发布的android2.3.7源代码编译而来的,

而miui目录中的这三个文件则是我们在android2.3.7源代码基础上修改后的代码编译而来的。这样我们可以先反编译这些文件,找出反编译后的差别之处,然后将这些差别之处应用到原厂ROM的这三个文件中。听起来是不是和Linux下的patch过程很相似,是的,确实相似,只不过通常的patch是基于源代码的,然后解决一些冲突。而我们是基于smali代码,然后解决一些冲突。(解决冲突现在可能不太明白,没关系,下面会有例子)。

3. 移植资源

在上一节中miui目录下的framework-res目录和framework-miui-res.apk这两个是和移植资源相关的。framework-res目录下是我们对系统资源所做的修改即/system/framework/framework-res.apk的修改,大家可以反编译framework-res.apk,将这些修改合到framework-res中,然后再编译回去,这个比较简单,不多做介绍。

framework-miui-res.apk是miui的资源包,所有的miui app都会用到它。这个资源包也需要放在/system/framework/目录中,在原厂ROM中,大家一般在/system/framework目录下除了framework-res.apk,也会发现一些其它的xxx-res.apk。为了针对这种情况,miui的资源ID都是以0x03开头的,一般的原厂ROM是2个资源包,framework-res.apk的资源ID是以0x01开头的,另外一个资源包以0x02开头。但是我们发现国行的defy比较变态,这个目录下有3个资源包,因此针对defy我们得特殊处理。所以如果你所移植的机型这个目录下不止两个资源包的话,需要和我们联系。未来我们会考虑MIUI的资源ID都以0x06开头,我们相信应该没有哪个原厂ROM变态到有5个资源包。

4. 修改smali

这一章我们重点介绍如何修改原厂ROM的smali将MIUI的修改应用到上面去。我们不会将所有的修改都会在文中列出,挑选几个有代表性的讲解,剩下的大家可以自己去做。我们以i9100为例讲述如何修改。在第二篇准备工作中,我们给出了i9100国行ROM的下载链接,并且讨论了如何在这个ROM的基础上做deodex。为了方便起见,我们把i9100原厂ROM做个deodex后的framework.jar,services.jar和android.policy.jar也放在了附件中。

4.1 比较差异

这里的比较差异包含两个部分:比较miui和原生android的差异,比较i9100和原生android的差异。

以framework.jar为例,首先可以建3个目录反汇编这3个jar包:

apktool d framework.jar

执行完毕后,会产生framework.jar.out目录。

接下来使用附件中的脚本rmline.sh运行如下命令:

./rmline.sh framework.jar.out

rmline.sh是用以把smali所有以.line开头的行去掉,这样我们容易比较smali代码上的差别。但是对于所移植的机型,请先复制一份为去掉.line的framework.jar.out版本,因为这些对调试很重要,我们能通过adb logcat报告的错误信息中去定位在哪一行。

接下来用大家说熟悉的文件比较工具来比较差异,Linux下推荐meld, Windows下推荐Beyond Compare。

用meld比较miui和原生android的区别,大家可以看到有很多新增的Miui开头的类,和一个新增的miui目录,这些新增的文件和目录我们直接拷到i9100的framework.jar.out中对应的目录中即可(使用有.line的版本)。为什么我们不把这些新增的类组织在一个单独的jar包中呢(请大家思考一下这个问题)。

不比较那些相同的和新加的,我们只比较修改过的文件,在附件中有一个change-list文件,其中列出了我们修改过的文件,你会发现和这个比较结果有一点不符,如果你比较那些文件,会发现只是一些微小的差异(比如说nop这种空指令),这是由于apktool反编译导致的。我们无需关心,我们只需要比较那些我们修改过的文件。

下面我们就开始修改smali文件了,我将这些修改分成3种情况,选择有代表性的3个文件加以介绍,这3种情况难度依次增加。

4.2 直接替换

以ActivityThread.smali为例,比较发现miui改了其中一个方法getTopLevelResources,而i9100和原生android的实现完全一样,这种情形是最简单也是最happy的,我们改的地方要适配的机型原厂ROM完全没有修改,直接替换就可以了。

4.3 线性代码

还是以ActivityThread.smali为例,对于这个文件,miui一共改了两个方法,一个是上面介绍的,另一个是applyConfigurationToResourcesLocked。通过比较得知,miui修改了这个方法,i9100也修改了这个方法。怎么办呢,我们先分析一下miui修改的代码:

.method final applyConfigurationToResourcesLocked(Landroid/content/res/Configuration;)Z

invoke-virtual {v5, p1}, Landroid/content/res/Configuration;->updateFrom(Landroid/content/res/Configuration;)I

move-result v0

.local v0, changes:I

invoke-static {v0}, Landroid/app/MiuiThemeHelper;->handleExtraConfigurationChanges(I)V

invoke-virtual {p0, v7}, Landroid/app/ActivityThread;->getDisplayMetricsLocked(Z)Landroid/util/DisplayMetrics;

move-result-object v1

.local v1, dm:Landroid/util/DisplayMetrics;

在上面将miui增加的代码用红色标出,在讲述之前,先解释一下smali代码的一些规律:

所有的局部变量用v开头,方法的顶部.locals 8表示这个方法使用8个局部变量。所有的参数用p开头,局部变量和参数都是从0开始编号。对于非静态方法来说,p0就是对象本身的引用,即this指针。

这里miui新增了一个静态方法调用,对于这种顺序执行的一段代码,我们称之为线性代码。这个例子比较简单,只新增了一个静态方法调用。线性代码的特点是只有一个入口和一个出口,在编译器的术语这叫做基本块。对于这种新增的代码,我们找出它的上下文,即修改的代码前后的操作。然后在i9100的该方法的smali代码中找到相应的位置,把这个修改应用到i9100中去。这种修改也相对简单,插入代码的相应位置比较好定位。

4.4 条件判断

这种情况指的是miui插入的代码并不是一个线性代码,而是有条件判断的。我们以Resources.smali为例,miui修改了其中的loadDrawable方法,修改后的结果如下:

.method loadDrawable(Landroid/util/TypedValue;I)Landroid/graphics/drawable/Drawable;

.end local v8           #e:Ljava/lang/Exception;

.end local v13          #rnf:Landroid/content/res/Resources$NotFoundException;

:cond_6

invoke-virtual/range {p0 … p2},

Landroid/content/res/Resources;->loadOverlayDrawable(Landroid/util/TypedValue;I)Landroid/graphics/drawable/Drawable;

move-result-object v6

if-nez v6, :cond_1

:try_start_1

move-object/from16 v0, p0

红色代码是miui插入的代码,我们再看一下i9100相对于原生android对这个方法的改动,发现改动非常大。这种情况怎么办呢,这种情况下的关键是找到所插入代码的入口点和出口点(即这段代码是从哪执行而来的,执行完毕后又往哪去开始执行代码)。

首先,我们发现插入代码的前面是一个标号:cond_6,这说明程序中应该有一个跳转语句跳转到这个标号:cond_6。而且这种程序应该也可以从:cond_6上面的语句顺序执行而来(即它可能有两个入口点),我们分别去找这两个入口点的代码。首先我们去找哪个语句使用了:cond_6,找到如下代码:

const-string v15, “.xml”

invoke-virtual {v9, v15}, Ljava/lang/String;->endsWith(Ljava/lang/String;)Z

move-result v15

if-eqz v15, :cond_6

可以发现这段代码是在判断v9这个字符串是否以".xml"结尾,如果不是的话,跳转到:cond_6。好,我们去i9100中找到对应的代码逻辑。对于这个例子,我们完全可以以".xml"作为一个关键字去i9100的loadDrawable方法中搜索一下,定位到如下代码:

const-string v17, “.xml”

move-object v0, v10

move-object/from16 v1, v17

invoke-virtual {v0, v1}, Ljava/lang/String;->endsWith(Ljava/lang/String;)Z

move-result v17

if-eqz v17, :cond_b

这段代码的逻辑和我们在miui中找到的代码一样,看来,我们应该把miui插入的代码插入到:cond_b之后。找到i9100代码中的:cond_b之后,我们看看这条代码后面的代码,发现和我们插入的代码后面的代码基本类似,这下可以确定miui新插入的代码应该放在:cond_b之后了。

再来看看出口点,miui插入的代码有两个出口点(是一个条件判断),

if-nez v6, :cond_1

如果v6为空往下执行,如果不为空,则跳转到:cond_1,好,我们来看看:cond_1的代码是在干嘛?:cond_1的代码如下:

:cond_1

:goto_1

if-eqz v6, :cond_2

move-object/from16 v0, p1

iget v0, v0, Landroid/util/TypedValue;->changingConfigurations:I

分享读者

作者2013年java转到Android开发,在小厂待过,也去过华为,OPPO等大厂待过,18年四月份进了阿里一直到现在。

被人面试过,也面试过很多人。深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长,而且极易碰到天花板技术停滞不前!

我们整理了一份阿里P7级别的Android架构师全套学习资料,特别适合有3-5年以上经验的小伙伴深入学习提升。

主要包括阿里,以及字节跳动,腾讯,华为,小米,等一线互联网公司主流架构技术。如果你有需要,尽管拿走好了。

35岁中年危机大多是因为被短期的利益牵着走,过早压榨掉了价值,如果能一开始就树立一个正确的长远的职业规划。35岁后的你只会比周围的人更值钱。

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

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

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

多是因为被短期的利益牵着走,过早压榨掉了价值,如果能一开始就树立一个正确的长远的职业规划。35岁后的你只会比周围的人更值钱。

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

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值