start - Receptor de difusión no funciona después de reiniciar el dispositivo en Android
sendbroadcast android example (5)
@Aritra, prueba esto
<receiver
android:name=".mics.BootReceiver"
android:enabled="true"
android:exported="true" >
<intent-filter android:priority="500" >
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
Elimine el filtro de intento quickBoot e intente ejecutarlo, según la documentación que solo solicitamos BootCompleted para lograrlo. Puede ser que esté interrumpiendo esto.
También un punto más importante a tener en cuenta:
No confíes completamente o prueba en dispositivos Mi, ya que tienen su propio sistema operativo que detiene las funciones básicas de Android, como que detienen los servicios de notificaciones Push y los servicios en segundo plano solo para optimizar el uso de la batería. Para probar esto en Mi dispositivo, marque su aplicación como "AutoStart" en la aplicación de seguridad y luego intente.
Ya revisé todas las preguntas relacionadas y no encontré ninguna solución para este problema. Entonces este es un problema absolutamente nuevo para mí.
Lo que tengo
Tengo una aplicación de Android que registra algunos receptores de difusión en su manifiesto. Así es como se ve mi manifiesto.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.app.myapp">
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.WRITE_CALL_LOG" />
<uses-permission android:name="com.android.vending.BILLING" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-feature
android:name="android.hardware.telephony"
android:required="false" />
<uses-feature
android:name="android.hardware.screen.portrait"
android:required="false" />
<application
android:name=".base.MyApp"
android:allowBackup="false"
android:icon="@drawable/ic_launcher"
android:label="@string/label_app_name"
android:largeHeap="true"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:replace="label, allowBackup">
<receiver android:name=".mics.BootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</receiver>
<receiver android:name=".PhoneCallReceiver">
<intent-filter>
<action android:name="android.intent.action.NEW_OUTGOING_CALL" />
</intent-filter>
</receiver>
<receiver
android:name=".mics.DeviceAdminReceiver"
android:permission="android.permission.BIND_DEVICE_ADMIN">
<intent-filter>
<action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
</intent-filter>
<meta-data
android:name="android.app.device_admin"
android:resource="@xml/device_admin" />
</receiver>
<receiver
android:name="com.clevertap.android.sdk.InstallReferrerBroadcastReceiver"
android:exported="true">
<intent-filter>
<action android:name="com.android.vending.INSTALL_REFERRER" />
</intent-filter>
</receiver>
<meta-data
android:name="com.app.myapp.utils.ImageLoaderModule"
android:value="GlideModule" />
<meta-data
android:name="com.app.myapp.utils.AudioCoverLoaderModule"
android:value="GlideModule" />
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
<activity
android:name=".core.activities.SplashActivity"
android:excludeFromRecents="true"
android:label="@string/label_app_name"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<activity-alias
android:name=".core.activities.SplashActivity-Alias"
android:icon="@drawable/ic_launcher"
android:label="@string/label_app_name"
android:noHistory="true"
android:targetActivity="com.app.myapp.core.activities.SplashActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.MONKEY" />
</intent-filter>
</activity-alias>
<activity
android:name=".core.flow.authFlow.activities.AuthFlowActivity"
android:excludeFromRecents="true"
android:label="@string/label_app_name"
android:screenOrientation="portrait" />
<service android:name=".features.fileCloudSync.KillNotificationService" />
</application>
</manifest>
También hay otras 10-15 actividades, pero se han eliminado por simplicidad. Y esta es la clase básica de receptor de arranque. Comienzo un servicio desde aquí.
public class BootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
AlertUtils.showToast(context, "BOOT COMPLETED", Toast.LENGTH_LONG);
}
}
}
y la clase de receptor de llamadas telefónicas se ve así (también se ha simplificado),
public class PhoneCallReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_NEW_OUTGOING_CALL)) {
AlertUtils.showToast(context, "PHONE CALL RECEIVED", Toast.LENGTH_LONG);
// Simplified for brevity
}
}
}
El problema
Todos estos receptores funcionan bien cuando instalo la aplicación y la inicio una vez. Pero después de reiniciar mi dispositivo, estos receptores no funcionan en absoluto. Ni BootCompleteReceiver
ni PhoneCallReceiver
reciben su método onReceive()
.
Mi suposición era que estos receptores se registrarían automáticamente después del reinicio, pero simplemente no funciona. Necesito que BootCompleteReceiver
funcione para poder comenzar un servicio importante en mi aplicación.
Mis observaciones
Lo he probado a fondo. Después de reiniciar el dispositivo, los receptores funcionan bien en mi Nexus 5X (Nougat), Nexus 6P (Nougat), YU Yuphoria (Lollipop) pero no en mi OnePlus 3 (Nougat) y Mi 4i (Lollipop) .
¿Cómo puede el mismo código funcionar perfectamente en algunos dispositivos y no funcionar en absoluto en los otros dispositivos? No he cambiado nada en absoluto.
¿Qué estoy haciendo mal aquí? Mi aplicación depende en gran medida de estas transmisiones e inicia servicios basados en ellas. Cualquier ayuda será muy apreciada.
EDIT 1
Para entender mejor el problema, acabo de crear un proyecto de prueba muy pequeño con solo una actividad y exactamente el mismo BootCompleteReceiver
y PhoneCallReceiver
.
Pero extrañamente, este proyecto funciona perfectamente en mi OnePlus 3, donde los receptores de mi aplicación real no funcionan después de un reinicio. Inicialmente estaba asumiendo que el problema está en el sistema operativo o en el dispositivo de alguna manera, pero no es así.
Entonces, ¿dónde está el problema real? ¿Está en mi aplicación (pero funciona perfectamente en otros dispositivos) o en el sistema operativo y el dispositivo (el pequeño proyecto de prueba funciona bien en el mismo sistema operativo y el mismo dispositivo)?
Es realmente confuso para mí. Necesitaría ayuda de un experto en esto.
EDIT 2
He intentado con la sugerencia de @shadygoneinsane. Aquí están mis observaciones.
1) Intenté enviar la transmisión BOOT_COMPLETED a través de ADB.
./adb shell am broadcast -a android.intent.action.BOOT_COMPLETED -p com.app.myapp
Y obtuve este rastro de pila,
Broadcasting: Intent { act=android.intent.action.BOOT_COMPLETED pkg=com.app.myapp }
java.lang.SecurityException: Permission Denial: not allowed to send broadcast android.intent.action.BOOT_COMPLETED from pid=25378, uid=2000
at android.os.Parcel.readException(Parcel.java:1683)
at android.os.Parcel.readException(Parcel.java:1636)
at android.app.ActivityManagerProxy.broadcastIntent(ActivityManagerNative.java:3696)
at com.android.commands.am.Am.sendBroadcast(Am.java:778)
at com.android.commands.am.Am.onRun(Am.java:404)
at com.android.internal.os.BaseCommand.run(BaseCommand.java:51)
at com.android.commands.am.Am.main(Am.java:121)
at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method)
at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:276)
Tal vez porque mi dispositivo no está rooteado. No puedo enviar esta transmisión de ninguna manera.
2) Intenté con la transmisión PROCESS_OUTGOING_CALLS después de eso.
./adb shell am broadcast -a android.intent.action.PROCESS_OUTGOING_CALLS -p com.app.myapp
Tengo esto,
Broadcasting: Intent { act=android.intent.action.PROCESS_OUTGOING_CALLS pkg=com.app.myapp }
Broadcast completed: result=0
Parece que la transmisión fue exitosa, pero no veo ningún Toast ni ningún registro. Luego abrí mi marcador para marcar un número y luego puedo ver el Toast y el registro ambos.
Parece que enviar la transmisión a través de ADB no funcionó , pero abrir el marcador y marcar un número sí funcionaba.
EDIT 3
Según la sugerencia de @ChaitanyaAtkuri, también he intentado agregar prioridad a los filtros de intenciones, pero eso no funcionó tan bien.
He usado prioridades como 500, 999 e incluso el valor entero más alto, pero nada funciona. Este problema también ocurre en algunas de mis aplicaciones de amigos también. Funcionan en algunos dispositivos y no funcionan en otros.
EDIT 4
Finalmente descubrí la causa raíz del problema en mi OnePlus 3 . Recientemente, mi OnePlus 3 se actualizó a Nougat e introdujeron una característica similar a los dispositivos Mi que impiden que ciertas aplicaciones se inicien automáticamente después del reinicio.
Al deshabilitar esta función, mi aplicación comenzó a recibir transmisiones después de reiniciarse perfectamente. Pero esto todavía no explica dos cosas.
1) Mi pequeño proyecto de prueba se incluye automáticamente en la lista blanca de aplicaciones de AutoLaunch y es por eso que funciona como se esperaba. Pero, ¿cómo es esto posible? ¿Por qué el sistema operativo considera que esta pequeña aplicación es digna de ser iniciada automáticamente?
2) Hay algunas aplicaciones como LockDown Pro, 500 Firepaper que está en la lista negra en la pantalla de aplicaciones de AutoLaunch, pero aún así, recibe transmisiones después de reiniciar en mi OnePlus 3 y Mi 4i. ¿Cómo es eso posible ahora? ¿Es de alguna manera posible permitir que mi aplicación se inicie automáticamente en estos dispositivos (OnePlus y Mi)?
EDIT 5
He intentado con la solución propuesta por @Rahul Chowdhury y realmente parece funcionar muy bien. Después de agregar el servicio de accesibilidad, el problema se resuelve.
Pero si el usuario revoca el permiso de accesibilidad después de otorgarlo, ¿hay alguna forma de que compruebe programáticamente si el permiso de accesibilidad está disponible para mi aplicación?
Aquí hay una solución probada y funcional en los dispositivos que mencionaste, OnePlus y Mi.
Como dijiste, la función de prevención de inicio automático en los dispositivos OnePlus y Mi evita que las aplicaciones inicien automáticamente sus servicios cuando se inicia el arranque para mejorar la velocidad general de arranque del dispositivo y el rendimiento de la batería. Sin embargo, hay una solución alternativa para que su aplicación funcione incluso cuando esta característica está activada.
Me di cuenta de que si tiene un servicio de AccessibilityService
en su aplicación y el usuario lo activa, entonces su aplicación pasa el filtro que aplican estos fabricantes y la aplicación recibe su evento de inicio completo y cualquier otro BroadcastReceiver
funciona como se espera.
La posible explicación de este truco puede ser que AccessibilityService
es un servicio de nivel de sistema, por lo que al registrar su propio servicio está pasando el filtro determinado aplicado por estos fabricantes y tan pronto como su sistema AccessibilityService
personalizado se active, su aplicación se activa al recibir el BroadcastReceiver
elegible que había registrado.
Entonces, aquí está cómo hacerlo,
Comience agregando este permiso a su AndroidManifest.xml
,
<uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE"/>
Esto le permitirá registrar el servicio de AccessibilityService
su aplicación con el sistema.
Ahora, agregue una configuración muy básica para su AccessibilityService
creando un archivo, por ejemplo, my_accessibility_service.xml
dentro de la carpeta XML debajo de su carpeta res en su proyecto.
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service
xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityFeedbackType="feedbackSpoken"
android:description="@string/service_desc"
android:notificationTimeout="100"/>
Solo queda un paso más por hacer, definir su AccessibilityService
personalizado en su proyecto,
public class MyAccessibilityService extends AccessibilityService {
@Override
public void onAccessibilityEvent(AccessibilityEvent event) { }
@Override
public void onInterrupt() {
}
}
Tenga en cuenta que, dado que no necesita AccessibilityService
para ningún propósito en lugar de esta solución, puede dejar los métodos reemplazados vacíos.
Finalmente, solo declare su AccessibilityService
en su AndroidManifest.xml
,
<service
android:name=".MyAccessibilityService"
android:label="@string/app_name"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService"/>
</intent-filter>
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/my_accessibility_service"/>
</service>
Eso es todo. Ahora, dentro de su aplicación, solo solicite a sus usuarios que activen el servicio de accesibilidad para su aplicación desde la configuración y déjelo encendido y ¡listo! Su aplicación funciona bien en todos los dispositivos, incluso cuando el sistema operativo pone un filtro sobre qué aplicaciones deben iniciarse automáticamente al arrancar.
EDIT 1
A continuación, le mostramos cómo puede verificar si el servicio de accesibilidad está activado o no para su aplicación.
private static final int ACCESSIBILITY_ENABLED = 1;
public static boolean isAccessibilitySettingsOn(Context context) {
int accessibilityEnabled = 0;
final String service = context.getPackageName() + "/" + MyAccessibilityService.class.getCanonicalName();
try {
accessibilityEnabled = Settings.Secure.getInt(
context.getApplicationContext().getContentResolver(),
android.provider.Settings.Secure.ACCESSIBILITY_ENABLED);
} catch (Settings.SettingNotFoundException e) {
Log.e("AU", "Error finding setting, default accessibility to not found: "
+ e.getMessage());
}
TextUtils.SimpleStringSplitter mStringColonSplitter = new TextUtils.SimpleStringSplitter('':'');
if (accessibilityEnabled == ACCESSIBILITY_ENABLED) {
String settingValue = Settings.Secure.getString(
context.getApplicationContext().getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
if (settingValue != null) {
mStringColonSplitter.setString(settingValue);
while (mStringColonSplitter.hasNext()) {
String accessibilityService = mStringColonSplitter.next();
if (accessibilityService.equalsIgnoreCase(service)) {
return true;
}
}
}
}
return false;
}
Espero que esto ayude.
Cómo iniciar el servicio en el arranque del dispositivo (aplicación de ejecución automática, etc.)
Para empezar: desde la versión Android 3.1+, no recibirá BOOT_COMPLETE si el usuario nunca inició su aplicación al menos una vez o la aplicación de "fuerza cerrada" del usuario. Esto se hizo para evitar que el malware registre automáticamente el servicio. Este agujero de seguridad se cerró en las versiones más nuevas de Android.
Solución:
Crea una aplicación con actividad. Cuando el usuario lo ejecuta una vez que la aplicación puede recibir BOOT_COMPLETE mensaje de difusión.
Por segundo: BOOT_COMPLETE se envía antes de que se monte el almacenamiento externo. Si la aplicación está instalada en el almacenamiento externo, no recibirá el mensaje de transmisión BOOT_COMPLETE.
En este caso, hay dos soluciones:
- Instala tu aplicación en el almacenamiento interno
- Instale otra aplicación pequeña en el almacenamiento interno. Esta aplicación recibe BOOT_COMPLETE y ejecuta la segunda aplicación en el almacenamiento externo.
Si su aplicación ya está instalada en el almacenamiento interno, el siguiente código puede ayudarlo a comprender cómo iniciar el servicio en el arranque del dispositivo.
En Manifest.xml
Permiso:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
Registra tu receptor BOOT_COMPLETED:
<receiver android:name="org.yourapp.OnBoot">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
Registra tu servicio:
<service android:name="org.yourapp.YourCoolService" />
En el receptor OnBoot.java:
public class OnBoot extends BroadcastReceiver
{
@Override
public void onReceive(Context context, Intent intent)
{
// Create Intent
Intent serviceIntent = new Intent(context, YourCoolService.class);
// Start service
context.startService(serviceIntent);
}
}
Para HTC es posible que también necesite agregar Manifiesto este código si el dispositivo no detecta RECEIVE_BOOT_COMPLETED:
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
El receptor ahora se ve así:
<receiver android:name="org.yourapp.OnBoot">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</receiver>
¿Cómo probar BOOT_COMPLETED sin reiniciar emulador o dispositivo real? Es fácil. Prueba esto:
adb -s device-or-emulator-id shell am broadcast -a android.intent.action.BOOT_COMPLETED
¿Cómo obtener la identificación del dispositivo? Obtener una lista de dispositivos conectados con id''s:
adb devices
adb en ADT por defecto puedes encontrarlo en:
adt-installation-dir/sdk/platform-tools
¡Disfrutar! )
Hola, llegué tarde a la fiesta pero estaba siguiendo esta pregunta desde el principio. Sé que los One-plus
y algunos otros OEM mantienen una lista de aplicaciones que pueden recibir transmisión BOOT_COMPLETED. Si su aplicación no está en la lista blanca, entonces su aplicación no se iniciará al arrancar. Ahora que tengo una solución que es muy eficiente en términos de memoria y recursos y que garantiza que comenzará su tarea o servicio después de reiniciar o reiniciar, tampoco necesita AccessibilityService
como se propone en esta answer . Aquí va..
- Agrega el
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
en tu archivo demanifest
.
2.Si no tiene una dependencia en com.google.android.gms:play-services-gcm
, agregue lo siguiente a la sección de dependencias de build.gradle:
compile ''com.firebase:firebase-jobdispatcher:0.5.2''
De lo contrario, agregue lo siguiente:
compile ''com.firebase:firebase-jobdispatcher-with-gcm-dep:0.5.2''
Esta es una library del equipo de firebase
que depende de la biblioteca google-play-service
para programar tus trabajos y desde mi punto de vista google-play-service
tiene el permiso para comenzar desde el inicio así que en vez del sistema, google-play-service
ejecutará tu trabajo tan pronto como el dispositivo se reinicie.
Ahora este paso es fácil Solo defina una clase JobService
public class MyJobService extends JobService { @Override public boolean onStartJob(JobParameters job) { Log.v("Running", "====>>>>MyJobService"); return false; // Answers the question: "Is there still work going on?" } @Override public boolean onStopJob(JobParameters job) { Log.v("Stopping", "====>>>>MyJobService"); return true; // Answers the question: "Should this job be retried?" }
}
Agregue su servicio de trabajo en el archivo de manifiesto.
<service android:exported="false" android:name=".MyJobService"> <intent-filter> <action android:name="com.firebase.jobdispatcher.ACTION_EXECUTE"/> </intent-filter> </service>
Programe este trabajo en cualquier lugar que desee, por ejemplo, cuando comience su aplicación.
FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(getApplicationContext())); Bundle myExtrasBundle = new Bundle(); myExtrasBundle.putString("some_key", "some_value"); Job myJob = dispatcher.newJobBuilder() // the JobService that will be called .setService(MyJobService.class) // uniquely identifies the job .setTag("my-unique-tag-test") // repeat the job .setRecurring(true) // persist past a device reboot .setLifetime(Lifetime.FOREVER) // start between 0 and 60 seconds from now .setTrigger(Trigger.executionWindow(0, 60)) // don''t overwrite an existing job with the same tag .setReplaceCurrent(false) // retry with exponential backoff .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL) // constraints that need to be satisfied for the job to run .setExtras(myExtrasBundle) .build(); dispatcher.mustSchedule(myJob);
6. ¡Eso es todo! Ahora puede ejecutar su tarea o servicio en el inicio del dispositivo sin importar si está en la lista blanca o no.
Hay un punto para tener en cuenta que el Servicio de Google Play debe estar instalado en el dispositivo, de lo contrario no funcionará.
La forma en que funciona IntentFilter
es que cada <intent-filter></intent-filter>
contiene una forma de activar el componente. Si tiene varias formas de activarlo (como dos acciones que desea escuchar en un BroadcastReceiver
), necesitará una definición independiente de <intent-filter></intent-filter>
para cada uno.
Por lo tanto, puedes intentar cambiar:
<receiver android:name=".mics.BootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</receiver>
a:
<receiver android:name=".mics.BootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</receiver>
Lea más aquí: Intentos y filtros de intención | Desarrolladores de Android
EDITAR
Si todavía no funciona, puede intentar probar si su declaración de manifiesto se realiza correctamente. Intente ejecutar el siguiente comando en su terminal, manteniendo el dispositivo de prueba conectado a la computadora:
adb shell am broadcast -a android.intent.action.BOOT_COMPLETED -n com.app.myapp/.mics.BootReceiver
Si esto no funciona, debe volver a verificar la declaración relativa del paquete del receptor en su archivo de manifiesto.
EDIT 2
Puede sonar extraño, pero intenta seguir estos pasos:
- Desinstale la aplicación de su teléfono (asegúrese de que esté desinstalada para todos los usuarios)
- Reinicia tu teléfono
- Limpia el proyecto
- Compila y ejecuta el proyecto en tu dispositivo de nuevo