Thread in Android - java

I have an example like this:
package android.uiexample;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ProgressBar;
import android.widget.RadioGroup;
import android.widget.Toast;
import android.widget.ToggleButton;
import android.widget.RadioGroup.OnCheckedChangeListener;
public class BasicViewsExampleActivity extends Activity
{
private static int progress = 0;
private ProgressBar progressBar;
private int progressStatus = 0;
private Handler handler = new Handler();
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.basic_views);
progressBar = (ProgressBar) findViewById(R.id.progressbar);
//---do some work in background thread---
new Thread(new Runnable()
{
public void run()
{
//---do some work here---
while (progressStatus < 10)
{
progressStatus = doSomeWork();
}
//---hides the progress bar---
handler.post(new Runnable()
{
public void run()
{
progressBar.setVisibility(View.GONE);
}
});
}
//---do some long lasting work here---
private int doSomeWork()
{
try {
//---simulate doing some work---
Thread.sleep(500);
} catch (InterruptedException e)
{
e.printStackTrace();
}
return ++progress;
}
}).start();
}
}
In this example, it use Handler to post a Runable to exc progressBar.setVisibility(View.GONE);. I don't know why i can't call progressBar.setVisibility(View.GONE); directly:
//---do some work here---
while (progressStatus < 10)
{
progressStatus = doSomeWork();
}
//---hides the progress bar---
progressBar.setVisibility(View.GONE);
Anybody can tell me why i can't?

The android UI framework (like pretty much every UI framework before it) only allows you to update the UI state from the main thread. You may want to look at AsyncTask which include the plumbing needed to route progress updates onto the main thread.

Take a look at the Handler class. It provides a simple way to enqueue Runnable callbacks to run on the UI event thread.

changing the progressBar visibility is a UI operation. All UI operations must be done on the UI thread.

Related

I need to cancel the Timer started in a service class from MainActivity, but get null reference exception

I have a service type class which starts a timertask in onCreate method and I need to stop the timer from MainActivity when user press the button. I know I have to keep the reference to the timer in my service, but I cannot figure out how to do that and need some help, please!
please take a look at my code
package com.example.timertest;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
Button button;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = findViewById(R.id.button);
button.setOnClickListener(this);
startService(new Intent(this, TimeService.class));
}
#Override
public void onClick(View v) {
if(v.getId() == R.id.button){
// i need here to call mTimer.cancel() in TimeService.class
//
}
}
}
// and here is the TimeService.java
package com.example.timertest;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.widget.Toast;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class TimeService extends Service {
public Timer mTimer;
private Handler mHandler = new Handler();
long NOTIFY_INTERVAL = 60 * 1000 * 1; // 1 min
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
if (mTimer != null) {
mTimer.cancel();
} else {
mTimer = new Timer();
}
mTimer.scheduleAtFixedRate(new RefreshDataTimerTask(), 0, NOTIFY_INTERVAL);
}
class RefreshDataTimerTask extends TimerTask {
#Override
public void run() {
mHandler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), getDateTime(), Toast.LENGTH_LONG).show();
}
});
}
private String getDateTime() {
SimpleDateFormat sdf = new SimpleDateFormat("[yyyy/MM/dd - HH:mm:ss]");
return sdf.format(new Date());
}
}
}
//and service registered in manifest
<service android:name=".TimeService"/>
I have tried to call the mTimer.cancel(), but got a null reference, because seems I have created a new instance of the service class.
This example shows a Toast with date and time each minute, i want when for example 15 seconds have past and i press the button, the timer to be canceled and to start counting 60 seconds again from the beginning. Inside this service i have many other things like notifications,channels, shared preferences and so on, so it will be good if i can operate only with the timer object.
Take a look on this API documentaion and this. There is example of how to bind service to activity after what you can get direct access to service methods.
After successful binding you can add method inside your service to stop the timer:
public void stopMyTimer() {
mTimer.cancel();
}
And call this method from Activity
This might not be best practice, but you can access public values by directly referring to the class:
TimeService.mTimer.cancel()
Which should work because Services function similarly to Singletons, in that each call of startService should be calling the same instance.

using or excecution of sleep in setOnclicklistner function

I code a program which I expected to perform follwing action sequentially onclick the button:
1.Display the text(eg: "abc") in textview.
2.sleep for sometime(eg:2000).
3.finally it has to change the text(eg: "xyz") in textview.
for that i try the below code. when I run this, first it sleep for 2000 millisecond and display "xyz", It doesnot displaying "abc"...
(* I noticed that sleep will excecuting first inside the setOnclickListner()function then only rest of the code are excecuting *)
please help me to perform the action in sequence...
If my question is silly please excuse as I was new to this....
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity {
Button b1;
TextView tv;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv =(TextView)findViewById(R.id.textView1);
b1=(Button)findViewById(R.id.button1);
b1.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
tv.setText("abc");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
tv.setText("xyz");
}
});
}}}
Use Handler.postDelayed instead of Thread.sleep which freeze main UI Thread for 2000. Do same using Handler as:
Create Runnable :
private Runnable runnable = new Runnable() {
public void run() {
tv.setText("xyz");
mHandler.removeCallbacks(runnable);
}
};
call Handler.postDelayed on Button click :
mHandler = new Handler();
b1.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
tv.setText("abc");
mHandler.postDelayed(runnable, 2000);
}
});

Android Timer and timerTask error

This code is too add a timer on the code.. but i am having a problem with my code when it comes to the timer code...
How will i improve the code for the timer inside the for loop of my code..
Hopefully you understand the my code.. and have improved my code.. Thanks!!!!
package com.thesis.americansignlanguage;
import java.io.IOException;
import java.io.InputStream;
import java.util.Timer;
import java.util.TimerTask;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.widget.ImageView;
import android.widget.TextView;
public class AlphabetCompareClass extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.alphabetcompare);
final String get;
Bundle gotWord = getIntent().getExtras();
get = gotWord.getString("key");
TextView Word = (TextView)findViewById(R.id.textView1);
final ImageView img = (ImageView)findViewById(R.id.image_alpha) ;
Word.setText(get);
for(int x = 0; x > gotWord.getString("key").length(); x++) {
final InputStream is;
Timer timer = new Timer();
TimerTask timertask = new TimerTask() {
#Override
public void run() {
try {
is = getResources().getAssets().open(get + ".png");
Bitmap bitmap = BitmapFactory.decodeStream(is);
img.setImageBitmap(bitmap);
} catch (IOException e) {
return;
}
}
};
}
};
}
Two problem found in current code:
First : Not using timer instance to schedule timerTask. do it as:
timer.schedule(timertask, 500, 3000);
Second : Updating UI from non- ui thread inside run method of TimerTask
Use runOnUiThread or Handler for updating Ui from run method of TimerTask
TimerTask runs on a different thread. Ui can be updated only from ui thread.
This img.setImageBitmap(bitmap); should be executed on the ui thread
Use runOnUiThread. You can also use a Handler. Also i do not understand the need for the for loop.
runOnUiThread(new Runnable() {
#Override
public void run() {
img.setImageBitmap(bitmap);
}
});

Background changing every second

I am trying to change background every second and this is my code:
package com.example.splasher;
import java.lang.reflect.Field;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import android.os.Bundle;
import android.app.Activity;
import android.graphics.Typeface;
import android.util.Log;
import android.view.Window;
import android.view.WindowManager;
import android.widget.TextView;
public class Views extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.view);
final ScrollTextView scrolltext=(ScrollTextView) findViewById(R.id.scrolltext);
if(MainActivity.bold.isChecked())
{
scrolltext.setTypeface(null, Typeface.BOLD);
};
if(MainActivity.italic.isChecked())
{
scrolltext.setTypeface(null, Typeface.ITALIC);
};
if((MainActivity.italic.isChecked())&&(MainActivity.bold.isChecked()))
{
scrolltext.setTypeface(null, Typeface.BOLD_ITALIC);
};
scrolltext.setTextSize(Integer.parseInt(MainActivity.tSize[MainActivity.Size.getSelectedItemPosition()]));
scrolltext.setText(MainActivity.text);
scrolltext.setTextColor(Integer.parseInt(MainActivity.colour[MainActivity.TextColour.getSelectedItemPosition()]));
scrolltext.startScroll();
scrolltext.setBackgroundColor(Integer.parseInt(MainActivity.colour[MainActivity.BackgroundColour.getSelectedItemPosition()]));
Thread thread = new Thread()
{
public void run()
{
if(MainActivity.random.isChecked())
{
int delay = 0; // delay for 5 sec.
int period = 1000; // repeat every sec.
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
int n=1;
public void run() {if (n==9)n=1;
scrolltext.setBackgroundColor(Integer.parseInt(MainActivity.colour[n]));
}
}, delay, period);
/*int n=1;
boolean constant=true;
while (constant==true){
if (n==10) n=1;
// int randInt = new Random().nextInt(2);
// scrolltext.setBackgroundColor(Integer.parseInt(MainActivity.colour[randInt]));
scrolltext.setBackgroundColor(Integer.parseInt(MainActivity.colour[n]));
n=n+1;
try
{
Thread.sleep(2000); // 1 second
} catch (Exception e)
{
e.printStackTrace();
}
}*/
}
}
};
thread.start();
// TextView textview=(TextView) findViewById (R.id.textView1);
// textview.setText(MainActivity.text);
// textview.setTextColor(Integer.parseInt(MainActivity.colour[MainActivity.TextColour.getSelectedItemPosition()]));
// textview.setBackgroundColor(Integer.parseInt(MainActivity.colour[MainActivity.BackgroundColour.getSelectedItemPosition()]));
// textview.setTextSize(Integer.parseInt(MainActivity.tSize[MainActivity.Size.getSelectedItemPosition()]));
// textview.setSelected(true);
}
}
But it force closes. Logcat shows: FATAL EXCEPTION: Timer-0; android.view.ViewRootŲcalledfromwrongThreadException: Only original threat that created a view hierarchy can touch its view.
What is wrong with this code?
You can not operate on the UI elements (like call scrolltext.setBackgroundColor()) in a thread separate from the so-called UI thread (which is basically the same thread onCreate() runs in.
To solve your issue, use a Handler. Create a class field:
Handler handler = new Handler();
And in your thread put all UI operations inside a Handler call:
handler.post(new Runnable() {
#Override
public void run () {
// All UI operations, like scrolltext.setBackgroundColor()
}
});
Your problem is
scrolltext.setBackgroundColor(Integer.parseInt(MainActivity.colour[n]));
You are updating UI from worker Thread(it not runs on UI Thread) and this is not allowed. Only Main(UI) Thread can manipulate with UI.
So you need to change it to runOnUiThread() or Handler.
Examples:
runOnUiThread(new Runnable(){
public void run() {
// do your stuff
}
});
Handler example.
Just trying to contribute here, but I have zero experience in android, so be cautions ;p
The exception message seems to suggest that the view can only be altered on the main thread. As you are running code which alters the view on a new thread, it doesn't like it.
Are you aware of a main Event-Dispatcher thread used in Android? Similar to how SWINGs GUI is updated on the EDT. If so, look for a way to tell the main drawing thread to do the final updates.
In swing, it would look something like:
EventQueue.invokeLater(new Runnable(){
public void run(){
}
}
Hope this has some meaning.

Thread remains in NEW state if started from AsyncTask by runOnUiThread

Here is an example application I wrote to reproduce the memory leak related issue i have met :
package a.b.mapleak;
import android.app.Activity;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MapLeak extends Activity
{
TextView callVoidText, getNewDataText, getDataStringText;
Button callVoidButton, getNewDataButton, getDataStringButton;
Handler callbackHandler;
MemoryLeak memleak;
private LoadAccountsTask mLoadAccountsTask = null;
private class LoadAccountsTask extends AsyncTask<Void, Void, Object[]> {
private int mCursor1;
private int mCursor2;
private Context context;
private UITask t = new UITask();
#Override
protected Object[] doInBackground(Void... params) {
runOnUiThread(t);
// Create the summaries cursor
mCursor1 = 1;
mCursor2 = 2;
return new Object[] { mCursor1, mCursor2};
};
private class UITask extends Thread {
#Override
public void run() {
int i = 2;
while (i>0) {
i--;
}
Thread.State state=t.getState();
Log.e(ALARM_SERVICE, "Thread state in run is " + state.toString());
}
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
getDataStringText = (TextView) findViewById(R.id.getDataString_text);
getDataStringButton = (Button) findViewById(R.id.getDataString_button);
getDataStringButton.setOnClickListener(new Button.OnClickListener() {
long i = 0;
public void onClick(View v) {
if (mLoadAccountsTask != null) mLoadAccountsTask.cancel(true);
mLoadAccountsTask = (LoadAccountsTask) new LoadAccountsTask(this).execute();
}
});
}
}
I found that the thread started in runOnUiThread has never been exited. For some unknown to me reason the task was running, but the output from
Log.e(ALARM_SERVICE, "Thread state in run is " + state.toString());
every time was "Thread state in run is NEW" e.g. ''The thread has been created, but has never been started.''. So the thread was running continuously preventing "LoadAccountsTask" class from being garbage collected. Thus, in this example, every time the button is pressed - the new "LoadAccountsTask" appeared in memory.
But, if to replace
private class UITask extends Thread
with
private class UITask implements Run
there was no memory leak, the thread exited successfully. Of course state=t.getState(); will not work in this case and should be commented.
Do anybody have an explanation why it is so?
Focusing on your immediate concern, runOnUiThread() takes a Runnable. While Thread does implement Runnable, runOnUiThread() will not treat it as a Thread. Please just pass a Runnable to runOnUiThread().
Second, never call runOnUiThread() from doInBackground(). Just perform the work from that Runnable in onPreExecute() or onPostExecute(), which are executed on the main application thread.
Third, if you ever do write a real Thread, don't busy-loop in it.

Categories