When should I release my own (Android NDK) pens?

I need to use a native library (this is not my solution). The library already has a JNI wrapper, and the Android NDK sample code works. But library initialization procedures return their own descriptors, and the developer needs to close them correctly .

Now an interesting question arises: where to call close_handle procedures from?

At least theoretically, each abnormal termination may result in temporary files remaining somewhere on disk or some other resource leak.

Initializing the library takes from 0.5 to 1 second and consumes a lot of memory.

An Activity is a controller (in the sense of MVC), Android can kill it for its own reasons, including turning the device, and the only function that is guaranteed to be called is onPause() . Thus, onPause / onResume is a bad place for a long resource-intensive operation.

(I know about android:configChanges="keyboardHidden|orientation" , but I would prefer a solution that doesn't need it.)

Application would be an ideal candidate (I believe the library is part of the model), but there is no application termination event.

A Service sounds promising that a native library is a service, but I just don't see how I can have the desired behavior: descriptors should be closed when the application exits.

Timeouts: It sounds like a compromise, but in fact it guarantees that the memory will not be available the moment it is needed, but will become available in a few seconds.

0
source share
2 answers

At the moment, it seems to me that I really need a service, but not exactly IntentService : IntentService calls stopSelf() , while my service should depend.

Interestingly, Service.onDestroy() is called when the user selects "stop" from "Starting Services", but not when the user selects "Force stop" from "Applications". "Stop Debugging" also does not call the Service.onDestroy() call.

EDIT:

My current solution is to use a class derived from a custom subclass of Service ; the code was borrowed from an IntentService source found somewhere on the network. In a derived subclass, I override onDestroy() and believe that a better notification of application termination is available.

 package com.xyz.customandroid; import android.app.Service; import android.content.Intent; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.Message; public abstract class HangAroundIntentService extends Service { /** An extra with this name and the value of boolean true marks an intent as a cancel intent. See {@link #markedAsCancelIntent(Intent)}. */ private static final String CANCEL_MARK = "com.xyz~.customandroid.HangAroundIntentService.cancelQueue()"; private static final int WHAT_MAGIC = 0; // the "what" field for messages private volatile Looper mServiceLooper; private volatile ServiceHandler mServiceHandler; private String mName; private boolean mRedelivery; private boolean mHangAround = true; private final class ServiceHandler extends Handler { public ServiceHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { onHandleIntent((Intent)msg.obj); if (!mHangAround) { stopSelf(msg.arg1); } } } /** * Creates an IntentService. Invoked by your subclass constructor. * * @param name Used to name the worker thread, important only for debugging. */ public HangAroundIntentService(String name) { super(); mName = name; } /** * Remove all pending messages from the handler queue. * Processing of the message already fetched from the queue * is not terminated by this function. * * Although this function is public, it is recommended * to use the cancel intents instead. * see {@link #markedAsCancelIntent(Intent)} and {@link #isCancelIntent(Intent)}. */ public void cancelQueue() { mServiceHandler.removeMessages(WHAT_MAGIC); } /** * Sets intent redelivery preferences. Usually called from the constructor * with your preferred semantics. * * <p>If enabled is true, * {@link #onStartCommand(Intent, int, int)} will return * {@link Service#START_REDELIVER_INTENT}, so if this process dies before * {@link #onHandleIntent(Intent)} returns, the process will be restarted * and the intent redelivered. If multiple Intents have been sent, only * the most recent one is guaranteed to be redelivered. * * <p>If enabled is false (the default), * {@link #onStartCommand(Intent, int, int)} will return * {@link Service#START_NOT_STICKY}, and if the process dies, the Intent * dies along with it. */ public void setIntentRedelivery(boolean enabled) { mRedelivery = enabled; } /** * If enabled is true (default), the service does not stop after processing an intent. */ public void setServiceHangAround(boolean enabled) { mHangAround = enabled; } @Override public void onCreate() { // TODO: It would be nice to have an option to hold a partial wakelock // during processing, and to have a static startService(Context, Intent) // method that would launch the service & hand off a wakelock. super.onCreate(); HandlerThread thread = new HandlerThread("IntentService[" + mName + "]"); thread.start(); mServiceLooper = thread.getLooper(); mServiceHandler = new ServiceHandler(mServiceLooper); } /** * If intent is a cancel intent, {@link #cancelQueue()} is invoked immediately; * no other action is done for a cancel intent, whatever information it might contain. * * Intents that are not cancel intents are queued * to be seen from {@link #onHandleIntent(Intent)}. */ @Override public void onStart(Intent intent, int startId) { if (isCancelIntent(intent)) { cancelQueue(); } else { Message msg = mServiceHandler.obtainMessage(WHAT_MAGIC); msg.arg1 = startId; msg.obj = intent; mServiceHandler.sendMessage(msg); } } @Override public int onStartCommand(Intent intent, int flags, int startId) { onStart(intent, startId); return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY; } @Override public void onDestroy() { mServiceLooper.quit(); } @Override public IBinder onBind(Intent intent) { return null; } /** * This method is invoked on the worker thread with a request to process. * Only one Intent is processed at a time, but the processing happens on a * worker thread that runs independently from other application logic. * So, if this code takes a long time, it will hold up other requests to * the same IntentService, but it will not hold up anything else. * * @param intent The value passed to {@link * android.content.Context#startService(Intent)}. */ protected abstract void onHandleIntent(Intent intent); /** * Mark an Intent as cancel intent. The Intent will not be placed in the queue; * instead, it will cause immediate cleaning of the queue * (unless you redefine {@link #onStart(Intent, int)} in a derived class). * @param intent to be modified * @return the original intent after modification */ public static Intent markedAsCancelIntent(Intent intent) { intent.putExtra(CANCEL_MARK, true); return intent; } /** * Check if the intent has been marked as a cancel intent. * @param intent to be checked * @return true if it is a cancel intent */ public static boolean isCancelIntent(Intent intent) { return intent.getBooleanExtra(CANCEL_MARK, false); } } 

and my class of service is defined as:

 public class MyService extends HangAroundIntentService { public MyService() { super("MyService"); } public void onDestroy() { MyData.getMyData().shutdown(); super.onDestroy(); } // service-specific static methods not shown } 

To be continued...

+1
source

In practice, it turned out that some own library of resource leakage; I had to restart the process (this did not kill the activity, but re-created the process):

how to programmatically restart the "Android app?

One can only hope that no library will check temporary files on disk ...

0
source

Source: https://habr.com/ru/post/1384349/


All Articles