Avoid upfront or Fix later!! Memory Leak in Android.

All the possible reasons for memory leaks in the Android Sub-System are due to incorrect handling of several components like Fragments, Handler etc.

Narendra Harny
Make Android

--

Photo by Maria Teneva on Unsplash

What is a memory leak?

When any object is created the memory will be allocated for that and once that object is no longer required it has to be cleared or deallocated from the memory. If the memory allocated to the object is not cleared for any reason the unused or allocated memory block is called a memory leak.

Memory leaks are a common problem among all programming languages due to different reasons. It can happen due to several programming mistakes.

Here is the list of all the reasons that can cause the memory leak issue!!

Unclosed resources

Unclosed references typically refer to references to objects that are not properly released or dereferenced when they are no longer needed, If those objects are still holding references to other objects or resources can lead to a memory leak.

For Example: If a class manages resources like files, database connections, or network connections, it’s crucial to ensure that you properly close or release those resources when they are no longer required.

Here’s a simplified example using file I/O:

 public class FileProcessor {
private BufferedReader reader;
public FileProcessor(String filePath) {
try {
FileReader fileReader = new FileReader(filePath);
this.reader = new BufferedReader(fileReader);
} catch (IOException e) { e.printStackTrace(); }
}
public String readLine() throws IOException {
return reader.readLine();
} // Missing close method
}

In this example, If an instance of File Processor is created but the close method is not called explicitly, Buffered Reader and File Reader resources won’t be released properly, potentially leading to a memory leak.

It’s essential to explicitly release resources using methods like close, disconnect, or other resource-specific cleanup methods when dealing with objects that manage external resources. The use of try-with-resources in Java can also help automatically close resources when they go out of scope.

How to prevent memory leaks due to Unclosed resources?

Cursor Objects

Cursor cursor = // obtain cursor
// Don't forget to close the cursor
cursor.close();java

Network Resource

HttpURLConnection connection = // obtain connection
// Use the connection
// …
// Close the connection
connection.disconnect();

AsyncTask or Threads

AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
// AsyncTask implementation
};
// Execute the task
task.execute();
// Ensure proper cleanup, e.g., cancelling the task
task.cancel(true);

Database Connections

SQLiteDatabase db = // obtain database
// Use the database
// …
// Close the database
db.close();

Camera and Sensor Resources

Camera camera = // obtain camera instance
// Use the camera
// …
// Release the camera when done
camera.release();

Broadcast Receiver

If you start services or use Broadcast Receiver, ensure that their resources are released appropriately. Unregister receivers and stop services when they are no longer needed.

Static references

If the static reference holds onto an object longer than necessary, it can lead to a memory leak because the object won’t be eligible for garbage collection when it’s no longer needed.

Here’s a simplified example:

public class SingletonPattern {
private static SingletonPattern instance;
private SingletonPattern() {
// Private constructor to enforce singleton pattern
}
public static SingletonPattern getInstance() {
if (instance == null) {
instance = new SingletonPattern();
}
return instance;
}
// Other class members…
}

In this example, SingletonPattern is a common pattern for creating a singleton object. However, if the instance reference is not set to null when it’s no longer needed, it will keep the object in memory even if the application no longer requires it.

To avoid memory leaks with static references, it’s important to nullify or release static references when they are no longer needed, especially in the context of Android components like Activities or Fragments. Otherwise, these references can outlive the normal lifecycle of objects, leading to increased memory usage and potential performance issues. Properly managing static references is crucial for preventing memory leaks in Android applications.

Avoid Creating Static References for specific APIs.

Application Context

// Instead of using a static reference to an activity
// Use application context
Context appContext = getApplicationContext();

Avoid Static References to UI Components

Avoid holding static references to UI components like Activity or Fragment. If you need to pass data between components, consider using explicit intents, callbacks, or interfaces instead of static references.

Use Weak Reference for Contexts

This allows the context to be garbage collected when it’s no longer needed

private static WeakReference<Context> contextReference;
// Store the context
contextReference = new WeakReference<>(context);

// Retrieve the context
Context context = contextReference.get();

if (context != null) // Use the context
}

Nullify Resources in onDestroy

public class MyActivity extends AppCompatActivity {
private static MyActivity instance;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
instance = this;
}

@Override
protected void onDestroy() {
super.onDestroy();
instance = null; // Nullify the instance
}
}

Use ApplicationContext in Singleton Classes

If you have singleton classes, use the application context instead of holding references to activity or fragment contexts. This helps avoid leaks associated with longer-lived singletons.

Manage Long-lived context objects.

A “long-lived context object” typically refers to holding a reference to a Context object for an extended period, especially in situations where it is associated component’s lifecycle.

The Context holds a reference to the Android application’s resources, and if not managed properly, it prevents those resources from being garbage collected.

For example, storing a reference to an Activity or Application Context in a static field or within a long-lived singleton can be problematic.

For example:

public class Singleton {
private static Context appContext;
public static void setContext(Context context) {
appContext = context;
}
public static Context getContext() {
return appContext;
}
}

If the set Context method is called with an Activity context, and this singleton is not appropriately managed (e.g., not setting app Context to null when the associated activity is destroyed), it can lead to a memory leak because the singleton retains a reference to the activity’s context, preventing it from being garbage collected.

To avoid such memory leaks, it’s crucial to be cautious with how and where you store references to Context objects.

Use application context when possible, prefer weak references, or ensure that references are appropriately cleared when no longer needed, especially in long-lived objects or singletons.

Additionally, consider using the Android X library’s View Model for retaining data across configuration changes, as it automatically handles the lifecycle of associated Context objects.

How to use Handler and Runnable instances

In Android, using Handler and Runnable can lead to memory leaks if not used carefully, particularly when there are references to an Activity or Fragment within the Runnable that is associated component’s lifecycle. This is a common scenario known as an implicit reference, and it can prevent the Activity or Fragment from being garbage collected, even after it’s no longer needed.

For example:

public class MainActivity extends AppCompatActivity {

private Handler handler = new Handler();

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Schedule a Runnable with the Handler
handler.postDelayed(new TestRunnable(), 5000);
}

private class TestRunnable implements Runnable {
@Override
public void run() {
// Perform some task referencing MyActivity
// For example, updating UI elements
TextView textView = findViewById(R.id.textView);
textView.setText("Task completed");
}
}
}

In this example, TestRunnable has an implicit reference to the outer MainActivity class because it updates a UI element within the Activity.

If the Activity is finished or destroyed before the Runnable executes, it will still hold a reference to the Activity, preventing it from being garbage collected. This can lead to a memory leak.

To address this issue, it’s important to use a WeakReference or ensure that the Runnable doesn’t hold strong references to the Activity or any other long-lived objects.

For Example:

public class MainActivity extends AppCompatActivity {
private Handler handler = new Handler();

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Schedule a Runnable with the Handler
handler.postDelayed(new MyRunnable(this), 5000);
}

private static class TestRunnable implements Runnable {
private WeakReference<MainActivity> activityReference;

TestRunnable(MainActivity activity) {
this.activityReference = new WeakReference<>(activity);
}

@Override
public void run() {
MainActivity activity = activityReference.get();
if (activity != null) {
// Perform some task referencing MyActivity
// For example, updating UI elements
TextView textView = activity.findViewById(R.id.textView);
textView.setText("Task completed");
}
}
}
}

Using WeakReference allows the Activity to be garbage collected if it’s no longer needed, preventing memory leaks.

Remove Handler Object’s Callbacks in onDestroy

@Override
protected void onDestroy() {
super.onDestroy();
// Remove callbacks to avoid memory leaks
handler.removeCallbacks(myRunnable);
}

Use getMainLooper for UIUpdate-related handlers

Handler uiHandler = new Handler(Looper.getMainLooper());

Memory leaks in views.

A common scenario for a memory leak in Android involving a View is when you have a custom View or a View within an Activity or Fragment, and you hold a strong reference to the Activity or Fragment within the View or a related callback. This can lead to the Activity or Fragment not being garbage collected.

For Example:

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

CustomView customView = findViewById(R.id.customView);

customView.setListener(new CustomViewListener() {
@Override
public void onViewClicked() {
// Perform some action referencing MyActivity
// For example, start a new activity
Intent intent = new Intent(MyActivity.this, AnotherActivity.class);
startActivity(intent);
}
});
}
}
public class CustomView extends View {
private CustomViewListener listener;

public CustomView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}

public void setListener(CustomViewListener listener) {
this.listener = listener;
}

@Override
public boolean onTouchEvent(MotionEvent event) {
// Trigger the listener callback when the view is clicked
if (event.getAction() == MotionEvent.ACTION_UP && listener != null) {
listener.onViewClicked();
}
return true;
}
}

==============================================================================

public interface CustomViewListener {
void onViewClicked();
}

In this example, the CustomView holds a reference to the CustomViewListener, which is an anonymous inner class implementing the interface. The anonymous inner class also implicitly holds a reference to the outer MainActivity. If the CustomView is held for a longer duration (e.g., by a singleton or a long-lived object), it prevents the MainActivity from being garbage collected even after it is destroyed.

To avoid such memory leaks, consider using a WeakReference or clearing references appropriately. Here’s an updated version using WeakReference:

public class CustomView extends View {
private WeakReference<CustomViewListener> listenerReference;

public CustomView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}

public void setListener(CustomViewListener listener) {
this.listenerReference = new WeakReference<>(listener);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
// Trigger the listener callback when the view is clicked
if (event.getAction() == MotionEvent.ACTION_UP) {
CustomViewListener listener = listenerReference.get();
if (listener != null) {
listener.onViewClicked();
}
}
return true;
}
}

Using WeakReference allows the MainActivity to be garbage collected when it’s no longer needed, helping to prevent memory leaks.

Inefficient bitmap handling

Memory leaks related to inefficient bitmap handling are common in Android when working with large images, especially if you’re not careful about recycling or releasing resources.

For Example:

public class MainActivity extends AppCompatActivity {
private ImageView imageView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_image);
imageView = findViewById(R.id.imageView);
// Load a large bitmap into the ImageView
Bitmap largeBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.large_image);
imageView.setImageBitmap(largeBitmap);
// Perform some other tasks, but forget to recycle the bitmap
// This can lead to a memory leak
}
}

In this example, a large bitmap is loaded into an ImageView. However, if you forget to recycle the bitmap, it remains in memory even after the activity is destroyed, causing a memory leak. This becomes more critical when dealing with multiple large images or frequent screen rotations, where memory usage can accumulate.

To address this issue and prevent memory leaks, it’s essential to recycle the bitmaps when they are no longer needed, typically in the onDestroy method:

    @Override
protected void onDestroy() {
super.onDestroy();
// Recycle the bitmap to avoid memory leaks
if (largeBitmap != null && !largeBitmap.isRecycled()) {
largeBitmap.recycle();
}
}

Recycling the bitmap in the onDestroy method ensures that the memory associated with the bitmap is released when the activity is destroyed, preventing a memory leak. Always be mindful of bitmap sizes and their lifecycle to avoid unnecessary memory consumption in Android applications.

Be cautious about keeping references to large bitmaps, especially in long-lived objects. Clear bitmap references when they are no longer needed, and consider using weak references if necessary.

When loading bitmaps from external sources (e.g., the network), use asynchronous loading mechanisms like AsyncTask or libraries such as Picasso or Glide. These libraries handle the loading process efficiently and help prevent UI freezes.

Leaking fragments

Memory leaks related to fragments in Android are often associated with retaining instances unnecessarily, holding references to the activity, or failing to release resources upon fragment destruction. Here’s an example illustrating a potential memory leak:

public class LeakyFragment extends Fragment {
private MyAsyncTask asyncTask;

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Start an AsyncTask that has an implicit reference to LeakyFragment
asyncTask = new SomeAsyncTask();
asyncTask.execute();
}

private class MyAsyncTask extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void… voids) {
// Some background work
return null;
}

@Override
protected void onPostExecute(Void aVoid) {
// Update UI or perform some action
// Be cautious if referencing LeakyFragment here
// This can lead to a memory leak
}
}
}

In this example, MyAsyncTask is an inner class of LeakyFragment and has an implicit reference to its outer class. If the AsyncTask is still running when the fragment is destroyed, it holds a reference to the fragment, preventing it from being garbage collected.

How to manage Fragments

To avoid such memory leaks, consider using a static inner class for AsyncTask or detaching the task from the fragment’s lifecycle:

public class NonLeakyFragment extends Fragment {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Start an AsyncTask with a weak reference to the fragment
MyAsyncTask asyncTask = new MyAsyncTask(this);
asyncTask.execute();
}

private static class MyAsyncTask extends AsyncTask<Void, Void, Void> {
private WeakReference<NonLeakyFragment> fragmentReference;

MyAsyncTask(NonLeakyFragment fragment) {
this.fragmentReference = new WeakReference<>(fragment);
}

@Override
protected Void doInBackground(Void… voids) {
// Some background work
return null;
}

@Override
protected void onPostExecute(Void aVoid) {
// Update UI or perform some action
// Be cautious if referencing the fragment using fragmentReference
// Make sure to check if the fragment is still valid
NonLeakyFragment fragment = fragmentReference.get();
if (fragment != null && !fragment.isDetached() && !fragment.isRemoving()) {
// Update UI or perform action safely
}
}
}
}

Using a WeakReference for the fragment helps to avoid a strong reference, allowing the fragment to be garbage collected when it’s no longer needed. Additionally, checking the fragment’s state before performing any UI updates ensures that you’re not interacting with a fragment that is in the process of being destroyed.

Listeners and callbacks

Memory leaks related to listeners and callbacks in Android often occur when objects with longer lifecycles hold references to objects with shorter lifecycles, preventing the latter from being garbage collected. Here’s an example that illustrates a potential memory leak:

public class LeakyActivity extends AppCompatActivity {
private CustomAsyncTask customAsyncTask;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_leaky);
// Start an AsyncTask that has an implicit reference to LeakyActivity
customAsyncTask = new CustomAsyncTask(new CustomCallback() {
@Override
public void onTaskCompleted() {
// Update UI or perform some action
// Be cautious if referencing LeakyActivity here
// This can lead to a memory leak
}
});
customAsyncTask.execute();
}

private static class CustomAsyncTask extends AsyncTask<Void, Void, Void> {
private CustomCallback callback;

CustomAsyncTask(CustomCallback callback) {
this.callback = callback;
}

@Override
protected Void doInBackground(Void… voids) {
// Some background work
return null;
}

@Override
protected void onPostExecute(Void aVoid) {
// Notify the callback when the task is completed
if (callback != null) {
callback.onTaskCompleted();
}
}
}

interface CustomCallback {
void onTaskCompleted();
}
}

In this example, LeakyActivity starts an AsyncTask (CustomAsyncTask) with a callback (CustomCallback). The callback has an implicit reference to the outer class (LeakyActivity). If the AsyncTask is still running when the activity is destroyed (e.g., due to a configuration change or activity destruction), it holds a reference to the activity, preventing it from being garbage collected.

To avoid such memory leaks, consider using a WeakReference for the callback or detaching the task from the activity’s lifecycle:

public class NonLeakyActivity extends AppCompatActivity {
private CustomAsyncTask customAsyncTask;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_non_leaky);
// Start an AsyncTask with a weak reference to the activity
customAsyncTask = new CustomAsyncTask(new CustomCallback(this));
customAsyncTask.execute();
}

private static class CustomAsyncTask extends AsyncTask<Void, Void, Void> {
private WeakReference<NonLeakyActivity> activityReference;
private CustomCallback callback;

CustomAsyncTask(CustomCallback callback) {
this.activityReference = new WeakReference<>(callback.getActivity());
this.callback = callback;
}

@Override
protected Void doInBackground(Void… voids) {
// Some background work
return null;
}

@Override
protected void onPostExecute(Void aVoid) {
// Notify the callback when the task is completed
NonLeakyActivity activity = activityReference.get();
if (activity != null && !activity.isFinishing()) {
callback.onTaskCompleted();
}
}
}

private static class CustomCallback {
private WeakReference<NonLeakyActivity> activityReference;

CustomCallback(NonLeakyActivity activity) {
this.activityReference = new WeakReference<>(activity);
}

NonLeakyActivity getActivity() {
return activityReference.get();
}

void onTaskCompleted() {
// Update UI or perform some action safely
NonLeakyActivity activity = activityReference.get();
if (activity != null && !activity.isFinishing()) {
// Update UI or perform action safely
}
}
}
}

Using a WeakReference for the callback and checking the state of the referenced activity before performing any UI updates helps prevent memory leaks. This way, the shorter-lived objects (callbacks) won’t hold strong references to longer-lived objects (activities) that should be eligible for garbage collection.

Implicit references

Memory leaks due to implicit references in Android often occur when a shorter-lived object holds a reference to a longer-lived one, preventing the latter from being garbage collected. One common scenario is using anonymous inner classes or non-static inner classes.

public class LeakyActivity extends AppCompatActivity {
private CustomAsyncTask customAsyncTask;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_leaky);
// Start an AsyncTask that has an implicit reference to LeakyActivity
customAsyncTask = new CustomAsyncTask(new CustomCallback() {
@Override
public void onTaskCompleted() {
// Update UI or perform some action
// Be cautious if referencing LeakyActivity here
// This can lead to a memory leak
}
});
customAsyncTask.execute();
}

private static class CustomAsyncTask extends AsyncTask<Void, Void, Void> {
private CustomCallback callback;

CustomAsyncTask(CustomCallback callback) {
this.callback = callback;
}

@Override
protected Void doInBackground(Void… voids) {
// Some background work
return null;
}

@Override
protected void onPostExecute(Void aVoid) {
// Notify the callback when the task is completed
}
if (callback != null) {
callback.onTaskCompleted();
}
}
}

interface CustomCallback {
void onTaskCompleted();
}

In this example, the anonymous inner class implementing CustomCallback has an implicit reference to the outer class (LeakyActivity). If the CustomAsyncTask is still running when the activity is destroyed (e.g., due to a configuration change or activity destruction), it holds a reference to the activity, preventing it from being garbage collected.

How to avoid Implicit references

To avoid such memory leaks, consider using a WeakReference or detaching the task from the activity’s lifecycle:

public class NonLeakyActivity extends AppCompatActivity {
private CustomAsyncTask customAsyncTask;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_non_leaky);
// Start an AsyncTask with a weak reference to the activity
customAsyncTask = new CustomAsyncTask(new CustomCallback(this));
customAsyncTask.execute();
}

private static class CustomAsyncTask extends AsyncTask<Void, Void, Void> {
private WeakReference<NonLeakyActivity> activityReference;
private CustomCallback callback;

CustomAsyncTask(CustomCallback callback) {
this.activityReference = new WeakReference<>(callback.getActivity());
this.callback = callback;
}

@Override
protected Void doInBackground(Void… voids) {
// Some background work
return null;
}

@Override
protected void onPostExecute(Void aVoid) {
// Notify the callback when the task is completed
NonLeakyActivity activity = activityReference.get();
if (activity != null && !activity.isFinishing()) {
callback.onTaskCompleted();
}
}
}

private static class CustomCallback {
private WeakReference<NonLeakyActivity> activityReference;

CustomCallback(NonLeakyActivity activity) {
this.activityReference = new WeakReference<>(activity);
}

NonLeakyActivity getActivity() {
return activityReference.get();
}

void onTaskCompleted() {
// Update UI or perform some action safely
NonLeakyActivity activity = activityReference.get();
if (activity != null && !activity.isFinishing()) {
// Update UI or perform action safely
}
}
}
}

Using a WeakReference or carefully managing the lifecycle of objects can help prevent memory leaks due to implicit references, ensuring that shorter-lived objects don’t hold strong references to longer-lived ones that should be eligible for garbage collection.

Native memory leaks

Native memory leaks in Android typically occur when working with native code through the Android NDK (Native Development Kit) and not managing allocated memory properly. Here’s a simplified example using C++ code to demonstrate a potential native memory leak:

Assume you have a native method in C++ that allocates memory but doesn’t free it:

#include <jni.h>
#include <cstring>
#include <cstdlib>
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_myapp_NativeMemoryLeakActivity_allocateMemory(JNIEnv env, jobject / this */) {
// Allocate native memory
char *memoryLeak = static_cast<char *>(malloc(100 * sizeof(char)));
// Return a string to Java
return env->NewStringUTF("Native memory allocated successfully");
}

In the above example, we allocate memory using malloc to store characters but do not free the allocated memory.

Now, imagine a scenario where this method is repeatedly called without releasing the allocated memory. Over time, it can lead to a native memory leak.

How to manage Native memory resources

To fix this, you should add a corresponding method to free the allocated memory:

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_myapp_NativeMemoryLeakActivity_allocateMemory(JNIEnv env, jobject / this */) {
// Allocate native memory
char *memoryLeak = static_cast<char *>(malloc(100 * sizeof(char)));
// Return a string to Java
return env->NewStringUTF("Native memory allocated successfully");
}
extern "C" JNIEXPORT void JNICALL
Java_com_example_myapp_NativeMemoryLeakActivity_freeMemory(JNIEnv env, jobject / this */) {
// Free the allocated native memory
free(memoryLeak);
}

In practice, you’d want to manage the lifecycle of native resources carefully, ensuring that memory allocated in native code is properly deallocated to prevent memory leaks. Remember to call free or the appropriate method to release native resources within your native code, and expose a corresponding method to the Java side to invoke this cleanup when necessary

Here are some best practices for managing native memory in Android!

1. Use try-finally Blocks for Memory Release:

When allocating native memory using functions like malloc or new, always release the memory using free or delete in a finally block to ensure proper cleanup, even in the event of an exception.

char* buffer = static_cast<char*>(malloc(100 * sizeof(char)));
if (buffer != nullptr) {
try {
// Use the allocated memory
} finally {
// Release the memory in all cases
free(buffer);
}
}

2. Avoid Global Static References:

Avoid using global static variables to hold native memory references. This can lead to memory leaks, especially when the reference persists longer than necessary.

3. Use Smart Pointers in C++:

If you are working with C++, consider using smart pointers, such as std::unique_ptr or std::shared_ptr, to automatically manage the lifecycle of native resources. These smart pointers release memory automatically when they go out of scope.

std::unique_ptr<char[]> buffer(new char[100]);
// No need to explicitly free, it will be done automatically.

4. Clean Up in JNI onUnload:

If you are using the Java Native Interface (JNI) to interface with native code, make sure to clean up native resources in the JNI_OnUnload function. This function is called when the Java Virtual Machine (JVM) unloads the native library.

JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved) {
// Clean up native resources
}

5. Use WeakGlobalRef for JNI References:

When holding references to Java objects in native code using JNI, use WeakGlobalRef to avoid preventing the associated Java object from being garbage collected.

jobject globalRef = env->NewGlobalRef(javaObject);
jobject weakGlobalRef = env->NewWeakGlobalRef(javaObject);
// Use weakGlobalRef in native code
// …
// Release the references appropriately
env->DeleteGlobalRef(globalRef);
env->DeleteWeakGlobalRef(weakGlobalRef);

Thanks for Reading!

Please comment with questions and suggestions!! If this blog is helpful for you then hit Please hit the clap! Follow on Medium, Connect on LinkedIn, Follow on Insta and send emails if any discussion is needed on the same topic on copy email.
Please Follow & subscribe to Make Android Publication by Narendra K H for prompt updates on Android platform-related blogs.

Thank you!

--

--

Narendra Harny
Make Android

Connect on https://medium.com/make-android | Write On, Android AOSP & Applications | Python | DevOps | Java | C++ | Kotlin | Shell | Linux | Android Auto | IVI