Exploration de la diffusion des quatre composants majeurs d'Android

Table des matières

1. Le mécanisme de diffusion

2. Explorer la diffusion

1. Enregistrement dynamique

2. Enregistrement statique et envoi de diffusion standard

3. Envoyer une diffusion ordonnée

3. Posez des questions

première question

deuxième question

troisième question

4. Explorer les questions

première question

deuxième question

troisième question


La diffusion est une bonne chose. Notre programme peut envoyer et recevoir des émissions pour informer d'autres programmes de certaines nouvelles, ou recevoir des messages d'autres programmes.

1. Le mécanisme de diffusion

Il existe deux types de diffusions :

  1. diffusion standard

    Il s'agit d'une diffusion asynchrone. Une fois la diffusion envoyée, tous les récepteurs de diffusion correspondants peuvent recevoir le message de diffusion. De telles émissions sont plus efficaces, mais ne peuvent pas être interceptées une fois envoyées.

  2. diffusion ordonnée

    Comme son nom l'indique, ce type de diffusion est envoyé séquentiellement et est une diffusion synchrone. Selon l'ordre dans lequel les poids sont diffusés. Et le programme qui reçoit l'émission en premier peut tronquer l'émission, empêchant les récepteurs suivants de la recevoir.

Il existe deux façons d'accepter la diffusion :

  1. enregistrement dynamique

    De cette façon, nous pouvons enregistrer ou désenregistrer des récepteurs de diffusion à tout moment dans le programme. Par exemple, nous enregistrons le récepteur de diffusion dans onResume() de Activity et annulons le récepteur de diffusion dans onPause(). L'avantage de ceci est qu'il est très flexible, mais la limitation est que l'activité doit être démarrée avant de pouvoir être reçue.

  2. enregistrement statique

    Nous pouvons écrire une classe qui hérite de BroadcastReceiver, enregistrer ce récepteur de diffusion dans AndroidManifest.xml et spécifier le type de diffusion à accepter.

2. Explorer la diffusion

1. Enregistrement dynamique

Essayons d'abord d'utiliser l'enregistrement dynamique pour recevoir une diffusion d'un système.

Créez une nouvelle activité et créez une classe interne à l'intérieur :

    inner class TimeChangeReceiver : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            Toast.makeText(context, "the time has changed!", Toast.LENGTH_SHORT).show()
        }
    }

Cette classe hérite de BroadcastReceiver, qui est un récepteur de diffusion. Ensuite, réécrivez la méthode onReceive() et traitez la logique à traiter lorsque la diffusion est reçue.

Ensuite, nous enregistrons dynamiquement le récepteur de diffusion dans onCreate().

    lateinit var timeChangeReceiver: TimeChangeReceiver
​
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_dynamic_broadcast)
        timeChangeReceiver = TimeChangeReceiver()
        val intentFilter = IntentFilter()
        // 系统自带的时间改变的广播
        intentFilter.addAction("android.intent.action.TIME_TICK")
        registerReceiver(timeChangeReceiver, intentFilter)
    }

Ici, registerReceiver() est appelé pour enregistrer le récepteur de diffusion. Le premier paramètre est de spécifier le récepteur de diffusion, et le deuxième paramètre est un IntentFilter, qui est utilisé pour spécifier les diffusions à accepter. android.intent.action.TIME_TICK est la diffusion fournie avec le système Android. Cette diffusion sera envoyée lorsque l'heure changera (en minutes).

N'oubliez pas que la diffusion doit être désenregistrée après l'enregistrement

    // 记得要注销掉接收器
    override fun onDestroy() {
        super.onDestroy()
        unregisterReceiver(timeChangeReceiver)
    }

Exécutez maintenant le programme et une invite apparaîtra toutes les minutes.

 

2. Enregistrement statique et envoi de diffusion standard

Comme pour l'enregistrement dynamique précédent, si l'activité ne démarre pas, le récepteur de diffusion ne peut pas être enregistré. Mais l'enregistrement statique n'a pas de telles restrictions.

Ici, nous pouvons créer un récepteur de diffusion BroadcastReceiver.

 

  • Exporté indique s'il est autorisé à accepter des émissions autres que ce programme

  • Activé indique si le récepteur est démarré.

Après sa création, il suffit de redémarrer la méthode onReceiv(). Cependant, dans Androidmanifest.xml, Android Studio a déjà enregistré ce récepteur pour nous.

        <receiver
            android:name=".MyBroadcastReceiver"
            android:enabled="true"
            android:exported="true">
        </receiver>

Ensuite, nous spécifions la méthode de diffusion que ce récepteur reçoit, qui est également définie dans Adroidmanifest.xml.

        <receiver
            android:name=".MyBroadcastReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter >
                <action android:name="com.bingbin.MY_BROADCAST" />
            </intent-filter>
        </receiver>

Ici, nous définissons que nous allons recevoir la diffusion com.bingbin.MY_BROADCAST, et maintenant nous allons implémenter l'envoi d'une diffusion.

Créez une nouvelle activité et ajoutez-y un bouton pour envoyer des diffusions.

        btn_sendBroadcast.setOnClickListener {
            val intent = Intent("com.bingbin.MY_BROADCAST")
            intent.setPackage(packageName)
            sendBroadcast(intent)
        }

La méthode d'envoi d'une diffusion est sendBoradcast(), qui doit transmettre un Intent(). Lorsque nous l'avons créé, nous avons spécifié que la diffusion devait être envoyée com.bingbin.MY_BROADCAST.

Voici une explication de la raison pour laquelle il y a la ligne intent.setPackage(packageName) : les diffusions implicites font référence aux diffusions qui ne spécifient pas de programme d'envoi, telles que les diffusions à la mise sous tension et les diffusions de changement d'heure. Les récepteurs enregistrés de manière statique peuvent recevoir ces diffusions, mais cela entraînera la réception malveillante de diffusions par certains programmes pour consommer des ressources. Android a donc désormais interdit aux récepteurs enregistrés de manière statique de recevoir des diffusions implicites. Nous devons donc ici spécifier le programme sous le même nom de package pour recevoir cette émission.

Après avoir cliqué sur le bouton, un message d'invite apparaîtra :

 

Afin de vérifier la diffusion standard, nous créons ici un autre BroadcastReceiver pour recevoir également la diffusion com.bingbin.MY_BROADCAST

class AnotherReceiver : BroadcastReceiver() {
​
    override fun onReceive(context: Context, intent: Intent) {
        Toast.makeText(context, "another receiver also has received my broadcast",
            Toast.LENGTH_SHORT).show()
    }
}
        <receiver
            android:name=".AnotherReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter >
                <action android:name="com.bingbin.MY_BROADCAST" />
            </intent-filter>
        </receiver>

À ce moment, appuyez à nouveau sur le bouton et deux messages d'invite apparaîtront.

 

 

3. Envoyer une diffusion ordonnée

La méthode pour envoyer une diffusion standard est sendBroadcast(), et la méthode pour envoyer une diffusion ordonnée est sendOrderBoradcast().

Ajoutons un autre bouton pour envoyer une diffusion commandée.
 

       btn_sendOrderBroadcast.setOnClickListener {
            val intent = Intent("com.bingbin.MY_BROADCAST")
            intent.setPackage(packageName)
            sendOrderedBroadcast(intent, null)
        }

Le deuxième paramètre peut être défini sur null.

Puisqu'il s'agit d'une émission ordonnée, comment l'ordre devrait-il être décidé ? Nous pouvons déterminer le poids dans la balise <intent-filter> enregistrée par Receiver dans AndroidManifest.xml.

        <receiver
            android:name=".AnotherReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter android:priority="10">
                <action android:name="com.bingbin.MY_BROADCAST" />
            </intent-filter>
        </receiver>
        <receiver
            android:name=".MyBroadcastReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter android:priority="100">
                <action android:name="com.bingbin.MY_BROADCAST" />
            </intent-filter>
        </receiver>

De cette façon, MyBroadcastReceiver recevra la diffusion en premier, et AnotherReceiver la recevra plus tard.

Si la méthode abortBroadcast() est appelée dans la logique selon laquelle MyBroadcastReceiver reçoit la diffusion , la diffusion peut être coupée afin qu'AnotherReceiver ne puisse pas recevoir la diffusion.

De cette façon, seul le message de MyBroadcastReceiver apparaîtra.

3. Posez des questions

première question

La diffusion android.intent.action.TIME_TICK du système précédemment utilisé est-elle une diffusion standard ou une diffusion ordonnée ? Comment définir la priorité pour BroadcastReceiver enregistré dynamiquement ?

deuxième question

Un BroadcastReceiver peut-il recevoir plusieurs diffusions ? Si ce n'est pas le cas, s'il existe une exigence selon laquelle une logique ne sera déclenchée que lorsque deux diffusions spécifiées différentes sont reçues au cours d'une période de temps, comment devrait-elle être mise en œuvre ?

troisième question

Quel est le cycle de vie d'un récepteur enregistré statiquement ? Si plusieurs diffusions sont reçues à la suite, s'agit-il d'un nouveau récepteur ou de la même instance à chaque fois ?

4. Explorer les questions

première question

Tout d'abord, parce que android.intent.action.TIME_TICK est une diffusion implicite, nous ne pouvons pas utiliser la méthode d'enregistrement statique pour tester, nous ne pouvons utiliser que l'enregistrement dynamique BroadcastReceiver.

Ensuite, nous écrivons deux BroadcastReceivers internes dans une classe, définissons des priorités pour eux, et le BroadcastReceiver avec une priorité plus élevée essaie d'intercepter la diffusion pour voir si celui avec une priorité inférieure la recevra toujours.

Ici, en passant, le problème de la définition de la priorité du BroadcastReceiver enregistré dynamiquement est résolu. Nous devons transmettre le paramètre IntentFilter() lors de l'enregistrement de la diffusion, et la priorité peut être définie dans IntentFilter.

class DynamicBroadcastActivity : AppCompatActivity() {
​
    lateinit var timeChangeReceiver: TimeChangeReceiver
    lateinit var anotherReceiver : AnotherTimeChangeReceiver
​
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_dynamic_broadcast)
        timeChangeReceiver = TimeChangeReceiver()
        val filter1 = IntentFilter()
        // 系统自带的时间改变的广播
        filter1.addAction("android.intent.action.TIME_TICK")
        filter1.priority = 100
        registerReceiver(timeChangeReceiver, filter1)
​
        anotherReceiver = AnotherTimeChangeReceiver()
        val filter2 = IntentFilter()
        // 系统自带的时间改变的广播
        filter2.addAction("android.intent.action.TIME_TICK")
        filter2.priority = 10
        registerReceiver(anotherReceiver, filter2)
    }
​
    // 记得要注销掉接收器
    override fun onDestroy() {
        super.onDestroy()
        unregisterReceiver(timeChangeReceiver)
        unregisterReceiver(anotherReceiver)
    }
​
    inner class TimeChangeReceiver : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            Toast.makeText(context, "Time has changed!", Toast.LENGTH_SHORT).show()
            // 尝试拦截
            abortBroadcast()
        }
    }
​
    inner class AnotherTimeChangeReceiver : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            Toast.makeText(context, "AnotherTimeChangeReceiver has received the broadcast",
                Toast.LENGTH_SHORT).show()
        }
    }
}

Après les tests, il a été constaté que AnotherTimeChangeReceiver recevait toujours la diffusion. Cela montre que cette diffusion système est une diffusion standard.

Vraisemblablement, si la diffusion du système est une diffusion ordonnée, alors un programme malveillant enregistre un BroadcastReceiver avec une priorité élevée pour l'intercepter, et des problèmes sont susceptibles de se produire.

deuxième question

Pour tester cela, définissons un bouton qui envoie deux diffusions.

    btn_sendTwoBroadcast.setOnClickListener { 
            val broad1 = Intent("com.bingbin.BROADCAST1")
            broad1.setPackage(packageName)
            val broad2 = Intent("com.bingbin.BROADCAST2")
            broad2.setPackage(packageName)
            sendBroadcast(broad1)
            sendBroadcast(broad2)
        }

Essayez ensuite de recevoir deux émissions dans le récepteur enregistré de manière statique.

        <receiver
            android:name=".StrongReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="com.bingbin.BROADCAST1"/>
                <action android:name="com.bingbin.BROADCAST2"/>
            </intent-filter>
        </receiver>

Ajoutez ensuite la logique de traitement

class StrongReceiver : BroadcastReceiver() {
​
    var i = 0
​
    override fun onReceive(context: Context, intent: Intent) {
        Toast.makeText(context, "I have received a broadcast $i", Toast.LENGTH_SHORT).show()
        i += 1
    }
}

Afin de distinguer combien de fois il a été appelé, j'ai ajouté une variable globale.

Vérifié le journal

 

Il a été constaté que la sortie était sortie deux fois, mais je n'ai pas changé. C'est un peu étrange.

Plus tard, je l'ai mis dans une classe statique

 

La sortie s'avère correcte.

Ainsi, il peut être déterminé ici qu'un BroadcastReceiver peut spécifier de recevoir plusieurs diffusions.

troisième question

En fait, nous pouvons deviner à partir de la pratique de la deuxième question que chaque fois qu'un récepteur reçoit une diffusion, il devrait s'agir d'une nouvelle instance.

Vérifions à nouveau

class MyBroadcastReceiver : BroadcastReceiver() {
​
    init {
        Log.d("MyBroadcastReceiver", "创建了一个新的实例")
    }
​
    override fun onReceive(context: Context, intent: Intent) {
        Toast.makeText(context, "I have received my own broadcast!",
            Toast.LENGTH_SHORT).show()
    }
}

Ici, j'ai ajouté une phrase de sortie de journal au constructeur de MyBroadcastReceiver.

 

Vous constaterez que chaque fois qu'une diffusion est reçue, le récepteur enregistré statiquement créera une nouvelle instance.

Vient alors la question, l'instance précédemment créée existera-t-elle toujours en mémoire ? Quand est-il détruit ? Cela causera-t-il un OOM s'il n'est pas détruit ?

Ensuite, j'ai trouvé un tel commentaire dans le code source de onReceive()

Si ce BroadcastReceiver a été lancé via une balise <receiver>, alors l'objet n'est plus vivant après le retour de cette fonction. Cela signifie que vous ne devez effectuer aucune opération qui vous renvoie un résultat de manière asynchrone.

Nous pouvons donc voir que s'il s'agit d'un récepteur enregistré statiquement, il sera détruit après avoir appelé onReceive(). Il n'y a pas de risque mémoire.

Lorsque nous nous enregistrons dynamiquement, nous devons également faire attention à appeler activement unregisterReceiver() pour annuler le récepteur, sinon il peut y avoir un risque de fuite de mémoire.

Je suppose que tu aimes

Origine blog.csdn.net/qq_43478882/article/details/122262862
conseillé
Classement