android逆向分析之smali练习
2015-07-15 13:19
841 查看
一、工具
第一章节分析了smali的语法
个人比较喜欢Sublime Text 2,多种语言的语法高亮,smali的语法插件,
二、java内部内访问外部类的私有属性补习
有的人不懂反编译后出现的this$0或者access$0等,如果你知道,就跳过该小节。
说起这个就要说java内部内是如何访问外部类的私有属性(private)的。
注意一点:java源码经过编译后无论是外部类还是内部类都是单独的文件,只不过内部类和它的外部类在同一目录。
OutClass源码
OutClass的反射后的代码
看的出多了几个access$#的方法。其实access$#和一般的方法一样,但是它是static ,可以通过类名直接访问。
Inner01反射后的代码
Inner01是静态内部类,不会有外部类的引用,所以在构造函数里就不会有OutClass这个引用。
Inner02反射后的代码
Inner02是非静态的,当我们Inner02 inner = new Inner02( )的时候,其实早就换成了
OutClass$Inner02 inner = new OutClass$Inner02(this);
这样非静态的内部类就把外部类的引用传递进来了,保存在this$0 这个变量中。
看到了吗!静态内部类与非静态内部类也不一样
为了看的更清楚,我们对class字节码进行反编译,需要知道一点jVM指令
反编译OutClass.class
反编译Inner01.class
反编译Inner02.class
非静态内部类中会有一个外部类的引用,this$0(内部类的嵌套可能会叫this$1),但是不能靠这个引用来访问外部类的私有域。那么非静态内部类是如何大摇大摆的访问外部类的私有域的呢?
1)、非静态内部类访问外部类的字段,
System.out.println(temp2);
其实就是this$0.access$4(this$0)来获取temp2的值。access$#都是静态的,所以用类的实例来调用是可以的。
2)、非静态内部类访问外部类的私有方法
setData(12);
3)、静态内部类访问外部类的字段
静态内部类只能访问外部类的静态属性(static,因为没有外部类的引用,只能靠外部类的类名访问)。
静态内部类是没有外部类的引用的
System.out.println(staticTemp5);
虽然没有引用,但是靠外部类的类名访问相应的access$#( )方法可以操控外部类的静态字段。
4)、静态内部类访问外部类的方法
只能访问外部类的静方法,依然靠access$#( )访问。
总结:静态的还是非静态的都是靠access方法访问,至于$后面的数字是根据私有属性的初始化位置来的(静态的早于非静态)。
smali里使内部类访问外部类的私有成员,编译器生成了形似 “外部类.access$XYZ”的函数。XYZ为数字。X是按照私有成员在内部类出现的顺序递增的。YZ为02的话,标明是基本变量成员;YZ为00的话标明是对象成员或者函数。
三、练习
我们拿魅族的应用中心做试验,来反编译一个可以折叠的TextView。
大致的java代码。为什么说大致,有的逻辑不是很好懂,有的地方按照我自己的逻辑写的,
正真的源码可能和我的还是有差别的。
attrs.xml
参考:http://android.blog.51cto.com/268543/384809/
http://blog.csdn.net/yuanyuan_186/article/details/41358597
JVM指令上
JVM指令下
JVM指令集
第一章节分析了smali的语法
个人比较喜欢Sublime Text 2,多种语言的语法高亮,smali的语法插件,
二、java内部内访问外部类的私有属性补习
有的人不懂反编译后出现的this$0或者access$0等,如果你知道,就跳过该小节。
说起这个就要说java内部内是如何访问外部类的私有属性(private)的。
注意一点:java源码经过编译后无论是外部类还是内部类都是单独的文件,只不过内部类和它的外部类在同一目录。
OutClass源码
public class OutClass { private int temp1 = 1; private int temp2 = 2; private int temp3 = 3; private static int staticTemp4 = 4; private static int staticTemp5 = 5; private static int staticTemp6 = 6; public int temp7 = 7; private void setData(int data) { this.temp1 = data; } static class Inner01 { void getData() { System.out.println(staticTemp4); System.out.println(staticTemp5); System.out.println(staticTemp6); } } class Inner02 { void getData() { System.out.println(temp1); System.out.println(temp2); System.out.println(temp3); System.out.println(temp7); setData(12); } } }我们在代码运行时利用反射
OutClass的反射后的代码
class OutClass { public OutClass(); private void setData(int); static int access$0(); static int access$1(); static int access$2(); static int access$3(OutClass); static int access$4(OutClass); static int access$5(OutClass); static void access$6(OutClass,int); private int temp1; private int temp2; private int temp3; private static int staticTemp4; private static int staticTemp5; private static int staticTemp6; public int temp7; }
看的出多了几个access$#的方法。其实access$#和一般的方法一样,但是它是static ,可以通过类名直接访问。
Inner01反射后的代码
Inner01是静态内部类,不会有外部类的引用,所以在构造函数里就不会有OutClass这个引用。
class OutClass$Inner01 { OutClass$Inner01(); void getData(); }
Inner02反射后的代码
Inner02是非静态的,当我们Inner02 inner = new Inner02( )的时候,其实早就换成了
OutClass$Inner02 inner = new OutClass$Inner02(this);
这样非静态的内部类就把外部类的引用传递进来了,保存在this$0 这个变量中。
class OutClass$Inner02 { OutClass$Inner02(OutClass); void getData(); final OutClass this$0; }
看到了吗!静态内部类与非静态内部类也不一样
为了看的更清楚,我们对class字节码进行反编译,需要知道一点jVM指令
反编译OutClass.class
E:\eclipse-rcp-indigo-SR1-win32-x86_64\workspace\ReflectTest\bin>javap -c OutClass.class Compiled from "OutClass.java" public class OutClass { public int temp7; static {}; Code: 0: iconst_4 1: putstatic #16 // Field staticTemp4:I 4: iconst_5 5: putstatic #18 // Field staticTemp5:I 8: bipush 6 10: putstatic #20 // Field staticTemp6:I 13: return public OutClass(); Code: 0: aload_0 //aload_0,第一个参数,就是this 1: invokespecial #25 // Method java/lang/Object."<init>":()V 4: aload_0 5: iconst_1 6: putfield #27 // Field temp1:I 9: aload_0 10: iconst_2 11: putfield #29 // Field temp2:I 14: aload_0 15: iconst_3 16: putfield #31 // Field temp3:I 19: aload_0 20: bipush 7 22: putfield #33 // Field temp7:I 25: return static int access$0(); Code: 0: getstatic #16 // Field staticTemp4:I 3: ireturn static int access$1(); Code: 0: getstatic #18 // Field staticTemp5:I 3: ireturn static int access$2(); Code: 0: getstatic #20 // Field staticTemp6:I 3: ireturn static int access$3(OutClass); Code: 0: aload_0 1: getfield #27 // Field temp1:I 4: ireturn static int access$4(OutClass); Code: 0: aload_0 1: getfield #29 // Field temp2:I 4: ireturn static int access$5(OutClass); Code: 0: aload_0 1: getfield #31 // Field temp3:I 4: ireturn static void access$6(OutClass, int); Code: 0: aload_0 1: iload_1 2: invokespecial #50 // Method setData:(I)V 5: return }
反编译Inner01.class
E:\eclipse-rcp-indigo-SR1-win32-x86_64\workspace\ReflectTest\bin>javap -c OutClass$Inner01.class Compiled from "OutClass.java" class OutClass$Inner01 { OutClass$Inner01(); Code: 0: aload_0 1: invokespecial #8 // Method java/lang/Object."<init>":()V 4: return void getData(); Code: 0: getstatic #15 // Field java/lang/System.out:Ljava/io/PrintStream; 3: invokestatic #21 // Method OutClass.access$0:()I 6: invokevirtual #27 // Method java/io/PrintStream.println:(I)V 9: getstatic #15 // Field java/lang/System.out:Ljava/io/PrintStream; 12: invokestatic #33 // Method OutClass.access$1:()I 15: invokevirtual #27 // Method java/io/PrintStream.println:(I)V 18: getstatic #15 // Field java/lang/System.out:Ljava/io/PrintStream; 21: invokestatic #36 // Method OutClass.access$2:()I 24: invokevirtual #27 // Method java/io/PrintStream.println:(I)V 27: return }
反编译Inner02.class
E:\eclipse-rcp-indigo-SR1-win32-x86_64\workspace\ReflectTest\bin>javap -c OutClass$Inner02.class Compiled from "OutClass.java" class OutClass$Inner02 { final OutClass this$0; OutClass$Inner02(OutClass); Code: 0: aload_0 1: aload_1 2: putfield #10 // Field this$0:LOutClass; 5: aload_0 6: invokespecial #12 // Method java/lang/Object."<init>":()V 9: return void getData(); Code: 0: getstatic #20 // Field java/lang/System.out:Ljava/io/PrintStream; 3: aload_0 4: getfield #10 // Field this$0:LOutClass; 7: invokestatic #26 // Method OutClass.access$3:(LOutClass;)I 10: invokevirtual #32 // Method java/io/PrintStream.println:(I)V 13: getstatic #20 // Field java/lang/System.out:Ljava/io/PrintStream; 16: aload_0 17: getfield #10 // Field this$0:LOutClass; 20: invokestatic #38 // Method OutClass.access$4:(LOutClass;)I 23: invokevirtual #32 // Method java/io/PrintStream.println:(I)V 26: getstatic #20 // Field java/lang/System.out:Ljava/io/PrintStream; 29: aload_0 30: getfield #10 // Field this$0:LOutClass; 33: invokestatic #41 // Method OutClass.access$5:(LOutClass;)I 36: invokevirtual #32 // Method java/io/PrintStream.println:(I)V 39: getstatic #20 // Field java/lang/System.out:Ljava/io/PrintStream; 42: aload_0 43: getfield #10 // Field this$0:LOutClass; 46: getfield #44 // Field OutClass.temp7:I 49: invokevirtual #32 // Method java/io/PrintStream.println:(I)V 52: aload_0 53: getfield #10 // Field this$0:LOutClass; 56: bipush 12 58: invokestatic #48 // Method OutClass.access$6:(LOutClass;I)V 61: return }
非静态内部类中会有一个外部类的引用,this$0(内部类的嵌套可能会叫this$1),但是不能靠这个引用来访问外部类的私有域。那么非静态内部类是如何大摇大摆的访问外部类的私有域的呢?
1)、非静态内部类访问外部类的字段,
System.out.println(temp2);
16: aload_0 // this指的是<span style="font-size: 18px;">Inner02</span><span style="font-size: 14px; font-family: Arial, Helvetica, sans-serif;">这个类的实例</span> 17: getfield #10 // Field this$0:LOutClass; 20: invokestatic #38 // Method OutClass.access$4:(LOutClass;)I 23: invokevirtual #32 // Method java/io/PrintStream.println:(I)V 26: getstatic #20 // Field java/lang/System.out:Ljava/io/PrintStream;
其实就是this$0.access$4(this$0)来获取temp2的值。access$#都是静态的,所以用类的实例来调用是可以的。
2)、非静态内部类访问外部类的私有方法
setData(12);
52: aload_0 //<span style="font-family: Arial, Helvetica, sans-serif;">this指的是</span><span style="font-family: Arial, Helvetica, sans-serif;"><span style="font-size:14px;">Inner02这个类的实例</span></span> 53: getfield #10 // Field this$0:LOutClass; 56: bipush 12 58: invokestatic #48 // Method OutClass.access$6:(LOutClass;I)V其实就是调用this$0.access$6(this$0,12),将参数12传递过去。
3)、静态内部类访问外部类的字段
静态内部类只能访问外部类的静态属性(static,因为没有外部类的引用,只能靠外部类的类名访问)。
静态内部类是没有外部类的引用的
System.out.println(staticTemp5);
9: getstatic #15 // Field java/lang/System.out:Ljava/io/PrintStream; 12: invokestatic #33 // Method OutClass.access$1:()I 15: invokevirtual #27 // Method java/io/PrintStream.println:(I)V
虽然没有引用,但是靠外部类的类名访问相应的access$#( )方法可以操控外部类的静态字段。
4)、静态内部类访问外部类的方法
只能访问外部类的静方法,依然靠access$#( )访问。
总结:静态的还是非静态的都是靠access方法访问,至于$后面的数字是根据私有属性的初始化位置来的(静态的早于非静态)。
smali里使内部类访问外部类的私有成员,编译器生成了形似 “外部类.access$XYZ”的函数。XYZ为数字。X是按照私有成员在内部类出现的顺序递增的。YZ为02的话,标明是基本变量成员;YZ为00的话标明是对象成员或者函数。
三、练习
我们拿魅族的应用中心做试验,来反编译一个可以折叠的TextView。
.class public Lcom/meizu/common/widget/FoldableTextView; .super Landroid/widget/TextView; .source "FoldableTextView.java" # interfaces .implements Landroid/view/View$OnClickListener; # annotations .annotation system Ldalvik/annotation/MemberClasses; value = { Lcom/meizu/common/widget/FoldableTextView$FoldingListener;, Lcom/meizu/common/widget/FoldableTextView$MoreClickSpan; } .end annotation # static fields .field private static final DEBUG:Z = false .field private static final ELLIPSIS_TWO_DOTS:Ljava/lang/String; = "\u2025" .field private static final TAG:Ljava/lang/String; = "FoldableTextView" # instance fields .field private mAlignViewEdge:Z .field private mClickToFold:Z .field private mEllipseText:Ljava/lang/CharSequence; .field private mFoldLineMax:I .field private mIsClickSpan:Z .field private mIsfolded:Z .field private mLinkColor:I .field private mListener:Lcom/meizu/common/widget/FoldableTextView$FoldingListener; .field private mUnfoldText:Ljava/lang/CharSequence; .field private mainText:Ljava/lang/CharSequence; # direct methods .method public constructor <init>(Landroid/content/Context;)V .locals 1 .param p1, "context" # Landroid/content/Context; .prologue .line 48 const/4 v0, 0x0 invoke-direct {p0, p1, v0}, Lcom/meizu/common/widget/FoldableTextView;-><init>(Landroid/content/Context;Landroid/util/AttributeSet;)V .line 49 return-void .end method .method public constructor <init>(Landroid/content/Context;Landroid/util/AttributeSet;)V .locals 1 .param p1, "context" # Landroid/content/Context; .param p2, "attrs" # Landroid/util/AttributeSet; .prologue .line 52 sget v0, Lcom/meizu/common/R$style;->Widget_MeizuCommon_FoldableTextView:I invoke-direct {p0, p1, p2, v0}, Lcom/meizu/common/widget/FoldableTextView;-><init>(Landroid/content/Context;Landroid/util/AttributeSet;I)V .line 53 return-void .end method .method public constructor <init>(Landroid/content/Context;Landroid/util/AttributeSet;I)V .locals 8 .param p1, "context" # Landroid/content/Context; .param p2, "attrs" # Landroid/util/AttributeSet; .param p3, "defStyle" # I .prologue const/4 v7, -0x1 const/4 v6, 0x1 const/4 v5, 0x0 .line 56 invoke-direct {p0, p1, p2, p3}, Landroid/widget/TextView;-><init>(Landroid/content/Context;Landroid/util/AttributeSet;I)V .line 39 iput-boolean v6, p0, Lcom/meizu/common/widget/FoldableTextView;->mIsfolded:Z .line 40 iput-boolean v5, p0, Lcom/meizu/common/widget/FoldableTextView;->mAlignViewEdge:Z .line 41 iput-boolean v5, p0, Lcom/meizu/common/widget/FoldableTextView;->mIsClickSpan:Z .line 42 iput-boolean v6, p0, Lcom/meizu/common/widget/FoldableTextView;->mClickToFold:Z .line 43 iput v7, p0, Lcom/meizu/common/widget/FoldableTextView;->mLinkColor:I .line 57 sget-object v4, Lcom/meizu/common/R$styleable;->FoldableTextView:[I invoke-virtual {p1, p2, v4, p3, v5}, Landroid/content/Context;->obtainStyledAttributes(Landroid/util/AttributeSet;[III)Landroid/content/res/TypedArray; move-result-object v0 .line 59 .local v0, "a":Landroid/content/res/TypedArray; invoke-virtual {v0}, Landroid/content/res/TypedArray;->getIndexCount()I move-result v3 .line 60 .local v3, "n":I const/4 v2, 0x0 .local v2, "i":I :goto_0 if-ge v2, v3, :cond_7 .line 61 invoke-virtual {v0, v2}, Landroid/content/res/TypedArray;->getIndex(I)I move-result v1 .line 62 .local v1, "attr":I sget v4, Lcom/meizu/common/R$styleable;->FoldableTextView_mzTextEllipse:I if-ne v1, v4, :cond_1 .line 63 invoke-virtual {v0, v1}, Landroid/content/res/TypedArray;->getText(I)Ljava/lang/CharSequence; move-result-object v4 iput-object v4, p0, Lcom/meizu/common/widget/FoldableTextView;->mEllipseText:Ljava/lang/CharSequence; .line 60 :cond_0 :goto_1 add-int/lit8 v2, v2, 0x1 goto :goto_0 .line 64 :cond_1 sget v4, Lcom/meizu/common/R$styleable;->FoldableTextView_mzTextUnfold:I if-ne v1, v4, :cond_2 .line 65 invoke-virtual {v0, v1}, Landroid/content/res/TypedArray;->getText(I)Ljava/lang/CharSequence; move-result-object v4 iput-object v4, p0, Lcom/meizu/common/widget/FoldableTextView;->mUnfoldText:Ljava/lang/CharSequence; goto :goto_1 .line 66 :cond_2 sget v4, Lcom/meizu/common/R$styleable;->FoldableTextView_mzMaxFoldLine:I if-ne v1, v4, :cond_3 .line 67 invoke-virtual {v0, v1, v5}, Landroid/content/res/TypedArray;->getInt(II)I move-result v4 iput v4, p0, Lcom/meizu/common/widget/FoldableTextView;->mFoldLineMax:I goto :goto_1 .line 68 :cond_3 sget v4, Lcom/meizu/common/R$styleable;->FoldableTextView_mzUnfoldAlignViewEdge:I if-ne v1, v4, :cond_4 .line 69 invoke-virtual {v0, v1, v5}, Landroid/content/res/TypedArray;->getBoolean(IZ)Z move-result v4 iput-boolean v4, p0, Lcom/meizu/common/widget/FoldableTextView;->mAlignViewEdge:Z goto :goto_1 .line 70 :cond_4 sget v4, Lcom/meizu/common/R$styleable;->FoldableTextView_mzClickToFold:I if-ne v1, v4, :cond_5 .line 71 invoke-virtual {v0, v1, v5}, Landroid/content/res/TypedArray;->getBoolean(IZ)Z move-result v4 iput-boolean v4, p0, Lcom/meizu/common/widget/FoldableTextView;->mClickToFold:Z goto :goto_1 .line 72 :cond_5 sget v4, Lcom/meizu/common/R$styleable;->FoldableTextView_mzLinkColor:I if-ne v1, v4, :cond_6 .line 73 invoke-virtual {v0, v1, v7}, Landroid/content/res/TypedArray;->getColor(II)I move-result v4 iput v4, p0, Lcom/meizu/common/widget/FoldableTextView;->mLinkColor:I goto :goto_1 .line 74 :cond_6 sget v4, Lcom/meizu/common/R$styleable;->FoldableTextView_mzIsFold:I if-ne v1, v4, :cond_0 .line 75 invoke-virtual {v0, v1, v6}, Landroid/content/res/TypedArray;->getBoolean(IZ)Z move-result v4 iput-boolean v4, p0, Lcom/meizu/common/widget/FoldableTextView;->mIsfolded:Z goto :goto_1 .line 78 .end local v1 # "attr":I :cond_7 invoke-virtual {v0}, Landroid/content/res/TypedArray;->recycle()V .line 79 iget-object v4, p0, Lcom/meizu/common/widget/FoldableTextView;->mUnfoldText:Ljava/lang/CharSequence; invoke-static {v4}, Landroid/text/TextUtils;->isEmpty(Ljava/lang/CharSequence;)Z move-result v4 if-eqz v4, :cond_8 .line 80 sget v4, Lcom/meizu/common/R$string;->more_item_label:I invoke-virtual {p1, v4}, Landroid/content/Context;->getString(I)Ljava/lang/String; move-result-object v4 iput-object v4, p0, Lcom/meizu/common/widget/FoldableTextView;->mUnfoldText:Ljava/lang/CharSequence; .line 85 :cond_8 iget-object v4, p0, Lcom/meizu/common/widget/FoldableTextView;->mEllipseText:Ljava/lang/CharSequence; invoke-static {v4}, Landroid/text/TextUtils;->isEmpty(Ljava/lang/CharSequence;)Z move-result v4 if-eqz v4, :cond_9 .line 86 const-string v4, "\u2025" iput-object v4, p0, Lcom/meizu/common/widget/FoldableTextView;->mEllipseText:Ljava/lang/CharSequence; .line 89 :cond_9 invoke-static {}, Landroid/text/method/LinkMovementMethod;->getInstance()Landroid/text/method/MovementMethod; move-result-object v4 invoke-virtual {p0, v4}, Lcom/meizu/common/widget/FoldableTextView;->setMovementMethod(Landroid/text/method/MovementMethod;)V .line 90 invoke-direct {p0, v6}, Lcom/meizu/common/widget/FoldableTextView;->setOnClickListener(Z)V .line 91 return-void .end method .method static synthetic access$100(Lcom/meizu/common/widget/FoldableTextView;)I .locals 1 .param p0, "x0" # Lcom/meizu/common/widget/FoldableTextView; .prologue .line 33 iget v0, p0, Lcom/meizu/common/widget/FoldableTextView;->mLinkColor:I return v0 .end method .method static synthetic access$200(Lcom/meizu/common/widget/FoldableTextView;)Lcom/meizu/common/widget/FoldableTextView$FoldingListener; .locals 1 .param p0, "x0" # Lcom/meizu/common/widget/FoldableTextView; .prologue .line 33 iget-object v0, p0, Lcom/meizu/common/widget/FoldableTextView;->mListener:Lcom/meizu/common/widget/FoldableTextView$FoldingListener; return-object v0 .end method .method static synthetic access$302(Lcom/meizu/common/widget/FoldableTextView;Z)Z .locals 0 .param p0, "x0" # Lcom/meizu/common/widget/FoldableTextView; .param p1, "x1" # Z .prologue .line 33 iput-boolean p1, p0, Lcom/meizu/common/widget/FoldableTextView;->mIsfolded:Z return p1 .end method .method static synthetic access$402(Lcom/meizu/common/widget/FoldableTextView;Z)Z .locals 0 .param p0, "x0" # Lcom/meizu/common/widget/FoldableTextView; .param p1, "x1" # Z .prologue .line 33 iput-boolean p1, p0, Lcom/meizu/common/widget/FoldableTextView;->mIsClickSpan:Z return p1 .end method .method private foldText(Ljava/lang/CharSequence;)Ljava/lang/CharSequence; .locals 14 .param p1, "text" # Ljava/lang/CharSequence; .prologue .line 205 invoke-virtual {p0}, Lcom/meizu/common/widget/FoldableTextView;->getLayout()Landroid/text/Layout; move-result-object v11 .line 206 .local v11, "layout":Landroid/text/Layout; new-instance v1, Landroid/text/SpannableStringBuilder; invoke-direct {v1, p1}, Landroid/text/SpannableStringBuilder;-><init>(Ljava/lang/CharSequence;)V .line 207 .local v1, "sb":Landroid/text/SpannableStringBuilder; new-instance v0, Landroid/text/DynamicLayout; invoke-virtual {v11}, Landroid/text/Layout;->getPaint()Landroid/text/TextPaint; move-result-object v2 invoke-virtual {v11}, Landroid/text/Layout;->getWidth()I move-result v3 invoke-virtual {v11}, Landroid/text/Layout;->getAlignment()Landroid/text/Layout$Alignment; move-result-object v4 const/high16 v5, 0x3f800000 # 1.0f const/4 v6, 0x0 const/4 v7, 0x0 invoke-direct/range {v0 .. v7}, Landroid/text/DynamicLayout;-><init>(Ljava/lang/CharSequence;Landroid/text/TextPaint;ILandroid/text/Layout$Alignment;FFZ)V .line 209 .local v0, "tmpLayout":Landroid/text/DynamicLayout; invoke-virtual {v0}, Landroid/text/DynamicLayout;->getLineCount()I move-result v2 iget v3, p0, Lcom/meizu/common/widget/FoldableTextView;->mFoldLineMax:I if-gt v2, v3, :cond_0 .line 253 .end local p1 # "text":Ljava/lang/CharSequence; :goto_0 return-object p1 .line 214 .restart local p1 # "text":Ljava/lang/CharSequence; :cond_0 iget v13, p0, Lcom/meizu/common/widget/FoldableTextView;->mFoldLineMax:I .line 215 .local v13, "lineMax":I :goto_1 const/4 v2, 0x1 if-le v13, v2, :cond_1 .line 216 add-int/lit8 v12, v13, -0x1 .line 217 .local v12, "line":I invoke-virtual {v0, v12}, Landroid/text/DynamicLayout;->getLineStart(I)I move-result v2 invoke-virtual {v0, v12}, Landroid/text/DynamicLayout;->getLineVisibleEnd(I)I move-result v3 if-ge v2, v3, :cond_4 .line 223 .end local v12 # "line":I :cond_1 add-int/lit8 v2, v13, -0x1 invoke-virtual {v0, v2}, Landroid/text/DynamicLayout;->getLineVisibleEnd(I)I move-result v10 .line 224 .local v10, "en":I iget-object v2, p0, Lcom/meizu/common/widget/FoldableTextView;->mEllipseText:Ljava/lang/CharSequence; invoke-static {v2}, Landroid/text/TextUtils;->isEmpty(Ljava/lang/CharSequence;)Z move-result v2 if-eqz v2, :cond_5 .line 225 invoke-virtual {v1}, Landroid/text/SpannableStringBuilder;->length()I move-result v2 invoke-virtual {v1, v10, v2}, Landroid/text/SpannableStringBuilder;->delete(II)Landroid/text/SpannableStringBuilder; .line 229 :goto_2 const/16 v2, 0x20 invoke-virtual {v1, v2}, Landroid/text/SpannableStringBuilder;->append(C)Landroid/text/SpannableStringBuilder; .line 231 invoke-virtual {v1}, Landroid/text/SpannableStringBuilder;->length()I move-result v8 .line 232 .local v8, "addIndex":I iget-object v2, p0, Lcom/meizu/common/widget/FoldableTextView;->mUnfoldText:Ljava/lang/CharSequence; invoke-virtual {v1, v2}, Landroid/text/SpannableStringBuilder;->append(Ljava/lang/CharSequence;)Landroid/text/SpannableStringBuilder; .line 233 new-instance v2, Lcom/meizu/common/widget/FoldableTextView$MoreClickSpan; invoke-direct {v2, p0, p1}, Lcom/meizu/common/widget/FoldableTextView$MoreClickSpan;-><init>(Lcom/meizu/common/widget/FoldableTextView;Ljava/lang/CharSequence;)V invoke-virtual {v1}, Landroid/text/SpannableStringBuilder;->length()I move-result v3 const/16 v4, 0x21 invoke-virtual {v1, v2, v8, v3, v4}, Landroid/text/SpannableStringBuilder;->setSpan(Ljava/lang/Object;III)V .line 236 if-lez v10, :cond_6 invoke-virtual {v0}, Landroid/text/DynamicLayout;->getLineCount()I move-result v2 if-le v2, v13, :cond_6 .line 237 move v9, v10 .line 239 .local v9, "delIndex":I :cond_2 add-int/lit8 v9, v9, -0x1 .line 240 add-int/lit8 v2, v9, 0x1 invoke-virtual {v1, v9, v2}, Landroid/text/SpannableStringBuilder;->delete(II)Landroid/text/SpannableStringBuilder; .line 241 if-lez v9, :cond_3 invoke-virtual {v0}, Landroid/text/DynamicLayout;->getLineCount()I move-result v2 if-gt v2, v13, :cond_2 .end local v9 # "delIndex":I :cond_3 :goto_3 move-object p1, v1 .line 253 goto :goto_0 .line 220 .end local v8 # "addIndex":I .end local v10 # "en":I .restart local v12 # "line":I :cond_4 move v13, v12 .line 221 goto :goto_1 .line 227 .end local v12 # "line":I .restart local v10 # "en":I :cond_5 invoke-virtual {v1}, Landroid/text/SpannableStringBuilder;->length()I move-result v2 iget-object v3, p0, Lcom/meizu/common/widget/FoldableTextView;->mEllipseText:Ljava/lang/CharSequence; invoke-virtual {v1, v10, v2, v3}, Landroid/text/SpannableStringBuilder;->replace(IILjava/lang/CharSequence;)Landroid/text/SpannableStringBuilder; goto :goto_2 .line 242 .restart local v8 # "addIndex":I :cond_6 iget-boolean v2, p0, Lcom/meizu/common/widget/FoldableTextView;->mAlignViewEdge:Z if-eqz v2, :cond_3 .line 243 :goto_4 invoke-virtual {v0}, Landroid/text/DynamicLayout;->getLineCount()I move-result v2 if-ne v2, v13, :cond_3 .line 244 const-string v2, " " invoke-virtual {v1, v8, v8, v2}, Landroid/text/SpannableStringBuilder;->replace(IILjava/lang/CharSequence;)Landroid/text/SpannableStringBuilder; .line 245 invoke-virtual {v0}, Landroid/text/DynamicLayout;->getLineCount()I move-result v2 if-le v2, v13, :cond_7 .line 246 add-int/lit8 v2, v8, 0x1 invoke-virtual {v1, v8, v2}, Landroid/text/SpannableStringBuilder;->delete(II)Landroid/text/SpannableStringBuilder; goto :goto_3 .line 249 :cond_7 add-int/lit8 v8, v8, 0x1 goto :goto_4 .end method .method private setOnClickListener(Z)V .locals 1 .param p1, "set" # Z .prologue .line 285 if-eqz p1, :cond_0 .line 286 invoke-virtual {p0, p0}, Lcom/meizu/common/widget/FoldableTextView;->setOnClickListener(Landroid/view/View$OnClickListener;)V .line 290 :goto_0 return-void .line 288 :cond_0 const/4 v0, 0x0 invoke-virtual {p0, v0}, Lcom/meizu/common/widget/FoldableTextView;->setOnClickListener(Landroid/view/View$OnClickListener;)V goto :goto_0 .end method # virtual methods .method public getFoldStatus()Z .locals 1 .prologue .line 155 iget-boolean v0, p0, Lcom/meizu/common/widget/FoldableTextView;->mIsfolded:Z return v0 .end method .method public onClick(Landroid/view/View;)V .locals 3 .param p1, "v" # Landroid/view/View; .prologue const/4 v2, 0x1 const/4 v1, 0x0 .line 304 iget-boolean v0, p0, Lcom/meizu/common/widget/FoldableTextView;->mIsClickSpan:Z if-eqz v0, :cond_1 .line 305 iput-boolean v1, p0, Lcom/meizu/common/widget/FoldableTextView;->mIsClickSpan:Z .line 329 :cond_0 :goto_0 return-void .line 308 :cond_1 iget-boolean v0, p0, Lcom/meizu/common/widget/FoldableTextView;->mClickToFold:Z if-eqz v0, :cond_0 .line 312 iget-boolean v0, p0, Lcom/meizu/common/widget/FoldableTextView;->mIsfolded:Z if-eqz v0, :cond_3 .line 314 iget-object v0, p0, Lcom/meizu/common/widget/FoldableTextView;->mListener:Lcom/meizu/common/widget/FoldableTextView$FoldingListener; if-eqz v0, :cond_2 iget-object v0, p0, Lcom/meizu/common/widget/FoldableTextView;->mListener:Lcom/meizu/common/widget/FoldableTextView$FoldingListener; invoke-interface {v0, p0, v1}, Lcom/meizu/common/widget/FoldableTextView$FoldingListener;->onFolding(Lcom/meizu/common/widget/FoldableTextView;Z)Z move-result v0 if-eqz v0, :cond_0 .line 317 :cond_2 iput-boolean v1, p0, Lcom/meizu/common/widget/FoldableTextView;->mIsfolded:Z .line 318 iget-object v0, p0, Lcom/meizu/common/widget/FoldableTextView;->mainText:Ljava/lang/CharSequence; sget-object v1, Landroid/widget/TextView$BufferType;->NORMAL:Landroid/widget/TextView$BufferType; invoke-virtual {p0, v0, v1}, Lcom/meizu/common/widget/FoldableTextView;->setText(Ljava/lang/CharSequence;Landroid/widget/TextView$BufferType;)V goto :goto_0 .line 322 :cond_3 iget-object v0, p0, Lcom/meizu/common/widget/FoldableTextView;->mListener:Lcom/meizu/common/widget/FoldableTextView$FoldingListener; if-eqz v0, :cond_4 iget-object v0, p0, Lcom/meizu/common/widget/FoldableTextView;->mListener:Lcom/meizu/common/widget/FoldableTextView$FoldingListener; invoke-interface {v0, p0, v2}, Lcom/meizu/common/widget/FoldableTextView$FoldingListener;->onFolding(Lcom/meizu/common/widget/FoldableTextView;Z)Z move-result v0 if-eqz v0, :cond_0 .line 325 :cond_4 iput-boolean v2, p0, Lcom/meizu/common/widget/FoldableTextView;->mIsfolded:Z .line 326 invoke-virtual {p0}, Lcom/meizu/common/widget/FoldableTextView;->requestLayout()V .line 327 invoke-virtual {p0}, Lcom/meizu/common/widget/FoldableTextView;->invalidate()V goto :goto_0 .end method .method protected onMeasure(II)V .locals 7 .param p1, "widthMeasureSpec" # I .param p2, "heightMeasureSpec" # I .prologue const/4 v6, 0x0 .line 179 invoke-super {p0, p1, p2}, Landroid/widget/TextView;->onMeasure(II)V .line 180 iget-boolean v3, p0, Lcom/meizu/common/widget/FoldableTextView;->mIsfolded:Z if-eqz v3, :cond_0 iget v3, p0, Lcom/meizu/common/widget/FoldableTextView;->mFoldLineMax:I if-gtz v3, :cond_1 .line 202 :cond_0 :goto_0 return-void .line 184 :cond_1 invoke-virtual {p0}, Lcom/meizu/common/widget/FoldableTextView;->getText()Ljava/lang/CharSequence; move-result-object v2 .line 185 .local v2, "text":Ljava/lang/CharSequence; iput-object v2, p0, Lcom/meizu/common/widget/FoldableTextView;->mainText:Ljava/lang/CharSequence; .line 186 iget-object v3, p0, Lcom/meizu/common/widget/FoldableTextView;->mainText:Ljava/lang/CharSequence; if-eqz v3, :cond_2 iget-object v3, p0, Lcom/meizu/common/widget/FoldableTextView;->mainText:Ljava/lang/CharSequence; instance-of v3, v3, Landroid/text/Spanned; if-eqz v3, :cond_2 .line 187 iget-object v3, p0, Lcom/meizu/common/widget/FoldableTextView;->mainText:Ljava/lang/CharSequence; check-cast v3, Landroid/text/Spanned; iget-object v4, p0, Lcom/meizu/common/widget/FoldableTextView;->mainText:Ljava/lang/CharSequence; invoke-interface {v4}, Ljava/lang/CharSequence;->length()I move-result v4 const-class v5, Lcom/meizu/common/widget/FoldableTextView$MoreClickSpan; invoke-interface {v3, v6, v4, v5}, Landroid/text/Spanned;->getSpans(IILjava/lang/Class;)[Ljava/lang/Object; move-result-object v1 check-cast v1, [Lcom/meizu/common/widget/FoldableTextView$MoreClickSpan; .line 188 .local v1, "spans":[Lcom/meizu/common/widget/FoldableTextView$MoreClickSpan; if-eqz v1, :cond_2 array-length v3, v1 if-lez v3, :cond_2 .line 189 aget-object v3, v1, v6 # getter for: Lcom/meizu/common/widget/FoldableTextView$MoreClickSpan;->mText:Ljava/lang/CharSequence; invoke-static {v3}, Lcom/meizu/common/widget/FoldableTextView$MoreClickSpan;->access$000(Lcom/meizu/common/widget/FoldableTextView$MoreClickSpan;)Ljava/lang/CharSequence; move-result-object v3 iput-object v3, p0, Lcom/meizu/common/widget/FoldableTextView;->mainText:Ljava/lang/CharSequence; .line 193 .end local v1 # "spans":[Lcom/meizu/common/widget/FoldableTextView$MoreClickSpan; :cond_2 iget-object v3, p0, Lcom/meizu/common/widget/FoldableTextView;->mainText:Ljava/lang/CharSequence; invoke-direct {p0, v3}, Lcom/meizu/common/widget/FoldableTextView;->foldText(Ljava/lang/CharSequence;)Ljava/lang/CharSequence; move-result-object v0 .line 194 .local v0, "dst":Ljava/lang/CharSequence; invoke-static {v2, v0}, Landroid/text/TextUtils;->equals(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Z move-result v3 if-nez v3, :cond_0 .line 196 iget-object v3, p0, Lcom/meizu/common/widget/FoldableTextView;->mListener:Lcom/meizu/common/widget/FoldableTextView$FoldingListener; if-eqz v3, :cond_3 iget-object v3, p0, Lcom/meizu/common/widget/FoldableTextView;->mListener:Lcom/meizu/common/widget/FoldableTextView$FoldingListener; const/4 v4, 0x1 invoke-interface {v3, p0, v4}, Lcom/meizu/common/widget/FoldableTextView$FoldingListener;->onFolding(Lcom/meizu/common/widget/FoldableTextView;Z)Z move-result v3 if-eqz v3, :cond_0 .line 199 :cond_3 sget-object v3, Landroid/widget/TextView$BufferType;->SPANNABLE:Landroid/widget/TextView$BufferType; invoke-virtual {p0, v0, v3}, Lcom/meizu/common/widget/FoldableTextView;->setText(Ljava/lang/CharSequence;Landroid/widget/TextView$BufferType;)V .line 200 invoke-super {p0, p1, p2}, Landroid/widget/TextView;->onMeasure(II)V goto :goto_0 .end method .method public setClickToFold(Z)V .locals 1 .param p1, "enabled" # Z .prologue .line 133 if-eqz p1, :cond_0 .line 134 const/4 v0, 0x1 iput-boolean v0, p0, Lcom/meizu/common/widget/FoldableTextView;->mClickToFold:Z .line 138 :goto_0 return-void .line 136 :cond_0 const/4 v0, 0x0 iput-boolean v0, p0, Lcom/meizu/common/widget/FoldableTextView;->mClickToFold:Z goto :goto_0 .end method .method public setFoldStatus(Z)V .locals 1 .param p1, "fold" # Z .prologue .line 164 iget-boolean v0, p0, Lcom/meizu/common/widget/FoldableTextView;->mIsfolded:Z if-eq v0, p1, :cond_0 .line 165 iput-boolean p1, p0, Lcom/meizu/common/widget/FoldableTextView;->mIsfolded:Z .line 166 invoke-virtual {p0}, Lcom/meizu/common/widget/FoldableTextView;->requestLayout()V .line 167 invoke-virtual {p0}, Lcom/meizu/common/widget/FoldableTextView;->invalidate()V .line 169 :cond_0 return-void .end method .method public setFoldText(Ljava/lang/String;Ljava/lang/String;Z)V .locals 0 .param p1, "strEllipse" # Ljava/lang/String; .param p2, "strUnfold" # Ljava/lang/String; .param p3, "alignViewEdge" # Z .prologue .line 101 iput-boolean p3, p0, Lcom/meizu/common/widget/FoldableTextView;->mAlignViewEdge:Z .line 102 if-eqz p1, :cond_0 .line 103 iput-object p1, p0, Lcom/meizu/common/widget/FoldableTextView;->mEllipseText:Ljava/lang/CharSequence; .line 105 :cond_0 if-eqz p2, :cond_1 .line 106 iput-object p2, p0, Lcom/meizu/common/widget/FoldableTextView;->mUnfoldText:Ljava/lang/CharSequence; .line 110 :cond_1 return-void .end method .method public setFolding(ILcom/meizu/common/widget/FoldableTextView$FoldingListener;)V .locals 1 .param p1, "lineMax" # I .param p2, "l" # Lcom/meizu/common/widget/FoldableTextView$FoldingListener; .prologue .line 120 iput p1, p0, Lcom/meizu/common/widget/FoldableTextView;->mFoldLineMax:I .line 121 iput-object p2, p0, Lcom/meizu/common/widget/FoldableTextView;->mListener:Lcom/meizu/common/widget/FoldableTextView$FoldingListener; .line 122 iget v0, p0, Lcom/meizu/common/widget/FoldableTextView;->mFoldLineMax:I if-lez v0, :cond_0 .line 123 invoke-virtual {p0}, Lcom/meizu/common/widget/FoldableTextView;->requestLayout()V .line 124 invoke-virtual {p0}, Lcom/meizu/common/widget/FoldableTextView;->invalidate()V .line 126 :cond_0 return-void .end method .method public setLinkColor(I)V .locals 0 .param p1, "color" # I .prologue .line 146 iput p1, p0, Lcom/meizu/common/widget/FoldableTextView;->mLinkColor:I .line 147 invoke-virtual {p0}, Lcom/meizu/common/widget/FoldableTextView;->invalidate()V .line 148 return-void .end method .method public setText(Ljava/lang/CharSequence;Landroid/widget/TextView$BufferType;)V .locals 0 .param p1, "text" # Ljava/lang/CharSequence; .param p2, "type" # Landroid/widget/TextView$BufferType; .prologue .line 173 invoke-super {p0, p1, p2}, Landroid/widget/TextView;->setText(Ljava/lang/CharSequence;Landroid/widget/TextView$BufferType;)V .line 174 invoke-virtual {p0}, Lcom/meizu/common/widget/FoldableTextView;->requestLayout()V .line 175 return-void .end method
大致的java代码。为什么说大致,有的逻辑不是很好懂,有的地方按照我自己的逻辑写的,
正真的源码可能和我的还是有差别的。
public class FoldableTextView extends TextView implements View.OnClickListener { private static final String ELLIPSIS_TWO_DOTS = "‥"; /** 是否边缘对齐 */ private boolean mAlignViewEdge = false; /** 是否能折叠 */ private boolean mClickToFold = true; private CharSequence mEllipseText; //.. private CharSequence mUnfoldText; //更多 /**折叠后显示的最多行数*/ private int mFoldLineMax; //显示的最多行数 private boolean mIsClickSpan = false; /**是否是折叠状态*/ private boolean mIsfolded = true; private int mLinkColor = -1; private FoldingListener mListener; private CharSequence mainText; public FoldableTextView(Context paramContext) { this(paramContext, null); } public FoldableTextView(Context paramContext, AttributeSet paramAttributeSet) { this(paramContext, paramAttributeSet, R.style.Widget_MeizuCommon_FoldableTextView); } public FoldableTextView(Context context, AttributeSet paramAttributeSet, int paramInt) { super(context, paramAttributeSet, paramInt); TypedArray localTypedArray = context.obtainStyledAttributes( paramAttributeSet, R.styleable.FoldableTextView, paramInt, 0); mEllipseText = localTypedArray .getText(R.styleable.FoldableTextView_mzTextEllipse); mUnfoldText = localTypedArray .getText(R.styleable.FoldableTextView_mzTextUnfold); mFoldLineMax = localTypedArray.getInt( R.styleable.FoldableTextView_mzMaxFoldLine, 0); mAlignViewEdge = localTypedArray.getBoolean( R.styleable.FoldableTextView_mzUnfoldAlignViewEdge, false); mClickToFold = localTypedArray.getBoolean( R.styleable.FoldableTextView_mzClickToFold, false); mLinkColor = localTypedArray.getColor( R.styleable.FoldableTextView_mzLinkColor, -1); mIsfolded = localTypedArray.getBoolean( R.styleable.FoldableTextView_mzIsFold, true); localTypedArray.recycle(); if (TextUtils.isEmpty(this.mUnfoldText)) { this.mUnfoldText = context.getString(R.string.more_item_label); } if (TextUtils.isEmpty(this.mEllipseText)) { this.mEllipseText = ELLIPSIS_TWO_DOTS; } setMovementMethod(LinkMovementMethod.getInstance()); setOnClickListener(true); } SpannableStringBuilder sb ; private CharSequence foldText(CharSequence text) { if(sb!=null && sb.length()>0) return sb; sb = new SpannableStringBuilder(text); Layout layout = this.getLayout(); DynamicLayout tmpLayout = new DynamicLayout(sb, layout.getPaint(), layout.getWidth(), layout.getAlignment(), 1.0f, 0, false); if (tmpLayout.getLineCount() <= mFoldLineMax) return sb; //行数是按照0开始计数的?如果是则返回偏移值。偏移值是按1开始计数的, //如果想获取该返回值那里 的字符(或者字符串中的位置),记得-1 int en = tmpLayout.getLineVisibleEnd(mFoldLineMax-1); MoreClickSpan span = new MoreClickSpan(sb); if (!TextUtils.isEmpty(mEllipseText)) { sb.replace(en, sb.length(), mEllipseText); }else { sb.delete(en, sb.length()); } sb.append(" "); int addIndex = sb.length(); sb.append(mUnfoldText); sb.setSpan(span, addIndex, sb.length(), Spanned.SPAN_POINT_MARK); if(!mAlignViewEdge) return sb; while (tmpLayout.getLineCount() <= mFoldLineMax) { sb.replace(addIndex, addIndex, " "); addIndex++ ; } sb.delete(addIndex-1, addIndex); return sb; } private void setOnClickListener(boolean paramBoolean) { if (paramBoolean) { setOnClickListener(this); return; } setOnClickListener(null); } public boolean getFoldStatus() { return mIsfolded; } @Override public void onClick(View view) { if(mIsClickSpan||!mClickToFold){ mIsClickSpan = false; return; } if (mIsfolded) { //已经折叠了,更改状态 mIsfolded = false; setText(mainText, TextView.BufferType.NORMAL); if (mListener != null) mListener.onFolding(this, false); } else { mIsfolded = true; setText(foldText(mainText)); if (mListener != null) mListener.onFolding(this, true); } } protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (!mIsfolded || mFoldLineMax <= 0)return; if(mainText == null || TextUtils.isEmpty(getText()))mainText = getText(); if (mainText != null && (mainText instanceof Spanned)) { CharSequence dst = foldText(mainText); if (!TextUtils.equals(mainText, dst) || (mListener != null && mListener.onFolding(this, true))) { setText(dst, TextView.BufferType.SPANNABLE); } } } public void setClickToFold(boolean clickToFold) { mClickToFold = clickToFold; } public void setFoldStatus(boolean foldStatus) { if (this.mIsfolded != foldStatus) { this.mIsfolded = foldStatus; requestLayout(); invalidate(); } } public void setFoldText(String ellipseText, String unfoldText, boolean isAlignViewEdge) { this.mAlignViewEdge = isAlignViewEdge; if (ellipseText != null) { mEllipseText = ellipseText; } if (unfoldText != null) { mUnfoldText = unfoldText; } } public void setFolding(int foldLineMax, FoldingListener listener) { this.mFoldLineMax = foldLineMax; this.mListener = listener; if (mFoldLineMax > 0) { requestLayout(); invalidate(); } } public void setLinkColor(int paramInt) { this.mLinkColor = paramInt; invalidate(); } @Override public void setText(CharSequence paramCharSequence, TextView.BufferType paramBufferType) { super.setText(paramCharSequence, paramBufferType); requestLayout(); } public static abstract interface FoldingListener { public abstract boolean onFolding(FoldableTextView textView, boolean isfolded); } private class MoreClickSpan extends ClickableSpan { private final CharSequence mText; public MoreClickSpan(CharSequence text) { this.mText = text; } @Override public void onClick(View view) { if ((mListener != null)&& (!mListener.onFolding(FoldableTextView.this, false))) { return; } //不是折叠状态 mIsfolded = false; setText(mText, TextView.BufferType.NORMAL); mIsClickSpan = true; } @Override public void updateDrawState(TextPaint textPaint) { if (mLinkColor == -1) { textPaint.setColor(textPaint.linkColor); return; } textPaint.setColor(mLinkColor); } } }
attrs.xml
<declare-styleable name="FoldableTextView"> <attr name="mzTextEllipse" format="string" /> <attr name="mzTextUnfold" format="string" /> <attr name="mzMaxFoldLine" format="integer" /> <attr name="mzUnfoldAlignViewEdge" format="boolean" /> <attr name="mzClickToFold" format="boolean" /> <attr name="mzLinkColor" format="color" /> <attr name="mzIsFold" format="boolean" /> </declare-styleable>
参考:http://android.blog.51cto.com/268543/384809/
http://blog.csdn.net/yuanyuan_186/article/details/41358597
JVM指令上
JVM指令下
JVM指令集
相关文章推荐
- Android Studio Mac 10.5+快捷键使用
- 代码二次封装-xUtils(android)
- Android 最火的快速开发框架AndroidAnnotations使用详解
- android 开源框架xUtils
- Android 利用Theme自定义Activity间的切换动画
- Android移动view动画问题
- Android反编译代码和防止反编译
- Android入门(54)——第九章 使用GestureOverlayView进行手势识别
- android之XListview,上拉下拉出现BUG
- Android应用开发学习—Toast使用方法大全
- Android include布局文件 使用错误
- Android 启动页面弹出效果
- 【AndroidStudio】AndroidStudio如何连接手机
- Android 快速开发框架AndroidAnnotations使用详解
- android数据库SQLiteOpenHelper
- Android使用Activity用作弹出式对话框
- Android的onTouch事件分发机制
- android之ActionBarActivity的用法
- android 录制视频(环信)
- Android ORMLite ForeignCollection关联外部集合