I'm trying to check existence of 2000 files in Asynctask.
In the initial execution, it works well.
But if I restart app about 10 times , loading speed slows down.
As I am a beginner developer, I lack understanding of Asynctask.
Please give me some advices.
This is my splash activity
public class SplashActivity extends AppCompatActivity {
getFirstData gfd;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.splash);
gfd = new getFirstData(this, (TextView) findViewById(R.id.textView18));
gfd.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, this);
}
#Override
protected void onDestroy() {
try
{
if (gfd.getStatus() == AsyncTask.Status.RUNNING)
{
gfd.cancel(true);
}
else
{
}
}
catch (Exception e)
{
}
super.onDestroy();
}
}
And this is my asynctask code
public class getFirstData extends AsyncTask<Context,Integer,Void> {
private PowerManager.WakeLock mWakeLock;
private Context context;
private TextView textview;
getFirstData(Context context,TextView tv){
this.context=context;
this.textview=tv;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
PowerManager pm = (PowerManager) this.context.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getClass().getName());
mWakeLock.acquire();
}
#Override
protected Void doInBackground(Context...contexts) {
Database.addDB();
for (int i = 0; i < Database.db_list.size(); i++) {
File filetemp = Database.getFilename(i, ".pdf", Database.db_list);
if (filetemp.exists()) {
Database.db_list.get(i).isDownloaded = true;
}
publishProgress(Database.db_list.size(),i);
}
return null;
}
#Override
protected void onProgressUpdate(Integer... params) {
super.onProgressUpdate(params);
textview.setText("Load("+params[1]*100/params[0]+"%)");
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
Intent intent = new Intent(this.context, MainActivity.class);
this.context.startActivity(intent);
((Activity)this.context).finish();
}
}
AsyncTask cancel method doesn't immediately stop your AsyncTask, instead it'll only 'cancel' after doInBackground completes. (Reference)
Calling this method will result in onCancelled(java.lang.Object) being invoked on the UI thread after doInBackground(java.lang.Object[]) returns. Calling this method guarantees that onPostExecute(Object) is never subsequently invoked, even if cancel returns false, but onPostExecute(Result) has not yet run. To finish the task as early as possible, check isCancelled() periodically from doInBackground(java.lang.Object[]).
If you want your AsyncTask to end as quickly as possible, just make a check every 10 (or whatever value you deem suitable) iterations. Something along the following lines should work.
#Override
protected Void doInBackground(Context...contexts) {
Database.addDB();
for (int i = 0; i < Database.db_list.size(); i++) {
File filetemp = Database.getFilename(i, ".pdf", Database.db_list);
if (filetemp.exists()) {
Database.db_list.get(i).isDownloaded = true;
}
publishProgress(Database.db_list.size(),i);
if (i%10==0 && isCancelled()) {
break;
}
}
return null;
}
I see you actually read the manual! Good work!
While its a good effort, unfortunately, the basic approach really just won't work.
I'm not completely clear on what is making the app slow down. If by "restart" you mean back-arrow and then start from the Desktop, then in is probably because you have many downloads running at once. Note that there is no way to stop your AsyncTask once you start it: cancel doesn't actually do anything, unless you implement it.
Your AsyncTask has all the typical problems with leaking a context (Studio is probably yelling at you about this already: pay attention). There is no reason to believe that the Activity that starts the task is still there when the task completes.
My suggestion is that you separate the state of the app from the Activity that views that state. This approach has lots of names but usually something like ViewModel. The View model is some kind of singleton that only allows users to see the Splash page until its state changes (it has the files downloaded). Then it shows the MainActivity.
Good luck!
Related
I have a splash screen Activity appear for 10 second while waiting this time the Activity check if tables are created and all data are loaded form server
if not it creates tables and load data to DB. every thing is OK but the problem is when loading data take more than 10 seconds the Splash Activity is finished and start another Activity
how i can keep splash activity wait until all data are loaded
here is my code
if(! (checkTables()&&checkData())){
progressDialog.show();
fillSamples();
fillExams();
fillQuestions();
fillSubQuestions();
createProfile();
}
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
progressDialog.dismiss();
Intent studentAccess = new Intent(SplashScreen.this,Samples.class);
startActivity(studentAccess);
finish();
}
},10000);
i am using volley StringRequest and ImageRequest to download data and images from remote server
You can use AsyncTask ,it is better for network calls by creating this inner class
private class Operation extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... params) {
//do what ever operations you want
for (int i = 0; i < 3; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.interrupted();
}
}
return "result";
}
#Override
protected void onPostExecute(String result) {
Intent i = new Intent(SplashScreen.this, MainActivity.class);
i.putExtra("data", result);
startActivity(i);
finish();
}
#Override
protected void onPreExecute() {}
#Override
protected void onProgressUpdate(Void... values) {}
}
and execute the process using private method in splash activity like this
new Operation().execute("");
Network calls should be always called from not main thread.
What you do is that you are making your main thread sleep . Never sleep you main thread. Android will kill your app. Use AsyncTask for network calls (doInBackground) and onPostExectute() do all other things.
I have a Timer in my App that infinitely runs an Animation. like this:
Timer t = new Timer();
t.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
//Running Animation Code
}
});
}
}, 1000, 1000);
Now I realized that this code runs even if user click Back Button of android. if fact it runs in the background and it seems uses a lot of memory.
I need this code run ONLY if user in the app. In fact when user click on Back Button, this Timer goes to end and if user clicks on Home Button, after a while that user doesn't use the App, terminates this Timer.
What I need is to prevent using memory. Because i realized if this codes runs a while, App freezes! I need a normal behavior.
If your Activity is the last element in the BackStack, then it will be put in the background as if you pressed the Home button.
As such, the onPause() method is triggered.
You can thus cancel your animation there.
#Override protected void onPause() {
this.timer.cancel();
}
You should as well start your animation in the onResume() method.
Note that onResume() is also called right after onCreate(); so it's even suitable to start the animation from a cold app start.
#Override protected void onResume() {
this.timer.scheduleAtFixedRate(...);
}
onPause() will be also called if you start another Application from your app (e.g: a Ringtone Picker). In the same way, when you head back to your app, onResume() will be triggered.
There is no need to add the same line of code in onBackPressed().
Also, what's the point in stopping the animation in onStop() or onDestroy()?
Do it in onPause() already. When your are app goes into the background, the animation will already be canceled and won't be using as much memory.
Don't know why I see such complicated answers.
You can do it like this, in onBackPressed() or onDestroy(), whatever suits you.
if (t != null) {
t.cancel();
}
If you need, you can start timer in onResume() and cancel it in onStop(), it entirely depend on you requirement.
If a caller wants to terminate a timer's task execution thread
rapidly, the caller should invoke the timer's cancel method. - Android Timer documentation
You should also see purge and
How to stop the Timer in android?
Disclaimer: This might not be the 100% best way to do this and it might be considered bad practice by some.
I have used the below code in a production app and it works. I have however edited it (removed app specific references and code) into a basic sample that should give you a very good start.
The static mIsAppVisible variable can be called anywhere (via your App class) in your app to check if code should run based on the condition that the app needs to be in focus/visible.
You can also check mIsAppInBackground in your activities that extend ParentActivity to see if the app is actually interactive, etc.
public class App extends Application {
public static boolean mIsAppVisible = false;
...
}
Create a "Parent" activity class, that all your other activities extend.
public class ParentActivity extends Activity {
public static boolean mIsBackPressed = false;
public static boolean mIsAppInBackground = false;
private static boolean mIsWindowFocused = false;
public boolean mFailed = false;
private boolean mWasScreenOn = true;
#Override
protected void onStart() {
applicationWillEnterForeground();
super.onStart();
}
#Override
protected void onStop() {
super.onStop();
applicationDidEnterBackground();
}
#Override
public void finish() {
super.finish();
// If something calls "finish()" it needs to behave similarly to
// pressing the back button to "close" an activity.
mIsBackPressed = true;
}
#Override
public void onWindowFocusChanged(boolean hasFocus) {
mIsWindowFocused = hasFocus;
if (mIsBackPressed && !hasFocus) {
mIsBackPressed = false;
mIsWindowFocused = true;
}
if (!mIsWindowFocused && mFailed)
applicationDidEnterBackground();
if (isScreenOn() && App.mIsAppVisible && hasFocus) {
// App is back in focus. Do something here...
// this can occur when the notification shade is
// pulled down and hidden again, for example.
}
super.onWindowFocusChanged(hasFocus);
}
#Override
public void onResume() {
super.onResume();
if (!mWasScreenOn && mIsWindowFocused)
onWindowFocusChanged(true);
}
#Override
public void onBackPressed() {
// this is for any "sub" activities that you might have
if (!(this instanceof MainActivity))
mIsBackPressed = true;
if (isTaskRoot()) {
// If we are "closing" the app
App.mIsAppVisible = false;
super.onBackPressed();
} else
super.onBackPressed();
}
private void applicationWillEnterForeground() {
if (mIsAppInBackground) {
mIsAppInBackground = false;
App.mIsAppVisible = true;
// App is back in foreground. Do something here...
// this happens when the app was backgrounded and is
// now returning
} else
mFailed = false;
}
private void applicationDidEnterBackground() {
if (!mIsWindowFocused || !isScreenOn()) {
mIsAppInBackground = true;
App.mIsAppVisible = false;
mFailed = false;
// App is not in focus. Do something here...
} else if (!mFailed)
mFailed = true;
}
private boolean isScreenOn() {
boolean screenState = false;
try {
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
screenState = powerManager.isInteractive();
} catch (Exception e) {
Log.e(TAG, "isScreenOn", e);
}
mWasScreenOn = screenState;
return screenState;
}
}
For your use you might want to create a method in your activity (code snippet assumes MainActivity) that handles the animation to call the t.cancel(); method that penguin suggested. You could then in the ParentActivity.applicationDidEnterBackground() method add the following:
if (this instanceof MainActivity) {
((MainActivity) this).cancelTimer();
}
Or you could add the timer to the ParentActivity class and then not need the instanceof check or the extra method.
onStart()
I know that onStart() method is called after onCreate() ( via Activity Lifecycle documentation ), but in my LibGDX project this doesn't happen. I' ve this code:
#Override
protected void onStart()
{
super.onStart();
Gdx.app.debug(TAG, "onStart");
}
but the string in debug terminal appears only if I resume the app from background. I need to do stuff after the initialise of the activity, when it becomes visible.
EDIT: MORE CODE
public class AndroidLauncher extends AndroidApplication {
private final static String TAG = AndroidLauncher.class.getSimpleName();
GoogleResolver googleResolver;
GoogleSignInAccount acct;
private Preferences googlePrefs;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
googleResolver = new GoogleResolverAndroid();
AndroidApplicationConfiguration config = new AndroidApplicationConfiguration();
config.useImmersiveMode = true;
config.useGyroscope = false;
config.useCompass = false;
config.useAccelerometer = false;
GoogleLoginHandler.getInstance().setContext(this.getContext());
GoogleLoginHandler.getInstance().startApiClient();
GameManager.getInstance().listener = googleResolver;
initialize(new MainCrucy(), config);
googlePrefs = Gdx.app.getPreferences(GOOGLE_PREF);
GoogleLoginHandler.getInstance().mGooglePrefs = Gdx.app.getPreferences(GOOGLE_PREF);
}
#Override
protected void onStart()
{
super.onStart();
Gdx.app.debug(TAG, "onStart");
OptionalPendingResult<GoogleSignInResult> opr = Auth.GoogleSignInApi.silentSignIn(GoogleLoginHandler.getInstance().getGoogleApiClient());
if (opr.isDone())
{
Gdx.app.debug(TAG, "Loggato");
GoogleSignInResult result = opr.get();
handleSignInResult(result);
} else {
opr.setResultCallback(new ResultCallback<GoogleSignInResult>() {
#Override
public void onResult(GoogleSignInResult googleSignInResult) {
handleSignInResult(googleSignInResult);
}
});
}
}
This is what I do. But onStart() does anything
How long do you wait after launching you application?
You have to remember that your app can take time to Start. If what you say is true than you wouldn't see Gdx debug - it's still fires at onStart().
So I assume:
you launch an app
you don't want to wait so you minimize that
you open it and onStart() ends and you see debug logs
By the way, could you show more code?
In the meantime look at the life cycle of Android app.
Android lifecycle
You can't use Gdx.app.debug() before the Libgdx application has had a chance to start up. I'm not positive if this happens before onStart() because libgdx doesn't run on the UI thread. Also, you must also use Gdx.app.setLogLevel(Application.LOG_DEBUG) first or calls to Gdx.app.debug() will do nothing.
But you can just use Android's Log.d() instead.
in the past few days I have been trying to figure something but had no luck, I am developing an android game, I have 3 packages for now each with its own purpose:
1 - package for GUI classes.
2 - package that has classes communicates with my wcf service (login/pass DB)
3 - package that holds my asynchronous classes/workers (like a bridge between GUI and SERVICE)
I am not sure if this is even the right approach when it comes to android/java game development, but what I want to achieve is a simple registeration/login in the GUI and when the user is done registering or logining, while the gui talks to the service through the "bridge", a message is displayed for the user like a dialog saying "registering" or "loging in".
Now I would like to hear tips/feedback from more experienced programmers, on how to acomplish this, and if this is the right aproach, and most importantly some examples for this specific case would be really helpfull, I tried to work with the asynctask but I couldn't figure out how to communicate between these 3 seperate packages and return the result from the service back to the gui through the async task.
Take a look at this
public class FindEventsActivity extends Activity {
ProgressDialog pd;
// lots of other code up here
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.clickete);
pd = new ProgressDialog(this);
pd.setMessage("loading");
findViewById(R.id.clickLayout).setOnClickListener(
new OnClickListener() {
#Override
public void onClick(View v) {
new LongOperation().execute("");
pd.show();
}
});
}
private class LongOperation extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... params) {
for (int i = 0; i < 15; i++) {
try {
Thread.sleep(1000); // Simulates your intensive work
// Update your progress if you want
this.publishProgress();
} catch (InterruptedException e) {
return "Failed";
}
}
return "Executed";
}
#Override
protected void onPostExecute(String result) {
// Handle fail or success accordingly
pd.dismiss();
}
#Override
protected void onPreExecute() {
}
#Override
protected void onProgressUpdate(Void... values) {
// Update UI according to your progress
}
}
}
Hope this helps and enjoy your work
I am working on Android AsyncTask, I wish to make a progress bar during my program is loading. Here's how I make it.
A class is declared here...
private ArrayList<String> result1 = new ArrayList<String>(); //class variable
onCreate()
{
Some stuff here...
new ATask().execute();
for (int i = 0; i <result1.size();i++)
{
output = output +result1.get(i) + "\n\n";
}
textView.setText(output);
}
private void do0()
{
ArrayList<Sentence> result = new ArrayList<Sentence>();
ArrayList<String> result2 = new ArrayList<String>();
result = do1("link", true); //just some function I am working
result1 = do2(result,10);//do2 return ArrayList<String>
}
private class ATask extends AsyncTask<String, Void, String>{
private ProgressDialog progress = null;
#Override
protected String doInBackground(String... params) {
do0();
return null;
}
#Override
protected void onCancelled() {
super.onCancelled();
}
#Override
protected void onPostExecute(String result) {
progress.dismiss();
//adapter.notifyDataSetChanged();
super.onPostExecute(result);
}
#Override
protected void onPreExecute() {
progress = new ProgressDialog(ReadWebPage.this);
progress.setMessage("Doing...");
progress.setProgressStyle(ProgressDialog.STYLE_SPINNER);
progress.show();
super.onPreExecute();
}
#Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
}
};
My intention is that, while the progress bar is loading, it will finish the do0() and modify result1, then my oncreate can use that result1 to display in it's TextView. However my TextView is always empty in this setting. So I move the
for (int i = 0; i <result1.size();i++)
{
output = output +result1.get(i) + "\n\n";
}
textView.setText(output);
into the do0() (right after the result1 = do2()), but then the program will crash. I am not familiar with these thread settings, thanks for your help in advance.
You'll be better served with a thread that holds a Handler object that was initialized on the main thread. Using the handler, you can post() little snippets to be executed on a main thread - like update a progress bar. You can do the same Handler trick from the AsyncTask, but IMHO threads are cleaner.
Said snippets should be implemented as Runnables. Feel free to use a nested anonymous class one-liner.
The problem is with the design of your code. AsyncTask happens asynchronously, so as soon as you call execute on your AsyncTask the rest of your onCreate will execute immediately. AsyncTask will essentially run on a new thread and execute in parallel with your Activity.
What I think you want is to set your TextView in the onPostExecute method of your AsyncTask. onPostExecute gets called after doInBackground is finished.
Also, it is important to keep in mind that doInBackground happens on a background thread, so you cannot make changes to your Activity's UI from code within it. onPre/PostExecute run on the UI thread, so you can make UI changes there, but any code within those methods will also block the UI.