Tencent / tinker

Tinker is a hot-fix solution library for Android, it supports dex, library and resources update without reinstall apk.
Other
17.1k stars 3.33k forks source link

开启加固模式( isProtectedApp = true),加载补丁后运行时异常 #1650

Open 345166018 opened 2 years ago

345166018 commented 2 years ago

项目中使用了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的分配
    }
}
345166018 commented 2 years ago

image 补充一下,报错的具体位置如上所示

liangdong9676 commented 1 year ago

gradle版本:6.7 gradle插件版本:4.2.1 这也太高版本了吧