Android 反编译之修改Smali 文件输出日志

本篇是Android反编译系列的 第五篇, 前四篇的地址在这里
第一篇 Apktool安装
第二篇 Apktool 基本使用方法
第三篇 App汉化与二次签名
第四篇 修改Smali文件,定位关键点


承接上篇Android反编译之修改Smali文件,本篇主要分析如何在 Smali文件中输出日志,帮助我们理解App 业务处理逻辑。

1.我们先写这样一个简单的demo

smaliLogDemoApp.gif

顺便安利一下Android 上gif 截图的软件LICEcap,免费开源,秋百万大神推荐

App 登陆界面,对用户名和密码做简单的逻辑验证,同时有一个FloatingActionBar,点击之后显示Snackbar。

我们现在要做的,在登陆验证的方法里,添加日志输出,分别输出登陆成功,登陆失败。在FloatingActionBar 的响应事件中,添加日志输出。

2.反编译SmaliLogDemo

demo 比较简单,我这里直接给出反编译之后的 Smali 文件。

.class public Lcom/imesong/smalilogdemo/MainActivity;
.super Landroid/support/v7/app/AppCompatActivity;
.source "MainActivity.java"


# instance fields
.field private login:Landroid/widget/Button;

.field private nameEdit:Landroid/support/v7/widget/AppCompatEditText;

.field private passwdEdit:Landroid/support/v7/widget/AppCompatEditText;


# direct methods
.method public constructor <init>()V
    .locals 0

    .prologue
    .line 16
    invoke-direct {p0}, Landroid/support/v7/app/AppCompatActivity;-><init>()V

    return-void
.end method

.method static synthetic access$000(Lcom/imesong/smalilogdemo/MainActivity;)Landroid/support/v7/widget/AppCompatEditText;
    .locals 1
    .param p0, "x0"    # Lcom/imesong/smalilogdemo/MainActivity;

    .prologue
    .line 16
    iget-object v0, p0, Lcom/imesong/smalilogdemo/MainActivity;->nameEdit:Landroid/support/v7/widget/AppCompatEditText;

    return-object v0
.end method

.method static synthetic access$100(Lcom/imesong/smalilogdemo/MainActivity;)Landroid/support/v7/widget/AppCompatEditText;
    .locals 1
    .param p0, "x0"    # Lcom/imesong/smalilogdemo/MainActivity;

    .prologue
    .line 16
    iget-object v0, p0, Lcom/imesong/smalilogdemo/MainActivity;->passwdEdit:Landroid/support/v7/widget/AppCompatEditText;

    return-object v0
.end method

.method static synthetic access$200(Lcom/imesong/smalilogdemo/MainActivity;Ljava/lang/String;Ljava/lang/String;)Z
    .locals 1
    .param p0, "x0"    # Lcom/imesong/smalilogdemo/MainActivity;
    .param p1, "x1"    # Ljava/lang/String;
    .param p2, "x2"    # Ljava/lang/String;

    .prologue
    .line 16
    invoke-direct {p0, p1, p2}, Lcom/imesong/smalilogdemo/MainActivity;->isLogingInfoValid(Ljava/lang/String;Ljava/lang/String;)Z

    move-result v0

    return v0
.end method

.method private isLogingInfoValid(Ljava/lang/String;Ljava/lang/String;)Z
    .locals 4
    .param p1, "name"    # Ljava/lang/String;
    .param p2, "passwd"    # Ljava/lang/String;

    .prologue
    const/4 v3, 0x0

    .line 59
    const/4 v0, 0x0

    .line 60
    .local v0, "isValid":Z
    invoke-static {p1}, Landroid/text/TextUtils;->isEmpty(Ljava/lang/CharSequence;)Z

    move-result v1

    if-nez v1, :cond_0

    invoke-static {p2}, Landroid/text/TextUtils;->isEmpty(Ljava/lang/CharSequence;)Z

    move-result v1

    if-eqz v1, :cond_1

    .line 61
    :cond_0
    const/4 v0, 0x0

    .line 62
    const-string v1, "\u7528\u6237\u540d\u3001\u5bc6\u7801\u4e0d\u80fd\u4e3a\u7a7a"

    invoke-static {p0, v1, v3}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;

    move-result-object v1

    invoke-virtual {v1}, Landroid/widget/Toast;->show()V

    .line 69
    :goto_0
    return v0
    
    .line 63
    :cond_1
    invoke-virtual {p2}, Ljava/lang/String;->length()I

    move-result v1

    const/4 v2, 0x6

    if-ge v1, v2, :cond_2

    .line 64
    const/4 v0, 0x0

    .line 65
    const-string v1, "\u5bc6\u7801\u957f\u5ea6\u5c0f\u4e8e6"

    invoke-static {p0, v1, v3}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;

    move-result-object v1

    invoke-virtual {v1}, Landroid/widget/Toast;->show()V

    goto :goto_0

    .line 67
    :cond_2
    const/4 v0, 0x1

    goto :goto_0
.end method


# virtual methods
.method protected onCreate(Landroid/os/Bundle;)V
    .locals 4
    .param p1, "savedInstanceState"    # Landroid/os/Bundle;

    .prologue
    .line 24
    invoke-super {p0, p1}, Landroid/support/v7/app/AppCompatActivity;->onCreate(Landroid/os/Bundle;)V

    .line 25
    const v2, 0x7f040019

    invoke-virtual {p0, v2}, Lcom/imesong/smalilogdemo/MainActivity;->setContentView(I)V

    .line 26
    const v2, 0x7f0c0069

    invoke-virtual {p0, v2}, Lcom/imesong/smalilogdemo/MainActivity;->findViewById(I)Landroid/view/View;

    move-result-object v1

    check-cast v1, Landroid/support/v7/widget/Toolbar;

    .line 27
    .local v1, "toolbar":Landroid/support/v7/widget/Toolbar;
    invoke-virtual {p0, v1}, Lcom/imesong/smalilogdemo/MainActivity;->setSupportActionBar(Landroid/support/v7/widget/Toolbar;)V

    .line 29
    const v2, 0x7f0c006a

    invoke-virtual {p0, v2}, Lcom/imesong/smalilogdemo/MainActivity;->findViewById(I)Landroid/view/View;

    move-result-object v0

    check-cast v0, Landroid/support/design/widget/FloatingActionButton;

    .line 30
    .local v0, "fab":Landroid/support/design/widget/FloatingActionButton;
    new-instance v2, Lcom/imesong/smalilogdemo/MainActivity$1;

    invoke-direct {v2, p0}, Lcom/imesong/smalilogdemo/MainActivity$1;-><init>(Lcom/imesong/smalilogdemo/MainActivity;)V

    invoke-virtual {v0, v2}, Landroid/support/design/widget/FloatingActionButton;->setOnClickListener(Landroid/view/View$OnClickListener;)V

    .line 38
    const v2, 0x7f0c006b

    invoke-virtual {p0, v2}, Lcom/imesong/smalilogdemo/MainActivity;->findViewById(I)Landroid/view/View;

    move-result-object v2

    check-cast v2, Landroid/support/v7/widget/AppCompatEditText;

    iput-object v2, p0, Lcom/imesong/smalilogdemo/MainActivity;->nameEdit:Landroid/support/v7/widget/AppCompatEditText;

    .line 39
    const v2, 0x7f0c006c

    invoke-virtual {p0, v2}, Lcom/imesong/smalilogdemo/MainActivity;->findViewById(I)Landroid/view/View;

    move-result-object v2

    check-cast v2, Landroid/support/v7/widget/AppCompatEditText;

    iput-object v2, p0, Lcom/imesong/smalilogdemo/MainActivity;->passwdEdit:Landroid/support/v7/widget/AppCompatEditText;

    .line 41
    const v2, 0x7f0c006d

    invoke-virtual {p0, v2}, Lcom/imesong/smalilogdemo/MainActivity;->findViewById(I)Landroid/view/View;

    move-result-object v2

    check-cast v2, Landroid/widget/Button;

    iput-object v2, p0, Lcom/imesong/smalilogdemo/MainActivity;->login:Landroid/widget/Button;

    .line 43
    iget-object v2, p0, Lcom/imesong/smalilogdemo/MainActivity;->login:Landroid/widget/Button;

    new-instance v3, Lcom/imesong/smalilogdemo/MainActivity$2;

    invoke-direct {v3, p0}, Lcom/imesong/smalilogdemo/MainActivity$2;-><init>(Lcom/imesong/smalilogdemo/MainActivity;)V

    invoke-virtual {v2, v3}, Landroid/widget/Button;->setOnClickListener(Landroid/view/View$OnClickListener;)V

    .line 56
    return-void
.end method

代码没有混淆,看起来很清楚。类名 是 MainActivity.java,有三个成员变量,一个 Button,两个 AppCompatEditText。下面还有 isLogingInfoValid 方法,有两个参数,分别是 name 和 passwd。最后的是 onCreate 方法。不过,大致浏览之后,好像没有发现 FloatingActionBar 的踪迹啊,不要着急,我们先把登陆逻辑判断的日志处理好,回过头来再查找 FloatingActionBar 也不迟。

我们先分析 isLogingInfValid 的处理逻辑。

方法第一行 .logcals ,表示使用4个寄存器,.param p1 表示形参 name, .param p2 表示形参 passwd。紧接着 .prologue 表示方法执行开始

#定义变量 v3,并赋值为0 
#smali 中注释使用#
const/4 v3,0x0

#定义Boolean变量 isValid,默认为false
.local v0,"isValid":Z

#调用TextUtils.isEmpty(string) 方法
#invoke-static ,调用一个static的静态方法
#{p1} 是 static 方法的入参
#Landroid/text/TextUtils 是这个方法的决定路径,L表示这是一个Object,Smali中只有两种类型 基础类型和引用类型(Object,使用L表示)
#->这个表示调用isEmpty 方法,熟悉脚本语言的开发,应该很熟悉,小括号中定义入参的类型,使用 ';' 结尾
#最后的 **Z** 表示代表返回值为Boolean 类型。
#这样完成了TextUtils.isEmpty(name) 方法的调用
invoke-static {p1}, Landroid/text/TextUtils;->isEmpty(Ljava/lang/CharSequence;)Z

#将结果赋值给 v1
move-result v1

#如果 v1 != 0 跳转到 :cond_0
#如果 v1==true 跳转到 :cond_0,如果是 false ,接着往下走
if-nez v1, :cond_0

#赋值 v0 == 0
:cond_0 
const/4 v0, 0x0
#下面紧接着定义 变量 v1,调用 Toast.make(context,"string",Toast.SHORT).show();方法
#之后执行:goto_0 返回 v0 的值。
#这个逻辑走下来,说明如果第一个参数,用户名为null,直接弹出Toast,返回false,符合我们都看到的现象。

如果要在用户名为空的时候添加一条日志怎么添加呢?下面我们看下如何添加日志。

1.修改寄存器数量
如果要添加日志,我们要输出我们想要显示的信息,这些也是要申请寄存器存放。
.logcal 4 修改为 .logcal 5

2.新增变量,定义我们要输出的信息
为了方便,这里讲 Tag 和信息定义相同,如果不想定义一致,可以再增加一个寄存器。

const-string v4 ,"nameNull"

3.调用Log.d("info","info");方法。

#注意参数,返回值必须完全正确
invoke-static {v4,v4};Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I

这样我们就完成了name 为空时的日志添加。我们重新打包,签名,运行看下结果。

apktool b smalilogdemo -o smalilogdemo_unsigned.apk

执行之后,却遇到下面的问题

I: Using Apktool 2.0.3
I: Checking whether sources has changed...
I: Checking whether resources has changed...
I: Building resources...
/Users/ericsong/Documents/android_decode/crack/smalilogdemo/res/layout-v21/abc_screen_toolbar.xml:5: error: No resource identifier found for attribute 'touchscreenBlocksFocus' in package 'android'

layout-v21 里面有个属性,我们现在的framework-resource 不支持,简单粗暴的解决方法,删除 layout-v21 文件夹。删除掉这些异常的Resource 文件之后,我们就可以正常打包了。

暂时没有找到比较好的回编译的方法,如果有新的发现,会及时更新

如果每个想要添加日志的地方,都这样修改,感觉还是很繁琐,能不能偷懒下,把日志输出的Smali 语法封装成一个文件,我们只需要调用这个文件就可以了呢?这样当然是可以的。

下面贴出 LogUtil.smali 文件

.class public LLogUtil;
.super Ljava/lang/Object;
.source "LLogUtil.java"
.method public static d(Ljava/lang/String;)V
    .locals 1
    .prologue
    const-string v0,"SmaliLogDemo"
    invoke-static {v0, p0}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
    return-void
 .end method

 .method public static d()V
    .locals 1
    .prologue
    const-string v0,"SmaliLogDemo"
    invoke-static {v0,v0},Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
    return-void
 .end method
 
 .method public static e(Ljava/lang/String;)V
    .locals 1
    .prologue
    const-string v0, "SmaliLogDemo"
    invoke-static {v0, p0}, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;)I
    return-void
.end method
.method public static I(I)V
    .locals 2
 
    .prologue
 
    const-string v0, "SmaliLogDemo_I"
 
    invoke-static {p0}, Ljava/lang/String;->valueOf(I)Ljava/lang/String;
 
    move-result-object v1
 
    invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
 
    return-void
.end method

#打印Long 参数
.method public static d(J)V
    .locals 2 
    .prologue
    const-string v0, "SmaliLogDemo_Long"
    invoke-static {p0, p1}, Ljava/lang/String;->valueOf(J)Ljava/lang/String;
    move-result-object v1
    invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
    return-void
.end method


#无参数 System.out.println("SmaliLogDemo_out")
.method public static p()V
    .locals 2
    .prologue
    const-string v0,"SmaliLogDemo_out"
    sget-object v1,Ljava/lang/System;->out:Ljava/io/PrintStream;
    invoke-virtual {v1,v0},Ljava/io/PrintStream;->println(Ljava/langString;)V
    return-void
.end method

#打印boolean
.method public static p(Z)V
    .locals 2
    .prologue
    const-string v0,"SmaliLogDemo_out_boolean"
    sget-object v1,Ljava/lang/System;->out:Ljava/io/PrintStream;
    invoke-virtual {v1,p0},Ljava/io/PrintStream;->println(Z)V
    return-void
.end method

现在的封装,基本上满足日常的需求,后面会持续完善。

我们再给出添加日志之后,MainActivity 中 isLogingInfoValid() 的代码

.method private isLogingInfoValid(Ljava/lang/String;Ljava/lang/String;)Z
    .locals 5
    .param p1, "name"    # Ljava/lang/String;
    .param p2, "passwd"    # Ljava/lang/String;

    .prologue
    const/4 v3, 0x0
    const-string v4,"isLogingInfoValid"
    invoke-static {v4},LLogUtil;->d(Ljava/lang/String;)V
    .line 58
    const/4 v0, 0x0

    .line 59
    .local v0, "isValid":Z
    invoke-static {p1}, Landroid/text/TextUtils;->isEmpty(Ljava/lang/CharSequence;)Z

    move-result v1

    if-nez v1, :cond_0

    invoke-static {p2}, Landroid/text/TextUtils;->isEmpty(Ljava/lang/CharSequence;)Z

    move-result v1

    if-eqz v1, :cond_1

    .line 60
    :cond_0
    const/4 v0, 0x0
    const-string v4,"isLogingInfoValid11111111111"
    invoke-static {v4},LLogUtil;->d(Ljava/lang/String;)V
    .line 61
    const-string v1, "\u7528\u6237\u540d\u3001\u5bc6\u7801\u4e0d\u80fd\u4e3a\u7a7a"

    invoke-static {p0, v1, v3}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;

    move-result-object v1

    invoke-virtual {v1}, Landroid/widget/Toast;->show()V

    .line 68
    :goto_0
    return v0

    .line 62
    :cond_1
    invoke-virtual {p2}, Ljava/lang/String;->length()I

    move-result v1

    const/4 v2, 0x6

    if-ge v1, v2, :cond_2

    .line 63
    const/4 v0, 0x0
    const-string v4,"isLogingInfoValid2222222222222"
    invoke-static {v4},LLogUtil;->d(Ljava/lang/String;)V
    .line 64
    const-string v1, "\u5bc6\u7801\u957f\u5ea6\u5c0f\u4e8e6"

    invoke-static {p0, v1, v3}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;

    move-result-object v1

    invoke-virtual {v1}, Landroid/widget/Toast;->show()V

    goto :goto_0

    .line 66
    :cond_2
    const-string v4,"isLogingInfoValid33333333333"
    invoke-static {v4},LLogUtil;->d(Ljava/lang/String;)V
    const/4 v0, 0x1

    goto :goto_0
.end method

里面一共打印了四组日志。

我们再看下 onClick 响应事件中的日志信息。

# virtual methods
.method public onClick(Landroid/view/View;)V
    .locals 4
    .param p1, "v"    # Landroid/view/View;

    .prologue
    .line 47
    iget-object v0, p0, Lcom/imesong/smalilogdemo/MainActivity$2;->this$0:Lcom/imesong/smalilogdemo/MainActivity;

    iget-object v1, p0, Lcom/imesong/smalilogdemo/MainActivity$2;->this$0:Lcom/imesong/smalilogdemo/MainActivity;

    # getter for: Lcom/imesong/smalilogdemo/MainActivity;->nameEdit:Landroid/support/v7/widget/AppCompatEditText;
    invoke-static {v1}, Lcom/imesong/smalilogdemo/MainActivity;->access$000(Lcom/imesong/smalilogdemo/MainActivity;)Landroid/support/v7/widget/AppCompatEditText;

    move-result-object v1

    invoke-virtual {v1}, Landroid/support/v7/widget/AppCompatEditText;->getText()Landroid/text/Editable;

    move-result-object v1

    invoke-virtual {v1}, Ljava/lang/Object;->toString()Ljava/lang/String;

    move-result-object v1

    iget-object v2, p0, Lcom/imesong/smalilogdemo/MainActivity$2;->this$0:Lcom/imesong/smalilogdemo/MainActivity;

    # getter for: Lcom/imesong/smalilogdemo/MainActivity;->passwdEdit:Landroid/support/v7/widget/AppCompatEditText;
    invoke-static {v2}, Lcom/imesong/smalilogdemo/MainActivity;->access$100(Lcom/imesong/smalilogdemo/MainActivity;)Landroid/support/v7/widget/AppCompatEditText;

    move-result-object v2

    invoke-virtual {v2}, Landroid/support/v7/widget/AppCompatEditText;->getText()Landroid/text/Editable;

    move-result-object v2

    invoke-virtual {v2}, Ljava/lang/Object;->toString()Ljava/lang/String;

    move-result-object v2

    # invokes: Lcom/imesong/smalilogdemo/MainActivity;->isLogingInfoValid(Ljava/lang/String;Ljava/lang/String;)Z
    invoke-static {v0, v1, v2}, Lcom/imesong/smalilogdemo/MainActivity;->access$200(Lcom/imesong/smalilogdemo/MainActivity;Ljava/lang/String;Ljava/lang/String;)Z

    move-result v0

    if-eqz v0, :cond_0

    .line 48
    const-string v3,"commit onclick"
    invoke-static {v3},LLogUtil;->e(Ljava/lang/String;)V
    iget-object v0, p0, Lcom/imesong/smalilogdemo/MainActivity$2;->this$0:Lcom/imesong/smalilogdemo/MainActivity;

    const-string v1, "\u767b\u9646\u6210\u529f"

    const/4 v2, 0x0

    invoke-static {v0, v1, v2}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;

    move-result-object v0

    invoke-virtual {v0}, Landroid/widget/Toast;->show()V

    .line 53
    :cond_0
    return-void
.end method

最后 看下 效果图

smaliLogDemoAppLog.gif

关于SmaliLogDemo 的理解,就到这里,最后附上 项目代码和Smali 文件

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,393评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,790评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,391评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,703评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,613评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,003评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,507评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,158评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,300评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,256评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,274评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,984评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,569评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,662评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,899评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,268评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,840评论 2 339

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,259评论 25 707
  • Ubuntu的发音 Ubuntu,源于非洲祖鲁人和科萨人的语言,发作 oo-boon-too 的音。了解发音是有意...
    萤火虫de梦阅读 99,087评论 9 467
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,712评论 6 342
  • 社会可以是美好的,可以是残酷的。但如果只有一个选项那它一定是残酷的。 胜利者铸造的浮光掠影海市蜃楼不过是表象。弱肉...
    为念心安阅读 345评论 0 1
  • 前言 之前写了一篇文章是关于自定义控件的。在学习自定义view的时候顺便把安卓的touch system(安卓触摸...
    Zane96阅读 1,157评论 0 1