Android: Why do I have to use runOnUIThread() Here? - android

I am working on a simple game with graphics using Open GL. I want it so that when a certain event happens, the player gets a point, and this updates a textview overlaying the GLSurfaceView on the game activity.
Thanks in advance!
Here are the relevant parts of my code:
The Activity:
public class playActivity extends Activity {
private GLSurfaceView myGLView;
private Handler mGameHandler;
private TextView mScoreView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mScoreView = new TextView(this);
mScoreView.setTextColor(0xFF00FF00); // Green
string myZeroString = "0";
mScoreView.setText(myZeroString); //Starting score will always be 0
mGameHandler = new Handler(){
public void handleMessage(Message msg){
if (msg.what == MyGLRenderer.GAME_SCORE_FLAG) {
int score = msg.arg1;
mScoreView.setText(Integer.toString(score));
}
}
};
myGLView = new MyGLSurfaceView(this);
setContentView(myGLView);
addContentView(mScoreView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
}
private void updateScoreBoard(int score){
mScoreView.setText(Integer.toString(score));
}
class MyGLSurfaceView extends GLSurfaceView{
private final MyGLRenderer myRenderer;
public MyGLSurfaceView(Context context){
super(context);
// Create an OpenGL ES 2.0 context
setEGLContextClientVersion(2);
myRenderer = new MyGLRenderer(context);
myRenderer.initiateHandler(mGameHandler);
// Set the Renderer for drawing on the GLSurfaceView
setRenderer(myRenderer);
setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
}
}
}
And the GlSurfaceView.Renderer:
public class MyGLRenderer implements GLSurfaceView.Renderer{
public static final int GAME_SCORE_FLAG = 1;
private Handler mGameHandler = null;
private int mScore = 0;
public void initiateHandler(Handler handler){
mGameHandler = handler;
}
...
#Override
public void onDrawFrame(GL10 unused){
if (scoreCondition){
mScore += 1;
if (mGameHandler != null) {
int flag = MyGLRenderer.GAME_SCORE_FLAG;
mGameHandler.dispatchMessage(Message.obtain(mGameHandler, flag, 2));
}
}
}
}
When I run this, it gives me the error:
"android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views."
However, in the top chunk of code, when I replace
int score = msg.arg1;
mScoreView.setText(Integer.toString(score));
with
final int score = 3;
runOnUiThread(new Runnable() {
#Override
public void run() {
mScoreView.setText(Integer.toString(score));
}
});
I no longer get the error and it works. I thought that in the first construction that when I called setText that it would have to be performed on the UI thread, but Android Studio thinks differently. Can anyone explain this to me? Thanks!

Change:
mGameHandler.dispatchMessage(Message.obtain(mGameHandler, flag, 2));
to:
mGameHandler.sendMessage(Message.obtain(mGameHandler, flag, 2));
Calling dispatchMessage() will cause the message to be delivered on the current thread (which is the GL thread in your case.)

Related

Chaining a sequence of actions when communicating in a UI thread

I am designing a small application which changes a TextView every second with a random letter.
For now, my code looks like this and works very well :
#BindView(R.id.textview_letter) TextView letterTV;
private static final int DELAY_MILLIS = 1000;
private static final int LOOP_MAX = 10;
private static final String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private Handler h = new Handler();
private Random random = new Random();
private int position = 0;
private Runnable run = new Runnable() {
#Override
public void run() {
if (position >= LOOP_MAX) {
letterTV.setText("THE END !");
h.removeCallbacks(run);
} else {
String randomLetter = String.valueOf(ALPHABET.charAt(random.nextInt(ALPHABET.length())));
letterTV.setText(randomLetter);
h.postDelayed(run, DELAY_MILLIS);
position++;
}
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mylayout);
ButterKnife.bind(this);
h.post(run);
}
I now want to improve it by setting the background of my TextView to black (or make it invisible) for few ms between each letter, so the user is notified in case there is twice the same letter in a row.
My only noob thought was to put another Runnable in my Runnable and handle them with split delay (like 700 and 300 ms for instance), but this seems hugely overcomplicated.
What is the right way to do this ?
(and BTW, is the Runnable/Handler really the most adapted pattern for me to use ?)
EDIT : In a non-UI thread I could do domething like this :
while (randomLetter.next()) {
letterTV.setText(randomLetter);
Thread.sleep(700);
letterTV.setBackgroundColor(Color.BLACK);
Thread.sleep(300);
}
Use RxAndroid, it will reduce the complexity:
public class Test extends AppCompatActivity {
private Disposable randomLetterObservable;
private TextView textView;
private Random random = new Random();
private final String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.textView);
randomLetterObservable = Observable.interval(500L, TimeUnit.MILLISECONDS)
.timeInterval()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Timed<Long>>() {
#Override
public void accept(Timed<Long> longTimed) throws Exception {
String randomLetter = String.valueOf(ALPHABET.charAt(random.nextInt(ALPHABET.length())));
textView.setText(randomLetter);
}
});
}
#Override
protected void onDestroy() {
super.onDestroy();
if (randomLetterObservable != null) {
randomLetterObservable.dispose();
}
}
}
with dependencies:
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
implementation 'io.reactivex.rxjava2:rxjava:2.1.8'
Edit: Here is a modified solution that also changes the background if the same letter occurs twice in a row.
public class Test extends AppCompatActivity {
private Disposable randomLetterObservable;
private TextView textView;
private Random random = new Random();
private final String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private String lastLetter;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.textView);
randomLetterObservable = Observable.interval(700L, TimeUnit.MILLISECONDS)
.timeInterval()
.observeOn(AndroidSchedulers.mainThread())
.switchMap(new Function<Timed<Long>, ObservableSource<String>>() {
#Override
public ObservableSource<String> apply(Timed<Long> longTimed) throws Exception {
String randomLetter = String.valueOf(ALPHABET.charAt(random.nextInt(ALPHABET.length())));
if (randomLetter.equals(lastLetter)) {
textView.setBackgroundColor(Color.BLACK);
return Observable.just(randomLetter)
.observeOn(Schedulers.computation())
.debounce(300L, TimeUnit.MILLISECONDS);
} else {
lastLetter = randomLetter;
return Observable.just(randomLetter);
}
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<String>() {
#Override
public void accept(String s) throws Exception {
textView.setText(s);
textView.setBackgroundColor(Color.TRANSPARENT);
}
});
}
#Override
protected void onDestroy() {
super.onDestroy();
if (randomLetterObservable != null) {
randomLetterObservable.dispose();
}
}
}
Events are emitted in a background thread, background color and letters are set on the main thread. I think this is pretty much what you asked for. (You might need to play with the values to get the desired effect).
Good luck.

How to restart screen in LibGDX using Init() methods?

I created a simple game in LibGDX that has multiple screens. I want to restart a certain screen after touching a restart button but I'm not sure how to do that. I made some research about this and all answers lead to not loading my assets in show() but rather in an init() method that I am not really familiar with. I want to know how can I restart a screen using this init() method. As of now I put most of my initialization in the constructor and some were initialized in methods such as the restartButton() method. Any other corrections or improvements in my code will be highly appreciated. Here is a snippet of my code:
public class LevelOneScreen implements Screen {
public MainShooter app;
private Stage stage;
private Stage stageCoin;
private Stage stageScore;
private Stage stageEnemies;
private Stage stageFX;
private Stage stageButton;
public Image aImage;
public Image bImage;
public Image cImage;
public Image dImage;
public Array<AntAnimation> AntAnimate;
public Array<CrumbAnimation> CrumbAnimate;
public Array<CoinAnimation> CoinAnimate;
public Texture firstTex;
public Texture secTex;
public Texture thirdTex;
public Texture fourthTex;
public LevelOneScreen(final MainShooter app){
this.app = app;
this.stage = new Stage(new StretchViewport(app.screenWidth, app.screenHeight, app.camera));
this.stageCoin = new Stage(new StretchViewport(app.screenWidth, app.screenHeight, app.camera));
this.stageScore = new Stage(new StretchViewport(app.screenWidth, app.screenHeight, app.camera));
this.stageEnemies = new Stage(new StretchViewport(app.screenWidth, app.screenHeight, app.camera));
this.stageFX = new Stage(new StretchViewport(app.screenWidth, app.screenHeight, app.camera));
this.stageButton = new Stage(new StretchViewport(app.screenWidth, app.screenHeight, app.camera));
AntAnimate = new Array<AntAnimation>();
CrumbAnimate = new Array<CrumbAnimation>();
CoinAnimate = new Array<CoinAnimation>();
restartButton();
levelOneBackground();
coinScore();
}
public void show() {
inputMultiplexer = new InputMultiplexer();
inputMultiplexer.addProcessor(stageCoin);
inputMultiplexer.addProcessor(stageScore);
inputMultiplexer.addProcessor(stageEnemies);
inputMultiplexer.addProcessor(stageFX);
inputMultiplexer.addProcessor(stageButton);
Gdx.input.setInputProcessor(inputMultiplexer);
}
public void levelOneBackGround(){
//code for the background
}
public void coinScore(){
//code for coinScore
}
public void restartButton(){
firstTex = MainShooter.manager.get("button.png", Texture.class);
aImage = new Image(firstTex);
stageButton.addActor(aImage);
aImage.addListener(new ActorGestureListener() {
public void touchDown(InputEvent event, float x, float y, int pointer, int button) {
// Creating new LevelOneScreen somehow affects the fps of my game.
app.setScreen(new LevelOneScreen(app));
}});
}
//some other codes like dispose, hide, etc.
}
This edit is a response to an answer provided by IronMonkey which seems to solve the problem but needs additional code. The problem here is that whenever the game is restarted, the actors from the restarted screen is on top of the actors on the previous screen(the screen before restarting). Here is a new snippet of my code showing how to restart the game using my GameInit():
public void GameInit(){
levelOneBackground();
scoreInt = 0;
coinInt = 0;
}
public void levelOneBackground(){
Runnable runAntAct1 = new Runnable(){
public void run(){
antAct1();
}
};
Runnable runAntAct2= new Runnable(){
public void run(){
antAct2();
}
};
Runnable runAntAct3 = new Runnable(){
public void run(){
antAct3();
}
};
Runnable runCoins = new Runnable(){
public void run(){
coinScattered();
}
};
levelOneTexture = CoinAssets.manager.get("woodenTable.png",Texture.class);
levelOneImage = new Image(levelOneTexture);
levelOneImage.setSize(app.screenWidth,app.screenHeight);
levelOneImage.setPosition(0,0);
stage.addActor(levelOneImage);
levelOneImage.addAction(sequence(run(runAct1),delay(2f),run(runAct2),delay(2f),run(runAct3),delay(2f),run(runCoins)));
}
public void restartButton(){
//some code for the position, size, texture of the button.
restartButton.addListener(new ActorGestureListener() {
public void touchDown(InputEvent event, float x, float y, int pointer, int button) {
GameInit();}});
}
public void antAct1(){
for(int i = 1; i < 4; i++){AntAnimate.add(new AntAnimation(app));}
AntAnimate.get(1).setPosition(x,y);
stageEnemies.addActor.(AntAnimate.get(1));
AntAnimate.get(1).addAction(//some actions);
AntAnimate.get(2).setPosition(x,y);
stageEnemies.addActor.(AntAnimate.get(2));
AntAnimate.get(2).addAction(//some actions);
AntAnimate.get(3).setPosition(x,y);
stageEnemies.addActor.(AntAnimate.get(3));
AntAnimate.get(3).addAction(//some actions);
}
public void antAct2(){
//almost the same with antAct1()
}
public void antAct3(){
//almost the same with antAct1()
}
public void coinScattered(){
//almost the same with antAct1()
}
show() is called automatically when the screen is first shown.
The init() method is one you create yourself.
You can call it whatever you want.
You should not load all the assets in init() just set everything up, score = 0, player.setPosition(0,0) etc.
So when you first call init() you set all variables. When you call init() again you set them again. (reset)
Efter edit of question:
In the methods called from GameInit() you load files that are already loaded and add actors to stages where those actors are already there and therefore now overlapping. You also add actions and create objects already created.
All GameInit() should do is set values for those already created objects.
So basically:
private void GameInit(){
AntAnimate.get(1).setPosition(x,y);
AntAnimate.get(2).setPosition(x,y);
AntAnimate.get(3).setPosition(x,y);
levelOneImage.setPosition(0,0);
scoreInt = 0;
coinInt = 0;
}
Loading and creation of objects should not be done more than once. Try just adding my GameInit() method, call it GameReset() and call it from your reset button.

How do update decoview chart with live data?

I have looked at the github resource and also here but I'm unable to get my graph to display live data. Here's my code.
public class Blink_HR extends Fragment {
TextView textView;
LinearLayout linearLayout;
DecoView mDecoView;
private int mBackIndex;
private int mSeries1Index;
private int mSeries2Index;
private int mSeries3Index;
private final float
mSeriesMax = 50f;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.blink_hr, container, false);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
textView = (TextView) getActivity().findViewById(R.id.meditation);
linearLayout = (LinearLayout) getView().findViewById(R.id.blinkHR);
mDecoView = (DecoView)getActivity().findViewById(R.id.dynamicArcView);
mDecoView.addEvent(new DecoEvent.Builder(mSeriesMax)
.setIndex(mBackIndex)
.setDuration(10)
.build());
SeriesItem seriesItem = new SeriesItem.Builder(Color.parseColor("#FFE2E2E2"))
.setRange(0, mSeriesMax, 0)
.setInitialVisibility(true)
.build();
mBackIndex = mDecoView.addSeries(seriesItem);
}
void update(int id, int value) {
String heart = String.valueOf(value);
Log.d("Blink Hai", heart);
if (value > 0 && mDecoView!=null && mSeries1Index!=0) {
SeriesItem seriesItem = new SeriesItem.Builder(Color.parseColor("#FFFF8800"))
.setRange(0, (float)value, 0)
.setInitialVisibility(false)
.build();
mSeries1Index = mDecoView.addSeries(seriesItem);
}
if (mDecoView != null) {
mDecoView.addEvent(new DecoEvent.Builder(42.4f)
.setIndex(mSeries1Index)
.setDelay(3250)
.build());
mDecoView.executeReset();
}
}
}
My update function is called every 1 second and I would expect the graph to update this data in real-time. However all I get is a blimp on the screen.
There is a couple of issues with the code
An event is added to the series mBackIndex before mBackIndex has been initialized
Update is triggered every 1 second but a 3.25 second delay is added to the event before it will be processed
The event on update always sets the DecoView position to 42.4
executeReset() is called every time the update is triggered, this
resets all series in the charts and cancels all pending animations
Here is some sample code that will update a DecoView every 1 second to a random position with animation
public class FauxFitActivity extends AppCompatActivity {
private DecoView mDecoView;
private int mSeries1Index;
private final float mSeriesMax = 50f;
private Handler mHandler = new Handler();
Runnable runnable = new Runnable() {
#Override
public void run() {
update();
mHandler.postDelayed(this, 1000);
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_faux_fit);
mDecoView = (DecoView) findViewById(R.id.dynamicArcView);
createDataSeries1();
// Start the timer
mHandler.post(runnable);
}
private void createDataSeries1() {
final SeriesItem seriesItem = new SeriesItem.Builder(Color.parseColor("#FFFF8800"))
.setRange(0, mSeriesMax, 0)
.setInitialVisibility(false)
.build();
mSeries1Index = mDecoView.addSeries(seriesItem);
}
private void update() {
final Random rand = new Random();
int newPosition = rand.nextInt((int)mSeriesMax);
mDecoView.addEvent(new DecoEvent.Builder(newPosition).setIndex(mSeries1Index).setDuration(1000).build());
}
}

Only the original thread that created a view hierarchy can touch its views

I'm trying to get an ImageView to animate between 2 images in my drawable folder.
I thought everything would work fine, but the log is showing an error: Only the original thread that created a view hierarchy can touch its views.
Heres my code:
public class ExerciseActivity extends Activity {
private ExercisesDataSource datasource;
private Cursor cursor;
private ImageView image_1_view;
private Timer _timer;
private int _index;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_exercise);
datasource = new ExercisesDataSource(this);
datasource.open();
cursor = datasource.fetchExercise(exerciseDataID);
image_1_view = (ImageView) findViewById(R.id.exercise_image);
_index = 1;
_timer = new Timer();
_timer.schedule(new TickClass(), 1000);
}
public class TickClass extends TimerTask
{
private int columnIndex;
#Override
public void run() {
if (_index == 1) {
columnIndex = cursor.getColumnIndex(MySQLiteHelper.COLUMN_IMAGE_1);
_index = 2;
}
else {
columnIndex = cursor.getColumnIndex(MySQLiteHelper.COLUMN_IMAGE_2);
_index = 1;
}
String image_1 = cursor.getString(columnIndex);
image_1 = image_1.replace(".png", "");
int resourceId = getResources().getIdentifier(getPackageName() + ":drawable/" + image_1, null, null);
image_1_view.setImageDrawable(getResources().getDrawable(resourceId));
}
}
}
I went ahead and set the classes and functions to public but that did not fix it.
All of the resources and everything are fine, how do I fix this error?
Your code in TickClass runs on another thread. To do UI work from here use runOnUiThread.
See the docu for details.
runOnUiThread(new Runnable() {
public void run() {
image_1_view.setImageDrawable(getResources().getDrawable(resourceId));
}
});
You should use a Handler. http://developer.android.com/reference/android/os/Handler.html
Create the handler in the onCreate. Then use the handler in with the other thread.
Wrap the code in within the post and this code will be executed on the UI thread which may change the UI components.
"public final boolean post (Runnable r)
Added in API level 1
Causes the Runnable r to be added to the message queue. The runnable will be run on the thread to which this handler is attached."
handler.post( new Runnable() {
#Override
public void run() {
image_1_view.setImageDrawable(getResources().getDrawable(resourceId));
}
});

Android emulator crashes while using concurrency

This question is about using the Google Android SDK, in the Java programming language.
My question could be boiled down to: Why is this code causing the android emulator to crash?
I've been wrestling for a few days with concurrency related to setting up different threads for a game app.
I have made many variations, but they have all failed. At this point, I just want to get a basic concurrent setup going. The worst part is that it is the emulator that crashes, so DDMS reports nothing; therefore I'm pretty clueless as to where the issue is.
The following code shows an activity (class Main), that calls class SceneManager, which creates a thread to be used for game logic stuff. A 3rd class, StatusChannel, is (will be) used to communicate status information between the different threads (Eventually, there will also be a OpenGL rendering thread).
The emulator crashes at different times. It may run for 20 seconds or for 5 minutes.
The setContentView(R.layout.main) in the Activity class just the set basic layout that Eclipse creates.
I've commented out the usage of Node (Created in the Activity class and accessed in SceneManager)
I have installed sdk versions 1.5 through 2.3 -- The current app is targeted at 2.1
The issue has something to do with the SceneManager class. I'm specially suspicious of the run() method.
Here are the 3 classes.
Sorry for the code length.
public class Main extends Activity {
private SceneManager mSceneManager;
private volatile Node mSceneGraph = new Node();
private volatile Status mStatusChannel = new Status();
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
Log.d("-- Main", "onCreate()");
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Holds the scene assets, such as the stage,
// the agents, camera, etc.
mSceneManager = new SceneManager(mSceneGraph, mStatusChannel);
mSceneManager.onCreate();
}
#Override
protected void onResume() {
Log.d("-- Main", "onResume()");
super.onResume();
mSceneManager.onResume();
}
#Override
protected void onPause() {
Log.d("-- Main", "onPause()");
super.onPause();
mSceneManager.onPause();
}
#Override
protected void onDestroy() {
Log.d("-- Main", "onDestroy()");
super.onDestroy();
mSceneManager.onDestroy();
}
}
public class SceneManager implements Runnable{
private Thread mThread;
private volatile Status mStatusChannel;
private volatile Node mSceneGraph;
private volatile long mMillis = 0;
private volatile PrepareVisitor mPrepareVisitor;
private volatile int mStatus = Status.UNKNOWN_STATUS;
SceneManager(Node sceneGraph, Status statusChannel) {
mPrepareVisitor = new PrepareVisitor();
mStatusChannel = statusChannel;
mSceneGraph = sceneGraph;
mMillis = SystemClock.uptimeMillis();
mThread = new Thread(this);
mThread.setName("LogicThread");
mStatusChannel.setSceneManagerStatus(Status.READY_STATUS);
}
public void onCreate() {
Log.d("-- SceneManager", "onCreate()...");
// This will start the thread in a paused state.
mThread.start();
}
public void onResume() {
Log.d("-- SceneManager", "onResume()...");
// Unpause the status manager, if it is currently paused.
if (mStatusChannel.getSceneManagerStatus() == Status.PAUSED_STATUS) {
mStatusChannel.setSceneManagerStatus(Status.READY_STATUS);
}
}
public void onPause() {
Log.d("-- SceneManager", "onPause()...");
if (mStatusChannel.getSceneManagerStatus() != Status.UNKNOWN_STATUS) {
mStatusChannel.setSceneManagerStatus(Status.PAUSED_STATUS);
}
}
public void onDestroy() {
mStatusChannel.setSceneManagerStatus(Status.QUIT_STATUS);
try {
mThread.join();
}
catch (InterruptedException e) {
Log.d("-- SceneManager", "InterruptedException");
}
}
/**
* This method should not be called by clients of this class.
*/
#Override
public void run() {
Log.d("-- SceneManager", "Called...");
// Main logic loop.
outer: while (true) {
// How much time has elapsed since last call.
long timeDelta = SystemClock.uptimeMillis() - mMillis;
switch (mStatus) {
case Status.READY_STATUS:
//mPrepareVisitor.go(mSceneGraph, timeDelta);
break;
case Status.PAUSED_STATUS:
break;
case Status.QUIT_STATUS:
break outer;
case Status.UNKNOWN_STATUS:
int renderStatus = mStatusChannel.getRendererStatus();
if (renderStatus == Status.READY_STATUS) {
mStatusChannel.setSceneManagerStatus(Status.READY_STATUS);
}
break;
}
mStatus = mStatusChannel.getSceneManagerStatus();
// Update the time.
mMillis = SystemClock.uptimeMillis();
}
}
}
public class Status {
/* Generic Statuses */
public final static int UNKNOWN_STATUS = 0;
public final static int READY_STATUS = 1;
public final static int PAUSED_STATUS = 2;
public final static int QUIT_STATUS = 3;
/* Current statuses values */
private int mSceneManagerStatus = UNKNOWN_STATUS ;
private int mRendererStatus = UNKNOWN_STATUS ;
public synchronized int getSceneManagerStatus() {
return mSceneManagerStatus;
}
public synchronized int getRendererStatus() {
return mRendererStatus;
}
public synchronized void setSceneManagerStatus(int status) {
mSceneManagerStatus = status;
}
public synchronized void setRendererStatus(int status) {
mRendererStatus = status;
}
}
-- EDIT --
This issue happens even with something as simple as this:
public class ThreadActivity extends Activity {
private Booboo mBooboo;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mBooboo = new Booboo();
mBooboo.onCreate();
}
}
public class Booboo implements Runnable {
private Thread mThread;
Booboo() {
mThread = new Thread(this, "SceneManagerThread");
}
public void onCreate() {
Log.d("Booboo", "Thread started");
mThread.start();
}
#Override
public void run() {
while (true) {}
}
}
I know the first reaction is to say that it's the while(true){}. Just remember that this is a contrived example to show the issue. In my own code, I do the lifecycle activity as described in the docs. The issue is that the emulator crashes after some time in an infinite loop like that, whether you have break conditions or not.
You probably want to look into AsyncTask. There is great article here : http://android-developers.blogspot.com/2009/05/painless-threading.html

Categories

Resources