Neptune 跨Dex调用问题解决

本文记录了在维护Neptune框架时遇到的跨Dex调用问题及其解决方案。Google在Android P中引入的新检测导致了跨dex文件的inline方法调用异常。分析了Neptune源码,提出了解决方案,包括避免插件中重复基础依赖,打破双亲委派模型等。同时,还探讨了其他修复策略,如避免内联、不预置ROM jar包等。
摘要由CSDN通过智能技术生成

前言

最近在维护Neptune框架的时候,遇到了一个问题,这里简单的记录下问题原因和解决方案。

Neptune

这是爱奇艺开源的两个插件化框架之一,一个是Neptune,一个是Qigsaw

Neptune is a flexible, powerful and lightweight plugin framework for Android.

Neptune早期借鉴了百度的插件化框架,后续借鉴了滴滴推出的插件化框架方案VirtualAPK,慢慢发展成为一套较为稳定的方案。

目前服务于爱奇艺和随刻两个App中,爱奇艺主App目前有25+插件,随刻有10+。

问题

[entrypoint_utils-inl.h:97] Inlined method resolution crossed dex file boundary: from java.lang.String com.xxx.xxx.xxx.a(android.content.Context, org.json.JSONObject) in /data/user/0/com.qiyi.video/app_pluginapp/...

分析

这是一个跨Dex调用的常见问题:

Google在Android P中添加了新的检测项,对国内大多数应用造成了严重影响:在调用resolve inline method时,如果检测到caller与callee处于不同的dex file,会主动发起abort(inline不允许跨dex文件),导致应用出现闪退等异常问题。

art/runtime/entrypoints/entrypoint_utils-inl.h

让我们看看相关的源码:

namespace art {

inline ArtMethod* GetResolvedMethod(ArtMethod* outer_method,
                                    const CodeInfo& code_info,
                                    const BitTableRange<InlineInfo>& inline_infos)
    REQUIRES_SHARED(Locks::mutator_lock_) {
  DCHECK(!outer_method->IsObsolete());

  // This method is being used by artQuickResolutionTrampoline, before it sets up
  // the passed parameters in a GC friendly way. Therefore we must never be
  // suspended while executing it.
  ScopedAssertNoThreadSuspension sants(__FUNCTION__);

  {
    InlineInfo inline_info = inline_infos.back();

    if (inline_info.EncodesArtMethod()) {
      return inline_info.GetArtMethod();
    }

    uint32_t method_index = code_info.GetMethodIndexOf(inline_info);
    if (inline_info.GetDexPc() == static_cast<uint32_t>(-1)) {
      // "charAt" special case. It is the only non-leaf method we inline across dex files.
      ArtMethod* inlined_method = jni::DecodeArtMethod(WellKnownClasses::java_lang_String_charAt);
      DCHECK_EQ(inlined_method->GetDexMethodIndex(), method_index);
      return inlined_method;
    }
  }

  // Find which method did the call in the inlining hierarchy.
  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
  ArtMethod* method = outer_method;
  for (InlineInfo inline_info : inline_infos) {
    DCHECK(!inline_info.EncodesArtMethod());
    DCHECK_NE(inline_info.GetDexPc(), static_cast<uint32_t>(-1));
    uint32_t method_index = code_info.GetMethodIndexOf(inline_info);
    ArtMethod* inlined_method = class_linker->LookupResolvedMethod(method_index,
                                                                   method->GetDexCache(),
                                                                   method->GetClassLoader());
    if (UNLIKELY(inlined_method == nullptr)) {
      LOG(FATAL) << "Could not find an inlined method from an .oat file: "
                 << method->GetDexFile()->PrettyMethod(method_index) << " . "
                 << "This must be due to duplicate classes or playing wrongly with class loaders";
      UNREACHABLE();
    }
    DCHECK(!inlined_method->IsRuntimeMethod());
    if (UNLIKELY(inlined_method->GetDexFile() != method->GetDexFile())) {
      // TODO: We could permit inlining within a multi-dex oat file and the boot image,
      // even going back from boot image methods to the same oat file. However, this is
      // not currently implemented in the compiler. Therefore crossing dex file boundary
      // indicates that the inlined definition is not the same as the one used at runtime.
      bool target_sdk_at_least_p =
          IsSdkVersionSetAndAtLeast(Runtime::Current()->GetTargetSdkVersion(), SdkVersion::kP);
      // 这里便是抛异常的地方    
      LOG(target_sdk_at_least_p ? FATAL : WARNING)
          << "Inlined method resolution crossed dex file boundary: from "
          << method->PrettyMethod()
          << " in " << method->GetDexFile()->GetLocation() << "/"
          << static_cast<const void*>(method->GetDexFile())
          << &#
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值