一、设置样式

URL:http://www.cnblogs.com/bdsdkrb/p/5715438.html

二、添加滚动

tv_read_text.movementMethod = ScrollingMovementMethod.getInstance()

三、Html.fromHtml() 支持的标签

URL:http://zyoo005.iteye.com/blog/1523874

TextView textView=(TextView)findViewById(R.id.hello);   
textView.setText(Html.fromHtml("Hello <b>World</b>,<font size=\"3\" color=\"red\">AnalysisXmlActivty!</font>"));   
<a href="...">  定义链接内容  
<b>  定义粗体文字   b 是blod的缩写  
<big>  定义大字体的文字  
<blockquote>  引用块标签   
属性:  
Common  -- 一般属性  
cite  -- 被引用内容的URI  
<br>   定义换行  
<cite>   表示引用的URI  
<dfn>   定义标签  dfn 是defining instance的缩写  
<div align="...">  
<em>  强调标签  em 是emphasis的缩写  
<font size="..." color="..." face="...">  
<h1>  
<h2>  
<h3>  
<h4>  
<h5>  
<h6>  
<i>   定义斜体文字  
<img src="...">  
<p>     段落标签,里面可以加入文字,列表,表格等  
<small>  定义小字体的文字  
<strike>   定义删除线样式的文字   不符合标准网页设计的理念,不赞成使用.   strike是strikethrough的缩写  
<strong>   重点强调标签  
<sub>   下标标签   sub 是subscript的缩写  
<sup>   上标标签   sup 是superscript的缩写  
<tt>   定义monospaced字体的文字  不赞成使用.  此标签对中文没意义  tt是teletype or monospaced text style的意思  
<u>   定义带有下划线的文字  u是underlined text style的意思  

一、注意事项

  • 不能热更新 Manifest 清单文件中的内容

二、相关配置

  • enableProxyApplication = true 情况下的配置

    执行 Gradle -> :app -> Tasks -> build -> assemableNone 后,会在 /app/build/bakApk/baseApkDir/none 目录下生成 APK

    检查 APK 的 manifest 文件,ApplicationName 会被替换,并生成几个额外的 meta-data,其中包括 TINKER_ID 和 TINKER_PATCH_APPLICATION

    • TINKER_ID = noneRelease_base-1.54
    • TINKER_PATCH_APPLICATION = xxx.xxx.CustomApplication

三、打包过程

  • 生成基包
    1. 修改 TinkerId 为 base-version
    2. 生成包 Gradle Projects -> :app -> Tasks -> build -> assembleRelease
    3. 获取 APK,在 /app/build/bakApk/baseApkDir/flavors/app-flavor-release.apk
  • 生成补丁包
    1. 修改代码
    2. 修改 TinkerId 为 patch-version-index
    3. 设置 baseApkDir 路径为基包的名称
    4. 生成基包 Gradle Projects -> :app -> Tasks -> tinker-support -> buildAllFlavorsTinkerPatchRelease
    5. 获取 APK,在 /app/build/output/patch/flavors/release/patch_signed_7zip.apk
  • 其它配置
    • buildAllFlavorsDir 构建多渠道补丁时使用
    • isProtectedApp 是否启用加固模式,默认为false
    • enableProxyApplication 是否开启反射Application模式

四、遇到的 BUG

  • 证书
    Execution failed for task ':app:tinkerPatchNoneRelease'.
    > Could not resolve all dependencies for configuration ':app:sevenZipToolsLocator'.
       > Could not download SevenZip-osx-x86_64.exe (com.tencent.mm:SevenZip:1.1.10)
          > Could not get resource 'https://jcenter.bintray.com/com/tencent/mm/SevenZip/1.1.10/SevenZip-1.1.10-osx-x86_64.exe'.
             > Could not GET 'https://jcenter.bintray.com/com/tencent/mm/SevenZip/1.1.10/SevenZip-1.1.10-osx-x86_64.exe'.
                > peer not authenticated
    

    原因:电脑上没有 SevenZip 签名软件

    解决:设置 path 路径

    sevenZip {
        zipArtifact = "com.tencent.mm:SevenZip:1.1.10"
        path = "/usr/local/bin/7za"
    }
    
  • BuglyFileProvider 文件冲突,应用无法安装
    Failed to install xxx.apk: Failure
    [INSTALL_FAILED_CONFLICTING_PROVIDER: Package couldn't be installed in /data/app/com.xxx: 
    Can't install because provider name com.tencent.bugly.beta.fileProvider 
    (in package com.xxx.xxx) is already used by com.xxx.bbb]
    

    原因:android:authorities 不能与已安装应用相同导致。

    在配置清单文件时没有添加 com.tencent.bugly.beta.fileProvider,导致自动生成了一个,并且 android:authorities=”com.tencent.bugly.beta.fileProvider”。

    解决:加上 BuglyFileProvider,代码如下:

    <provider
        android:name="com.tencent.bugly.beta.utils.BuglyFileProvider"
        android:authorities="${applicationId}.fileProvider"
        android:exported="false"
        android:grantUriPermissions="true"
        tools:replace="name,authorities,exported,grantUriPermissions">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/provider_paths"
            tools:replace="name,resource"/>
    </provider>
    

五、使用 Python 编写脚本提取相关信息(不重要)

因为同时要为好多个应用添加 Bugly 的热更新,而且有四个渠道的安装包,手动将每个文件复制出来并重命名非常的繁琐。借此写一个脚本,顺便练练手(之前只看过文档):

import os
import shutil
import time

BASE_PROJECT_BUILD_DIR = "/Users/ionesmile/Documents/iOnesmileDocs/WorkSpace/Snaillove/ColorLampChangda/app/build"

BAK_PATH = BASE_PROJECT_BUILD_DIR + "/bakApk/"

baseApkDir = "app-0206-18-36-11"

FLAVORS_ALIAS_MAP = {"self360": "360", "selfBaidu": "baidu"}


def getApkVersionCode(baseApk):
    return "1.38"


def getProjectName():
    return "i-lamp"


def copyBaseApkRenameToPath(outPath):
    outPath = os.path.join(outPath, "base")
    if not os.path.exists(outPath):
        os.makedirs(outPath)
    for flavorApk in os.listdir(BAK_PATH + baseApkDir):
        if os.path.isdir(os.path.join(BAK_PATH + baseApkDir, flavorApk)):
            baseApk = os.path.join(BAK_PATH + baseApkDir, flavorApk + "/app-"+flavorApk+"-release.apk")
            print "baseApk", baseApk
            if os.path.exists(baseApk):
                if not FLAVORS_ALIAS_MAP.get(flavorApk, None) is None:
                    flavorApk = FLAVORS_ALIAS_MAP.get(flavorApk, None)
                out_file = os.path.join(outPath, "android_"+getProjectName()+"_v"+getApkVersionCode(baseApk)+"_"+getDateYyyyMMdd()+"_"+flavorApk+".apk")
                print "out_file", out_file
                shutil.copyfile(baseApk, out_file)


def copyBaseFileRenameToPath(outPath):
    outPath = os.path.join(outPath, "baseFile")
    if not os.path.exists(outPath):
        os.makedirs(outPath)

    for flavorApk in os.listdir(BAK_PATH + baseApkDir):
        if os.path.isdir(os.path.join(BAK_PATH + baseApkDir, flavorApk)):
            baseApk = os.path.join(BAK_PATH + baseApkDir, flavorApk)

            out_flavor_path = os.path.join(outPath, flavorApk)
            if not os.path.exists(out_flavor_path):
                os.makedirs(out_flavor_path)

            for child_file in os.listdir(baseApk):
                item_file = os.path.join(baseApk, child_file)
                if os.path.isfile(item_file) and not item_file.endswith(".apk"):
                    out_file = os.path.join(out_flavor_path, child_file)
                    print "item_file", item_file
                    print "out_file", out_file
                    print ""
                    shutil.copyfile(item_file, out_file)



def copyPatchApkRenameToPath(outPath):
    outPath = os.path.join(outPath, "patch")
    if not os.path.exists(outPath):
        os.makedirs(outPath)

    patchPath = os.path.join(BASE_PROJECT_BUILD_DIR, "outputs/patch")
    for flavorApk in os.listdir(patchPath):
        if os.path.isdir(os.path.join(patchPath, flavorApk)):
            baseApk = os.path.join(patchPath, flavorApk + "/release/patch_signed_7zip.apk")
            print "baseApk", baseApk
            if os.path.exists(baseApk):
                if not FLAVORS_ALIAS_MAP.get(flavorApk, None) is None:
                    flavorApk = FLAVORS_ALIAS_MAP.get(flavorApk, None)
                out_file = os.path.join(outPath, flavorApk + "_patch_"+getApkVersionCode(baseApk)+"_7zip.apk")
                print "out_file", out_file
                shutil.copyfile(baseApk, out_file)


def getDateYyyyMMdd():
    return time.strftime("%Y%m%d", time.localtime(time.time()))


# 复制基包和 R 等文件到指定的目录
# copyBaseApkRenameToPath("/Users/ionesmile/Desktop/iLamp")
# copyBaseFileRenameToPath("/Users/ionesmile/Desktop/iLamp")

# 提取补丁包到指定目录
copyPatchApkRenameToPath("/Users/ionesmile/Desktop/iLamp")

因为上传不同的市场,需要内置不同的更新包,而 360 和 百度 的更新包在上传时会有冲突,所以需要根据情况来编译对应的更新包。

build.gradle 中 Flavor 编译,并设置输出文件名代码如下:

apply plugin: 'com.android.application'

android {

    productFlavors {
        none { }
        self { }
        self360 { }
        selfBaidu { }
    }

    // 设置输出文件名称(不必须)
    android.applicationVariants.all { variant ->
        variant.outputs.each { output ->
            def flavorAliasMap = ['self360':'360', 'selfBaidu':'baidu']
            String flavorType = flavorAliasMap.get(productFlavors[0].name) == null ? productFlavors[0].name : flavorAliasMap.get(productFlavors[0].name)
            String fileName = "android_ilight_pro_v${defaultConfig.versionName}_${new Date().format("yyyyMMdd")}_${flavorType}.apk"
            output.outputFile = new File(output.outputFile.parent, fileName)
        }
    }
}

dependencies {
    ...

    // 使用 flavorCompile 依赖不同库
    selfCompile(name: 'updateSelfLibrary_20171221', ext: 'aar')
    self360Compile(name: 'update360Library_20171221', ext: 'aar')
    selfBaiduCompile(name: 'updateBaiduLibrary_20171221', ext: 'aar')
}

repositories {
    flatDir { dirs 'libs' }

    jcenter()
}

build.gradle 中签名配置代码如下(不必须):

apply plugin: 'com.android.application'

android {

    // 配置签名包
    signingConfigs {
        config {
            keyAlias 'xxx'
            keyPassword 'xxxxxx'
            storeFile file('/Users/ionesmile/Documents/iOnesmileDocs/WorkDoc/keystore')
            storePassword 'xxxxxx'
        }
    }

    buildTypes {
        release {
            signingConfig signingConfigs.config
        }
    }
}

目前代码中使用反射的方式来出发检测版本,如下:

/** 动态导入不同的更新库,通过反射方式检查更新 **/
public final static void checkVersionUpdate(Activity activity) {
    final String[] updateClassArr = new String[]{
            "com.snaillove.common.update.DynamicUpdateSelf",
            "com.snaillove.common.update.DynamicUpdateBaidu",
            "com.snaillove.common.update.DynamicUpdate360"
    };
    for (String clazzName : updateClassArr) {
        try {
            Class cls = Class.forName(clazzName);
            Method setMethod = cls.getDeclaredMethod("exec", Activity.class);
            setMethod.invoke(cls.newInstance(), activity);
        } catch (Exception e) {
        }
    }
}