当前位置:   article > 正文

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

mtk移植包

总结

本文讲解了我对Android开发现状的一些看法,也许有些人会觉得我的观点不对,但我认为没有绝对的对与错,一切交给时间去证明吧!愿与各位坚守的同胞们互相学习,共同进步!

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

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

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

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

我们在i9100中发现了一段类似的代码:

:cond_1

:goto_1

if-eqz v7, :cond_2

move-object/from16 v0, p1

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

只不过是v6变成了v7,说明这段代码检测v7的值,因此我们需要将我们插入的代码改为:

invoke-virtual/range {p0 … p2},

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

move-result-object v7

if-nez v7, :cond_1

4.5 内部类

在这一节的最后我们来介绍一下内部类。对于Java文件中的每一个内部类,都会产生一个单独的smali文件,比如ActivityThread 1. s m a l i ,这些文件的命名规范是如果是匿名类,外部类 + 1.smali,这些文件的命名规范是如果是匿名类,外部类+ 1.smali,这些文件的命名规范是如果是匿名类,外部类++数字。否则的话是外部类+$+内部类的名字。

当在内部类中调用外部类的私有方法时,编译器会自动合成一个静态函数。比如下面这个类:

public class Hello {

public class A {

void func() {

setup();

}

}

private void setup() {

}

}

我们在内部类A的func方法中调用了外部类的setup方法,最终编译的smali代码为:

Hello$A.smali文件代码片段:

# virtual methods

.method func()V

.locals 1

.prologue

总结

【Android 详细知识点思维脑图(技能树)】

我个人是做Android开发,已经有十来年了,目前在某创业公司任职CTO兼系统架构师。虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。

这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司19年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

由于篇幅有限,这里以图片的形式给大家展示一小部分。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

最后,赠与大家一句话,共勉!

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

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

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

个方向参考。

最后,赠与大家一句话,共勉!

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

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

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/喵喵爱编程/article/detail/954571
推荐阅读
相关标签
  

闽ICP备14008679号