一、apk签名(必须)

weixin_40855550 · · 99 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

一、apk签名(必须)

1.apk签名(数字签名)

apk签名:开发者(开发商)通过数字证书(也可以叫签名文件),给apk应用打上带有个人(公司)信息的标记,以表示自己是该应用的开发者或拥有者。签名后的apk解压后能看到META-INF文件夹,并且里面会有3个签名相关的文件(MANIFEST.MF,CERT.SF,CERT.RSA)

数字签名: 哈希算法(sha1, md5) + 私钥加密

查看apk的拥有者

keytool -printcert -file CERT.RSA

关于Android数字签名相关事项:

所有的应用都必须进行签名,android系统不会安装没有签名的应用;

使用不同数字证书签名的两个apk,无法实现覆盖安装。所以,应用在版本升级打包时必须使用同一个数字证书(keystore文件)进行签名; 这一点,也保证了用户手机上已安装的应用,不会被原作者外的第三者开发的假冒应用安装替换,因为第三者不可能持有原作者的数字证书(签名文件)

应用上线需要release签名(或者叫正式签名:指使用个人或公司的签名文件签名),不能用debug签名(开发调试签名:指使用sdk自带的签名文件签名)

数字证书都是有有效期的,Android只是在应用程序安装的时候才会检查证书的有效期。如果程序已经安装在系统中,即使证书过期也不会影响程序的正常使用,一般开启99年就好了

apk签名可以使用自签名的数字证书(自己创建的keystore签名文件),不需要使用CA机构认证的数字证书(需要给CA机构付费);

2.生成签名文件的两种方式

通过IDE(AndroidStudio, Eclipse)生成

通过命令行keytool命令生成

keytool -genkeypair -validity [签名文件的有效期,单位为天] -keystore [keystore签名文件(数字证书)] -storepass [store密码] -alias [密钥对的别名] -keypass [密钥密码]

列表出所有的密钥对:
keytool -list -v -keystore itheima.keystore

参考: https://www.chinassl.net/?f=faq&a=view&r=457

3.AndroidStudio中导出正式签名包

android {

    // 定义签名用到的数字证书
    signingConfigs {
        config {
            keyAlias 'itheima'
            keyPassword '123456'
            storeFile file('E:/_GZ05/_code/01.ProGuardDemo_AS/itheima.jks')
            storePassword '123456'
        }
    }
    ...

    buildTypes {
        // debug签名, 直接运行使用
        debug {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }

        // 打正式包
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.config  // 使用上面配置的签名文件进行签名
        }
    }
}

二、Android代码混淆(非必须)


1. 混淆简介

  1. Android代码混淆是一种应用源代码保护技术,用来防止别人对apk进行逆向分析;
  2. 从Android2.3开始,Google在SDK中加入了一个叫ProGuard的工具,使用它来进行代码混淆。
  3. proguard是一个压缩、优化和混淆java字节码文件的免费工具,其作用:

                删除代码中的注释;

                    删除代码中没有用到的类、字段、方法和属性;

                    会把代码中的包名、类名、方法名变量名等修改为abcd...这种没有意义的名字,使得反编译出来的代码难以阅读,从而达到防止apk被破解和逆向分析的目的;

                    经过ProGuard混淆后apk安装包会变小

2. 如何使用Proguard进行代码混淆?

buildTypes {
    release {
        // 修改此参数为true,表示要进行混淆
        minifyEnabled true
        // 表示要使用sdk中的proguard-android.txt和当前项目中的proguard-rules.pro指定混淆规则
        // 通常只需修改后者就可以了
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }

    // 指定jdk为1.7
    compileOptions {
        targetCompatibility 1.7
        sourceCompatibility 1.7
    }
}
在app下的proguard-rules.pro文件中配置混淆规则
打开gradle窗口,双击如下操作菜单项(assembleRelease: 打正式的发布包,installRelease: 安装正式的发布包), 生成出来的apk就是经过混淆的


3. 混淆规则配置[重点]

【注意】:有些java类是不能进行混淆,需要配置混淆规则。

1.所有的第三方包jar/库, 都不进行混淆。 对于每一个第三方的jar或库都需要配置如下两行,其中#号为注释,

#com.umeng.analytics:是要配置的第三方库的包名,配置成包名前缀com.umeng也可以
-dontwarn com.umeng.analytics.**               # 不要提示警告
-keep class com.umeng.analytics.** { *; }      # 对指定的类不进行混淆

2.注意:如果你使用的proguard的版本比较低, 对于每一个jar或第三方依赖, 还需要配置如下一行,不加可能会出现“类1 can't find referenced class 类2”这样的错。

-libraryjars libs/umeng-analytics-v5.6.1.jar   

只要用到反射的类都不能进行混淆

3.如果使用了Gson解析json字符串,如果需要如下配置

# 保留要解析的javabean中的泛型信息
-keepattributes Signature

# bean包及其子包下, 所有要通过Gson反射解析的javabean都不能进行混淆
-keep class com.xxx.bean.**
-keep class com.xxx.bean.** { *;}
4.如果使用了EventBus, 用来接收事件的以onEvent (onEventMainThread)开头的方法不能进行混淆:

# 如果用到了WebView中js与java代码的相互调用,js要调用的java类中的方法不能混淆
-keep class com.xxx.WebViewActivity {
    public *;
}

# 回调方法声明在内部类JsInterface中的配置方法
-keep class com.xxx.WebViewActivity2$JsInterface {
    public *;
}

参考第三方库的github官方文档进行混淆规则的配置, 例如下面的这些库官网上都有混淆配置说明。

https://github.com/bumptech/glide

4.混淆后生成的相关文件

dump.txt
说明 APK 中所有类文件的内部结构。
mapping.txt
提供原始与混淆过的类、方法和字段名称之间的转换。
seeds.txt
列出未进行混淆的类和成员。
usage.txt
列出从 APK 移除的代码。

使用mapping.txt文件对出错日志还原:
retrace.bat -verbose mapping.txt stacktrace.txt > out.txt

其中mapping.txt文件较为重要(app/build/output/mapping/mapping.txt),该文件需要存档,以便用来还原和查看混淆后的出错日志;

在使用友盟的bug自动捕获功能时,需要把mapping.txt上传到友盟后台。

三、Android资源混淆(非必须)

通过微信资源混淆工具实现(不是必须: 微信6.0,QQ6.0,网易新闻客户端)

AndResGuard

  • 对res下的资源文件进行混淆, 将原本冗长的资源路径变短,例如将res/drawable/wechat变为r/d/a。
  • AndResGuard不涉及编译过程,只需输入一个apk(签名未签名都可以,处理后会直接将原签名删除),可得到一个实现资源混淆后的apk;

使用第三方工具AndResGuard-master(微信资源混淆)实现资源混淆;

四、多渠道打包(非必须)

1.gradle打包,2.其他工具打包(美团多渠道)

目的:统计的时候,检测各个市场的数据

五、apk加固(必须)

三方平台: 梆梆安全, 360加固保, 爱加密, 阿里聚安全, 娜迦, 腾讯加固(乐固)

操作步骤: 生成一个混淆签名的正式包 --> 上传apk --> 下载加固后的apk --> 重新签名

  • 梆梆安全: https://www.bangcle.com
  • 360加固保: http://jiagu.360.cn/
  • 爱加密: http://www.ijiami.cn/ 加固 效果差一点,还能看得到里面的类,但是没有方法体实现。
  • 阿里聚安全: https://jaq.alibaba.com
  • 娜迦: http://www.nagain.com/
  • 腾讯加固: http://wiki.open.qq.com/wiki/%E5%BA%94%E7%94%A8%E5%8A%A0%E5%9B%BA


六、apk瘦身(非必须)

减小apk安装包的大小

必要性

同样的功能,apk越小越好,用户下载动机更大。

了解apk的组成

  • classes.dex: 是java源码编译后生成的java字节码文件
  • resources.arsc:编译后的二进制资源文件
  • AndroidManifest.xml
  • assets: 目录可以存放一些配置文件或资源文件
  • lib: 存放jar包,子目录armeabi存放的是一些so文件
  • META-INF: 目录下存放的是签名信息,用来保证apk包的完整性和系统的安全
  • res: apk图片布局等资源文件 


辅助分析工具

(1)nimbledroid[了解]: 能够得知app内存使用,网络使用,磁盘输入/输出,文件大小等一些NimbleDroid认为至关重要的数据; * 官网:https://nimbledroid.com/(无需翻墙,访问比较慢) * 注册 * 登录 * 上传apk * 生成分析结果图;

(2)Android Studio 2.3 apk逆向工具--APK Analyzer[推荐] 参考:http://blog.csdn.net/nicolelili1/article/details/52669823

瘦身方案

1.开启minifyEnabled(开启混淆功能:会删除没用到的类或方法)

2.开启shrinkResources(去除无用资源:会使用一个像素的图片替换没有用到的图片)

buildTypes {
    release {
        minifyEnabled true
        shrinkResources true // 要与minifyEnable=true同时使用, 否则会出错
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        signingConfig signingConfigs.config
    }
    debug {
        signingConfig signingConfigs.config
    }
}

//添加如下配置就ok了
lintOptions {
    checkReleaseBuilds false
    abortOnError false // 出错时继续执行,不退出
}

3.删除无用的语言资源(国际化文件)

defaultConfig {
    ... 
    resConfigs "zh" // 默认会保留英文语言包 + 这里配置的zh中文语言包;
}   

4.对于非透明的大图,jpg将会比png的大小有显著的优势,在启动页,活动页等之类的大图展示区采用jpg将是明智的选择;

  • 使用【格式工厂工具】进行转换:png -> jpg
  • jpg: 压缩率较高,但是没有透明度信息,适用于启动界面的背景图片
  • png: 有透明度信息,压缩率比jpg较小
  • bmp: 几乎没有压缩
5.使用tinypng有损压缩

官方网站: https://tinypng.com/ (无需翻墙)TinyPNG 使用一种智能有损压缩技术(通过降低图片中的颜色数量,来减少存储图片所需要的数据)来降低PNG图片的大小。这样的压缩对图片的效果影响是很小的,但是可以大大降低图片的大小,并且还能保持 PNG 的 alpha 透明度因为 TinyPNG 将 PNG 图片压缩成8位的 PNG(而不是24位),所以它的压缩比例非常高,至少都有 50% 以上的压缩比例,有些甚至可以达到70%,并且压缩之后的图片和原图人眼基本看不出区别。

6.使用webp图片格式

  • 从Android 4.0+开始原生支持,但是不支持包含透明度,直到Android 4.2.1+才支持显示含透明度的webp,使用的时候需要注意

  • 了解webp格式: http://isux.tencent.com/introduction-of-webp.html(WebP探寻之路)

  • Android中图片优化之webp使用: http://blog.csdn.net/reboot123/article/details/46552437
  • android4.0以下使用第三方兼容webp图片:http://blog.csdn.net/jiwangkailai02/article/details/17015451

  • 使用【格式工厂】进行转换;

7.通过v4包中的DrawableCompat,通过着色方案完成selector效果

8.使用shape文件替换图片

9.使用尽量优化的图片;

10.使用第三方包时把用到的代码添加到项目中来,避免引用整一个第三方库。

11.So的使用, 不用兼容所有的cpu架框

七、增量更新(非必须)

1. 背景

  1. window系统的升级过程
  2. android系统的升级过程
  3. android studio的升级过程
  4. 国内某些应用升级过程
  5. 自从Android 4.1 开始, Google Play 引入了应用程序的增量更新功能,App使用该升级方式,可节省约2/3的流量。

2.什么是增量更新

升级的时候只需要从服务器下载和本地版本不同的部分,然后在本地在合成新的apk包,也就是只需要升级增加的部分

3.增量更新实现流程

服务器生成和管理差异包客户端下载差异包,并且与旧包全并形成新的安装包客户端再安装新的安装包

4.增量更新实现工具--bsdiff

bsdiff: 开源的二进制比较工具作用: apk文件的差分、合成网站: http://www.daemonology.net/bsdiff/

5.增量更新优缺点

节省流量,加快更新速度需要生成和管理很多的差异包

7.原理

服务端:用已有的包和最新的包生成差分包,

客户端: 下载生成的查分包,最后合并校验,跟新成功

注意:程序不是很大,比如只有2、3M,那么完全没有必要使用增量更新,增量更新适用于apk包比较大的情况,比如游戏客户端

八、热修复(非必须)

1. 概念

一种快速、低成本修复产品软件版本缺陷的方式;不用重新发包, 在用户不知情的情况下修复bug

2. 热修复框架及对比

阿里巴巴:AndFix和Dexposed
微信开源:Tinker
大众点评:Nuwa
美团:Robust
阿里的开源项目AndFix和Dexposed,目前dexposed的兼容性较差,只有2.3,4.0~4.4兼容,其他Android版本不兼容或未测试;而andfix则兼容2.3~7.0。

微信tinker: github tinkerAndFix: https://github.com/alibaba/AndFix[阿里]Nuwa: https://github.com/jasonross/Nuwa [个人]HotFix: https://github.com/dodola/HotFix [个人]Dexposed: https://github.com/alibaba/dexposed [阿里]

http://www.cnblogs.com/alibaichuan/p/5863616.html

https://blog.csdn.net/itachi85/article/details/79522200

阿里巴巴的AndFix(即时) 采用了底层替换方案

和微信的Tinker(不即时)  

Instant Run方案  底层替换方案


1. 阿里AndFix

创建一个新项目AndroidFixDemo, 添加脚本依赖

compile 'com.alipay.euler:andfix:0.3.1@aar' 

添加权限

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>

添加添加补丁的代码:实现在应用启动时(Application的onCreate方法)加载补丁,实现热修复:(注意: 后面生成的补丁名字叫out.apatch, 并且要放在sdcard根目录下)

PatchManager patchManager = new PatchManager(this);
patchManager.init("1.0");

// 加载补丁
patchManager.loadPatch();

// add patch at runtime
try {
    // 注意: 补丁要放到sdkcard根目录下,并且名字为out.apatch
    String patchFileString = Environment.getExternalStorageDirectory()
            .getAbsolutePath() + "/out.apatch";

    // 添加新补丁
    patchManager.addPatch(patchFileString);
    Log.d("AndFix", "添加新补丁:" + patchFileString);

} catch (Exception e) {
    Log.e("AndFix", "", e);
}

对比两个apk生成补丁out.apatch(注意apk需要进行签名,否则会修复失败): 解压项目AndFix-master.zip/tools里的apkpatch-1.0.3.zip, 生成两个apk(原apk和修复后的apk),再通过apkpatch工具对比两个apk, 在文件夹patch目录下生成补丁, 然后把后缀名为.apatch的文件修改为out.apatch(补丁名需为:out.apatch)

语法
apkpatch -f <新apk> -t <旧apk> -o <输出全路径> -k <签名文件> -p <密码> -a <别名> -e <密码>

# 默认签名
apkpatch -f app-debug-v2.apk -t app-debug-v1.apk -o C:\Users\Administrator\Desktop\apkpatch-1.0.3\patch -k itheima.jks -p christ -a christ-e christ

安装运行旧版本;

把补丁out.apatch放到sdcard根目录下:

# 通过adb命令把out.apatch补丁push到sdcard根目录
adb push out.apatch /sdcard/
关闭旧版本(要退出当前进程),再打开应用,应用会加载补丁,查看是否热修复成功。

2.微信Tinker(bugly集成)

bugly: bug捕获, 集成Tinker的热修复 

集成指南:https://bugly.qq.com/docs/user-guide/instruction-manual-android-hotfix/?v=20170322165254

1.project下build.gradle添加:

// tinkersupport插件, 其中lastest.release指拉取最新版本,也可以指定明确版本号,例如1.0.4
classpath "com.tencent.bugly:tinker-support:latest.release"
2.module下的build.gradle添加:

android {
    defaultConfig {
        ...
        ndk {
            //设置支持的SO库架构
            abiFilters 'armeabi' //, 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a'
        }
    }
}

dependencies {
    ...

    // 多dex配置
    compile "com.android.support:multidex:1.0.1"
    // 其中latest.release指代最新版本号,也可以指定明确的版本号,例如2.3.2
    compile 'com.tencent.bugly:crashreport_upgrade:latest.release'
    compile 'com.tencent.bugly:nativecrashreport:latest.release' 
}
3.在模块下创建tinker-support.gradle,该文件的配置如下:

apply plugin: 'com.tencent.bugly.tinker-support'

def bakPath = file("${buildDir}/bakApk/")

/**
 * 此处填写每次构建生成的基准包目录
 */
def baseApkDir = "app-0208-15-10-00"

/**
 * 对于插件各参数的详细解析请参考
 */
tinkerSupport {

    // 开启tinker-support插件,默认值true
    enable = true

    // 指定归档目录,默认值当前module的子目录tinker
    autoBackupApkDir = "${bakPath}"

    // 是否启用覆盖tinkerPatch配置功能,默认值false
    // 开启后tinkerPatch配置不生效,即无需添加tinkerPatch
    overrideTinkerPatchConfiguration = true

    // 编译补丁包时,必需指定基线版本的apk,默认值为空
    // 如果为空,则表示不是进行补丁包的编译
    // @{link tinkerPatch.oldApk }
    baseApk = "${bakPath}/${baseApkDir}/app-release.apk"

    // 对应tinker插件applyMapping
    baseApkProguardMapping = "${bakPath}/${baseApkDir}/app-release-mapping.txt"

    // 对应tinker插件applyResourceMapping
    baseApkResourceMapping = "${bakPath}/${baseApkDir}/app-release-R.txt"

    // 构建基准包和补丁包都要指定不同的tinkerId,并且必须保证唯一性
    tinkerId = "base-1.0.1"

    // 构建多渠道补丁时使用
    // buildAllFlavorsDir = "${bakPath}/${baseApkDir}"

    // 是否开启反射Application模式
    enableProxyApplication = false

}

/**
 * 一般来说,我们无需对下面的参数做任何的修改
 * 对于各参数的详细介绍请参考:
 * https://github.com/Tencent/tinker/wiki/Tinker-%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97
 */
tinkerPatch {
    //oldApk ="${bakPath}/${appName}/app-release.apk"
    ignoreWarning = false
    useSign = true
    dex {
        dexMode = "jar"
        pattern = ["classes*.dex"]
        loader = []
    }
    lib {
        pattern = ["lib/*/*.so"]
    }

    res {
        pattern = ["res/*", "r/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]
        ignoreChange = []
        largeModSize = 100
    }

    packageConfig {
    }

    sevenZip {
        zipArtifact = "com.tencent.mm:SevenZip:1.1.10"
    //        path = "/usr/local/bin/7za"
    }

    buildConfig {
        keepDexApply = false
        //tinkerId = "1.0.1-base"
        //applyMapping = "${bakPath}/${appName}/app-release-mapping.txt" //  可选,设置mapping文件,建议保持旧apk的proguard混淆方式
        //applyResourceMapping = "${bakPath}/${appName}/app-release-R.txt" // 可选,设置R.txt文件,通过旧apk文件保持ResId的分配
    }
}

4.并在app中的build.gradle文件最后添加:

apply from: 'tinker-support.gradle'

5.创建SampleApplicationLike,并指定appid:

public class SampleApplicationLike extends DefaultApplicationLike {

    public static final String TAG = "Tinker.SampleApplicationLike";

    public SampleApplicationLike(Application application, int tinkerFlags,
            boolean tinkerLoadVerifyFlag, long applicationStartElapsedTime,
            long applicationStartMillisTime, Intent tinkerResultIntent) {
        super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent);
    }

    @Override
    public void onCreate() {
        super.onCreate();
        // 这里实现SDK初始化,appId替换成你的在Bugly平台申请的appId
        // 调试时,将第三个参数改为true
        Bugly.init(getApplication(), "a858252095", false);
    }

    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    @Override
    public void onBaseContextAttached(Context base) {
        super.onBaseContextAttached(base);
        // you must install multiDex whatever tinker is installed!
        MultiDex.install(base);

        // 安装tinker
        // TinkerManager.installTinker(this); 替换成下面Bugly提供的方法
        Beta.installTinker(this);
    }

    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    public void registerActivityLifecycleCallback(Application.ActivityLifecycleCallbacks callbacks) {
        getApplication().registerActivityLifecycleCallbacks(callbacks);
    }
}
6.创建SampleApplication,注意替换如下的包名:(如果项目中要使用自定义的Application需要继承此SampleApplication)

public class SampleApplication extends TinkerApplication {
    public SampleApplication() {
        super(ShareConstants.TINKER_ENABLE_ALL, "包名.SampleApplicationLike",
                "com.tencent.tinker.loader.TinkerLoader", false);
    }
}

7.在清单文件中配置SampleApplication

<application
    ...
    android:name=".SampleApplication">
8.配置权限

<uses-permission android:name="android.permission.READ_PHONE_STATE" /> <--!危险权限需要动态申请 -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_LOGS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <--!危险权限需要动态申请 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <--!危险权限需要动态申请 -->
9.配置签名文件, 生成经过正式签名的apk

signingConfigs {
    config {
        keyAlias 'christ'
        keyPassword '123456'
        storeFile file('C:/christ01.jks')
        storePassword '123456'
    }
}

release {
    // 使用上面配置的签名文件
    signingConfig signingConfigs.config
}

debug {

    // 使用上面配置的签名文件
    signingConfig signingConfigs.config
}

完整接入流程

  1. 打基准包安装并上报联网(注:填写唯一的tinkerId)
  2. 对基准包的bug修复(可以是Java代码变更,资源的变更)
  3. 修改基准包路径、修改补丁包tinkerId、mapping文件路径(如果开启了混淆需要配置)、resId文件路径
  4. 执行buildTinkerPatchRelease打Release版本补丁包
  5. 选择app/build/outputs/patch目录下的补丁包并上传(注:不要选择tinkerPatch目录下的补丁包,不然上传会有问题)
  6. 编辑下发补丁规则,点击立即下发
  7. 杀死进程并重启基准包,请求补丁策略(SDK会自动下载补丁并合成)
  8. 再次重启基准包,检验补丁应用结果
  9. 查看页面,查看激活数据的变化




本文来自:CSDN博客

感谢作者:weixin_40855550

查看原文:一、apk签名(必须)

99 次点击  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet