tinker
tinker copied to clipboard
开启加固模式( isProtectedApp = true),加载补丁后运行时异常
项目中使用了ViewBinding,开启加固模式( isProtectedApp = true),加载补丁后运行时异常 Caused by: java.lang.NullPointerException: Missing required view with ID: cn.com.app:id/iv_last_ali
异常类型:app运行时异常
手机型号:oppo R17
手机系统版本:Android 10
tinker版本:1.9.14.17
gradle版本:6.7 gradle插件版本:4.2.1
是否使用热更新SDK: Bugly SDK
系统:Windows
堆栈/日志:
2022-04-28 10:03:49.767 31027-31027/? V/Tinker.TinkerUncaughtExceptionHandler: uncaughtException:Unable to start activity ComponentInfo{cn.com.app/com.baseextend.base.activity.BaseStandardActivity}: java.lang.NullPointerException: Missing required view with ID: cn.com.app:id/iv_last_ali
2022-04-28 10:03:49.768 31027-31027/? V/Tinker.TinkerUncaughtExceptionHandler: tinker has fast crash 1 times
2022-04-28 10:03:49.769 31027-31027/? E/Tinker.UncaughtHandler: TinkerUncaughtHandler catch exception:java.lang.RuntimeException: Unable to start activity ComponentInfo{cn.com.app/com.baseextend.base.activity.BaseStandardActivity}: java.lang.NullPointerException: Missing required view with ID: cn.com.app:id/iv_last_ali
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3015)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3093)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1794)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:192)
at android.app.ActivityThread.main(ActivityThread.java:6872)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:549)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:817)
Caused by: java.lang.NullPointerException: Missing required view with ID: cn.com.app:id/iv_last_ali
at cn.com.app.databinding.FragmentLoginMainBinding.bind(FragmentLoginMainBinding.java:216)
at cn.com.app.databinding.FragmentLoginMainBinding.inflate(FragmentLoginMainBinding.java:110)
at com.app.ui.login.LoginMainFragment$binding$2.invoke(LoginMainFragment.kt:41)
at com.app.ui.login.LoginMainFragment$binding$2.invoke(LoginMainFragment.kt:41)
at com.basemodule.basem.FragmentBindingDelegate.getValue(FragmentBindingDelegate.kt:29)
at com.app.ui.login.LoginMainFragment.getBinding(LoginMainFragment.kt:41)
at com.app.ui.login.LoginMainFragment.getBinding(LoginMainFragment.kt:38)
at com.basemodule.basem.BaseFragment.onCreateView(BaseFragment.kt:88)
at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2699)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:320)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1199)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1368)
at androidx.fragment.app.FragmentManager.moveFragmentToExpectedState(FragmentManager.java:1446)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1509)
at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:447)
at androidx.fragment.app.FragmentManager.executeOps(FragmentManager.java:2181)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2004)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1959)
at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1861)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2641)
at androidx.fragment.app.FragmentManager.dispatchActivityCreated(FragmentManager.java:2589)
at androidx.fragment.app.FragmentController.dispatchActivityCreated(FragmentController.java:247)
at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:541)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1340)
at android.app.Activity.performStart(Activity.java:7142)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2978)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3093)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1794)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:192)
at android.app.ActivityThread.main(ActivityThread.java:6872)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:549)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:817)
classpath 'com.android.tools.build:gradle:4.2.1'
classpath "com.tencent.bugly:tinker-support:1.2.3"
api 'com.tencent.tinker:tinker-android-lib:1.9.14.17'
api 'com.tencent.bugly:crashreport_upgrade:1.5.23'
ViewBinding相关代码如下:
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
import androidx.viewbinding.ViewBinding
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
class FragmentBindingDelegate<VB : ViewBinding>(
private val inflate: (li: LayoutInflater, container: ViewGroup?, attachToRoot: Boolean) -> VB
) : ReadOnlyProperty<BaseFragment, VB> {
private var isInitialized = false
private var _binding: VB? = null
private val binding: VB get() = _binding!!
override fun getValue(thisRef: BaseFragment, property: KProperty<*>): VB {
if (!isInitialized) {
thisRef.viewLifecycleOwner.lifecycle.addObserver(object : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun onDestroyView() {
_binding = null
}
})
_binding = inflate.invoke(thisRef.layoutInflater, thisRef.tempContainer, false)
thisRef.tempContainer = null
isInitialized = true
}
return binding
}
}
fun <VB : ViewBinding> Fragment.doBinding(
inflate: (li: LayoutInflater, container: ViewGroup?, attachToRoot: Boolean) -> VB
) = FragmentBindingDelegate(inflate)
@Launch(path = RoutePath.LOGIN_THIRD)
class LoginThirdFragment : BaseSimpleFragment() {
override val binding by doBinding(FragmentLoginSecondBinding::inflate)
override fun initAndObserve() {
binding.tvText.text = "页面3"
binding.ckbProtocol.isChecked = true
}
}
tinker-support.gradle 文件如下:
apply plugin: 'com.tencent.bugly.tinker-support'
//配置打包生成的apk的路径,定位在APP模块下的build/bakApk目录下
//形式如/user/**/desktop/org/.../main/app/bakApk
def bakPath = file("${project(":app").buildDir}/bakApk/")
//此处填写需要构建patch的发布包所在目录
//因为每次打包都会在上面的bakApk目录下生成一个以当前打包时间命名的文件件,文件件中存放着本次打包生成的apk,mapping.txt,R.txt.
def baseApkDir = 'app-0427-17-58-38'
//https://bugly.qq.com/docs/utility-tools/plugin-gradle-hotfix/
tinkerSupport {
// 开启tinker-support插件,默认值true
enable = true
// 是否启用覆盖tinkerPatch配置功能,默认值false
// tinkerSupport是bugly提供的配置属性
// 如果为true ,下面tinkerPatch中配置的属性不会生效,推荐这种。tinker的配置太繁琐复杂
overrideTinkerPatchConfiguration = true
// 生成patch.dex目录,默认值当前module的子目录tinker
autoBackupApkDir = "${bakPath}"
// 编译补丁包时,必需指定基线版本的apk,也就是针对哪个线上版本生成patch
baseApk = "${bakPath}/${baseApkDir}/rel/app-rel-release.apk"
// 对应tinker插件applyMapping 混淆规则
baseApkProguardMapping = "${bakPath}/${baseApkDir}/rel/app-rel-release-mapping.txt"
// 对应tinker插件applyResourceMapping 资源id映射
baseApkResourceMapping = "${bakPath}/${baseApkDir}/rel/app-rel-release-R.txt"
//自动生成下面的tinker的值 时间戳形式0707-12-10-10
autoGenerateTinkerId = true
// 构建基准包和补丁包都要指定不同的tinkerId,并且必须保证唯一性
// tinkerId = "base-1.0.2"
// 是否开启加固模式,默认为false
isProtectedApp = true
// 是否开启反射Application模式
enableProxyApplication = true
// 是否支持新增非export的Activity(注意:设置为true才能修改AndroidManifest文件)
supportHotplugComponent = true
}
/**
* 一般来说,我们无需对下面的参数做任何的修改
* 对于各参数的详细介绍请参考:
* https://github.com/Tencent/tinker/wiki/Tinker-%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97
*/
tinkerPatch {
//oldApk ="${bakPath}/${appName}/app-release.apk"
ignoreWarning = true
useSign = true
dex {
dexMode = "jar"
pattern = ["classes*.dex"]
loader = []
}
lib {
pattern = ["lib/*/*.so"]
}
res {
pattern = ["res/*", "r/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]
ignoreChange = []
largeModSize = 100
}
packageConfig {
}
sevenZip {
zipArtifact = "com.tencent.mm:SevenZip:1.1.10"
// path = "/usr/local/bin/7za"
}
buildConfig {
keepDexApply = false
//tinkerId = "1.0.1-base"
//applyMapping = "${bakPath}/${appName}/app-release-mapping.txt" // 可选,设置mapping文件,建议保持旧apk的proguard混淆方式
//applyResourceMapping = "${bakPath}/${appName}/app-release-R.txt" // 可选,设置R.txt文件,通过旧apk文件保持ResId的分配
}
}
补充一下,报错的具体位置如上所示
gradle版本:6.7 gradle插件版本:4.2.1 这也太高版本了吧