最让移动端开发崩溃的就是,刚强推了一个版本就出了重大bug,只能重新发版解决


但集成了热修复框架就能迎刃而解,经过对比最终选用了腾讯微信团队的Tinker


性能还不错,支持的修复场景也多,主要是免费(ಥ_ಥ) 


下面介绍一下接入流程



一、接入


推荐Android Gradle Plugin Version使用3.1.4

Gradle Version使用4.4


首先在gradle.properties配置版本号,方便后期维护


TINKER_VERSION=1.9.13.2


在项目的build.gradle配置依赖


classpath "com.tencent.tinker:tinker-patch-gradle-plugin:${TINKER_VERSION}"


在app的build.gradle配置依赖


compileOnly "com.tencent.tinker:tinker-android-anno:${TINKER_VERSION}"

annotationProcessor "com.tencent.tinker:tinker-android-anno:${TINKER_VERSION}"

implementation "com.tencent.tinker:tinker-android-lib:${TINKER_VERSION}"


二、多渠道配置


//证书配置

signingConfigs {
  debug {
    storeFile file('../debug.jks')
    storePassword ''
    keyAlias ''
    keyPassword ''
  }
  release {
    storeFile file('../release.jks')
    storePassword ''
    keyAlias ''
    keyPassword ''
  }

}


//build配置

buildTypes {
  debug {
    minifyEnabled false
    useProguard false
    proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
    signingConfig signingConfigs.debug
    buildConfigField "boolean", "ISDEBUG", "true"
  }

  release {
    minifyEnabled false
    useProguard false
    proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
    signingConfig signingConfigs.release
    buildConfigField "boolean", "ISDEBUG", "false"
  }
}

//多渠道配置

productFlavors {
  product {
    //生产
    applicationId "com.kevin.blog"
    versionCode 1
    versionName '1.0.0'
    manifestPlaceholders = [app_name : "@string/app_name"]
  }

  develop {
    //测试
    applicationId "com.kevin.develop.blog"
    versionCode 1
    versionName '1.0.0'
    manifestPlaceholders = [app_name : "@string/app_name_test"]
  }

}


三、Tinker配置


配置项含义可到官网查询


apply plugin: 'com.tencent.tinker.patch'
tinkerPatch {

  oldApk = ''
  ignoreWarning = false
  useSign = true
  tinkerEnable = true
  buildConfig {
    applyResourceMapping = ''
    tinkerId = ''
    keepDexApply = false
    isProtectedApp = false
    supportHotplugComponent = true
  }

  dex {
    dexMode = "jar"
    pattern = ["classes*.dex",
               "assets/secondary-dex-?.jar"]
    loader = ["tinker.sample.android.app.BaseBuildInfo"]
  }

  lib {
    pattern = ["lib/*/*.so"]
  }

  res {
    pattern = ["res/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]
    ignoreChange = ["assets/sample_meta.txt"]
    largeModSize = 100
  }
  sevenZip {
    zipArtifact = "com.tencent.mm:SevenZip:1.1.10"
  }
}


四、Gradle脚本实现自动打包


原创超级方便的脚本,可以把基础包和补丁复制到tinker目录下


//productVersion
def productVersionCode = 1
def productVersionName = '1.0.0'


//develop
def developVersionCode = 1
def developVersionName = '1.0.0'

//10 release  20 debug
def buildType = 10

// 1 product   2 develop
def productFlavor = 2

def patchPath = ''

def getPatchPath(){
  return patchPath
}

def bakPath = file("../tinker/")
ext {
  tinkerEnabled = true

  tinkerOldApkPath = ""
  tinkerApplyResourcePath = ""
  tinkerId = ""

  switch (buildType + productFlavor) {
    case 11:
      tinkerOldApkPath =
          "${bakPath}/product_release/TinkerBase${productVersionName}_product_release/TinkerBase${productVersionName}_product_release.apk"
      tinkerApplyResourcePath =
          "${bakPath}/product_release/TinkerBase${productVersionName}_product_release/TinkerBase${productVersionName}_product_release-R.txt"
      tinkerId = "${productVersionName}"
      patchPath = "/outputs/apk/product/tinkerPatch/product/release/patch_signed_7zip.apk"
      break
    case 12:
      tinkerOldApkPath =
          "${bakPath}/develop_release/TinkerBase${developVersionName}_develop_release/TinkerBase${developVersionName}_develop_release.apk"
      tinkerApplyResourcePath =
          "${bakPath}/develop_release/TinkerBase${developVersionName}_develop_release/TinkerBase${developVersionName}_develop_release-R.txt"
      tinkerId = "${developVersionName}"
      patchPath = "/outputs/apk/develop/tinkerPatch/develop/release/patch_signed_7zip.apk"
      break
    case 21:
      tinkerOldApkPath =
          "${bakPath}/product_debug/TinkerBase${productVersionName}_product_debug/TinkerBase${productVersionName}_product_debug.apk"
      tinkerApplyResourcePath =
          "${bakPath}/product_debug/TinkerBase${productVersionName}_product_debug/TinkerBase${productVersionName}_product_debug-R.txt"
      tinkerId = "${productVersionName}"
      patchPath = "/outputs/apk/product/tinkerPatch/product/debug/patch_signed_7zip.apk"
      break
    case 22:
      tinkerOldApkPath =
          "${bakPath}/develop_debug/TinkerBase${developVersionName}_develop_debug/TinkerBase${developVersionName}_develop_debug.apk"
      tinkerApplyResourcePath =
          "${bakPath}/develop_debug/TinkerBase${developVersionName}_develop_debug/TinkerBase${developVersionName}_develop_debug-R.txt"
      tinkerId = "${developVersionName}"
      patchPath = "/outputs/apk/product/tinkerPatch/product/debug/patch_signed_7zip.apk"
      break
  }
}


//文件复制
  def date = new Date().format("YYYY-MM-dd_HH-mm-ss")

  android.applicationVariants.all { variant ->
    def taskName = variant.name

    tasks.all {
      if ("assemble${taskName.capitalize()}".equalsIgnoreCase(it.name)) {

        it.doLast {
          def baseName = variant.baseName.replaceAll(/-/, "_")
          def fileNamePrefix = "${project.name}-${variant.baseName}"
          def newFileNamePrefix = "TinkerBase${variant.productFlavors[0].versionName}_${baseName}"
          def destPath = file("${bakPath}/${baseName}/TinkerBase${variant.productFlavors[0].versionName}_${baseName}")
          if (destPath != null && !destPath.exists()) {
            copy {
              from variant.outputs.first().outputFile
              into destPath
              rename { String fileName ->
                fileName.replace("${fileNamePrefix}.apk", "${newFileNamePrefix}.apk")
              }

              from "${buildDir}/intermediates/symbols/${variant.dirName}/R.txt"
              into destPath
              rename { String fileName -> fileName.replace("R.txt", "${newFileNamePrefix}-R.txt")
              }
            }
          }
        }
      }

      if ("tinkerPatch${taskName.capitalize()}".equalsIgnoreCase(it.name)) {
        it.doLast {
          def baseName = variant.baseName.replaceAll(/-/, "_")
          def fileNamePrefix = "patch_signed_7zip"
          def newFileNamePrefix = "TinkerPatch(Base_${baseName}_${variant.productFlavors[0].versionName})_${date}"
          def destPath = file("${bakPath}/${baseName}/TinkerBase${variant.productFlavors[0].versionName}_${baseName}/patch")


          copy {
            from "${buildDir}/${patchPath}"
            into destPath
            rename { String fileName ->
              fileName.replace("${fileNamePrefix}.apk", "${newFileNamePrefix}.apk")
            }

          }

        }
      }
    }
  }
}


五、Application改造


这是比较麻烦的一步,如果你的application不是继承Application那就麻烦了,需要改造为直接继承Application


改完之后可以愉快的开始了


首先建立TinkerApplication继承DefaultApplicationLike,代码如下


@SuppressWarnings("unused")
@DefaultLifeCycle(
    application = ".MyApplication",     //application类名
    loaderClass = "com.tencent.tinker.loader.TinkerLoader",
    flags = ShareConstants.TINKER_ENABLE_ALL,
    loadVerifyFlag = false)
public class TinkerApplication extends DefaultApplicationLike {

  private static ApplicationComponent mApplicationComponent;
  public static ApplicationComponent getApplicationComponent(){
    return mApplicationComponent;
  }
 

  

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

  
  @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
  @Override
  public void onBaseContextAttached(Context base) {
    super.onBaseContextAttached(base);

    //把你原来application内容移到这里

    
    MultiDex.install(base);
    TinkerInstaller.install(this);
  }

  


  @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
  public void registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks callback) {
    getApplication().registerActivityLifecycleCallbacks(callback);
  }
}


注意原application中引用application的地方改成getApplication()


然后把AndroidManifest.xml里application路径改为@DefaultLifeCycle注解中声明的路径即可


六、使用


安装


TinkerInstaller.onReceiveUpgradePatch(context, 路径);



卸载

Tinker.with(getApplicationContext()).cleanPatch();

Tinker.with(getApplicationContext()).cleanPatchByVersion(版本号)



杀死应用


ShareTinkerInternals.killAllOtherProcess(getApplicationContext());



Hack方式修复so


TinkerLoadLibrary.installNavitveLibraryABI(this, abi);



非Hack方式修复so


TinkerLoadLibrary.loadLibraryFromTinker(getApplicationContext(), "lib/" + abi, 模块名);

TinkerLoadLibrary.loadArmLibrary(getApplicationContext(), 模块名);

TinkerLoadLibrary.loadArmV7Library(getApplicationContext(), 模块名)





个人网站运营不易ヾ(◍°∇°◍)ノ゙如果有帮到你赞助一下吧

Kevin博客
  • 最新评论
  • 总共0条评论