android找不到方法(unable to resolve virtual method)的警告与分析

今天同事问我这里怎么会提示这个

W/dalvikvm: VFY: unable to resolve virtual method 1886: 
Landroid/preference/PreferenceFragment;.onAttach (Landroid/content/ContextV
D/dalvikvm: VFY: replacing opcode 0x6f at 0x0000

环境

是这样出现的
minSdkVersion 14
targetSdkVersion 27

FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.setting_frame,new SettingWelcomeFragment());
transaction.commit();

SettingWelcomeFragment中

public class SettingWelcomeFragmentextends Fragment {

    protected boolean onKeyDown(int keyCode,KeyEvent event){
        return false;
    }

    @Override
    public void onAttach(Activity activity){
        super.onAttach(activity);
        Log.e("activity","onAttach");
    }

    @Override
    public void onAttach(Context context){
        Log.e("context","onAttach");
        super.onAttach(context);
    }
}

在api19的手机上会出现这个提示。

原因

1,onAttach(Context context)是API27新加的方法,在API14的手机上是没有这个方法的。
2,打印log的时候,可以看到android4.4的机器上没有打印出context的log(就是完全没被调用到。)
3,但是还是会提示,是因为加载class的时候,检查到有super.onAttach(context) 的方法,art试图建索引,但是没建成功,所以提示报错。

这个提示在哪里打印的

这个代码是在art中打印出来的,所以打印的时间点是在加载类的时候(即ClassLoader),所以这个提示只会出现一次.在加载类的时候,没找到方法的时候。提示
第一行提示是说找不到,第二行提示是把这个找不到的方法替换成空方法。

这个比较好找,因为源码中只有一个地方的前缀是VFY

method_verifier

std::ostream& MethodVerifier::LogVerifyInfo() {
  return info_messages_ << "VFY: " << PrettyMethod(dex_method_idx_, *dex_file_)
                        << '[' << reinterpret_cast<void*>(work_insn_idx_) << "] : ";
}

dex_to_dex_compiler

void DexCompiler::CompileReturnVoid(Instruction* inst, uint32_t dex_pc) {
  DCHECK_EQ(inst->Opcode(), Instruction::RETURN_VOID);
  if (unit_.IsConstructor()) {
    // Are we compiling a non clinit constructor which needs a barrier ?
    if (!unit_.IsStatic() &&
        driver_.RequiresConstructorBarrier(Thread::Current(), unit_.GetDexFile(),
                                           unit_.GetClassDefIndex())) {
      return;
    }
  }
  // Replace RETURN_VOID by RETURN_VOID_NO_BARRIER.
  VLOG(compiler) << "Replacing " << Instruction::Name(inst->Opcode())
                 << " by " << Instruction::Name(Instruction::RETURN_VOID_NO_BARRIER)
                 << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method "
                 << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true);
  inst->SetOpcode(Instruction::RETURN_VOID_NO_BARRIER);
}

为什么编译没报错呢,因为编译的时候有这个方法,但是实际跑的时候,手机上的库和编译时候的库有出入,所以art在这里做了简单的兼容/提示处理。

避免这个问题可以这样做:

解决方法一

把targetsdk修改成19,编译过程中as会提示错误的,最大程度的避免这个奇奇怪怪的错误

解决方法二

用SupportV4的方法getSupportFragmentManager,不用系统上的getFragmentManager的方法,SupportV4本身做了不少兼容处理,所以也不会有警告,也能被调用到。

猜你喜欢

转载自blog.csdn.net/yeshennet/article/details/80181258
今日推荐