When using the console of AndroidStudio on my app it shows:
W/System: A resource failed to call release.
Sometimes it is said multiple times. I know what it means but I've checked the almost 2k lines of code multiple times but I'm clueless what I'm missing to close/release.
Is there any way to expand on this information from the console? or how would you do to target what the resource is or when it fails to close? Any ideas are welcome. Thanks.
The answer from #guest works, but you can achieve the exact same thing without resorting to reflection using Strict Mode. Specifically, something like:
StrictMode.setVmPolicy(new VmPolicy.Builder()
.detectLeakedClosableObjects()
.penaltyLog()
.build());
In general, strict mode can do much more for you though (see link above to doc), and all you need to do for a default setup is:
StrictMode.enableDefaults(); # <-- This includes warning on leaked closeables
To enable strict mode "as soon as possible" you can add either of the above code options to the constructor of your application class, e.g.:
public class MyApp extends Application {
public MyApp() {
if(BuildConfig.DEBUG)
StrictMode.enableDefaults();
}
}
Note that in addition to just creating the class above, you need to tell Android that you have created a custom application class in the AndroidManifest.xml (so that an instance of it gets created when your application process starts, instead of Android creating the default Application class). You need to add/modify the android:name attribute of the <application> tag to point to the fully resolved package path of your custom application class (MyApp in this case):
<application
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:name="com.example.app.MyApp" <-- IMPORTANT PART: ADAPT FOR YOUR ACTUAL PROJECT
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
This message comes from dalvik.system.CloseGuard. When debugging, you can set it up to create stack traces as you create resources, so that you can track down what objects aren't being closed.
It's not part of the framework API, so I'm using reflection to turn that on:
try {
Class.forName("dalvik.system.CloseGuard")
.getMethod("setEnabled", boolean.class)
.invoke(null, true);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
more info: https://wh0.github.io/2020/08/12/closeguard.html
I don't think that you can get any more information out of Logcat.
The memory view of the Android Profiler is probably a good place to start. Looking at it while using your app should give you an idea of what actions cause memory to be allocated and not released.
You can also select sections from the timeline and drill down to specific allocations by
class.
Alternatively LeakCanary is a great library to detect memory leaks.
Fixed it by removing a function call that called itself through another function thereby making an infinite loop of calls to itself
Late answer, but it may be useful to someone else:
I faced the same error, but I forgot I had my VPN running in the background. Disconnecting the VPN did the trick for me.
This is to say it may be due to resources unrelated to your app or IDE you may want to check, like an antivirus, VPN, etc.
Related
I've been trying to get my app to be able to delete an audio file. However, after trying many possible solutions, I couldn't really find one that works.
Here is my solution so far:
public static void deleteFiles(List<Track> tracks, Context context,
final MutableLiveData<IntentSender> deletionIntentSenderLD){
final Uri AUDIO_URI = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
for(Track t : tracks){
try {
context.getContentResolver().delete(ContentUris
.withAppendedId(AUDIO_URI, t.getUriId()), null, null);
}catch (SecurityException securityException) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (securityException instanceof RecoverableSecurityException) {
deletionIntentSenderLD
.postValue(((RecoverableSecurityException) securityException)
.getUserAction().getActionIntent().getIntentSender());
} else
throw securityException;
} else
throw securityException;
}
}
}
When the try block fails a SecurityException is catch then the IntentSender is passed to the live data that is observed in a fragment:
audioViewModel.getDeletionIntentSenderLD().observe(getViewLifecycleOwner(),
intentSender -> {
try {
startIntentSenderForResult(intentSender, DELETE_PERMISSION_REQUEST,
null, 0 ,0, 0,
null);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
});
I've tried implementing the onRequestPermissionResult() method but that doesn't do anything. I've also tried deleting the files using File file = new File(), however, due to the changes made to Android 10, I didn't expect it to work.
So after many Google searches, I've come to the conclusion that the best approach (to my knowledge) is to simply turn off scoped storage for Android Q (10).
Here, I'll provide two solutions. The first is the one where I turn it off and the second is the one where scope storage is still enable. However, a thing you should note is that the second solution is a little buggy, at times it actually does delete both the actual media file and updates the Media Store, but most times it simply deletes from the Media Store only. Obviously, this isn't a very good solution as on reboot your application would then load those files back in because the Media Store would scan for them.
Solution 1 - Turn off Scoped Storage
For this solution you can still target Android 11. All you have to do is go to the build.gradle file at the Module Level and set the compileSdkVersion and targetSdkVersion to 30.
After that, you go into the AndroidManifest.xml and have the uses-permission and application tag set up like this:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="29"
tools:ignore="ScopedStorage"/>
<application
android:requestLegacyExternalStorage="true"
...
After having done that, you could use the Content Resolver to delete the media file (and update the Media Store) and you do not have to worry about catching a security exception like its said in the Android docs. Your implementation for Android 11s delete operation should not be affected.
Solution-ish 2 - Turn on Scoped Storage
Firstly, in your manifest ensure that the WRITE_EXTERNAL_STORAGE permissions maxSdkVersion is set to 28. Also ensure that requestLegacyExternalStorage is set to false (don't think this is required). Then simply copy the code in my original post. You do not require a Live Data if you are doing the delete operation from your activity/fragment. But you should note that startIntentSenderForResult() requires an activity.
But as I mentioned before, I did experience some bugs with this. The most frustrating thing about this solution though is that it does not delete the actual file but instead deletes the entry from the Media Store. Maybe this has something to do with the fact that #blackapps mentioned, which is that you cannot bulk delete and I might have implemented it slightly wrong. Nevertheless, this is horrible for user experience if bulk deletion is impossible in Android 10.
The tutorials I followed for this are:
https://developer.android.com/training/data-storage/shared/media#remove-item
https://www.raywenderlich.com/9577211-scoped-storage-in-android-10-getting-started#toc-anchor-007
https://www.solutionanalysts.com/blog/scoped-storage-in-android-10/
Side Note - Delete on Android 11
To delete on Android 11 you just need to call createDeleteRequest() which should return a PendingIntent. From this PendingIntent you could get the IntentSender by using getIntentSender. Pass this intent sender to the activity/fragment then call startIntentSenderForResult() in your activity/fragment. This pops up a dialog to the user asking them if the application can delete a file. If the user gives permission the system goes ahead and deletes the file and updates the Media Store.
Side Side Note - Scoped Storage, Android 10 and Future
From everything I've seen, it seems to suggest that scoped storage is only enforced in Android 11 but I'm not entirely sure if the legacy option would still be available in Android 10 indefinitely. But I would have to do more research on this...
I am working on a GUI application that uses JavaFX(not fxml) and exported as a JAR. For slow machine, impatient user click more than once on JAR, and multiple instances of application started.
I'm looking for a solution to let only one instance can be run at a time on a system and if the user clicks again while the application is running nothing happens. I think it's called singleton but don't know how to implement it.
You could try JUnique. It's an open source library doing exactly what you ask for. Import junique-1.0.4.jar to your project as a library. It's just 10kb file.
It's manual neatly describes how to implement it on a project. For a JavaFX application, implementation would look something like this:
Make sure to import these classes to your main
import it.sauronsoftware.junique.AlreadyLockedException;
import it.sauronsoftware.junique.JUnique;
public static void main(String[] args) {
String appId = "myapplicationid";
boolean alreadyRunning;
try {
JUnique.acquireLock(appId);
alreadyRunning = false;
} catch (AlreadyLockedException e) {
alreadyRunning = true;
}
if (!alreadyRunning) {
launch(args); // <-- This the your default JavaFX start sequence
}else{ //This else is optional. Just to free up memory if you're calling the program from a terminal.
System.exit(1);
}
}
One easy solution that I've used is, when you start the application, it creates a file (I named it .lock but you can call it whatever you want), unless the file already exists, in which case the application terminates its execution instead of creating the file.
You will need to bind your application with a resource. It can be a file, port etc.
You can change the code on startup to check if the file is locked. The below code will give you some idea
FileOutputStream foStream = new FileOutputStream("/tmp/testfile.txt");
FileChannel channel = fileOutputStream.getChannel();
FileLock lock = channel.lock();
If you'd properly package your JavaFX code as a real application instead of just throwing it into a jar, you might get that functionality for free and without all these hacks. If I package my JavaFX code on my Mac with the jpackage tool, the result will be a full featured macOS application. That means that when I double-click its icon somewhere several times, only one instance of the application will be started. This is the default behaviour on Macs and properly packaged JavaFX applications just stick to that rule too. I can't say however what the behaviour on Windows or Linux is because I currently don't have such a box running. Maybe someone who knows can add this as a comment.
This is happening in several places for multiple class types but I'll stick with a button example for now.
So I have a button which I want talkback to announce as "Play". The content description is set to "Play". However, talkback is also announcing the class too, so it reads as "Play Button".
I tried a solution I found elsewhere by overloading the onInitializeAccessibilityNodeInfo method
private void setupContentDescriptors() {
mPlayPauseButton.setAccessibilityDelegate(new View.AccessibilityDelegate() {
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info)
{
super.onInitializeAccessibilityNodeInfo(host, info);
//blanked to prevent talkback from announcing class/type
info.setClassName("");
info.setContentDescription("Play");
}
});
}
Setting the class name to "" worked perfectly, but I soon found out this solution only worked for API 23 and above.
According to the docs, "Starting in API 23, delegate methods are called after host methods, which all properties to be modified without being overwritten by the host class."
I've tried several other methods to no avail.
Ideas?
Prior to API 23, you will need to create a subclass and implement onInitializeAccessibilityNodeInfo() if you need to override the class name. You cannot override it by using a delegate.
That said, TalkBack is attempting to provide a consistent and high-quality experience for your user by speaking role descriptions. In the vast majority of cases, you should not attempt to override this behavior.
If you have a small and well-known user circle, maybe this is another alternative to the answer of alanv.
In Talkback 5.2.1(*) you can do this:
Under "Settings -> Accessibility -> Talkback -> Settings -> Verbosity
There you can switch on / off the entry "Speak element type".
With this the user itself can decide if he wants to hear the element type or not. This is another argument NOT to tinker with the way Talkback reads elements.
(*) I did not find any documentation about when the verbosity-setting for speaking elements was introduced. On my Android devices with Talkback 5.2.1 it's working, while devices with Talkback 5.0.3 dont have this setting. So anywhere in between it had to be introduced.
have you tried
ViewCompat.setAccessibilityDelegate(mPlayPauseButton, new
AccessibilityDelegateCompat() {
#Override
public void onInitializeAccessibilityNodeInfo(View host,
AccessibilityNodeInfoCompat info) {
super.onInitializeAccessibilityNodeInfo(host, info);
info.setClassName(null);
info.setContentDescription("your label");
}
})
ViewCompat should take care of the version handling.
I need to check dynamically if the used device supports openGL ES 3.0.
How can I do that?
I can't find anything in google or here...
I'm going to expand on this answer. There's the initial part in that answer with the configuration info and getting the reqGlEsVersion. Note that it gets the actual OpenGL version of the device, not the required one declared in the manifest as suggested in some comments.
However, I stumbled over a fairly obvious method in the ConfigurationInfo class called getGlEsVersion. It relies on the reqGlEsVersion system in the ConfigurationInfo class, and returns a String value. With some tiny setup, I made a simple test snippet:
ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo();
System.out.println(Double.parseDouble(configurationInfo.getGlEsVersion()));
System.out.println(configurationInfo.reqGlEsVersion >= 0x30000);
System.err.println(String.format("%X", configurationInfo.reqGlEsVersion));
The device I tested on supports and uses GLES 3.2, and the test app declares GLES3.0 in the manifest
Runnig this prints:
3.2
true
30002 //A note about this one: It doesn't print as 0x30002 because of the printing method. But it is the same as 0x30002
The entire point of those three print statements was to show that either way works. It prints the version supported of the device, and not that declared in the manifest as mentioned in some comments.
So you can use either of these ways to check the version of OpenGL ES on a given device. Personally, I use this:
double version = Double.parseDouble(configurationInfo.getGlEsVersion());
System.out.println("Version: " + version);//Optional obviously, but this is the reason I use the String and parse it to a double
if(version < 3)
throw new RuntimeException("Unsupported GLES version");
but this also works (and is slightly more memory efficient):
int version = configurationInfo.getGlEsVersion();
//format and print here if wanted
if(version < 0x30000)
throw new RuntimeException("Unsupported GLES version");
you could of course show a toast and quit, a dialog, an activity, a fragment, whatever you feel like if the user doesn't meet the requirement. You also should, because if it comes from a third party site, there's a chance requirements were bypassed. I just throw an exception because I declare the usage of GLES3 in the manifest, meaning that throw shouldn't happen in the first place.
As for the manifest tag:
<uses-feature android:glEsVersion="0x00030000" android:required="true" />
This doesn't prevent apps with GLES2 or lower from installing it if they got for an instance a direct install from an APK file or through USB debugging. It's a flag that tells Google Play (or any other stores that exist and check for that) that GLES < (in this case) 3 isn't supported and Google Play would prevent the install. But anyone could install it from a mirror that ignores things like that, so the tag itself isn't a way of preventing GLES2 and lower devices from installing it.
And there's of course the eglCreateContext() method, but that only works with the native system. It does not work with things like LibGDX or anything that uses a separate renderer since it would create a different context than the library would use.
And glGetString(GL_VERSION) would work mostly anywhere, assuming the method is supported by the library/framework. It's natively integrated into GLES2 though, and requires actually creating the OpenGL context first. Using the first methods seem like a better choice on Android. However, this is, of course, up to you.
yes download CPU-Z and every piece of info of your phone is in this app.
its on the play store as: CPU-Z.
I think this code will help you
final ActivityManager activityManager =
(ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
final ConfigurationInfo configurationInfo =
activityManager.getDeviceConfigurationInfo();
final boolean supportsEs2 = configurationInfo.reqGlEsVersion >= 0x30000;
this article can help you more
http://www.learnopengles.com/android-lesson-one-getting-started/
This is documented here in the Android SDK documentation:
http://developer.android.com/guide/topics/graphics/opengl.html#version-check
You basically have 3 options:
If your app only works with ES 3.0, you request that version in your manifest:
<uses-feature android:glEsVersion="0x00030000" android:required="true" />
You try to create an ES 3.0 context using eglCreateContext(), and check if it succeeds.
You create an ES 2.0 context, and then check the supported version with glGetString(GL_VERSION).
The link above has sample code for solutions 2 and 3.
I'm trying get some licensing code from AndroidPit.com working, but I get "Unable to start service Intent". Basically my code looks like this:
Intent licenseIntent = new Intent("de.androidpit.app.services.ILicenseService");
if (mContext.bindService(licenseIntent, this, Context.BIND_AUTO_CREATE))
{
// success
}
else
{
// failure (I get this all the time(
}
I tried passing ILicenseService class explicitly:
Intent licenseIntent = new Intent(mContext, de.androidpit.app.services.ILicenseService.class);
but I still get the same problem.
I managed to get Android Market LVL library working which uses identical code, so I don't really understand why it fails to find "de.androidpit.app.services.ILicenseService", but manages to find "com.android.vending.licensing.ILicensingService".
Most of the answers I found here say that you need to append stuff to your AndroidManifest.xml, but you don't anything for "com.android.vending.licensing.ILicensingService" to work, so I guess I shouldn't need anything "de.androidpit.app.services.ILicenseService" (they both derive from android.os.IInterface).
Thanks in advance.
Most of the answers I found here say that you need to append stuff to your AndroidManifest.xml
Those answers are correct.
but you don't anything for "com.android.vending.licensing.ILicensingService" to work
That is because com.android.vending.licensing.ILicensingService is a remote service, one that is not in your project, but rather in the firmware of the device.
so I guess I shouldn't need anything "de.androidpit.app.services.ILicenseService" (they both derive from android.os.IInterface).
That is flawed reasoning. By your argument, java.util.HashMap does not go in the manifest, and both java.util.HashMap and any implementation of Activity all derive from Object, so therefore you do not need to put your activities in the manifest. If you try this, you will quickly discover that your activities no longer work.
If it is a component (activity, service, content provider, or some implementations of BroadcastReceiver), and the implementation of the component is in your project (directly, via a JAR, via a library project, etc.), you must have an entry in the manifest for it.
Wherever you got the service from should provide you with instructions for adding the service to your manifest, and they should also supply you with instructions for creating the Intent used to bind to it. If they do not provide this documentation, perhaps you should reconsider your use of this product.
The solution in my case was to start a server part on my phone (AppCenter from AndroidPit.com in this case). No entries in AndroidManifest are necessary for the client application.