APK 简析

Posted Only-xiaoxiao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了APK 简析相关的知识,希望对你有一定的参考价值。

简要分析apk 压缩包里各个目录的功能

Apk结构

apk 【Android Package】,实质是一个压缩文件,将.apk后缀改为.zip即可解压,获取其中文件。

文件 注释
assets目录 存放APK的静态资源文件,比如视频,音频,图片等
lib 目录 armeabi-v7a基本通用所有android设备,arm64-v8a只适用于64位的android设备,x86常见用于android模拟器,其目录下的.so文件是c或c++编译的动态链接库文件【逆向分析时要注意.so文件对应的架构】
META-INF目录 保存应用的签名信息,签名信息可以验证APK文件的完整性。用来保证apk包的完整性和系统的安全。【验证文件是否被修改】
res目录 res目录存放资源文件,包括图片,字符串等等,APK的界面由其中的layout文件设计
AndroidMainfest.xml文件 APK的应用清单信息,它描述了应用的名字,版本,权限,引用的库文件等等信息
classes.dex文件 在Android系统中,.dex文件是可以直接在Dalvik虚拟机中加载运行的文件【类似于.class文件与JVM】。而.dex是java源码编译后生成的java字节码文件,APK运行时的主要逻辑。【与ELF、PE文件相当】
resources.arsc文件 resources.arsc是编译后的二进制资源文件。通常本地化、汉化资源存储在该文件文件中。它是一个映射表,映射着资源和id,通过资源文件中的id就可以找到对应的资源。【汉化处理的关键】

Android四大组件

安卓四大组件【活动、服务、内容提供者、广播接收者】

Activity

  • 一个Activity通常就是一个单独的屏幕(窗口)。
  • Activity之间通过Intent进行通信。
  • android应用中每一个Activity都必须要在AndroidManifest.xml配置文件中声明,否则系统将不识别也不执行该Activity。

Service

和Activity一样,它有自己的生命周期,创建配置方式也很相似。不同点在于,Service长期运行于后台,用于执行长期运行但并不和用户交互的任务。

所以当某个程序组件需要在运行时需要与用户进行交互,需要提供某种界面时,这时就是用Activity;如果不需要与用户交互,只需要运行于后台,像后台下载东西或是后台播放音乐等等,此时就应该考虑使用Service。

ContentProvider

  • android平台提供了Content Provider使一个应用程序的指定数据集提供给其他应用程序。其他应用可以通过ContentResolver类从该内容提供者中获取或存入数据。
  • 只有需要在多个应用程序间共享数据是才需要内容提供者。例如,通讯录数据被多个应用程序使用,且必须存储在一个内容提供者中。它的好处是统一数据访问方式。
  • ContentProvider实现数据共享。ContentProvider用于保存和获取数据,并使其对所有应用程序可见。这是不同应用程序间共享数据的唯一方式,因为android没有提供所有应用共同访问的公共存储区。
  • 开发人员不会直接使用ContentProvider类的对象,大多数是通过ContentResolver对象实现对ContentProvider的操作。
  • ContentProvider使用URI来唯一标识其数据集,这里的URI以content:// 作为前缀,表示该数据由ContentProvider来管理。

Broadcast Receiver

(1)你的应用可以使用它对外部事件进行过滤,只对感兴趣的外部事件(如当电话呼入时,或者数据网络可用时)进行接收并做出响应。广播接收器没有用户界面。然而,它们可以启动一个activity或serice来响应它们收到的信息,或者用NotificationManager来通知用户。通知可以用很多种方式来吸引用户的注意力,例如闪动背灯、震动、播放声音等。一般来说是在状态栏上放一个持久的图标,用户可以打开它并获取消息。

(2)广播接收者的注册有两种方法,分别是程序动态注册和AndroidManifest文件中进行静态注册。

(3)动态注册广播接收器特点是当用来注册的Activity关掉后,广播也就失效了。静态注册无需担忧广播接收器是否被关闭,只要设备是开启状态,广播接收器也是打开着的。也就是说哪怕app本身未启动,该app订阅的广播在触发时也会对它起作用。

(4)Android还有一套本地广播机制,就是为了解决广播的安全问题,因为系统全局广播可以被其他任何程序接收到,一些携带关键性数据的广播就可能被其他应用程序截获。而本地广播机制发出的广播只能在应用程序的内部进行传递,并且只能接收来自本应用程序的广播,这样就不存在安全问题了。

(5)今天了解了Android的静态注册和动态注册,Android在8.0以后,为了提高效率,删除了静态注册,防止关闭App后广播还在,造成内存泄漏。现在静态注册的广播需要指定包名,而动态注册就没有这个问题。并且,无论是静态注册广播还是动态注册广播,在接收广播的时候都不能拦截广播,否则会报错。 谷歌官网的原文是:应用无法使用其清单注册大部分隐式广播。不过,是不能对大部分的广播进行注册,但还是有些广播可以进行静态注册的,比如对接收Android开机的广播通过静态注册还是能够正常接收的。

AndroidManifest.xml

AndroidManifest.xml文件是整个应用程序的信息描述文件,定义了应用程序中包含的Activity,Service,Content provider和BroadcastReceiver组件信息。

每个应用程序在根目录下必须包含一个AndroidManifest.xml文件,且文件名不能修改。它描述了package中暴露的组件,他们各自的实现类,各种能被处理的数据和启动位置。

Manifest:属性

属性 定义
xmlns:android 定义android命名空间,一般为http://schemas.android.com/apk/res/android,使得Android中各种标准属性能在文件中使用,提供大部分元素中的数据。
package 指定本应用内java主程序包的包名,它也是一个应用进程的默认名称。【逆向分析时的首要关注点】
sharedUserId 表明数据权限,因为默认情况下,Android给每个APK分配一个唯一的UserID,所以是默认禁止不同APK访问共享数据的。若要共享数据,第一可以采用Share Preference方法,第二种就可以采用sharedUserId了,将不同APK的sharedUserId都设为一样,则这些APK之间就可以互相共享数据了。
sharedUserLabel 共享的用户名,它只有在设置了sharedUserId属性的前提下才会有意义。
versionCode 提供给设备程序识别版本(升级)用的,必须是一个interger值代表app更新过多少次。
versionName 提供给用户,如APP版本号设置为1.1版,后续更新版本设置为1.2、2.0版本等

Application:属性

属性 定义
android:allowClearUserData 用户是否能选择自行清除数据,默认为true
android:debuggable 当设置为true时,表明该APP在手机上可以被调试。默认为false,在false的情况下不能调试该APP。【逆向分析时需要自行修改或者添加的部分,方便动调调试】
android:icon 声明整个APP的图标,图片一般都放在drawable文件夹下。
android:name 为应用程序所实现的Application子类的全名。当应用程序进程开始时,该类在所有应用程序组件之前被实例化。
android:presistent 该应用程序是否应该在任何时候都保持运行状态,默认为false。
android:process 应用程序运行的进程名,它的默认值为元素里设置的包名,当然每个组件都可以通过设置该属性来覆盖默认值。如果你想两个应用程序共用一个进程的话,你可以设置他们的android:process相同,但前提条件是他们共享一个用户ID以及被赋予了相同证书。

Activity:属性

略【】

Service:属性

【1】service与activity同级,与activity不同的是,它不能自己启动的,运行在后台的程序,如果我们退出应用时,Service进程并没有结束,它仍然在后台运行。比如听音乐,网络下载数据等,都是由service运行的

【2】service生命周期:Service只继承了onCreate(),onStart(),onDestroy()三个方法,第一次启动Service时,先后调用了onCreate(),onStart()这两个方法,当停止Service时,则执行onDestroy()方法,如果Service已经启动了,当我们再次启动Service时,不会在执行onCreate()方法,而是直接执行onStart()方法

【3】service与activity间的通信

Service后端的数据最终还是要呈现在前端Activity之上的,因为启动Service时,系统会重新开启一个新的进程,这就涉及到不同进程间通信的问题了(AIDL),Activity与service间的通信主要用IBinder负责。

Receiver:属性
Provider:属性
meta-data:属性
activity-alias:属性

DEX 文件结构

Dex文件的整体结构如下,文件头指定了一些属性,索引区记录着一些偏移和索引,数据区用于存放真正所需要的数据。

具体定义可见Cross Reference: /dalvik/libdex/DexFile.h (androidxref.com)

使用010 Editor的dex.bt 模板可以对dex文件进行解析。

双开及原理

双开:简单来说,就是手机同时运行两个或多个相同的应用,例如同时运行两个微信

原理 解释
修改包名 让手机系统认为这是2个APP,这样的话就能生成2个数据存储路径,此时的多开就等于你打开了两个互不干扰的APP
修改Framework 对于有系统修改权限的厂商,可以修改Framework来实现双开的目的,例如:小米自带多开
通过虚拟化技术实现 虚拟Framework层、虚拟文件系统、模拟Android对组件的管理、虚拟应用进程管理 等一整套虚拟技术,将APK复制一份到虚拟空间中运行,例如:平行空间
以插件机制运行 利用反射替换,动态代过滤理,hook了系统的大部分与system—server进程通讯的函数,以此作为“欺上瞒下”的目的,欺骗系统“以为”只有一个apk在运行,瞒过插件让其“认为”自己已经安装。例如:VirtualApp

汉化APK

【汉化:使用专门的工具对外文版的软件资源进行读取、翻译、修改、回写等一系列处理,使软件的菜单、对话框、提示等用户界面显示为中文,而程序的内核和功能保持不变,这个过程即为软件汉化】

这里还需要注意的是,如果要直装应用,那就应该先签名安装,看看是否有签名校验导致的闪退

学习参考链接:
吾爱破解安卓逆向入门教程《安卓逆向这档事》二、初识APK文件结构、双开、汉化、基础修改 (qq.com)
Android逆向笔记 —— DEX 文件格式解析 - 知乎 (zhihu.com)
(200条消息) Android四大组件(整理相关知识点)_安卓四大组件_xchaha的博客-CSDN博客

【做学习总结之用,侵权联系即删】

简析静态xml布局如何通过动态代码实现

先看一下xml代码:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    android:id="@+id/contentView"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">

    <!--静态xml加载部分-->
    <LinearLayout
        android:id="@+id/ll01"
        android:layout_width="0dp"
        android:background="@android:color/holo_orange_dark"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:orientation="vertical">

        <TextView
            android:id="@+id/tv_static_loading"
            android:layout_width="150dp"
            android:layout_height="wrap_content"
            android:layout_gravity="right"
            android:layout_marginTop="10dp"
            android:text="我是静态xml加载的布局"/>

        <TextView
            android:layout_width="150dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:gravity="center"
            android:text="静态xml加载的布局"/>

        <Button
            android:layout_width="150dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:gravity="left|center"
            android:text="按钮1"/>

        <ImageView
            android:layout_width="150dp"
            android:layout_height="150dp"
            android:scaleType="fitCenter"
            android:src="@mipmap/q01"/>

        <TextView
            android:id="@+id/tv_margin"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:background="#000"
            android:text="marginLeft为10dp"
            android:textColor="#fff"
            android:textSize="20sp"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingLeft="20dp"
            android:background="#000"
            android:text="padLeft为20dp"
            android:textColor="#fff"
            android:textSize="20sp"/>
    </LinearLayout>

    <!--动态xml加载部分-->
  <!--  <LinearLayout
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="wrap_content">

    </LinearLayout>-->

</LinearLayout>



 
<LinearLayout
        android:id="@+id/ll01"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:orientation="vertical">
将会从此处开始用动态代码重复下面的写法,当然也少不了一些说明。

整体架构如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    android:id="@+id/contentView"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">

    <!--静态xml加载部分-->
    <LinearLayout
        android:id="@+id/ll01"
        android:layout_width="0dp"
        android:background="@android:color/holo_orange_dark"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:orientation="vertical">

        <TextView
            android:id="@+id/tv_static_loading"
            android:layout_width="150dp"
            android:layout_height="wrap_content"
            android:layout_gravity="right"
            android:layout_marginTop="10dp"
            android:text="我是静态xml加载的布局"/>

        <TextView
            android:layout_width="150dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:gravity="center"
            android:text="静态xml加载的布局"/>

        <Button
            android:layout_width="150dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:gravity="left|center"
            android:text="按钮1"/>

        <ImageView
            android:layout_width="150dp"
            android:layout_height="150dp"
            android:scaleType="fitCenter"
            android:src="@mipmap/q01"/>

        <TextView
            android:id="@+id/tv_margin"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:background="#000"
            android:text="marginLeft为10dp"
            android:textColor="#fff"
            android:textSize="20sp"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingLeft="20dp"
            android:background="#000"
            android:text="padLeft为20dp"
            android:textColor="#fff"
            android:textSize="20sp"/>
    </LinearLayout>

    <!--动态xml加载部分-->
  <!--  <LinearLayout
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="wrap_content">

    </LinearLayout>-->

</LinearLayout>





会看到布局文件中有宽度和字体大小单位分别是dp和sp,然而动态加载时,只能以px为单位,因此,我们先把单位初始化为px单位,看mian.java:

package com.demo.linearlayoutdemo;

import android.graphics.Color;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.TypedValue;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;

public class MainLLDemoActivity extends AppCompatActivity {
    /**
     * 150dp对应的px值
     */
    private int on150Dp;
    /**
     * 1dp = ?px
     */
    private int perDp;

    private int on10Dp;

    private int on20Dp;

    private float on20Sp ;

    private TextView tvStaticLoading;
    private TextView tvMargin;
    private ViewGroup contentView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_lldemo);
        tvStaticLoading = (TextView) findViewById(R.id.tv_static_loading);
        tvMargin = (TextView) findViewById(R.id.tv_margin);
        contentView = (ViewGroup) findViewById(R.id.contentView);
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        on150Dp = tvStaticLoading.getWidth();
        perDp = on150Dp / 150;
        on10Dp = perDp * 10;
        on20Dp = perDp * 20;
        on20Sp = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 20,
                getResources().getDisplayMetrics());
    }

}

此处,我们分2个方法拿到dp和sp,在这里会看到,博主是

@Override
    public void onWindowFocusChanged(boolean hasFocus) {

    }
在这个方法里面取值,因为在onCreate中是取不到值得。看看以下这个与众不同的取法。

     on20Sp = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 20, 
            getResources().getDisplayMetrics());
这个方法也是用于将dp和sp转换成对应的尺寸,看看TypedValue.applyDimension做了啥:

    /**
     * Converts an unpacked complex data value holding a dimension to its final floating 
     * point value. The two parameters <var>unit</var> and <var>value</var>
     * are as in {@link #TYPE_DIMENSION}.
     *  
     * @param unit The unit to convert from.
     * @param value The value to apply the unit to.
     * @param metrics Current display metrics to use in the conversion -- 
     *                supplies display density and scaling information.
     * 
     * @return The complex floating point value multiplied by the appropriate 
     * metrics depending on its unit. 
     */
    public static float applyDimension(int unit, float value,
                                       DisplayMetrics metrics)
    {
        switch (unit) {
        case COMPLEX_UNIT_PX:
            return value;
        case COMPLEX_UNIT_DIP:
            return value * metrics.density;
        case COMPLEX_UNIT_SP:
            return value * metrics.scaledDensity;
        case COMPLEX_UNIT_PT:
            return value * metrics.xdpi * (1.0f/72);
        case COMPLEX_UNIT_IN:
            return value * metrics.xdpi;
        case COMPLEX_UNIT_MM:
            return value * metrics.xdpi * (1.0f/25.4f);
        }
        return 0;
    }
可以看出,他是将对应的px,dp,sp,pt,in,mm转换成对应的尺寸,其中dp和px有一个叫density系数维系关系,sp和对应的尺寸也有一个scaledDensity系数维系关系。
好的,现在开始说说动态代码写布局的实现方式了。之前说过了,在onCreate的时候是得不到对应的dp和sp值得,所以我们在得到值后再动态加载。

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        on150Dp = tvStaticLoading.getWidth();
        perDp = on150Dp / 150;
        on10Dp = perDp * 10;
        on20Dp = perDp * 20;
        dynamicXML();
    }

    private void dynamicXML() {
        
    }

此时,先看看开始的效果:




这是一开始的部分:

    <LinearLayout
        android:id="@+id/ll01"
        android:layout_width="0dp"
        android:background="@android:color/holo_orange_dark"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:orientation="vertical">
动态写法:

    private void dynamicXML() {
        LinearLayout topLL = getLinearLayout();
    }

    private LinearLayout getLinearLayout(){
        LinearLayout ll = new LinearLayout(this);
        LinearLayout.LayoutParams lllp = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT,1);
        contentView.addView(ll,lllp);
        ll.setBackgroundColor(Color.BLUE);
        ll.setOrientation(LinearLayout.VERTICAL);
        return ll;
    }
此时,看看效果如何:


LinearLayout.LayoutParams lllp = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT,1);
对应的参数分别如下:

(int width, int height, float weight) 
这就相当于静态xml的如下写法:

 <LinearLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
将布局添加到xml布局中,最上层的那个LInearLayout,contentView的排序是横向方式,将背景设置成蓝色,排序方式设置为纵向,反正就是和上述的xml一样。
        contentView.addView(ll,lllp);
        ll.setBackgroundColor(Color.BLUE);
        ll.setOrientation(LinearLayout.VERTICAL);

接下来,要实现这1个

        <TextView
            android:id="@+id/tv_static_loading"
            android:layout_width="150dp"
            android:layout_height="wrap_content"
            android:layout_gravity="right"
            android:layout_marginTop="10dp"
            android:text="我是静态xml加载的布局"/>


    private void dynamicXML() {
        LinearLayout topLL = getLinearLayout();
        setTextViewOne(topLL);
    }



    private void setTextViewOne(ViewGroup vp){
        TextView textView = new TextView(this);
        LinearLayout.LayoutParams ll= new LinearLayout.LayoutParams(on150Dp, ViewGroup.LayoutParams.WRAP_CONTENT);
        ll.gravity = Gravity.RIGHT;
        textView.setBackgroundColor(Color.WHITE);
        ll.setMargins(0,on10Dp,0,0);
        textView.setText("我是动态加载的布局");
        vp.addView(textView,ll);
    }
效果图如下:


将“我是动态加载的布局”,改成和上面一样后,在看看效果:



这里的效果和左边的一样,150DP刚好是那字体的长度。

然后再写:

       <TextView
            android:layout_width="150dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:gravity="center"
            android:text="静态xml加载的布局"/>

把tv1和tv2的一起看

    private LinearLayout getLinearLayout(){
        LinearLayout ll = new LinearLayout(this);
        LinearLayout.LayoutParams lllp = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT,1);
        contentView.addView(ll,lllp);
        ll.setBackgroundColor(Color.BLUE);
        ll.setOrientation(LinearLayout.VERTICAL);
        return ll;
    }

    private void setTextViewOne(ViewGroup vp){
        TextView textView = new TextView(this);
        LinearLayout.LayoutParams ll= new LinearLayout.LayoutParams(on150Dp, ViewGroup.LayoutParams.WRAP_CONTENT);
        ll.gravity = Gravity.RIGHT;
        ll.setMargins(0,on10Dp,0,0);
        textView.setText("我是动态加载的布局");
        vp.addView(textView,ll);
    }

    private void setTextViewTwo(ViewGroup vp){
        TextView textView = new TextView(this);
        LinearLayout.LayoutParams ll= new LinearLayout.LayoutParams(on150Dp, ViewGroup.LayoutParams.WRAP_CONTENT);
        ll.setMargins(0,on10Dp,0,0);
        textView.setText("我也是动态加载的布局");
        textView.setGravity(Gravity.CENTER);
        vp.addView(textView,ll);
    }
在One的方法,是调用了ll.gravity,而Two的方法,则是调用textview.gravity,然后在看看xml上的差异。

        <TextView
            android:id="@+id/tv_static_loading"
            android:layout_width="150dp"
            android:layout_height="wrap_content"
            android:layout_gravity="right"
            android:layout_marginTop="10dp"
            android:text="我是静态xml加载的布局"/>

        <TextView
            android:layout_width="150dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:gravity="center"
            android:text="静态xml加载的布局"/>
可以看到,如果是
            android:layout_gravity="right"
则调用ll.gravity,如果是
            android:gravity="center"
则调用textview.gravity,得出一个规律就是,凡是android:layout_打头的属性,都要用LayoutParams调用,在这里因为父布局是LInearLayout,所以用到LinearLayout.LayoutParams。

接下来动态设置下面这2个控件:

        <Button
            android:layout_width="150dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:gravity="left|center"
            android:text="按钮1"/>

        <ImageView
            android:layout_width="150dp"
            android:layout_height="150dp"
            android:scaleType="fitCenter"
            android:src="@mipmap/q01"/>

添加代码如下:

    private void setButton(ViewGroup vp){
        Button btn = new Button(this);
        LinearLayout.LayoutParams ll = new LinearLayout.LayoutParams(on150Dp, ViewGroup.LayoutParams.WRAP_CONTENT);
        ll.setMargins(0,on10Dp,0,0);
        btn.setGravity(Gravity.LEFT|Gravity.CENTER);
        btn.setText("按钮1");
        vp.addView(btn,ll);
    }

    private void setImageView(ViewGroup vp){
        ImageView iv = new ImageView(this);
        LinearLayout.LayoutParams ll = new LinearLayout.LayoutParams(on150Dp, on150Dp);
        iv.setScaleType(ImageView.ScaleType.FIT_CENTER);
        iv.setImageResource(R.mipmap.q01);
        vp.addView(iv,ll);
    }
调用方法也是雷同的:

    private void dynamicXML() {
        LinearLayout topLL = getLinearLayout();
        setTextViewOne(topLL);
        setTextViewTwo(topLL);
        setButton(topLL);
        setImageView(topLL);
    }

此时,看看效果图:


会发现蓝色部分的按钮比较奇葩,那是因为通过xml设置的button默认会加上一个选择器,我们也通过代码给奇葩的button加上一个选择器看看:

        btn.setBackgroundResource(android.R.drawable.btn_default);
此时,再看看效果图:



正常了吧!

最后2个了,先看看他们的静态设置:

        <TextView
            android:id="@+id/tv_margin"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:background="#000"
            android:text="marginLeft为10dp"
            android:textColor="#fff"
            android:textSize="20sp"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingLeft="20dp"
            android:background="#000"
            android:text="padLeft为20dp"
            android:textColor="#fff"
            android:textSize="20sp"/>
先设置倒数第二个的动态加载:

    private void setLastButOne(ViewGroup vp){
        TextView tv = new TextView(this);
        LinearLayout.LayoutParams ll = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
        ll.setMargins(on10Dp,0,0,0);
        tv.setBackgroundColor(Color.rgb(0,0,0));
        tv.setText("marginLeft为10dp");
        tv.setTextColor(Color.rgb(0xff,0xff,0xff));
        tv.setTextSize(on20Sp);
        vp.addView(tv,ll);
    }
再看看效果图:



会发现,设置的on20Sp好像不太给力,然后再将它设置成:

        tv.setTextSize(20);
再看看效果图:

此刻好像20也是对应20SP,为什么sp可以这样设置,而没有直接设置dp的方法,看看源码的setTextSize是怎样的:

    public void setTextSize(float size) {
        setTextSize(TypedValue.COMPLEX_UNIT_SP, size);
    }
会看到,原来他已经把20转换成SP单位了,所以设置字体大小的时候不用我们操心了!

最后一步的代码则是:

    private void setLastOne(ViewGroup vp){
        TextView tv = new TextView(this);
        LinearLayout.LayoutParams ll = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
        tv.setPadding(on20Dp,0,0,0);
        tv.setBackgroundColor(Color.rgb(0,0,0));
        tv.setText("padLeft为20dp");
        tv.setTextColor(Color.rgb(0xff,0xff,0xff));
        tv.setTextSize(20);
        vp.addView(tv,ll);
    }
此时注意padding和margin的区别:

在xml上:

            android:layout_marginLeft="10dp"
            android:paddingLeft="20dp"
一个是android:layout_开头的,一个则不是,根据上面说到的,也可以猜到代码的写法了。

        ll.setMargins(on10Dp,0,0,0);
        tv.setPadding(on20Dp,0,0,0);


好的,就说到这里了,附上全部代码。

xml文章开头就有了,因此这里附上的是main.java文件的代码:

package com.demo.linearlayoutdemo;

import android.graphics.Color;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

public class MainLLDemoActivity extends AppCompatActivity {
    /**
     * 150dp对应的px值
     */
    private int on150Dp;
    /**
     * 1dp = ?px
     */
    private int perDp;

    private int on10Dp;

    private int on20Dp;

    private float on20Sp ;

    private TextView tvStaticLoading;
    private TextView tvMargin;
    private ViewGroup contentView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_lldemo);
        tvStaticLoading = (TextView) findViewById(R.id.tv_static_loading);
        tvMargin = (TextView) findViewById(R.id.tv_margin);
        contentView = (ViewGroup) findViewById(R.id.contentView);
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        on150Dp = tvStaticLoading.getWidth();
        perDp = on150Dp / 150;
        on10Dp = perDp * 10;
        on20Dp = perDp * 20;
        on20Sp = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 20,
                getResources().getDisplayMetrics());
        dynamicXML();
        Toast.makeText(this, "tvMargin.getTextSize():" + tvMargin.getTextSize(), Toast.LENGTH_SHORT).show();
    }

    private void dynamicXML() {
        LinearLayout topLL = getLinearLayout();
        setTextViewOne(topLL);
        setTextViewTwo(topLL);
        setButton(topLL);
        setImageView(topLL);
        setLastButOne(topLL);
        setLastOne(topLL);
    }

    private LinearLayout getLinearLayout(){
        LinearLayout ll = new LinearLayout(this);
        LinearLayout.LayoutParams lllp = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT,1);
        contentView.addView(ll,lllp);
        ll.setBackgroundColor(Color.rgb(0,0,255));
        ll.setOrientation(LinearLayout.VERTICAL);
        return ll;
    }

    private void setTextViewOne(ViewGroup vp){
        TextView textView = new TextView(this);
        LinearLayout.LayoutParams ll= new LinearLayout.LayoutParams(on150Dp, ViewGroup.LayoutParams.WRAP_CONTENT);
        ll.gravity = Gravity.RIGHT;
        ll.setMargins(0,on10Dp,0,0);
        textView.setText("我是动态加载的布局");
        vp.addView(textView,ll);
    }

    private void setTextViewTwo(ViewGroup vp){
        TextView textView = new TextView(this);
        LinearLayout.LayoutParams ll= new LinearLayout.LayoutParams(on150Dp, ViewGroup.LayoutParams.WRAP_CONTENT);
        ll.setMargins(0,on10Dp,0,0);
        textView.setText("我也是动态加载的布局");
        textView.setGravity(Gravity.CENTER);
        vp.addView(textView,ll);
    }

    private void setButton(ViewGroup vp){
        Button btn = new Button(this);
        LinearLayout.LayoutParams ll = new LinearLayout.LayoutParams(on150Dp, ViewGroup.LayoutParams.WRAP_CONTENT);
        ll.setMargins(0,on10Dp,0,0);
        btn.setGravity(Gravity.LEFT|Gravity.CENTER);
        btn.setText("按钮1");
        btn.setBackgroundResource(android.R.drawable.btn_default);
        vp.addView(btn,ll);
    }

    private void setImageView(ViewGroup vp){
        ImageView iv = new ImageView(this);
        LinearLayout.LayoutParams ll = new LinearLayout.LayoutParams(on150Dp, on150Dp);
        iv.setScaleType(ImageView.ScaleType.FIT_CENTER);
        iv.setImageResource(R.mipmap.q01);
        vp.addView(iv,ll);
    }

    private void setLastButOne(ViewGroup vp){
        TextView tv = new TextView(this);
        LinearLayout.LayoutParams ll = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
        ll.setMargins(on10Dp,0,0,0);
        tv.setBackgroundColor(Color.rgb(0,0,0));
        tv.setText("marginLeft为10dp");
        tv.setTextColor(Color.rgb(0xff,0xff,0xff));
        tv.setTextSize(20);
        vp.addView(tv,ll);
    }

    private void setLastOne(ViewGroup vp){
        TextView tv = new TextView(this);
        LinearLayout.LayoutParams ll = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
        tv.setPadding(on20Dp,0,0,0);
        tv.setBackgroundColor(Color.rgb(0,0,0));
        tv.setText("padLeft为20dp");
        tv.setTextColor(Color.rgb(0xff,0xff,0xff));
        tv.setTextSize(20);
        vp.addView(tv,ll);
    }
}

最后的效果展示图:







以上是关于APK 简析的主要内容,如果未能解决你的问题,请参考以下文章

简析静态xml布局如何通过动态代码实现

Android 之LocalBroadcastManager原理简析

Android 之LocalBroadcastManager原理简析

ArrayDeque 简析

3D线激光成像数学模型简析

Android之ViewStub源码简析