I'm stuck with a memory leak that I cannot fix. I identified where it occurs, using the MemoryAnalizer but I vainly struggle to get rid of it. Here is the code:
public class MyActivity extends Activity implements SurfaceHolder.Callback {
...
Camera.PictureCallback mPictureCallbackJpeg = new Camera.PictureCallback() {
public void onPictureTaken(byte[] data, Camera c) {
try {
// log the action
Log.e(getClass().getSimpleName(), "PICTURE CALLBACK JPEG: data.length = " + data);
// Show the ProgressDialog on this thread
pd = ProgressDialog.show(MyActivity.this, "", "Préparation", true, false);
// Start a new thread that will manage the capture
new ManageCaptureTask().execute(data, c);
}
catch(Exception e){
AlertDialog.Builder dialog = new AlertDialog.Builder(MyActivity.this);
...
dialog.create().show();
}
}
class ManageCaptureTask extends AsyncTask<Object, Void, Boolean> {
protected Boolean doInBackground(Object... args) {
Boolean isSuccess = false;
// initialize the bitmap before the capture
((myApp) getApplication()).setBitmapX(null);
try{
// Check if it is a real device or an emulator
TelephonyManager telmgr = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
String deviceID = telmgr.getDeviceId();
boolean isEmulator = "000000000000000".equalsIgnoreCase(deviceID);
// get the bitmap
if (isEmulator) {
((myApp) getApplication()).setBitmapX(BitmapFactory.decodeFile(imageFileName));
} else {
((myApp) getApplication()).setBitmapX(BitmapFactory.decodeByteArray((byte[]) args[0], 0, ((byte[])args[0]).length));
}
((myApp) getApplication()).setImageForDB(ImageTools.resizeBmp(((myApp) getApplication()).getBmp()));
// convert the bitmap into a grayscale image and display it in the preview
((myApp) getApplication()).setImage(makeGrayScale());
isSuccess = true;
}
catch (Exception connEx){
errorMessageFromBkgndThread = getString(R.string.errcapture);
}
return isSuccess;
}
protected void onPostExecute(Boolean result) {
// Pass the result data back to the main activity
if (MyActivity.this.pd != null) {
MyActivity.this.pd.dismiss();
}
if (result){
((ImageView) findViewById(R.id.apercu)).setImageBitmap(((myApp) getApplication()).getBmp());
((myApp) getApplication()).setBitmapX(null);
}
else{
// there was an error
ErrAlert();
}
}
}
};
private void ErrAlert(){
// notify the user about the error
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
...
dialog.create().show();
}
}
The activity is terminated on a button click, like this:
Button use = (Button) findViewById(R.id.use);
use.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(MyActivity.this, NextActivity.class);
intent.putExtra("dbID", "-1");
intent.putExtra("category", category);
((myApp) getApplication()).setBitmapX(null);
MyActivity.this.startActivity(intent);
MyActivity.this.finish();
}
});
MemoryAnalyzer indicated the memory leak at:
((myApp) getApplication()).setBitmapX(BitmapFactory.decodeByteArray((byte[]) args[0], 0, ((byte[])args[0]).length));
I am grateful for any suggestion, thank you in advance.
Is your thread garbage collected after onPostExecute is called or is it still in the memory?
A Async Task will not be canceled or destroyed at the moment the activity is dismissed. If your thread is more or less lightweight and finishes after a small time, just keep it running and add a MyActivity.this.isFinishing() clause in the onPostExecute() method.
Your Task stores a implicit reference to your Activity MyActivity.this because it is a private class inside the activity. This means that your Activity will not be garbage collected until the task exits.
You can try below code snippet
protected void onPostExecute(Boolean result) {
if(YourActivity.this.isFinished()){
//to smomething here
}
}
Related
I have network operation inside a thread which in oncreate() based on network response I need to process the next step but the thread is running after the activity life cycle.
I called networkRequest() in oncreate() in activity
private void networkRequest() {
final String[] resp = new String[1];
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
try {
resp[0] = AttemptingUploadCheckList.getJsonObj(url);
JSONObject response = new JSONObject(resp[0]);
if (response != null) {
version_code = response.getInt("version_code");
recommended_update = response.getBoolean("recommended_update");
forced_update = response.getBoolean("forced_update");
}
if (recommended_update) {
recomendUpadate();
} else if (forced_update)
onUpdateNeeded(url);
else {
Intent intent = new Intent(SplashActivity.this, LoginActivity.class);
startActivity(intent);
finish();
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
thread.start();
}
Thread is not bound with the activity. It's not running with the main thread.
Android said if you want to perform any long running tasks like api call, data from database then you need to use the AsyncTask or the Service.
In your case, you can use the AsycnTask for the fetching data.
class MyAsync extends AsyncTask<Void, Void, Void>{
final String[] resp = new String[1];
JSONObject response;
#Override
protected void onPreExecute() {
super.onPreExecute();
// Show Progress Dialog
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
// Hide Progress Dialog
if (response != null) {
version_code = response.getInt("version_code");
recommended_update = response.getBoolean("recommended_update");
forced_update = response.getBoolean("forced_update");
}
if (recommended_update) {
recomendUpadate();
} else if (forced_update)
onUpdateNeeded(url);
else {
Intent intent = new Intent(SplashActivity.this, LoginActivity.class);
startActivity(intent);
finish();
}
}
#Override
protected Void doInBackground(Void... voids) {
try {
resp[0] = AttemptingUploadCheckList.getJsonObj(url);
response = new JSONObject(resp[0]);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
For executing the above AsynTask
private void networkRequest() {
new MyAsync().execute();
}
Thread does not care about Activity or any other Component's lifecycle Except the Process in which it is Running.
You need to check for state of component yourself.
I can provide some example code but i really do not understand what exactly you are trying to do .
Considering you are making a network request there. Java thread individually is hard to handle in such cases considering the fact that after response we need to move on to Main thread to update the UI. So i highly recommend you should use a Network API Library probably RetroFit .
You can check state of the Component like isFinishing() in Activity .
Every time I`m trying to finish an activity inside of a timer method, the activity comes back alive over and over again.
I running this activity:
public class PlayerNoAdmin extends ActionBarActivity {
Timer myTimer; boolean isAdmin;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_player_no_admin);
Intent oldIntent = getIntent();
if (oldIntent != null && oldIntent.hasExtra("THE_LIST")){
songs = oldIntent.getParcelableArrayListExtra("THE_LIST");
id = oldIntent.getIntExtra("ID",0);
listId = oldIntent.getIntExtra("LIST_ID",0);
isAdmin = oldIntent.getBooleanExtra("IS_ADMIN",false);
}
//update the list every k seconds
myTimer = new Timer();
myTimer.schedule(new TimerTask() {
#Override
public void run() {
TimerMethod();
}
}, 0, k_time2Update);
}
private void TimerMethod() {
//This method is called directly by the timer
//and runs in the same thread as the timer.
//We call the method that will work with the UI
//through the runOnUiThread method.
this.runOnUiThread(Timer_Tick);
}
private Runnable Timer_Tick = new Runnable() {
public void run() {
//Here check for update in the list every 30 seconds and send the new location
String url = getRunUrl();
new TaskMusicPlay().execute(url);
}
};
private class TaskMusicPlay extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... params) {
String jsonResult = null;
try {
String url = params[0];
TestMain client = new TestMain();
jsonResult = client.doGetRequest(url);
} catch (IOException e) {
e.printStackTrace();
}
return jsonResult;
}
#Override
protected void onPostExecute(String aVoid) {
super.onPostExecute(aVoid);
checkIfNew(aVoid);
}
private void checkIfNew(String result) {
try {
JSONObject object = new JSONObject(result);
String temp = object.getJSONObject("info").getString("isAdmin");
isAdmin = (temp.equals("true"));
if (isAdmin) {
Intent intent = new Intent(getApplication(),YouTubePlayer.class);
intent.putExtra("THE_LIST", songs);
intent.putExtra("ID", id);
intent.putExtra("LIST_ID",listId);
intent.putExtra("IS_ADMIN",isAdmin);
startActivity(intent);
finish();
}
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
}
At the end, I succeeded to move to the YouTubePlayer activity, but every few seconds the app returns to the code of this activity (and then executes again the startActivity call and goes back to YouTubePlayer) and that's going on and on.
Your Timer is periodically calling the player to start over and over again.
You must make a cancel() call to the Timer if it is no longer needed so you prevent it from holding a reference for your activity and thus preventing from being removed from the backstack and GC.
http://developer.android.com/reference/java/util/Timer.html
And your Timer is not running on the same thread as it's code because the timer thread iis another Thread and the Code in the Timer is running on UI. You can check it out by adding some logs in the Timer's run method outside of the runOnUIThread() and inside of it.
i am trying to implement below code every thing works fine how ever
here is the situation i am facing
mainactivity just has a button to open splashactivity
splash activity does the parsing part then opens the listactvity
my workflow is if user has clicked on the button in main activity
it opens the splash activity and redirects to listactivty the
problem occurs when on the splash activity if a user clicks the
andorid back button it does go back to the mainactivity however the
parsing in splashactivity continues and user is redirected to
listactivty this thing should not happen, when user on splash
activity has clicked backbutton it should go back to mainactivity
and stay there itself
how can this be done
package com.site.name;
public class SplashActivity extends Activity {
RSSFeed feed;
String fileName;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.splash);
fileName = "TDRSSFeed.td";
Intent i = getIntent();
int position = i.getExtras().getInt("position");
String[] country = i.getStringArrayExtra("country");
// //public String RSSFEEDURL = "http://blogname.blogspot.com//feeds/posts/default/-/Awards?alt=rss";
Toast.makeText(getApplicationContext(), country[position], Toast.LENGTH_SHORT).show();
//Toast.makeText(getApplicationContext(), country[position], Toast.LENGTH_SHORT).show();
File feedFile = getBaseContext().getFileStreamPath(fileName);
ConnectivityManager conMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
if (conMgr.getActiveNetworkInfo() == null) {
// No connectivity. Check if feed File exists
if (!feedFile.exists()) {
// No connectivity & Feed file doesn't exist: Show alert to exit
// & check for connectivity
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(
"Unable to reach server, \nPlease check your connectivity.")
.setTitle("TD RSS Reader")
.setCancelable(false)
.setPositiveButton("Exit",
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog,
int id) {
finish();
}
});
AlertDialog alert = builder.create();
alert.show();
} else {
// No connectivty and file exists: Read feed from the File
Toast toast = Toast.makeText(this,
"No connectivity!",
Toast.LENGTH_LONG);
toast.show();
//feed = ReadFeed(fileName);
startLisActivity(feed);
}
} else {
// Connected - Start parsing
new AsyncLoadXMLFeed().execute();
}
}
private void startLisActivity(RSSFeed feed) {
Bundle bundle = new Bundle();
bundle.putSerializable("feed", feed);
// launch List activity
Intent intent = new Intent(SplashActivity.this, ListActivity.class);
intent.putExtras(bundle);
startActivity(intent);
overridePendingTransition(R.anim.slide_in, R.anim.slide_out);
// kill this activity
finish();
}
private class AsyncLoadXMLFeed extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
// Obtain feed
DOMParser myParser = new DOMParser();
Intent i = getIntent();
int position = i.getExtras().getInt("position");
String[] country = i.getStringArrayExtra("country");
//feed = myParser.parseXml(RSSFEEDURL);
//feed = myParser.parseXml("http://blogname.blogspot.com//feeds/posts/default/-/Awards?alt=rss");
feed = myParser.parseXml("http://blogname.blogspot.com//feeds/posts/default/-/" + country[position] + "?alt=rss");
if (feed != null && feed.getItemCount() > 0)
WriteFeed(feed);
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
startLisActivity(feed);
}
}
// Method to write the feed to the File
private void WriteFeed(RSSFeed data) {
FileOutputStream fOut = null;
ObjectOutputStream osw = null;
try {
fOut = openFileOutput(fileName, MODE_PRIVATE);
osw = new ObjectOutputStream(fOut);
osw.writeObject(data);
osw.flush();
}
catch (Exception e) {
e.printStackTrace();
}
finally {
try {
fOut.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
#Override
public void onBackPressed() {
//Include the code here
return;
}
}
You can cancel parsing the feed in MainActivity's onRestart() as this is one method of MainActivity that will be called when you press back from your Parsing Activity.
You can read more about Activity Life Cycle here.
For detecting the Back button press event use the following method
#Override
public void onBackPressed() {
// do something on back.
// Change your activity by calling intent
return;
}
If you want to do anything with your back button then you have to override it manually.
#Override
Public void onBackPressed() {
//do whatever you want to do as your question is quite confusing.
return;
}
I have a Form with edittexts and a button to call the camera with an intent (return a bitmap that is put into the imageview)...From the portrait mode i enter all edittext filed and then click the camera button which forwards me to the camera - in the camera i take a picture after what I get returned to Activity 1 (staying in portrait orientation - and all editext fields are restore in onRestoreInstanceState()) - and the last callback method of Activity 1 is onResume() (what is ok) - But the problem comes when I make an orientation change from this portrait to landscape mode - the callback methods are following
So the last callback orientation change is onPause(). I do not understand why? The problem is that onSaveInstanceState is called prior of onPause - so when I turn back to portrait mode everything will be empty (editexts, imageview..) - this strange behavior continues on every orientation change (the onPause() is called last).
I am sure this problem has to do something with the taking an image (startInentforResult....) because everything (editext fields) works fine on orientation change prior to taking an image...sometimes I can also take an image and it works fine, but in most cases not...
So my question is what is it that "drives" my Activity up to the onPause() method instead up to the onResume()?
Thanks, I would really appreciate if somebody knows the solution because I am struggling with this already a few days and could not find the solution.
The project has many classes but this is the activity code (Important to note is that the problem arises only when I take an image from camera app, after that the activity lifecycle goes crazy - also this activity is called from the main activity with 'startIntentforResult()'. I do not use 'android:configChanges="orientation|keyboardHidden"' to stop the recreatioin ):
public class NewCounterActivity extends Activity {
Button btnCreate;
Button btnCancel;
Button btnTakeImg;
ImageView counterImgView;
CheckBox existsDamage;
EditText inputNameFirst;
EditText inputNameLast;
EditText inputAdresse;
EditText inputCounterID;
EditText inputCounterValue;
EditText inputDescription;
TextView registerErrorMsg;
DatabaseHandler db;
//Data to be submitted
String nameFirst;
String nameLast;
String adresse;
String counterID;
String counterValue;
String countDescript;
String existsDmg;
Bitmap counterBitmap;
Bitmap recievedBitmap;
String longitude;
String latitude;
LocationTracker gps;
// JSON Response node names
private static String KEY_SUCCESS = "success";
private static String KEY_ERROR = "error";
private static String KEY_ERROR_MSG = "error_msg";
//The dimensions of the ImageView
int targetW;
int targetH;
// Some lifecycle callbacks so that the image can survive orientation change
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Log.e("onSaveInstanceState", "fadsfass");
outState.putParcelable("bitmap", counterBitmap);
outState.putString("fname", inputNameFirst.getText().toString());
outState.putString("lname", inputNameLast.getText().toString());
outState.putString("adrese", inputAdresse.getText().toString());
outState.putString("cID", inputCounterID.getText().toString());
outState.putString("cValue", inputCounterValue.getText().toString());
outState.putString("Descript", inputDescription.getText().toString());
outState.putString("ErrorMsg", registerErrorMsg.getText().toString());
outState.putBoolean("damageCheck", existsDamage.isChecked());
((MyApplicationClass) getApplication()).detach(this);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
Log.e("onRestoreInstanceState", "fadsfass");
counterBitmap = savedInstanceState.getParcelable("bitmap");
counterImgView.setImageBitmap(counterBitmap);
inputNameFirst.setText(savedInstanceState.getString("fname"));
inputNameLast.setText(savedInstanceState.getString("lname"));
inputAdresse.setText(savedInstanceState.getString("adrese"));
inputCounterID.setText(savedInstanceState.getString("cID"));
inputCounterValue.setText(savedInstanceState.getString("cValue"));
inputDescription.setText(savedInstanceState.getString("Descript"));
registerErrorMsg.setText(savedInstanceState.getString("ErrorMsg"));
existsDamage.setChecked(savedInstanceState.getBoolean("damageCheck"));
((MyApplicationClass) getApplication()).attach(this);
}
#Override
public void onContentChanged() {
// TODO Auto-generated method stub
super.onContentChanged();
Log.e("onContetnChanged", "fadsfass");
}
#Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
Log.e("onDestroy", "fadsfass");
}
#Override
public void onDetachedFromWindow() {
// TODO Auto-generated method stub
super.onDetachedFromWindow();
Log.e("onDetachedFromWindow", "fadsfass");
}
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
Log.e("onPause", "fadsfass");
}
#Override
protected void onRestart() {
// TODO Auto-generated method stub
super.onRestart();
Log.e("onRestart", "fadsfass");
}
#Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
Log.e("onResume", "fadsfass");
}
#Override
protected void onStart() {
// TODO Auto-generated method stub
super.onStart();
Log.e("onStart", "fadsfass");
}
#Override
protected void onStop() {
// TODO Auto-generated method stub
super.onStop();
Log.e("onStop", "fadsfass");
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.newcounteractivity_layout);
Log.e("onCreate", "mActivity equals NULL");
inputNameFirst = (EditText) findViewById(R.id.createFirstName);
inputNameLast = (EditText) findViewById(R.id.createLastName);
inputAdresse = (EditText) findViewById(R.id.createAdresse);
inputCounterID = (EditText) findViewById(R.id.createCounterID);
inputCounterValue = (EditText) findViewById(R.id.createCounterValue);
inputDescription = (EditText) findViewById(R.id.createDescription);
registerErrorMsg = (TextView) findViewById(R.id.create_error);
btnCreate = (Button) findViewById(R.id.btnCreate);
btnCancel = (Button) findViewById(R.id.btnCancel);
btnTakeImg = (Button) findViewById(R.id.btnImage);
counterImgView = (ImageView) findViewById(R.id.counterImgView);
existsDamage = (CheckBox) findViewById(R.id.createDamageExists);
//REGISTER BUTTON CLICK EVENTS
btnCreate.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
//new DoBackgroundTask(NewCounterActivity.this).execute();
//CounterUser data to submit
nameFirst = inputNameFirst.getText().toString().trim();
nameLast = inputNameLast.getText().toString().trim();
adresse = inputAdresse.getText().toString().trim();
counterID = inputCounterID.getText().toString().trim();
counterValue = inputCounterValue.getText().toString().trim();
countDescript = inputDescription.getText().toString().trim();
existsDmg = Integer.toString((existsDamage.isChecked()) ? 1 : 0);
// create LocationTracker class object
gps = new LocationTracker(NewCounterActivity.this);
if(!gps.canGetLocation()){
gps.stopUsingGPS();
gps.showSettingsAlert();
//Ovo se mozda treba i izbaciti
gps.getLocation();
}
else{
processInput();
}
}
});
btnCancel.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Intent returnIntent = new Intent();
setResult(RESULT_CANCELED, returnIntent);
finish();
}
});
btnTakeImg.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if(isIntentAvailable(NewCounterActivity.this, MediaStore.ACTION_IMAGE_CAPTURE)){
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(takePictureIntent,2);
}
else {
Toast.makeText(NewCounterActivity.this, "No Camera Available", Toast.LENGTH_SHORT).show();
}
}
});
}
/************************************************************************************************
* Methods used in this class
* */
public void processInput(){
//Get current Longitude and Latitude
longitude = Double.toString(gps.getLongitude());
latitude = Double.toString(gps.getLatitude());
//Na kraju iskljuci location updatese - ne moze na emulatru jer ja emit coordinate preko DDMS... a kad emit on mora biti ukljucen da bi primio
//gps.stopUsingGPS();
Toast.makeText(getApplicationContext(), "Your Location is - \nLat: " + longitude + "\nLong: " + latitude, Toast.LENGTH_LONG).show();
if (!nameFirst.equals("") && !nameLast.equals("") && !adresse.equals("") && !counterID.equals("") && !counterValue.equals("")
&& counterBitmap != null ){
new DoBackgroundTask(NewCounterActivity.this).execute();
}
else{
// Not all fields are filled
registerErrorMsg.setText("Not all fields are filled");
}
}
//Method to check whether an app can handle your intent
public boolean isIntentAvailable(Context context, String action) {
final PackageManager packageManager = context.getPackageManager();
final Intent intent = new Intent(action);
List<ResolveInfo> list = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
return list.size() > 0;
}
/**************************************************************************************************
*
* When the calling activity, Activity #1, resumes after having called another activity, Activity #2, using startActivityForResult,
* the method onActivityResult in Activity #1 is called BEFORE onResume.
* This is important to know if you are instantiating your SQLite Database objects from within onResume in Activity #1. If so, you will also need to instantiate the object from within onActivityResult,
* when returning from Activity #2.
*
* startActivityForResult() is asynchronous. It can feel synchronous to the user since the UI will change and your calling activity will be paused
* (your onPause() method will be called).
*/
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// TODO Auto-generated method stub
super.onActivityResult(requestCode, resultCode, data);
Log.e("onActivityResult", "fadsfass");
if (requestCode == 2) {
if(resultCode == RESULT_OK){
Bundle extras = data.getExtras();
recievedBitmap = (Bitmap) extras.get("data");
}
if (resultCode == RESULT_CANCELED) {
Toast.makeText(NewCounterActivity.this, "No Image Taken", Toast.LENGTH_SHORT).show();
}
}
}
/**
* Koristim onWindowFocusChanged jer kad se vratim na Activity 1 onda dodje do potpunog recreate Activitija i getWidth()/height() ne mogu dobiti
* ni u jednom od lifecicle methoda - naime ide start onCreate,...onActivityResult(), onResume() - u onactivityResult izvadim bitmap i pohranim ga u receivedBitmap
* te kad getWidth() postane dostupan system invoke ovu dole methodu. :D
*/
#Override
public void onWindowFocusChanged(boolean hasFocus){
if(recievedBitmap != null){
targetW=counterImgView.getWidth();
targetH=counterImgView.getHeight();
Log.e("onWindowFocusChanged", "fadsfass" + " " + targetW + " " + targetH);
// http://stackoverflow.com/questions/4837715/how-to-resize-a-bitmap-in-android
// http://sunil-android.blogspot.com/2013/03/resize-bitmap-bitmapcreatescaledbitmap.html
// Scale or resize Bitmap to ImageView dimensions
counterBitmap = Bitmap.createScaledBitmap(recievedBitmap, targetW, targetH, false);
/**
* Canvas: trying to use a recycled bitmap android.graphics - This exception occurs when you try to recycle a bitmap which is already recycled.
* http://androdevvision.blogspot.com/2011/10/solution-for-out-of-memory-error-and.html
*/
if(recievedBitmap != null && !recievedBitmap.isRecycled()){
recievedBitmap.recycle();
recievedBitmap = null;
}
counterImgView.setImageBitmap(counterBitmap);
}
}
/************************************************************************************************
* Background AsyncTask to create new counterUser - https://github.com/callorico/CustomAsyncTask - najbolje radi
* new DoBackgroundTask(NewCounterActivity.this).execute();
* */
private static class DoBackgroundTask extends CustomAsyncTask<Void, Integer, JSONObject> {
private static final String TAG = "DoBackgroundTask";
private ProgressDialog mProgress;
private int mCurrProgress;
private NewCounterActivity myActivity = null;
public DoBackgroundTask(NewCounterActivity activity) {
super(activity);
}
#Override
protected void onPreExecute() {
super.onPreExecute();
showProgressDialog();
}
#Override
protected void onActivityDetached() {
if (mProgress != null) {
mProgress.dismiss();
mProgress = null;
}
}
#Override
protected void onActivityAttached() {
showProgressDialog();
}
private void showProgressDialog() {
mProgress = new ProgressDialog(mActivity);
mProgress.setProgressStyle(ProgressDialog.STYLE_SPINNER);
mProgress.setIndeterminate(true);
mProgress.setMessage(" Saljem na server... ");
mProgress.setCancelable(true);
mProgress.setOnCancelListener(new OnCancelListener() {
#Override
public void onCancel(DialogInterface dialog) {
cancel(true);
}
});
mProgress.show();
mProgress.setProgress(mCurrProgress);
}
#Override
protected JSONObject doInBackground(Void... params) {
//so you need to either pass an instance of the outer class to the inner class method (or its constructor) as a parameter,
//or create it inside the method.
JSONObject json = null;
if(mActivity != null){
myActivity = (NewCounterActivity) mActivity;
//Prepare counterBitmap as String
ByteArrayOutputStream stream = new ByteArrayOutputStream();
//Write a compressed version of the bitmap to the specified output stream.
myActivity.counterBitmap.compress(Bitmap.CompressFormat.JPEG, 90, stream);
byte [] b_array = stream.toByteArray();
String bitmapString = Base64.encodeBytes(b_array);
//Get workerId from logged worker
Functions workerFunction = new Functions();
DatabaseHandler db = new DatabaseHandler(mActivity);
String workerID = db.retrieveWorker().get("workerId");
if(myActivity != null){
//Get JsonObject from Functions.java
json = workerFunction.newCounterUser(myActivity.counterID, myActivity.counterValue, myActivity.adresse, myActivity.nameFirst, myActivity.nameLast, bitmapString, myActivity.existsDmg, myActivity.countDescript, workerID, myActivity.longitude, myActivity.latitude);
}
}
return json;
}
#Override
protected void onPostExecute(JSONObject jsonObject) {
super.onPostExecute(jsonObject);
if (mActivity != null) {
mProgress.dismiss();
try {
if (jsonObject.getString(KEY_SUCCESS) != null) {
myActivity.registerErrorMsg.setText("");
String res = jsonObject.getString(KEY_SUCCESS);
if(Integer.parseInt(res) == 1){
// counterUser successfully registered
Toast.makeText(mActivity, "New counterUser is created", Toast.LENGTH_LONG).show();
// Return back to MainActivity
Intent returnIntent = new Intent();
returnIntent.putExtra("result",jsonObject.toString());
mActivity.setResult(RESULT_OK,returnIntent);
// Close all views before launching MainActivity
returnIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
mActivity.finish();
}else{
// Error in registration
myActivity.registerErrorMsg.setText("Error occured in registration");
}
}
} catch (JSONException e) {
Log.e("Error","NO Json at all");
e.printStackTrace();
}
} else {
Log.d(TAG, "AsyncTask finished while no Activity was attached.");
}
}
}
Same issues on call to recreate(), when activity has been updated it get onPause after onResume. Tested on emulator, bug exists on Marshallow and below.
This is my fix
private static boolean isRecreate = false;
private void reCreateActivity() {
isRecreate = true;
recreate();
}
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if(isRecreate) {
isRecreate = false;
runOnUiThread(new Runnable() {
#Override
public void run() {
onResume();
}
});
}
}
I don't know if that's correct, but it works.
EDIT
Best solution to avoid this issues, call recreate in postDelayed with 0 delayMillis
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
recreate();
}
}, 0);
It is only normal, that strange code produces strange behavior ...
replace this line:
((MyApplicationClass) getApplication()).detach(this);
with this line:
super.onSaveInstanceState(outState);
I have created a Login activity which uses another class - LoginService which is an AsyncTask for the network communication.
public void onClick(View view) {
if (editTextPassword.getText().toString() != null & editTextUsername.getText().toString() != null){
new LoginService(editTextUsername.getText().toString(), editTextPassword.getText().toString()).execute();
if(loginSuccess!=false){
//Used to move to the Cases Activity
Intent casesActivity = new Intent(getApplicationContext(), CasesActivity.class);
startActivity(casesActivity);
}else{
Toast.makeText(getApplicationContext(),"Incorrect Details", Toast.LENGTH_LONG).show();
}
}
else{
//Display Toaster for error
Toast.makeText(getApplicationContext(),"Please enter your details", Toast.LENGTH_LONG).show();
}
}
Before the LoginService has finished executing, the activity has already moved to another activity via the Intent variable. I do not understand why. The idea of the LoginService is to validate the credentials of the user. If it returns true, then it can switch to the other activity.
You do not want to do this in this way. The .execute() will begin as soon as possible, but there is no guarantee (and perhaps guaranteed not to) that it will get your loginSuccess value back to you in time.
Everything after new LoginService(...).execute(); should be moved into onPostExecute():
private Context mContext = null;
public void setContext(Context context) {
mContext = context;
}
#Override
protected void onPostExecute(Void result) {
if(loginSuccess!=false){
//Used to move to the Cases Activity
Intent casesActivity = new Intent(mContext, CasesActivity.class);
startActivity(casesActivity);
}else{
Toast.makeText(mContext,"Incorrect Details", Toast.LENGTH_LONG).show();
}
}
Then, you have to call setContext() like so:
LoginService service = new LoginService(editTextUsername.getText().toString(), editTextPassword.getText().toString());
service.setContext(getApplicationContext());
service.execute();
You should move
Intent casesActivity = new Intent(getApplicationContext(), CasesActivity.class);
startActivity(casesActivity);
}else{
Toast.makeText(getApplicationContext(),"Incorrect Details", Toast.LENGTH_LONG).show();
}
into LoginService's onPostExecute.
In this way you are sure the asynctask has finished its work.
In any case it's quite strange the other activity gets started, it might be because of an old assignement of loginSuccess to true
How to return the result from the asynctask?
Catch the result of AsyncTask from onPostExecute().
#Override
public void onPostExecute(Boolean result)
{
boolean loginSuccess = result;
if(loginSuccess!=false) {
Intent casesActivity = new Intent(getApplicationContext(), CasesActivity.class);
startActivity(casesActivity);
}
else {
Toast.makeText(getApplicationContext(),"Incorrect Details", Toast.LENGTH_LONG).show();
}
}
The data type of result in AsyncTask depends on the 3rd Type parameter.
Sometimes we think execute() method of AsyncTask will return a result which is wrong. It will return an AsyncTask itself