import javax.microedition.midlet.*; import javax.microedition.lcdui.*; public class Uppgift1 extends MIDlet implements CommandListener { private Display displayen; private List listan; private TextBox textrutan; private Form formuläret; private StringItem etiketten; private ChoiceGroup valgruppen; private Command exitkommandot, okkommandot, tillbakakommandot; private int antal_kommandon = 0; public Uppgift1() { listan = new List("Välj ett av följande alternativ", Choice.EXCLUSIVE); textrutan = new TextBox("Textrutan", "Antal kommandon: " + antal_kommandon, 100, 0); formuläret = new Form("Formuläret"); exitkommandot = new Command("Avsluta", Command.EXIT, 0); okkommandot = new Command("Välj", Command.OK, 1); tillbakakommandot = new Command("Tillbaka", Command.BACK, 1); listan.append("Fortsätt visa den här listan", null); listan.append("Visa textrutan", null); listan.append("Visa formuläret", null); listan.append("Avsluta", null); listan.addCommand(okkommandot); listan.addCommand(exitkommandot); listan.setCommandListener(this); textrutan.addCommand(exitkommandot); textrutan.addCommand(tillbakakommandot); textrutan.setCommandListener(this); etiketten = new StringItem("Antal kommandon: ", "" + antal_kommandon); etiketten.setLayout(Item.LAYOUT_2); formuläret.append(etiketten); valgruppen = new ChoiceGroup("Välj ett av följande alternativ", ChoiceGroup.EXCLUSIVE); valgruppen.append("Gå tillbaka till listan", null); valgruppen.append("Visa textrutan", null); valgruppen.append("Fortsätt visa det här formuläret", null); valgruppen.append("Avsluta", null); formuläret.append(valgruppen); formuläret.addCommand(exitkommandot); formuläret.addCommand(okkommandot); formuläret.setCommandListener(this); } // Uppgift1 public void startApp() { displayen = Display.getDisplay(this); displayen.setCurrent(listan); } public void commandAction(Command kommandot, Displayable s) { ++antal_kommandon; textrutan.setString("Antal kommandon: " + antal_kommandon); etiketten.setText("" + antal_kommandon); if (kommandot == exitkommandot) { destroyApp(false); notifyDestroyed(); } else if (kommandot == okkommandot && displayen.getCurrent() == listan) { int valet; valet = listan.getSelectedIndex(); if (valet == 0) { displayen.setCurrent(listan); // Onödigt } else if (valet == 1) { displayen.setCurrent(textrutan); } else if (valet == 2) { displayen.setCurrent(formuläret); } else if (valet == 3) { destroyApp(false); notifyDestroyed(); } } else if (kommandot == okkommandot && displayen.getCurrent() == formuläret) { int valet; valet = valgruppen.getSelectedIndex(); if (valet == 0) { displayen.setCurrent(listan); } else if (valet == 1) { displayen.setCurrent(textrutan); } else if (valet == 2) { displayen.setCurrent(formuläret); // Onödigt } else if (valet == 3) { destroyApp(false); notifyDestroyed(); } } else if (kommandot == tillbakakommandot) { displayen.setCurrent(listan); } } // commandAction public void destroyApp(boolean unconditional) { } public void pauseApp() { } } // class Uppgift1
Kommentar: Med den här lösningen gör Tillbaka-kommandot i textrutan att man alltid kommer till den första listan, även om man kom till textrutan från formuläret. Om man vill komma tillbaka till formuläret får man låta programmet hålla reda på var man kom ifrån.
Omstuvning av den kompilerade byte-koden, till exempel genom att byta namn på klasser och metoder, så att den blir svårare att förstå och de-kompilera till källkod. Dessutom blir den kortare, bland annat genom att oanvända klasser och metoder tas bort.
b)
Efter kompileringen. Det är den kompilerade byte-koden som obfuskeras, och innan kompileringen finns den ju inte.
(Man skulle förstås kunna tänka sig att obfuskera källkoden, men det fungerar inte lika bra. Det skulle till exempel inte gå att stoppa in illegal kod för att förvirra de-kompilatorer.)
c)
Före för-verifieringen. För-verifiering kontrollerar den byte-kod som ska skickas till telefonerna, och det är ju den obfuskerade koden som skickas. (Men det kan, kanske, fungera ett obfuskera efter för-verifieringen, med vissa obfuskerare.)
d)
Före det att MIDlet:en skickas till telefonen. Om den o-obfuskerade byte-koden skickades till telefonen, skulle ju telefonens ägare (eller någon som har möjlighet att tjuvlyssna på nätverket) kunna få tillgång till den. Dessutom gör obfuskeringen att byte-koden blir mindre, och man vill inte skicka onödiga data över det dyra och långsamma mobilnätet.
MIDP innehåller gränssnittskomponenter, som Screen och Command, och andra plattformsspecifika klasser, som RecordStore. En klass som inte är meningsfull annat än i mobilsammanhang (antingen på en mobiltelefon eller en liknande apparat, till exempel en personsökare) finns förmodligen i MIDP och inte i CLDC.
Mobiltelefoner (och andra "MID:ar") har dessutom för det mesta en ganska liten skärm, där det skulle vara svårt att få plats med klickbara knappar med text.
(Andra små datorer, till exempel handdatorer med Pocket PC, är tänkta för en mer stillasittande användning, och har större skärm samt en tryckkänslig skärm med pekpenna. Användargränssnittet för dessa har vanliga knappar på skärmen att "klicka" på med pekpennan.)
Lösningen med kommandon ger dessutom flexibilitet till J2ME-implementationen. Olika hårdvara kan ha olika mekanismer för att låta användaren ge kommandon. Om man kör J2ME på hårdvara som faktiskt har mus eller pekskärm, skulle den till och med kunna visa kommandona som knappar som man kan klicka på.
a)
private void spara_antal_kommandon() { try { RecordStore postlagret = RecordStore.openRecordStore("antal-kommandon", true); // Vi skapar aldrig mer än en enda post String strängen = "" + antal_kommandon; byte[] data = strängen.getBytes(); if (postlagret.getNumRecords() == 0) postlagret.addRecord(data, 0, data.length); else postlagret.setRecord(1, data, 0, data.length); postlagret.closeRecordStore(); } catch (RecordStoreException e) { // Ignorera eventuella fel } }
Kommentar: Ovanstående kod kan läcka resurser. Om det skulle uppstå ett fel som kastar ett undantag till exempel i addRecord, kommer closeRecordStore inte att anropas. (Se s 338-339 i kapitel 17 i kursboken.) Nedanstående kod är en säkrare lösning. (Motsvarande modifiering kan också göras i b-uppgiften nedan.)
private void spara_antal_kommandon() { RecordStore postlagret = null; try { postlagret = RecordStore.openRecordStore("antal-kommandon", true); // Vi skapar aldrig mer än en enda post String strängen = "" + antal_kommandon; byte[] data = strängen.getBytes(); if (postlagret.getNumRecords() == 0) postlagret.addRecord(data, 0, data.length); else postlagret.setRecord(1, data, 0, data.length); } catch (RecordStoreException e) { // Ignorera eventuella fel } finally { try { if (postlagret != null) postlagret.closeRecordStore(); } catch (RecordStoreException e) { } } }
b)
Om det misslyckas att läsa posten från postlagret, struntar vi i att sätta variabeln antal_kommandon, och den kommer alltså att behålla sitt ursprungsvärde, 0.
private void hämta_antal_kommandon() { try { RecordStore postlagret = RecordStore.openRecordStore("antal-kommandon", false); byte[] data = postlagret.getRecord(1); String strängen = new String(data); antal_kommandon = Integer.parseInt(strängen); postlagret.closeRecordStore(); } catch (RecordStoreException e) { // Ignorera eventuella fel } }