Android Safe vs Unsafe Usage of Inner Classes - java

I'm new to Android development and I've been having a lot of trouble finding ways to make sure my app doesn't leak memory. I've read online that using inner classes can cause memory leaks, especially when they may outlive their Activity. Here are some of the inner classes I use. Which of these, if any, could cause a memory leak?
public class TitleScreenActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_title_screen);
final MyHandler mHandler = new MyHandler(this);
final Intent intent = new Intent(mHandler.mActivity.get(), IntroActivity.class);
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
startActivity(intent);
mHandler.mActivity.get().finish();
}
}, 2000);
}
private static class MyHandler extends Handler {
public final WeakReference<TitleScreenActivity> mActivity;
public MyHandler(TitleScreenActivity activity) {
mActivity = new WeakReference<>(activity);
}
}
}
I based this code off of what I found in this article. However, I don't think it would be possible for me to make my Runnable static, like they did in the article. Can this code cause a memory leak, even though I finish the Activity in the runnable?
Another example:
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(R.string.no_tiles);
builder.setPositiveButton(R.string.store, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// start new Activity
}
});
builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// Do nothing, just close dialog
}
});
AlertDialog dialog = builder.create();
dialog.show();
I have trouble believing that this could cause a leak because it doesn't contain an explicit reference to an Activity, View, etc and it (to the best of my knowledge) the dialog gets destroyed when the user clicks a button, so the listeners would be destroyed as well. However, I wanted to make sure.
Last example:
TranslateAnimation translate;
translate = new TranslateAnimation(0, 0, 0, 100);
translate.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
// modify a few variables
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
translate.setDuration(200);
findViewById(R.id.game_board).startAnimation(translate);
I'm not sure if this could cause because theoretically the animation could outlive the Activity it's in.
All help is appreciated, thanks!

Related

Progress bar not showing up till last moment

This is my first android app,
I am calling function result on button click but as its call to findTimeTable takes time i want to show a progress bar but for some reason progress bar fails to show up until last moment just before the dialog box is created which defeats my purpose of showing the progress bar for that time period. In-fact that last moment is only visible when i don't set their visibility back to what it was.
public void result(View view) throws IOException {
findViewById(R.id.loadingPanel).setVisibility(View.VISIBLE);
findViewById(R.id.chooseFile).setVisibility(View.GONE);
findViewById(R.id.chooseDay).setVisibility(View.GONE);
findViewById(R.id.chooseTime).setVisibility(View.GONE);
findViewById(R.id.findFaculty).setVisibility(View.GONE);
String n = findTimetable(index_day, index_time);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("hello" +n);
builder.setCancelable(false);
builder.setPositiveButton("OK",
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i){
}
});
final Dialog mDialog = builder.create();
mDialog.show();
findViewById(R.id.loadingPanel).setVisibility(View.GONE);
findViewById(R.id.chooseFile).setVisibility(View.VISIBLE);
findViewById(R.id.chooseDay).setVisibility(View.VISIBLE);
findViewById(R.id.chooseTime).setVisibility(View.VISIBLE);
findViewById(R.id.findFaculty).setVisibility(View.VISIBLE);
}
I found a relevant answer android progress bar not showing
but as i am not familiar with threads in programming i could not implement it properly on my code.
please help.
Use an AsyncTask for the heavy comupting and add the progress bar in its Pre/Post states, and if you choose to update the GUI from there you need to run that code on the UI Thread.
Example AsyncTask:
private class LongOperation extends AsyncTask<String, Void, String> {
#Override
protected void onPreExecute() {
// Start progressbar
}
#Override
protected String doInBackground(String... params) {
// Computing goes here
return null;
}
#Override
protected void onPostExecute(String result) {
// Stop progressbar
UpdateMyGUI();
}
#Override
protected void onProgressUpdate(Void... values) {
// Update progressbar (?)
}
}
private void updateMyGUI(){
runOnUiThread (new Runnable() {
public void run() {
// Update GUI Here.
}
});
}

Multiple DialogFragments with different functionalities

So i'm developing Android app where user is often asked what he wants to do on some actions (Button click etc.). For that i was using AlertDialog and wrote text on it and added Buttons i needed. It was working super until i realized, that on device rotation, an opened AlertDialog would disappear.
I found on the web that the proper way to handle rotation is to use Fragment, so i choose to make class extending DialogFragment:
public class MyDialogFragment extends DialogFragment {
public interface YesNoListener {
void onYes();
void onNo();
void onNeu();
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
String[] data = getArguments().getStringArray("data"); //vzame vrednosti, ki smo jih nastavili preden se pokliče .show();
if(data != null) {
switch (data.length) {
case 3:
return new AlertDialog.Builder(getActivity())
.setTitle(data[0])
.setMessage(data[1])
.setNeutralButton(data[2], new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
((YesNoListener) getActivity()).onNeu();
}
})
.create();
case 4:
return new AlertDialog.Builder(getActivity())
.setTitle(data[0])
.setMessage(data[1])
.setPositiveButton(data[2], new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
((YesNoListener) getActivity()).onYes();
}
})
.setNegativeButton(data[3], new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
((YesNoListener) getActivity()).onNo();
}
})
.create();
case 5:
return new AlertDialog.Builder(getActivity())
.setTitle(data[0])
.setMessage(data[1])
.setPositiveButton(data[2], new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
((YesNoListener) getActivity()).onYes();
}
})
.setNegativeButton(data[3], new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
((YesNoListener) getActivity()).onNo();
}
})
.setNeutralButton(data[4], new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
((YesNoListener) getActivity()).onNeu();
}
})
.create();
default:
return null;
}
}
else
return null;
}
}
I used interface too so i could implement Button.onClick() behaviour in the class where i would use MyDialogFragment. With getArguments() i passed all texts that would be used in Dialog.
The problem is that i can only use this class for one Dialog, since i have to Override interface functions, but i have multiple Dialogs with different behaviour.
I wanted to solve this issue with three public Objects Runnable, where i would just initialize Runnable where i need to change the behaviour of the Button.onClick()
...
Runnable runnablePositive, runnableNegative, runnableNeutral;
...
#Override
public void onYes(){
threadPositive.start();
}
#Override
public void onNo(){
threadNegative.start();
}
#Override
public void onNeu(){
threadNeutral.start();
}
static MyDialogFragment newInstance(String[] arg) {
MyDialogFragment f = new MyDialogFragment();
Bundle args = new Bundle();
args.putStringArray("data", arg);
f.setArguments(args);
return f;
}
...and on usage:
threadPositive = new Thread()
{
public void run()
{
//do A
}
};
threadNegative = new Thread()
{
public void run()
{
//do B
}
};
threadNeutral = new Thread()
{
public void run()
{
//do C
}
};
newInstance(new String[]{title, besedilo, nevtralno}).show(getFragmentManager(), "tag");
It is working good untill i open Dialog and rotate (this is the main problem, other things somehow work) the device (reason why i use DialogFragment in the first place). All the variables are "deleted" on the rotation and i already passed all variables i needed for further work, but there comes new issue, which i have no idea how to solve: i can't pass Objects on rotation, whether i try with onRetainCustomNonConfigurationInstance() or onSaveInstanceState() all in vain...
So i have no idea how to solve this, i had gone through hundreds of question regarding similar issue, but had no luck... And i would be grateful for any helpful advice or answer regarding this problem (even if it is a different way to solve the problem).
Thanks in advance!
If you have multiple dialogs with different behavior, then you should simply create more instances of your dialog and assign them different tags. Instead of doing just
newDialogFragment(someArgs).show(getFragmentManager(), "tag")
you can do
newDialogFragment(someArgs).show(getFragmentManager(), "dialogWithArgs")
newDialogFragment(someOtherArgs).show(getFragmentManager(), "dialogWithOtherArgs")
and so on. Your interface should be changed to
public interface YesNoListener {
void onYes(String tag);
void onNo(String tag);
void onNeu(String tag);
}
When you call its methods from the dialog, pass the fragment tag so you know which dialog called the method. That way you can handle any number of dialogs easily.
As to saving the objects, those that don't change, go into the arguments, those that do, should be made Parcelable and saved to Bundle, if you can't, then you can create a separate fragment and call setRetainInstance(true) on it, then store the objects in it.

Show alert dialog from background process in any activity

How to get result of background process in any Activity?
I'm working with OTA update. App handle that in background with handler. When update is done I have to show alert dialog to the user. I can show it in SettingsActivity where I start with OTA but user can be in any other Activity.
private void checkIsUpdated() {
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
mDevice.getDevice().read(GattUUID.LS_DEVICE_INFORMATION, GattUUID.LC_FIRMWARE, new
BleDevice.ReadWriteListener() {
#Override
public void onEvent(ReadWriteEvent e) {
if (e.wasSuccess()) {
if (firmwareVersion.equals(e.data_string())) {
showAlertDialog("OTA update failed", "Device is not updated");
} else {
showAlertDialog("OTA update is successful.\nDevice is updated to new " +
"firmware!", "Device is updated");
}
Log.i("OTA", "Read firmware is new success");
} else {
Log.e("OTA", "Read firmware is new success");
}
}
});
}
}, 30000);
}
AlertDialog code
private void showAlertDialog(String message, String title) {
AlertDialog.Builder builder = new AlertDialog.Builder(mContext, R.style.SwipeDialogLight);
builder.setTitle(title)
.setMessage(message)
.setCancelable(false)
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
AlertDialog alert = builder.create();
if (!alert.isShowing()) {
alert.show();
}
}
As #W4R10CK stated, I thought that too. The BroadcastReceiver might not a very good idea. But later, I gave a thought about it and if you call checkIsUpdated function inside a Service and send the Broadcast from it, it might be a feasible solution.
So basically you need a BaseActivity class and in which you need to have a BroadcastReceiver. You need to register it onResume and again unregister it onPause.
And you need to have an abstract method to be overriden in each of your Activity too.
So your BaseActivity may look like this..
public abstract class BaseActivity extends AppCompatActivity {
private final Context context;
public BaseActivity() {
this.context = setContext();
}
protected abstract Context setContext();
#Override
public void onResume() {
super.onResume();
registerBroadcastReceiver();
}
#Override
public void onPause() {
super.onPause();
unRegisterBroadcastReceiver();
}
// Declare your BroadcastReceiver here
private class MyBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
showAlertDialog(context);
}
}
}
As #rogerwar suggested in the comments, the correct approach is a broadcast receiver.
Vogella has a nice tutorial
Since you want to have it in all activities, you can make a base class for all your activities and in this base class you can register the receiver in the onStart and unregister it in the onStop.

Native Android UI for using Within LibGdx

Hi there StackOverflow!
I had been loocking on a way to Use the native Android Dialogs and Confimation Boxes in Libgdx...
All that i did by now was a Title and an Image under it:
Dialog yourmsgbox = new Dialog("Title", jsons);
yourmsgbox.setBounds(0f,0f,100f,200f);
yourmsgbox.add(choiceImg);
mainClass.addActor(yourmsgbox);
I suck a little at this but all the codes that i find in Google to do that are Or for Desktop or very especific for that Type of game + Even after some tries to copy the code and adapt it to my .java Files im still getting errors....
So if you guys could guide through a step by step ((Or a list Number of online items that i could follow to get this done I WOULD BE VERYY GRATEFULL !!!))
[[My Json file is EXTREMELY BUGGY, so if I could not have to mess with that Stubborn uiskin.json, I would Thank you :]]
Sorry my bad english
Please i'd apreciate a little help!?
UPDATE:: Sorry i have
two MainClasses for this project and i pick the wrong Logcat :)
I just use showMessage(); in the beggining of the create(), it
crashes when i get into the app. Here is what i did:
I Created an Inferface in core Project:
public interface NativeDialogInterface {
void showMessage(final String title, final String message, final String okButtonText);
}
Created AndroidNativeDialog in -android Project folder:
public class AndroidNativeDialog implements NativeDialogInterface {
private Activity activity;
public void initialize(Activity activity) {
this.activity = activity;
}
#Override
public void showMessage(final String title, final String message, final String okButtonText) {
this.activity.runOnUiThread(new Runnable() {
#Override
public void run() {
final AlertDialog alertDialog = new AlertDialog.Builder(activity).create();
alertDialog.setTitle(title);
alertDialog.setMessage(message);
alertDialog.setButton(okButtonText, new DialogInterface.OnClickListener() {
#Override
public void onClick(final DialogInterface arg0, final int arg1) {
alertDialog.cancel();
}
});
alertDialog.show();
}
});
}
}
*Strange that it says as warning "Method setButton(...) is deprecated"
Then i added new (dialogInterface) in the AndroidLaucher.java:
public class AndroidLauncher extends AndroidApplication {
private AndroidNativeDialog dialogInteface;
#Override
protected void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AndroidApplicationConfiguration config = new AndroidApplicationConfiguration();
dialogInterface = new AndroidNativeDialog();
initialize(new IndexMain(dialogInteface), config);
}
}
Then in the MainClass what i did was:
btnWindow.addListener(new ClickListener(){
#Override
public void clicked(InputEvent event, float x, float y) {
mainScreen.addActor(andWindow);
dialogInteface.showMessage("TITLE", "ThE MeSsaGe", "Okayy");
Timer.schedule(new Timer.Task() {
#Override
public void run() {
andWindow.setBounds(Gdx.graphics.getWidth(), 0f, 1f, 1f);
}
}, 17);
}
});
I head to that link that "Fuat Coçkun" provided and i learn a lot about these type of structures but it seems i still have something wrongg
Its WORKS perfectly until i click that /\ Button, the button is ok if i delete the showMessage(...);
new LogCat: http://pastebin.com/NbgnyrAJ
Sorry for my bad english.
I can give you example usage of native android AlertDialog with libgdx. Firstly you need an interface in your core Project as follows :
public interface NativeDialogInterface {
void showMessage(final String title, final String message, final String okButtonText);
}
You need different implementations for each of platform you support in your project. Android project implementation will use Dialog, AlertDialog or whatever you want to use as native android component. This example shows AlertDialog implementation:
public class AndroidNativeDialog implements NativeDialogInterface {
private Activity activity;
public void initialize(Activity activity) {
this.activity = activity;
}
#Override
public void showMessage(final String title, final String message, final String okButtonText) {
this.activity.runOnUiThread(new Runnable() {
#Override
public void run() {
final AlertDialog alertDialog = new AlertDialog.Builder(activity).create();
alertDialog.setTitle(title);
alertDialog.setMessage(message);
alertDialog.setButton(okButtonText, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface arg0, int arg1) {
alertDialog.cancel();
}
});
alertDialog.show();
}
});
}
}
You need to call "initialize" method of your instance in your activity(onCreate is proper.) for setting activity field.
You can use any dummy implementation for the Desktop module of your libgdx project. Following implementation only logs the data you passed.
public class DesktopNativeDialog implements NativeDialogInterface {
#Override
public void showMessage(final String title, final String message, final String okButtonText) {
System.out.println("Title : " + title);
System.out.println("Message : " + message);
System.out.println("OkButtonText : " + okButtonText);
}
}
That's all. You should have a field typed NativeDialogInterface in your Core module and call "showMessage" method with your parameter. You will see a console log if you run your application on desktop. You will see native Android alert dialog on your glSurfaceView when you run your application on device/emulator.
I did this and created an expansion for libGDX. You can use it or check the source: https://github.com/TomGrill/gdx-dialogs

Preventing Android dialog from extending activities

How can I prevent my dialogs from extending activities?
When I create it in my main activity it doesn't dismiss itself when I click "Okay", which creates a new activity. The new activity that is created extends from the MainActivity.
I am using shared preferences to determine where to send the user when they open the app. I'm not sure if that could be playing into this situation.
I want to prevent the dialogs from extending the MainActivity. It shouldn't be showing up on the other activities that I create.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
SharedPreferences sharedPreferences = getSharedPreferences("version", 0);
int savedVersionCode = sharedPreferences.getInt("VersionCode", 0);
int appVershionCode = 0;
try { appVershionCode = getPackageManager().getPackageInfo(getPackageName(), 0).versionCode; }
catch (NameNotFoundException nnfe) { Log.w(TAG, "$ Exception because of appVershionCode : " + nnfe); }
if(savedVersionCode == appVershionCode){
// Returning user
Log.d(TAG, "$$ savedVersionCode == appVershionCode");
// Temporary Navigation
final Builder alertDialogBuilder = new Builder(this);
new AlertDialog.Builder(new ContextThemeWrapper(getBaseContext(), android.R.style.Theme_Dialog));
alertDialogBuilder.setTitle("Temporary Navigation");
alertDialogBuilder.setMessage("Go to the new activity.");
alertDialogBuilder.setPositiveButton("Okay", new OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Log.d(TAG, "$$ onClick");
Intent newactivity = new Intent(MAINACTIVITY.this,NEWACTIVITY.class);
startActivity(newactivity);
dialog.cancel();
}
});
alertDialogBuilder.show();
// End
} else {
// First time visitor
Log.d(TAG, "$$ savedVersionCode != appVershionCode");
// Hide graphics meant for returning users
((Button)findViewById(R.id.Button01)).setVisibility(View.INVISIBLE);
SharedPreferences.Editor sharedPreferencesEditor = sharedPreferences.edit();
sharedPreferencesEditor.putInt("VersionCode", appVershionCode);
sharedPreferencesEditor.commit();
Builder alertDialogBuilder = new Builder(this);
alertDialogBuilder.setTitle("Welcome");
alertDialogBuilder.setMessage("Click Okay to continue.");
alertDialogBuilder.setNeutralButton("Okay", new OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Log.d(TAG, "$$ onClick");
Intent leagues = new Intent(MAINACTIVITY.this,NEWACTIVITY.class);
startActivity(leagues);
}
});
alertDialogBuilder.show();
}
}
Try dialog.dismiss() like such:
#Override
public void onClick(DialogInterface dialog, int which) {
Log.d(TAG, "$$ onClick");
Intent newactivity = new Intent(MAINACTIVITY.this,NEWACTIVITY.class);
startActivity(newactivity);
dialog.dismiss();
}
From a little understanding of your code, my suggestion would be keeping a check of when you want dialog to be shown and when you dont. e.g You can use a static boolean flag showDialog, set it true/false in your activity according to the use.
if(savedVersionCode == appVershionCode && showDialog)
if(savedVersionCode == appVershionCode && !showDialog)
Its more over over a programmatic problem with a programmatic solution. This is approach would be just a suggestion. As you are following a singleTon type of structure so you must be sure of methods you want to carry further.
Second approach could be, do not do it this way. The common method you want to implement in your activity is related to SharedPref checking, so why not :
Create a class which extends Activity.
Add your SharedPref related methods in it.
Now you can extend that class to all you activities.
public class commonMethod extends Activity{
public void my_sharedPrefMethod(){
// do some thing with prefs
}
#OverRide
public void onCreate(){
super.onCreate();
// common onCreate code for every activity. Recommend not to change this
// so that you can implement
}
// you can also write other methods onPause(), onDestroy() here too.
{
Now you can extend your classes with this class commonMethod. e.g
public class main extends commonMethod{
#overRide
public void oncreate(){
}
#overRide
public void my_sharedPrefMethod(){
}
public void showMyDialog(){
// this way dialog box would not be shown on every activity but just this one.
}
}

Categories