[ERLEDIGT] Android Studio meint ich soll lokale Variablen verwenden

deka

deka

Dauergast
351
Hallo zusammen,

es geht um eine Warnung in Android Studio, die mich etwas verwirrt.
In meiner Activity erzeuge ich sehr viele TextViews in der onCreate. Alle TextViews habe ich als private Klassenvariablen deklariert. Nun warnt mich Android Studio, dass ich anstatt der Klassenvariablen lokale Variablen verwenden soll.
Das Problem ist aber, dass ich die TextViews nicht nur in der onCreate verwende, sondern in einer weiteren Methode. Das hieße ja, wenn ich alle TextViews als lokale Variablen in der onCreate erzeuge, müsste ich quasi meine zweite Methode mit all den TextViews als Argument aufrufen. Die Lösung finde ich auch nicht so schön.

Code:
public class ActAchievement extends AppCompatActivity {

    private TextView tvAchieve1;
    private TextView tvAchieve2;
    private TextView tvAchieve3;
    private TextView tvAchieve4;
    private TextView tvAchieve5;
    private TextView tvAchieve6;
    private TextView tvAchieveSub2;
    private TextView tvAchieveSub3;
    private TextView tvAchieveSub4;
    private TextView tvAchieveSub5;
    private TextView tvAchieveSub6;
    private TextView tvCurrentRank;
    private TextView tvCurrentQuestions;
    private int correctQuestions;
    private Cursor questionCursor;
    private String currentRank;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.act_achievement);

        tvCurrentRank = (TextView) findViewById(R.id.tv_current_rank);
        tvCurrentQuestions = (TextView) findViewById(R.id.tv_current_questions);
        tvAchieve1 = (TextView) findViewById(R.id.tv_achieve1);
        tvAchieve2 = (TextView) findViewById(R.id.tv_achieve2);
        tvAchieve3 = (TextView) findViewById(R.id.tv_achieve3);
        tvAchieve4 = (TextView) findViewById(R.id.tv_achieve4);
        tvAchieve5 = (TextView) findViewById(R.id.tv_achieve5);
        tvAchieve6 = (TextView) findViewById(R.id.tv_achieve6);
        tvAchieveSub2 = (TextView) findViewById(R.id.tv_achieve_sub2);
        tvAchieveSub3 = (TextView) findViewById(R.id.tv_achieve_sub3);
        tvAchieveSub4 = (TextView) findViewById(R.id.tv_achieve_sub4);
        tvAchieveSub5 = (TextView) findViewById(R.id.tv_achieve_sub5);
        tvAchieveSub6 = (TextView) findViewById(R.id.tv_achieve_sub6);

        currentRank = getResources().getString(R.string.currentRank);

        detectRank();
    }

    public void detectRank() {
        questionCursor = MyDatabase.getInstance(this).getCorrectQuestions();
        correctQuestions = questionCursor.getInt(questionCursor.getColumnIndexOrThrow((MyDatabase.ResultColumns.CORRECT)));

        tvCurrentRank.setText(currentRank + " " + tvAchieve1.getText());
        tvCurrentQuestions.setText(correctQuestions + "/255");

        if (correctQuestions >= 5) {
            setRank(tvAchieve2, tvAchieveSub2);
        }

        if (correctQuestions >= 10) {
            setRank(tvAchieve3, tvAchieveSub3);
        }

        if (correctQuestions >= 150) {
            setRank(tvAchieve4, tvAchieveSub4);
        }

        if (correctQuestions >= 200) {
            setRank(tvAchieve5, tvAchieveSub5);
        }

        if (correctQuestions == 255) {
            setRank(tvAchieve6, tvAchieveSub6);
        }
    }

    public void setRank(TextView tvAchieve, TextView tvAchieveSub) {
        tvCurrentRank.setText(currentRank + " " + tvAchieve.getText());
        tvAchieve.setTextColor(ContextCompat.getColor(this, R.color.colorAccent));
        tvAchieveSub.setTextColor(ContextCompat.getColor(this, R.color.colorAccent));
    }
}


Verstehe ich hier etwas falsch?
 
Zuletzt bearbeitet:
Moin, ich sehe da erstmal keinen Fehler in der Deklaration deiner Variablen. In welcher Methode musst du denn die Objekte benutzen bzw. wo liegt diese Methode? Normalerweise - wenn die Methode in der Klasse steht, in welcher auch deine Variablen deklariert werden - sollte das kein Problem sein.

Wie schaut denn die Meldung die du bekommst genau aus?
 
Ich greife in der Methode detectRank() auf sie zu. Also werden sie nur dort gebraucht. Und in dieser Methode gebe ich sie ja als Argumente für die Methode setRank() mit.

Die Meldung sieht so aus: "Field can be converted to a local variable"
 
Kann auch sein, dass AS rummeckert, da du die Variablen nur in deiner OnCreate (und den Methoden in OnCreate) nutzt und du sie deshalb auch erst im OnCreate deklarieren könntest. Dein Code müsste aber auch so funktionieren, richtig? Glaube, der Hinweis/Warnung ist rein strukturmäßig von AS aus ein "Fehler".

Kannst ja mal testen, ob die Meldung verschwindet, wenn du die angemeckerten Variablen im OnCreate anstatt am Start deiner Klasse deklarierst.

PS: Ups, glaube ich habe deinen Code falsch gelesen.. lass mich nochmal schauen..
 
Zuletzt bearbeitet:
Das habe ich ja bereits in meinem Eingangspost erwähnt: wenn ich alles in der onCreate() deklariere, muss ich ALLE Objekte als Argument für die detectRank() mitgeben. Natürlich verschwinden dadurch die Warnungen, aber ich persönlich finde die Lösung nicht sehr schön. Daher die Frage, ob das auch anders gelöst werden soll oder eben nur mit dieser einen Variante.

Mein Code funktioniert ohne Probleme. Ich möchte es nur ein wenig optimieren :)
 
Hallo Deka,

EDIT : Vergiss was ich vorher geschrieben habe :)

Könntest du bitte mal genau sagen an welcher stelle AS meckert und genau die Beschreibung ?

Ich kann auch auf anhieb nichts ungewöhnliches feststellen :)
 
Zuletzt bearbeitet:
Android Studio bezieht die Warnung "Field can be converted to a local variable" auf alle privaten Klassenvariablen der Activity.
 
Alles klar , jetzt habe ich auch verstanden , was du meinst :)

AS weist dich darauf hin , dass du das "private" weglassen kannst, weil der "private" weitere Resourcen benötigt.
in diesem Falle redundant.

Aus der Praxis : Nett gemeinter hinweis , aber ich deklariere IMMER private oder public.
Alleine aus der Übersicht wegen und mir ist im Prinzip dieser Hinweis schnuppe.
 
Zuletzt bearbeitet:
  • Danke
Reaktionen: lordzwieback und deka
Vielen Dank. Ich mache das auch so, dass ich in der Regel immer private deklariere.
In welchem Zusammenhang verbraucht private weitere Ressourcen?
 
Brich die Erklärung simple herunter : Jede weitere angegebene Eigenschaft einer deklarierten Variable , veranlasst den Compiler,
für diese eine Resource freizustellen ( bzw zu reservieren)

Bsp : Warum "double" , wenn auch "int" reicht.

Redundanz (Informationstheorie) – Wikipedia
Eine Informationseinheit ist dann redundant, wenn sie ohne Informationsverlust weggelassen werden kann
 
  • Danke
Reaktionen: deka
Danke für die Erklärung. Das heißt aber in meinem Fall kann ich die Meldung von Android Studio einfach ignorieren? Oder kann ich hier noch etwas optimieren?
 
Die gelben Hinweise auf der Seite sind Optimierungshilfen, die allerdings nicht immer Sinn machen , weil
diese sich nur zu diesem Zeitpunkt auf das offene File beziehen.

AS weis aber nicht , was du ggf später noch damit vorhast.

Es gibt sogar einige Fälle , bei denen du "rot" angezeigt bekommst , obwohl alles perfekt in Ordnung ist.
Das kann zum Beispiel bei Kompatibiliätsgründen der API Versionen auftreten.
In so einem Falle musst du sogar diese ignorieren,weil sie definitv falsch sind .

Nimm sie als Anhaltspunkt und entscheide aus dem Bauch heraus
 
  • Danke
Reaktionen: deka
Sorry Stefan, ich muss hier mal reingrätschen, weil ein paar Sachen einfach Falsch sind.

Der Fehler "Field can be converted to a local variable" hat nichts mit private zu tun. AS hat nur festgestellt, dass du in deiner onCreate sofort die detectRank aufrufst. Diese verwendet als einzige die deklarierten Felder. D.h. der ganze Block
Code:
       tvCurrentRank = (TextView) findViewById(R.id.tv_current_rank);
       tvCurrentQuestions = (TextView) findViewById(R.id.tv_current_questions);
       tvAchieve1 = (TextView) findViewById(R.id.tv_achieve1);
       tvAchieve2 = (TextView) findViewById(R.id.tv_achieve2);
       tvAchieve3 = (TextView) findViewById(R.id.tv_achieve3);
       tvAchieve4 = (TextView) findViewById(R.id.tv_achieve4);
       tvAchieve5 = (TextView) findViewById(R.id.tv_achieve5);
       tvAchieve6 = (TextView) findViewById(R.id.tv_achieve6);
       tvAchieveSub2 = (TextView) findViewById(R.id.tv_achieve_sub2);
       tvAchieveSub3 = (TextView) findViewById(R.id.tv_achieve_sub3);
       tvAchieveSub4 = (TextView) findViewById(R.id.tv_achieve_sub4);
       tvAchieveSub5 = (TextView) findViewById(R.id.tv_achieve_sub5);
       tvAchieveSub6 = (TextView) findViewById(R.id.tv_achieve_sub6);

       currentRank = getResources().getString(R.string.currentRank);
könnte auch in die detectRank rein und dann können die Variablen local bleiben.
Es kann sogar Sinn machen die findById Aufrufe in die jeweiligen If Statements zu packen, da dann bei der Initialisierung niemals alle Views in der Hierarchie gesucht werden müssten, sondern nur die, die auch wirklich gesetzt werden. Das spart Speicher und vor allem Laufzeit.

AS wird dich nie dazu auffordern ein private wegzulassen. Ein private ist nämlich gerade nicht redundant, da ohne private das Feld package protected wäre und alle Klassen im gleichen Package darauf zugreifen können. AS wird dich höchstens mal hinweisen, dass ein PUBLIC weggelassen werden kann. (Häufig das an der Klasse, wenn die Klasse nur im gleichen Package verwendet wird) Aber IntelliJ bzw. AS fordern normalerweise nicht dazu auf die Sichtbarkeit zu erhöhen.
Ein private benötigt auch keine Ressourcen mehr, da es nur zur Compilezeit verwendet wird, damit der Compiler weiß, welche Klassen was anfassen dürfen und was nicht. Die Information private/protected/static/final... wird für die Laufzeit in EINEM int gespeichert um per Reflection darauf zugreifen zu können. D.h. es ist für den Speicherverbrauch NACH Compilierung egal ob du 0, 1 oder 5 Modifier gesetzt hast.

Last but not least kann man die Warnungen von IntelliJ ignorieren, man sollte sich aber fragen warum. a) hat die Standardeinstellung schon ihren Sinn und b) übersieht man gerne echte Warnungen wenn rechts in der Leiste alles gelb und rot blüht. (API Zugriffe sollten z.B. mit @TargetApi gekennzeichnet werden, dann ist auch der Fehler weg und man weiß später noch, dass man die Methode nur callen sollte wenn man API>x hat)
Wenn man bestimmte Warnungen wirklich nicht sehen will, sollte man sie doch bitte in den Einstellungen unter Inspections ausschalten.
Man behält dann einfach besser den Überblick. (auch ein Ignore an der einen Stelle wo es auftritt finde ich vertretbar)
Ich persönlich versuche einfach meine Dateien komplett "grün" zu halten, was die Warnungen und Fehler betrifft.
Dazu ein Tipp: mit Alt+Enter an der Stelle wo die Warnung auftritt bekommt man verschiedene Möglichkeiten damit umzugehen. Oft bietet AS/IntelliJ einem direkt die Lösung an, alternativ kann man die Warnung ignorieren lassen.

Grüße
David
 
  • Danke
Reaktionen: lordzwieback, DagobertDokate, deka und eine weitere Person
Hallo David,

Sorry Stefan, ich muss hier mal reingrätschen, weil ein paar Sachen einfach Falsch sind.

Keine Ursache , dafür sind wir da :) (Diskussion und Richtigstellung erwünscht ) !

Da du dich offensichtlich tiefer reingekniet hast :

Warum "meckert" dann AS , selbst wenn du die oben als private deklarierten Variablen mehrfach an verschiedenen Stellen
im Source verwendest.

Probiere es mal aus - zumindest meiner tut es mit dem Redundant Hinweis ...
 
Zuletzt bearbeitet:
Wie gewünscht habe ich die Situation oben mal exakt nachgebaut. Code kopiert (bis auf den Cursor) und in AS eingefügt.
Interessanterweise wird bei mir nur correctQuestions angemahnt. Was auch korrekt aussieht:
upload_2017-5-2_23-6-10.png

Der Rest wird nicht angemeckert.
Verwende ich correctQuestions jetzt noch in der onCreate zum Beispiel, dann ist die Meldung weg.
Kann also leider das Verhalten nicht ganz nachvollziehen.

AS Version 2.3.1
Gleiches Verhalten in IntelliJ 2017.1.2

Ansonsten beschwert er sich noch, dass setRank public ist und private sein könnte und dass man in Android keinen Text für setText konkatenieren sollte, sondern Platzhalter verwenden soll.

Kann also leider das ursprüngliche Problem (und damit dein followup) nicht nachstellen mit den aktuellen IDE Versionen.
 
  • Danke
Reaktionen: deka
@deek
Vielen Dank für deine Mühe. Ich schaue es mir morgen noch mal genau an und gebe hier Bescheid welche Felder genau gemeint sind.
 
Boah, Android Studio regt mich manchmal echt auf. Ich habe eben mal nachgeschaut. Auf einmal sind nur die beiden Variablen questionCursor und correctQuestions gemeint. Was auch Sinn macht, da beide nur in einer Methode verwendet werden. Irgendwie scheint es mir so, als würde Android Studio die Warnings ab und zu nicht richtig aktualisieren. Denn vor paar Tagen war wirklich der ganze Block an Variablen gemeint.

Trotzdem danke an euch für die Hilfe.
 
Wenn man sicher ist das AS unrecht hat (oder man sichergehen will das es sich nicht irrt) hilft meistens ein rebuild. Reicht das nicht ist es auch hilfreich den Cach zu invalidieren^^ Unter der aktuellen Beta ist das mein zweitgrößtes Hobby :D

lg.
 
Ja, das Problem, dass ich auch mit Android Studio habe ist, dass die immer auf der aktuellen EAP (Early Access Preview) von IntelliJ aufbauen. In der AS Beta heißt das insbesondere, dass ein Beta Modul auf eine Beta Basis trifft... Das ist etwas viel Beta fürs produktive Arbeiten.
 
Ja aber da Java 9 vor der Tür steht^^ wird es Zeit endlich java 8 in Android zu integrieren :D und das macht das as studio 2.4 wesentlich besser als 2.3. Deswegen benutze ich die 2.4 imo hauptsächlich.
 

Ähnliche Themen

5
Antworten
0
Aufrufe
1.141
586920
5
SaniMatthias
Antworten
19
Aufrufe
940
swa00
swa00
D
Antworten
23
Aufrufe
2.504
Data2006
D
Zurück
Oben Unten