Chaining a sequence of actions when communicating in a UI thread - android

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.

Related

How to update recyclerView from web service periodically without leaking memory

My application makes a webservice call using volley, in order to update recyclerView every 10 seconds. Besides memory usage increase in 10 seconds constantly until it hits the max heap size. Then GC starts doing its job, but the memory usage does not come back down like at the beginning.
Using Eclipse MAT or Android Studio analyzer tasks, I could'nt find a single leak in my code.
I want to know that if there are suspects of leaking in my code. Any help will be appreciated.
Below I have 3 classes:
EventService send a message to MainActivity using sendBroadcast() in every 10 seconds.
MainActiviy will get message from EventService using BroadcastReceiver and calls update operation within its Fragment
EventListFragment, which is inside the MainActivity, contains a RecyclerView, that needs to be updated.
Here is my EventService:
public class EventService extends Service {
private volatile boolean isCanceled = false;
public static final String KEY_MESSAGE = "connection";
public EventService() {}
#Override
public int onStartCommand(final Intent intent, int flags, int startId) {
Runnable r = new Runnable() {
#Override
public void run() {
while (!isCanceled) {
try {
Intent i = new Intent("android.intent.action.MAIN");
AppController.getInstance().cancelPendingRequests("json_obj_req");
i.putExtra(KEY_MESSAGE, MESSAGE);
sendBroadcast(i);
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Thread eventThread = new Thread(r);
eventThread.start();
return Service.START_STICKY;
}
#Override
public void onDestroy() {
isCanceled = true;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
Here is my MainActivity:
public class MainActivity extends AppCompatActivity {
private Intent intent;
private BroadcastReceiver mReceiver;
private EventListFragment eventListFragment;
private IntentFilter intentFilter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
private void setView() {
eventListFragment = (EventListFragment) getSupportFragmentManager().findFragmentById(R.id.frgEventList);
}
#Override
protected void onResume() {
intent = new Intent(this, EventService.class);
mReceiver = new MyReceiver(eventListFragment);
this.registerReceiver(mReceiver, intentFilter);
intent = new Intent(this, EventService.class);
startService(intent);
}
#Override
protected void onPause() {
super.onPause();
stopService(intent);
unregisterReceiver(mReceiver);
}
private static class MyReceiver extends BroadcastReceiver {
private WeakReference<EventListFragment> eventListFragment = null;
public MyReceiver(EventListFragment eventFragment) {
this.eventListFragment = new WeakReference<>(eventFragment);
}
#Override
public void onReceive(Context context, Intent intent) {
String mssg = intent.getStringExtra(KEY_MESSAGE);
EventListFragment eventFragment = eventListFragment.get();
if (mssg.equals(MESSAGE) && eventFragment != null) {
//Update recyclerView
eventFragment.eventToList();
}
}
}
}
And here is my EventListFragment:
public class EventListFragment extends Fragment {
private View view;
private RecyclerView recyclerView;
private LinearLayoutManager mLayoutManager;
private EventAdapter eventAdapter;
private RequestData requestData;
private ArrayList<EventModel> eventList;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_event_list, container, false);
return view;
}
#Override
public void onResume() {
super.onResume();
setView();
setControl();
}
private void setView() {
recyclerView = (RecyclerView) view.findViewById(R.id.frg_recycler_view);
}
private void setControl() {
if (eventAdapter == null && mLayoutManager == null) {
eventList = new ArrayList<>();
eventAdapter = new EventAdapter(getActivity().getApplicationContext(), eventList);
mLayoutManager = new LinearLayoutManager(getActivity().getApplicationContext());
recyclerView.setLayoutManager(mLayoutManager);
recyclerView.setHasFixedSize(true);
recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), LinearLayoutManager.VERTICAL));
recyclerView.setAdapter(eventAdapter);
}
recyclerView.addOnItemTouchListener(new RecyclerItemListener(getActivity().getApplicationContext(), recyclerView, new RecyclerItemListener.RecyclerTouchListener() {
#Override
public void onClickItem(View v, int position) {
EventModel model = eventList.get(position);
SQLiteHandler db = SQLiteHandler.getInstance(getActivity().getApplicationContext());
//some instances
}
#Override
public void onLongClickItem(View v, int position) {
}
}));
}
//make service call
public void eventToList() {
if (requestData == null) {
requestData = new RequestData(getActivity());
}
final ArrayList<EventModel> newList = new ArrayList<>(); //are you leaking?
requestData.getEventToday(new RequestData.VolleyCallback() {
#Override
public void onSuccess(JSONObject result) {
for (int i = 0; i < result.length(); i++) {
try {
JSONObject item = result.getJSONObject(Integer.toString(i));
EventModel eventModel = new EventModel();
String title = item.getString("title");
String start = item.getString("start");
String end = item.getString("end");
String date = item.getString("date");
eventModel.setDate(date);
eventModel.setStartTime(start);
eventModel.setEndTime(end);
eventModel.setTitle(title);
newList.add(eventModel);
} catch (JSONException e) {
e.printStackTrace();
}
}
eventAdapter.update(newList);
}
});
}
}
Many thanks!
First of all, a design consideration: is it necessary to call web service every 10 seconds? Do you know when/how often server data changes?
Every time you read data from web server, application have to do a lot of work: you create many object, update the adapter etc. Moreover, think about network traffic, you use network every 10 seconds.
There are somethings you can do:
Increment wait time: in this way, you reduce the number of created object/per seconds.
Reduce local reference for temporary objects (see following code)
Check if recycler view's adapter, before add new values, the old ones was correctly deferred.
Evaluate if it is possible to use technology to push data, you to avoid data polling. You can see GCM.
For consideration #2, i try rewrite eventToList method:
public void eventToList() {
if (requestData == null) {
requestData = new RequestData(getActivity());
}
requestData.getEventToday(new RequestData.VolleyCallback() {
#Override
public void onSuccess(JSONObject result) {
ArrayList<EventModel> newList = new ArrayList<>();
JSONObject item;
EventModel eventModel;
String title;
String start;
String end;
String date;
for (int i = 0; i < result.length(); i++) {
try {
item = result.getJSONObject(Integer.toString(i));
eventModel = new EventModel();
title = item.getString("title");
start = item.getString("start");
end = item.getString("end");
date = item.getString("date");
eventModel.setDate(date);
eventModel.setStartTime(start);
eventModel.setEndTime(end);
eventModel.setTitle(title);
newList.add(eventModel);
} catch (JSONException e) {
e.printStackTrace();
}
}
eventAdapter.update(newList);
}
});
}

No text appears on TextView

This is my code, it should print a random value (between 0 and 1) but it doesn't do that!
I don't know how to fix it! I tried multiple things, none of them are working out!
Here's the code:
package com.example.lode.coder;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.TextView;
import java.util.Random;
public class Coder extends AppCompatActivity {
TextView display;
String one;
boolean bl= true;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_coder);
display = (TextView) findViewById(R.id.text);
}
void all() {
while (bl) {
Random rand = new Random();
int n = rand.nextInt(2);
one = n + (String) display.getText();
display.setText(one);
}
}
}
You don't show where the all() method is called. If it's not called anywhere, that would explain why display never gets any text set.
But assuming that it is called somewhere, there's still a big problem: the all() method is an infinite loop. Until you return control to the framework, any changes you make to display won't show up on the screen. (In fact, your app will likely then be killed off by the framework when it notices that the app has become unresponsive.)
If you want to change the text continuously, look into using a Handler. You can create a Runnable that does the actual change and then reschedules itself to run again after a short time. Don't use a loop like you currently have in all().
Something like this would work:
public class Coder extends AppCompatActivity {
private static long UPDATE_INTERVAL = 500; // every half second -- adjust as needed
TextView display;
String one;
Handler handler;
Runnable updater;
Random rand = new Random();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_coder);
display = (TextView) findViewById(R.id.text);
Handler handler = new Handler();
updater = new Runnable() {
#Override public void run() {
int n = rand.nextInt(2);
one = n + display.getText().toString();
display.setText(one);
handler.postDelayed(updater, UPDATE_INTERVAL);
}
}
}
#Override protected void onStart() { // or override onResume() instead
super.onStart();
startUpdates();
}
#Override protected void onStop() { // or override onPause() instead
super.onStop();
stopUpdates();
}
void startUpdates() {
handler.post(updater);
}
void stopUpdates() {
handler.removeCallbacks(updater);
}
}
you haven't called all() function anywhere and even if you do, the logic would still be wrong because bl always stays true and so the while loop is an infinite loop which never stops. Try this code:
public class Coder extends AppCompatActivity {
TextView display;
String one;
boolean bl= true;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_coder);
display = (TextView) findViewById(R.id.text);
all();
}
void all() {
Random rand = new Random();
int n = rand.nextInt(2);
one = n + display.getText().toString();
display.setText(one);
}
}
if you want the loop you need to set your bl variable to false at some point to stop the loop from going on infinitely.

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());
}
}

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

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.)

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