日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

接入Tinker热修复和踩坑

發布時間:2025/3/21 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 接入Tinker热修复和踩坑 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

公司最近項目上線后總是遇見各種問題或bug,而我最近就一直在背黑鍋,幸虧最近不用上Google Play了,趕緊加上熱更新來脫離苦海吧,誰知道接入Tinker的過程中也踩了將近一周的坑,哎...一言難盡

為什么選擇Tinker和對比了也懶得說了(其實是因為隔壁阿里收費)

而且需要注意,中間很多版本不要使用最新的,要使用我寫的,不然有問題了找都找不到(我在這中間查,試,測,中間用了不知道多長時間)

正文

此次接入Tinker是直接用的Tinker,沒有使用Bugly的方式

官方地址

不多感慨了,直接上步驟吧

1.項目的gradle->buildscript.dependencies中加入

classpath ('com.tencent.tinker:tinker-patch-gradle-plugin:1.9.1')//版本號不能改

2.module的gradle->dependencies中加入

implementation('com.tencent.tinker:tinker-android-lib:1.9.9')//版本號不能改

3.改造Application,Tinker有兩種改造Application的方法:

第一種手動改造,兼容性最高

第二種使用注解自動改造

本來嫌費事用的第二種,然后接入后各種問題(但也不知這的問題),然后我又改成第一種了

首先寫一個類

public class SampleApplicationLike extends DefaultApplicationLike {public SampleApplicationLike(Application application, int tinkerFlags, boolean tinkerLoadVerifyFlag,long applicationStartElapsedTime, long applicationStartMillisTime, Intent tinkerResultIntent) {super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent);}@Overridepublic void onCreate() {super.onCreate();//在這里把之前Application中的初始化搬到這里}@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)@Overridepublic void onBaseContextAttached(Context base) {super.onBaseContextAttached(base);MultiDex.install(base);//這里加上分dex,我用的androidx的TinkerManager.setTinkerApplicationLike(this);TinkerManager.setUpgradeRetryEnable(true);TinkerManager.installTinker(this);Tinker.with(getApplication());}

然后改造你的Application,讓其"真"·什么都不做,改成我這個樣子

public class BaseApplication extends TinkerApplication {public BaseApplication() {super(ShareConstants.TINKER_ENABLE_ALL, SampleApplicationLike.class.getName());} }

4.加上以下工具類(在上面的代碼中用到了)

/*** Created by zhangshaowen on 16/6/30.* we use BuildInfo instead of {@link BuildInfo} to make less change*/ public class BuildInfo {/*** they are not final, so they won't change with the BuildConfig values!*/public static boolean DEBUG = BuildConfig.DEBUG;public static String VERSION_NAME = BuildConfig.VERSION_NAME;public static int VERSION_CODE = BuildConfig.VERSION_CODE;public static String MESSAGE = BuildConfig.MESSAGE;public static String TINKER_ID = BuildConfig.TINKER_ID;public static String PLATFORM = BuildConfig.PLATFORM;} import android.content.Context; import android.os.Looper; import android.os.MessageQueue;import com.tencent.tinker.lib.reporter.DefaultLoadReporter; import com.tencent.tinker.lib.util.UpgradePatchRetry; import com.tencent.tinker.loader.shareutil.ShareConstants;import java.io.File;/*** optional, you can just use DefaultLoadReporter* Created by zhangshaowen on 16/4/13.*/ public class SampleLoadReporter extends DefaultLoadReporter {private final static String TAG = "Tinker.SampleLoadReporter";public SampleLoadReporter(Context context) {super(context);}@Overridepublic void onLoadPatchListenerReceiveFail(final File patchFile, int errorCode) {super.onLoadPatchListenerReceiveFail(patchFile, errorCode);SampleTinkerReport.onTryApplyFail(errorCode);}@Overridepublic void onLoadResult(File patchDirectory, int loadCode, long cost) {super.onLoadResult(patchDirectory, loadCode, cost);switch (loadCode) {case ShareConstants.ERROR_LOAD_OK:SampleTinkerReport.onLoaded(cost);break;}Looper.getMainLooper().myQueue().addIdleHandler(new MessageQueue.IdleHandler() {@Overridepublic boolean queueIdle() {if (UpgradePatchRetry.getInstance(context).onPatchRetryLoad()) {SampleTinkerReport.onReportRetryPatch();}return false;}});}@Overridepublic void onLoadException(Throwable e, int errorCode) {super.onLoadException(e, errorCode);SampleTinkerReport.onLoadException(e, errorCode);}@Overridepublic void onLoadFileMd5Mismatch(File file, int fileType) {super.onLoadFileMd5Mismatch(file, fileType);SampleTinkerReport.onLoadFileMisMatch(fileType);}/*** try to recover patch oat file** @param file* @param fileType* @param isDirectory*/@Overridepublic void onLoadFileNotFound(File file, int fileType, boolean isDirectory) {super.onLoadFileNotFound(file, fileType, isDirectory);SampleTinkerReport.onLoadFileNotFound(fileType);}@Overridepublic void onLoadPackageCheckFail(File patchFile, int errorCode) {super.onLoadPackageCheckFail(patchFile, errorCode);SampleTinkerReport.onLoadPackageCheckFail(errorCode);}@Overridepublic void onLoadPatchInfoCorrupted(String oldVersion, String newVersion, File patchInfoFile) {super.onLoadPatchInfoCorrupted(oldVersion, newVersion, patchInfoFile);SampleTinkerReport.onLoadInfoCorrupted();}@Overridepublic void onLoadInterpret(int type, Throwable e) {super.onLoadInterpret(type, e);SampleTinkerReport.onLoadInterpretReport(type, e);}@Overridepublic void onLoadPatchVersionChanged(String oldVersion, String newVersion, File patchDirectoryFile, String currentPatchName) {super.onLoadPatchVersionChanged(oldVersion, newVersion, patchDirectoryFile, currentPatchName);}} import android.app.ActivityManager; import android.content.Context; import android.content.SharedPreferences;import com.tencent.tinker.lib.listener.DefaultPatchListener; import com.tencent.tinker.lib.util.TinkerLog; import com.tencent.tinker.loader.shareutil.ShareConstants; import com.tencent.tinker.loader.shareutil.SharePatchFileUtil; import com.tencent.tinker.loader.shareutil.ShareTinkerInternals;import java.io.File; import java.util.Properties;/*** Created by zhangshaowen on 16/4/30.* optional, you can just use DefaultPatchListener* we can check whatever you want whether we actually send a patch request* such as we can check rom space or apk channel*/ public class SamplePatchListener extends DefaultPatchListener {private static final String TAG = "Tinker.SamplePatchListener";protected static final long NEW_PATCH_RESTRICTION_SPACE_SIZE_MIN = 60 * 1024 * 1024;private final int maxMemory;public SamplePatchListener(Context context) {super(context);maxMemory = ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();TinkerLog.i(TAG, "application maxMemory:" + maxMemory);}/*** because we use the defaultCheckPatchReceived method* the error code define by myself should after {@code ShareConstants.ERROR_RECOVER_INSERVICE** @param path* @param newPatch* @return*/@Overridepublic int patchCheck(String path, String patchMd5) {File patchFile = new File(path);TinkerLog.i(TAG, "receive a patch file: %s, file size:%d", path, SharePatchFileUtil.getFileOrDirectorySize(patchFile));int returnCode = super.patchCheck(path, patchMd5);if (returnCode == ShareConstants.ERROR_PATCH_OK) {returnCode = Utils.checkForPatchRecover(NEW_PATCH_RESTRICTION_SPACE_SIZE_MIN, maxMemory);}if (returnCode == ShareConstants.ERROR_PATCH_OK) {SharedPreferences sp = context.getSharedPreferences(ShareConstants.TINKER_SHARE_PREFERENCE_CONFIG, Context.MODE_MULTI_PROCESS);//optional, only disable this patch file with md5int fastCrashCount = sp.getInt(patchMd5, 0);if (fastCrashCount >= SampleUncaughtExceptionHandler.MAX_CRASH_COUNT) {returnCode = Utils.ERROR_PATCH_CRASH_LIMIT;}}// Warning, it is just a sample case, you don't need to copy all of these// Interception some of the requestif (returnCode == ShareConstants.ERROR_PATCH_OK) {Properties properties = ShareTinkerInternals.fastGetPatchPackageMeta(patchFile);if (properties == null) {returnCode = Utils.ERROR_PATCH_CONDITION_NOT_SATISFIED;} else {String platform = properties.getProperty(Utils.PLATFORM);TinkerLog.i(TAG, "get platform:" + platform);// check patch platform requireif (platform == null || !platform.equals(BuildInfo.PLATFORM)) {returnCode = Utils.ERROR_PATCH_CONDITION_NOT_SATISFIED;}}}SampleTinkerReport.onTryApply(returnCode == ShareConstants.ERROR_PATCH_OK);return returnCode;} } import android.content.Context; import android.content.Intent;import com.tencent.tinker.lib.reporter.DefaultPatchReporter; import com.tencent.tinker.loader.shareutil.SharePatchInfo;import java.io.File; import java.util.List;/*** optional, you can just use DefaultPatchReporter* Created by zhangshaowen on 16/4/8.*/ public class SamplePatchReporter extends DefaultPatchReporter {private final static String TAG = "Tinker.SamplePatchReporter";public SamplePatchReporter(Context context) {super(context);}@Overridepublic void onPatchServiceStart(Intent intent) {super.onPatchServiceStart(intent);SampleTinkerReport.onApplyPatchServiceStart();}@Overridepublic void onPatchDexOptFail(File patchFile, List<File> dexFiles, Throwable t) {super.onPatchDexOptFail(patchFile, dexFiles, t);SampleTinkerReport.onApplyDexOptFail(t);}@Overridepublic void onPatchException(File patchFile, Throwable e) {super.onPatchException(patchFile, e);SampleTinkerReport.onApplyCrash(e);}@Overridepublic void onPatchInfoCorrupted(File patchFile, String oldVersion, String newVersion) {super.onPatchInfoCorrupted(patchFile, oldVersion, newVersion);SampleTinkerReport.onApplyInfoCorrupted();}@Overridepublic void onPatchPackageCheckFail(File patchFile, int errorCode) {super.onPatchPackageCheckFail(patchFile, errorCode);SampleTinkerReport.onApplyPackageCheckFail(errorCode);}@Overridepublic void onPatchResult(File patchFile, boolean success, long cost) {super.onPatchResult(patchFile, success, cost);SampleTinkerReport.onApplied(cost, success);}@Overridepublic void onPatchTypeExtractFail(File patchFile, File extractTo, String filename, int fileType) {super.onPatchTypeExtractFail(patchFile, extractTo, filename, fileType);SampleTinkerReport.onApplyExtractFail(fileType);}@Overridepublic void onPatchVersionCheckFail(File patchFile, SharePatchInfo oldPatchInfo, String patchFileVersion) {super.onPatchVersionCheckFail(patchFile, oldPatchInfo, patchFileVersion);SampleTinkerReport.onApplyVersionCheckFail();} } import java.io.File;/*** optional, you can just use DefaultTinkerResultService* we can restart process when we are at background or screen off* Created by zhangshaowen on 16/4/13.*/ public class SampleResultService extends DefaultTinkerResultService {private static final String TAG = "Tinker.SampleResultService";private static boolean isUploaded = false;@Overridepublic 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 processTinkerServiceInternals.killTinkerPatchServiceProcess(getApplicationContext());//在這里自定義處理加載成功和失敗String log = result.isSuccess ? "patch success, please restart process 補丁加載成功,請重啟應用" : "patch fail, please check reason 補丁加載失敗!!!!!!!!!!!";try {if (!isUploaded) {StringExtendFunKt.showDebugToast(log);StringExtendFunKt.w(log, "lllttt_tinker");UmengDiyEventUtilKt.actionEvent(SampleApplicationLike.getApp(),"updateHotFix",new UmengEventBean("fixSuccess", result.isSuccess),new UmengEventBean("systemInfo", JSONObject.toJSONString(YxUtil.getInstance().getInputDevice())));}isUploaded = true;} catch (Exception e) {AnyExtendFunKt.upload(e);}// 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 fileif (result.isSuccess) {deleteRawPatchFile(new File(result.rawPatchFilePath));//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 apiif (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 offTinkerLog.i(TAG, "tinker wait screen to restart process");new Utils.ScreenState(getApplicationContext(), new Utils.ScreenState.IOnScreenOff() {@Overridepublic 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 processandroid.os.Process.killProcess(android.os.Process.myPid());}} import com.tencent.tinker.lib.util.TinkerLog; import com.tencent.tinker.loader.shareutil.ShareConstants; import com.tencent.tinker.loader.shareutil.ShareTinkerInternals;/*** a simple tinker data reporter* Created by zhangshaowen on 16/9/17.*/ public class SampleTinkerReport {private static final String TAG = "Tinker.SampleTinkerReport";// KEY - PVpublic static final int KEY_REQUEST = 0;public static final int KEY_DOWNLOAD = 1;public static final int KEY_TRY_APPLY = 2;public static final int KEY_TRY_APPLY_SUCCESS = 3;public static final int KEY_APPLIED_START = 4;public static final int KEY_APPLIED = 5;public static final int KEY_LOADED = 6;public static final int KEY_CRASH_FAST_PROTECT = 7;public static final int KEY_CRASH_CAUSE_XPOSED_DALVIK = 8;public static final int KEY_CRASH_CAUSE_XPOSED_ART = 9;public static final int KEY_APPLY_WITH_RETRY = 10;//Key -- try apply detailpublic static final int KEY_TRY_APPLY_UPGRADE = 70;public static final int KEY_TRY_APPLY_DISABLE = 71;public static final int KEY_TRY_APPLY_RUNNING = 72;public static final int KEY_TRY_APPLY_INSERVICE = 73;public static final int KEY_TRY_APPLY_NOT_EXIST = 74;public static final int KEY_TRY_APPLY_GOOGLEPLAY = 75;public static final int KEY_TRY_APPLY_ROM_SPACE = 76;public static final int KEY_TRY_APPLY_ALREADY_APPLY = 77;public static final int KEY_TRY_APPLY_MEMORY_LIMIT = 78;public static final int KEY_TRY_APPLY_CRASH_LIMIT = 79;public static final int KEY_TRY_APPLY_CONDITION_NOT_SATISFIED = 80;public static final int KEY_TRY_APPLY_JIT = 81;//Key -- apply detailpublic static final int KEY_APPLIED_UPGRADE = 100;public static final int KEY_APPLIED_UPGRADE_FAIL = 101;public static final int KEY_APPLIED_EXCEPTION = 120;public static final int KEY_APPLIED_DEXOPT_OTHER = 121;public static final int KEY_APPLIED_DEXOPT_EXIST = 122;public static final int KEY_APPLIED_DEXOPT_FORMAT = 123;public static final int KEY_APPLIED_INFO_CORRUPTED = 124;//package checkpublic static final int KEY_APPLIED_PACKAGE_CHECK_SIGNATURE = 150;public static final int KEY_APPLIED_PACKAGE_CHECK_DEX_META = 151;public static final int KEY_APPLIED_PACKAGE_CHECK_LIB_META = 152;public static final int KEY_APPLIED_PACKAGE_CHECK_APK_TINKER_ID_NOT_FOUND = 153;public static final int KEY_APPLIED_PACKAGE_CHECK_PATCH_TINKER_ID_NOT_FOUND = 154;public static final int KEY_APPLIED_PACKAGE_CHECK_META_NOT_FOUND = 155;public static final int KEY_APPLIED_PACKAGE_CHECK_TINKER_ID_NOT_EQUAL = 156;public static final int KEY_APPLIED_PACKAGE_CHECK_RES_META = 157;public static final int KEY_APPLIED_PACKAGE_CHECK_TINKERFLAG_NOT_SUPPORT = 158;//version checkpublic static final int KEY_APPLIED_VERSION_CHECK = 180;//extract errorpublic static final int KEY_APPLIED_PATCH_FILE_EXTRACT = 181;public static final int KEY_APPLIED_DEX_EXTRACT = 182;public static final int KEY_APPLIED_LIB_EXTRACT = 183;public static final int KEY_APPLIED_RESOURCE_EXTRACT = 184;//cost timepublic static final int KEY_APPLIED_SUCC_COST_5S_LESS = 200;public static final int KEY_APPLIED_SUCC_COST_10S_LESS = 201;public static final int KEY_APPLIED_SUCC_COST_30S_LESS = 202;public static final int KEY_APPLIED_SUCC_COST_60S_LESS = 203;public static final int KEY_APPLIED_SUCC_COST_OTHER = 204;public static final int KEY_APPLIED_FAIL_COST_5S_LESS = 205;public static final int KEY_APPLIED_FAIL_COST_10S_LESS = 206;public static final int KEY_APPLIED_FAIL_COST_30S_LESS = 207;public static final int KEY_APPLIED_FAIL_COST_60S_LESS = 208;public static final int KEY_APPLIED_FAIL_COST_OTHER = 209;// KEY -- load detailpublic static final int KEY_LOADED_UNKNOWN_EXCEPTION = 250;public static final int KEY_LOADED_UNCAUGHT_EXCEPTION = 251;public static final int KEY_LOADED_EXCEPTION_DEX = 252;public static final int KEY_LOADED_EXCEPTION_DEX_CHECK = 253;public static final int KEY_LOADED_EXCEPTION_RESOURCE = 254;public static final int KEY_LOADED_EXCEPTION_RESOURCE_CHECK = 255;public static final int KEY_LOADED_MISMATCH_DEX = 300;public static final int KEY_LOADED_MISMATCH_LIB = 301;public static final int KEY_LOADED_MISMATCH_RESOURCE = 302;public static final int KEY_LOADED_MISSING_DEX = 303;public static final int KEY_LOADED_MISSING_LIB = 304;public static final int KEY_LOADED_MISSING_PATCH_FILE = 305;public static final int KEY_LOADED_MISSING_PATCH_INFO = 306;public static final int KEY_LOADED_MISSING_DEX_OPT = 307;public static final int KEY_LOADED_MISSING_RES = 308;public static final int KEY_LOADED_INFO_CORRUPTED = 309;//load package checkpublic static final int KEY_LOADED_PACKAGE_CHECK_SIGNATURE = 350;public static final int KEY_LOADED_PACKAGE_CHECK_DEX_META = 351;public static final int KEY_LOADED_PACKAGE_CHECK_LIB_META = 352;public static final int KEY_LOADED_PACKAGE_CHECK_APK_TINKER_ID_NOT_FOUND = 353;public static final int KEY_LOADED_PACKAGE_CHECK_PATCH_TINKER_ID_NOT_FOUND = 354;public static final int KEY_LOADED_PACKAGE_CHECK_TINKER_ID_NOT_EQUAL = 355;public static final int KEY_LOADED_PACKAGE_CHECK_PACKAGE_META_NOT_FOUND = 356;public static final int KEY_LOADED_PACKAGE_CHECK_RES_META = 357;public static final int KEY_LOADED_PACKAGE_CHECK_TINKERFLAG_NOT_SUPPORT = 358;public static final int KEY_LOADED_SUCC_COST_500_LESS = 400;public static final int KEY_LOADED_SUCC_COST_1000_LESS = 401;public static final int KEY_LOADED_SUCC_COST_3000_LESS = 402;public static final int KEY_LOADED_SUCC_COST_5000_LESS = 403;public static final int KEY_LOADED_SUCC_COST_OTHER = 404;public static final int KEY_LOADED_INTERPRET_GET_INSTRUCTION_SET_ERROR = 450;public static final int KEY_LOADED_INTERPRET_INTERPRET_COMMAND_ERROR = 451;public static final int KEY_LOADED_INTERPRET_TYPE_INTERPRET_OK = 452;interface Reporter {void onReport(int key);void onReport(String message);}private static Reporter reporter = null;public void setReporter(Reporter reporter) {this.reporter = reporter;}public static void onTryApply(boolean success) {if (reporter == null) {return;}reporter.onReport(KEY_TRY_APPLY);reporter.onReport(KEY_TRY_APPLY_UPGRADE);if (success) {reporter.onReport(KEY_TRY_APPLY_SUCCESS);}}public static void onTryApplyFail(int errorCode) {if (reporter == null) {return;}switch (errorCode) {case ShareConstants.ERROR_PATCH_NOTEXIST:reporter.onReport(KEY_TRY_APPLY_NOT_EXIST);break;case ShareConstants.ERROR_PATCH_DISABLE:reporter.onReport(KEY_TRY_APPLY_DISABLE);break;case ShareConstants.ERROR_PATCH_INSERVICE:reporter.onReport(KEY_TRY_APPLY_INSERVICE);break;case ShareConstants.ERROR_PATCH_RUNNING:reporter.onReport(KEY_TRY_APPLY_RUNNING);break;case ShareConstants.ERROR_PATCH_JIT:reporter.onReport(KEY_TRY_APPLY_JIT);break;case Utils.ERROR_PATCH_ROM_SPACE:reporter.onReport(KEY_TRY_APPLY_ROM_SPACE);break;case Utils.ERROR_PATCH_GOOGLEPLAY_CHANNEL:reporter.onReport(KEY_TRY_APPLY_GOOGLEPLAY);break;case ShareConstants.ERROR_PATCH_ALREADY_APPLY:reporter.onReport(KEY_TRY_APPLY_ALREADY_APPLY);break;case Utils.ERROR_PATCH_CRASH_LIMIT:reporter.onReport(KEY_TRY_APPLY_CRASH_LIMIT);break;case Utils.ERROR_PATCH_MEMORY_LIMIT:reporter.onReport(KEY_TRY_APPLY_MEMORY_LIMIT);break;case Utils.ERROR_PATCH_CONDITION_NOT_SATISFIED:reporter.onReport(KEY_TRY_APPLY_CONDITION_NOT_SATISFIED);break;}}public static void onLoadPackageCheckFail(int errorCode) {if (reporter == null) {return;}switch (errorCode) {case ShareConstants.ERROR_PACKAGE_CHECK_SIGNATURE_FAIL:reporter.onReport(KEY_LOADED_PACKAGE_CHECK_SIGNATURE);break;case ShareConstants.ERROR_PACKAGE_CHECK_DEX_META_CORRUPTED:reporter.onReport(KEY_LOADED_PACKAGE_CHECK_DEX_META);break;case ShareConstants.ERROR_PACKAGE_CHECK_LIB_META_CORRUPTED:reporter.onReport(KEY_LOADED_PACKAGE_CHECK_LIB_META);break;case ShareConstants.ERROR_PACKAGE_CHECK_PATCH_TINKER_ID_NOT_FOUND:reporter.onReport(KEY_LOADED_PACKAGE_CHECK_PATCH_TINKER_ID_NOT_FOUND);break;case ShareConstants.ERROR_PACKAGE_CHECK_APK_TINKER_ID_NOT_FOUND:reporter.onReport(KEY_LOADED_PACKAGE_CHECK_APK_TINKER_ID_NOT_FOUND);break;case ShareConstants.ERROR_PACKAGE_CHECK_TINKER_ID_NOT_EQUAL:reporter.onReport(KEY_LOADED_PACKAGE_CHECK_TINKER_ID_NOT_EQUAL);break;case ShareConstants.ERROR_PACKAGE_CHECK_PACKAGE_META_NOT_FOUND:reporter.onReport(KEY_LOADED_PACKAGE_CHECK_PACKAGE_META_NOT_FOUND);break;case ShareConstants.ERROR_PACKAGE_CHECK_RESOURCE_META_CORRUPTED:reporter.onReport(KEY_LOADED_PACKAGE_CHECK_RES_META);break;case ShareConstants.ERROR_PACKAGE_CHECK_TINKERFLAG_NOT_SUPPORT:reporter.onReport(KEY_LOADED_PACKAGE_CHECK_TINKERFLAG_NOT_SUPPORT);break;}}public static void onLoaded(long cost) {if (reporter == null) {return;}reporter.onReport(KEY_LOADED);if (cost < 0L) {TinkerLog.e(TAG, "hp_report report load cost failed, invalid cost");return;}if (cost <= 500) {reporter.onReport(KEY_LOADED_SUCC_COST_500_LESS);} else if (cost <= 1000) {reporter.onReport(KEY_LOADED_SUCC_COST_1000_LESS);} else if (cost <= 3000) {reporter.onReport(KEY_LOADED_SUCC_COST_3000_LESS);} else if (cost <= 5000) {reporter.onReport(KEY_LOADED_SUCC_COST_5000_LESS);} else {reporter.onReport(KEY_LOADED_SUCC_COST_OTHER);}}public static void onLoadInfoCorrupted() {if (reporter == null) {return;}reporter.onReport(KEY_LOADED_INFO_CORRUPTED);}public static void onLoadFileNotFound(int fileType) {if (reporter == null) {return;}switch (fileType) {case ShareConstants.TYPE_DEX_OPT:reporter.onReport(KEY_LOADED_MISSING_DEX_OPT);break;case ShareConstants.TYPE_DEX:reporter.onReport(KEY_LOADED_MISSING_DEX);break;case ShareConstants.TYPE_LIBRARY:reporter.onReport(KEY_LOADED_MISSING_LIB);break;case ShareConstants.TYPE_PATCH_FILE:reporter.onReport(KEY_LOADED_MISSING_PATCH_FILE);break;case ShareConstants.TYPE_PATCH_INFO:reporter.onReport(KEY_LOADED_MISSING_PATCH_INFO);break;case ShareConstants.TYPE_RESOURCE:reporter.onReport(KEY_LOADED_MISSING_RES);break;}}public static void onLoadInterpretReport(int type, Throwable e) {if (reporter == null) {return;}switch (type) {case ShareConstants.TYPE_INTERPRET_GET_INSTRUCTION_SET_ERROR:reporter.onReport(KEY_LOADED_INTERPRET_GET_INSTRUCTION_SET_ERROR);reporter.onReport("Tinker Exception:interpret occur exception " + Utils.getExceptionCauseString(e));break;case ShareConstants.TYPE_INTERPRET_COMMAND_ERROR:reporter.onReport(KEY_LOADED_INTERPRET_INTERPRET_COMMAND_ERROR);reporter.onReport("Tinker Exception:interpret occur exception " + Utils.getExceptionCauseString(e));break;case ShareConstants.TYPE_INTERPRET_OK:reporter.onReport(KEY_LOADED_INTERPRET_TYPE_INTERPRET_OK);break;}}public static void onLoadFileMisMatch(int fileType) {if (reporter == null) {return;}switch (fileType) {case ShareConstants.TYPE_DEX:reporter.onReport(KEY_LOADED_MISMATCH_DEX);break;case ShareConstants.TYPE_LIBRARY:reporter.onReport(KEY_LOADED_MISMATCH_LIB);break;case ShareConstants.TYPE_RESOURCE:reporter.onReport(KEY_LOADED_MISMATCH_RESOURCE);break;}}public static void onLoadException(Throwable throwable, int errorCode) {if (reporter == null) {return;}boolean isCheckFail = false;switch (errorCode) {case ShareConstants.ERROR_LOAD_EXCEPTION_DEX:if (throwable.getMessage().contains(ShareConstants.CHECK_DEX_INSTALL_FAIL)) {reporter.onReport(KEY_LOADED_EXCEPTION_DEX_CHECK);isCheckFail = true;TinkerLog.e(TAG, "tinker dex check fail:" + throwable.getMessage());} else {reporter.onReport(KEY_LOADED_EXCEPTION_DEX);TinkerLog.e(TAG, "tinker dex reflect fail:" + throwable.getMessage());}break;case ShareConstants.ERROR_LOAD_EXCEPTION_RESOURCE:if (throwable.getMessage().contains(ShareConstants.CHECK_RES_INSTALL_FAIL)) {reporter.onReport(KEY_LOADED_EXCEPTION_RESOURCE_CHECK);isCheckFail = true;TinkerLog.e(TAG, "tinker res check fail:" + throwable.getMessage());} else {reporter.onReport(KEY_LOADED_EXCEPTION_RESOURCE);TinkerLog.e(TAG, "tinker res reflect fail:" + throwable.getMessage());}break;case ShareConstants.ERROR_LOAD_EXCEPTION_UNCAUGHT:reporter.onReport(KEY_LOADED_UNCAUGHT_EXCEPTION);break;case ShareConstants.ERROR_LOAD_EXCEPTION_UNKNOWN:reporter.onReport(KEY_LOADED_UNKNOWN_EXCEPTION);break;}//reporter exception, for dex check fail, we don't need to report stacktraceif (!isCheckFail) {reporter.onReport("Tinker Exception:load tinker occur exception " + Utils.getExceptionCauseString(throwable));}}public static void onApplyPatchServiceStart() {if (reporter == null) {return;}reporter.onReport(KEY_APPLIED_START);}public static void onApplyDexOptFail(Throwable throwable) {if (reporter == null) {return;}if (throwable.getMessage().contains(ShareConstants.CHECK_DEX_OAT_EXIST_FAIL)) {reporter.onReport(KEY_APPLIED_DEXOPT_EXIST);} else if (throwable.getMessage().contains(ShareConstants.CHECK_DEX_OAT_FORMAT_FAIL)) {reporter.onReport(KEY_APPLIED_DEXOPT_FORMAT);} else {reporter.onReport(KEY_APPLIED_DEXOPT_OTHER);reporter.onReport("Tinker Exception:apply tinker occur exception " + Utils.getExceptionCauseString(throwable));}}public static void onApplyInfoCorrupted() {if (reporter == null) {return;}reporter.onReport(KEY_APPLIED_INFO_CORRUPTED);}public static void onApplyVersionCheckFail() {if (reporter == null) {return;}reporter.onReport(KEY_APPLIED_VERSION_CHECK);}public static void onApplyExtractFail(int fileType) {if (reporter == null) {return;}switch (fileType) {case ShareConstants.TYPE_DEX:reporter.onReport(KEY_APPLIED_DEX_EXTRACT);break;case ShareConstants.TYPE_LIBRARY:reporter.onReport(KEY_APPLIED_LIB_EXTRACT);break;case ShareConstants.TYPE_PATCH_FILE:reporter.onReport(KEY_APPLIED_PATCH_FILE_EXTRACT);break;case ShareConstants.TYPE_RESOURCE:reporter.onReport(KEY_APPLIED_RESOURCE_EXTRACT);break;}}public static void onApplied(long cost, boolean success) {if (reporter == null) {return;}if (success) {reporter.onReport(KEY_APPLIED);}if (success) {reporter.onReport(KEY_APPLIED_UPGRADE);} else {reporter.onReport(KEY_APPLIED_UPGRADE_FAIL);}TinkerLog.i(TAG, "hp_report report apply cost = %d", cost);if (cost < 0L) {TinkerLog.e(TAG, "hp_report report apply cost failed, invalid cost");return;}if (cost <= 5000) {if (success) {reporter.onReport(KEY_APPLIED_SUCC_COST_5S_LESS);} else {reporter.onReport(KEY_APPLIED_FAIL_COST_5S_LESS);}} else if (cost <= 10 * 1000) {if (success) {reporter.onReport(KEY_APPLIED_SUCC_COST_10S_LESS);} else {reporter.onReport(KEY_APPLIED_FAIL_COST_10S_LESS);}} else if (cost <= 30 * 1000) {if (success) {reporter.onReport(KEY_APPLIED_SUCC_COST_30S_LESS);} else {reporter.onReport(KEY_APPLIED_FAIL_COST_30S_LESS);}} else if (cost <= 60 * 1000) {if (success) {reporter.onReport(KEY_APPLIED_SUCC_COST_60S_LESS);} else {reporter.onReport(KEY_APPLIED_FAIL_COST_60S_LESS);}} else {if (success) {reporter.onReport(KEY_APPLIED_SUCC_COST_OTHER);} else {reporter.onReport(KEY_APPLIED_FAIL_COST_OTHER);}}}public static void onApplyPackageCheckFail(int errorCode) {if (reporter == null) {return;}TinkerLog.i(TAG, "hp_report package check failed, error = %d", errorCode);switch (errorCode) {case ShareConstants.ERROR_PACKAGE_CHECK_SIGNATURE_FAIL:reporter.onReport(KEY_APPLIED_PACKAGE_CHECK_SIGNATURE);break;case ShareConstants.ERROR_PACKAGE_CHECK_DEX_META_CORRUPTED:reporter.onReport(KEY_APPLIED_PACKAGE_CHECK_DEX_META);break;case ShareConstants.ERROR_PACKAGE_CHECK_LIB_META_CORRUPTED:reporter.onReport(KEY_APPLIED_PACKAGE_CHECK_LIB_META);break;case ShareConstants.ERROR_PACKAGE_CHECK_PATCH_TINKER_ID_NOT_FOUND:reporter.onReport(KEY_APPLIED_PACKAGE_CHECK_PATCH_TINKER_ID_NOT_FOUND);break;case ShareConstants.ERROR_PACKAGE_CHECK_APK_TINKER_ID_NOT_FOUND:reporter.onReport(KEY_APPLIED_PACKAGE_CHECK_APK_TINKER_ID_NOT_FOUND);break;case ShareConstants.ERROR_PACKAGE_CHECK_TINKER_ID_NOT_EQUAL:reporter.onReport(KEY_APPLIED_PACKAGE_CHECK_TINKER_ID_NOT_EQUAL);break;case ShareConstants.ERROR_PACKAGE_CHECK_PACKAGE_META_NOT_FOUND:reporter.onReport(KEY_APPLIED_PACKAGE_CHECK_META_NOT_FOUND);break;case ShareConstants.ERROR_PACKAGE_CHECK_RESOURCE_META_CORRUPTED:reporter.onReport(KEY_APPLIED_PACKAGE_CHECK_RES_META);break;case ShareConstants.ERROR_PACKAGE_CHECK_TINKERFLAG_NOT_SUPPORT:reporter.onReport(KEY_APPLIED_PACKAGE_CHECK_TINKERFLAG_NOT_SUPPORT);break;}}public static void onApplyCrash(Throwable throwable) {if (reporter == null) {return;}reporter.onReport(KEY_APPLIED_EXCEPTION);reporter.onReport("Tinker Exception:apply tinker occur exception " + Utils.getExceptionCauseString(throwable));}public static void onFastCrashProtect() {if (reporter == null) {return;}reporter.onReport(KEY_CRASH_FAST_PROTECT);}public static void onXposedCrash() {if (reporter == null) {return;}if (ShareTinkerInternals.isVmArt()) {reporter.onReport(KEY_CRASH_CAUSE_XPOSED_ART);} else {reporter.onReport(KEY_CRASH_CAUSE_XPOSED_DALVIK);}}public static void onReportRetryPatch() {if (reporter == null) {return;}reporter.onReport(KEY_APPLY_WITH_RETRY);}} import android.content.Context; import android.content.SharedPreferences; import android.os.SystemClock;import com.tencent.tinker.entry.ApplicationLike; import com.tencent.tinker.lib.tinker.TinkerApplicationHelper; import com.tencent.tinker.lib.util.TinkerLog; import com.tencent.tinker.loader.shareutil.ShareConstants; import com.tencent.tinker.loader.shareutil.ShareTinkerInternals;/*** optional, use dynamic configuration is better way* for native crash,* <p/>* Created by zhangshaowen on 16/7/3.* tinker's crash is caught by {@code LoadReporter.onLoadException}* use {@code TinkerApplicationHelper} api, no need to install tinker!*/ public class SampleUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {private static final String TAG = "Tinker.SampleUncaughtExHandler";private final Thread.UncaughtExceptionHandler ueh;private static final long QUICK_CRASH_ELAPSE = 10 * 1000;public static final int MAX_CRASH_COUNT = 3;private static final String DALVIK_XPOSED_CRASH = "Class ref in pre-verified class resolved to unexpected implementation";public SampleUncaughtExceptionHandler() {ueh = Thread.getDefaultUncaughtExceptionHandler();}@Overridepublic void uncaughtException(Thread thread, Throwable ex) {TinkerLog.e(TAG, "uncaughtException:" + ex.getMessage());tinkerFastCrashProtect();tinkerPreVerifiedCrashHandler(ex);ueh.uncaughtException(thread, ex);}/*** Such as Xposed, if it try to load some class before we load from patch files.* With dalvik, it will crash with "Class ref in pre-verified class resolved to unexpected implementation".* With art, it may crash at some times. But we can't know the actual crash type.* If it use Xposed, we can just clean patch or mention user to uninstall it.*/private void tinkerPreVerifiedCrashHandler(Throwable ex) {ApplicationLike applicationLike = TinkerManager.getTinkerApplicationLike();if (applicationLike == null || applicationLike.getApplication() == null) {TinkerLog.w(TAG, "applicationlike is null");return;}if (!TinkerApplicationHelper.isTinkerLoadSuccess(applicationLike)) {TinkerLog.w(TAG, "tinker is not loaded");return;}Throwable throwable = ex;boolean isXposed = false;while (throwable != null) {if (!isXposed) {isXposed = Utils.isXposedExists(throwable);}// xposed?if (isXposed) {boolean isCausedByXposed = false;//for art, we can't know the actually crash type//just ignore artif (throwable instanceof IllegalAccessError && throwable.getMessage().contains(DALVIK_XPOSED_CRASH)) {//for dalvik, we know the actual crash typeisCausedByXposed = true;}if (isCausedByXposed) {SampleTinkerReport.onXposedCrash();TinkerLog.e(TAG, "have xposed: just clean tinker");//kill all other process to ensure that all process's code is the same.ShareTinkerInternals.killAllOtherProcess(applicationLike.getApplication());TinkerApplicationHelper.cleanPatch(applicationLike);ShareTinkerInternals.setTinkerDisableWithSharedPreferences(applicationLike.getApplication());return;}}throwable = throwable.getCause();}}/*** if tinker is load, and it crash more than MAX_CRASH_COUNT, then we just clean patch.*/private boolean tinkerFastCrashProtect() {ApplicationLike applicationLike = TinkerManager.getTinkerApplicationLike();if (applicationLike == null || applicationLike.getApplication() == null) {return false;}if (!TinkerApplicationHelper.isTinkerLoadSuccess(applicationLike)) {return false;}final long elapsedTime = SystemClock.elapsedRealtime() - applicationLike.getApplicationStartElapsedTime();//this process may not install tinker, so we use TinkerApplicationHelper apiif (elapsedTime < QUICK_CRASH_ELAPSE) {String currentVersion = TinkerApplicationHelper.getCurrentVersion(applicationLike);if (ShareTinkerInternals.isNullOrNil(currentVersion)) {return false;}SharedPreferences sp = applicationLike.getApplication().getSharedPreferences(ShareConstants.TINKER_SHARE_PREFERENCE_CONFIG, Context.MODE_MULTI_PROCESS);int fastCrashCount = sp.getInt(currentVersion, 0) + 1;if (fastCrashCount >= MAX_CRASH_COUNT) {SampleTinkerReport.onFastCrashProtect();TinkerApplicationHelper.cleanPatch(applicationLike);TinkerLog.e(TAG, "tinker has fast crash more than %d, we just clean patch!", fastCrashCount);return true;} else {sp.edit().putInt(currentVersion, fastCrashCount).commit();TinkerLog.e(TAG, "tinker has fast crash %d times", fastCrashCount);}}return false;} } import com.tencent.tinker.entry.ApplicationLike; import com.tencent.tinker.lib.listener.PatchListener; import com.tencent.tinker.lib.patch.AbstractPatch; import com.tencent.tinker.lib.patch.UpgradePatch; import com.tencent.tinker.lib.reporter.LoadReporter; import com.tencent.tinker.lib.reporter.PatchReporter; import com.tencent.tinker.lib.tinker.TinkerInstaller; import com.tencent.tinker.lib.util.TinkerLog; import com.tencent.tinker.lib.util.UpgradePatchRetry;/*** Created by zhangshaowen on 16/7/3.*/ public class TinkerManager {private static final String TAG = "Tinker.TinkerManager";private static ApplicationLike applicationLike;private static SampleUncaughtExceptionHandler uncaughtExceptionHandler;private static boolean isInstalled = false;public static void setTinkerApplicationLike(ApplicationLike appLike) {applicationLike = appLike;}public static ApplicationLike getTinkerApplicationLike() {return applicationLike;}public static void initFastCrashProtect() {if (uncaughtExceptionHandler == null) {uncaughtExceptionHandler = new SampleUncaughtExceptionHandler();Thread.setDefaultUncaughtExceptionHandler(uncaughtExceptionHandler);}}public static void setUpgradeRetryEnable(boolean enable) {UpgradePatchRetry.getInstance(applicationLike.getApplication()).setRetryEnable(enable);}/*** all use default class, simply Tinker install method*/public static void sampleInstallTinker(ApplicationLike appLike) {if (isInstalled) {TinkerLog.w(TAG, "install tinker, but has installed, ignore");return;}TinkerInstaller.install(appLike);isInstalled = true;}/*** you can specify all class you want.* sometimes, you can only install tinker in some process you want!** @param appLike*/public static void installTinker(ApplicationLike appLike) {if (isInstalled) {TinkerLog.w(TAG, "install tinker, but has installed, ignore");return;}//or you can just use DefaultLoadReporterLoadReporter loadReporter = new SampleLoadReporter(appLike.getApplication());//or you can just use DefaultPatchReporterPatchReporter patchReporter = new SamplePatchReporter(appLike.getApplication());//or you can just use DefaultPatchListenerPatchListener patchListener = new SamplePatchListener(appLike.getApplication());//you can set your own upgrade patch if you needAbstractPatch upgradePatchProcessor = new UpgradePatch();TinkerInstaller.install(appLike,loadReporter, patchReporter, patchListener,SampleResultService.class, upgradePatchProcessor);isInstalled = true;} } import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Environment; import android.os.StatFs;import com.tencent.tinker.lib.util.TinkerLog; import com.tencent.tinker.loader.shareutil.ShareConstants;import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.PrintStream;/*** Created by zhangshaowen on 16/4/7.*/ public class Utils {private static final String TAG = "Tinker.Utils";/*** the error code define by myself* should after {@code ShareConstants.ERROR_PATCH_INSERVICE*/public static final int ERROR_PATCH_GOOGLEPLAY_CHANNEL = -20;public static final int ERROR_PATCH_ROM_SPACE = -21;public static final int ERROR_PATCH_MEMORY_LIMIT = -22;public static final int ERROR_PATCH_CRASH_LIMIT = -23;public static final int ERROR_PATCH_CONDITION_NOT_SATISFIED = -24;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;}@Deprecatedpublic 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 directlyThrowable 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;}}public static class ScreenState {public interface IOnScreenOff {void onScreenOff();}public ScreenState(final Context context, final IOnScreenOff onScreenOffInterface) {IntentFilter filter = new IntentFilter();filter.addAction(Intent.ACTION_SCREEN_OFF);context.registerReceiver(new BroadcastReceiver() {@Overridepublic 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)) {if (onScreenOffInterface != null) {onScreenOffInterface.onScreenOff();}}context.unregisterReceiver(this);}}, filter);}} }

5.配置清單文件

Application配置上剛才寫的那個BaseApplication

然后配置service

<serviceandroid:name=".app.tinker.SampleResultService"android:exported="false" />

6.版本號配置

gradle版本改為3.5.3

classpath 'com.android.tools.build:gradle:3.5.3'

gradle.zip版本修改(在gradle-wrapper.properties文件內)

distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip

app.gradle中修改版本號

compileSdkVersion 29 //推薦 buildToolsVersion '29.0.3' //推薦 minSdkVersion 19//必須小于等于19,因為大于19之后就改了生成dex的方式,這個東西坑了我好久 minifyEnabled false//公司項目里沒有用混淆,用的是加固,這里如果打開會有很多配置不相同,沒有試過

7.app.gradle中增加如下代碼(標記edit this的是可以需要更改的)

defaultConfig {...javaCompileOptions { annotationProcessorOptions { includeCompileClasspath = true } }buildConfigField "String", "MESSAGE", "\"I am the base apk\""buildConfigField "String", "TINKER_ID", "\"${getTINKER_ID()}\""buildConfigField "String", "PLATFORM", "\"all\""}dexOptions {jumboMode = true} def bakPath = file("${buildDir}/bakApk/")def getTINKER_ID() {//todo 每次打包使用build->assembleAli命令,并保存release apk和release R.txt//todo 每次發布熱更新先配置本文件,在使用tinker->tinkerPatchAliRelease命令,然后找到對應文件修改文件名為path.aaareturn android.defaultConfig.versionName+"_1" //todo edit this,這里經我測試每次打基礎包和增量包都要修改,所以我設置的是版本名稱和追加熱更版本 }ext {//for some reason, you may want to ignore tinkerBuild, such as instant run debug build?tinkerEnabled = true//基準apk路徑tinkerOldApkPath = "C:\\Users\\a\\Desktop\\oldApks\\ali-release_v0.8.6.apk"//todo edit this//未開啟混淆,則不需要填寫tinkerApplyMappingPath = "C:\\Users\\a\\Desktop\\oldApks"//基準apk中的R文件路徑tinkerApplyResourcePath = "C:\\Users\\a\\Desktop\\oldApks\\app-ali-release-R.txt"//todo edit this//如果你修復了res文件,需要指定你bug版本的R.txt文件tinkerBuildFlavorDirectory = "C:\\Users\\a\\Desktop\\oldApks\\app-ali-release-R.txt"//todo edit this}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 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 = true/*** 可選,默認為“true”* 是否簽名補丁文件* 如果沒有,你必須自己做。否則在補丁加載過程中無法檢查成功* 我們將使用sign配置與您的構建類型*/useSign = true/*** 可選,默認為“true”* 是否使用tinker構建*/tinkerEnable = buildWithTinker()/*** 警告,applyMapping會影響正常的android build!*/buildConfig {/*** 可選,默認為'null'* 如果我們使用tinkerPatch構建補丁apk,你最好應用舊的* apk映射文件如果minifyEnabled是啟用!* 警告:你必須小心,它會影響正常的組裝構建!*/applyMapping = getApplyMappingPath()/*** 可選,默認為'null'* 最好保留R.txt文件中的資源id,以減少java更改*/applyResourceMapping = getApplyResourceMappingPath()/*** 必需,默認'null'* 因為我們不想檢查基地apk與md5在運行時(它是慢)* tinkerId用于在試圖應用補丁時標識唯一的基本apk。* 我們可以使用git rev,svn rev或者簡單的versionCode。* 我們將在您的清單中自動生成tinkerId*/tinkerId = getTINKER_ID()/*** 如果keepDexApply為true,則表示dex指向舊apk的類。* 打開這可以減少dex diff文件大小。*/keepDexApply = false/*** optional, default 'false'* Whether tinker should treat the base apk as the one being protected by app* protection tools.* If this attribute is true, the generated patch package will contain a* dex including all changed classes instead of any dexdiff patch-info files.** 是否使用加固模式,僅僅將變更的類合成補丁。注意,這種模式僅僅可以用于加固應用中。*/isProtectedApp = true //todo 注意這里,如果是debug測試或者app不進行加固,請關閉/*** optional, default 'false'* Whether tinker should support component hotplug (add new component dynamically).* If this attribute is true, the component added in new apk will be available after* patch is successfully loaded. Otherwise an error would be announced when generating patch* on compile-time.** <b>Notice that currently this feature is incubating and only support NON-EXPORTED Activity</b>*/supportHotplugComponent = 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","com.xx.xx.view.BaseApplication"//todo 這里加上你的Application]}lib {/*** 可選,默認'[]'* apk中的圖書館應該處理tinkerPatch* 它支持*或?模式。* 對于資源庫,我們只是在補丁目錄中恢復它們* 你可以得到他們在TinkerLoadResult與Tinker*/pattern = ["lib/*/*.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.2")}//或者您可以添加外部的配置文件,或從舊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() > 0def date = new Date().format("MMdd-HH-mm-ss")/*** bak apk and mapping*/android.applicationVariants.all { variant ->/*** task type, you want to bak*/def taskName = variant.nametasks.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}") : bakPathif (variant.metaClass.hasProperty(variant, 'packageApplicationProvider')) {def packageAndroidArtifact = variant.packageApplicationProvider.get()if (packageAndroidArtifact != null) {try {from new File(packageAndroidArtifact.outputDirectory.getAsFile().get(), variant.outputs.first().apkData.outputFileName)} catch (Exception e) {from new File(packageAndroidArtifact.outputDirectory, variant.outputs.first().apkData.outputFileName)}} else {from variant.outputs.first().mainOutputFile.outputFile}} else {from variant.outputs.first().outputFile}into destPathrename { String fileName ->fileName.replace("${fileNamePrefix}.apk", "${newFileNamePrefix}.apk")}from "${buildDir}/outputs/mapping/${variant.dirName}/mapping.txt"into destPathrename { String fileName ->fileName.replace("mapping.txt", "${newFileNamePrefix}-mapping.txt")}from "${buildDir}/intermediates/symbols/${variant.dirName}/R.txt"from "${buildDir}/intermediates/symbol_list/${variant.dirName}/R.txt"from "${buildDir}/intermediates/runtime_symbol_list/${variant.dirName}/R.txt"into destPathrename { String fileName ->fileName.replace("R.txt", "${newFileNamePrefix}-R.txt")}}}}}}project.afterEvaluate {//sample use for build all flavor for one timeif (hasFlavors) {task(tinkerPatchAllFlavorRelease) {group = 'tinker'def originOldPath = getTinkerBuildFlavorDirectory()for (String flavor : flavors) {def tinkerTask = tasks.getByName("tinkerPatch${flavor.capitalize()}Release")dependsOn tinkerTaskdef 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"project.tinkerPatch.oldApk = getOldApkPath()System.out.println("lllttt oldApk=" + project.tinkerPatch.oldApk)project.tinkerPatch.buildConfig.applyMapping = ""project.tinkerPatch.buildConfig.applyResourceMapping = originOldPathSystem.out.println("lllttt old R=" + project.tinkerPatch.buildConfig.applyResourceMapping)}}}task(tinkerPatchAllFlavorDebug) {group = 'tinker'def originOldPath = getTinkerBuildFlavorDirectory()for (String flavor : flavors) {def tinkerTask = tasks.getByName("tinkerPatch${flavor.capitalize()}Debug")dependsOn tinkerTaskdef 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"project.tinkerPatch.oldApk = getOldApkPath()System.out.println("lllttt oldApk=" + project.tinkerPatch.oldApk)project.tinkerPatch.buildConfig.applyMapping = ""project.tinkerPatch.buildConfig.applyResourceMapping = originOldPathSystem.out.println("lllttt old R=" + project.tinkerPatch.buildConfig.applyResourceMapping)}}}}} }

8.經過上面的配置后就可以用了,這一節我們說說怎么使用

雙擊該位置生成基準包,如果沒有多渠道就點擊assemble,這時會生成debug和release包和對應的R.txt文件

具體生成位置是:build-bakApk

然后修改app.gradle中的tinkerOldApkPath,tinkerApplyResourcePath,tinkerBuildFlavorDirectory三項配置和getTINKER_ID()方法中的后綴自增,并修改bug或者修改ui或邏輯

然后按照下面生成debug或者release包

生成的位置是:build-outputs-apk-tinkerPatch-patch_signed_7zip.apk(這個就是生成的差量包)

我們可以放到存儲目錄,也可以放到cache目錄

然后調用下面的api來安裝增量包,并且會在上面配置的SampleResultService中有回調,成功后重啟app或者鎖屏后就可以了

9.各種api

//安裝增量包 //TinkerInstaller.onReceiveUpgradePatch(getContext(), Environment.getExternalStorageDirectory().getAbsolutePath() + "/patch_signed_7zip.apk") //安裝增量so(未測試) //TinkerLoadLibrary.installNavitveLibraryABI(getApplicationContext(), "armeabi"); // System.loadLibrary("stlport_shared"); //清除增量包 //Tinker.with(getApplicationContext()).cleanPatch(); //是否安裝了增量包 //Tinker.with(getApplicationContext()).isTinkerLoaded(); //兼容google play //https://github.com/Tencent/tinker/issues/1314

然后就可以難過的玩耍了(其實聽說bugly接入方式更簡單,問題更少,不過這是后話了)

結語

第一次寫文章不想寫結語

完成這次接入并測試完成,我大概用了三個這樣的瀏覽器tab(同時打開,因為都有用,沒用的都關了)

如果有問題或者有問題(沒有語句bug)請提交評論

end

總結

以上是生活随笔為你收集整理的接入Tinker热修复和踩坑的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

久久精品中文字幕少妇 | 久久久久久欧美二区电影网 | 美女亚洲精品 | 精品国偷自产在线 | 在线国产精品视频 | 成人黄色视 | 亚洲人xxx| 99综合久久 | 奇米影视777四色米奇影院 | 超碰免费观看 | www.福利 | 日韩欧美视频一区二区三区 | 免费看毛片在线 | 一区二区三区四区免费视频 | 久久国产精品免费视频 | 亚洲精品乱码久久久一二三 | 成片免费观看视频大全 | 韩国av免费观看 | 国产成人三级一区二区在线观看一 | 精品国产午夜 | 国产一级久久久 | 成人av直播| 久久精品人| 欧美久久久 | 亚洲精品国产精品国 | 久久国内精品视频 | 久久五月婷婷综合 | 国产三级视频 | 中文字幕免费久久 | 亚洲精品乱码久久久久v最新版 | 欧美视频在线二区 | 日韩欧美在线视频一区二区三区 | 日韩精品久久久久久中文字幕8 | 亚洲国产中文在线 | 欧美极品裸体 | 免费av网址在线观看 | 69av免费视频 | 久久中文字幕视频 | 国产大尺度视频 | 99在线精品视频 | 欧美一区二区三区在线视频观看 | 91精品成人久久 | 欧美先锋影音 | av在线播放快速免费阴 | 国产精品18videosex性欧美 | 在线免费观看一区二区三区 | 精品美女视频 | 91亚洲在线 | 丁香六月久久综合狠狠色 | 狠狠躁18三区二区一区ai明星 | 美女视频黄是免费的 | 免费男女羞羞的视频网站中文字幕 | 中文一二区 | 91精品第一页 | 欧美污污视频 | 久久99国产一区二区三区 | 久久99亚洲精品久久久久 | 国产a级片免费观看 | 久久精品黄色 | 久热精品国产 | 国产精品毛片一区二区 | 插综合网| 激情五月婷婷综合 | 色婷婷伊人 | 超碰在线最新地址 | 国产电影黄色av | 国产成人在线综合 | 最近在线中文字幕 | 欧美aa一级片 | 国内三级在线观看 | 欧美成人91| 五月婷婷视频在线观看 | 在线视频电影 | 久久精品网站视频 | 国产精品成人国产乱 | 免费看的黄色录像 | 国产高清在线不卡 | 人人搞人人干 | 日本性动态图 | 色婷婷综合久久久 | 久久99热精品 | 美女视频黄色免费 | 成年人视频在线免费 | 亚洲视频免费 | 亚洲,播放 | 九色91av| 免费黄色在线网址 | 激情图片久久 | 99c视频高清免费观看 | 日韩精品高清视频 | 欧美a性| 午夜91视频| 996久久国产精品线观看 | 亚洲精品国产精品国自产观看浪潮 | 波多野结衣视频一区 | 日韩sese| 91天天操| 亚洲精品视频在线看 | 国产精品视频 | 亚洲成人精品久久久 | 国产精品九九热 | 亚洲国产成人久久 | 亚洲欧洲xxxx| 国产又粗又硬又长又爽的视频 | 国产精品欧美 | 最新日韩视频 | 人人澡人人爽欧一区 | 久久精品91久久久久久再现 | 黄色资源在线观看 | 日日麻批40分钟视频免费观看 | 欧美在线观看视频 | 日韩在线观| 精精国产xxxx视频在线播放 | 天天射天天射天天射 | 成年免费在线视频 | 人人爽人人做 | 在线一二三区 | 日本xxxxav | 欧美激情另类 | 日产乱码一二三区别在线 | 免费v片| 美女天天操 | 黄色的网站免费看 | 国产日韩精品在线观看 | 久久精品国产精品亚洲精品 | 午夜.dj高清免费观看视频 | 日韩欧美一区二区在线播放 | 亚洲国产美女精品久久久久∴ | 成人黄色在线 | 婷婷久久一区 | 美女国产 | 国产精品色婷婷视频 | 国产一级片免费视频 | 色射爱 | 久久综合久色欧美综合狠狠 | 在线视频久久 | a在线播放| 久久成人一区二区 | av国产在线观看 | 日韩精品视频第一页 | 久久久久久久久久福利 | 国产中文a | 1024手机看片国产 | 五月天精品视频 | 久久av一区二区三区亚洲 | 99精品热 | 在线黄色av电影 | 日韩av电影手机在线观看 | av电影久久 | 91丨九色丨首页 | 97超碰在线久草超碰在线观看 | 狠狠干狠狠艹 | 欧美乱大交 | 久久99精品久久久久久久久久久久 | 久久国产精品精品国产色婷婷 | 午夜精品福利一区二区三区蜜桃 | 中文字幕专区高清在线观看 | 国产精品高潮在线观看 | 综合婷婷久久 | 国产区在线看 | 91九色免费视频 | 中文字幕精品一区二区精品 | 国产一区二区电影在线观看 | 午夜影视一区 | 久久精品视频网 | 国产精品视频内 | 久99久精品视频免费观看 | 国产乱对白刺激视频在线观看女王 | 99r国产精品 | 久久夜色精品国产亚洲aⅴ 91chinesexxx | 99精品欧美一区二区蜜桃免费 | 在线观看成人小视频 | 国产又粗又硬又爽视频 | 一区二区激情视频 | 欧美黄色软件 | 二区在线播放 | 日本精品一区二区在线观看 | 精品成人网 | 色999视频| 国产精品欧美日韩在线观看 | 免费午夜在线视频 | 免费观看日韩av | 国产一级在线播放 | 国产一区二区中文字幕 | 国产精品亚洲a | 国产精品一区免费观看 | 操久久网| 999久久 | 成人国产精品免费观看 | 欧美最猛性xxxxx(亚洲精品) | 日韩黄色一级电影 | 久久久wwww| 国产成人精品免费在线观看 | 91黄色成人| 久久高视频 | 黄色免费网 | 精品你懂的 | 亚洲欧美日韩国产精品一区午夜 | 亚洲精品国产精品乱码在线观看 | 成 人 黄 色 片 在线播放 | 91亚洲永久精品 | 在线免费看黄网站 | 成人av亚洲 | 月下香电影 | 国产免费一区二区三区网站免费 | 美女网站视频免费黄 | 干 操 插 | 中文字幕一区二区三区四区视频 | 国产精品美女久久 | 午夜精品久久久久久99热明星 | 久久人人爽人人人人片 | 九九热在线精品 | av电影在线观看完整版一区二区 | 日本中文字幕在线观看 | 91免费版在线观看 | 天天拍天天爽 | 青青河边草免费视频 | 国产91精品看黄网站在线观看动漫 | 亚洲欧美少妇 | 免费久草视频 | 五月激情姐姐 | 97在线观看免费高清完整版在线观看 | 国产亚洲精品久 | 久久毛片视频 | 99精品视频在线观看 | 经典三级一区 | 免费看一级特黄a大片 | 日韩精品视频免费专区在线播放 | 日本在线中文 | 91麻豆免费视频 | 国产精品女 | 93久久精品日日躁夜夜躁欧美 | 激情婷婷在线观看 | 精品国内自产拍在线观看视频 | 国产精品美女久久久久久网站 | 九九热久久久 | 色久综合| 日韩电影在线观看中文字幕 | 黄色毛片电影 | 永久免费毛片 | 国产精品久久久久久久久久免费 | 免费高清男女打扑克视频 | 欧美午夜寂寞影院 | 一本一本久久aa综合精品 | 日韩精品一区二区在线观看视频 | 日韩精品亚洲专区在线观看 | 精品久久久久亚洲 | av色综合 | 国产一二区在线观看 | 欧美视频不卡 | 97香蕉久久超级碰碰高清版 | 精品国产乱码久久久久久浪潮 | 中文字幕之中文字幕 | 麻豆精品视频在线 | 在线免费91 | 久久久综合精品 | 中文字幕日韩伦理 | 国产精品免费一区二区三区在线观看 | 欧美亚洲一区二区在线 | 国产高清在线观看av | 日本精品久久久久影院 | 最新av网站在线观看 | 天天综合婷婷 | 亚洲国产精品成人va在线观看 | 狠狠狠狠狠操 | 久久艹精品 | 久久精品日产第一区二区三区乱码 | 免费看的av片 | 国产小视频在线播放 | 午夜av影院 | 在线色资源 | 欧美色道 | 最新日韩电影 | 久久精品视频在线看 | 伊人久久精品久久亚洲一区 | 亚洲人精品午夜 | 中文在线字幕观看电影 | 天天操夜夜看 | 亚州精品成人 | 日韩黄色大片在线观看 | 国产一区二区三区免费在线 | 一级片免费视频 | 91免费高清视频 | 天天激情天天干 | 丝袜护士aⅴ在线白丝护士 天天综合精品 | 免费久久久久久久 | 丁香花中文在线免费观看 | av一区在线 | 欧美日本中文字幕 | 狠狠狠狠狠色综合 | 久久久久久久久久久国产精品 | 久草久草在线 | 91精品视频网站 | 一区二区三区国产精品 | 黄色片视频在线观看 | 亚洲色图色| 水蜜桃亚洲一二三四在线 | 欧美色噜噜噜 | www黄色av | 国产精品久久免费看 | 91.麻豆视频 | 黄色免费网站 | 国产综合小视频 | 99在线精品观看 | 一二三区av | 在线观看精品黄av片免费 | 免费高清在线观看成人 | 91黄色在线看 | 97在线精品 | 99热精品免费观看 | 免费看的黄色 | 色噜噜日韩精品欧美一区二区 | 不卡视频在线看 | 婷婷伊人五月天 | 久久日韩精品 | 天天爱天天操天天射 | 欧美日韩久久久 | 2021国产视频 | 日韩午夜电影 | 久久激情五月激情 | 成片人卡1卡2卡3手机免费看 | 久久少妇| 激情久久久久久久久久久久久久久久 | av无限看 | 一区二区三区福利 | 日韩欧美一区二区三区视频 | 一区二区三区四区影院 | 亚洲成人家庭影院 | 精品91在线 | 五月天国产精品 | 黄色大全视频 | 国产激情电影综合在线看 | 午夜精品久久久久久久99水蜜桃 | 欧美日韩在线看 | 国产在线观看中文字幕 | 成 人 黄 色 片 在线播放 | 午夜999| 丁香婷婷综合激情五月色 | 日韩在线短视频 | 丁香婷婷综合五月 | 夜夜高潮夜夜爽国产伦精品 | 成人在线观看资源 | 天天干天天天天 | 天天射色综合 | 国产一区在线观看免费 | 免费av网站在线看 | a午夜电影| 久久久久日本精品一区二区三区 | 国产精品一区二区三区在线 | 视频在线一区二区三区 | 99久久9| 五月综合激情网 | 国产 中文 日韩 欧美 | 亚洲国产综合在线 | 久久久久久电影 | 亚洲一区二区高潮无套美女 | 色婷婷免费视频 | 国产999精品视频 | 国内久久久 | 日本最新一区二区三区 | 亚洲精品视频在线观看网站 | 亚洲人人网 | 国产一级特黄毛片在线毛片 | 999在线视频| 在线观看中文字幕亚洲 | 国产青草视频在线观看 | 亚洲最大的av网站 | 午夜视频在线观看欧美 | 免费av的网站 | 国产三级国产精品国产专区50 | 亚洲毛片一区二区三区 | 中文字幕久久亚洲 | 在线免费中文字幕 | 97精品国产97久久久久久久久久久久 | 97操操| 亚洲成人av在线播放 | 亚洲综合色站 | 亚洲综合成人婷婷小说 | 免费a视频在线观看 | 国产黄在线 | 久久精品爱视频 | 九九热免费精品视频 | 九色免费视频 | 久久tv | 美女免费黄视频网站 | 国产资源av | 国产视频亚洲 | 成人观看视频 | 中文字幕一区二区三区四区久久 | 人人插人人费 | 99久久国产免费,99久久国产免费大片 | 国产精品永久久久久久久www | 日韩精品一区二区久久 | 综合激情av | 国产一级高清视频 | 日韩欧美99 | 国产在线资源 | www色,com| 国产精品久久久久久久av电影 | 国产偷国产偷亚洲清高 | 色综合久 | 中文永久免费观看 | 国产精品一区二区免费在线观看 | 日日麻批40分钟视频免费观看 | 国产欧美精品一区二区三区四区 | 成人精品视频久久久久 | 最近日韩免费视频 | 美女久久久久 | 91精品人成在线观看 | 天天色草 | 美女视频黄色免费 | 色网免费观看 | 日本公妇色中文字幕 | 久久久激情网 | 处女av在线 | 成人一级视频在线观看 | 91久久久久久久一区二区 | 欧美一级免费在线 | 精品久久国产 | 国产亚洲一区二区在线观看 | 精品日韩中文字幕 | 国产成人一区二区三区在线观看 | 欧美二区视频 | 成人天堂网| 在线观看视频黄色 | 亚洲va欧美va | 九月婷婷人人澡人人添人人爽 | 亚洲区色| 亚洲 欧洲 国产 日本 综合 | 亚洲精品综合欧美二区变态 | 91精品视频在线看 | 久久天天躁狠狠躁亚洲综合公司 | av先锋影音少妇 | 久久1区 | 欧美色综合天天久久综合精品 | 麻豆视频网址 | 亚洲va欧美va国产va黑人 | 69久久夜色精品国产69 | 中文字幕 国产 一区 | 爱爱av网| 日韩www在线 | 五月婷婷六月丁香激情 | 国产视频久久久 | 久久精品亚洲国产 | 天天干天天拍天天操天天拍 | 久草线| 国产精品一区专区欧美日韩 | 欧美日韩视频网站 | ,午夜性刺激免费看视频 | 久久色中文字幕 | 午夜av激情 | 九九在线高清精品视频 | 日韩网站一区 | 中文字幕文字幕一区二区 | 免费视频一级片 | 亚洲精品成人免费 | 在线免费观看麻豆 | 亚洲第一久久久 | 天天操天天透 | 欧美一级黄大片 | 亚洲狠狠婷婷 | 8x8x在线观看视频 | 亚洲五月花 | 婷婷深爱网 | 免费观看一级成人毛片 | 黄色软件在线观看视频 | 人人爽人人澡 | 色大片免费看 | 五月导航 | 久久久久国产精品免费网站 | 最近中文字幕完整视频高清1 | 免费观看国产成人 | 黄色亚洲 | 国产日韩视频在线播放 | 久久久久久久久爱 | 国产成人精品a | 香蕉色综合 | 开心婷婷色| 日日干视频 | 又粗又长又大又爽又黄少妇毛片 | 五月宗合网| 4438全国亚洲精品观看视频 | 亚洲在线视频免费观看 | www.精选视频.com | 久草在线资源网 | 亚洲乱亚洲乱妇 | 欧美日韩中文视频 | 天天操天天添 | 亚洲国产精品人久久电影 | 成人avav| 久久人人爽人人爽人人 | 免费成人结看片 | 九色精品免费永久在线 | 国产精品免费一区二区三区在线观看 | 成人观看 | 欧美精彩视频在线观看 | 久久久久免费看 | 在线观看中文字幕 | 国产精品久久久久久久久久久杏吧 | 色婷丁香 | 四虎精品成人免费网站 | 国产精品久久精品国产 | 欧美日韩视频一区二区 | 欧美日韩精品在线播放 | 色婷婷啪啪免费在线电影观看 | 夜色.com | 欧美日bb| 日本久久综合视频 | 日韩精品免费在线 | 国产一级视屏 | 99精品一区| 久久久久久久免费 | 亚洲第一av在线播放 | 999视频网站 | 国产青春久久久国产毛片 | 国产精品男女 | 免费a现在观看 | 国内精品久久久久久久97牛牛 | 91视频在线自拍 | 国产精品123 | 精品久久国产一区 | 国产精品资源网 | 欧美日韩在线视频一区 | 99爱在线观看 | 国产a高清| 国产高清专区 | 狠狠的操狠狠的干 | 狠狠网 | 一区二区三区免费在线观看视频 | 久久久国内精品 | 亚洲一区网| 99亚洲视频 | 亚洲国产日韩在线 | 特级西西人体444是什么意思 | 9久久精品| 九九热精品视频在线播放 | 天天在线视频色 | 九九视频免费 | 韩日精品中文字幕 | 国产中文字幕三区 | 欧美在线观看视频 | 99视频国产精品免费观看 | 国产美女网 | 免费观看www视频 | 91伊人影院 | 国产你懂的在线 | 五月激情站| 日韩欧美在线第一页 | 中文字幕在线字幕中文 | 国产日产精品一区二区三区四区 | 六月激情丁香 | 久久精品香蕉视频 | 国产精品岛国久久久久久久久红粉 | av 一区二区三区 | 中文字幕色在线 | 日韩欧美在线一区 | 国产精品乱码高清在线看 | 伊人婷婷综合 | av日韩不卡 | 亚洲精品国产精品国 | 国产精品欧美久久久久三级 | 亚洲精品国产片 | 777奇米四色 | 中文字幕久久精品一区 | 精品久久影院 | 成人在线观看免费视频 | 色综合亚洲精品激情狠狠 | 国产亚洲精品久久网站 | 国产中文字幕精品 | 91麻豆文化传媒在线观看 | 免费99视频 | 欧美一级日韩三级 | 天堂在线一区二区 | se婷婷| 丁香导航 | 国产精品精品国产色婷婷 | 久久成人免费视频 | 成年人免费在线 | 国产手机视频在线观看 | 麻豆视频观看 | 午夜国产在线观看 | 在线一区电影 | 久久这里只精品 | 国产精品第二十页 | 色偷偷88888欧美精品久久久 | 亚洲女人av | 日韩网站免费观看 | 久久黄色精品视频 | 精品国产色 | 免费观看的av网站 | 69绿帽绿奴3pvideos | 久久久久久久免费看 | 在线观看免费av网站 | 99视频免费看 | 色999在线 | 中文字幕一区二区三区久久蜜桃 | 在线高清av | 日韩精品视频在线免费观看 | 四虎8848免费高清在线观看 | 亚州视频在线 | 日本黄色黄网站 | 久久视频国产 | 亚洲视频 在线观看 | 免费看黄电影 | 最新av中文字幕 | 精品女同一区二区三区在线观看 | 天天精品视频 | www.香蕉视频在线观看 | 免费视频一区 | 天无日天天操天天干 | 69精品在线 | 97理论片 | 91视频91蝌蚪| 亚洲理论在线观看 | 中文字幕一区二区三区四区久久 | 青青久草在线 | 日韩超碰 | 9797在线看片亚洲精品 | 日韩成人在线一区二区 | 久久久久久国产一区二区三区 | 麻豆视传媒官网免费观看 | 国产精品3 | 国产一卡久久电影永久 | 中文字幕免费在线 | 日韩精品一区二 | 久 久久影院 | 亚洲欧洲一区二区在线观看 | 91秒拍国产福利一区 | 美女很黄免费网站 | 99爱精品视频 | 91在线视频播放 | 久久国产一区 | 蜜桃视频日韩 | 久久精品—区二区三区 | 国产精品网红直播 | 国产原厂视频在线观看 | а天堂中文最新一区二区三区 | 99国内精品久久久久久久 | 中文字幕电影高清在线观看 | 日韩在线电影一区 | 最新色站| 91在线中字 | 久久精品3| 久久精精品 | 国产手机在线观看 | 亚洲精品久久久久久国 | 香蕉影视app | 国产精品久久久久影院日本 | 九九热免费视频在线观看 | 久久午夜精品视频 | 天天爱天天舔 | 日韩欧美有码在线 | 美女精品久久久 | 麻豆影视网站 | 国产精品观看视频 | 国产精品国产三级国产aⅴ无密码 | 久久视频一区 | 亚洲在线黄色 | 亚洲 综合 激情 | 天天综合网入口 | 国产一级片一区二区三区 | 911国产精品| 国产第一福利网 | 欧美成人高清 | 亚洲高清国产视频 | 国产视频一区二区在线 | 成人播放器| 五月精品 | 国产精品永久免费观看 | 综合网天天射 | 欧美国产亚洲精品久久久8v | 亚州免费视频 | 国产专区在线 | av线上免费观看 | 亚洲国产精品传媒在线观看 | 色婷婷久久久综合中文字幕 | 国产亚洲精品综合一区91 | 亚洲精品天天 | 国产黄色片久久 | 97国产视频 | 久久黄色a级片 | 国产成人精品在线 | 欧美精品中文字幕亚洲专区 | av在线成人| 久久亚洲精品国产亚洲老地址 | 福利在线看片 | 免费日韩电影 | 美女国内精品自产拍在线播放 | 日韩欧美在线不卡 | 99re国产| 国产二区视频在线 | 99欧美精品 | 亚洲日本黄色 | www.黄色片网站 | 亚洲 成人 一区 | 精品国产成人在线影院 | 午夜美女av | 日韩精品最新在线观看 | 九色视频网站 | 国产另类av | 少妇bbbb搡bbbb桶 | 毛片网站在线看 | 99精品免费在线 | 精品毛片久久久久久 | 亚洲 中文 欧美 日韩vr 在线 | 特黄色大片 | 欧美性大战 | 开心丁香婷婷深爱五月 | 国产91精品看黄网站 | 久久久久久久久福利 | 人人爽久久涩噜噜噜网站 | 香蕉视频网站在线观看 | 日本激情视频中文字幕 | 精品国产一区在线观看 | 久久人人插 | 国产又粗又长又硬免费视频 | 视频一区二区精品 | 亚洲最大av网站 | 日产乱码一二三区别免费 | 国产精品99久久免费黑人 | 91成人在线观看高潮 | 日韩av线观看 | 日日夜夜狠狠干 | 国产精品不卡av | 日日夜夜人人天天 | 免费亚洲黄色 | 国产精品免费看久久久8精臀av | 国产欧美高清 | 久久人人爽人人人人片 | 亚洲经典视频在线观看 | 99精品视频在线观看播放 | 日韩美一区二区三区 | 国产久草在线观看 | 亚在线播放中文视频 | 黄色在线免费观看网站 | 中文久久精品 | 又色又爽又激情的59视频 | 欧美激情视频在线免费观看 | 国产中文字幕在线观看 | 国产一级视屏 | 久久香蕉国产精品麻豆粉嫩av | 久久久五月婷婷 | 日韩av在线资源 | 一级黄毛片| 欧美日韩一区二区三区在线观看视频 | 97手机电影网 | 日韩一级片大全 | 成年人视频免费在线 | 日韩成人精品一区二区三区 | 日韩在线电影观看 | 黄色免费网站 | av在线影片 | 久久草网站 | 久久精品国产一区二区 | 玖玖色在线观看 | 国产精品久久一区二区三区, | 天天操欧美| 国产亚洲精品无 | 国产高清成人 | 一本—道久久a久久精品蜜桃 | 亚洲精选视频在线 | 精品国内自产拍在线观看视频 | 久久夜夜爽 | 欧美中文字幕第一页 | 亚洲久草在线 | 国产精品美女久久久久久久久 | 免费视频 三区 | 国产福利免费看 | 在线观看亚洲成人 | 国产无遮挡又黄又爽馒头漫画 | 五月综合久久 | 自拍超碰在线 | 国产精品18久久久久白浆 | 黄网站免费久久 | 日韩精品一区二区三区不卡 | 波多野结衣电影一区二区三区 | 97色婷婷成人综合在线观看 | 国产精品综合在线 | 在线视频你懂 | 精品五月天 | 国产精品久久久久久久久大全 | 免费视频在线观看网站 | 久久蜜桃av | 女人高潮特级毛片 | 久草国产在线观看 | 日韩精品一区二区三区免费观看视频 | 一区二区三区韩国免费中文网站 | 色偷偷88888欧美精品久久 | 精品国产成人av在线免 | 久久久久久久久久影视 | 贫乳av女优大全 | 亚洲成人第一区 | 午夜成人免费电影 | 国产精品白丝jk白祙 | 超碰在97 | 日韩精品免费一线在线观看 | 成人久久18免费网站 | 四虎国产精品成人免费影视 | 日日日爽爽爽 | 成年人电影免费看 | 麻豆视频观看 | 亚洲国产视频a | 视频99爱 | 综合亚洲视频 | av大全在线免费观看 | 国产午夜麻豆影院在线观看 | 日本护士撒尿xxxx18 | 91日韩精品 | 国内精品视频久久 | 首页国产精品 | 国产一区在线免费观看 | 欧美精品久久久久久久免费 | 在线 国产 亚洲 欧美 | 成人网444ppp | 午夜99| 青青草华人在线视频 | 999成人免费视频 | 久久精品男人的天堂 | 久久中文网 | 欧美日韩精品网站 | 色婷婷九月| 国产黄影院色大全免费 | 97精品一区二区三区 | 欧美国产三区 | 免费av小说| av黄色一级片 | 麻豆果冻剧传媒在线播放 | 五月开心网 | 亚洲一区视频免费观看 | 热re99久久精品国产99热 | 欧美激情视频免费看 | 久久久久久福利 | 制服丝袜天堂 | avwww在线 | 天天操天天色天天射 | 九九交易行官网 | 国产视频在线免费 | 成人av免费播放 | 97超碰人人看 | 青草视频在线 | 久草在线最新免费 | 久久久久久久久久伊人 | 欧美久久久久久久久 | 成人av资源网站 | 色夜影院 | 日本午夜在线观看 | 久久狠狠干 | 国产视频在线播放 | 日韩中文字幕免费看 | 视频一区久久 | 超碰97免费在线 | 91精品在线麻豆 | 久久黄色精品视频 | 国产在线2020 | 久久69av| www.看片网站 | 午夜久久电影网 | 欧美巨大 | 日本久久免费电影 | 丁香久久激情 | 日韩午夜小视频 | 中文字幕精品三区 | 在线а√天堂中文官网 | 日本mv大片欧洲mv大片 | 欧美国产精品久久久久久免费 | 国产日本高清 | 国产精品麻豆免费版 | 天天综合网 天天综合色 | 99c视频在线| 精品一区二区久久久久久久网站 | 国产精品国产三级在线专区 | 在线播放亚洲激情 | 久久精品波多野结衣 | 中国一级特黄毛片大片久久 | 精品久久久国产 | 三日本三级少妇三级99 | 日日射天天射 | 天天做日日做天天爽视频免费 | 韩日av一区二区 | 91精品国产福利 | 一区二区三区国产欧美 | 中日韩欧美精彩视频 | 免费看的黄色片 | 国内小视频| 日韩在线视频观看免费 | 久久精品国产精品亚洲 | 97香蕉久久超级碰碰高清版 | 天天射天天艹 | 亚洲乱码中文字幕综合 | 蜜臀av麻豆 | 亚洲精品网站在线 | 国产成人免费av电影 | 性色视频在线 | 69成人在线| 国产精品入口传媒 | 日本成人中文字幕在线观看 | 亚洲成人免费在线 | 韩国一区二区三区视频 | 久久人人爽人人人人片 | 欧美精品一区二区三区四区在线 | 草久久久久久久 | 久久久婷 | 久久久久久免费视频 | a视频免费在线观看 | 97色狠狠| 99精品视频在线观看免费 | 午夜精品久久久久久久99无限制 | 亚洲一区二区三区四区在线视频 | 九九热免费视频在线观看 | 在线精品亚洲 | 黄色av大片 | 日韩在线观看不卡 | 欧美日韩亚洲精品在线 | 国产综合激情 | www.久久爱.cn | 在线视频 区 | 国产精品国产三级国产aⅴ9色 | 丁香五月亚洲综合在线 | 婷婷精品 | 久久综合色婷婷 | 波多野结衣在线播放视频 | 国产一级不卡视频 | 国产一区在线观看视频 | 91激情小视频 | 久久综合狠狠狠色97 | 麻豆精品传媒视频 | 国产免费观看av | 少妇性bbb搡bbb爽爽爽欧美 | www.69xx| 色wwww| 91福利小视频 | 国内精品视频久久 | 精品一区二区在线观看 | 色综合五月 | 二区中文字幕 | 婷婷视频在线观看 | 成人三级网址 | 色婷婷www| 波多野结衣电影久久 | 人人澡人人爱 | 午夜精品久久久久久久久久久久久久 | 国产一级h | 国产成人高清 | 国产精品久久久久久久久久新婚 | 亚洲激情p | 欧美 国产 视频 | 国内精品在线看 | 黄色网在线播放 | 日韩久久影院 | 国产成人区 | 三级a毛片 | 中文字幕亚洲高清 | 高清av免费一区中文字幕 | www看片网站| 国产精品久久久久久久久婷婷 | 久久久久久久久久影视 | 日本3级在线观看 | 久艹视频免费观看 | 奇米影视8888| 久久精品久久精品久久39 | 色综合久久久久网 | 日韩av一区二区三区四区 | 波多野结衣一区三区 | 一本一本久久a久久精品综合 | 欧美一级在线看 | 久久久久亚洲精品 | 国产精品永久在线观看 | avove黑丝 | 日韩色一区二区三区 | 亚洲美女视频在线 | 久久国产美女 | 日韩av成人在线 | 97精产国品一二三产区在线 | 日韩专区在线播放 | 狠狠狠狠干 | 精品久久久久久电影 | 欧美日韩国产精品一区 | 91麻豆精品国产91久久久无需广告 | 国产成人三级在线观看 | 狠狠狠狠狠狠狠干 | 国产黄网站在线观看 | 免费看91的网站 | 国产精品久久久久久久久久久久午 | 精品久久1 | 在线观看成人国产 | 操操操日日日干干干 | 国产精品视频app | 成人午夜电影在线观看 | 最近中文字幕免费 | 91九色成人蝌蚪首页 | 成人禁用看黄a在线 | 午夜精品一区二区三区免费 | www.夜色.com | 中文字幕在线观看第一区 | 欧美91片 | 亚洲一区二区精品视频 | 韩国av免费观看 | 免费一级片久久 |