How to avoid Runnable callback leak? - java

I have codes below that will execute a callback from another thread when I finish the activity. So how to avoid to call the callback or the codes in the callback when the activity has been finished?
public static interface Callback{
public void onCallback();
}
class ActivityA {
TextView tv = ...;
Handler handler = ...;
public void onClick(View v) {
Business.callThread(new Callback() {
#Override
public void onCallback() {
handler.post(new Runnable(){
public void run(){
tv.setText("xxxx");
}
});
}
});
}
}
class Business {
public static void callThread(final Callback listener) {
new Thread(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(5000); //sleep 5s;
} catch (InterruptedException e) {
e.printStackTrace();
}
listener.onCallback();
}
}).start();
}
}

Garbage collector counts references to objects. However, there are couple of reference types. Useful in your case is WeakReference:
Weak reference objects, which do not prevent their referents from being made finalizable, finalized, and then reclaimed.
Create runnable as a class with constructor:
static class CallbackRunnable implements Runnable {
WeakReference<Callback> listener;
public CallbackRunnable(Callback listener) {
this.listener = new WeakReference<Callback>(listener);
}
#Override
public void run() {
try {
Thread.sleep(5000); //sleep 5s;
} catch (InterruptedException e) {
e.printStackTrace();
}
if (listener.get() == null) {
return;
}
listener.get().onCallback();
}
}
then call listener like:
if (listener.get() == null) {
return;
}
listener.get().onCallback();
callThread method implementation:
public static void callThread(final Callback listener) {
new Thread(new CallbackRunnable(listener)).start();
}

Related

invoking callback after a thread exit - Android

I can't find a none blocking way to invoke my callback method from the main thread as my second thread exits.
I tried to use the loading.isAlive() in a while loop but it freeze the screen.
public class MainActivity extends AppCompatActivity {
private ProgressBar progressBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progressBar = findViewById(R.id.load_opening_screen);
Thread loading = new LoadingBarThread();
loading.start();
}
private class LoadingBarThread extends Thread {
#Override
public void run() {
for (int i=0;i<=100;i++) {
progressBar.setProgress(i);
try {
Thread.sleep(40);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
private void callback(){
// start a new activity for example
}
}
I would like to start a new activity as the ProgressBar reaches 100.
Use a Handler object and at the end of your run() function, post a Runnable to that Handler. Have the Runnable call the callback method. Handler will execute it on the thread its attached to (whatever thread it was created on).
So your new class looks like:
private class LoadingBarThread extends Thread {
Handler handler = new Handler();
#Override
public void run() {
for (int i=0;i<=100;i++) {
progressBar.setProgress(i);
try {
Thread.sleep(40);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
handler.post(new Runnable() {
public void run() {
callback();
}
});
}
}

Android - Pause thread and continue after receive response from another thread [duplicate]

I have a thread that running into an activity. I don't want that the thread continuos running when the user click the home button or, for example, the user receive a call phone.
So I want pause the thread and resume it when the user re-opens the application.
I've tried with this:
protected void onPause() {
synchronized (thread) {
try {
thread.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
super.onPause();
}
protected void onResume() {
thread.notify();
super.onResume();
}
It stops the thread but don't resume it, the thread seems freezed.
I've also tried with the deprecated method Thread.suspend() and Thread.resume(), but in this case into Activity.onPause() the thread doesn't stop.
Anyone know the solution?
Use wait() and notifyAll() properly using a lock.
Sample code:
class YourRunnable implements Runnable {
private Object mPauseLock;
private boolean mPaused;
private boolean mFinished;
public YourRunnable() {
mPauseLock = new Object();
mPaused = false;
mFinished = false;
}
public void run() {
while (!mFinished) {
// Do stuff.
synchronized (mPauseLock) {
while (mPaused) {
try {
mPauseLock.wait();
} catch (InterruptedException e) {
}
}
}
}
}
/**
* Call this on pause.
*/
public void onPause() {
synchronized (mPauseLock) {
mPaused = true;
}
}
/**
* Call this on resume.
*/
public void onResume() {
synchronized (mPauseLock) {
mPaused = false;
mPauseLock.notifyAll();
}
}
}
Try the below code it will work
Thread thread=null;
OnResume()
public void onResume(){
super.onResume();
if(thread == null){
thread = new Thread()
{
#Override
public void run() {
try {
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
thread.start();
}
}
onPause()
#Override
public void onPause(){
super.onPause();
if(thread != null){
Thread moribund = thread;
thread = null;
moribund.interrupt();
}
}

A thread start on a button click and ending the thread with another button click

Sir Please help me to add a thread that starts on a button click and ends the thread with another button click. In between I have a sound playing till the thread stops.
You can try this simple code:
final volatile boolean toExit = false;
final Thread t = new Thread(new Runnable() {
#Override
public void run() {
while(!toExit){
// Your code
Thread.sleep(100);
}
}
});
findViewById(R.id.button1).setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
t.start();
}
});
findViewById(R.id.button2).setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
toExit = true;
}
});
The thread will stop after button2 clicked and run to while(!toExit).
Threads stop method is deprecated.
The best solution will be having a boolean variable in the run method.
Your Thread:
public class MyThread implements Runnable {
private static final Logger LOGGER = LoggerFactory.getLogger(IndexProcessor.class);
private volatile boolean running = true;
public void terminate() {
running = false;
}
#Override
public void run() {
while (running) {
try {
//Your code that needs to be run multiple times
LOGGER.debug("Processing");
} catch (InterruptedException e) {
LOGGER.error("Exception", e);
running = false;
}
}
}
}
In your Activity:
MyThread t=new Thread();
findViewById(R.id.button1).setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
t.start();
}
});
findViewById(R.id.button2).setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
t.terminate();
}
});
Use Below Code
public class SomeBackgroundProcess implements Runnable {
Thread backgroundThread;
public void start() {
if( backgroundThread == null ) {
backgroundThread = new Thread( this );
backgroundThread.start();
}
}
public void stop() {
if( backgroundThread != null ) {
backgroundThread.interrupt();
}
}
public void run() {
try {
Log.i("Thread starting.");
while( !backgroundThread.interrupted() ) {
doSomething();
}
Log.i("Thread stopping.");
} catch( InterruptedException ex ) {
// important you respond to the InterruptedException and stop processing
// when its thrown! Notice this is outside the while loop.
Log.i("Thread shutting down as it was requested to stop.");
} finally {
backgroundThread = null;
}
}
Hope this will help you

Not be able to update the list view, after removing some items

I have to update the list after optimizing the running apps ....
m_optimizeBtn.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
launchProgressRing(OptimizationActivity.this);
listAdaptor.notifyDataSetChanged();
}
});
}
Killing the running process in a seprate thread....
public void launchProgressRing(Context ctx){
final ProgressDialog opt_proDialog=new ProgressDialog(ctx);
opt_proDialog.setTitle("Please wait...");
opt_proDialog.setMessage("Optimizing power draining apps...");
opt_proDialog.setIndeterminate(true);
opt_proDialog.show();
opt_proDialog.setCancelable(false);
new Thread(new Runnable()
{
#Override
public void run()
{
//TODO: optimize apps
m_cPowerDrainingApps.killBgRunningProcesses(runningAppsList);
try
{
Thread.sleep(1500);
} catch (InterruptedException e)
{
e.printStackTrace();
}
runOnUiThread(new Runnable()
{
#Override
public void run()
{
opt_proDialog.dismiss();
}
});
}
}).start();
}
listAdaptor.notifyDataSetChanged() is not working ,don't know why ???
What I suggest is to use AsyncTask to do the job. AsyncTask has two good methods for you:
doInBackground: which you can put most of the background tasks in there
onPostExecute : which you can put the logic of what needs to be done when the background task has finished its job.
So your code should look like this:
public class BackgroundTask extends AsyncTask<Void,Void,Void>{
private ListAdapter mAdapter;
public BackgroundTask(ListAdapter adapter)
{
mAdapter = adapter
}
public Void doInBackground (Void... params)
{
//define m_cPowerDrainingApps somewhere
m_cPowerDrainingApps.killBgRunningProcesses(runningAppsList);
try
{
Thread.sleep(1500);
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
public Void onPostExecute (Void... params)
{
//do your UI things
mAdapter.notifyDataSetChanged();
}
}
and then run this with:
new BackgroundTask(listAdapter).execute()
Use a Handler and its postDelayed method to invalidate the list's adapter as follows:
final Handler handler = new Handler()
handler.postDelayed( new Runnable() {
#Override
public void run() {
adapter.notifyDataSetChanged();
handler.postDelayed( this, 60 * 1000 );
}
}, 60 * 1000 );
You must only update UI in the main (UI) thread.

Android Callback Error

I want to make a callback function in the ShipInfoManager to inform the MainActivity to do some action.
If I put onEvent() into Runnable, it runs.
However If I put it like this, it shows an error.
Is there any way to fire the callback after loading data?
Or, is there any way to do the callback like Android's API's LocatioManger's requestLocationUpdates, giving a callback when the data/variables is changed?
Thank you for any replies!
MyCallback Interface:
interface MyCallback {
void callbackCall();
}
ShipInfoManager class:
public class ShipInfoManager {
Context mContext;
public ShipInfoManager(Context _mContext) {
this.mContext = _mContext;
reloadData();
startTimer();
}
MyCallback callback;
void onEvent() {
callback.callbackCall();
}
private void startTimer() {
/* RUN EVERY MIN */
final Handler handler = new Handler();
Timer timer = new Timer();
TimerTask doAsynchronousTask = new TimerTask() {
#Override
public void run() {
handler.post(new Runnable() {
#SuppressWarnings("unchecked")
public void run() {
try {
reloadData();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
};
// TEMP SUSPEND FROM PREVENTING RELOAD //
timer.schedule(doAsynchronousTask, 0, 5000);
}
/* JSON handling and extraction */
private void reloadData() {
//Do sth to reload the data
//After reload, I want to fire the callback
onEvent();
}
}
It looks like you haven't any listeners to your callback and you're not checking for this.
You should replace your ShipInfoManager with this:
public class ShipInfoManager {
public interface MyCallback {
void callbackCall();
}
public void setCustomEventListener(MyCallback eventListener) {
callback = eventListener;
}
Context mContext;
public ShipInfoManager(Context _mContext) {
this.mContext = _mContext;
reloadData();
startTimer();
}
MyCallback callback;
void onEvent() {
// Check if we have listeners
if (callback != null)
callback.callbackCall();
}
private void startTimer() {
/* RUN EVERY MIN */
final Handler handler = new Handler();
Timer timer = new Timer();
TimerTask doAsynchronousTask = new TimerTask() {
#Override
public void run() {
handler.post(new Runnable() {
#SuppressWarnings("unchecked")
public void run() {
try {
reloadData();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
};
// TEMP SUSPEND FROM PREVENTING RELOAD //
timer.schedule(doAsynchronousTask, 0, 5000);
}
/* JSON handling and extraction */
private void reloadData() {
//Do sth to reload the data
//After reload, I want to fire the callback
onEvent();
}
}
Inside your Activity or Fragment you should have something like:
public class MainActivity extends ActionBarActivity implements ShipInfoManager.MyCallback {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ShipInfoManager s = new ShipInfoManager(this);
s.setCustomEventListener(this);
}
#Override
public void callbackCall() {
}
}
I changed my MainActivity like this. It works now.
Thank you for your suggestion and reply!!!!!
public class MainActivity extends ActionBarActivity {
ShipInfoManager mShipInfo;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mShipInfo = new ShipInfoManager(this);
Log.i("Show Ship List Size", String.valueOf(mShipInfo.get_shipsList().size()));
Log.i("Show Ship - 6", String.valueOf(mShipInfo.getShip(6).getShip_name()));
mShipInfo.callback = new ShipInfoManager.MyCallback() {
#Override
public void callbackCall() {
Log.i("Call Back", "it is called");
}
};
}

Categories