Race condition in android dlopen()? - android

My Android app has a simple "loader" NativeActivity with a very simple android_main() which only loads a different shared object and passes control to it:
typedef void (*Tandroid_main)( android_app*);
void android_main( android_app* state )
{
void* glib = dlopen("libmain.so", RTLD_NOW);
void* fmain = dlsym(glib, "android_main");
Tandroid_main libmain = (Tandroid_main)fmain;
libmain(state)
}
This works well.. about half of the times. Other times it crashes since dlopen() fails and return NULL with errno=2 (No such file).
Due to the strange inconsistency of this occurrence I suspected a timing issue and indeed, adding a sleep(1) before dlopen() stopped it from happening. Something more robust than sleep(1) would be just trying it in a loop:
int count = 0;
void* glib = dlopen(soName, RTLD_NOW);
while(glib == NULL) {
sched_yield();
++count;
glib = dlopen(soName, RTLD_NOW);
}
The count I'm getting from this loop is usually something in the range of 10-70 on my device. But this is a hackish ugly solution.
What is really going on here? How come I can only load other shared objects only slightly after the NativeActivity starts? Is there a better way to find when when it's safe to load it?
It should be noted that I am also calling System.loadLibrary("main") from my NativeActivity's onCreate()

Not sure, but it is recommended to call loadLibrary() from a static initializer:
public class MainActivity extends Activity {
static {
System.loadLibrary("main")
}
...
}
Does it help?

Related

Xamarin.Forms leak on Android doing simple push/pop of page?

I'm investigating a memory leak (or leaks) which seem to be core to our Forms app.
We're using FreshMvvm, which previously had a pretty bad leak due to Pages/PageModels not being garbage collected. Michael Ridland (FreshMvvm) author) fixed that, which is great.
However, we're still seeing the figure returned by GC.GetTotalMemory() steadily increase. It does decrease, but the general trend is upwards.
I've stripped things right back so that I've just got a plain Page which pushes another page and we can then pop back to the first.
Doing this, I'm seeing GC.GetTotalMemory() generally increase by 2K - 4K every cycle. This is after forcibly calling GC.Collect();
We're running Xamarin.Forms 2.3.4.270 - I know this is quite old, but we really don't want to upgrade as we don't want to introduce 'new Forms' bugs! However, I have tried experimentally upgrading to 2.5.0.121934, and I see the same behaviour (as well as aspects of the app stop working with this version).
Short of using Profiler, are there any investigative techniques I can use to find the culprit?
My next step will be to strip more and more out!
I've encountered a similar issue in the past with Xamarin.Forms memory leaks and to help figure what views/pages were the culprit this class was created:
public static class Refs
{
private static readonly List<WeakReference> _refs = new List<WeakReference>();
public static readonly BindableProperty IsWatchedProperty = BindableProperty.CreateAttached("IsWatched", typeof(bool), typeof(Refs), false, propertyChanged: OnIsWatchedChanged);
public static bool GetIsWatched(BindableObject obj)
{
return (bool)obj.GetValue(IsWatchedProperty);
}
public static void SetIsWatched(BindableObject obj, bool value)
{
obj.SetValue(IsWatchedProperty, value);
}
private static void OnIsWatchedChanged(BindableObject bindable, object oldValue, object newValue)
{
AddRef(bindable);
}
public async static void AddRef(object p)
{
GC.Collect();
await Task.Delay(100);
GC.Collect();
_refs.Add(new WeakReference(p));
foreach (var #ref in _refs)
{
if (#ref.IsAlive)
{
var obj = #ref.Target;
Debug.WriteLine("IsAlive: " + obj.GetType().Name);
}
else
{
Debug.WriteLine("IsAlive: False");
}
}
Debug.WriteLine("---------------");
}
}
Then to use it you can use the IsWatched property in your pages xaml or in code behind you can just do Refs.AddRef(this); after InitializeComponent(); and just navigate to and from the page several times. If it is being gc'ed the console should print IsAlive: False if not it will print out the type.
Most often the leaks we had were fixed by clearing the BindingContext and/or setting some things to null when leaving the page.
protected override void OnParentSet()
{
base.OnParentSet();
if (Parent == null)
{
BindingContext = null;
}
}

Android application crashing when stepping into a method of a base class

I'm very new to Android programming and I am using Android Studio to target a Nexus 9. So far it's been a good/great experience.
I'm finally encountering some odd behavior though, when I tried something a little advanced -- sorry for the oddball question, I'd be happy with just some troubleshooting tips, since the observations don't give me much to go on. Here goes...
I have one class derived from another, as follows:
cWidget, which has method OnMyEvent()
cFrame extends cWidget, overrides OnMyEvent()
Each of my cWidgets (and hence my cFrames) has a linked list of "child" cWidgets (and/or cFrames), to form a tree structure. In both my cWidget.OnMyEvent() method and the cFrame.OnMyEvent() override I loop through the child cWidgets and call the OnMyEvent() on each -- so that my event is kind of "passed down" through the hierarchy by traversing the tree. My hope is that if the child is a cWidget it calls cWidget.OnMyEvent() and if it's actually a cFrame it calls the cFrame.OnMyEvent() override (this is how it would work in .NET, I should write some code to verify this is how it works here in Java but I realize now this is currently an assumption).
The problem: when I debug and set a breakpoint in cWidget.OnMyEvent() it never fires, even though there are definitely cWidgets in the tree. When I breakpoint on the call from cFrame.OnMyEvent() to a child cWidget.OnMyEvent(), and I inspect all the local variables, everything looks right; ie the child is a cWidget as expected, and nothing is null... but if I resume execution it still does not trip the breakpoint in the cWidget.OnMyEvent() as expected, it just passes over it. Even weirder, if I "Step Into" the call to cWidget.OnMyEvent(), my application halts with an "Unfortunately, MyFirstApp has stopped", and no exception report in my logcat.
So... very sorry for the long description but I'm not sure what's important and what's not. Without an exception report I'm not sure how to treat this problem, and there is some chance I am breaking some rules by linking together parents and children in the same tree and hoping Java knows whether to call the base method or the override. (This worked in .NET, and so far everything there has worked here but maybe not in this case.)
Thanks a lot for any thoughts or troubleshooting tips.
EDIT: I boiled it down and tested it, get similar results but still don't know why. I defined cA with an AddChild and Handle method, then derived cB and overrode Handle. I created a tree (two cAs as children of a cB) and then called cB handle. When I try to build a tree and call Handle it crashes. I'm guessing I'm trying a .NET trick that is disallowed here.
// *************
// BASE CLASS WITH AddChild, Handle
// *************
public class cA
{
public int m_Tag = 0;
protected cA ptr_FirstChild = null;
protected cA ptr_LastChild = null;
protected cA ptr_Parent = null;
protected cA ptr_NextSibling = null;
public cA(int tag)
{
m_Tag = tag;
}
public void AddChild(cA a)
{
a.ptr_Parent = this;
if (ptr_FirstChild == null)
{
ptr_FirstChild = a;
ptr_LastChild = a;
}
else
{
ptr_LastChild.ptr_NextSibling = a;
ptr_LastChild = a;
}
}
public void Handle()
{
int a;
a=3;
cA tmp = ptr_FirstChild;
while (tmp!= null)
{
tmp.Handle();
tmp = tmp.ptr_NextSibling;
}
}
}
// *************
// DERIVED CLASS, overrides Handle
// *************
public class cB extends cA
{
public cB(int tag)
{
super(tag);
}
#Override
public void Handle()
{
int a;
a=4;
cA tmp = ptr_FirstChild;
while (tmp!= null)
{
tmp.Handle();
tmp = tmp.ptr_NextSibling;
}
}
}
// *************
// Usage of classes
// build a tree with both cAs and cBs, then call
// root.Handle, hoping to traverse the tree.
// *************
public cA ptr_Root; // define the 'root' of the tree
cA a1 = new cA(1); // instantiate all leaves
cA a2 = new cA(2);
cB b1 = new cB(1);
ptr_Root = b1; // build the tree
b1.AddChild(a1);
b1.AddChild(a2);
b1.Handle(); // call Handle on the root, intending to traverse the tree, but this halts the program

Is it safe to write Android compatible code this way?

is it safe to write such compatible code on Android?
if (Build.os.SDK_INT >= 11) {
newClass instance = new newClass();
....
}
else {
oldClass instance = new oldClass();
....
}
one of my colleagues argue with me that ClassNotFoundException might be thrown up when running the above code since ClassLoader is attempting to load newClass on an android os device which is below android 11. But I've tried couple times, and didn't see this happen.
After googling around for couple hours, I didn't find any information on how and when android default classLoader loads a specific class.
You should check the compatability like the following... It gives you more accurate than the above:
private static int currentApi = 0;
public static int getApiLevel() {
if (currentApi > 0) {
return currentApi;
}
if (android.os.Build.VERSION.SDK.equalsIgnoreCase("3")) {
currentApi = 3;
} else {
try {
Field f = android.os.Build.VERSION.class.getDeclaredField("SDK_INT");
currentApi = (Integer) f.get(null);
} catch (Exception e) {
return 0;
}
}
return currentApi;
}
you can alway use reflection to check if the class exists:
try {
Class.forName("yourclass")
} catch (ClassNotFoundExecption) {
oldClass instance = new oldClass();
}
Yes, this is safe to do on recent versions of Android. I want to say froyo and above, but it may have been even earlier than that. I don't recall for sure.
What happens is that dalvik performs a verification pass on the dex file at install time. For any classes/methods/fields that it can't resolve, it replaces those accesses with an instruction that throws a VerifyError.
In your example, if that code got loaded on, e.g. api 10, newClass instance = new newClass() would conceptually be replaced with throw new VerifYError(). So as long as that branch never gets executed at runtime, everything is good.
Short answer - don't do it.
Most VMs only load a class when it is absolutely needed. However a class loader is allowed to cache binary representation of classes beforehand[1].
Class loaders are allowed to cache binary representations of types,
load types early in anticipation of eventual use, or load types
together in related groups.
[1] - http://www.artima.com/insidejvm/ed2/lifetype2.html
[2] - http://developer.android.com/tools/extras/support-library.html
Edit - Have you checked if the class you need is available as part of the android support package ? [2]

How to restart an Activity automatically after it crashes?

Is there a way for me to create a service to track my activity class and restart it after a crash? Note that i CANNOT use uncaughthandlers thread method to restart my app. My app is supposed to crash, don't worry about that part. My app is something simple, like this
private class AudioRenderer extends Activity {
private MediaPlayer AudioRenderer(String filePath) {
File location = new File(filePath);
Uri path = Uri.fromFile(location);
mp= MediaPlayer.create(this, path);
}
return mp
}
Once this crashes, the service listening in the background will restart my app automatically. Anybody knows how this is possible? Thanks!
You can do that, yes, as explained below. But if such techniques may make sense for experiments, they are definitely not suitable for production. That would be awfully ugly and unefficient.
This said, here is a way to go:
Make your Service sticky or redelivered to ensure it will always be running after having been started once and not explicitely stopped.
in your Activity class, statically store WeakReferences pointing to all its running instances and provide a way to statically check whether at least one of them is currently allocated:
public class MyActivity extends Activity {
private static ArrayList<WeakReference<MyActivity >> sMyInstances = new ArrayList<WeakReference<MyActivity >>();
public MyActivity() {
sMyInstances.add(new WeakReference<MyActivity >(this));
}
private static int nbInstances() {
int ret = 0;
final int size = sMyInstances.size();
for (int ctr = 0; ctr < size; ++ctr) {
if (sMyInstances.get(ctr).get() != null) {
ret++;
}
}
return ret;
}
}
(WeakReference are references to objects that do not prevent these objects to be garbage-collected, more details here)
Then, from your Service, call MyActivity.nbInstances() from time to time. It will return 0 a (usually short but theoretically unpredictable) while after the crash of the last running MyActivity instance. Warning: it will do so unless you have a memory leak concerning this Activity or its underlying Context as this leak would prevent the garbage collection of the instance that crashed.
Then you just have to start a new instance of your Activity from your Service, using startActivity(Intent)

Should accessing SharedPreferences be done off the UI Thread?

With the release of Gingerbread, I have been experimenting with some of the new API's, one of them being StrictMode.
I noticed that one of the warnings is for getSharedPreferences().
This is the warning:
StrictMode policy violation; ~duration=1949 ms: android.os.StrictMode$StrictModeDiskReadViolation: policy=23 violation=2
and it's being given for a getSharedPreferences() call being made on the UI thread.
Should SharedPreferences access and changes really be made off the UI thread?
I'm glad you're already playing with it!
Some things to note: (in lazy bullet form)
if this is the worst of your problems, your app's probably in a good spot. :) Writes are generally slower than reads, though, so be sure you're using SharedPreferenced$Editor.apply() instead of commit(). apply() is new in GB and async (but always safe, careful of lifecycle transitions). You can use reflection to conditionally call apply() on GB+ and commit() on Froyo or below. I'll be doing a blogpost with sample code of how to do this.
Regarding loading, though...
once loaded, SharedPreferences are singletons and cached process-wide. so you want to get it loaded as early as possible so you have it in memory before you need it. (assuming it's small, as it should be if you're using SharedPreferences, a simple XML file...) You don't want to fault it in the future time some user clicks a button.
but whenever you call context.getSharedPreferences(...), the backing XML file is stat'd to see if it's changed, so you'll want to avoid those stats during UI events anyway. A stat should normally be fast (and often cached), but yaffs doesn't have much in the way of concurrency (and a lot of Android devices run on yaffs... Droid, Nexus One, etc.) so if you avoid disk, you avoid getting stuck behind other in-flight or pending disk operations.
so you'll probably want to load the SharedPreferences during your onCreate() and re-use the same instance, avoiding the stat.
but if you don't need your preferences anyway during onCreate(), that loading time is stalling your app's start-up unnecessarily, so it's generally better to have something like a FutureTask<SharedPreferences> subclass that kicks off a new thread to .set() the FutureTask subclasses's value. Then just lookup your FutureTask<SharedPreferences>'s member whenever you need it and .get() it. I plan to make this free behind the scenes in Honeycomb, transparently. I'll try to release some sample code which
shows best practices in this area.
Check the Android Developers blog for upcoming posts on StrictMode-related subjects in the coming week(s).
Accessing the shared preferences can take quite some time because they are read from flash storage. Do you read a lot? Maybe you could use a different format then, e.g. a SQLite database.
But don't fix everything you find using StrictMode. Or to quote the documentation:
But don't feel compelled to fix everything that StrictMode finds. In particular, many cases of disk access are often necessary during the normal activity lifecycle. Use StrictMode to find things you did by accident. Network requests on the UI thread are almost always a problem, though.
One subtlety about Brad's answer: even if you load the SharedPreferences in onCreate(), you should probably still read values on the background thread because getString() etc. block until reading the shared file preference in finishes (on a background thread):
public String getString(String key, String defValue) {
synchronized (this) {
awaitLoadedLocked();
String v = (String)mMap.get(key);
return v != null ? v : defValue;
}
}
edit() also blocks in the same way, although apply() appears to be safe on the foreground thread.
(BTW sorry to put this down here. I would have put this as a comment to Brad's answer, but I just joined and don't have enough reputation to do so.)
I know this is an old question but I want to share my approach. I had long reading times and used a combination of shared preferences and the global application class:
ApplicationClass:
public class ApplicationClass extends Application {
private LocalPreference.Filter filter;
public LocalPreference.Filter getFilter() {
return filter;
}
public void setFilter(LocalPreference.Filter filter) {
this.filter = filter;
}
}
LocalPreference:
public class LocalPreference {
public static void saveLocalPreferences(Activity activity, int maxDistance, int minAge,
int maxAge, boolean showMale, boolean showFemale) {
Filter filter = new Filter();
filter.setMaxDistance(maxDistance);
filter.setMinAge(minAge);
filter.setMaxAge(maxAge);
filter.setShowMale(showMale);
filter.setShowFemale(showFemale);
BabysitApplication babysitApplication = (BabysitApplication) activity.getApplication();
babysitApplication.setFilter(filter);
SecurePreferences securePreferences = new SecurePreferences(activity.getApplicationContext());
securePreferences.edit().putInt(Preference.FILER_MAX_DISTANCE.toString(), maxDistance).apply();
securePreferences.edit().putInt(Preference.FILER_MIN_AGE.toString(), minAge).apply();
securePreferences.edit().putInt(Preference.FILER_MAX_AGE.toString(), maxAge).apply();
securePreferences.edit().putBoolean(Preference.FILER_SHOW_MALE.toString(), showMale).apply();
securePreferences.edit().putBoolean(Preference.FILER_SHOW_FEMALE.toString(), showFemale).apply();
}
public static Filter getLocalPreferences(Activity activity) {
BabysitApplication babysitApplication = (BabysitApplication) activity.getApplication();
Filter applicationFilter = babysitApplication.getFilter();
if (applicationFilter != null) {
return applicationFilter;
} else {
Filter filter = new Filter();
SecurePreferences securePreferences = new SecurePreferences(activity.getApplicationContext());
filter.setMaxDistance(securePreferences.getInt(Preference.FILER_MAX_DISTANCE.toString(), 20));
filter.setMinAge(securePreferences.getInt(Preference.FILER_MIN_AGE.toString(), 15));
filter.setMaxAge(securePreferences.getInt(Preference.FILER_MAX_AGE.toString(), 50));
filter.setShowMale(securePreferences.getBoolean(Preference.FILER_SHOW_MALE.toString(), true));
filter.setShowFemale(securePreferences.getBoolean(Preference.FILER_SHOW_FEMALE.toString(), true));
babysitApplication.setFilter(filter);
return filter;
}
}
public static class Filter {
private int maxDistance;
private int minAge;
private int maxAge;
private boolean showMale;
private boolean showFemale;
public int getMaxDistance() {
return maxDistance;
}
public void setMaxDistance(int maxDistance) {
this.maxDistance = maxDistance;
}
public int getMinAge() {
return minAge;
}
public void setMinAge(int minAge) {
this.minAge = minAge;
}
public int getMaxAge() {
return maxAge;
}
public void setMaxAge(int maxAge) {
this.maxAge = maxAge;
}
public boolean isShowMale() {
return showMale;
}
public void setShowMale(boolean showMale) {
this.showMale = showMale;
}
public boolean isShowFemale() {
return showFemale;
}
public void setShowFemale(boolean showFemale) {
this.showFemale = showFemale;
}
}
}
MainActivity (activity that get called first in your application):
LocalPreference.getLocalPreferences(this);
Steps explained:
The main activity calls getLocalPreferences(this) -> this will read your preferences, set the filter object in your application class and returns it.
When you call the getLocalPreferences() function again somewhere else in the application it first checks if it's not available in the application class which is a lot faster.
NOTE: ALWAYS check if an application wide variable is different from NULL, reason -> http://www.developerphil.com/dont-store-data-in-the-application-object/
The application object will not stay in memory forever, it will get killed. Contrary to popular belief, the app won’t be restarted from scratch. Android will create a new Application object and start the activity where the user was before to give the illusion that the application was never killed in the first place.
If I didn't check on null I would allow a nullpointer to be thrown when calling for example getMaxDistance() on the filter object (if the application object was swiped from the memory by Android)
SharedPreferences class does some reads & writes within XML files on disk, so just like any other IO operation it could be blocking. The amount of data currently stored in SharedPreferences affects the time and resource consumed by the API calls. For minimal amounts of data it's a matter of a few milliseconds (sometimes even less than a millisecond) to get/put data. But from the point of view of an expert it could be important to improve the performance by doing the API calls in background. For an asynchronous SharedPreferences I suggest checking out the Datum library.
i do not see any reason to read them from a background thread. but to write it i would. at startup time the shared preference file is loaded into memory so its fast to access, but to write things can take a bit of time so we can use apply the write async. that should be the difference between commit and apply methods of shared prefs.

Categories

Resources