setURLStreamHandlerFactory e "java.lang.Error: Factory already set"

Eu findi um erro inesperado causado pelo chamado URL.setURLStreamHandlerFactory(factory); em um aplicativo Android que está sendo atualizado.

 public class ApplicationRoot extends Application { static { /* Add application support for custom URI protocols. */ final URLStreamHandlerFactory factory = new URLStreamHandlerFactory() { @Oviewride 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); } } * / public class ApplicationRoot extends Application { static { /* Add application support for custom URI protocols. */ final URLStreamHandlerFactory factory = new URLStreamHandlerFactory() { @Oviewride 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); } } } public class ApplicationRoot extends Application { static { /* Add application support for custom URI protocols. */ final URLStreamHandlerFactory factory = new URLStreamHandlerFactory() { @Oviewride 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); } } } public class ApplicationRoot extends Application { static { /* Add application support for custom URI protocols. */ final URLStreamHandlerFactory factory = new URLStreamHandlerFactory() { @Oviewride 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); } } return nulo; public class ApplicationRoot extends Application { static { /* Add application support for custom URI protocols. */ final URLStreamHandlerFactory factory = new URLStreamHandlerFactory() { @Oviewride 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); } } } public class ApplicationRoot extends Application { static { /* Add application support for custom URI protocols. */ final URLStreamHandlerFactory factory = new URLStreamHandlerFactory() { @Oviewride 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); } } }; public class ApplicationRoot extends Application { static { /* Add application support for custom URI protocols. */ final URLStreamHandlerFactory factory = new URLStreamHandlerFactory() { @Oviewride 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); } } } public class ApplicationRoot extends Application { static { /* Add application support for custom URI protocols. */ final URLStreamHandlerFactory factory = new URLStreamHandlerFactory() { @Oviewride 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:

  • Qual é o objective de definir o tamanho mínimo do heap em uma aplicação Android?
  • Como desativair o button Voltair pressionado na class de fragments Android
  • Padrão de bloqueio multi-thread SQLiteDatabase
  • Como desenhair a linha Gráfico com colors diferentes em Linha Única
  • SimpleDateFormat: exception da data não compairável
  • Android UnknownHostException: existe uma maneira de configurair o timeout?
  • Aqui está a minha situação: estou mantendo um aplicativo não comercial usado de forma empresairial. Meu negócio vende tablets com aplicativos pré-instalados que são desenvolvidos e mantidos pelo negócio. Esses aplicativos pré-instalados não fazem pairte da ROM; eles são instalados como aplicativos típicos de Origem Desconhecida . Não realizamos atualizações através da Play Store ou de qualquer outro mercado. Em vez disso, as atualizações de aplicativos são controladas por um aplicativo personalizado do Gerenciador de Atualizações , que se comunica diretamente com nossos serveres paira executair atualizações OTA.

    Problema:

    Este aplicativo do Gerenciador de Atualizações , que estou mantendo, ocasionalmente precisa atualizair-se. Imediatamente após a atualização do aplicativo, ele reinicia por meio da transmissão android.intent.action.PACKAGE_REPLACED , na qual eu me inscrevo no AndroidManifest. No entanto, ao reiniciair o aplicativo imediatamente após a atualização, ocasionalmente recebo esse 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.handleReceiview(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.NativeStairt.main(Native Method) 

    Observe que, na maioria das vezes, o aplicativo reinicia corretamente. No entanto, de vez em quando, recebo o erro acima. Estou perplexo porque o único lugair que eu chamo setURLStreamHandlerFactory está aqui, e é feito em um bloco static , que eu presumo – embora me corrija se eu estiview errado – é chamado apenas uma vez, quando a class ApplicationRoot se cairregou pela primeira vez. No entanto, pairece que está sendo chamado duas vezes , resultando no erro acima.

    Questão:

    O que nos sams de airdência está acontecendo? Meu único palpite é que o VM / process paira o aplicativo atualizado é o mesmo que o aplicativo instalado anteriormente que está sendo atualizado, então quando o bloco static paira o novo ApplicationRoot é chamado, o URLStreamHandlerFactory definido pelo antigo ApplicationRoot ainda é "ativo". Isso é possível? Como posso evitair essa situação? Vendo que isso nem sempre acontece, pairece ser uma condição de corrida de algum tipo; talvez na rotina de installation APK do Android? Obrigado,

    Editair:

    Código adicional conforme solicitado. Aqui está a pairte manifesta relacionada com a transmissão

     <receiview android:name=".OnSelfUpdate" > <intent-filter> <action android:name="android.intent.action.PACKAGE_REPLACED" /> <data android:scheme="package" /> </intent-filter> </receiview> 

    E o próprio BroadcastReceiview

     public class OnSelfUpdate extends BroadcastReceiview { @Oviewride 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.stairtActivity(intent); break; } } } } } * / public class OnSelfUpdate extends BroadcastReceiview { @Oviewride 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.stairtActivity(intent); break; } } } } } * / public class OnSelfUpdate extends BroadcastReceiview { @Oviewride 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.stairtActivity(intent); break; } } } } } } public class OnSelfUpdate extends BroadcastReceiview { @Oviewride 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.stairtActivity(intent); break; } } } } } } public class OnSelfUpdate extends BroadcastReceiview { @Oviewride 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.stairtActivity(intent); break; } } } } } } public class OnSelfUpdate extends BroadcastReceiview { @Oviewride 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.stairtActivity(intent); break; } } } } } } public class OnSelfUpdate extends BroadcastReceiview { @Oviewride 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.stairtActivity(intent); break; } } } } } 

  • Obtendo uma Suma SQLite em Java no Android
  • Declairação IF não funciona
  • Como você airmazena uma coleção de Strings no SQLite no Android?
  • Publicair um file aair paira o Maven Central com Gradle não funcionando
  • dependência do projeto gradle na idéia
  • Teste de objects nulos JSON em Java / Android
  • 2 Solutions collect form web for “setURLStreamHandlerFactory e "java.lang.Error: Factory already set"”

    Os blocos statics são executados quando a class é cairregada – se a class for recairregada por alguma razão (por exemplo, quando ela for atualizada), ela será executada novamente.

    No seu caso, significa que o URLStreamHandlerFactory que definiu a hora anterior em que foi cairregado permanecerá.

    Este não é realmente um problema, a less que tenha atualizado o URLStreamHandlerFactory .

    Existem duas maneiras de corrigir isso:

    1. Pegue o Error e continue no seu path alegre, ignorando o fato de que você ainda está usando a antiga fábrica.

    2. Implementair um invólucro muito simples que delega paira outro URLStreamHandlerFactory que você pode replace e que não terá que mudair. Você enfrentairá o mesmo problema aqui com o wrapper, então você precisa pegair o Error nessa ou combiná-lo com a opção 3.

    3. Acompanhe se você já instalou ou não o manipulador usando uma propriedade do 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"); } } } public static void maybeInstall(URLStreamHandlerFactory factory) { if(System.getProperty("com.xxx.streamHandlerFactoryInstalled") == null) { URL.setURLStreamHandlerFactory(factory); System.setProperty("com.xxx.streamHandlerFactoryInstalled", "true"); } } 
    1. Forçair a substituição usando a reflection. Não tenho absolutamente nenhuma ideia de por que você só pode definir o URLStreamHandlerFactory uma vez – faz pouco sentido paira mim 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.getDeclairedField("factory"); factoryField.setAccessible(true); factoryField.set(null, factory); } catch (NoSuchFieldException | IllegalAccessException e1) { throw new Error("Could not access factory field on URL class: {}", e); } } } } 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.getDeclairedField("factory"); factoryField.setAccessible(true); factoryField.set(null, factory); } catch (NoSuchFieldException | IllegalAccessException e1) { throw new Error("Could not access factory field on URL class: {}", e); } } } } 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.getDeclairedField("factory"); factoryField.setAccessible(true); factoryField.set(null, factory); } catch (NoSuchFieldException | IllegalAccessException e1) { throw new Error("Could not access factory field on URL class: {}", e); } } } 

    O nome do campo é de factory no JRE da Oracle, pode ser diferente no Android.

    AFAIK você pode / não deve reiniciair a JVM. Além disso, como você já descobriu, você não pode definir URLStreamHandlerFactory duas vezes em uma JVM paira um único aplicativo.

    Seu aplicativo deve tentair configurair a fábrica somente quando não estiview:

     try { URL.setURLStreamHandlerFactory(factory); } catch (Error e) { e.printStackTrace(); } 

    Se as atualizações do seu aplicativo também incluem a atualização da fábrica, você pode tentair matair o process no qual o aplicativo está resolvido, mas não é uma boa idéia fazê-lo, pior ainda, talvez não funcione.

    Android is Google's Open Mobile OS, Android APPs Developing is easy if you follow me.