setURLStreamHandlerFactory y "java.lang.Error: La fábrica ya está configurada"
Me he encontrado con un error inesperado causado por la llamada URL.setURLStreamHandlerFactory(factory);
en una aplicación de Android que se está actualizando.
public class ApplicationRoot extends Application { static { /* Add application support for custom URI protocols. */ final URLStreamHandlerFactory factory = new URLStreamHandlerFactory() { @Override public URLStreamHandler createURLStreamHandler(final String protocol) { if (ExternalProtocol.PROTOCOL.equals(protocol)) { return new ExternalProtocol(); } if (ArchiveProtocol.PROTOCOL.equals(protocol)) { return new ArchiveProtocol(); } return null; } }; URL.setURLStreamHandlerFactory(factory); } }
Intro:
- Editar texto aceptar sólo números no números decimales en android
- Zoom automático en Google Maps en java? (Dependiendo de la resolución de pantalla de Android)
- Obtener el nombre de ubicación actual del usuario sin necesidad de utilizar gps o Internet, sino mediante Network_Provider en android
- BaseGameUtils GoogleApiClient.ApiOptions no se puede resolver con un tipo
- Google Places api AutocompletePrediction prediction.getDescription () MissingDespués de actualizar a Play services a 9.4.0
Aquí está mi situación: estoy manteniendo una aplicación no comercial utilizada en una empresa de moda. Mi negocio vende tabletas con aplicaciones preinstaladas que son desarrolladas y mantenidas por la empresa. Estas aplicaciones preinstaladas no forman parte de la ROM; se instalan como aplicaciones de fuente desconocida típicas. No realizamos actualizaciones a través de Play Store ni en ningún otro mercado. Por el contrario, las actualizaciones de las aplicaciones son controladas por una aplicación personalizada de Update Manager , que se comunica directamente con nuestros servidores para realizar actualizaciones OTA.
Problema:
Esta aplicación de Gestor de actualizaciones , que estoy manteniendo, de vez en cuando necesita actualizarse. Inmediatamente después de que la aplicación se actualice, se reinicia por medio de la emisión android.intent.action.PACKAGE_REPLACED
, en la que me registro en el AndroidManifest. Sin embargo, al reiniciar la aplicación inmediatamente después de la actualización, de vez en cuando recibir este Error
java.lang.Error: Factory already set at java.net.URL.setURLStreamHandlerFactory(URL.java:112) at com.xxx.xxx.ApplicationRoot.<clinit>(ApplicationRoot.java:37) at java.lang.Class.newInstanceImpl(Native Method) at java.lang.Class.newInstance(Class.java:1208) at android.app.Instrumentation.newApplication(Instrumentation.java:996) at android.app.Instrumentation.newApplication(Instrumentation.java:981) at android.app.LoadedApk.makeApplication(LoadedApk.java:511) at android.app.ActivityThread.handleReceiver(ActivityThread.java:2625) at android.app.ActivityThread.access$1800(ActivityThread.java:172) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1384) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:146) at android.app.ActivityThread.main(ActivityThread.java:5653) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:515) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1291) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1107) at dalvik.system.NativeStart.main(Native Method)
Tenga en cuenta que la mayoría de las veces, la aplicación se reinicia correctamente. Sin embargo, de vez en cuando, obtengo el error anterior. Estoy perplejo porque el único lugar al que llamo setURLStreamHandlerFactory
está aquí, y se hace en un bloque static
, que presumo -aunque me corrija si estoy equivocado- sólo se llama una vez, cuando la clase ApplicationRoot
carga primero. Sin embargo, parece que se está llamando dos veces , lo que resulta en el error anterior.
Pregunta:
¿Qué es lo que está ocurriendo en los llamativos sams ? Mi única suposición en este punto es que el proceso VM / para la aplicación actualizada es la misma que la aplicación instalada anteriormente que se está actualizando, por lo que cuando el bloque static
para el nuevo ApplicationRoot
se llama, el URLStreamHandlerFactory
establecido por el antiguo ApplicationRoot
sigue siendo "activo". es posible? ¿Cómo puedo evitar esta situación? Al ver que no siempre sucede, parece ser una condición de carrera de algún tipo; tal vez dentro de la rutina de instalación de APK de Android? Gracias,
Editar:
Código adicional solicitado. Aquí está la parte manifiesta que trata de la transmisión
<receiver android:name=".OnSelfUpdate" > <intent-filter> <action android:name="android.intent.action.PACKAGE_REPLACED" /> <data android:scheme="package" /> </intent-filter> </receiver>
Y el propio BroadcastReceiver
public class OnSelfUpdate extends BroadcastReceiver { @Override public void onReceive(final Context context, final Intent intent) { /* Get the application(s) updated. */ final int uid = intent.getIntExtra(Intent.EXTRA_UID, 0); final PackageManager packageManager = context.getPackageManager(); final String[] packages = packageManager.getPackagesForUid(uid); if (packages != null) { final String thisPackage = context.getPackageName(); for (final String pkg : packages) { /* Check to see if this application was updated. */ if (pkg.equals(thisPackage)) { final Intent intent = new Intent(context, MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); break; } } } } }
- Error de permiso de Android Bluetooth_admin
- Cómo cambiar el tamaño y la posición de Drawables dentro de un LayerDrawable en Android
- Cómo configurar el modo retrato con libGDX?
- Formato de moneda en Android utilizando separador decimal incorrecto
- Android: trate de enviar sms falsos a mí mismo sin el uso de la red móvil
- Error de HttpsURLConnectionImpl.getInputStream () en la plataforma Android
- Uso de hormigas-fuente 7 o superior para permitir que el operador de diamante
- Cómo establecer layout_columnWeight para un niño GridLayout cuando se agrega mediante programación?
Los bloques estáticos se ejecutan cuando se carga la clase – si la clase se vuelve a cargar por algún motivo (por ejemplo, cuando se ha actualizado) se ejecutará de nuevo.
En su caso, significa que se URLStreamHandlerFactory
el URLStreamHandlerFactory
que estableció el tiempo anterior al que se cargó.
Esto no es realmente un problema a menos que haya actualizado el URLStreamHandlerFactory
.
Hay dos maneras de arreglar esto:
-
Coja el
Error
y continúe en su manera alegre, ignorando el hecho de que usted todavía está utilizando la fábrica vieja. -
Implemente un contenedor muy simple que delega a otro
URLStreamHandlerFactory
que puede reemplazar y que no tendrá que cambiar. Usted funcionará en el mismo problema aquí con el envoltorio sin embargo así que usted necesita coger elError
en ése o combinarlo con la opción 3. -
Compruebe si ya ha instalado el controlador utilizando una propiedad del sistema.
Código:
public static void maybeInstall(URLStreamHandlerFactory factory) { if(System.getProperty("com.xxx.streamHandlerFactoryInstalled") == null) { URL.setURLStreamHandlerFactory(factory); System.setProperty("com.xxx.streamHandlerFactoryInstalled", "true"); } }
- Reemplazo de fuerza mediante la reflexión. No tengo absolutamente ninguna idea de por qué sólo se puede establecer la
URLStreamHandlerFactory
una vez – no tiene mucho sentido para mí TBH.
Código:
public static void forcefullyInstall(URLStreamHandlerFactory factory) { try { // Try doing it the normal way URL.setURLStreamHandlerFactory(factory); } catch (final Error e) { // Force it via reflection try { final Field factoryField = URL.class.getDeclaredField("factory"); factoryField.setAccessible(true); factoryField.set(null, factory); } catch (NoSuchFieldException | IllegalAccessException e1) { throw new Error("Could not access factory field on URL class: {}", e); } } }
El nombre del campo es de factory
en JRE
de Oracle, puede ser diferente en Android.
AFAIK puede / no debe reiniciar la JVM. Además, como ya lo has descubierto, no puedes establecer URLStreamHandlerFactory
dos veces en una JVM para una sola aplicación.
Su aplicación debe intentar establecer la fábrica sólo cuando no está:
try { URL.setURLStreamHandlerFactory(factory); } catch (Error e) { e.printStackTrace(); }
Si las actualizaciones de su aplicación también incluyen la actualización de la fábrica, podría intentar matar el proceso en el que reside su aplicación, pero no es una buena idea hacerlo, peor aún, puede que ni siquiera funcione.
- ¿Cómo crear un enlace simbólico con shortpath?
- ¿Cómo establezco el nivel de api mínimo para los proyectos en Android Studio?