App架构师实践指南三之基础组件

1、基础组件库
随着时间的增长,代码量的逐渐积累,新旧项目之间有太多可以服用的代码。下面是整理的公共代码库。


2、关于加密
密钥的保护以及网络传输安全是移动应用安全最关键的内容,涉及密码学(用于加密、认证和鉴定的学科)。常见的加密算法,主要分为对称加密算法、非对称加密算法和Hash算法。
---对称加密算法。安全性取决于加密算法本身和密钥的私密性,相对于非对称加密算法,密钥管理比较难,速度快几个数量级,适合大数据量的加密处理,对称加密算法流程图如图6-6所示。

---非对称加密算法。非对称加密算法中需要公开密钥(public key)和私有密钥(private key)两个密钥,密钥与数据是一一对应的。密钥容易管理,安全性高,但加解密速度慢,适合小数据量加解密或数据签名,非对称加密算法流程如图6-6(b)所示。
---Hash(哈希)算法。Hash函数是一种将任意长度的消息压缩到某一固定长度(消息摘要)的函数(该过程不可逆),可用于数字签名、消息的完整性检测、消息起源的认证检测等。Hash算法常见的有MD5、SHA、HMAC、RIPEMD、HAVAL、N-Hash、Tiger等。
具体选择和使用时,对称加密算法建议选择AES,非对称加密算法建议选择ECC或RSA,消息摘要可以用MD5,数字签名相关可以用DSA等。如果使用OkHttp网络库,可以通过OkHttp Builder等certificatePinner方法预设服务端证书等pinging值,也可以通过interceptor插入加解密代码。
---Base64.不要使用Base64来加密数据,Base64只是一种编码方式。
---随机数。使用SecureRandom代替Random类来获取随机数,但注意不要为SecureRandom设置种子。
---Hash算法。建议使用SHA-256、SHA-3算法代替MD2、MD4、MD5、SHA-1、RIPEMD算法来加密用户密码等敏感信息,后者已有很多破解算法。对多个串联字符串做Hash加密,要注意避免Hash值一样。
---消息验证算法。建议使用HMAC-SHA256算法,避免使用CBC-MAC.
---对称加密算法。DES默认的是56位的加密密钥,已经不安全,不建议使用,建议使用AES算法(不要使用Android默认的ECB模式,显示置顶位CBC或CFB模式)。
---非对称算法。密钥长度不要低于512位,建议使用2048位的密钥长度。RSA加密算法应使用Clipher.getInstance(RSA/ECB/OAEPWithSHA256AndMGF1Padding),否则会存在重放攻击的风险。

---密钥存储。动态/运行时密钥存储用Android Keystore,其提供类随机密钥生成和存储密钥功能,其key是依托于硬件的KeyChain存储在系统中,而非App目录下,其他应用是无法访问获取的,预存密钥通过so库预设key/secret存储。

3、启动引导模块
通用简单型启动引导页逻辑:用户单击启动-->启动页(延时或网络加载等)-->引导页-->主页。
如果不是第一次启动,则逻辑变为:用户单击启动-->启动页-->主页。
采用MVP模式,业务逻辑在Presenter中完成,UI相关中**view和**Activity中呈现。

扫描二维码关注公众号,回复: 2282185 查看本文章

4、注册登陆模块
从产品角度来看,一个App按照是否需要登陆可以分为3类:第一类是依托账号建立产品服务的(如微信),必须登陆;第二类是按需登陆的(如知乎等),浏览无需登陆,收藏/评论等需要登陆;第三类是无需登陆的,主要是工具类应用,如计算器。所以,不同应用对注册登陆模块会有不同的要求,同时用户注册/登陆也是多样性的,可以通过用户名、邮箱账号、手机账号等注册/登陆,另外,现在三方登陆(微信/qq/微博等)也是常见的一种方式。


5、编译打包
---SRC-->DEX(Dalvik Executable)/RES
---- 使用AAPT(The Android Assert Packaing Tool)编译打包资源文件,生成R.java文件、resource.arsc文件和打包资源文件。
---- 使用AIDL(Android Interface Definition Lauguage)处理.aidl文件,生成.java文件
---- 使用Java Compiler(javac)工具,将源码编译成.class文件
---DEX-->APK。使用apkbuilder工具,将资源文件和.dex文件生成未签名的APK安装文件。
---APK sign。使用jarsigner工具,进行APK签名【分为两种:一种是用于调试的debug.keystore(自动生成);另一种是用于发布的release.keystore(手动生成)】

---APK align。使用zipalign工具,将签名后的APK进行对齐处理。


6、Gradle实用技巧

Gradle是一种基于Groovy语法的项目构建工具,其运行在JVM上,借鉴了脚本语言诸多特性,兼容java,可直接使用java各种类库。
---Gradle Task。Task(任务)是Gradle中的一个核心概念,每一个声明的任务都可以看作是一个任务对象,可以拥有自己的属性和方法(默认类型是DefaultTask),同java中java.lang.Object类似。人物之间可以相互依赖,使用关键字dependsOn,还可以通过doFirst和doLast等在任务执行生命周期中插入具体业务逻辑,常见的任务类型有用于复制的Copy、用于打包的Jar、用于执行的JavaExec等。
---- gradle assemble,生成所有渠道Debug和Release包
---- gradle assembleAndroidTest,生成所有渠道的测试包。
---- gradle assembleDebug,生成所有渠道等Debug包。
---- gradle assembleRelese,生成所有渠道的Release包。
---- gradle assebleXXX,生成某个渠道的Debug和Release包。

---Gradle加速。

---- 常规设置。如开启Gradle daemon进程等(gradle.properties文件,建议使用全局配置),代码如下
     org.gradle.daemon=true //开启Gradle守护进程
     Org.gradle.jvmargs=-Xmx2048 -XX:MaxPermSize=512m  --XX:+HeapDumpOnOutOfMemoryError   //JVM内存
     Org.gradle.parallel=true  //并行项目执行(多module依赖复杂慎用)
     org.gradle.configureondeamand=true

----开启增量编译。在对应module多build.gradle文件中,按如下设置
    dexOptions{
    incremental true
    }

----屏蔽不需要多Task。屏蔽不需要多Task或特定的Task,按如下设置。
    //屏蔽系统Task
    tasks.whenTaskAdded{task ->
      if(task.name.contains("lint"))    //跳过lint检查
        ||task.name.equals("clean")      //如果instant run不生效,把clean这行去掉
        ||task.name.equals("Aidl")      //如果项目中有用到aidl,则不可以舍弃这个任务
        ||task.name.equals("mockableAndroidJar")      //用不到测试的时候,就可以先关闭
        ||task.name.equals("UnitTest")      
        ||task.name.equals("AndroidTest")      
        ||task.name.equals("Ndk")      //用不到NDK和JNI的也可以关闭掉
        ||task.name.equals("Jni")      

        ){
          task.enabled = false
        }
    }

    //屏蔽指定Task XX
    gradle.taskGraph.whenReady{
      Tasks.each{task  ->
        if(task.name.contains("XX")){
     task.enabled = false
        }
      }
    }

----代理设置。在根目录的gradle.properties中配置,代码如下。
    systemProp.http.proxyHost=127.0.0.1
    systemProp.http.proxyPort=1010
    systemProp.https.proxyHost=127.0.0.1
    systemProp.https.proxyPort=1010


---Google官方关于Gradle加速的17条实用建议
---- 通过productFlavors设置build variant,针对不同product保留对应的配置信息,加速构建,类似多渠道打包。
---- 避免编译不必要的资源。如dev包通过设置resConfigs “en”“xxhdpi”,只使用英文string资源和xxhdpi的屏幕密度资源,代码如下。
     productFlavors{
    Dev{
    ...
    //the following configuration limits the "dev"flavor to using 
    //English string resources and xxhdpi screen-density resources.
    resConfigs "en","xxhdpi"
    }
    ...
     }
----配置debug构建的Crushlytics为Disable(Crushlytics为崩溃上报分析工具,Debug阶段可能并不需要),如果Debug期间需要开启Crushlytics,那也可以设置alwaysUpdateBuildId为false,避免每次都更新ID,代码如下
    Android{
    ...
    buildTypes{
        debug{
            ext.enableCrashlytics = false
            ext.alwaysUpdateBuildId = false
        }
    }
    }
----用静态的构建配置来构建你的Debug版,避免在Debug下使用动态配置(如version codes,version names,resources等),类似下面要阐述的版本号/依赖统一管理
----用静态的版本依赖,避免使用+号
    Com.android.tools.build:gradle:2.+    //动态依赖
    Com.android.tools.build:gradle:2.3.0    //静态依赖
----配置on demand 为enable状态,指定Gradle仅能配置你想要构建的Modules。
    Android Studio路径为:File-->Settings-->Build-->Compiler-->check Configure on demand.
----建议使用library模块,模块化代码抽离。
----当你的构建消耗时间过长时,如果存在较复杂和独立的构建逻辑,考虑将其创建为独立的Tasks(自定义Gradle插件),按需使用。
----配置dexOptions(Android Studio 2.1新增)和开启library pre-dexing(DEX预处理)。两者都是针对DEX构建优化,dexOptions可以配置包括preDexLibraaies、maxProcesCount和javaMaxHeadSize,代码如下。
    android{
    ...
    dexOptions{
        preDexLibraries true
           maxProcessCount 8
        //instead of setting the heap size for the DEX process, increase Gradle's
        //heap size to enable dex-in-process.
        //javaMaxHeapSize "2048m"
    }    
    }    
----增加Gradle堆大小(开启Dex-in-process).Dex-in-process默认允许多个DEX进程运行在一个单独的VM中,所以可以通过分配足够的内存来开启这个特性(Android Studio 2.1+)
----将图片转换成WebP格式,不用在构建时做压缩。WebP是一种具备JPEG类似的有损压缩和PNG的透明支持的高压缩质量的图片格式,同时可以减少包Size。
----禁止使用PNG crunching。也是一种禁止构建时默认压缩图片的方法。
    android{
    ...
    aaptOptions{
        cruncherEnabled false
    }
    }
----使用Instant Run.
----使用构建缓存,Anroid Gradle插件2.3.0+默认开启了构建缓存。
----避免使用注解处理器,使用注解处理器时将导致增量构建不可用。
----Profile your build。这条主要针对那些超级App(拥有大量自定义构建逻辑等),需要知晓每个阶段/每个Task的时间消耗来优化那些耗时逻辑,build profile的生成通过在Android Studio的命令行中操作(View-->Tool Windows-->Terminal),具体如下:
------清除:gradlew clean(Windows)或./graldew clean(Mac)
------构建:gradlew-->profile-->recompile-scripts-->offline-->rerun-tasks assembleFlavorDe不过(其中,profile表示开启profiling;offline表示禁止Gradle获取离线依赖,防止Gradle更新数据影响报告;rerun-tasks表示强制Gradle返回所有Task并忽略任何Task的优化;recompile-scripts表示强制脚本重新编译跳过cache)。
-----查看:找到project-root/build/reports/profile/目录下的profile_timestamp.html文件,在浏览器中打开即可呈现完整时间消耗的构建报告。
----项目组件化。

---多渠道打包。其本质上productFlavors的使用,结合占位符与AndroidManifest的使用,可以为不同渠道设置不同包名。另外,还可以结合脚本实现快速渠道打包,开源项目packer-ng-plugin声称100个渠道打包只需要10s。

    productFlavors{
        dev{
            applicationIdSuffix ".debug"   //不同包名设置,便于线上和开发包安装同一首手机
        }
        google pay{}
        qihoo360{}
        xiaomi{}
        Tencent{
            manifestPlaceholders = [UMENG_CHANNEL:"Tencent"] //结合占位符
        }
    }


---Gradle通用技巧
----Log开关控制。定义动态编译生成对象,通过buildConfigField控制,然后在java代码中通过BuildConfig.enableLog来获取,代码如下。
    buildTypes{
        debug{
            buildConfigField("boolean","enableLog","true")
        }
        release{
            buildConfigField("boolean","enableLog",“false”)
        }
    }

----版本号/依赖统一管理。建立独立的gradle(config.gradle),然后apply from进当前gradle,通过设置project.ext,再通过rootProject.ext进行引用,以下代码为XKnife-Android的global_config.gradle文件的一部分。
    ext{
        abortOnLintError = false
        checkLintRelease = false
        
        android= [compileSdkVersion   :  24,
              buildToolsVersion   :  "25.0.2",
                   applicationId       :  "com.skyseraph.xknife",
              applicationIdUserLogin   :  “com.skyseraph.xknife.module.userlogin”,
              applicationIdLaunch      :   "com.skyseraph.xknife.module.launch",
              applicationIdUpgrade     :   "com.skyseraph.xknife.module.upgrade",
              minSdkVersion.           :   15,
              targetSdkVersion.        :   24,
               versionCode              :    1,
              versionName              :    "1.0.0",
               testInstrumentationRunner:    "android.support.test.runner.AndroidJUnitRunner"
            
        ]
        ...  ...
    }

    //使用
    applicationId rootProject.ext.android ["applicationId"]

    另外,还可以在gradle.properties文件中定义一些统一的编译常量(如定义常量XX=1,然后在需要的module中通过project.XX引用)。
----APK输出名字定制化。定制化APK输出名字,自动加上版本号、时间等信息,避免手动重命名
    applicationVariants.all{variant ->
        variant.outputs.each{output ->
            output.outputFile = new File(
                output.outputile =new File(
                    output.outputFile.parent + "/${variant.buildType.name}","XXX-
                    ${variant.buildType.name}-${variant.versionName}-${variant.
                    productFlavors[0].name.apk".toLowerCase())
        }
    }
----构建不同的名称、版本号和App ID等,代码如下
    buildTypes{
        debug{
            applicationIdSuffix  ".debug"
            versionNameSuffix    "-debug"
            resValue "string","app_name","XXX(debug)"
        }
        release{
            resValue "string","app_name","XXX"
        }
    }
----修改默认的Build配置文件名(settings.gradle文件)
    rootProject.buildFiledName = xx.gradle

----java版本设置。在Gradle中设置java版本,代码如下。
    compileOptions{
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility javaVersion.VERSION_1_8
    }

猜你喜欢

转载自blog.csdn.net/nicolelili1/article/details/81085208