¿Es posible reiniciar la aplicación de Android después de llamar a ActivityManager.clearApplicationUserData ()

8

Mi aplicación Android actual necesita llamar

 ActivityManager.clearApplicationUserData()

para simular al usuario que borra el almacenamiento de la aplicación

Que funciona bien.

Un efecto secundario de las llamadas clearApplicationUserData()es que la aplicación está (comprensiblemente) cerrada.

Lo que da una mala experiencia de usuario.

Tengo dificultades para reiniciar mi aplicación una vez que llamé clearApplicationUserData().

He intentado usar startActivity, Alarm Managercon Pending Intent, Foreground/ Backgroundservice.

Nada funciona.

¿Es imposible reiniciar una aplicación de Android que ha llamado clearApplicationUserData()?

Héctor
fuente
1
Supongo que su aplicación termina en el estado detenido, como si el usuario hubiera hecho clic en "Forzar detención". Todas las alarmas, trabajos, etc. pendientes se cancelarán. "¿Es imposible reiniciar una aplicación de Android que ha llamado clearApplicationUserData()?" - Dudo que sea posible desde tu propia aplicación. Muestre un mensaje al usuario explicando que esto sucederá antes de llamar a ese método, y el usuario puede reiniciar la aplicación desde el iniciador.
CommonsWare
1
Creo que esto lo que su construcción para .. Alarm Manager pendiente de Intención, primer plano / fondo claro de todos modos recibirá ..si se va a utilizar clearApplicationUserData, entonces éste será el flujo deseado .. Puede descargar el código fuente para ello ..
ADM
@CommonsWare ¿De todos modos puedo habilitar el registro detallado (o similar) para ver qué sucede realmente al llamar a este método?
Héctor
1
No sé a qué te refieres con "lo que realmente sucede", pero puedes ver Logcat para ver qué se está registrando. Si lo está viendo desde Android Studio, cambie el filtro de gravedad a "detallado" y asegúrese de que el menú desplegable final esté configurado en "Sin filtros", para ver todos los mensajes.
CommonsWare
2
He recorrido un camino similar en el pasado. En última instancia, fue más fácil borrar manualmente la base de datos y las preferencias compartidas y reiniciar MainActivity. Si esto es posible, ¡se recomienda encarecidamente! Si no, ¿tal vez algo así como programar una tarea con WorkManager(que no esté (?) Afectada por datos claros de la aplicación) para iniciar su actividad?
JakeSteam

Respuestas:

4

( 1a respuesta : esta respuesta solo funciona en situaciones limitadas, no es una respuesta completa)

public boolean clearApplicationUserData ()

Descripción

Devolución : truesi la aplicación solicitó con éxito que se borren los datos de la aplicación; falsede otra manera.

Como se indicó en el sitio web de referencia, tenemos un retorno antes de que se cierre la solicitud. Por lo tanto, vamos a utilizar este retornado para reiniciar la aplicación.

if(ActivityManager.clearApplicationUserData)
{
     doRestart = true;
}

cuando Actividad onDestroy()y onStop()se llaman reiniciar aplicación.

   @Override
   protected void onDestroy() {
       super.onDestroy();
       if(doRestart){
           Intent intent = new Intent(this, Activity.class);
           this.startActivity(intent);
       }
   }

    @Override
    protected void onStop() {
        super.onStop();
        if(doRestart){
            Intent intent = new Intent(this, Activity.class);
            this.startActivity(intent);
        }
    }

Ponemos la acción de reinicio en ambos onDestroy()y onStop()para asegurarnos de que la aplicación se reinicie nuevamente.

Y también, creo que es una buena idea forzar la detención de la actividad antes de que el sistema operativo la detenga.

if(ActivityManager.clearApplicationUserData)
{
     doRestart = true;
     finish(); <= i mean this 
}

es porque, se asegura de que onDestroy()y onStop()será invocado.

Mr.AF
fuente
Después de llamar ActivityManager.clearApplicationUserData()al proceso de la aplicación se mata a la fuerza. No se ejecuta ninguna otra línea de código.
Onik
@Onik, ¿lo intentaste? cuando se cierra la actividad, siempre se llama a onDestroy () .y también la fuente oficial dice que devuelve un valor booleano; de lo contrario, debería devolver un vacío.
Mr.AF
1
Por supuesto que lo intenté, ¿por qué escribiría el comentario con una información tan detallada? Si tiene otro comportamiento de invocación de método, supongo que hay una diferencia en las implementaciones de API, lo que hace que su versión del sistema operativo sea específica.
Onik
@ Mr.AF gracias por tomarse el tiempo para responder. Probaré su solución con seguridad
Héctor el
@Onik mira mi próxima respuesta :)
Mr.AF
3

Mi sugerencia puede sonar trivial pero, ¿ha considerado no llamar ActivityManager.clearApplicationUserData()?

Aquí lo que dice la documentación sobre este método:

Permite que una aplicación borre sus propios datos del disco. Esto equivale a que el usuario elija borrar los datos de la aplicación desde la interfaz de usuario de la configuración del dispositivo. Borra todos los datos dinámicos asociados con la aplicación, sus datos privados y los datos en su área privada en el almacenamiento externo, pero no elimina la aplicación instalada ni los archivos OBB.

Entonces, para imitar este comportamiento, solo necesita borrar los directorios de almacenamiento internos y externos. No se necesitan permisos para acceder a ninguno de esos.

Ilya Gazman
fuente
2

( 2ª respuesta : necesito mucha más contribución)

Después de 8 horas investigando en el sistema operativo Android y el sitio web de desarrolladores de Android para encontrar una solución para reiniciar la actividad cuando clearApplicationUserDatase invoca. Finalmente, podría encontrar una solución agradable / piratería .

Esta solución se ve como zidane dribble :)

Vamos a introducir la solución. en un primer momento, clearApplicationUserDatase borran todas las pistas de aplicación cuando se invoca como tareas, notificaciones, alarmas , etc, por lo tanto, explícita vocación de actividad es imposible .

La forma implícita es la única forma posible de llamar a la actividad.

Después de un par de pruebas, descubrí que la aplicación manifestregistrada intent-filters no se eliminaría y pueden escuchar las transmisiones entrantes del sistema .

Aproximadamente, el 98% de las transmisiones del sistema no serían recibidas por una aplicación autorizada y ese 2% restante podría no transmitirse muy pronto.

Entonces lo que hay que hacer ? hmmm? vamos hombre, debo encontrar una solución ...

bingo, debo activar algo para que el sistema lo transmita <= parece piratería :)

así que decido elegir WIFI_STATE_CHANGED porque

  • Permiso de acceso fácil
  • El sistema lo transmite con retraso <= esto asegura que la aplicación esté cerrada antes de la transmisión

manifest.xml

<receiver
   android:name=".PackageDataClearedReceiver"
   android:enabled="true"
   android:exported="true">
     <intent-filter android:priority="100">
       <action android:name="android.net.wifi.WIFI_STATE_CHANGED" />
       <action android:name="android.net.wifi.STATE_CHANGE" />
     </intent-filter>
 </receiver>

MainActivity.java

public class MainActivity extends AppCompatActivity {
    ActivityManager am;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        AppCompatButton btn = findViewById(R.id.btn);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        am = (ActivityManager) getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
                        if (am != null) {
                            ExecutorService pool = Executors.newFixedThreadPool(2);
                            final Collection<Future> futures = new HashSet<Future>();
                            futures.add(pool.submit(new Runnable() {
                                @Override
                                public void run() {
                                    WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
                                    wifiManager.setWifiEnabled(true);
                                    wifiManager.setWifiEnabled(false);
                                    am.clearApplicationUserData();
                                }
                            }));
                            for (Future future : futures) {
                                future.isDone();
                            }
                        }

                    }
                }).start();
            }
        });
    }
}

manifestación

ingrese la descripción de la imagen aquí

tenga en cuenta que es solo un producto mínimo viable que debe desarrollarse más para que funcione perfectamente.

Mr.AF
fuente
este enfoque no funcionará para mi aplicación, ya que detecto cambios en el estado de WIFI para mostrar mensajes a mis usuarios. Si adopto su enfoque en la segunda solución cada vez que cambie el estado de WIFI, comenzaré mi actividad principal
Héctor
@ Héctor, sí, necesita mucho más desarrollo y personalización según su necesidad, ya que dije que es un producto mínimo viable. Necesita personalizarlo
Mr.AF
@ Héctor, debe poner condiciones en Receiver para reiniciar la actividad cuando sea necesario
Mr.AF
0

Esperemos que esto sirva de ayuda.

Pregunta: ¿Es imposible reiniciar una aplicación de Android que haya llamado clearApplicationUserData ()? Respuesta: no

Una vez que llame a clearApplicationUserData (), llame al siguiente método.

private void triggerRebirth(Context context) {
     PackageManager packageManager = context.getPackageManager();
     Intent intent = packageManager.getLaunchIntentForPackage(context.getPackageName());
     ComponentName componentName = intent.getComponent();

     Intent mainIntent = Intent.makeRestartActivityTask(componentName);
     context.startActivity(mainIntent);
     Runtime.getRuntime().exit(0);
}

Feliz codificación.

Whales_Corps
fuente
-1

Al borrar los datos de la aplicación en el dispositivo a través de la API, se clearApplicationUserData()restablece la aplicación como si acabara de instalarse. Como ha encontrado, también se borran las alarmas y transmisiones registradas con su aplicación. La forma más eficiente de mantener su aplicación en primer plano sería borrar los datos usted mismo, como han señalado otros, en lugar de utilizar la API. Aquí hay un ejemplo: https://stackoverflow.com/a/9073473/949224


Sin embargo , si está decidido a usar la API (lo que garantiza que se borren todos los datos) y la aplicación se detenga por la fuerza, tengo una sugerencia:

Cree una pequeña aplicación complementaria que se pueda iniciar justo antes de borrar los datos de su aplicación. La aplicación complementaria simplemente vuelve a iniciar su aplicación, posiblemente después de un breve tiempo de espera.

    Intent launchIntent = getPackageManager().getLaunchIntentForPackage("example.com.testrelaunchapp");
    if (launchIntent != null) {
        startActivity(launchIntent);//null pointer check in case package name was not found
    } else {
        Log.w( TAG, "Unable to resolve launch activity of relauncher companion app");
    }

    ((ActivityManager)getSystemService(Context.ACTIVITY_SERVICE))
                    .clearApplicationUserData();   

La aplicación complementaria en sí misma debe cerrarse después, e idealmente debería ocultarse de la Pila de actividades, etc.

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    final Intent launchIntent = getPackageManager().getLaunchIntentForPackage("example.com.yourmainapp");
    if (launchIntent != null) {
        Handler handler = new Handler(getMainLooper());
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                Log.i( TAG, "About to act on launchIntent");
                launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
                startActivity(launchIntent);
                finish();
                System.exit(0);
            }
        }, 1000);
    }    
 }     

He visto que esto funciona con Android 6.0, pero no hay garantías de que sea versátil y funcione en todos los ámbitos. Si lo desea, habría más cosas que hacer para que la aplicación complementaria no tenga interfaz de usuario y se oculte del iniciador del teléfono. También es probable que desee agrupar el APK como un archivo dentro de su propia aplicación e instalarlo en la primera ejecución, lo que necesitaría que el usuario habilite la instalación desde "Fuentes desconocidas" (no Play Store). Se puede hacer a través de intentos a la configuración correcta del sistema, si es necesario, pero los usuarios necesitarían una buena explicación de por qué esto es necesario.

Entonces, como decía, el enfoque más simple es borrar los datos y los permisos de la aplicación usted mismo.

dr_g
fuente
Creo que no es una solución viable. porque su solución necesita paquetes adicionales instalados que lo hacen imposible. y si pudiéramos tener paquetes adicionales o aplicaciones complementarias, habría muchas maneras de comenzar la actividad.
Mr.AF
Ciertamente es posible pedirle al usuario que instale otra aplicación desde la suya. La pregunta es si desea este comportamiento como parte de cómo opera. Como mencioné, no lo recomendaría, pero aún así vale la pena mencionar la idea en principio en caso de que sea útil para otros.
dr_g