亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Android微信Tinker熱更新詳細(xì)使用

 更新時(shí)間:2017年01月16日 14:15:00   作者:Gimi丶  
這篇文章主要介紹了Android微信Tinker熱更新的詳細(xì)使用方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

先看一下效果圖

這里寫(xiě)圖片描述

Tinker已知問(wèn)題

由于原理與系統(tǒng)限制,Tinker有以下已知問(wèn)題:

  • Tinker不支持修改AndroidManifest.xml,Tinker不支持新增四大組件;
  • 由于Google Play的開(kāi)發(fā)者條款限制,不建議在GP渠道動(dòng)態(tài)更新代碼;
  • 在Android N上,補(bǔ)丁對(duì)應(yīng)用啟動(dòng)時(shí)間有輕微的影響;
  • 不支持部分三星android-21機(jī)型,加載補(bǔ)丁時(shí)會(huì)主動(dòng)拋出”TinkerRuntimeException:checkDexInstall failed”;
  • 由于各個(gè)廠商的加固實(shí)現(xiàn)并不一致,在1.7.6以及之后的版本,tinker不再支持加固的動(dòng)態(tài)更新;
  • 對(duì)于資源替換,不支持修改remoteView。例如transition動(dòng)畫(huà),notification icon以及桌面圖標(biāo)。

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

先看結(jié)構(gòu)圖,只有幾個(gè)類(lèi)而已:

這里寫(xiě)圖片描述

項(xiàng)目中的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中的關(guān)聯(lián)屬性添加進(jìn)去,這些屬性都是經(jīng)過(guò)測(cè)試過(guò)的,都有注釋顯示,如果自己需要其他屬性,可以自己去github上查看并集成,文章末尾會(huì)送上地址,ps:官方的集成特別麻煩,有時(shí)候一整天都有可能搞不定,根據(jù)自己的需求和情況來(lái)添加,末尾會(huì)送上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 {
 /**
  * 默認(rèn)為null
  * 將舊的apk和新的apk建立關(guān)聯(lián)
  * 從build / bakApk添加apk
  */
 oldApk = getOldApkPath()
 /**
  * 可選,默認(rèn)'false'
  *有些情況下我們可能會(huì)收到一些警告
  *如果ignoreWarning為true,我們只是斷言補(bǔ)丁過(guò)程
  * case 1:minSdkVersion低于14,但是你使用dexMode與raw。
  * case 2:在AndroidManifest.xml中新添加Android組件,
  * case 3:裝載器類(lèi)在dex.loader {}不保留在主要的dex,
  * 它必須讓tinker不工作。
  * case 4:在dex.loader {}中的loader類(lèi)改變,
  * 加載器類(lèi)是加載補(bǔ)丁dex。改變它們是沒(méi)有用的。
  * 它不會(huì)崩潰,但這些更改不會(huì)影響。你可以忽略它
  * case 5:resources.arsc已經(jīng)改變,但是我們不使用applyResourceMapping來(lái)構(gòu)建
  */
 ignoreWarning = false

 /**
  *可選,默認(rèn)為“true”
  * 是否簽名補(bǔ)丁文件
  * 如果沒(méi)有,你必須自己做。否則在補(bǔ)丁加載過(guò)程中無(wú)法檢查成功
  * 我們將使用sign配置與您的構(gòu)建類(lèi)型
  */
 useSign = true

 /**
  可選,默認(rèn)為“true”
  是否使用tinker構(gòu)建
  */
 tinkerEnable = buildWithTinker()

 /**
  * 警告,applyMapping會(huì)影響正常的android build!
  */
 buildConfig {
  /**
  *可選,默認(rèn)為'null'
  * 如果我們使用tinkerPatch構(gòu)建補(bǔ)丁apk,你最好應(yīng)用舊的
  * apk映射文件如果minifyEnabled是啟用!
  * 警告:你必須小心,它會(huì)影響正常的組裝構(gòu)建!
  */
  applyMapping = getApplyMappingPath()
  /**
  *可選,默認(rèn)為'null'
  * 很高興保持資源ID從R.txt文件,以減少java更改
  */
  applyResourceMapping = getApplyResourceMappingPath()

  /**
  *必需,默認(rèn)'null'
  * 因?yàn)槲覀儾幌霗z查基地apk與md5在運(yùn)行時(shí)(它是慢)
  * tinkerId用于在試圖應(yīng)用補(bǔ)丁時(shí)標(biāo)識(shí)唯一的基本apk。
  * 我們可以使用git rev,svn rev或者簡(jiǎn)單的versionCode。
  * 我們將在您的清單中自動(dòng)生成tinkerId
  */
  tinkerId = getTinkerIdValue()

  /**
  *如果keepDexApply為true,則表示dex指向舊apk的類(lèi)。
  * 打開(kāi)這可以減少dex diff文件大小。
  */
  keepDexApply = false
 }

 dex {
  /**
  *可選,默認(rèn)'jar'
  * 只能是'raw'或'jar'。對(duì)于原始,我們將保持其原始格式
  * 對(duì)于jar,我們將使用zip格式重新包裝dexes。
  * 如果你想支持下面14,你必須使用jar
  * 或者你想保存rom或檢查更快,你也可以使用原始模式
  */
  dexMode = "jar"

  /**
  *必需,默認(rèn)'[]'
  * apk中的dexes應(yīng)該處理tinkerPatch
  * 它支持*或?模式。
  */
  pattern = ["classes*.dex",
   "assets/secondary-dex-?.jar"]
  /**
  *必需,默認(rèn)'[]'
  * 警告,這是非常非常重要的,加載類(lèi)不能隨補(bǔ)丁改變。
  * 因此,它們將從補(bǔ)丁程序中刪除。
  * 你必須把下面的類(lèi)放到主要的dex。
  * 簡(jiǎn)單地說(shuō),你應(yīng)該添加自己的應(yīng)用程序{@code tinker.sample.android.SampleApplication}
  * 自己的tinkerLoader,和你使用的類(lèi)
  *
  */
  loader = [
   //use sample, let BaseBuildInfo unchangeable with tinker
   "tinker.sample.android.app.BaseBuildInfo"
  ]
 }

 lib {
  /**
  可選,默認(rèn)'[]'
  apk中的圖書(shū)館應(yīng)該處理tinkerPatch
  它支持*或?模式。
  對(duì)于資源庫(kù),我們只是在補(bǔ)丁目錄中恢復(fù)它們
  你可以得到他們?cè)赥inkerLoadResult與Tinker
  */
  pattern = ["lib/armeabi/*.so"]
 }

 res {
  /**
  *可選,默認(rèn)'[]'
  * apk中的什么資源應(yīng)該處理tinkerPatch
  * 它支持*或?模式。
  * 你必須包括你在這里的所有資源,
  * 否則,他們不會(huì)重新包裝在新的apk資源。
  */
  pattern = ["res/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]

  /**
  *可選,默認(rèn)'[]'
  *資源文件排除模式,忽略添加,刪除或修改資源更改
  * *它支持*或?模式。
  * *警告,我們只能使用文件沒(méi)有relative與resources.arsc
  */
  ignoreChange = ["assets/sample_meta.txt"]

  /**
  *默認(rèn)100kb
  * *對(duì)于修改資源,如果它大于'largeModSize'
  * *我們想使用bsdiff算法來(lái)減少補(bǔ)丁文件的大小
  */
  largeModSize = 100
 }

 packageConfig {
  /**
  *可選,默認(rèn)'TINKER_ID,TINKER_ID_VALUE','NEW_TINKER_ID,NEW_TINKER_ID_VALUE'
  * 包元文件gen。路徑是修補(bǔ)程序文件中的assets / package_meta.txt
  * 你可以在您自己的PackageCheck方法中使用securityCheck.getPackageProperties()
  * 或TinkerLoadResult.getPackageConfigByName
  * 我們將從舊的apk清單為您自動(dòng)獲取TINKER_ID,
  * 其他配置文件(如下面的patchMessage)不是必需的
  */
  configField("patchMessage", "tinker is sample to use")
  /**
  *只是一個(gè)例子,你可以使用如sdkVersion,品牌,渠道...
  * 你可以在SamplePatchListener中解析它。
  * 然后你可以使用補(bǔ)丁條件!
  */
  configField("platform", "all")
  /**
  * 補(bǔ)丁版本通過(guò)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來(lái)試試
  */
 sevenZip {
  /**
  * 可選,默認(rèn)'7za'
  * 7zip工件路徑,它將使用正確的7za與您的平臺(tái)
  */
  zipArtifact = "com.tencent.mm:SevenZip:1.1.10"
  /**
  * 可選,默認(rèn)'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和服務(wù) ,name的application必須是.AMSKY,如果你添加不進(jìn)去,或者是紅色的話(huà),請(qǐng)先build一下,如果你已經(jīng)有了自己的application,后面我會(huì)說(shuō)怎么來(lái)集成,Service中做的操作是在你加載成功熱更新插件后,會(huì)提示你更新成功,并且這里做了鎖屏操作就會(huì)加載熱更新插件,繼續(xù)往下看。

<?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.到這里就已經(jīng)基本集成的差不多了,剩下的就是代碼里面的集成,首先是application,這里主要說(shuō)如果是自已已經(jīng)存在的application的時(shí)候改怎么操作 ,這個(gè)applicaiton可以說(shuō)就是自己的一個(gè)application,只不過(guò)寫(xiě)法,要這樣去寫(xiě),可以在onCreate中做自己的一些操作,只不過(guò)清單文件中,要寫(xiě)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());
 //這個(gè)只是一個(gè)Toast提示
 Toast.makeText(
 getApplication(),"沒(méi)鳥(niǎo)用,就是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中來(lái)加載熱更新文件,在點(diǎn)擊加載的時(shí)候,就直接鎖屏加載(不要?jiǎng)h除service),當(dāng)然退出app,下次進(jìn)來(lái)也是可以加載的嗎,這里加載補(bǔ)丁插件的話(huà),路徑可以自己設(shè)置,我是放在根目錄的debug文件夾當(dāng)中的,并且我的補(bǔ)丁插件名字叫patch,可以自行更改。

public class MainActivity extends AppCompatActivity {

 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 }

 /**
 * 加載熱補(bǔ)丁插件
 * @param v
 */
 public void loadPatch(View v) {
 TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(), "/sdcard/debug/patch.apk");
 }

 /**
 * 殺死應(yīng)用加載補(bǔ)丁
 * @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;
 }
 }
}

到這里就已經(jīng)集成完畢,下面來(lái)說(shuō)下使用的方法

這是有bug的版本,我們測(cè)試就使用assembleDebug來(lái)測(cè)試 ,注意沒(méi)點(diǎn)擊assembleDebug之前,build文件夾里面是沒(méi)有bakApk文件夾的

這里寫(xiě)圖片描述這里寫(xiě)圖片描述

2.點(diǎn)擊assembleDebug之后會(huì)出現(xiàn)bakApk這個(gè)文件夾,里面就有apk文件,如果失敗,記得clean一下,然后build一下

這里寫(xiě)圖片描述

3.接下來(lái)在build文件夾里面,更改ext中的屬性,將bakApk中生成的apk文件和R文件復(fù)制到ext這里,如果你打的release包有mapping的話(huà)同樣復(fù)制到這里,我們這里是debug測(cè)試,所以沒(méi)有mapping文件

這里寫(xiě)圖片描述

4.下面就修改我們需要更新,或者更改的bug,我這里是添加一張圖片,并且更改標(biāo)題顯示

這是有bug的版本,我還沒(méi)添加圖片,更改標(biāo)題

這里寫(xiě)圖片描述

這里我添加了一張aa的圖片,并且更改了標(biāo)題

這里寫(xiě)圖片描述

5.接下來(lái)我們運(yùn)行tinker下面的tinkerPatchDebug,來(lái)生成補(bǔ)丁包,這個(gè)補(bǔ)丁包在outputs下面

這里寫(xiě)圖片描述

點(diǎn)擊完成后,就會(huì)生成tinkerPatch文件夾

這里寫(xiě)圖片描述

將tinkerPatch文件夾下面的patch_signed_7zip.apk文件,粘貼出來(lái),改成你的MainActivity中加載的文件名字,我這里叫patch,然后點(diǎn)擊加載沒(méi)加載之前

這里寫(xiě)圖片描述

加載之后,鎖頻,解鎖 ,補(bǔ)丁已經(jīng)加載出來(lái)了,并且文件夾中的補(bǔ)丁已經(jīng)不在了,因?yàn)樗屠蟖pk合并了

這里寫(xiě)圖片描述

注意

簽名文件的話(huà) 在build的signingConfigs中設(shè)置,以及左側(cè)的kestore文件夾中設(shè)置 ,如下圖
這里寫(xiě)圖片描述
這里寫(xiě)圖片描述

項(xiàng)目github地址:TinkerDemo

Tinker原項(xiàng)目地址https://github.com/Tencent/tinker
Tinker使用指南:https://github.com/Tencent/tinker/wiki
Tinker一鍵集成(這個(gè)簡(jiǎn)單,但是不能從自己服務(wù)器上下載補(bǔ)丁,不需配置Tinker自己的后臺(tái),有部分局限性,自行選擇):https://github.com/TinkerPatch/tinkerpatch-sdk/blob/master/docs/tinkerpatch-android-sdk.md
Tinker一鍵集成后臺(tái)http://www.tinkerpatch.com/

更多精彩內(nèi)容請(qǐng)點(diǎn)擊《Android微信開(kāi)發(fā)教程匯總》,《java微信開(kāi)發(fā)教程匯總》歡迎大家學(xué)習(xí)閱讀。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Flutter onTap中讓你脫穎而出的5條規(guī)則

    Flutter onTap中讓你脫穎而出的5條規(guī)則

    這篇文章主要為大家介紹了Flutter onTap中讓你脫穎而出的5條規(guī)則,小事情決定了你的熟練程度,這些小細(xì)節(jié)的有趣之處在于它們的豐富性
    2023-11-11
  • Android實(shí)現(xiàn)Service下載文件,Notification顯示下載進(jìn)度的示例

    Android實(shí)現(xiàn)Service下載文件,Notification顯示下載進(jìn)度的示例

    本篇文章主要介紹了Android實(shí)現(xiàn)Service下載文件,Notification顯示下載進(jìn)度,具有一定的參考價(jià)值,有興趣的可以了解一下。
    2017-01-01
  • Android Studio添加日志過(guò)濾方式

    Android Studio添加日志過(guò)濾方式

    這篇文章主要介紹了Android Studio添加日志過(guò)濾方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-04-04
  • 很實(shí)用的Android日期計(jì)算類(lèi)

    很實(shí)用的Android日期計(jì)算類(lèi)

    這篇文章主要為大家詳細(xì)介紹了很實(shí)用的Android日期計(jì)算類(lèi),一個(gè)是獲取與今天時(shí)間差,另一個(gè)是日期格式化工具類(lèi),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-02-02
  • Android json解析及簡(jiǎn)單例子

    Android json解析及簡(jiǎn)單例子

    這篇文章主要為大家詳細(xì)介紹了Android json解析及簡(jiǎn)單例子,具有一定的參考價(jià)值,感興趣的朋友可以參考一下
    2016-06-06
  • Android Presentation雙屏異顯開(kāi)發(fā)流程詳細(xì)講解

    Android Presentation雙屏異顯開(kāi)發(fā)流程詳細(xì)講解

    最近開(kāi)發(fā)的一個(gè)項(xiàng)目,有兩個(gè)屏幕,需要將第二個(gè)頁(yè)面投屏到副屏上,這就需要用到Android的雙屏異顯(Presentation)技術(shù)了,研究了一下,這里做下筆記
    2023-01-01
  • Android開(kāi)發(fā)實(shí)現(xiàn)Gallery畫(huà)廊效果的方法

    Android開(kāi)發(fā)實(shí)現(xiàn)Gallery畫(huà)廊效果的方法

    這篇文章主要介紹了Android開(kāi)發(fā)實(shí)現(xiàn)Gallery畫(huà)廊效果的方法,結(jié)合具體實(shí)例形式分析了Android使用Gallery實(shí)現(xiàn)畫(huà)廊功能的具體操作技巧與相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2017-06-06
  • Android藍(lán)牙庫(kù)FastBle的基礎(chǔ)入門(mén)使用

    Android藍(lán)牙庫(kù)FastBle的基礎(chǔ)入門(mén)使用

    這篇文章主要給大家介紹了關(guān)于Android藍(lán)牙庫(kù)FastBle的基礎(chǔ)入門(mén)使用的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2018-07-07
  • Flutter實(shí)現(xiàn)旋轉(zhuǎn)掃描效果

    Flutter實(shí)現(xiàn)旋轉(zhuǎn)掃描效果

    這篇文章主要介紹了通過(guò)Flutter RotationTransition實(shí)現(xiàn)雷達(dá)旋轉(zhuǎn)掃描的效果,文中的示例代碼講解詳細(xì),感興趣的同學(xué)可以動(dòng)手試一試
    2022-01-01
  • Android獲取app應(yīng)用程序大小的方法

    Android獲取app應(yīng)用程序大小的方法

    本文通過(guò)一段代碼給大家介紹android獲取app應(yīng)用程序大小的方法,由于android對(duì)這種方法進(jìn)行了封裝,我們沒(méi)有權(quán)限去調(diào)用這個(gè)方法,只能通過(guò)aidl,然后用java的反射機(jī)制去調(diào)用系統(tǒng)級(jí)方法,感興趣的朋友一起學(xué)習(xí)吧
    2015-11-11

最新評(píng)論