Android 위 챗 Tinker 핫 업데이트 상세 사용

일단 효과 도 를 볼 게 요.
这里写图片描述
질문
원리 와 시스템 제한 으로 인해 Tinker 는 다음 과 같은 이미 알 고 있 는 문제 가 있 습 니 다.
  • Tinker 는 AndroidManifest.xml 수정 을 지원 하지 않 습 니 다.Tinker 는 4 대 구성 요 소 를 추가 하 는 것 을 지원 하지 않 습 니 다.
  • Google Play 의 개발 자 약관 제한 으로 인해 GP 채널 에서 코드 를 동적 으로 업데이트 하 는 것 을 권장 하지 않 습 니 다.
  • 안 드 로 이 드 N 에서 패 치 는 응용 시작 시간 에 경미 한 영향 을 미친다.
  • 은 일부 삼 성 안 드 로 이 드-21 모델 을 지원 하지 않 으 며 패 치 를 불 러 올 때'TinkerRuntime Exception:checkDex Install failed'를 자발적으로 던 집 니 다.
  • 은 각 업 체 의 보강 실현 이 일치 하지 않 기 때문에 1.7.6 과 그 후의 버 전에 서 tinker 는 보강 의 동적 업 데 이 트 를 지원 하지 않 습 니 다.
  • 은 자원 교체 에 대해 reoteView 수정 을 지원 하지 않 습 니 다.예 를 들 어 transition 애니메이션,notification icon,데스크 톱 아이콘 등 이 있 습 니 다.
  • 1.우선 프로젝트 의 build 에 tinker 플러그 인 을 통합 합 니 다.다음 과 같 습 니 다(현재 최신 버 전 은 1.7.6)
    먼저 구조 도 를 보면 몇 가지 종류 만 있 을 뿐이다.
    这里写图片描述
    프로젝트 의 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:공식 적 인 집성 은 매우 번 거 로 워 서 하루 종일 정 해 지지 않 을 수도 있 습 니 다.자신의 수요 와 상황 에 따라 추가 합 니 다.마지막 에 데모 가 올 라 갑 니 다.
    
    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.목록 파일 에 애플 리 케 이 션 과 서 비 스 를 통합 합 니 다.name 의 애플 리 케 이 션 은 반드시.AMSKY 여야 합 니 다.만약 에 추가 할 수 없 거나 빨간색 이 라면 먼저 build 하 십시오.만약 에 자신의 애플 리 케 이 션 이 있다 면 그 다음 에 제 가 어떻게 통합 하 는 지 말씀 드 리 겠 습 니 다.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 일 때 어떻게 조작 하 는 지 를 말 합 니 다.이 application 은 자신의 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.서비스 파일
    
    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 가 있 는 버 전 입 니 다.저 희 는 assemble Debug 를 사용 하여 테스트 합 니 다.assemble Debug 를 누 르 기 전에 build 폴 더 에 bakApk 폴 더 가 없습니다.
    这里写图片描述 这里写图片描述
    2.assemble Debug 를 누 르 면 bakApk 라 는 폴 더 가 나타 납 니 다.안에 apk 파일 이 있 습 니 다.실패 하면 clean 을 기억 하고 build 를 하 십시오.
    这里写图片描述
    3.다음 build 폴 더 에서 ext 의 속성 을 변경 합 니 다.bakApk 에서 생 성 된 apk 파일 과 R 파일 을 ext 에 복사 합 니 다.만약 에 release 패키지 에 mapping 이 있다 면 여기 로 복사 합 니 다.debug 테스트 이기 때문에 mapping 파일 이 없습니다.
    这里写图片描述
    4.업데이트 하거나 변경 해 야 할 bug 를 수정 합 니 다.그림 을 추가 하고 제목 표 시 를 변경 합 니 다.
    이것 은 bug 가 있 는 버 전 입 니 다.저 는 아직 그림 을 추가 하지 않 았 습 니 다.제목 을 바 꾸 었 습 니 다.
    这里写图片描述
    여기에 aa 그림 을 추가 하고 제목 을 바 꿨 습 니 다.
    这里写图片描述
    5.다음 에 tinker 아래 에 있 는 tinkerPatchDebug 를 실행 하여 패 치 패 치 를 만 듭 니 다.이 패 치 는 outputs 아래 에 있 습 니 다.
    这里写图片描述
    클릭 이 완료 되면 tinkerPatch 폴 더 가 생 성 됩 니 다.
    这里写图片描述
    tinkerPatch 폴 더 아래 patchsigned_7zip.apk 파일,붙 여 넣 기,MainActivity 에서 불 러 온 파일 이름 으로 바 꿉 니 다.저 는 patch 라 고 합 니 다.그리고 불 러 오기 전에 클릭 하 십시오.
    这里写图片描述
    불 러 온 후 잠 금 주파수,잠 금 해제,패 치 를 불 러 왔 습 니 다.폴 더 에 있 는 패 치가 없습니다.오래된 apk 와 합 쳐 졌 기 때 문 입 니 다.
    这里写图片描述
    주의 하 다.
    파일 에 서명 하면 build 의 signingConfigs 에 설정 하고 왼쪽 kestore 폴 더 에 설정 합 니 다.다음 그림 입 니 다.
    这里写图片描述
    这里写图片描述
    프로젝트 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 원 키 통합 배경:http://www.tinkerpatch.com/
    더 많은 하 이 라이트 내용 은'Android 위 챗 개발 튜 토리 얼 집계','자바 위 챗 개발 튜 토리 얼 집계'을 클릭 하여 읽 기 를 환영 합 니 다.
    이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

    좋은 웹페이지 즐겨찾기