Bonjour,

je rencontre un problème en développant une application chronomètre.

je sais qu'il existe plein d'applications Chronometre/CompteARebours sur le market, mais je voulais m'en créer un :
  • Je ne peux me permettre de me connecter à Internet vu mon forfait téléphonique
  • Cela me permettrait de progresser davantage sur Android


Je ne pense pas que mes fichiers ressources strings.xml et colors.xml vous soient indispensables en revanche :

code de chronometer_view.xml
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
 
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_height="fill_parent"
  android:background="@color/chronometer_view_layout_background"
  android:orientation="vertical" android:layout_width="fill_parent">
<TextView 
android:id="@+id/valeur_chrono"
android:textStyle="bold"
android:typeface="monospace"
android:text="00:00:00"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="50sp"
android:gravity="center_horizontal"
android:background="@color/chronometer_background"
android:textColor="@color/chronometer_foreground"/>
<LinearLayout 
android:layout_width="fill_parent"
android:background="@color/chronometer_layout_background"
android:orientation="horizontal"
android:layout_height="wrap_content" android:gravity="center_vertical|center_horizontal">
<Button 
android:id="@+id/action_chrono" 
android:layout_width="wrap_content" 
android:layout_height="wrap_content"
android:layout_below="@id/valeur_chrono" 
android:text="@string/chronometer_action_text_start"
android:textSize="8sp"/>
<Button 
android:id="@+id/initialisation_chrono" 
android:layout_below="@id/valeur_chrono"
android:layout_toRightOf="@id/action_chrono"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/initialisation_chrono_text"
android:textSize="8sp"/>
<Button 
android:id="@+id/configuration_chrono" 
android:layout_below="@id/valeur_chrono"
android:layout_toRightOf="@id/initialisation_chrono"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/configuration_chrono_text"
android:textSize="8sp"/>
</LinearLayout>
</LinearLayout>
code de ChronometerActivity.java
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
 
package org.isgreat.loloof64.android.chronometer;
 
import java.util.Observable;
import java.util.Observer;
 
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
 
public class ChronometerActivity extends Activity implements OnClickListener, Observer {
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.chronometer_view);
        actionButton = (Button) findViewById(R.id.action_chrono);
        resetButton = (Button) findViewById(R.id.initialisation_chrono);
        setupButton = (Button) findViewById(R.id.configuration_chrono);
        chronometerValue = (TextView) findViewById(R.id.valeur_chrono);
        actionButton.setOnClickListener(this);
        resetButton.setOnClickListener(this);
        setupButton.setOnClickListener(this);
        ////// temporaire /////////////////
        resetButton.setEnabled(false);
        setupButton.setEnabled(false);
        ///////////////////////////////////
    }
 
    @Override
    public void onResume(){
    	super.onResume();
    	chronometerMotor = new ChronometerMotor();
    	chronometerMotor.addObserver(this);
    	chronometer = new Thread(chronometerMotor);
    }
 
	@Override
	public void onClick(final View view) {
		new Thread(){
 
			{
				setDaemon(true);
			}
 
			public void run(){
				if (view == actionButton){
					toggleChronometerState();
				}
				else if (view == resetButton){
					resetChronometer();
				}
				else if (view == setupButton){
					showSetupScreen();
				}
			}
		}.start();
	}
 
	@Override
	public void update(Observable observable, final Object data) {
		runOnUiThread(new Thread(){
			public void run(){
				long totalSeconds = (Long) data;
				String timeString = getTimeString(totalSeconds);
				chronometerValue.setText(timeString);
			}
		});
	}
 
	private String getTimeString(long totalSeconds) {
		int seconds = (int) totalSeconds % 60;
		int minits = (int) (totalSeconds / 60) % 60;
		int hours = (int) totalSeconds / 3600;
		return String.format("%02d:%02d:%02d", hours, minits, seconds);
	}
 
	private void showSetupScreen() {
		throw new UnsupportedOperationException();
	}
 
	private void resetChronometer() {
		throw new UnsupportedOperationException();
	}
 
	private void toggleChronometerState() {
		if ( ! chronometer.isAlive() )
			chronometer.start();
		else
			chronometerMotor.toggleState();
	}
 
	private Button actionButton;
	private Button resetButton;
	private Button setupButton;
	private TextView chronometerValue;
	private ChronometerMotor chronometerMotor;
    private Thread chronometer;
}
code de ChronometerMotor.java
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
 
package org.isgreat.loloof64.android.chronometer;
 
import java.util.Observable;
 
public class ChronometerMotor extends Observable implements Runnable {
 
	public ChronometerMotor(){
	}
 
	@Override
	public void run() {
		while(true){
			try {
				Thread.sleep(1000);
			} catch (InterruptedException exception) {
				exception.printStackTrace();
			}
			/*
			 * Causera certainement des soucis de Garbage Collection
			 * Mais en attedant de trouver mieux ...
			 */
			new UpdateThread().start();
		}
	}
 
	public void toggleState(){
		//Non supporté pour le moment	
	}
 
	private class UpdateThread extends Thread{
 
		public UpdateThread(){
			setDaemon(true);
		}
 
		public void run(){
			totalSeconds++;
			setChanged();
			notifyObservers(totalSeconds);
		}
	}
 
	private long totalSeconds;
}
Le problème c'est que le code s'éxécute normallement sitôt que je le lance depuis Eclipse, et que je clique sur le bouton Demarrer (le bouton actionButton dans l'activité). Mais dès que je quitte l'application avec le bouton Return ou Home, et je clique sur le raccourcis de mon émulateur, j'ai une exception
Affichage du Logcat pour l'exception
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 
11-26 15:42:18.141: ERROR/AndroidRuntime(6113): Uncaught handler: thread main exiting due to uncaught exception
11-26 15:42:18.169: ERROR/AndroidRuntime(6113): java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{org.isgreat.loloof64/org.isgreat.loloof64.ChronometerActivity}: java.lang.ClassNotFoundException: org.isgreat.loloof64.ChronometerActivity in loader dalvik.system.PathClassLoader@437358a0
11-26 15:42:18.169: ERROR/AndroidRuntime(6113):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2194)
11-26 15:42:18.169: ERROR/AndroidRuntime(6113):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2284)
11-26 15:42:18.169: ERROR/AndroidRuntime(6113):     at android.app.ActivityThread.access$1800(ActivityThread.java:112)
11-26 15:42:18.169: ERROR/AndroidRuntime(6113):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1692)
11-26 15:42:18.169: ERROR/AndroidRuntime(6113):     at android.os.Handler.dispatchMessage(Handler.java:99)
11-26 15:42:18.169: ERROR/AndroidRuntime(6113):     at android.os.Looper.loop(Looper.java:123)
11-26 15:42:18.169: ERROR/AndroidRuntime(6113):     at android.app.ActivityThread.main(ActivityThread.java:3948)
11-26 15:42:18.169: ERROR/AndroidRuntime(6113):     at java.lang.reflect.Method.invokeNative(Native Method)
11-26 15:42:18.169: ERROR/AndroidRuntime(6113):     at java.lang.reflect.Method.invoke(Method.java:521)
11-26 15:42:18.169: ERROR/AndroidRuntime(6113):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:782)
11-26 15:42:18.169: ERROR/AndroidRuntime(6113):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:540)
11-26 15:42:18.169: ERROR/AndroidRuntime(6113):     at dalvik.system.NativeStart.main(Native Method)
11-26 15:42:18.169: ERROR/AndroidRuntime(6113): Caused by: java.lang.ClassNotFoundException: org.isgreat.loloof64.ChronometerActivity in loader dalvik.system.PathClassLoader@437358a0
11-26 15:42:18.169: ERROR/AndroidRuntime(6113):     at dalvik.system.PathClassLoader.findClass(PathClassLoader.java:243)
11-26 15:42:18.169: ERROR/AndroidRuntime(6113):     at java.lang.ClassLoader.loadClass(ClassLoader.java:573)
11-26 15:42:18.169: ERROR/AndroidRuntime(6113):     at java.lang.ClassLoader.loadClass(ClassLoader.java:532)
11-26 15:42:18.169: ERROR/AndroidRuntime(6113):     at android.app.Instrumentation.newActivity(Instrumentation.java:1097)
11-26 15:42:18.169: ERROR/AndroidRuntime(6113):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2186)
11-26 15:42:18.169: ERROR/AndroidRuntime(6113):     ... 11 more
J'ai aussi une deuxième question :
Vous avez certainement remarqué le commentaire dans la classe ChronometerMotor, à propos du ramasse-miettes. Je crée un nouveau Thread pour libérer la boucle principale de comptage, et ainsi réaliser les opérations de mise à jour de manière plus efficace. Mais, comment obtenir le même résultat en évitant de libérer l'instance courante de Thread et créer une nouvelle à chaque fois ? J'ai essayé de m'aventurer dans le Thread.wait() et Thread.notify ... sans grand succès.