2013-03-22 6 views
65

У меня есть класс FragmentActivity с внутренним классом, который должен отображать Dialog. Но я должен сделать это static. Eclipse предлагает мне подавить ошибку @SuppressLint("ValidFragment"). Это плохой стиль, если я это делаю и каковы возможные последствия?Внутренний класс фрагмента должен быть статическим

public class CarActivity extends FragmentActivity { 
//Code 
    @SuppressLint("ValidFragment") 
    public class NetworkConnectionError extends DialogFragment { 
    private String message; 
    private AsyncTask task; 
    private String taskMessage; 
    @Override 
    public void setArguments(Bundle args) { 
     super.setArguments(args); 
     message = args.getString("message"); 
    } 
    public void setTask(CarActivity.CarInfo task, String msg) { 
     this.task = task; 
     this.taskMessage = msg; 
    } 
    @Override 
    public Dialog onCreateDialog(Bundle savedInstanceState) { 
     // Use the Builder class for convenient dialog construction 
     AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); 
     builder.setMessage(message).setPositiveButton("Go back", new DialogInterface.OnClickListener() { 
     @Override 
     public void onClick(DialogInterface dialog, int id) { 
      Intent i = new Intent(getActivity().getBaseContext(), MainScreen.class); 
      startActivity(i); 
     } 
     }); 
     builder.setNegativeButton("Retry", new DialogInterface.OnClickListener() { 
     @Override 
     public void onClick(DialogInterface dialog, int id) { 
      startDownload(); 
     } 
     }); 
     // Create the AlertDialog object and return it 
     return builder.create(); 
    } 
    } 

startDownload() начинает AsyncTask.

+1

может показать код – rkmax

+2

в общем его плохой практике игнорировать ворса. Это довольно умный инструмент. Попробуйте опубликовать свой код, чтобы получить ответ о том, как вы могли бы лучше работать. –

+0

Вы проверили этот http://code.google.com/p/android/issues/detail?id=41800, чтобы узнать, что такое ValidFragment? Линт говорит, что: «Каждый фрагмент должен иметь пустой конструктор, поэтому он может быть создан» – sandrstar

ответ

88

Нестационарные внутренние классы действительно содержат ссылку на их родительские классы. Проблема с созданием внутреннего класса Fragment non-static заключается в том, что вы всегда держите ссылку на операцию . The GarbageCollector не удалось найти Активность. Таким образом, вы можете «просочиться» Деятельность, если, например, меняется ориентация. Потому что Фрагмент все еще может жить и вставляется в новый Активность.

EDIT:

Поскольку некоторые люди спрашивали меня почему, например, я начал писать один, делая это, я нашел еще несколько проблем при использовании не статических фрагментов:

  • Они не могут быть использованы в xml-файл, поскольку у них нет пустого конструктора (у них может быть пустой конструктор, но вы обычно создаете нестатические вложенные классы, делая myActivityInstance.new Fragment(), и это отличается от вызова только пустого конструктора)
  • Они не могут b e повторно используется вообще, так как FragmentManager иногда вызывает этот пустой конструктор. Если вы добавили фрагмент в некоторые транзакции.

Так что для того, чтобы сделать мой пример работы мне пришлось добавить

wrongFragment.setRetainInstance(true); 

линии, чтобы не делать аварии приложения об изменении ориентации.

Если вы выполните этот код, у вас будет активность с некоторыми текстовыми изображениями и 2 кнопками - кнопки увеличат счетчик. И Фрагменты показывают, какую ориентацию они считают своей деятельностью. В начале все работает правильно. Но после изменения ориентации экрана только первый фрагмент работает корректно - второй по-прежнему вызывает материал при его старой активности.

Мой класс активность:

package com.example.fragmenttest; 

import android.annotation.SuppressLint; 
import android.app.Activity; 
import android.app.Fragment; 
import android.app.FragmentTransaction; 
import android.content.res.Configuration; 
import android.os.Bundle; 
import android.view.LayoutInflater; 
import android.view.View; 
import android.view.ViewGroup; 
import android.widget.Button; 
import android.widget.LinearLayout; 
import android.widget.TextView; 

public class WrongFragmentUsageActivity extends Activity 
{ 
private String mActivityOrientation=""; 
private int mButtonClicks=0; 
private TextView mClickTextView; 


private static final String WRONG_FRAGMENT_TAG = "WrongFragment" ; 

@Override 
protected void onCreate(Bundle savedInstanceState) 
{ 
    super.onCreate(savedInstanceState); 
    int orientation = getResources().getConfiguration().orientation; 
    if (orientation == Configuration.ORIENTATION_LANDSCAPE) 
    { 
     mActivityOrientation = "Landscape"; 
    } 
    else if (orientation == Configuration.ORIENTATION_PORTRAIT) 
    { 
     mActivityOrientation = "Portrait"; 
    } 

    setContentView(R.layout.activity_wrong_fragement_usage); 
    mClickTextView = (TextView) findViewById(R.id.clicksText); 
    updateClickTextView(); 
    TextView orientationtextView = (TextView) findViewById(R.id.orientationText); 
    orientationtextView.setText("Activity orientation is: " + mActivityOrientation); 

    Fragment wrongFragment = (WrongFragment) getFragmentManager().findFragmentByTag(WRONG_FRAGMENT_TAG); 
    if (wrongFragment == null) 
    { 
     wrongFragment = new WrongFragment(); 
     FragmentTransaction ft = getFragmentManager().beginTransaction(); 
     ft.add(R.id.mainView, wrongFragment, WRONG_FRAGMENT_TAG); 
     ft.commit(); 
     wrongFragment.setRetainInstance(true); // <-- this is important - otherwise the fragment manager will crash when readding the fragment 
    } 
} 

private void updateClickTextView() 
{ 
    mClickTextView.setText("The buttons have been pressed " + mButtonClicks + " times"); 
} 

private String getActivityOrientationString() 
{ 
    return mActivityOrientation; 
} 


@SuppressLint("ValidFragment") 
public class WrongFragment extends Fragment 
{ 


    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 
    { 
     LinearLayout result = new LinearLayout(WrongFragmentUsageActivity.this); 
     result.setOrientation(LinearLayout.VERTICAL); 
     Button b = new Button(WrongFragmentUsageActivity.this); 
     b.setText("WrongFragmentButton"); 
     result.addView(b); 
     b.setOnClickListener(new View.OnClickListener() 
     { 
      @Override 
      public void onClick(View v) 
      { 
       buttonPressed(); 
      } 
     }); 
     TextView orientationText = new TextView(WrongFragmentUsageActivity.this); 
     orientationText.setText("WrongFragment Activities Orientation: " + getActivityOrientationString()); 
     result.addView(orientationText); 
     return result; 
    } 
} 

public static class CorrectFragment extends Fragment 
{ 
    private WrongFragmentUsageActivity mActivity; 


    @Override 
    public void onAttach(Activity activity) 
    { 
     if (activity instanceof WrongFragmentUsageActivity) 
     { 
      mActivity = (WrongFragmentUsageActivity) activity; 
     } 
     super.onAttach(activity); 
    } 

    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 
    { 
     LinearLayout result = new LinearLayout(mActivity); 
     result.setOrientation(LinearLayout.VERTICAL); 
     Button b = new Button(mActivity); 
     b.setText("CorrectFragmentButton"); 
     result.addView(b); 
     b.setOnClickListener(new View.OnClickListener() 
     { 
      @Override 
      public void onClick(View v) 
      { 
       mActivity.buttonPressed(); 
      } 
     }); 
     TextView orientationText = new TextView(mActivity); 
     orientationText.setText("CorrectFragment Activities Orientation: " + mActivity.getActivityOrientationString()); 
     result.addView(orientationText); 
     return result; 
    } 
} 

public void buttonPressed() 
{ 
    mButtonClicks++; 
    updateClickTextView(); 
} 

} 

Обратите внимание, что вы должны, вероятно, не отбрасывать деятельность в onAttach, если вы хотите использовать фрагмент в различных видах деятельности - но здесь его работать на примере.

activity_wrong_fragement_usage.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
xmlns:tools="http://schemas.android.com/tools" 
android:layout_width="match_parent" 
android:layout_height="match_parent" 
android:orientation="vertical" 
tools:context=".WrongFragmentUsageActivity" 
android:id="@+id/mainView"> 

<TextView 
    android:id="@+id/orientationText" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:text="" /> 

<TextView 
    android:id="@+id/clicksText" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:text="" /> 



<fragment class="com.example.fragmenttest.WrongFragmentUsageActivity$CorrectFragment" 
      android:id="@+id/correctfragment" 
      android:layout_width="wrap_content" 
      android:layout_height="wrap_content" /> 


</LinearLayout> 
+0

Очень интересный и, вероятно, очень полезный ответ. Я имею дело с тем же вопросом. Не могли бы вы отбросить какой-то источник, на котором основан ваш ответ? – Egis

+3

@Egis, возможно, это может дать вам некоторое представление о вложенных статических внутренних классах: http://docs.oracle.com/javase/tutorial/java/javaOO/nested.html. – AxeEffect

+1

У вас есть ссылка на этот ответ? –

5

Если и развивать его в андроиде студии, то никаких проблем, если вы не дать ему как статической.Проект будет работать без каких-либо ошибок, и во время генерации apk вы получите сообщение об ошибке: этот внутренний фрагмент должен быть статическим [ValidFragment]

Thats lint error, вы, вероятно, строите с градиентом, чтобы отключить прерывание при ошибках , добавить:

lintOptions { 
    abortOnError false 
} 

to build.gradle. `

+6

вы правы, он пройдет процесс строительства, но правильно ли использовать его таким образом? потому что есть проблема с утечкой памяти, и поэтому андроид-студия предупреждает нас. – XcodeNOOB

+0

Иногда я думаю, что abortOnError для false - это просто сложно, я предпочитаю настраивать правило lint: «Внутренний класс должен быть статичным» до слабого предупреждения или информации. –

16

Я не буду говорить о внутренних фрагментах, но более конкретно о диалоговом диалоге, определенном в рамках действия, потому что это 99% случаев для этого вопроса.
С моей точки зрения, я не хочу, чтобы мой DialogFragment (ваш NetworkConnectionError) был статичным, потому что я хочу иметь возможность вызывать переменные или методы из моего содержащего класса (Activity) в нем.
Он не будет статичным, но я не хочу генерировать memoryLeaks.
Какое решение?
Простой. Когда вы заходите в onStop, убедитесь, что вы убили DialogFragment. Это так просто. код выглядит как-то вроде этого:

public class CarActivity extends AppCompatActivity{ 

/** 
* The DialogFragment networkConnectionErrorDialog 
*/ 
private NetworkConnectionError networkConnectionErrorDialog ; 
//... your code ...// 
@Override 
protected void onStop() { 
    super.onStop(); 
    //invalidate the DialogFragment to avoid stupid memory leak 
    if (networkConnectionErrorDialog != null) { 
     if (networkConnectionErrorDialog .isVisible()) { 
      networkConnectionErrorDialog .dismiss(); 
     } 
     networkConnectionErrorDialog = null; 
    } 
} 
/** 
* The method called to display your dialogFragment 
*/ 
private void onDeleteCurrentCity(){ 
    FragmentManager fm = getSupportFragmentManager(); 
    networkConnectionErrorDialog =(DeleteAlert)fm.findFragmentByTag("networkError"); 
    if(networkConnectionErrorDialog ==null){ 
     networkConnectionErrorDialog =new DeleteAlert(); 
    } 
    networkConnectionErrorDialog .show(getSupportFragmentManager(), "networkError"); 
} 

И таким образом вы избежать утечек памяти (потому что это плохо), и вы страхуете вы не имеете [ругательство] статический фрагмент, который не может получить доступ к полям и методам вашей деятельности в , Это хороший способ справиться с этой проблемой, с моей точки зрения.

+0

выглядит неплохо, но он будет получать ошибки во время генерации apk, например, @hakri Reddy, упомянутого ниже. –

+0

Да, но это не потому, что lint недостаточно умен, что нам нужно быть «таким же глупым, как это», утечка memort исчезла, используя эта техника (CanaryLeak покажет вам это) ... Кстати, я сначала запустил свой apk с lint, чтобы обнаружить другие ошибки, которые я мог бы сделать в своем коде, исправить проблему, я думаю, мне нужно, а затем запустить ее с помощью abortOnError false. И в каком-то проекте я настраиваю Lint на это конкретное правило («Внутренний класс должен быть статичным»), –

+0

ohh ... но в моем случае фактическое поколение apk выполняется другой командой, сидящей в офисе в США (я в Индии, и я предоставляю только ссылки репозитория git-кода), потому что они не передают файлы сертификатов подписывания компании никому. Поэтому они определенно не собираются слушать мою причину и не менять свои настройки :( –

3

Если вы хотите получить доступ к членам внешнего класса (активность) и до сих пор не хотят, чтобы члены статичными в деятельности (поскольку фрагмент должен быть публичным статическим), вы можете сделать переопределение onActivityCreated

public static class MyFragment extends ListFragment { 

    private OuterActivityName activity; // outer Activity 

    @Override 
    public void onActivityCreated(Bundle savedInstanceState) { 
     super.onActivityCreated(savedInstanceState); 
     activity = (OuterActivityName) getActivity(); 
     ... 
     activity.member // accessing the members of activity 
     ... 
    } 
-1

добавить аннотацию, прежде чем внутренний класс

@SuppressLint («validFragment»)

Смежные вопросы