While investigating memory issues in our application, it turns out that if the application Activity is a MapActivity, the first instance of it won't be finalized. Leading to other memory leak such as the view passed to setContentView.
Does anyone notice that before?
Here is the testing code showing that "MainActivity : 1" is not finalized whereas it is if MainActivity inherits from Activity.
To test, one needs to change device or emulator orientation many times.
import com.google.android.maps.MapActivity;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
public class MainActivity extends MapActivity {
private static final String defaultTag = "MA";
private static final boolean isDebugModeActivate = true;
private static final boolean isClassTagDisplayed = false;
private static final boolean isWebModeActivate = false;
static public void d(Object thiso, String message)
{
String tag = defaultTag + (isClassTagDisplayed == true ? "_" + thiso.getClass().getSimpleName() : "");
message = (isClassTagDisplayed == false ? thiso.getClass().getSimpleName() + " : " : "") + message;
Log.d(tag, message);
}
public MainActivity()
{
counter++;
uid++;
id = uid;
d(this, id + " tst constructor (" + counter + ")");
}
private static int counter = 0;
private static int uid = 0;
private final int id;
protected void finalize() throws Throwable
{
counter--;
d(this, id + " tst finalize (" +counter + ") ");
super.finalize();
}
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
#Override
protected boolean isRouteDisplayed()
{
return false;
}
}
Thank you,
David
Perhaps you should exchange notes with NickT here
Related
This question already has answers here:
Why a non-static inner-class cannot have static members (fields and methods)? [duplicate]
(3 answers)
RecyclerView: Inner classes cannot have static declaration
(2 answers)
Closed 4 years ago.
I have posted the HandlerThread class where I got an error on the following line:
private final static String TAG_LOG = MainAct.TAG_LOG + "->" + RoomPersistentDBHandlerThread.class.getSimpleName();
The error was inner class cant have inner static memedeclartion.
Can any of you explain to me why for the aforementioned line of code, I could not declare static variable inside the
inner class, despite in the immediate following line, the static variable has no errors.
thanks
code:
class MainAct {
public void buildPersistentDB() {
}
private class RoomPersistentDBHandlerThread extends HandlerThread implements Handler.Callback {
private final static String TAG_LOG = MainAct.TAG_LOG + "->" + RoomPersistentDBHandlerThread.class.getSimpleName();
private final static String THROW_ON_LOOPER_NOT_INITIALIZED = "LOOPER_NOT_INITIALIZED";
private WeakReference<Context> mWeakReferenceToActMain;
private Handler mHandler = null;
private final static String markerAstrisks = "++++++++++++++++++++++++ ";
public RoomPersistentDBHandlerThread(String name, Context context) {
super(name);
Log.w(TAG_LOG, "RoomPersistentDBHandlerThread constructor is called.");
this.mWeakReferenceToActMain = new WeakReference<>(context);
}
#Override
protected void onLooperPrepared() {
super.onLooperPrepared();
Log.w(TAG_LOG, "onLooperPrepared is called.");
Log.v(TAG_LOG, markerAstrisks + " [getLooper: " + this.getLooper() + "] " + markerAstrisks);
try {
this.mHandler = Optionals.toOptional(this.getLooper())
.map(looper -> new Handler(looper, this))
.orElseThrow(() -> new NullPointerException(THROW_ON_LOOPER_NOT_INITIALIZED));
} catch (Throwable throwable) {
throwable.printStackTrace();
}//eof catch
}//eof onLooperPrepared
public void enqueueMessage(int what) {
Log.w(TAG_LOG, "enqueueMessage is called for what = " + what);
this.mHandler.sendEmptyMessage(what);
}
#Override
public boolean handleMessage(Message msg) {
Log.w(TAG_LOG, "handleMessage is called for msg.what = " + msg.what);
switch(msg.what) {
case WHAT_MSG_INIT_PERSISTENT_DATABASE:
mWeakReferenceToActMain.get()
break;
}
return true;
}
}//eof-RoomPersistentDBHandlerThread
}
This question already has answers here:
How does Log.wtf() differ from Log.e()?
(7 answers)
Closed 7 years ago.
I encountered the method
public static void wtf(String format, Object... args) {
Log.wtf(TAG, buildMessage(format, args));
}
public static int wtf(String tag, String msg, Throwable tr) {
throw new RuntimeException("Stub!");
}
When I was having a closer look on the Android Volley, However this method is used to log the error in the Volley By the Library Developers but is this making any other sense other than the usual ones?
I am not sure if a programmer should have such naming conventions?
It's made very clear in the API docs that WTF stands, in this case for What a Terrible Failure. You can take a page out of Dr. Evil's book and say it like this: ;).
Quoting from developer.android.com:
What a Terrible Failure: Report a condition that should never happen.
The error will always be logged at level ASSERT with the call stack.
Used to detect log like:
package com.android.volley;
import android.os.SystemClock;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
/** Logging helper class. */
public class VolleyLog {
public static String TAG = "Volley";
public static boolean DEBUG = Log.isLoggable(TAG, Log.VERBOSE);
/**
* Customize the log tag for your application, so that other apps
* using Volley don't mix their logs with yours.
* <br />
* Enable the log property for your tag before starting your app:
* <br />
* {#code adb shell setprop log.tag.<tag>}
*/
public static void setTag(String tag) {
d("Changing log tag to %s", tag);
TAG = tag;
// Reinitialize the DEBUG "constant"
DEBUG = Log.isLoggable(TAG, Log.VERBOSE);
}
public static void v(String format, Object... args) {
if (DEBUG) {
Log.v(TAG, buildMessage(format, args));
}
}
public static void d(String format, Object... args) {
Log.d(TAG, buildMessage(format, args));
}
public static void e(String format, Object... args) {
Log.e(TAG, buildMessage(format, args));
}
public static void e(Throwable tr, String format, Object... args) {
Log.e(TAG, buildMessage(format, args), tr);
}
public static void wtf(String format, Object... args) {
Log.wtf(TAG, buildMessage(format, args));
}
public static void wtf(Throwable tr, String format, Object... args) {
Log.wtf(TAG, buildMessage(format, args), tr);
}
/**
* Formats the caller's provided message and prepends useful info like
* calling thread ID and method name.
*/
private static String buildMessage(String format, Object... args) {
String msg = (args == null) ? format : String.format(Locale.US, format, args);
StackTraceElement[] trace = new Throwable().fillInStackTrace().getStackTrace();
String caller = "<unknown>";
// Walk up the stack looking for the first caller outside of VolleyLog.
// It will be at least two frames up, so start there.
for (int i = 2; i < trace.length; i++) {
Class<?> clazz = trace[i].getClass();
if (!clazz.equals(VolleyLog.class)) {
String callingClass = trace[i].getClassName();
callingClass = callingClass.substring(callingClass.lastIndexOf('.') + 1);
callingClass = callingClass.substring(callingClass.lastIndexOf('$') + 1);
caller = callingClass + "." + trace[i].getMethodName();
break;
}
}
return String.format(Locale.US, "[%d] %s: %s",
Thread.currentThread().getId(), caller, msg);
}
/**
* A simple event log with records containing a name, thread ID, and timestamp.
*/
static class MarkerLog {
public static final boolean ENABLED = VolleyLog.DEBUG;
/** Minimum duration from first marker to last in an marker log to warrant logging. */
private static final long MIN_DURATION_FOR_LOGGING_MS = 0;
private static class Marker {
public final String name;
public final long thread;
public final long time;
public Marker(String name, long thread, long time) {
this.name = name;
this.thread = thread;
this.time = time;
}
}
private final List<Marker> mMarkers = new ArrayList<Marker>();
private boolean mFinished = false;
/** Adds a marker to this log with the specified name. */
public synchronized void add(String name, long threadId) {
if (mFinished) {
throw new IllegalStateException("Marker added to finished log");
}
mMarkers.add(new Marker(name, threadId, SystemClock.elapsedRealtime()));
}
/**
* Closes the log, dumping it to logcat if the time difference between
* the first and last markers is greater than {#link #MIN_DURATION_FOR_LOGGING_MS}.
* #param header Header string to print above the marker log.
*/
public synchronized void finish(String header) {
mFinished = true;
long duration = getTotalDuration();
if (duration <= MIN_DURATION_FOR_LOGGING_MS) {
return;
}
long prevTime = mMarkers.get(0).time;
d("(%-4d ms) %s", duration, header);
for (Marker marker : mMarkers) {
long thisTime = marker.time;
d("(+%-4d) [%2d] %s", (thisTime - prevTime), marker.thread, marker.name);
prevTime = thisTime;
}
}
#Override
protected void finalize() throws Throwable {
// Catch requests that have been collected (and hence end-of-lifed)
// but had no debugging output printed for them.
if (!mFinished) {
finish("Request on the loose");
e("Marker log finalized without finish() - uncaught exit point for request");
}
}
/** Returns the time difference between the first and last events in this log. */
private long getTotalDuration() {
if (mMarkers.size() == 0) {
return 0;
}
long first = mMarkers.get(0).time;
long last = mMarkers.get(mMarkers.size() - 1).time;
return last - first;
}
}
}
I am trying to write a simple app for Google TV which generates a random number from 1-10 then randomly selects a channel (501-510) and loads it.
I have tried the official google documentation but the official sample project doesnt compile. I have also read Is the GTV Channel Listing/Change API/Sample is broken on LG G2? and tried to adapt this into the google version however the app crashes on loading.
Im sure this must be a simple fix. I do not need to get information about the channel or search for them using the tutorial at https://developers.google.com/tv/android/docs/gtv_provider.
Any help greatly appreciated.
The access to the providers have changed if you use the code below things should improve.
Permissions:
<uses-permission android:name="com.google.android.tv.permission.READ_CHANNELS"/>
<uses-permission android:name="com.google.android.tv.mediadevices.permission.READ_STREAMS"/>
code:
public abstract class ChannelList {
private static ChannelList mCL=null;
public abstract String getPROVIDER_URI();
public abstract String getCALL_SIGN_COLUMN();
public abstract String getURI_COLUMN();
public abstract String getNUMBER_COLUMN();
public abstract String getNAME_COLUMN();
public static ChannelList getChannelList() {
if (mCL != null)
return mCL;
int mGtvLibraryVersion = 0;
try {
Class<?> cl = Class.forName("com.google.android.tv.Version");
mGtvLibraryVersion = cl.getField("GTV_SDK_INT").getInt(null);
} catch (Exception ex) {}
Log.d("Resolution Test", "Version " + mGtvLibraryVersion);
mCL= mGtvLibraryVersion > 0 ? new Version3ChannelList(): new Version2ChannelList();
return mCL;
}
/**
* Use the getChannelList factory to obtain an instance of a subclass of
* ChannelList
*/
private ChannelList() {
}
#Override
public String toString() {
return "SDK Provider: " + getPROVIDER_URI() + "\n" +
"Columns: " + getCALL_SIGN_COLUMN() + " " + getURI_COLUMN() + " " + getNUMBER_COLUMN() + " "
+ getNAME_COLUMN();
}
public static final class Version2ChannelList extends ChannelList {
#Override
public String getPROVIDER_URI() {
return "content://com.google.android.tv.provider/channel_listing";
}
#Override
public String getCALL_SIGN_COLUMN() {
return "callsign";
}
#Override
public String getURI_COLUMN() {
return "channel_uri";
}
#Override
public String getNUMBER_COLUMN() {
return "channel_number";
}
#Override
public String getNAME_COLUMN() {
return "channel_name";
}
}
public static final class Version3ChannelList extends ChannelList {
#Override
public String getPROVIDER_URI() {
return "content://com.google.tv.mediadevicesapp.MediaDevicesProvider/channel_list";
}
#Override
public String getCALL_SIGN_COLUMN() {
return "subName";
}
#Override
public String getURI_COLUMN() {
return "url";
}
#Override
public String getNUMBER_COLUMN() {
return "channelNumber";
}
#Override
public String getNAME_COLUMN() {
return "name";
}
}
I would like to store class object in android sharedpreference. I did some basic search on that and I got some answers like make it serializable object and store it but my need is so simple. I would like to store some user info like name, address, age and boolean value is active. I made one user class for that.
public class User {
private String name;
private String address;
private int age;
private boolean isActive;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public boolean isActive() {
return isActive;
}
public void setActive(boolean isActive) {
this.isActive = isActive;
}
}
Thanks.
Download gson-1.7.1.jar from this link: GsonLibJar
Add this library to your android project and configure build path.
Add the following class to your package.
package com.abhan.objectinpreference;
import java.lang.reflect.Type;
import android.content.Context;
import android.content.SharedPreferences;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
public class ComplexPreferences {
private static ComplexPreferences complexPreferences;
private final Context context;
private final SharedPreferences preferences;
private final SharedPreferences.Editor editor;
private static Gson GSON = new Gson();
Type typeOfObject = new TypeToken<Object>(){}
.getType();
private ComplexPreferences(Context context, String namePreferences, int mode) {
this.context = context;
if (namePreferences == null || namePreferences.equals("")) {
namePreferences = "abhan";
}
preferences = context.getSharedPreferences(namePreferences, mode);
editor = preferences.edit();
}
public static ComplexPreferences getComplexPreferences(Context context,
String namePreferences, int mode) {
if (complexPreferences == null) {
complexPreferences = new ComplexPreferences(context,
namePreferences, mode);
}
return complexPreferences;
}
public void putObject(String key, Object object) {
if (object == null) {
throw new IllegalArgumentException("Object is null");
}
if (key.equals("") || key == null) {
throw new IllegalArgumentException("Key is empty or null");
}
editor.putString(key, GSON.toJson(object));
}
public void commit() {
editor.commit();
}
public <T> T getObject(String key, Class<T> a) {
String gson = preferences.getString(key, null);
if (gson == null) {
return null;
}
else {
try {
return GSON.fromJson(gson, a);
}
catch (Exception e) {
throw new IllegalArgumentException("Object stored with key "
+ key + " is instance of other class");
}
}
} }
Create one more class by extending Application class like this
package com.abhan.objectinpreference;
import android.app.Application;
public class ObjectPreference extends Application {
private static final String TAG = "ObjectPreference";
private ComplexPreferences complexPrefenreces = null;
#Override
public void onCreate() {
super.onCreate();
complexPrefenreces = ComplexPreferences.getComplexPreferences(getBaseContext(), "abhan", MODE_PRIVATE);
android.util.Log.i(TAG, "Preference Created.");
}
public ComplexPreferences getComplexPreference() {
if(complexPrefenreces != null) {
return complexPrefenreces;
}
return null;
} }
Add that application class in your manifest's application tag like this.
<application android:name=".ObjectPreference"
android:allowBackup="false"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
....your activities and the rest goes here
</application>
In Your Main Activity where you wanted to store value in Shared Preference do something like this.
package com.abhan.objectinpreference;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
public class MainActivity extends Activity {
private final String TAG = "MainActivity";
private ObjectPreference objectPreference;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
objectPreference = (ObjectPreference) this.getApplication();
User user = new User();
user.setName("abhan");
user.setAddress("Mumbai");
user.setAge(25);
user.setActive(true);
User user1 = new User();
user1.setName("Harry");
user.setAddress("London");
user1.setAge(21);
user1.setActive(false);
ComplexPreferences complexPrefenreces = objectPreference.getComplexPreference();
if(complexPrefenreces != null) {
complexPrefenreces.putObject("user", user);
complexPrefenreces.putObject("user1", user1);
complexPrefenreces.commit();
} else {
android.util.Log.e(TAG, "Preference is null");
}
}
}
In another activity where you wanted to get the value from Preference do something like this.
package com.abhan.objectinpreference;
import android.app.Activity;
import android.os.Bundle;
public class SecondActivity extends Activity {
private final String TAG = "SecondActivity";
private ObjectPreference objectPreference;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
objectPreference = (ObjectPreference) this.getApplication();
ComplexPreferences complexPreferences = objectPreference.getComplexPreference();
android.util.Log.i(TAG, "User");
User user = complexPreferences.getObject("user", User.class);
android.util.Log.i(TAG, "Name " + user.getName());
android.util.Log.i(TAG, "Address " + user.getAddress());
android.util.Log.i(TAG, "Age " + user.getAge());
android.util.Log.i(TAG, "isActive " + user.isActive());
android.util.Log.i(TAG, "User1");
User user1 = complexPreferences.getObject("user", User.class);
android.util.Log.i(TAG, "Name " + user1.getName());
android.util.Log.i(TAG, "Address " + user1.getAddress());
android.util.Log.i(TAG, "Age " + user1.getAge());
android.util.Log.i(TAG, "isActive " + user1.isActive());
} }
Hope this can help you. In this answer I used your class for the reference 'User' so you can better understand. However we can not relay on this method if you opted to store very large objects in preference as we all know that we have limited memory size for each app in data directory so that if you are sure you have only limited data to store in shared preference you can use this alternative.
Any suggestions on this implement are most welcome.
the other way is to save each property by itself..Preferences accept only primitive types, so you can't put a complex Object in it
You can use the global class
public class GlobalState extends Application
{
private String testMe;
public String getTestMe() {
return testMe;
}
public void setTestMe(String testMe) {
this.testMe = testMe;
}
}
and then Locate your application tag in nadroid menifest, and add this into it :
android:name="com.package.classname"
and you can set and get the data from any of your activity by using the following code.
GlobalState gs = (GlobalState) getApplication();
gs.setTestMe("Some String");</code>
// Get values
GlobalState gs = (GlobalState) getApplication();
String s = gs.getTestMe();
You could just add some normal SharedPreferences "name", "address", "age" & "isActive" and simply load them when instantiating the class
Simple solution of how to store login value in by SharedPreferences.
You can extend the MainActivity class or other class where you will store the "value of something you want to keep". Put this into writer and reader classes:
public static final String GAME_PREFERENCES_LOGIN = "Login";
Here InputClass is input and OutputClass is output class, respectively.
// This is a storage, put this in a class which you can extend or in both classes:
//(input and output)
public static final String GAME_PREFERENCES_LOGIN = "Login";
// String from the text input (can be from anywhere)
String login = inputLogin.getText().toString();
// then to add a value in InputCalss "SAVE",
SharedPreferences example = getSharedPreferences(GAME_PREFERENCES_LOGIN, 0);
Editor editor = example.edit();
editor.putString("value", login);
editor.commit();
Now you can use it somewhere else, like other class. The following is OutputClass.
SharedPreferences example = getSharedPreferences(GAME_PREFERENCES_LOGIN, 0);
String userString = example.getString("value", "defValue");
// the following will print it out in console
Logger.getLogger("Name of a OutputClass".class.getName()).log(Level.INFO, userString);
I've been looking through the code of the GoogleIO Android app and I notice the their did not call stop() function on the GoogleAnalytics' instance. What will happen if we don't call stop()?
This is the code:
package com.google.android.apps.iosched.util;
import com.google.android.apps.analytics.GoogleAnalyticsTracker;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Build;
import android.preference.PreferenceManager;
import android.util.Log;
/**
* Helper singleton class for the Google Analytics tracking library.
*/
public class AnalyticsUtils {
private static final String TAG = "AnalyticsUtils";
GoogleAnalyticsTracker mTracker;
private Context mApplicationContext;
/**
* The analytics tracking code for the app.
*/
// TODO: insert your Analytics UA code here.
private static final String UACODE = "INSERT_YOUR_ANALYTICS_UA_CODE_HERE";
private static final int VISITOR_SCOPE = 1;
private static final String FIRST_RUN_KEY = "firstRun";
private static final boolean ANALYTICS_ENABLED = true;
private static AnalyticsUtils sInstance;
/**
* Returns the global {#link AnalyticsUtils} singleton object, creating one if necessary.
*/
public static AnalyticsUtils getInstance(Context context) {
if (!ANALYTICS_ENABLED) {
return sEmptyAnalyticsUtils;
}
if (sInstance == null) {
if (context == null) {
return sEmptyAnalyticsUtils;
}
sInstance = new AnalyticsUtils(context);
}
return sInstance;
}
private AnalyticsUtils(Context context) {
if (context == null) {
// This should only occur for the empty Analytics utils object.
return;
}
mApplicationContext = context.getApplicationContext();
mTracker = GoogleAnalyticsTracker.getInstance();
// Unfortunately this needs to be synchronous.
mTracker.start(UACODE, 300, mApplicationContext);
Log.d(TAG, "Initializing Analytics");
// Since visitor CV's should only be declared the first time an app runs, check if
// it's run before. Add as necessary.
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mApplicationContext);
final boolean firstRun = prefs.getBoolean(FIRST_RUN_KEY, true);
if (firstRun) {
Log.d(TAG, "Analytics firstRun");
String apiLevel = Integer.toString(Build.VERSION.SDK_INT);
String model = Build.MODEL;
mTracker.setCustomVar(1, "apiLevel", apiLevel, VISITOR_SCOPE);
mTracker.setCustomVar(2, "model", model, VISITOR_SCOPE);
// Close out so we never run this block again, unless app is removed & =
// reinstalled.
prefs.edit().putBoolean(FIRST_RUN_KEY, false).commit();
}
}
public void trackEvent(final String category, final String action, final String label,
final int value) {
// We wrap the call in an AsyncTask since the Google Analytics library writes to disk
// on its calling thread.
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... voids) {
try {
mTracker.trackEvent(category, action, label, value);
Log.d(TAG, "iosched Analytics trackEvent: "
+ category + " / " + action + " / " + label + " / " + value);
} catch (Exception e) {
// We don't want to crash if there's an Analytics library exception.
Log.w(TAG, "iosched Analytics trackEvent error: "
+ category + " / " + action + " / " + label + " / " + value, e);
}
return null;
}
}.execute();
}
public void trackPageView(final String path) {
// We wrap the call in an AsyncTask since the Google Analytics library writes to disk
// on its calling thread.
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... voids) {
try {
mTracker.trackPageView(path);
Log.d(TAG, "iosched Analytics trackPageView: " + path);
} catch (Exception e) {
// We don't want to crash if there's an Analytics library exception.
Log.w(TAG, "iosched Analytics trackPageView error: " + path, e);
}
return null;
}
}.execute();
}
/**
* Empty instance for use when Analytics is disabled or there was no Context available.
*/
private static AnalyticsUtils sEmptyAnalyticsUtils = new AnalyticsUtils(null) {
#Override
public void trackEvent(String category, String action, String label, int value) {}
#Override
public void trackPageView(String path) {}
};
}
As you can see, they start the tracker with 5 minutes interval mTracker.start(UACODE, 300, mApplicationContext); but never call the mTracker.stop() method. Will there be any consequences? Does it mean the service will dispatch the data even when the app is closed or stopped?
EasyTracker - I found this blog entry in the google analytics blog. There they describe an EasyTracker class, that wraps the normal Tracker class and has some nice features.
Configuration via resource file (no coding needed)
Everything's done in a separate thread (not in the ui-thread as with the normal Tracker)
...
And this EasyTracker does not need to be stopped explicity either.
There shouldn't be happen much -- when you close or stop the app, and even if it still lives in the background and there's no activity anymore, no new data will be gathered (because no one invokes track() anymore. So I think it's just good manners to explicitly stop the tracker.