21 Nov
Ich traf letztens auf einen sehr spezielles Java Problem, bei dem es dann zu Problemen bei der Serialisierung von Objekten kommt.
Ziel sollte es sein eine innere Klasse zu serialisieren während die äußere Klasse eine grafische Oberfläche und mehrere Ereignishandler beinhaltet und den JFileChooser verwendet. Dies schlägt fehl.
Hier der stark vereinfachte, aber trotzdem komplett lauffähige Aufbau eines solchen fehlerhaften Programms: (Video unten)
import java.awt.Color;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
public class GraphEditorGerüst extends JFrame implements MouseListener {
ArrayList<Kreis> kreise = new ArrayList<Kreis>(); // globale Kreisvariable
public GraphEditorGerüst(){
super("GraphEditor");
setSize(150, 100);
addMouseListener(this);
}
public class Kreis implements Serializable {
// innere, zu serialisierende, Klasse
public static final long serialVersionUID = 3L;
public int x, y, dx, dy, id, idcounter = 0;
public Color c = Color.BLACK;
public Kreis(int x, int y, int dx, int dy, Color c) {
this.x = x;
this.y = y;
this.dx = dx;
this.dy = dy;
this.c = c;
this.id = ++idcounter;
}
}
// Ereignishandler mouseClicked
public void mouseClicked(MouseEvent me){
// ein Dummykreis erstellen, der nachher geschrieben wird
kreise.add(new Kreis(0,0,0,0,Color.BLACK));
// neues JFileChooser Objekt
JFileChooser fc = new JFileChooser();
// JFileChooser Dialog anzeigen und Rückgabewert speichern
int fcreturn = fc.showDialog(this, "Speichern");
// wenn auf "Speichern" geklickt wurde
if (fcreturn == JFileChooser.APPROVE_OPTION) {
// ausgewählte Datei in ein File Objekt speichern
File savefile = fc.getSelectedFile();
try {
FileOutputStream fos = new FileOutputStream(savefile);
ObjectOutputStream oos = new ObjectOutputStream(fos);
for (Kreis k : kreise) {
// hier tritt der Fehler auf
oos.writeObject(k);
}
oos.close();
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args){
// Erzeugen eines Objektes von GraphEditorGerüst
GraphEditorGerüst test = new GraphEditorGerüst();
test.setVisible(true);
}
@Override
public void mouseEntered(MouseEvent e) {}
@Override
public void mouseExited(MouseEvent e) {}
@Override
public void mousePressed(MouseEvent e) {}
@Override
public void mouseReleased(MouseEvent e) {}
}
Sieht an sich eigentlich unproblematisch aus.
Die innere Klasse soll serialisiert werden, hat das Interface implementiert und nutzt definitiv nur serialisierbare Datentypen und Elemente. Die äußere Klasse kümmert sich um die GUI und einige Ereignishandler. Einer dieser Handler enthält den JFileChooser zum Auswählen einer Datei, in die ein ObjectOutputStream nun die serialisierte innere Klasse schreiben soll. Es kommt beim Speichern, genauer gesagt bei der Ausführung von oos.writeObject, zu folgendem Fehler:
java.io.NotSerializableException:
javax.swing.plaf.metal.MetalFileChooserUI
alertnativ auch auf javax.swing.plaf.basic.BasicFileChooserUI$AcceptAllFileFilter
mit folgender Stack Trace:
Den Code könnt ihr bequem mit den Links/Rechts Pfeiltasten horizontal bewegen.
java.io.NotSerializableException: javax.swing.plaf.metal.MetalFileChooserUI at java.io.ObjectOutputStream.writeObject0(Unknown Source) at java.io.ObjectOutputStream.defaultWriteFields(Unknown Source) at java.io.ObjectOutputStream.writeSerialData(Unknown Source) at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source) at java.io.ObjectOutputStream.writeObject0(Unknown Source) at java.io.ObjectOutputStream.writeObject(Unknown Source) at javax.swing.event.EventListenerList.writeObject(Unknown Source) at sun.reflect.GeneratedMethodAccessor6.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at java.io.ObjectStreamClass.invokeWriteObject(Unknown Source) at java.io.ObjectOutputStream.writeSerialData(Unknown Source) at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source) at java.io.ObjectOutputStream.writeObject0(Unknown Source) at java.io.ObjectOutputStream.defaultWriteFields(Unknown Source) at java.io.ObjectOutputStream.defaultWriteObject(Unknown Source) at javax.swing.JComponent.writeObject(Unknown Source) at sun.reflect.GeneratedMethodAccessor10.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at java.io.ObjectStreamClass.invokeWriteObject(Unknown Source) at java.io.ObjectOutputStream.writeSerialData(Unknown Source) at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source) at java.io.ObjectOutputStream.writeObject0(Unknown Source) at java.io.ObjectOutputStream.defaultWriteFields(Unknown Source) at java.io.ObjectOutputStream.writeSerialData(Unknown Source) at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source) at java.io.ObjectOutputStream.writeObject0(Unknown Source) at java.io.ObjectOutputStream.defaultWriteFields(Unknown Source) at java.io.ObjectOutputStream.defaultWriteObject(Unknown Source) at java.awt.Window.writeObject(Unknown Source) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at java.io.ObjectStreamClass.invokeWriteObject(Unknown Source) at java.io.ObjectOutputStream.writeSerialData(Unknown Source) at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source) at java.io.ObjectOutputStream.writeObject0(Unknown Source) at java.io.ObjectOutputStream.writeObject(Unknown Source) at java.awt.Window.writeObject(Unknown Source) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at java.io.ObjectStreamClass.invokeWriteObject(Unknown Source) at java.io.ObjectOutputStream.writeSerialData(Unknown Source) at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source) at java.io.ObjectOutputStream.writeObject0(Unknown Source) at java.io.ObjectOutputStream.defaultWriteFields(Unknown Source) at java.io.ObjectOutputStream.writeSerialData(Unknown Source) at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source) at java.io.ObjectOutputStream.writeObject0(Unknown Source) at java.io.ObjectOutputStream.writeObject(Unknown Source) at java.util.ArrayList.writeObject(Unknown Source) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at java.io.ObjectStreamClass.invokeWriteObject(Unknown Source) at java.io.ObjectOutputStream.writeSerialData(Unknown Source) at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source) at java.io.ObjectOutputStream.writeObject0(Unknown Source) at java.io.ObjectOutputStream.writeObject(Unknown Source) at GraphEditor.actionPerformed(GraphEditor.java:191) at javax.swing.AbstractButton.fireActionPerformed(Unknown Source) at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source) at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source) at javax.swing.DefaultButtonModel.setPressed(Unknown Source) at javax.swing.AbstractButton.doClick(Unknown Source) at javax.swing.plaf.basic.BasicMenuItemUI.doClick(Unknown Source) at javax.swing.plaf.basic.BasicMenuItemUI$Handler.mouseReleased(Unknown Source) at java.awt.Component.processMouseEvent(Unknown Source) at javax.swing.JComponent.processMouseEvent(Unknown Source) at java.awt.Component.processEvent(Unknown Source) at java.awt.Container.processEvent(Unknown Source) at java.awt.Component.dispatchEventImpl(Unknown Source) at java.awt.Container.dispatchEventImpl(Unknown Source) at java.awt.Component.dispatchEvent(Unknown Source) at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source) at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source) at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source) at java.awt.Container.dispatchEventImpl(Unknown Source) at java.awt.Window.dispatchEventImpl(Unknown Source) at java.awt.Component.dispatchEvent(Unknown Source) at java.awt.EventQueue.dispatchEventImpl(Unknown Source) at java.awt.EventQueue.access$000(Unknown Source) at java.awt.EventQueue$3.run(Unknown Source) at java.awt.EventQueue$3.run(Unknown Source) at java.security.AccessController.doPrivileged(Native Method) at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source) at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source) at java.awt.EventQueue$4.run(Unknown Source) at java.awt.EventQueue$4.run(Unknown Source) at java.security.AccessController.doPrivileged(Native Method) at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source) at java.awt.EventQueue.dispatchEvent(Unknown Source) at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source) at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source) at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source) at java.awt.EventDispatchThread.pumpEvents(Unknown Source) at java.awt.EventDispatchThread.pumpEvents(Unknown Source) at java.awt.EventDispatchThread.run(Unknown Source)
Hier die Problemanalyse:
Die erste Lösung geht wohl am schnellsten und hilft in den meisten Fällen ohne weitere Probleme zu generieren. Die zwei nächsten Lösungen schränken die Möglichkeiten ein. Die vierte Lösung ist am einfachsten umzusetzen; nur die innere, zu serialisierende, Klasse wird in eine eigene Class Datei ausgelagert und entsprechend verwendet. Die fünfte Lösung lagert zusätzlich noch alle Ereignishandler aus (was sowieso nicht schlecht ist, da es zur logischen Trennung und Übersichtlichkeit beiträgt), kann aber, je nach Größe des Projekts, relativ aufwändig werden.
Hier der Code eines komplett lauffähigen Programms, bei der die zu serialisierende Klasse ausgelagert wurde:
GraphEditor.java:
Den Code könnt ihr bequem mit den Links/Rechts Pfeiltasten horizontal bewegen.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class GraphEditor extends JFrame implements MouseListener, MouseMotionListener, ActionListener {
//Globale Variablen
JTextField txt = new JTextField(10);
JPanel panel1 = new JPanel();
//Globale Variablen
Kreis gezogenerKreis = null; //Kreis-Objekt zum Zwischenspeichern des bewegten Kreises
//Kreisdurschnitt
final int dX = 16, dY = 16;
//Erstkonfiguration und Koordinatenkorrekturen
int korrekturX = 0, korrekturY = 0;
int korrekturX1 = 0, korrekturY1 = 0, korrekturX2 = 0, korrekturY2 = 0, configclickcounter = 1;
boolean configured = false;
//ArrayList zum Aufnehmen der Kreis-Objekte
ArrayList<Kreis> kreise = new ArrayList<Kreis>();
//Konstruktor der Klasse GraphEditor
public GraphEditor(){
//Aufruf des Konstruktors der Oberklasse
super("GraphEditor");
//Schließen des Fensters
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
//Menüleiste
JMenuBar mbar = new JMenuBar();
this.setJMenuBar(mbar);
//Menü
JMenu mGraph = new JMenu("Graph");
JMenu mVertex = new JMenu("Vertex");
//Menüelemente
JMenuItem mGraphOpen = new JMenuItem("open");
JMenuItem mGraphNew = new JMenuItem("new");
JMenuItem mGraphSave = new JMenuItem("save");
JMenuItem mVertexInsert = new JMenuItem("insert");
JMenuItem mVertexMove = new JMenuItem("move");
JMenuItem mVertexDelete = new JMenuItem("delete");
JMenuItem mVertexNone = new JMenuItem("none");
//Hinzufügen der Elemente zur Menüleiste
mGraph.add(mGraphNew);
mGraph.add(mGraphOpen);
mGraph.add(mGraphSave);
mVertex.add(mVertexInsert);
mVertex.add(mVertexMove);
mVertex.add(mVertexDelete);
mVertex.add(mVertexNone);
mbar.add(mGraph);
mbar.add(mVertex);
//Haupt-Panel
this.setLayout(new BorderLayout());
panel1.setBackground(Color.white);
this.add(panel1, BorderLayout.CENTER);
panel1.addMouseListener(this);
panel1.addMouseMotionListener(this);
//unteres Panel für Label und Textfeld
JPanel panel2 = new JPanel();
this.add(panel2, BorderLayout.SOUTH);
//Label
JLabel label = new JLabel("Selected Action", JLabel.RIGHT);
panel2.setLayout(new GridLayout(1,2,5,0));
panel2.add(label);
//Panel für Textfeld
JPanel panel3 = new JPanel();
panel3.setLayout(new FlowLayout(3));
panel2.add(panel3);
//Textfeld
txt.setText("none");
txt.setDisabledTextColor(Color.black);
txt.setEnabled(false);
panel3.add(txt);
//ActionListener
mVertex.addActionListener(this); // vertex:
mVertexInsert.addActionListener(this);
mVertexDelete.addActionListener(this);
mVertexMove.addActionListener(this);
mVertexNone.addActionListener(this);
mGraph.addActionListener(this); // graph:
mGraphNew.addActionListener(this);
mGraphOpen.addActionListener(this);
mGraphSave.addActionListener(this);
//ActionCommands
mVertex.setActionCommand("vertex"); // vertex:
mVertexInsert.setActionCommand("insert");
mVertexDelete.setActionCommand("delete");
mVertexMove.setActionCommand("move");
mVertexNone.setActionCommand("none");
mGraph.setActionCommand("graph"); // graph:
mGraphNew.setActionCommand("new");
mGraphOpen.setActionCommand("open");
mGraphSave.setActionCommand("save");
}
//Überschriebene Paint-Methode zum Zeichnen der Kreise
public void paint(Graphics g) {
super.paint(g);
for (Kreis k: kreise) {
g.setColor(k.c);
g.fillOval(k.x - k.dx/2 + korrekturX, k.y - k.dy/2 + korrekturY, k.dx, k.dy);
}
}
//ActionEvent
public void actionPerformed(ActionEvent ae){
//Zwischenspeichern
String action = ae.getActionCommand();
//werden unabhängig von der Erstkonfiguration immer ausgeführt
if(action == "delete"){
txt.setText("Vertex: delete");
}
if(action == "move"){
txt.setText("Vertex: move");
}
if(action == "none"){
txt.setText("none");
}
//wenn insert zum ersten Mal ausgewählt wird: Infofenster anzeigen
if (configured == false && action == "insert") {
int wahl = JOptionPane.showConfirmDialog(this, "" +
"Bei der ersten Verwendung des Programms muss eine Konfiguration erfolgen. \n" +
"Bitte klicken Sie an eine Stelle mittig im Zeichenbereich und danach\n " +
"exakt in den weißen Punkt in der Mitte des gezeichneten Kreises!",
"Konfiguration nötig!", JOptionPane.OK_CANCEL_OPTION);
if (wahl == JOptionPane.OK_OPTION) {
txt.setText("Vertex: insert");
}
}
//wenn Erstkonfiguration schon abgeschlossen ist
else if (configured == true) {
if(action == "insert"){
txt.setText("Vertex: insert");
}
}
//Löschen der Kreise aus der Liste
if(action == "new") {
kreise.clear();
repaint();
}
//Speichern der gezeichneten Kreise
if(action == "save") {
JFileChooser fc = new JFileChooser();
int fcreturn = fc.showDialog(this, "Speichern");
if (fcreturn == JFileChooser.APPROVE_OPTION) {
File savefile = fc.getSelectedFile();
try {
FileOutputStream fos = new FileOutputStream(savefile);
ObjectOutputStream oos = new ObjectOutputStream(fos);
for (Kreis k : kreise) {
oos.writeObject(k);
}
oos.close();
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
//Öffnen einer bereits gespeicherten Datei mit Punkten
if(action == "open") {
kreise.clear();
JFileChooser fc = new JFileChooser();
int fcreturn = fc.showDialog(this, "Öffnen");
if (fcreturn == JFileChooser.APPROVE_OPTION) {
File openfile = fc.getSelectedFile();
fc = null;
FileInputStream fis;
ObjectInputStream ois;
try {
fis = new FileInputStream(openfile);
ois = new ObjectInputStream(fis);
try {
while (true) {
kreise.add((Kreis) ois.readObject());
repaint();
}
}
catch (EOFException e) {
System.out.println("Dateiende erreicht");
ois.close();
fis.close();
}
}
catch (IOException e) {
e.printStackTrace();
}
catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
repaint();
}
//MouseEvents
public void mouseClicked(MouseEvent me){
repaint();
int cursorX = me.getX();
int cursorY = me.getY();
//Erstkonfiguration der Koordinatenkorrektur starten, wenn noch nicht erfolgt
if (configured == false) {
//erster Klick der Erstkonfiguration, Textfeld mit "Vertex: insert" nur, wenn der Benutzer im Dialog "OK" geklickt hat
if (configclickcounter == 1 && txt.getText().equals("Vertex: insert")) {
//erstes Koordinatenpaar lesen
korrekturX1 = me.getX();
korrekturY1 = me.getY();
configclickcounter++;
/* Kreis mit den unkonfigurierten Standardkoordinaten zeichnen;
* dieser wird links oberhalb des Klickpunktes erscheinen, je nach Betriebssystem(/-version)
* und Windows-Design unterschiedlich!
* Ein kleiner weißer Kreis symbolisiert die Mitte, die angeklickt werden muss. */
kreise.add(new Kreis(cursorX, cursorY, dX, dY));
kreise.add(new Kreis(cursorX, cursorY, 2, 2, Color.WHITE));
//zweiter Klick der Erstkonfiguration
} else if (configclickcounter == 2 && txt.getText().equals("Vertex: insert")) {
//Konfigurationskreise löschen
kreise.clear();
repaint();
//zweites Koordinatenpaar lesen
korrekturX2 = me.getX();
korrekturY2 = me.getY();
//Korrekturen errechnen
korrekturX = korrekturX1 - korrekturX2;
korrekturY = korrekturY1 - korrekturY2;
//Konfiguration beenden, Infodialog anzeigen
configured = true;
JOptionPane.showConfirmDialog(this, "" +
"Die Konfiguration ist abgeschlossen. \n" +
"Koordinatenkorrektur für X: " + korrekturX + " | Y: " + korrekturY,
"Konfiguration abgschlossen!", JOptionPane.OK_CANCEL_OPTION);
}
}
//Erstkonfiguration beendet
else if (configured == true){
if(txt.getText().equals("Vertex: insert")){
kreise.add(new Kreis(cursorX, cursorY, dX, dY));
}
}
if(txt.getText().equals("Vertex: delete")){
for(Kreis k : kreise){
if(cursorX <= (k.x + dX/2.0) && cursorX >= (k.x - dX/2.0) && cursorY <= (k.y + dY/2.0) && cursorY >= (k.y - dY/2.0)){
kreise.remove(k);
break;
}
}
}
}
public void mousePressed(MouseEvent me){
if(txt.getText().equals("Vertex: move")){
int cursorX = me.getX();
int cursorY = me.getY();
for(Kreis k : kreise){
if(cursorX <= (k.x + dX/2.0) && cursorX >= (k.x - dX/2.0) && cursorY <= (k.y + dY/2.0) && cursorY >= (k.y - dY/2.0)){
gezogenerKreis = k;
}
}
}
}
public void mouseEntered(MouseEvent arg0){}
public void mouseExited(MouseEvent arg0){}
public void mouseReleased(MouseEvent arg0){
gezogenerKreis = null;
}
//MouseMotionEvents
public void mouseDragged(MouseEvent me) {
if(txt.getText().equals("Vertex: move")){
if(gezogenerKreis != null) {
gezogenerKreis.x = me.getX();
gezogenerKreis.y = me.getY();
repaint();
}
}
}
public void mouseMoved(MouseEvent e){}
//Main-Methode
public static void main(String[] args){
//GraphEditor-Objekt
GraphEditor ui = new GraphEditor();
//Setzen der Größe und Sichtbarkeit des GraphEditor-Objekts
ui.setSize(550, 350);
ui.setVisible(true);
}
}
Kreis.java:
Den Code könnt ihr bequem mit den Links/Rechts Pfeiltasten horizontal bewegen.
import java.awt.Color;
import java.io.Serializable;
public class Kreis implements Serializable {
//Innere Kreis-Klasse
public static final long serialVersionUID = 3L;
public int x, y, dx, dy, id;
public Color c = Color.BLACK;
int counter = 0;
// 2 verschieden überladene Konstruktoren ermöglichen die freie Farbwahl für jeden einzelnen Kreis
public Kreis(int x, int y, int dx, int dy, Color c) {
this.x = x;
this.y = y;
this.dx = dx;
this.dy = dy;
this.c = c;
this.id = ++counter;
}
public Kreis(int x, int y, int dx, int dy) {
this.x = x;
this.y = y;
this.dx = dx;
this.dy = dy;
}
@Override
public String toString() {
return "x:"+ x + " y:" + y + " dx:" + dx + " dy:" + dy + " color:" + c.toString()+ "\n";
}
}
16 Nov

Kurz zur Einleitung, weil das mein erster Post zu diesem Thema ist:
Monkeyrunner ist ein Tool des Android SDK, welches als Schnittstelle zu dem angeschlossenen Android Smartphone fungiert. Es bietet eine, mit Python Scripten angesprochene, Fernsteuerung des Geräts mit einigen Features. So lassen sich lange Interaktionsfolgen automatisiert verscripten und “unbemannt” ausführen.
Also, Screen Lock & Unlock mit Monkeyrunner:
# simulierter Druck auf den Power Knopf -> Screen aus
device.press("POWER", MonkeyDevice.DOWN_AND_UP)
# eigene Methode zum Aufwecken des Bildschirms
# nochmal ein Druck auf POWER funktioniert nicht
device.wake()
# 1 Sekunde warten
MonkeyRunner.sleep(1)
# Ziehen des Entsperr-Rings (vom unteren Rand in die Mitte (HTC One X))
device.drag((354, 1191), (324, 525), 1, 10)
# entsperrt!
Der Sperrbildschirm ist in diesem Beispiel nur mit dem üblichen Entsperr-Ring von HTC versehen, kein Muster oder PIN.
Die einzige Hürde ist also das Festlegen der Koordinaten beim Entsperren. Dazu später noch ein weiteres Tutorial.
A short introduction first because this is the first post on this topic:
Monkeyrunner is a tool which provides an interface between your computer and the connected Android smartphone. It is controlled by Python scripts and is capable of serving as a Android device remote control with functions like sending various inputs, running apps, taking and saving screenshots from the device. This way you can automatize complex interactions and inputs and run them unattended.
How to lock & unlock the Android device with Monkeyrunner:
# simulate a POWER button press -> turn off the screen
device.press("POWER", MonkeyDevice.DOWN_AND_UP)
# monkeyrunner method to unlock the screen
# POWER button press doesn't work to turn on
device.wake()
# wait a second
MonkeyRunner.sleep(1)
# drag the unlock-ring (from bottom to center (HTC One X))
device.drag((354, 1191), (324, 525), 1, 10)
# unlocked!
This example shows that the lock screen is just secured by the HTC unlock-ring, which must simply be dragged from the bottom to the center. No lock pattern or PIN here (which is much easier to solve with monkeyrunner).
So there’s just one small thing to consider: defining the coordinates of your unlock … thingy’s start end end position. Doing so depends on the device and Android version your using. There will be a tutorial here about that very soon.
19 Oct
Ich brauche die UNIX Shell auf meinem Windows System. Und es war erstaunlich einfach.
Cygwin heißt die sich praktisch von selbst installierende Software, die das komplette Linux Look & Feel mitbringt. Zusätzlich ermöglicht es der Installer auf einfachsten Wege hunderte Linux Pakete gleich mitzuinstallieren.

Nach der Installation liegt auf dem Desktop (wenn nicht während der Installation deaktiviert) eine Verknüpfung zum Cygwin Terminal, welcher einer Linux Umgebung nachempfunden ist. Von dort kann man wie auch im Linux Terminal Befehle absetzen, Programme starten, ausführen usw.

Es wird auch direkt eine Linux-ähnliche Ordnerstruktur erstellt und eingegebene Pfade entsprachend übersetzt, sodass auch das Dateisystem Linux-like anmutet:
MS-DOS style path detected: p:/Cygwin/home/Hannes/prog.sh
Preferred POSIX equivalent is: /home/Hannes/prog.sh
Die Ausgaben des Cygwin Terminals entsprechend nicht 1:1 den Ausgaben von Linux Distributionen, man sollte den dort entwickelten Code also nicht ohne Weiteres in Linuxumgebungen übernehmen.
Aber für erste Tests (und die 2 Linux Bash Vorlesungen meines Studiums) reicht es.
18 Sep
Ich musste heute via Batch 2 Firefox Versionsnummern vergleichen und die kleinere Version erkennen. Ist auf den ersten Moment nicht so einfach wie es klingt aber machbar.
Die Versionsnummern bei Firefox bestehen aus maximal 3 Zahlen (seit Version 3), getrennt durch 2 Punkte; ggf. weniger.
Firefox 3.6.28
Firefox 15.0
Ein direkter Vergleich mit Batch ist aufgrund der Punkte nicht möglich; einfach nur die Punkte entfernen würde aber false positives ermöglichen:
3.6.28 = 3628
15.0 = 150
3.6.28 >(!) 15.0
Meine Lösung ist nicht sonderlich schön aber sie funktioniert:
@echo off
setlocal
set alt=9.6.28
set neu=10.0
for /f "tokens=1,2,3 delims=. " %%a in ("%alt%") do set alt1=%%a&set alt2=%%b&set alt3=%%c
for /f "tokens=1,2,3 delims=. " %%a in ("%neu%") do set neu1=%%a&set neu2=%%b&set neu3=%%c
set /a encalt=alt1*1000000+alt2*1000+alt3
set /a encneu=neu1*1000000+neu2*1000+neu3
echo %encalt%
echo %encneu%
echo %encalt% kleiner als %encneu% ?
if %encalt% LSS %encneu% echo JA
endlocal
Es werden Versionsnummern mit bis zu 3 Zahlen verarbeitet; weniger Zahlen geht immer, für mehr Zahlen einfach nur den for Befehl erweitern. Für jede Version wird ein numerischer Wert errechnet und dieser dann verglichen.
Hier ein Test, bei dem ich erst 9.6.28 mit 10.0 vergleiche und dann 9.999.999 mit 10.0. In beiden Fällen wir korrekt ausgegeben, dass 10.0 größer ist. Für größere Versionsnummer in den einzelnen Parts die Multiplikation anpassen.

Fragen, Anregungen, Erweiterungen; Kommentare sind willkommen
12 Sep
Heute in der Firma zusammengetippt, getestet, läuft bisher an 2 Test-PCs und wird in naher Zukunft verteilt.
Für alle, die es interessiert und natürlich für hilfreiches Feedback, was man verbessern könnte.
Mein .NET Framework 4.5 Deployment Batch Script:
Color 9f
@echo on
title Installiere .NET Framework 4.5...
setlocal
:: dotnet45-installer.bat
:: Installiert .NET Framework 4.5 silent mit Logging
:: Hannes Schurig - 11.09.2012
:: Letzte Änderung - Version 0.4 am 11.09.2012
set wd=\\server\dotNET_Framework\45
set log=\\server\dotNET_Framework\45\log.txt
:checkversion
REM prüfe ob .NET 4.5 schon installiert ist
for /f "tokens=1,2,3 delims= " %%a in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Client" /v "Version"^|findstr "Version"') do set netversion=%%c
if not %netversion:~0,3%==4.5 goto install
echo %date% %time:~0,5% - %computername% hat schon >> %log%
goto end
:install
%wd%\dotnetfx45_full_x86_x64.exe /passive /log "%wd%\logs\%computername%.txt" /norestart
set netinstallerrlvl=%errorlevel%
if not %netinstallerrlvl%==0 goto fehler
:checkinstallation
REM überprüfe ob die Installation erfolgreich war
for /f "tokens=1,2,3 delims= " %%a in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Client" /v "Version"^|findstr "Version"') do set newnetversion=%%c
if not %newnetversion:~0,3%==4.5 set netinstallerrlvl=99 & goto fehler
if %newnetversion:~0,3%==4.5 echo %date% %time:~0,5% - %computername% hat erfolgreich Version %netversion% installiert >> %log%
goto end
:fehler
REM Return Codes: http://msdn.microsoft.com/en-us/library/ee942965.aspx
if %netinstallerrlvl%==1602 echo echo %date% %time:~0,5% - %computername% FEHLER: Benutzer hat die Installation abgebrochen >> %log%
if %netinstallerrlvl%==1603 echo echo %date% %time:~0,5% - %computername% FEHLER: Ein fataler Installationsfehler ist aufgetreten >> %log%
if %netinstallerrlvl%==1641 echo echo %date% %time:~0,5% - %computername% HINWEIS: Installation erfolgreich aber Neustart notwendig >> %log%
if %netinstallerrlvl%==3010 echo echo %date% %time:~0,5% - %computername% HINWEIS: Installation erfolgreich aber Neustart notwendig >> %log%
if %netinstallerrlvl%==5100 echo echo %date% %time:~0,5% - %computername% FEHLER: Das System entspricht nicht den Anforderungen >> %log%
if %netinstallerrlvl%==99 echo echo %date% %time:~0,5% - %computername% FEHLER: Installationserrorlevel 0 aber erneute Registryprüfung gibt nicht Version 4.5 zurück. Ungewöhnlicher Fall, Überprüfung notwendig >> %log%
goto end
:end
endlocal
Ausgabe in der log.txt ist dann sowas:
11.09.2012 16:40 – IQB177 hat erfolgreich Version 4.5.50709 installiert
Ich benutze momentan noch den Parameter /passive, welcher zwar eine GUI anzeigt aber keine Nutzerinteraktion zulässt. Dadurch erfährt aber der Benutzer zumindest den aktuellen Status und Fortschritt der Installation. Vielleicht wechsel ich noch zu /q, wo keinerlei GUI zu sehen ist, absolut unsichtbar.
via: .NET Versionsnummern, .NET Framework 4 Deployment, .NET Framework 4 Allgemein
Momentan sind noch keine Einstellungen möglich.
Es wird bald möglich sein, auf Wunsch html7bar komplett zu deaktivieren.
Kommentare