欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android微信Tinker热更新详细使用

程序员文章站 2024-02-16 19:13:16
先看一下效果图 tinker已知问题 由于原理与系统限制,tinker有以下已知问题: tinker不支持修改androidmanifest.xml,ti...

先看一下效果图

Android微信Tinker热更新详细使用

tinker已知问题

由于原理与系统限制,tinker有以下已知问题:

  • tinker不支持修改androidmanifest.xml,tinker不支持新增四大组件;
  • 由于google play的开发者条款限制,不建议在gp渠道动态更新代码;
  • 在android n上,补丁对应用启动时间有轻微的影响;
  • 不支持部分三星android-21机型,加载补丁时会主动抛出”tinkerruntimeexception:checkdexinstall failed”;
  • 由于各个厂商的加固实现并不一致,在1.7.6以及之后的版本,tinker不再支持加固的动态更新;
  • 对于资源替换,不支持修改remoteview。例如transition动画,notification icon以及桌面图标。

1.首先在项目的build中,集成tinker插件 ,如下所示(目前最新版是1.7.6)

先看结构图,只有几个类而已:

Android微信Tinker热更新详细使用

项目中的build集成

buildscript {
 repositories {
 jcenter()
 }
 dependencies {
 classpath 'com.android.tools.build:gradle:2.2.3'
 classpath ('com.tencent.tinker:tinker-patch-gradle-plugin:1.7.6')
 // note: do not place your application dependencies here; they belong
 // in the individual module build.gradle files
 }
}

allprojects {
 repositories {
 jcenter()
 }
}

task clean(type: delete) {
 delete rootproject.builddir
}

1.再将app的build中的关联属性添加进去,这些属性都是经过测试过的,都有注释显示,如果自己需要其他属性,可以自己去github上查看并集成,文章末尾会送上地址,ps:官方的集成特别麻烦,有时候一整天都有可能搞不定,根据自己的需求和情况来添加,末尾会送上demo

apply plugin: 'com.android.application'

def javaversion = javaversion.version_1_7
android {
 compilesdkversion 23
 buildtoolsversion "23.0.2"

 compileoptions {
 sourcecompatibility javaversion
 targetcompatibility javaversion
 }
 //recommend
 dexoptions {
 jumbomode = true
 }


 defaultconfig {
 applicationid "com.tinker.demo.tinkerdemo"
 minsdkversion 15
 targetsdkversion 22
 versioncode 1
 versionname "1.0"
 testinstrumentationrunner "android.support.test.runner.androidjunitrunner"

 buildconfigfield "string", "message", "\"i am the base apk\""

 buildconfigfield "string", "tinker_id", "\"${gettinkeridvalue()}\""
 buildconfigfield "string", "platform", "\"all\""
 }

 signingconfigs {
 release {
  try {
  storefile file("./keystore/release.keystore")
  storepassword "testres"
  keyalias "testres"
  keypassword "testres"
  } catch (ex) {
  throw new invaliduserdataexception(ex.tostring())
  }
 }

 debug {
  storefile file("./keystore/debug.keystore")
 }
 }

 buildtypes {
 release {
  minifyenabled true
  signingconfig signingconfigs.release
  proguardfiles getdefaultproguardfile('proguard-android.txt'), 'proguard-rules.pro'
 }
 debug {
  debuggable true
  minifyenabled false
  signingconfig signingconfigs.debug
 }
 }

 sourcesets {
 main {
  jnilibs.srcdirs = ['libs']
 }
 }


}

dependencies {
 compile filetree(dir: 'libs', include: ['*.jar'])
 androidtestcompile('com.android.support.test.espresso:espresso-core:2.2.2', {
 exclude group: 'com.android.support', module: 'support-annotations'
 })
 compile "com.android.support:appcompat-v7:23.1.1"
 testcompile 'junit:junit:4.12'

 compile("com.tencent.tinker:tinker-android-lib:${tinker_version}") { changing = true }
 provided("com.tencent.tinker:tinker-android-anno:${tinker_version}") { changing = true }
 compile "com.android.support:multidex:1.0.1"
}

def gitsha() {
 try {
 // string gitrev = 'git rev-parse --short head'.execute(null, project.rootdir).text.trim()
 string gitrev = "1008611"
 if (gitrev == null) {
  throw new gradleexception("can't get git rev, you should add git to system path or just input test value, such as 'testtinkerid'")
 }
 return gitrev
 } catch (exception e) {
 throw new gradleexception("can't get git rev, you should add git to system path or just input test value, such as 'testtinkerid'")
 }
}

def bakpath = file("${builddir}/bakapk/")

ext {
 //for some reason, you may want to ignore tinkerbuild, such as instant run debug build?
 tinkerenabled = true

 //for normal build
 //old apk file to build patch apk
 tinkeroldapkpath = "${bakpath}/app-debug-0113-14-01-29.apk"
 //proguard mapping file to build patch apk
 tinkerapplymappingpath = "${bakpath}/app-debug-1018-17-32-47-mapping.txt"
 //resource r.txt to build patch apk, must input if there is resource changed
 tinkerapplyresourcepath = "${bakpath}/app-debug-0113-14-01-29-r.txt"

 //only use for build all flavor, if not, just ignore this field
 tinkerbuildflavordirectory = "${bakpath}/app-1018-17-32-47"
}

def getoldapkpath() {
 return hasproperty("old_apk") ? old_apk : ext.tinkeroldapkpath
}

def getapplymappingpath() {
 return hasproperty("apply_mapping") ? apply_mapping : ext.tinkerapplymappingpath
}

def getapplyresourcemappingpath() {
 return hasproperty("apply_resource") ? apply_resource : ext.tinkerapplyresourcepath
}

def gettinkeridvalue() {
 return hasproperty("tinker_id") ? tinker_id : gitsha()
}

def buildwithtinker() {
 return hasproperty("tinker_enable") ? tinker_enable : ext.tinkerenabled
}

def gettinkerbuildflavordirectory() {
 return ext.tinkerbuildflavordirectory
}

if (buildwithtinker()) {
 apply plugin: 'com.tencent.tinker.patch'

 tinkerpatch {
 /**
  * 默认为null
  * 将旧的apk和新的apk建立关联
  * 从build / bakapk添加apk
  */
 oldapk = getoldapkpath()
 /**
  * 可选,默认'false'
  *有些情况下我们可能会收到一些警告
  *如果ignorewarning为true,我们只是断言补丁过程
  * case 1:minsdkversion低于14,但是你使用dexmode与raw。
  * case 2:在androidmanifest.xml中新添加android组件,
  * case 3:装载器类在dex.loader {}不保留在主要的dex,
  * 它必须让tinker不工作。
  * case 4:在dex.loader {}中的loader类改变,
  * 加载器类是加载补丁dex。改变它们是没有用的。
  * 它不会崩溃,但这些更改不会影响。你可以忽略它
  * case 5:resources.arsc已经改变,但是我们不使用applyresourcemapping来构建
  */
 ignorewarning = false

 /**
  *可选,默认为“true”
  * 是否签名补丁文件
  * 如果没有,你必须自己做。否则在补丁加载过程中无法检查成功
  * 我们将使用sign配置与您的构建类型
  */
 usesign = true

 /**
  可选,默认为“true”
  是否使用tinker构建
  */
 tinkerenable = buildwithtinker()

 /**
  * 警告,applymapping会影响正常的android build!
  */
 buildconfig {
  /**
  *可选,默认为'null'
  * 如果我们使用tinkerpatch构建补丁apk,你最好应用旧的
  * apk映射文件如果minifyenabled是启用!
  * 警告:你必须小心,它会影响正常的组装构建!
  */
  applymapping = getapplymappingpath()
  /**
  *可选,默认为'null'
  * 很高兴保持资源id从r.txt文件,以减少java更改
  */
  applyresourcemapping = getapplyresourcemappingpath()

  /**
  *必需,默认'null'
  * 因为我们不想检查基地apk与md5在运行时(它是慢)
  * tinkerid用于在试图应用补丁时标识唯一的基本apk。
  * 我们可以使用git rev,svn rev或者简单的versioncode。
  * 我们将在您的清单中自动生成tinkerid
  */
  tinkerid = gettinkeridvalue()

  /**
  *如果keepdexapply为true,则表示dex指向旧apk的类。
  * 打开这可以减少dex diff文件大小。
  */
  keepdexapply = false
 }

 dex {
  /**
  *可选,默认'jar'
  * 只能是'raw'或'jar'。对于原始,我们将保持其原始格式
  * 对于jar,我们将使用zip格式重新包装dexes。
  * 如果你想支持下面14,你必须使用jar
  * 或者你想保存rom或检查更快,你也可以使用原始模式
  */
  dexmode = "jar"

  /**
  *必需,默认'[]'
  * apk中的dexes应该处理tinkerpatch
  * 它支持*或?模式。
  */
  pattern = ["classes*.dex",
   "assets/secondary-dex-?.jar"]
  /**
  *必需,默认'[]'
  * 警告,这是非常非常重要的,加载类不能随补丁改变。
  * 因此,它们将从补丁程序中删除。
  * 你必须把下面的类放到主要的dex。
  * 简单地说,你应该添加自己的应用程序{@code tinker.sample.android.sampleapplication}
  * 自己的tinkerloader,和你使用的类
  *
  */
  loader = [
   //use sample, let basebuildinfo unchangeable with tinker
   "tinker.sample.android.app.basebuildinfo"
  ]
 }

 lib {
  /**
  可选,默认'[]'
  apk中的图书馆应该处理tinkerpatch
  它支持*或?模式。
  对于资源库,我们只是在补丁目录中恢复它们
  你可以得到他们在tinkerloadresult与tinker
  */
  pattern = ["lib/armeabi/*.so"]
 }

 res {
  /**
  *可选,默认'[]'
  * apk中的什么资源应该处理tinkerpatch
  * 它支持*或?模式。
  * 你必须包括你在这里的所有资源,
  * 否则,他们不会重新包装在新的apk资源。
  */
  pattern = ["res/*", "assets/*", "resources.arsc", "androidmanifest.xml"]

  /**
  *可选,默认'[]'
  *资源文件排除模式,忽略添加,删除或修改资源更改
  * *它支持*或?模式。
  * *警告,我们只能使用文件没有relative与resources.arsc
  */
  ignorechange = ["assets/sample_meta.txt"]

  /**
  *默认100kb
  * *对于修改资源,如果它大于'largemodsize'
  * *我们想使用bsdiff算法来减少补丁文件的大小
  */
  largemodsize = 100
 }

 packageconfig {
  /**
  *可选,默认'tinker_id,tinker_id_value','new_tinker_id,new_tinker_id_value'
  * 包元文件gen。路径是修补程序文件中的assets / package_meta.txt
  * 你可以在您自己的packagecheck方法中使用securitycheck.getpackageproperties()
  * 或tinkerloadresult.getpackageconfigbyname
  * 我们将从旧的apk清单为您自动获取tinker_id,
  * 其他配置文件(如下面的patchmessage)不是必需的
  */
  configfield("patchmessage", "tinker is sample to use")
  /**
  *只是一个例子,你可以使用如sdkversion,品牌,渠道...
  * 你可以在samplepatchlistener中解析它。
  * 然后你可以使用补丁条件!
  */
  configfield("platform", "all")
  /**
  * 补丁版本通过packageconfig
  */
  configfield("patchversion", "1.0")
 }
 //或者您可以添加外部的配置文件,或从旧apk获取元值
 //project.tinkerpatch.packageconfig.configfield("test1", project.tinkerpatch.packageconfig.getmetadatafromoldapk("test"))
 //project.tinkerpatch.packageconfig.configfield("test2", "sample")

 /**
  * 如果你不使用zipartifact或者path,我们只是使用7za来试试
  */
 sevenzip {
  /**
  * 可选,默认'7za'
  * 7zip工件路径,它将使用正确的7za与您的平台
  */
  zipartifact = "com.tencent.mm:sevenzip:1.1.10"
  /**
  * 可选,默认'7za'
  * 你可以自己指定7za路径,它将覆盖zipartifact值
  */
// path = "/usr/local/bin/7za"
 }
 }

 list<string> flavors = new arraylist<>();
 project.android.productflavors.each {flavor ->
 flavors.add(flavor.name)
 }
 boolean hasflavors = flavors.size() > 0
 /**
 * bak apk and mapping
 */
 android.applicationvariants.all { variant ->
 /**
  * task type, you want to bak
  */
 def taskname = variant.name
 def date = new date().format("mmdd-hh-mm-ss")

 tasks.all {
  if ("assemble${taskname.capitalize()}".equalsignorecase(it.name)) {

  it.dolast {
   copy {
   def filenameprefix = "${project.name}-${variant.basename}"
   def newfilenameprefix = hasflavors ? "${filenameprefix}" : "${filenameprefix}-${date}"

   def destpath = hasflavors ? file("${bakpath}/${project.name}-${date}/${variant.flavorname}") : bakpath
   from variant.outputs.outputfile
   into destpath
   rename { string filename ->
    filename.replace("${filenameprefix}.apk", "${newfilenameprefix}.apk")
   }

   from "${builddir}/outputs/mapping/${variant.dirname}/mapping.txt"
   into destpath
   rename { string filename ->
    filename.replace("mapping.txt", "${newfilenameprefix}-mapping.txt")
   }

   from "${builddir}/intermediates/symbols/${variant.dirname}/r.txt"
   into destpath
   rename { string filename ->
    filename.replace("r.txt", "${newfilenameprefix}-r.txt")
   }
   }
  }
  }
 }
 }
 project.afterevaluate {
 //sample use for build all flavor for one time
 if (hasflavors) {
  task(tinkerpatchallflavorrelease) {
  group = 'tinker'
  def originoldpath = gettinkerbuildflavordirectory()
  for (string flavor : flavors) {
   def tinkertask = tasks.getbyname("tinkerpatch${flavor.capitalize()}release")
   dependson tinkertask
   def preassembletask = tasks.getbyname("process${flavor.capitalize()}releasemanifest")
   preassembletask.dofirst {
   string flavorname = preassembletask.name.substring(7, 8).tolowercase() + preassembletask.name.substring(8, preassembletask.name.length() - 15)
   project.tinkerpatch.oldapk = "${originoldpath}/${flavorname}/${project.name}-${flavorname}-release.apk"
   project.tinkerpatch.buildconfig.applymapping = "${originoldpath}/${flavorname}/${project.name}-${flavorname}-release-mapping.txt"
   project.tinkerpatch.buildconfig.applyresourcemapping = "${originoldpath}/${flavorname}/${project.name}-${flavorname}-release-r.txt"

   }

  }
  }

  task(tinkerpatchallflavordebug) {
  group = 'tinker'
  def originoldpath = gettinkerbuildflavordirectory()
  for (string flavor : flavors) {
   def tinkertask = tasks.getbyname("tinkerpatch${flavor.capitalize()}debug")
   dependson tinkertask
   def preassembletask = tasks.getbyname("process${flavor.capitalize()}debugmanifest")
   preassembletask.dofirst {
   string flavorname = preassembletask.name.substring(7, 8).tolowercase() + preassembletask.name.substring(8, preassembletask.name.length() - 13)
   project.tinkerpatch.oldapk = "${originoldpath}/${flavorname}/${project.name}-${flavorname}-debug.apk"
   project.tinkerpatch.buildconfig.applymapping = "${originoldpath}/${flavorname}/${project.name}-${flavorname}-debug-mapping.txt"
   project.tinkerpatch.buildconfig.applyresourcemapping = "${originoldpath}/${flavorname}/${project.name}-${flavorname}-debug-r.txt"
   }

  }
  }
 }
 }
}

3.在清单文件中集成application和服务 ,name的application必须是.amsky,如果你添加不进去,或者是红色的话,请先build一下,如果你已经有了自己的application,后面我会说怎么来集成,service中做的操作是在你加载成功热更新插件后,会提示你更新成功,并且这里做了锁屏操作就会加载热更新插件,继续往下看。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 package="com.tinker.demo.tinkerdemo">

 <uses-permission android:name="android.permission.write_external_storage"/>
 <uses-permission android:name="android.permission.read_external_storage"/>

 <application
 android:allowbackup="true"
 android:icon="@mipmap/ic_launcher"
 android:label="@string/app_name"
 android:supportsrtl="true"
 android:name=".amsky"
 android:theme="@style/apptheme">

 <service
  android:name=".service.sampleresultservice"
  android:exported="false"/>

 <activity android:name=".mainactivity">
  <intent-filter>
  <action android:name="android.intent.action.main" />

  <category android:name="android.intent.category.launcher" />
  </intent-filter>
 </activity>
 </application>

</manifest>

4.到这里就已经基本集成的差不多了,剩下的就是代码里面的集成,首先是application,这里主要说如果是自已已经存在的application的时候改怎么操作 ,这个applicaiton可以说就是自己的一个application,只不过写法,要这样去写,可以在oncreate中做自己的一些操作,只不过清单文件中,要写amsky

@suppresswarnings("unused")
@defaultlifecycle(application = "com.tinker.demo.tinkerdemo.amsky",
   flags = shareconstants.tinker_enable_all,
   loadverifyflag = false)
public class sampleapplicationlike extends defaultapplicationlike {
 private static final string tag = "tinker.sampleapplicationlike";

public sampleapplicationlike(application application, int tinkerflags, boolean tinkerloadverifyflag,long applicationstartelapsedtime, long applicationstartmillistime, intent tinkerresultintent,resources[] resources, classloader[] classloader, assetmanager[] assetmanager) {

 super(application,tinkerflags,tinkerloadverifyflag,applicationstartelapsedtime,applicationstartmillistime, tinkerresultintent, resources, classloader, assetmanager);

 }

 /**
 * install multidex before install tinker
 * so we don't need to put the tinker lib classes in the main dex
 *
 * @param base
 */
 @targetapi(build.version_codes.ice_cream_sandwich)
 @override
 public void onbasecontextattached(context base) {
 super.onbasecontextattached(base);
 //multidex必须在tinker初始化之前
 multidex.install(base);
 //这里就是初始化tinker
 tinkerinstaller.install(this,new defaultloadreporter(getapplication()),new defaultpatchreporter(getapplication()),
 new defaultpatchlistener(getapplication()),sampleresultservice.class,new upgradepatch());
 tinker tinker = tinker.with(getapplication());
 //这个只是一个toast提示
 toast.maketext(
 getapplication(),"没鸟用,就是toast提示而已", toast.length_short).show();
 }

 @override
 public void oncreate() {
 super.oncreate();
 //这里可以做自己的操作
 }

 @targetapi(build.version_codes.ice_cream_sandwich)
 public void registeractivitylifecyclecallbacks(application.activitylifecyclecallbacks callback) {
 getapplication().registeractivitylifecyclecallbacks(callback);
 }

}

5.这里就是在mainactivity中来加载热更新文件,在点击加载的时候,就直接锁屏加载(不要删除service),当然退出app,下次进来也是可以加载的吗,这里加载补丁插件的话,路径可以自己设置,我是放在根目录的debug文件夹当中的,并且我的补丁插件名字叫patch,可以自行更改。

public class mainactivity extends appcompatactivity {

 @override
 protected void oncreate(bundle savedinstancestate) {
 super.oncreate(savedinstancestate);
 setcontentview(r.layout.activity_main);
 }

 /**
 * 加载热补丁插件
 * @param v
 */
 public void loadpatch(view v) {
 tinkerinstaller.onreceiveupgradepatch(getapplicationcontext(), "/sdcard/debug/patch.apk");
 }

 /**
 * 杀死应用加载补丁
 * @param v
 */
 public void killapp(view v) {
 sharetinkerinternals.killallotherprocess(getapplicationcontext());
 android.os.process.killprocess(android.os.process.mypid());
 }

 @override
 protected void onresume() {
 super.onresume();
 utils.setbackground(false);
 }

 @override
 protected void onpause() {
 super.onpause();
 utils.setbackground(true);
 }
}

6.service文件

public class sampleresultservice extends defaulttinkerresultservice {
 private static final string tag = "tinker.sampleresultservice";


 @override
 public void onpatchresult(final patchresult result) {
 if (result == null) {
  tinkerlog.e(tag, "sampleresultservice received null result!!!!");
  return;
 }
 tinkerlog.i(tag, "sampleresultservice receive result: %s", result.tostring());

 //first, we want to kill the recover process
 tinkerserviceinternals.killtinkerpatchserviceprocess(getapplicationcontext());

 handler handler = new handler(looper.getmainlooper());
 handler.post(new runnable() {
  @override
  public void run() {
  if (result.issuccess) {
   toast.maketext(getapplicationcontext(), "patch success, please restart process", toast.length_long).show();
  } else {
   toast.maketext(getapplicationcontext(), "patch fail, please check reason", toast.length_long).show();
  }
  }
 });
 // is success and newpatch, it is nice to delete the raw file, and restart at once
 // for old patch, you can't delete the patch file
 if (result.issuccess) {
  file rawfile = new file(result.rawpatchfilepath);
  if (rawfile.exists()) {
  tinkerlog.i(tag, "save delete raw patch file");
  sharepatchfileutil.safedeletefile(rawfile);
  }
  //not like tinkerresultservice, i want to restart just when i am at background!
  //if you have not install tinker this moment, you can use tinkerapplicationhelper api
  if (checkifneedkill(result)) {
  if (utils.isbackground()) {
   tinkerlog.i(tag, "it is in background, just restart process");
   restartprocess();
  } else {
   //we can wait process at background, such as onappbackground
   //or we can restart when the screen off
   tinkerlog.i(tag, "tinker wait screen to restart process");
   new screenstate(getapplicationcontext(), new screenstate.ionscreenoff() {
   @override
   public void onscreenoff() {
    restartprocess();
   }
   });
  }
  } else {
  tinkerlog.i(tag, "i have already install the newly patch version!");
  }
 }
 }

 /**
 * you can restart your process through service or broadcast
 */
 private void restartprocess() {
 tinkerlog.i(tag, "app is background now, i can kill quietly");
 //you can send service or broadcast intent to restart your process
 android.os.process.killprocess(android.os.process.mypid());
 }

 static class screenstate {
 interface ionscreenoff {
  void onscreenoff();
 }

 screenstate(context context, final ionscreenoff onscreenoffinterface) {
  intentfilter filter = new intentfilter();
  filter.addaction(intent.action_screen_off);
  context.registerreceiver(new broadcastreceiver() {

  @override
  public void onreceive(context context, intent in) {
   string action = in == null ? "" : in.getaction();
   tinkerlog.i(tag, "screenreceiver action [%s] ", action);
   if (intent.action_screen_off.equals(action)) {

   context.unregisterreceiver(this);

   if (onscreenoffinterface != null) {
    onscreenoffinterface.onscreenoff();
   }
   }
  }
  }, filter);
 }
 }

}

7.utils文件

public class utils {

 /**
 * the error code define by myself
 * should after {@code shareconstants.error_patch_inservice
 */
 public static final int error_patch_googleplay_channel = -5;
 public static final int error_patch_rom_space  = -6;
 public static final int error_patch_memory_limit  = -7;
 public static final int error_patch_already_apply  = -8;
 public static final int error_patch_crash_limit  = -9;
 public static final int error_patch_retry_count_limit = -10;
 public static final int error_patch_condition_not_satisfied = -11;

 public static final string platform = "platform";

 public static final int min_memory_heap_size = 45;

 private static boolean background = false;

 public static boolean isgoogleplay() {
 return false;
 }

 public static boolean isbackground() {
 return background;
 }

 public static void setbackground(boolean back) {
 background = back;
 }

 public static int checkforpatchrecover(long roomsize, int maxmemory) {
 if (utils.isgoogleplay()) {
  return utils.error_patch_googleplay_channel;
 }
 if (maxmemory < min_memory_heap_size) {
  return utils.error_patch_memory_limit;
 }
 //or you can mention user to clean their rom space!
 if (!checkromspaceenough(roomsize)) {
  return utils.error_patch_rom_space;
 }

 return shareconstants.error_patch_ok;
 }

 public static boolean isxposedexists(throwable thr) {
 stacktraceelement[] stacktraces = thr.getstacktrace();
 for (stacktraceelement stacktrace : stacktraces) {
  final string clazzname = stacktrace.getclassname();
  if (clazzname != null && clazzname.contains("de.robv.android.xposed.xposedbridge")) {
  return true;
  }
 }
 return false;
 }

 @deprecated
 public static boolean checkromspaceenough(long limitsize) {
 long allsize;
 long availablesize = 0;
 try {
  file data = environment.getdatadirectory();
  statfs sf = new statfs(data.getpath());
  availablesize = (long) sf.getavailableblocks() * (long) sf.getblocksize();
  allsize = (long) sf.getblockcount() * (long) sf.getblocksize();
 } catch (exception e) {
  allsize = 0;
 }

 if (allsize != 0 && availablesize > limitsize) {
  return true;
 }
 return false;
 }

 public static string getexceptioncausestring(final throwable ex) {
 final bytearrayoutputstream bos = new bytearrayoutputstream();
 final printstream ps = new printstream(bos);

 try {
  // print directly
  throwable t = ex;
  while (t.getcause() != null) {
  t = t.getcause();
  }
  t.printstacktrace(ps);
  return tovisualstring(bos.tostring());
 } finally {
  try {
  bos.close();
  } catch (ioexception e) {
  e.printstacktrace();
  }
 }
 }

 private static string tovisualstring(string src) {
 boolean cutflg = false;

 if (null == src) {
  return null;
 }

 char[] chr = src.tochararray();
 if (null == chr) {
  return null;
 }

 int i = 0;
 for (; i < chr.length; i++) {
  if (chr[i] > 127) {
  chr[i] = 0;
  cutflg = true;
  break;
  }
 }

 if (cutflg) {
  return new string(chr, 0, i);
 } else {
  return src;
 }
 }
}

到这里就已经集成完毕,下面来说下使用的方法

这是有bug的版本,我们测试就使用assembledebug来测试 ,注意没点击assembledebug之前,build文件夹里面是没有bakapk文件夹的

Android微信Tinker热更新详细使用Android微信Tinker热更新详细使用

2.点击assembledebug之后会出现bakapk这个文件夹,里面就有apk文件,如果失败,记得clean一下,然后build一下

Android微信Tinker热更新详细使用

3.接下来在build文件夹里面,更改ext中的属性,将bakapk中生成的apk文件和r文件复制到ext这里,如果你打的release包有mapping的话同样复制到这里,我们这里是debug测试,所以没有mapping文件

Android微信Tinker热更新详细使用

4.下面就修改我们需要更新,或者更改的bug,我这里是添加一张图片,并且更改标题显示

这是有bug的版本,我还没添加图片,更改标题

Android微信Tinker热更新详细使用

这里我添加了一张aa的图片,并且更改了标题

Android微信Tinker热更新详细使用

5.接下来我们运行tinker下面的tinkerpatchdebug,来生成补丁包,这个补丁包在outputs下面

Android微信Tinker热更新详细使用

点击完成后,就会生成tinkerpatch文件夹

Android微信Tinker热更新详细使用

将tinkerpatch文件夹下面的patch_signed_7zip.apk文件,粘贴出来,改成你的mainactivity中加载的文件名字,我这里叫patch,然后点击加载没加载之前

Android微信Tinker热更新详细使用

加载之后,锁频,解锁 ,补丁已经加载出来了,并且文件夹中的补丁已经不在了,因为它和老apk合并了

Android微信Tinker热更新详细使用

注意

签名文件的话 在build的signingconfigs中设置,以及左侧的kestore文件夹中设置 ,如下图
Android微信Tinker热更新详细使用
Android微信Tinker热更新详细使用

项目github地址:tinkerdemo

tinker原项目地址https://github.com/tencent/tinker
tinker使用指南:https://github.com/tencent/tinker/wiki
tinker一键集成(这个简单,但是不能从自己服务器上下载补丁,不需配置tinker自己的后台,有部*限性,自行选择):https://github.com/tinkerpatch/tinkerpatch-sdk/blob/master/docs/tinkerpatch-android-sdk.md
tinker一键集成后台

更多精彩内容请点击《android微信开发教程汇总》,《java微信开发教程汇总》欢迎大家学习阅读。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。