Android: Updating gridview multiple times after x seconds - java

I am having a problem updating the view in android every x seconds.
To get to know how android works I am writing a small game.
It has a GameController which holds the game loop. Inside the loop, the logic is executed and afterwards the gui will be informed about the changes.
The problem is that the changes cannot be seen in the view. The view stays the same until the game loop finishes and updates then only once.
Here is my code (the important parts):
GameController.java:
IGui gui;
public void play() {
while (playing) {
getAndProcessInput();
updateGui();
try {
Thread.sleep(500);
} catch (InterruptedException ex) {
}
}
}
private void updateGui() {
gui.setPlayer(x, y);
// ...
}
GameActivity.java:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
GridView gridview = (GridView) findViewById(R.id.GridView1);
TextAdapter = new TextAdapter(this);
gridview.setAdapter(textAdapter);
GameController c = new GameController();
// here, the game loop shoud start.
// what i tried:
// new Thread(c).start(); <-- causes crash
// c.play(); <-- causes view to frease until game loop is done
this.runOnUiThread(c); <-- causes view to frease until game loop is done
}
TextAdapter.java:
public class TextAdapter extends BaseAdapter implements IGui {
private final Context context;
private final String[] texts;
public TextAdapter(Context context) {
this.context = context;
texts = new String[height * width];
for (int i = 0; i < texts.length; i++) {
texts[i] = " ";
}
}
public int getCount() {
return height * width;
}
public Object getItem(int position) {
return null;
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
TextView tv;
if (convertView == null) {
tv = new TextView(context);
tv.setLayoutParams(new GridView.LayoutParams(25, 25));
} else {
tv = (TextView) convertView;
}
tv.setText(texts[position]);
return tv; // <-- this happens only when game loop is done, not everytime something changed
}
#Override
public void setPlayer(int x, int y) {
texts[width * y + x] = "X";
// this.notifyDataSetChanged(); <-- this does not help (view still freases)
// gridview.invalidateViews(); does not help either
}
}
I googled a lot and tried a lot as well (and I do know that similar questions where asked here already, but they did not help me either), but somehow it just does not work.
I cannot get it do work that the view and logic run on different theads in android, and if they run on the same thread the logic blocks the view.
Any help would be greatly appreciated.
// edit:
If I try new Thread(c).start(); LogCat sais:
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
And if I add Looper.prepare(); :
java.lang.RuntimeException: Unable to start activity ComponentInfo{GameActivity}: java.lang.RuntimeException: Only one Looper may be created per thread
If I try this.runOnUiThread(c); there are no errors.

First, this doesn't seems like the way to crate a game, you will need to use SurfaceView, or GLSurfaceView to better do what you want.
You can also look for Cocos2d for android, it's a 2D platform (that was ported from iPhone) that makes you life easier:
http://code.google.com/p/cocos2d-android/
I muse warn you though, I tried it a couple months back, and it was not production grade yet, it did crash from time to time.
Anyway, if you still want to continue heading your way I'll try answering your question:
I think you are messing too much with the way these stuff should work. try understanding first how handlers work with threads.
Do anything you want on your thread:
new Thread(new Runnable()
{
#Override
public void run()
{
try
{
calculateGameChangesHere();
handler.sendEmptyMessage(SUCCESS);
}
catch (Exception e)
{
handler.sendEmptyMessage(FAILURE);
}
}
}).start();
When your data is ready, tell the handler to put it in a view and show it:
protected Handler handler = new Handler()
{
#Override
public void handleMessage(Message msg)
{
if (msg.what == SUCCESS)
{
setCalculatedDataToaView(); // the data you calculated from your thread can now be shown in one of your views.
}
else if (msg.what == FAILURE)
{
errorHandlerHere();//could be your toasts or any other error handling...
}
}
};
This goes to everything that requires heavy processing/networking that shouldn't block your UI thread.
Hope this helps.

Not sure what you are trying to achieve by refreshing a listView every few seconds. Anyways if you are writing some 2d games, you have to use SurfaceView , which is especially meant for continuous refreshing.

Had the same problem and solved it using a pretty simple code.
Tips:
GridView must be refreshed by the UI thread
To display every change you must keep the loop and the Sleep method away from the UI thread
Solution:
Method to update the grid (put on your Activity that you build your GridView at the first place or wherever)
public void UpdateGrid(){
//your logic
//your way to change grid values (there are tones of other ways)
CustomGridAdapter cga = new CustomGridAdapter(this, values);
gridView.setAdapter(cga);
gridView.invalidateViews();
}
Build the non UI Thread that does the work
public class DataReceiver implements Runnable {
private MainActivity mainActivity;
public DataReceiver(MainActivity ma) {
mainActivity = ma;
}
#Override
public void run() {
for (int i = 0; i < 100; i++) {
mainActivity.runOnUiThread(new Runnable() {
public void run() {
//update the grid here
mainActivity.UpdateGrid();
}
});
//sleep here
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}

Related

Progress bar.setProgress(variable) in a background thread

this is my first question on stack (great community, thanks!)
So, I have this problem:
I'm developing an android application with a tab layout. Whenever I navigate to the "Status" tab, I'd like to show a circular ProgressBar that is updated according to the value of a variable. Let's call this variable prog. Moreover, I'd like to display in the center of the ProgressBar a TextView that displays the same value of prog.
I have came up with this part of code:
public class StatusFragment extends Fragment
{
private ProgressBar torqueRefProgressBar;
private TextView torqueRefTextView;
RunningThreadExample r;
private Thread t1;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
//Inflate the layout for this fragment
return ...;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState)
{
// Initializations and other parts not relevant for the question...
this.torqueRefProgressBar = getView().findViewById(R.id.torqueRefProgressBar);
this.torqueRefTextView = getView().findViewById(R.id.torqueRefTextView);
t1 = new Thread( this.torqueRefProgressBar, this.torqueRefTextView)); // passing arguments by value but values are references to the objects, therefore I'm passing by ref
t1.start();
}
}
In this Fragment I create the ProgressBar and the TextView and then pass them to the Runnable as you can see below
public class RunningThreadExample implements Runnable
{
ProgressBar torqueRefProgBar_thread;
TextView torqueRefTextView_thread;
public RunningThreadExample(ProgressBar progressBar, TextView textView)
{
this.torqueRefProgBar_thread = progressBar;
this.torqueRefTextView_thread = textView;
this.endScale = this.torqueRefProgBar_thread.getMax();
}
#Override
public void run()
{
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
float i = 0;
double temp = 0;
while(Running)
{
try
{
Thread.sleep(1000/60); // update freq 60Hz
}
catch (InterruptedException e)
{
e.printStackTrace();
}
temp = getCurrentVariableValue(); // Not shown here (returns a double)
this.torqueRefProgBar_thread.setProgress((int) temp);
this.torqueRefTextView_thread.setText(String.valueOf(temp));
}
Log.i(INFO_TAG,"Finishing thread!!!!!");
}
}
I'd like this update to run all the time the app is working (for this reason I've neglected AsyncTask). After a while, it happens that the ProgressBar stops updating, while the TextView continues to work without any problem.
I've been reading that a possible cause could be that the calling setProgress(temp) might stuck the UI. However I can't understand while the TextView is still working.
Can you suggest a better way to make the progressBar update?
Thank you!
See this thread:
Android basics: running code in the UI thread
So to summarize, Android requires certain types and sections of code to run in a specific context. Many tools such as Handlers are available to help with these tasks but it was difficult to learn to begin with.
You cannot affect View from any Thread other than Main Thread. Do the following in run() method
runOnUiThread(new Runnable() {
//
run(){
this.torqueRefProgBar_thread.setProgress((int) temp);
} this.torqueRefTextView_thread.setText(String.valueOf(temp));
);

Dynamic UI Progress Updates using Interface + ExecutorService Issue

So I have a music application. I am trying to update the UI with the progress of the media player (like current time, current song, album cover) everytime the song changes. I found that using interfaces was a awesome magical way of communication between activity and fragments so I implemented an interface in my MusicManger class. My code will show what and how did it.
Two problems
1) Commented look below, ExecutorService seems to stop after one loop. No Errors in catch block (this is why I tagged with java)
2) Commented please look, All the System.out methods print but the UI doesn't update. I do believe I called the method from mainThread so it should update.
I'll show code in logical order will add titles in bold before code segment to tell you basic idea of code.
Passing UI references from fragment to MusicManager class, code below in Fragment class
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_item_songlist, container, false);
// Set the adapter
TextView musicManagerSongName, musicManagerCurrent, musicManagerTotal;
ProgressBar musicManagerProgress;
ImageView musicManagerImageView;
mListView = (AbsListView) view.findViewById(R.id.slist);
musicManagerSongName = (TextView)view.findViewById(R.id.textView12);
musicManagerCurrent = (TextView)view.findViewById(R.id.textView10);
musicManagerTotal = (TextView)view.findViewById(R.id.textView11);
musicManagerProgress = (ProgressBar)view.findViewById(R.id.progressBar);
musicManagerImageView = (ImageView)view.findViewById(R.id.imageView2);
MainActivity.mediaPlayer.passUIReferences(musicManagerSongName, musicManagerCurrent, musicManagerTotal, musicManagerProgress, musicManagerImageView, view);
// line above is a method within MusicManager that takes the references will show code next!
ImageButton playbutton = (ImageButton)view.findViewById(R.id.playbuttonbar);
ImageButton nextButton = (ImageButton)view.findViewById(R.id.nextbuttonbar);
ImageButton backButton = (ImageButton)view.findViewById(R.id.backbuttonbar);
ImageButton toggleButton = (ImageButton)view.findViewById(R.id.shufflebuttonbar);
ImageButton pausebutton = (ImageButton)view.findViewById(R.id.pausebuttonbar);
playbutton.setBackgroundResource(R.drawable.playbuttonbar);
playbutton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
try {
MainActivity.mediaPlayer.stateChange(1);
}catch(Exception e) {
}
}
});
backButton.setBackgroundResource(R.drawable.backbutton1);
nextButton.setBackgroundResource(R.drawable.nextbutton1);
toggleButton.setBackgroundResource(R.drawable.shufflebuttonselected);
pausebutton.setBackgroundResource(R.drawable.pausebutton1);
pausebutton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
try {
MainActivity.mediaPlayer.stateChange(0);
} catch (Exception e){
}
}
});
mListView.setAdapter(mAdapter);
((MainActivity) mListener).restoreActionBar();
return view;
}
As Commended above the code that is located in MusicManager class that takes references and stores them. Also shows interface implementation with MusicManager class. And the Executor service
public void passUIReferences(View... views) {
this.uiElements = views;
}
private ExecutorService executorService = Executors.newSingleThreadExecutor();
private MediaplayerUpdateInterface uiUpdateInterface;
public MediaPlayerManager(MediaplayerUpdateInterface inter) {
this.player = new MediaPlayer();
this.uiUpdateInterface = inter;
// The below line starts the single thread while loop for excutorservice and only loops and prints "this" once after I start one song then it never loops again
executorService.submit(new Runnable() {
#Override
public void run() {
while(true) {
if (player.isPlaying() && uiElements != null) {
System.out.println("this");
uiUpdateInterface.updateUI(uiElements, 0);
}
try {
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
}
public interface MediaplayerUpdateInterface {
public void updateUI(View[] views, int type);
}
Finally some code from MainActivity class that actually is suppose to update the UI note that both println's work as expected but only once as stated above because of the executorservice issue
public static MediaPlayerManager mediaPlayer = new MediaPlayerManager(new MediaPlayerManager.MediaplayerUpdateInterface() {
#Override
public void updateUI(View[] views, int type) {
System.out.println("check1 " + type);
updateMediaplayerViews(views, type);
}
});
private static void updateMediaplayerViews(View[] views, int type)
{
switch(type) {
case 0:
System.out.println("that?");
((TextView)views[0]).setText(mediaPlayer.getCurrentSongInfo().getName().length() > 22? mediaPlayer.getCurrentSongInfo().getName().substring(0, 19)+"..." : mediaPlayer.getCurrentSongInfo().getName());
break;
}
views[views.length - 1].invalidate();
}
The view array is shown perviously! Also the last view in the array is shown as the main view for songlist fragment.
I am sorry for all the code I've tried to debug it as you can see from my println's there is just something I am unaware of going on here.
Ok so there was an error that I needed to catch to see within the following code:
private static void updateMediaplayerViews(View[] views, int type)
{
switch(type) {
case 0:
System.out.println("that?");
((TextView)views[0]).setText(mediaPlayer.getCurrentSongInfo().getName().length() > 22? mediaPlayer.getCurrentSongInfo().getName().substring(0, 19)+"..." : mediaPlayer.getCurrentSongInfo().getName());
break;
}
views[views.length - 1].invalidate();
}
The issue is I was trying to change the view from a different thread then the one which created it. Solving it was pretty long and painful but basically I made it nonstactic used more interfaces then used the famous
Mainactivity.runOnUiThread(new Runnable(....));

Android: trying to add a wait in between two animations, getting Object not locked by thread before wait() error

I'm new to Android and Java programming. I created an app for a Coursera class. It has two animations that occur repeatedly. I want them to animate like a drum beat - 1,2,1,2,1,2,1,2,1,2.
I wrote two recursive methods that run until the user clicks a Reset button. I tried adding a wait in between the method calls so the second would kick off right after the first one.
This is the part of my code that has the wait.
mHandler = new Handler(); // .os package class when importing
mLeftfoot = findViewById(R.id.leftfoot);
mRightfoot = findViewById(R.id.rightfoot);
mFootAnim = AnimationUtils.loadAnimation(this, R.anim.foot); //this looks to the foot.xml file for the animations
leftstepRecursive();
try {
wait(600);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
rightstepRecursive();
This part of my code shows the recursive methods, which could probably be written better I know. A lot of DRY...
private void leftstepRecursive() {
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
mLeftfoot.startAnimation(mFootAnim);
if (!pleaseStop)
leftstepRecursive();
}
}, mIntervalleft);}
private void rightstepRecursive() {
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
mRightfoot.startAnimation(mFootAnim);
if (!pleaseStop)
rightstepRecursive();
}
}, mIntervalright);
Any better ideas to implement this left, right, left, right, left, right, left, right, left, right, idea?
Or, what I can do to correct the wait?
I also tried this:
private void rightstepRecursive() {
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
Object testobj = mRightfoot.startAnimation(mFootAnim);
synchronized (testobj) {
testobj.wait(600);
}
if (!pleaseStop)
rightstepRecursive();
}
}, mIntervalright);
#LuigiMendoza. I tried thread(sleep). No errors, but both animations move simultaneously not sure why...
private void rightstepRecursive() {
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
mRightfoot.startAnimation(mFootAnim);
if (!pleaseStop)
rightstepRecursive();
}
}, mIntervalright);
try
{
Thread.sleep(500);
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
I also get this message repeating in Logcat.
02-11 12:15:32.261: I/Choreographer(30480): Skipped 302 frames! The application may be doing too much work on its main thread.
You should really utilize the Animations framework and some of the classes it provides such as AnimationListeners and AnimatorSets. The Animations framework provides a lot of functionality that would really simplify what you are trying to do i.e. repeating animations, adding delays, timing animations, etc.
Here's a really bare bones example to give the basic idea:
AnimatorSet animations;
//Play the animation
private void playAnimation(){
//If they haven't stopped the animation, loop it.
ObjectAnimator anim1 = ObjectAnimator.ofFloat(target, propertyName, values);
ObjectAnimator anim2 = ObjectAnimator.ofFloat(target, propertyName, values);
animations = new AnimatorSet();
animations.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
playAnimation();
}
});
animations.play(anim1).before(anim2);
animations.start();
}
//Called when cancel is selected
private void stopAnimation(){
animations.end();
}

android activity create multiple instance of my thread

i need to check a variable for change, if the change happens i will update a textview.
so i created a new thread with a while loop for this purpose. i check the variable every 1 second, via Thread.sleep()
this thread created and started in onCreate(). so it's one time.
the problem is every time i flip my phone (from vertical to horizontal or ...) a new thread will be created.
here is my code:
public class HomeActivity extends Activity
{
private final static int LOAD_SUCCESSFULL = 1;
private final long LOCATION_THREAD_SLEEP = 1000;
private boolean flag = false;
static TextView lat;
static TextView lon;
static Location currentLocation = null;
Thread locationThread;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
setContentView(R.layout.new_home2);
this.getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.new_home_titlebar);
lat = (TextView)findViewById(R.id.t2rt3);
lon = (TextView)findViewById(R.id.t2rt4);
/* FindLocation class is a helper class that find location via simcard or gps in separate thread,
same problem happen with this thread also, it create multiple thread, for ease of work i
commented this part.
*/
//FindLocation fn = new FindLocation(this);
locationThread = new Thread(null, loadLocation, "loadLocationHomePage");
locationUpdater();
}
private static Handler locationUpdateHandler = new Handler()
{
public void handleMessage(Message msg)
{
switch(msg.what)
{
case LOAD_SUCCESSFULL:
lat.setText(Double.toString(currentLocation.getLatitude()));
lon.setText(Double.toString(currentLocation.getLongitude()));
//stopThread();
break;
}
}
};
private Runnable loadLocation = new Runnable()
{
public void run()
{
//boolean flag = false;
while(!flag)
{
if(Data.currLocation != null)
{
currentLocation = new Location(Data.currLocation);
Message msg = locationUpdateHandler.obtainMessage(LOAD_SUCCESSFULL);
locationUpdateHandler.sendMessage(msg);
//return;
flag = true;
//return;
}
else
{
try
{
Thread.sleep(LOCATION_THREAD_SLEEP);
}
catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
};
public void locationUpdater()
{
//Thread locationThread = new Thread(null, loadLocation, "loadLocationHomePage");
locationThread.start();
}
so how i can solve this?
Actually the problem is that EveryTime you flip the phone a new instance of Activity is created and because of this you on every rotation you get a call on onCreate() where you are blindly creating a new Thread and Starting the new Thread.
This is the default behavior of every Activity but we can change this re-creation of Activity by stating an attribute in AndroidManifest file for the Activity
<activity
android:name="yourPackage.ActivityName"
android:configChanges="keyboardHidden|orientation|screenSize"
</activity>
This will prevent from creation of Activity on orientation change.
You will also get these orientation event if you override
#Override
public void onConfigurationChanged(Configuration newConfig) {}
Hope this will solve this problem without implementing such a complex logic which may broke in some other uses case.
I think you aren't perhaps going about this in the most efficient way possible.
But if your question is simply, how do i prevent multiple worker threads from being spawned, you should look into a UIless fragment.
http://www.vogella.com/articles/AndroidFragments/article.html#headlessfragments1
i don't know why android doing this. if i putted my code in onResume(), then this behavior make sence but in my case, i don't know.
anyway i found a dirty solution. the idea is finding list of all thread and search them for mine, if it existed prevent to create another.
public boolean checkThreadExist()
{
Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
Thread[] threadArray = threadSet.toArray(new Thread[threadSet.size()]);
for(int i = 0; i < threadArray.length ; i++)
{
if(threadArray[i].getName().equalsIgnoreCase("loadLocationHomePage"))
return true;
}
return false;
}
updated onCreate() :
if(checkThreadExist())
{
}
else
{
locationThread = new Thread(null, loadLocation, "loadLocationHomePage");
locationUpdater();
}

Why does my app force close when I setText in an update method?

I have an android app I am just experimenting things on and I cannot seem to figure out why my app force closes when I update a TextView via a while loop. When I comment out the updateText method it runs fine.
public class GameThread extends Thread {
Thread t;
private int i;
private boolean running;
private long sleepTime;
GameView gv;
public GameThread() {
t = new Thread(this);
t.start();
i = 0;
sleepTime = 1000;
}
public void initView(GameView v) {
this.gv = v;
}
public void setRunning(boolean b) {
this.running = b;
}
public boolean getRunning() {
return running;
}
public void run() {
while(running) {
i++;
update();
try {
t.sleep(sleepTime);
} catch(InterruptedException e) {
}
}
}
public void update() {
gv.setText(i); // when this is uncommented, it causes force close
Log.v("Semajhan", "i = " + i);
}
public class GameView extends LinearLayout {
public TextView tv;
public GameView(Context c) {
super(c);
this.setBackgroundColor(Color.WHITE);
tv = new TextView(c);
tv.setTextColor(Color.BLACK);
tv.setTextSize(20);
this.addView(tv);
}
public void setText(int i) {
tv.setText("i count: " + i);
}
public class Exp extends Activity {
GameThread t;
GameView v;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
v = new GameView(this);
setContentView(v);
t = new GameThread();
t.setRunning(true);
t.initView(v);
}
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (t.getRunning() == true) {
t.setRunning(false);
Log.v("Semajhan", "STOPPED");
} else {
t.setRunning(true);
Log.v("Semajhan", "RESTART");
}
}
return true;
}
protected void onDestroy() {
Log.v("Semajhan", "DESTROYING");
super.onDestroy();
}
protected void onStop() {
Log.v("Semajhan", "Stopping");
super.onStop();
}
I though i'd post the whole app since it is relatively small and so that I could get some help without confusion.
First, when you get a Force Close dialog, use adb logcat, DDMS, or the DDMS perspective in Eclipse to examine LogCat and look at the stack trace associated with your crash.
In this case, your exception will be something to the effect of "Cannot modify the user interface from a non-UI thread". You are attempting to call setText() from a background thread, which is not supported.
Using a GameThread makes sense if you are using 2D/3D graphics. It is not an appropriate pattern for widget-based applications. There are many, many, many, many examples that demonstrate how to create widget-based applications without the use of a GameThread.
You have to call it from the UI thread.
For more info check: Painless Threading .
If you decide to use a Handler, the easiest solution for you will be to:
Extend a View, override it's onDraw , in it draw the game objects, after you have calculated the game data for them first of course
The Handler: (in your Activity)
private Handler playHandler = new Handler() {
public void handleMessage(Message msg) {
gameView.postInvalidate(); // gameView is the View that you extended
}
};
The game thread has a simple
Message.obtain(playHandler).sendToTarget();
In 2 words, the View is responsible for the drawing (you can move the calculations in a separate class, and call it before the onDraw), the thread is responsible only for scheduled calls to the Handler, and the Handler is responsible only to tell the View to redraw itself.
You cannot update the UI of your app outside of the UI Thread, which is the 'main' thread you start in. In onCreate(Context) of you app, you are creating the game thread object, which is what is doing the updating of your UI.
You should use a Handler:
http://developer.android.com/reference/android/os/Handler.html

Categories

Resources