AsyncTaskLoader again initialized whenever app is minimized and resumed - android

When app is started it works fine i can swipe left and right without any problem. But as soon as app is been minimized and resumed it again calls the loader and data is been fetched again it results into more no of dots in bottom.
ps: The loader is been called again as dots are in onLoadfinshed.
At first launch
Intial launch
After minimizing and resuming the app
after resuming
package com.example.kaushal.slider;
/**
* Created by kaushal on 25-09-2017.
*/
import android.app.LoaderManager;
import android.content.Loader;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.widget.ImageView;
import android.widget.LinearLayout;
import java.util.List;
public class MainActivity1 extends AppCompatActivity implements LoaderManager.LoaderCallbacks<List<video1>> {
customadap adap;
ViewPager viewPager;
private List<video1> videolist;
int LoaderId = 1;
LinearLayout slidedotepanel;
int dotscount;
ImageView[] dots;
String jsonurl = "";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main1);
viewPager =(ViewPager)findViewById(R.id.viewpager);
slidedotepanel = (LinearLayout)findViewById(R.id.SliderDots);
LoaderManager lm = getLoaderManager();
lm.initLoader(LoaderId,null,this);
}
#Override
public Loader<List<video1>> onCreateLoader(int i, Bundle bundle) {
return new videoLoader1(this,jsonurl);
}
#Override
public void onLoadFinished(Loader<List<video1>> loader, List<video1> videos) {
adap = new customadap(videos,this);
viewPager.setAdapter(adap);
dotscount = adap.getCount();
dots = new ImageView[dotscount];
for(int i = 0;i<dotscount;i++) {
dots[i] = new ImageView(this);
dots[i].setImageDrawable(ContextCompat.getDrawable(this, R.drawable.nonactive_dot));
LinearLayout.LayoutParams layout_linear = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
layout_linear.setMargins(8, 0, 8, 0);
slidedotepanel.addView(dots[i], layout_linear);
}
dots[0].setImageDrawable(ContextCompat.getDrawable(this,R.drawable.active_dot));
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
for(int i =0; i<dotscount;i++){
dots[i].setImageDrawable(ContextCompat.getDrawable(getApplicationContext(), R.drawable.nonactive_dot));
}
dots[position].setImageDrawable(ContextCompat.getDrawable(getApplicationContext(), R.drawable.active_dot));
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
}
#Override
public void onLoaderReset(Loader<List<video1>> loader) {
}
/*
public void getlib(){
StringRequest stringRequest = new StringRequest(jsonurl, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
try {
JSONArray jsonArray = new JSONArray(response);
JSONObject jsonObject = jsonArray.getJSONObject(0);
JSONArray jarray = jsonObject.getJSONArray("videolist");
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
});
}*/
}
Updated: currently i am handling it via deleting the loader on LoadFinished in last line it works fine but won't able to handle orientation changes any better approach appreciated.

In the Activity's onCreate, we should check with the load manager if an existing thread already exists. If it does, we should not call initLoader. I've provided a simple example of how to use AsyncTaskLoader.
import android.os.Bundle;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.AsyncTaskLoader;
import android.support.v4.content.Loader;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.SeekBar;
import java.lang.ref.WeakReference;
public class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Integer> {
private static final String TAG = MainActivity.class.getSimpleName();
private static final int TASK_LOADER_ID = 10;
#Override
protected void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "onCreate. Entered function.");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LoaderManager loaderManager = getSupportLoaderManager();
Loader<Integer> myLoader = loaderManager.getLoader(TASK_LOADER_ID);
if (myLoader != null && MyAsyncTask.isRunning()) {
Log.d(TAG, "onCreate --> Existing loader exists and is running. Re-using it.");
// We use initLoader instead of restartLoader as callbacks
// must be replaced with those from this new activity
loaderManager.initLoader(TASK_LOADER_ID, null, this);
MyAsyncTask.setActivity(new WeakReference<>(this));
showProcess( true);
} else {
Log.d(TAG, "onCreate --> Loader is not active.");
showProcess( false);
}
}
private void showProcess(boolean pShowProcess) {
SeekBar seekBar = (SeekBar) findViewById(R.id.sb_progress);
Button btnStart = (Button) findViewById(R.id.btn_start);
Button btnCancel = (Button) findViewById(R.id.btn_cancel);
if (pShowProcess) {
seekBar.setVisibility(View.VISIBLE);
btnStart.setEnabled(false);
btnCancel.setEnabled(true);
}
else {
seekBar.setVisibility(View.INVISIBLE);
seekBar.setProgress(0);
btnStart.setEnabled(true);
btnCancel.setEnabled(false);
}
}
public void clickStart(View view) {
LoaderManager loaderManager = getSupportLoaderManager();
// Restart existing loader if it exists, otherwise a new one (initLoader) is auto created
loaderManager.restartLoader(TASK_LOADER_ID, null, this);
}
// A graceful attempt to stop the loader
public void clickCancel(View view) {
Loader<Integer> myLoader = getSupportLoaderManager().getLoader(TASK_LOADER_ID);
if (myLoader != null) {
MyAsyncTask.cancelled(true);
}
}
#Override
public Loader<Integer> onCreateLoader(int pID, Bundle pArgs) {
Log.d(TAG, "onCreateLoader. Entered function.");
showProcess(true);
return new MyAsyncTask(this);
}
#Override
public void onLoadFinished(Loader<Integer> pLoader, Integer pResult) {
Log.d(TAG, "onLoadFinished --> Number of items processed = " + pResult);
showProcess( false);
getLoaderManager().destroyLoader(TASK_LOADER_ID);
}
#Override
public void onLoaderReset(Loader<Integer> pLoader) { }
private static class MyAsyncTask extends AsyncTaskLoader<Integer> {
private final static int SLEEP_TIME = 10 * 10; // 100 milliseconds
static WeakReference<MainActivity> aActivity;
private static boolean isRunning = false, cancelled = true;
private Integer aResult; // Holds the results once the task is finished or cancelled
MyAsyncTask(MainActivity pActivity) {
super(pActivity);
aActivity = new WeakReference<>(pActivity);
}
synchronized static void setActivity(WeakReference<MainActivity> pActivity) {
aActivity = pActivity;
}
synchronized static void cancelled(boolean pCancelled) {
cancelled = pCancelled;
}
static boolean isRunning() {
return isRunning;
}
#Override
protected void onStartLoading() {
Log.d(TAG, "onStartLoading. Entered function. cancelled = " + cancelled);
super.onStartLoading();
if (aResult != null) {
deliverResult(aResult);
return;
}
if (!isRunning) { // Don't start a new process unless explicitly initiated by clickStart
Log.d(TAG, "onStartLoading --> No existing process running, so we can start a new one.");
forceLoad();
}
}
#Override
public Integer loadInBackground() {
Log.d(TAG, "loadInBackground. Entered function.");
isRunning = true;
cancelled = false;
int i;
for (i = 1; i < 100 && !cancelled; i++) {
try {
Thread.sleep(SLEEP_TIME);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
if (aActivity.get() != null) {
SeekBar seekBar = (SeekBar) aActivity.get().findViewById(R.id.sb_progress);
seekBar.setProgress(i);
}
if (i % 15 == 0) {
Log.d(TAG, "Process running with i = " + i);
}
}
isRunning = false;
aResult = i;
return aResult;
}
}
}
activity_main.xml is given below.
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.snoopy.loadertest.MainActivity">
<SeekBar
android:id="#+id/sb_progress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="#+id/btn_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="clickStart"
android:text="Start Process"
app:layout_constraintBottom_toTopOf="#id/sb_progress"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
When the 'Start Button' is clicked, it makes the SeekBar visible and starts the AsyncTaskLoader in a separate thread. While the task is running, you can rotate the device and send it to the background. On restarting the app, onCreate checks if the task exists. If so, it updates the new Activity object to the task so that the new progress bar can be updated by the task. I've tested this and it works. This should give you a better idea on how to use AsyncTaskLoader and manage resume.

Related

realm large RealmResults block UI thread when data is modified

I've been trying to test the performance of Realm with large datasets. It seems to perform really well inserting and with queries that result in moderate resultset sizes but as soon as there is a result set that is large it starts impacting the main thread even though the actual writes are running async. I've written a test activity that shows the issue below. A few notes:
Attendee is a model with a primary key of "name" and an indexed integer field of "age".
The view has a text field showing the count, a button that calls reloadData on click, and an element that is interactive (I used a slider) to see skipped frames when using it.
If you change the query for the list so that it's causes a smaller result set (instead of less then change to equalsTo) then the issue goes away.
Is this a bug or am I doing something wrong?
package com.test.ui;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.test.R;
import com.test.model.Attendee;
import io.realm.OrderedRealmCollection;
import io.realm.Realm;
import io.realm.RealmAsyncTask;
import io.realm.RealmRecyclerViewAdapter;
import io.realm.RealmResults;
public class LoginActivity extends AppCompatActivity {
private static final int RECORD_COUNT = 200000;
private static final int TRANSACTION_SIZE = 1000;
private RealmAsyncTask mTask = null;
private RecyclerView mAttendeeList;
private TextView mCountText;
private Button mButton;
private Handler handler;
private Realm mRealm;
private RealmResults<Attendee> mActualList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
mRealm = Realm.getDefaultInstance();
mCountText = (TextView) findViewById(R.id.count_text);
mButton = (Button) findViewById(R.id.button);
handler = new Handler(Looper.getMainLooper());
setCountText((int) mRealm.where(Attendee.class).count());
mActualList = mRealm.where(Attendee.class).lessThan("age", 20).findAllAsync();
mRealm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
realm.deleteAll();
setCountText(0);
}
}, new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
mButton.setEnabled(true);
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(Throwable error) {
String text = "Error deleting";
Log.e("ANRTEST", text, error);
Toast.makeText(LoginActivity.this, text, Toast.LENGTH_LONG).show();
}
});
}
private void setCountText(int size) {
mCountText.setText(String.format("Count: %s", size));
}
#Override
protected void onDestroy() {
mActualList = null;
if(mTask != null && !mTask.isCancelled()) {
mTask.cancel();
mTask = null;
}
if(mRealm != null && !mRealm.isClosed()) {
mRealm.close();
}
super.onDestroy();
}
public void loadData(final TimerUtil t) {
Realm.Transaction.OnError onError = new Realm.Transaction.OnError() {
#Override
public void onError(Throwable error) {
mTask = null;
Toast.makeText(LoginActivity.this, "Finished should show now", Toast.LENGTH_LONG).show();
}
};
Realm.Transaction.OnSuccess onSuccess = new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
mTask = null;
int count = (int) mRealm.where(Attendee.class).count();
if (count >= RECORD_COUNT) {
Log.v("ANRTEST", String.format("Finished in %s seconds", t.currentElapsed() / 1000));
mButton.setEnabled(false);
} else {
handler.postDelayed(new Runnable() {
#Override
public void run() {
loadData(t);
}
}, 300);
}
setCountText(count);
}
};
mTask = mRealm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
int innerStart = (int) realm.where(Attendee.class).count();
int i = 0;
while (i < TRANSACTION_SIZE && i + innerStart < RECORD_COUNT) {
Attendee attendee = new Attendee();
int innerValue = innerStart + i;
attendee.name = "name " + (innerValue + 1);
attendee.age = innerValue % 50;
realm.insert(attendee);
i++;
}
Log.v("ANRTEST", String.format("Checkpoint %s (%s seconds)", Math.min(innerStart + i, RECORD_COUNT), t.currentElapsed() / 1000));
}
}, onSuccess, onError);
}
public void reloadData(View view) {
//Setup start of process
mButton.setEnabled(false);
final TimerUtil t = TimerUtil.start();
loadData(t);
}
public static class TimerUtil {
private final long mStartTime;
public TimerUtil(long startTime) {
mStartTime = startTime;
}
public static TimerUtil start() {
return new TimerUtil(System.currentTimeMillis());
}
public long currentElapsed() {
return System.currentTimeMillis() - mStartTime;
}
}
}

Cancel and start AsyncTask again in Android

I'm having trouble starting a task after it has been cancelled the first time. It will run when entering the view, and starting the task, then cancel works when the fragment is first destroyed. But when re-entering the view, the AsyncTask will no longer run.
Is it possible some class state that needs to be cleaned up? Or do I need to remove the Fragment with the AsyncTask from the back stack?
Here is my code below:
package com.emijit.lighteningtalktimer;
import android.content.ContentUris;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.emijit.lighteningtalktimer.data.Timer;
import com.emijit.lighteningtalktimer.data.TimerContract.TimerEntry;
public class RunTimerFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {
private static final String LOG_TAG = RunTimerFragment.class.getSimpleName();
private View rootView;
private Uri mUri;
private long mTimerId = 0;
private Timer mTimer;
private RunTimerTask mRunTimerTask;
private static final int DETAIL_LOADER = 0;
public static String URI = "URI";
public RunTimerFragment() {
}
static class ViewHolder {
TextView intervals;
TextView timerSeconds;
TextView timerMinutes;
TextView timerHours;
public ViewHolder(View view) {
intervals = (TextView) view.findViewById(R.id.run_timer_intervals);
timerSeconds = (TextView) view.findViewById(R.id.timer_seconds);
timerMinutes = (TextView) view.findViewById(R.id.timer_minutes);
timerHours = (TextView) view.findViewById(R.id.timer_hours);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_run_timer, container, false);
ViewHolder viewHolder = new ViewHolder(rootView);
rootView.setTag(viewHolder);
Bundle arguments = getArguments();
if (arguments != null) {
mUri = arguments.getParcelable(URI);
if (mUri != null) {
mTimerId = ContentUris.parseId(mUri);
Log.d(LOG_TAG, "mTimerId: " + mTimerId);
}
}
return rootView;
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
getLoaderManager().initLoader(DETAIL_LOADER, null, this);
}
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
if (mTimerId != 0) {
return new CursorLoader(
getActivity(),
TimerEntry.CONTENT_URI,
null,
TimerEntry._ID + " = ?",
new String[] { Long.toString(mTimerId) },
null
);
}
return null;
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
if (cursor != null && cursor.moveToFirst()) {
mTimer = new Timer(cursor);
}
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
}
public void startTimer() {
mRunTimerTask = new RunTimerTask();
mRunTimerTask.execute(mTimer);
}
#Override
public void onPause() {
super.onPause();
mRunTimerTask.cancel(true);
}
private class RunTimerTask extends AsyncTask<Timer, Integer, Long> {
private final String LOG_TAG = RunTimerTask.class.getSimpleName();
Timer mTimer;
int mCurrentSeconds = 0;
int mCurrentIntervals = 0;
#Override
protected Long doInBackground(Timer... params) {
Log.d(LOG_TAG, "doInBackground");
mTimer = params[0];
while (mTimer.getIntervals() > mCurrentIntervals) {
try {
Thread.sleep(1000);
mCurrentSeconds++;
publishProgress(mCurrentSeconds);
} catch (InterruptedException e) {
Log.d(LOG_TAG, e.toString());
}
}
return (long) mCurrentIntervals;
}
#Override
protected void onProgressUpdate(Integer... values) {
// do stuff
}
#Override
protected void onPostExecute(Long aLong) {
super.onPostExecute(aLong);
Log.d(LOG_TAG, "onPostExecute");
}
}
}
I think the issue might be in the catch block. Not sure about the mTimer.getIntervals() because it's from third party.
When you cancel the task. The InterruptedException will be caught in the task thread. Then your loop will still keep going because you didn't return or break the loop.
Since all AsyncTask will be queued up in one thread pool of size 1, even if you start another AsyncTask, it will still be blocked.
In your sample, I cannot see any call to startTimer(). Should you be overiding onResume() and calling startTimer() in there?

Getting Correct Value from Bundle after replacing fragment holding AsyncTask

The issue I am having is that I have created a simple count down timer, using AsyncTasks and using the SetRetainInstance(true) to ensure that even with orientation change the counter updates on the ui.
The issue is that I have an editText that gives me the values for the timer and then should pass them on to the Task to count Down. I must be missing something somewhere because, I cannot seem to get the new value.
This is the code that I am using as the Fragment:
package com.example.app;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.SystemClock;
import android.support.v4.app.Fragment;
import android.util.Log;
public class TaskFragment extends Fragment {
private static final String TAG = TaskFragment.class.getSimpleName();
String i;
int counter;
Bundle bundle;
static interface TaskCallbacks {
public void onPreExecute();
public void onProgressUpdate(int timer);
public void onCancelled();
public void onPostExecute();
}
private TaskCallbacks mCallbacks;
private DummyTask mTask;
private boolean mRunning;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (!(activity instanceof TaskCallbacks)) {
throw new IllegalStateException("Activity must implement the TaskCallbacks interface.");
}
mCallbacks = (TaskCallbacks) activity;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
bundle=getArguments();
i = bundle.getString("SecValue");
Log.i("VertygoEclypse - TaskFragment-onCreate", i);
counter=Integer.parseInt(i);
}
#Override
public void onDestroy() {
super.onDestroy();
cancel();
}
public void start() {
if (!mRunning) {
bundle=getArguments();
i=bundle.getString("SecValue");
mTask = new DummyTask();
Log.i("VertygoEclypse - TaskFragment - start", i);
mTask.execute();
mRunning = true;
} else{
mTask.cancel(true);
}
}
public void cancel() {
if (mRunning) {
mTask.cancel(true);
mTask = null;
mRunning = false;
}
}
public boolean isRunning() {
return mRunning;
}
private class DummyTask extends AsyncTask<Void, Integer, Void> {
#Override
protected void onPreExecute() {
mCallbacks.onPreExecute();
mRunning = true;
counter=Integer.parseInt(i);
Log.i("Vertygo Eclypse - AsyncTask - onPreExecute", i);
}
#Override
protected Void doInBackground(Void... ignore) {
do {
publishProgress(counter);
SystemClock.sleep(1000);
counter=counter-1;
if(isCancelled()){
mTask.cancel(true);
break;
}
} while (counter>0);
return null;
}
#Override
protected void onProgressUpdate(Integer... timer) {
mCallbacks.onProgressUpdate(timer[0]);
}
#Override
protected void onCancelled() {
mCallbacks.onCancelled();
mRunning = false;
}
#Override
protected void onPostExecute(Void ignore) {
mCallbacks.onPostExecute();
mRunning = false;
}
}
}
That being said I have Log.i set up at a number of spots the onCreate, the start, the pre-execute and the post-execute.
The following excerpt from the logcat shows that some of the values show the entered text, but the start and preexecute are holding the old values:
01-17 17:37:12.383 10261-10261/com.example.app I/VertygoEclypse - replaceFrag﹕ 35
01-17 17:37:12.383 10261-10261/com.example.app I/VertygoEclypse - replaceFrag﹕ 35
01-17 17:37:12.383 10261-10261/com.example.app I/Vertygo Eclypse - MainActivity - replaceFrag﹕ 35
01-17 17:37:12.403 10261-10261/com.example.app I/VertygoEclypse - TaskFragment-onCreate﹕ 35
01-17 17:37:17.247 10261-10261/com.example.app I/VertygoEclypse - TaskFragment - start﹕ 15
01-17 17:37:17.259 10261-10261/com.example.app I/Vertygo Eclypse - AsyncTask - onPreExecute﹕ 15
I am also using Bundle to transfer the text from an EditText to the fragment and using getString() to get the value based on a key.
Below is the MainActivity so far.
package com.example.app;
import android.annotation.TargetApi;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import java.util.concurrent.TimeUnit;
public class MainActivity extends FragmentActivity implements TaskFragment.TaskCallbacks {
private static final String TAG = MainActivity.class.getSimpleName();
private static final String KEY_CURRENT_PROGRESS = "current_progress";
private static final String KEY_PERCENT_PROGRESS = "percent_progress";
private static final String TIME_COUNT = "time_count";
private TaskFragment mTaskFragment;
private ProgressBar mProgressBar;
private TextView mPercent, tv1;
private Button mButton;
private EditText secentered;
public String sample;
Bundle bundl;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv1 = (TextView) findViewById(R.id.textView1);
secentered = (EditText) findViewById(R.id.valueentered);
mButton = (Button) findViewById(R.id.task_button);
initialfrag();
mButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if (mTaskFragment.isRunning()) {
mButton.setText("Start");
mTaskFragment.cancel();
replaceFrag();
} else {
mButton.setText("Cancel");
mTaskFragment.start();
}
}
});
if (savedInstanceState != null) {
tv1.setText(savedInstanceState.getString(TIME_COUNT));
}
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(TIME_COUNT, tv1.getText().toString());
}
#Override
public void onPreExecute() {
mButton.setText(getString(R.string.cancel));
Toast.makeText(this, R.string.task_started_msg, Toast.LENGTH_SHORT).show();
}
#Override
public void onProgressUpdate(int timer) {
long timelong = timer*1000;
String tval = getDurationBreakdown(timelong);
tv1.setText(tval);
}
#Override
public void onCancelled() {
mButton.setText(getString(R.string.start));
tv1.setText("0 seconds");
mTaskFragment.cancel();
replaceFrag();
Toast.makeText(this, R.string.task_cancelled_msg, Toast.LENGTH_SHORT).show();
}
#Override
public void onPostExecute() {
mButton.setText(getString(R.string.start));
tv1.setText("Completed");
mTaskFragment.cancel();
replaceFrag();
Toast.makeText(this, R.string.task_complete_msg, Toast.LENGTH_SHORT).show();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_trigger_config_change:
recreate();
return true;
}
return super.onOptionsItemSelected(item);
}
public static String getDurationBreakdown(long secondstobreak) {
if(secondstobreak < 0)
{
throw new IllegalArgumentException("Duration must be greater than zero!");
}
long hours = TimeUnit.MILLISECONDS.toHours(secondstobreak);
secondstobreak-=TimeUnit.HOURS.toMillis(hours);
long minutes = TimeUnit.MILLISECONDS.toMinutes(secondstobreak);
secondstobreak-=TimeUnit.MINUTES.toMillis(minutes);
long seconds = TimeUnit.MILLISECONDS.toSeconds(secondstobreak);
secondstobreak-=TimeUnit.SECONDS.toMillis(seconds);
StringBuilder sb = new StringBuilder(64);
if(hours<10){
sb.append("0"+hours);
}else {
sb.append(hours);
}
sb.append(" : ");
if(minutes<10){
sb.append("0"+minutes);
}else{
sb.append(minutes);
}
sb.append(" : ");
if(seconds<10){
sb.append("0"+seconds);
} else {
sb.append(seconds);
}
sb.append(" remaining");
return (sb.toString());
}
public void replaceFrag(){
Bundle bundle = new Bundle();
String tester2 = secentered.getText().toString();
Log.i("VertygoEclypse - replaceFrag", tester2);
if(tester2.matches("")){
bundle.putString("SecValue", "15");
} else {
Log.i("VertygoEclypse - replaceFrag", tester2);
bundle.putString("SecValue", tester2);
}
FragmentTransaction rfm = getSupportFragmentManager().beginTransaction();
rfm.remove(mTaskFragment);
rfm.detach(mTaskFragment);
TaskFragment mTaskFragment = new TaskFragment();
Log.i("Vertygo Eclypse - MainActivity - replaceFrag", tester2);
mTaskFragment.setArguments(bundle);
rfm.add(mTaskFragment, "task").commit();
}
public void initialfrag(){
bundl = new Bundle();
String tester = secentered.getText().toString();
Log.i("VertygoEclypse - initialFrag", tester);
if(tester.matches("")){
bundl.putString("SecValue", "15");
} else{
Log.i("VertygoEclypse - initialFrag", tester);
bundl.putString("SecValue", tester);
}
FragmentManager fm = getSupportFragmentManager();
mTaskFragment = (TaskFragment) fm.findFragmentByTag("task");
if (mTaskFragment == null) {
mTaskFragment = new TaskFragment();
mTaskFragment.setArguments(bundl);
fm.beginTransaction().add(mTaskFragment, "task").commit();
}
}
}
I know that the value is being passed to the fragment, however I am not certain if the AsyncTask or the Fragment is being replaced or refreshed. What I would like Ideally is to have the AsyncTask killed and a new one created using the new value from the Bundle.
Any help would be greatly appreciated.
Ok all it seems that I have stumbled upon the answer. the issue was th int counter, validcounter; it seems that without the static in front of it, the variable creates it's own instance of it. so with the following static int counter, validcounter; the issue is resolved.
thanks for the sounding board.
regards
cchinchoy

Show Dialog depending on ViewPager state

I have a ViewPager, and lets imagine, that i am on page 0.
I have a button on this page, and on this button click, i want to show a dialog, and change page to the page 1.
When my page changes to page 1, i want the dialog to dissapear.
When i did it, i didn't see the dialog, it was appearing and dissapearing when page was changed, but i am sure, that i have 1000ms delay between this actions.
Please help, how can i show the dialog?
package com.example.ViewPagerDialog;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
public class MyActivity extends Activity {
private int currentPage;
/**
* Called when the activity is first created.
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final MyViewPager viewPager = (MyViewPager) findViewById(R.id.view_pager);
final Button leftSwitcher = (Button) findViewById(R.id.left_switcher);
final Button rightSwitcher = (Button) findViewById(R.id.right_switcher);
final ProgressDialog progressDialog = new ProgressDialog(this);
leftSwitcher.setVisibility(View.GONE);
progressDialog.setTitle("Wait...");
viewPager.setAdapter(new MyPagerAdapter(this));
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
public void onPageScrolled(int i, float v, int i2) {
}
public void onPageSelected(int i) {
progressDialog.dismiss();
currentPage = i;
if (i == 0) {
leftSwitcher.setVisibility(View.GONE);
} else if (i == 1) {
leftSwitcher.setVisibility(View.VISIBLE);
rightSwitcher.setVisibility(View.VISIBLE);
} else if (i == 2) {
rightSwitcher.setVisibility(View.GONE);
}
}
public void onPageScrollStateChanged(int i) {
}
});
leftSwitcher.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
progressDialog.show();
sleepThread();
viewPager.setCurrentItem(currentPage - 1);
}
});
rightSwitcher.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
progressDialog.show();
sleepThread();
viewPager.setCurrentItem(currentPage + 1);
}
});
}
private void sleepThread() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private class MyPagerAdapter extends PagerAdapter {
View[] views = new View[3];
public MyPagerAdapter(Context context) {
TextView view1 = new TextView(context);
TextView view2 = new TextView(context);
TextView view3 = new TextView(context);
view1.setText("View 1");
view2.setText("View 2");
view3.setText("View 3");
views[0] = view1;
views[1] = view2;
views[2] = view3;
}
#Override
public int getCount() {
return views.length;
}
#Override
public boolean isViewFromObject(View view, Object o) {
return (view.equals(o));
}
#Override
public Object instantiateItem(ViewGroup collection, int position) {
collection.addView(views[position]);
return views[position];
}
#Override
public void destroyItem(android.view.ViewGroup container, int position, java.lang.Object object) {
container.removeView(views[position]);
}
}
}
First of all, never block the UI thread with Thread.sleep() like you do. By using Thread.sleep() you'll basically set the show command for the dialog(which will happen after you return from the onCLick() method), sleep one second(and your app will freeze) and then set the page on the ViewPager which will trigger the listener, dismissing the dialog. Instead you could use a Handler to
private Handler mHandler = new Handler();
// in the onClick method
progressDialog.show();
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
viewPager.setCurrentItem(currentPage - 1);
}
}, 1000);

Need help fixing my class: 'This Handler class should be static or leaks might occur'

I created a simple crossfade image class for use in my app. But... i have an error i can't fix due to lack of knowledge. I found this post This Handler class should be static or leaks might occur: IncomingHandler but i have no clue how to fix this in my class. It is a very straightforward class. Create, initialize and start to use it.
I hope someone can help me fix this warning and while we are at it, some hints and tips on my code or comments are very welcome too ;)
MainActivity.java
package com.example.crossfadeimage;
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
Xfade xfade = new Xfade();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// INIT(current activity, image id's, time between fades, fade speed)
xfade.init(this, new int[]{ R.id.image1, R.id.image2, R.id.image3 }, 3000, 500);
xfade.start();
}
}
Xfade.java
package com.example.crossfadeimage;
import android.app.Activity;
import android.os.Handler;
import android.os.Message;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.widget.ImageView;
public class Xfade {
private Activity activity;
// Handler
private Handler handlerTimer = new Handler();
private static final int UPDATE_STUFF_ON_DIALOG = 999;
private int updateTime;
private Animation fadeIn;
private Animation fadeOut;
public int[] xfadeImages;
public int xfadeCounter = 1;
public void init(Activity thisActivity, int[] images, int time,
int animationSpeed) {
activity = thisActivity;
xfadeImages = images;
updateTime = time;
// Set Animations
fadeIn = new AlphaAnimation(0, 1);
fadeIn.setDuration(animationSpeed);
fadeOut = new AlphaAnimation(1, 0);
fadeOut.setDuration(animationSpeed);
// Hide all images except the first
// which is always visible
for (int image = 1; image < xfadeImages.length; image++) {
ImageView thisImage = (ImageView) activity
.findViewById(xfadeImages[image]);
thisImage.setVisibility(4);
}
}
public void start() {
handlerTimer.removeCallbacks(taskUpdateStuffOnDialog);
handlerTimer.postDelayed(taskUpdateStuffOnDialog, updateTime);
}
private Runnable taskUpdateStuffOnDialog = new Runnable() {
public void run() {
Message msg = new Message();
msg.what = UPDATE_STUFF_ON_DIALOG;
handlerEvent.sendMessage(msg);
// Repeat this after 'updateTime'
handlerTimer.postDelayed(this, updateTime);
}
};
private Handler handlerEvent = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_STUFF_ON_DIALOG: {
crossFade();
}
break;
default: {
super.handleMessage(msg);
}
break;
}
}
};
public void crossFade() {
if (xfadeCounter == 0) {
ImageView lastImage = (ImageView) activity
.findViewById(xfadeImages[xfadeImages.length - 1]);
lastImage.setVisibility(4);
}
if (xfadeCounter < xfadeImages.length) {
ImageView thisImage = (ImageView) activity
.findViewById(xfadeImages[xfadeCounter]);
thisImage.setVisibility(0);
thisImage.startAnimation(fadeIn);
xfadeCounter++;
} else {
// Hide all images except the first
// before fading out the last image
for (int image = 1; image < xfadeImages.length; image++) {
ImageView thisImage = (ImageView) activity
.findViewById(xfadeImages[image]);
thisImage.setVisibility(4);
}
// Fadeout
ImageView lastImage = (ImageView) activity
.findViewById(xfadeImages[xfadeImages.length - 1]);
lastImage.startAnimation(fadeOut);
// LastImage is faded to alpha 0 so it doesn't have to be hidden
// anymore
xfadeCounter = 1;
}
}
}
This allows you to modify views and have your handler set to static.
package com.testing.test;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.TextView;
public class MainActivity extends Activity implements Runnable {
private static final int THREAD_RESULT = 1000;
private TextView mTextView;
private static Handler mHandler;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView) findViewById(R.id.text);
}
#Override
protected void onResume() {
super.onResume();
mHandler = new CustomHandler(this);
new Thread(this).start();
}
#Override
public void run() {
// Do some threaded work
// Tell the handler the thread is finished
mHandler.post(new Runnable() {
#Override
public void run() {
mHandler.sendEmptyMessage(THREAD_RESULT);
}
});
}
private class CustomHandler extends Handler {
private MainActivity activity;
public CustomHandler(MainActivity activity) {
super();
this.activity = activity;
}
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case THREAD_RESULT:
activity.mTextView.setText("Success!");
break;
}
}
}
}

Categories

Resources