XML中定义的Android片段是片段内的不同实例,而不是其父活动

时间:2016-09-16 17:42:07

标签: android android-activity fragment

我有一个用XML描述的片段定义的活动。在活动中,我检索对片段的引用:

@Override
public void onStart() {
    super.onStart();

    FragmentManager manager = getSupportFragmentManager();
    mFragment = (DetailActivityFragment) manager.findFragmentById(R.id.fragment);
    ...
}

我调用片段的(自定义)updateUI()方法,它的ID与活动中的ID不同:

DetailActivity: tbm 440; got fragment: (0x038c6009) (invisible)
DetailActivityFragment: tbm 162; fragment: (0x05f54af9) updating UI

然后,当碎片被破坏时:

DetailActivityFragment: tbm 131; destroying fragment (0x038c6009)

即。在活动中创建的相同片段,与实际片段中的不同

如果重要,请按照以下方式记录片段ID:

Log.d(TAG, String.format("tbm 162; (0x%08x) updating UI", System.identityHashCode(this));

此外,< tbm 162'中显示的ID log语句始终相同,并且始终匹配第一次实例化片段的ID。

这种堕落有什么特别的原因吗?片段的内存ID如何在实例化时间和片段在内部引用的时间之间发生变化?问题当然是在updateUI方法中,引用了包含在(现在隐藏和分离的)原始片段中的UI元素,因此UI实际上从未实际变化。

TIA!

编辑:

updateUI()方法除了显示某些字段外什么都不做,所以我认为它不相关,但是如果我遗漏了某些内容,请求

public void updateUi(final InformativeBean bean) {
    Log.d(TAG, String.format("tbm 162; (0x%08x) (%s) updating UI",
                             System.identityHashCode(this),
                             this));

    if (mView == null || bean == null) {
        return;
    }

    if (bean.getBattery() != null) {
        TextView battery_info = (TextView) mView.findViewById(R.id.battery_field);
        battery_info.setText(String.format("%s%%", bean.getBattery().getPercentage()));
    }

    ImageView burnin_view = (ImageView) mView.findViewById(R.id.burnin_flag_image);
    if (bean.isBurninComplete()) {
        burnin_view.setImageResource(R.drawable.ic_flag_burnin_complete);

    } else {
        burnin_view.setImageResource(R.drawable.ic_flag_burnin_incomplete);

    }

    Program program = bean.getProgram();
    Program new_program = bean.getPendingProgram();

    TextView current_program_view = (TextView) mView.findViewById(R.id.current_program_field);
    current_program_view.setBackgroundColor(mTransparentColor);

    if (new_program == null) {
        if (program == null) {
            return;
        }

        current_program_view.setText(program.toString());

    } else if (program == null || program.equals(new_program)) {
        current_program_view.setText(new_program.toString());

    } else {
        current_program_view.setText(String.format("%s -> %s", program, new_program));
        current_program_view.setBackgroundColor(mProgramBackgroundColor);

    }

}

编辑:

即使更奇怪,片段甚至会在其内部更改ID:

DetailActivityFragment: tbm 065; (0x06a9e492) (DetailActivityFragment{6a9e492 #0 id=0x7f0d0075}) in attach
DetailActivityFragment: tbm 104; (0x06a9e492) (DetailActivityFragment{6a9e492 #0 id=0x7f0d0075}) in onCreateView
DetailActivity: tbm 463; got fragment: (0x06a9e492) (DetailActivityFragment{6a9e492 #0 id=0x7f0d0075}) (invisible)
DetailActivityFragment: tbm 162; (0x068d3e1a) (DetailActivityFragment{68d3e1a}) updating UI

所以它是onAttach和onCreateView中的0x06a9e492(以及活动接收的引用),但在updateUI中固执地为0x068d3e1a。

1 个答案:

答案 0 :(得分:0)

经过大量调查后发现,包含错误片段的活动内的字段持有对该活动的引用。更改字段以便传递当前活动实例(vs持有对字段外部活动的隐式引用)修复了问题。我仍然有点困惑的是片段的ID即使在“活跃”时也能改变,但至少我可以取得进步。

这有很大帮助:http://www.androiddesignpatterns.com/2013/01/inner-class-handler-memory-leak.html。我一开始并没有发现这个问题是一种泄漏。

实际上,这是(原始的,错误的)代码:

DetailActivity
    ...
    some_field = new Listener() {
        // The outer activity is retained, causing its fragment to be (partially?) re-used.
        onSomeEvent() {
            Toast.makeText(DetailActivity.this, "Got Some Event", ...).show();
        }
    };
    ...

新的工作代码更像是:

DetailActivity
    ...
    some_field = ListenerWithActivity.getInstance(this);
    ...

ListenerWithActivity:

     public static ListenerWithActivity getInstance(Activity activity) {
         ListenerWithActivity existing = getExisting();
         if (existing == null) {
             existing = new ListenerWithActivity();
         }

         existing.setActivity(activity);
         return existing;
     }

     public void onSomeEvent() {
         Toast.makeText(getActivity(), "Got Some Event", ...).show();
     }