目录
一.什么是广播
二.广播有什么用
3.注册广播
4.其他
5.创建方法
四.发送自定义广播(即非系统广播)
六.实战:模拟强制下线
kotlin版本代码:
动态注册
静态注册:
自定义广播:
发送有序广播
强制下线功能
参考资料:郭霖老师第一行代码第二版第五章
一.什么是广播
在Android中,Broadcast是一种广泛运用的在应用程序之间传输信息的机制。在生活中,我们的收音机的广播电台需要调到特定的频率才能接收到内容,在android中也是一样,通过sendBroadcast来发送广播并携带一个action,只有接收者的action和发送者action相同,才可以接收到这个广播.
二.广播有什么用
监听应用发出的广播消息,并且做出响应,包括不同组件之间的通信,应用与应用之间的都可以监听到
组件之间:比如当手机电量到30的时候,我在页面弹出一个框
应用之间:打开淘宝时支付时唤起支付宝
3.注册广播
1.动态注册
在代码中注册
2.静态注册
在manifest中注册
动态和静态区别:静态广播的生存期比动态广播的长很多.
动态广播无需在manifest中注册,而静态广播需要
注意有时候而且有时候需要一些权限
Android8.0之后,所有隐式广播(没有指定发送到哪个应用程序的广播,大多数系统广播属于隐式广播)不允许使用静态注册的方法
4.其他
标准广播
有序广播:即多个广播可以设置优先级,发送的时候是sendOrderBroadcast(action),可以在manifest中的receiver中设置android:priority:"100"来设置优先级,也可以使用abortBroadcast()来阻断后面的广播
本地广播:全局广播可以被其他程序接收到,本地广播不会,需要多加LocalBroadcastManager
5.创建方法
动态广播:
Intent intent = new Intent(action写在里面); sendBroadcast(intent);
在另一个活动中(一般是BaseActivity)里:一个IntentFilter来接收action,一个继承了BroadCast的Receiver,注册一下,注销一下就可以i
静态广播:通过编译器新建一个Receiver,某一个活动发出action,Receiver来接收,要自己在manifest里设置action
举栗代码:
public class MainActivity extends AppCompatActivity { private IntentFilter intentFilter; private NetworkChangeReceiver networkChangeReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); intentFilter = new IntentFilter(); intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE"); networkChangeReceiver = new NetworkChangeReceiver(); registerReceiver(networkChangeReceiver,intentFilter); } @Override protected void onDestroy() { super.onDestroy(); unregisterReceiver(networkChangeReceiver); } class NetworkChangeReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context,"network changes",Toast.LENGTH_LONG).show(); } } }
代码解释:
首先定义了一个IntentFilter(意图过滤器).然后新建一个类去继承了BroadReceiver这个类,然后在onceiver方法里去写你要接受什么样的广播就行了.然后调用registerReceiver方法注册,最后在ondestory中取消了注册即可.
问题:IntentFilter这个东西是干嘛的呢?
答:主要用来过滤隐式意图。当用户进行一项操作的时候,Android系统会根据配置的 “意图过滤器”来寻找可以响应该操作的组件,服务。当网络变化时,系统发出的就是一条android.net.conn.CONNECTIVITY_CHANGE的广播,所以我们就用IntentFilter把它捕获.
注意:动态注册的广播注册器一定要取消注册才行.
第二步:
因为这里我们要访问系统的网络,所以需要声明权限,在manifest下
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
然后我们运行程序,点击home键,在模拟机上找到控制手机流量开关的地方,打开关闭流量即看到toast弹出(先把wifi那个关了,才有反应,在实体手机上似乎是只在应用内部显示,无法在全局显示)
改进只显示网络变化不够,还要知道是网络是打开还是关闭:
所以就在onReceive方法中改:
class NetworkChangeReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { ConnectivityManager connectivityManager = (ConnectivityManager)getSystemService (Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); if(networkInfo != null && networkInfo.isConnected()){ Toast.makeText(context,"移动网络打开",Toast.LENGTH_SHORT).show(); } else{ Toast.makeText(context,"移动网络关闭",Toast.LENGTH_SHORT).show(); } } }
解释:ConnectivityManager这个类有很多作用,可以用来监控网络连接
getSystemService:系统服务,然后参数是要获取的系统服务,如这里就获取了连接服务
然后调用connectivityManager的一个方法getActiveNetworkInfo()去获取网络状态
四.发送自定义广播(即非系统广播)
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="https://schemas.android.com/apk/res/android" xmlns:app="https://schemas.android.com/apk/res-auto" xmlns:tools="https://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".SecondActivity"> <EditText android:id="@+id/account" android:hint="请输入你的账号" android:layout_width="match_parent" android:layout_height="wrap_content" /> <EditText android:id="@+id/password" android:hint="请输入你的密码" android:layout_width="match_parent" android:layout_height="wrap_content" /> <Button android:id="@+id/login" android:layout_gravity="center" android:text="登录" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
package com.example.myapplication; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.support.v4.content.LocalBroadcastManager; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.TextView; import android.widget.Toast; public class MainActivity extends AppCompatActivity { private TextView textView; Context context; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView =findViewById(R.id.textview); textView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent("broadcast"); //需要加上下面这一句,不然收不到广播,第一个参数为包名,第二个参数为广播接收器 intent.setComponent(new ComponentName("com.example.myapplication","com.example.myapplication.MyReceiver")); intent.putExtra("content","你的账号"+account.getText().toString()+"你的密码"+password.getText().toString()); sendBroadcast(intent); } }); } }
新建一个接收器
package com.example.myapplication; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.widget.Toast; public class MyReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String content = intent.getStringExtra("content"); Toast.makeText(context,content,Toast.LENGTH_SHORT).show(); } }
在manifests中,把要接收的action写好,所以是一个静态广播
<receiver android:name=".MyReceiver"> <intent-filter> <action android:name="broadcast" /> </intent-filter> </receiver>
六.实战:模拟强制下线
因为我们在被强制下线时不知道在哪个界面,所以首先创建一个ActivityColletor来作为专门的集合类对所有活动进行管理
public class ActivityCollector { public static List<Activity> activities = new ArrayList<>(); public static void addActivity(Activity activity){ activities.add(activity); } public static void removeActivity(Activity activity){ activities.remove(activity); } public static void finishAll(){ for (Activity activity : activities){ if (!activity.isFinishing()){ activity.finish(); } } activities.clear(); } }
通过上面的集合类进行活动的管理,还要有一个类BaseActivity作为所有活动的父类去接受广播,这样不管你在哪个活动中都可以通过父类BaseActivity去调用开放方法finishAll去终结活动.(在这里还有一个需要注意的地方,就是我们的取消注册接收器放在了onPause()方法中,这样就是为了当一个activity失去栈顶位置就会自动取消广播接收器的注册.)
public class BaseActivity extends AppCompatActivity { private OfflineReceiver offlineReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityCollector.addActivity(this); } @Override protected void onResume() { super.onResume(); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction("offline"); offlineReceiver = new OfflineReceiver(); registerReceiver(offlineReceiver,intentFilter); } @Override protected void onPause() { super.onPause(); if (offlineReceiver != null){ unregisterReceiver(offlineReceiver); } } @Override protected void onDestroy() { super.onDestroy(); ActivityCollector.removeActivity(this); } class OfflineReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, final Intent intent) { AlertDialog.Builder builder = new AlertDialog.Builder(BaseActivity.this); builder.setMessage("你被强制下线"); builder.setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { ActivityCollector.finishAll(); Intent intent1 = new Intent(BaseActivity.this,ThirdActivity.class); startActivity(intent1); } }); builder.show(); } } }
第三个activity:(实现登录)
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="https://schemas.android.com/apk/res/android" xmlns:app="https://schemas.android.com/apk/res-auto" xmlns:tools="https://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <EditText android:id="@+id/account" android:hint="请输入你的账号" android:layout_width="match_parent" android:layout_height="wrap_content" /> <EditText android:id="@+id/password" android:hint="请输入你的密码" android:layout_width="match_parent" android:layout_height="wrap_content" /> <Button android:id="@+id/login" android:layout_gravity="center" android:text="登录" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
public class ThirdActivity extends BaseActivity{ private EditText account; private EditText password; private Button Login; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_third); account = findViewById(R.id.account); password = findViewById(R.id.password); Login = findViewById(R.id.login); Login.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String content = account.getText().toString(); String content1 = password.getText().toString(); if (content.equals("admin")&&content1.equals("123456")){ Intent intent = new Intent(ThirdActivity.this,fourth.class); startActivity(intent); } else { AlertDialog.Builder builder = new AlertDialog.Builder(ThirdActivity.this); builder.setMessage("用户名或密码错误"); builder.setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }); builder.show(); } } }); } }
第四个acticvity,实现强制下线
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="https://schemas.android.com/apk/res/android" xmlns:app="https://schemas.android.com/apk/res-auto" xmlns:tools="https://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".fourth"> <Button android:id="@+id/force_offline" android:text="强制下线" android:layout_marginTop="25dp" android:layout_gravity="center_horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
package com.example.myapplication; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; public class fourth extends BaseActivity { private Button force; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_fourth); force = findViewById(R.id.force_offline); force.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent("offline"); sendBroadcast(intent); } }); } }
kotlin版本代码:
一个监听时间变化的系统广播:一分钟toast一次
动态注册
class BroadcastActivity : AppCompatActivity() { lateinit var timeChangeReceiver: TimeChangeReceiver override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_broadcast) val intentFilter = IntentFilter() intentFilter.addAction("android.intent.action.TIME_TICK") timeChangeReceiver = TimeChangeReceiver() registerReceiver(timeChangeReceiver, intentFilter) } override fun onDestroy() { super.onDestroy() unregisterReceiver(timeChangeReceiver) } inner class TimeChangeReceiver : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { Toast.makeText(context, "Time has changed", Toast.LENGTH_SHORT).show() } } }
静态注册:
前面说了Android8.0之后隐式广播不允许静态注册,而以下这个开机广播比较特殊,还可以使用静态注册
可以通过新建一个Broadcast Receiver来创建一个广播接收者
class BootCompleteReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { Toast.makeText(context, "Boot Complete", Toast.LENGTH_SHORT).show() } }
最后需要在manifest中写权限:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<receiver android:name=".other.BootCompleteReceiver" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver>
运行结果:
自定义广播:
这里使用静态广播去接收一个按钮发出来的广播
class MyReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { Toast.makeText(context, "received in MyBroadcastReceiver", Toast.LENGTH_SHORT).show() } }
这里定义我们的action,一会发广播时就要匹配这个值
<receiver android:name=".other.MyReceiver" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="com.example.timingdemo.other.MY_BROADCAST" /> </intent-filter> </receiver>
默认情况下我们发出的自定义广播为隐式广播,所以一定要调用setPackage()方法
button.setOnClickListener { val intent = Intent("com.example.timingdemo.other.MY_BROADCAST") intent.setPackage(packageName) //这一行必须加,否则为隐式广播无法被接收 sendBroadcast(intent) }
发送有序广播
定义另外一个Receiver
class AnotherBroadcastReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { Toast.makeText(context, "received in AnotherBroadcastReceiver", Toast.LENGTH_SHORT).show() } }
设定优先级
<receiver android:name=".other.AnotherBroadcastReceiver" android:enabled="true" android:exported="true"> <intent-filter android:priority="50"> <action android:name="com.example.timingdemo.other.MY_BROADCAST" /> </intent-filter> </receiver> <receiver android:name=".other.MyReceiver" android:enabled="true" android:exported="true"> <intent-filter android:priority="100"> <action android:name="com.example.timingdemo.other.MY_BROADCAST" /> </intent-filter> </receiver>
改用这个方法即可 sendOrderedBroadcast(intent, null)
button.setOnClickListener { val intent = Intent("com.example.timingdemo.other.MY_BROADCAST") intent.setPackage(packageName) //这一行必须加,否则为隐式广播无法被接收 sendOrderedBroadcast(intent, null) }
如果想要阻断后面的广播,使用
abortBroadcast()
强制下线功能
与上面那个功能一样,只不过改为了kotlin版本
object ActivityCollector { private val activities = ArrayList<Activity>() fun addActivity(activity: Activity){ activities.add(activity) } fun removeActivity(activity: Activity){ activities.remove(activity) } fun finishAll(){ for (activity in activities) { if (!activity.isFinishing){ activity.finish() } } activities.clear() } }
open class BaseActivity : AppCompatActivity() { lateinit var receiver: ForceOfflineReceiver override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ActivityCollector.addActivity(this) } override fun onDestroy() { super.onDestroy() ActivityCollector.removeActivity(this) } override fun onResume() { super.onResume() val intentFilter = IntentFilter() intentFilter.addAction("com.example.timingdemo.base.FOR_OFFCE_OFFLINE") receiver = ForceOfflineReceiver() registerReceiver(receiver, intentFilter) } override fun onPause() { super.onPause() unregisterReceiver(receiver) } inner class ForceOfflineReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent?) { AlertDialog.Builder(context).apply { setTitle("Warning") setMessage("you are forced to be offline. please try to login again.") setCancelable(false) //如果lambda参数未使用,可以使用下划线来替代,只有一个参数时可以使用it来代替 setPositiveButton("OK") { _ , _ -> ActivityCollector.finishAll() val intent = Intent(context, LoginActivity::class.java) context.startActivity(intent) } show() } } } }
class LoginActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_login) val accountEdit = findViewById<EditText>(R.id.accountEdit) val passwordEdit = findViewById<EditText>(R.id.passwordEdit) login.setOnClickListener { val account = accountEdit.text.toString() val password = passwordEdit.text.toString() if (account == "admin" && password == "123") { val intent = Intent(this, Main2Activity::class.java) startActivity(intent) finish() } else { Toast.makeText(this, "account or password is invalid", Toast.LENGTH_SHORT).show() } } } }
class Main2Activity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main2) forceOffLine.setOnClickListener { val intent = Intent("com.example.timingdemo.base.FOR_OFFCE_OFFLINE") sendBroadcast(intent) } } }
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="https://schemas.android.com/apk/res/android" xmlns:app="https://schemas.android.com/apk/res-auto" xmlns:tools="https://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".activity.LoginActivity"> <TextView android:id="@+id/textView4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:text="账户" android:textSize="16sp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/textView7" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="32dp" android:text="密码" android:textSize="16sp" app:layout_constraintEnd_toEndOf="@+id/textView4" app:layout_constraintStart_toStartOf="@+id/textView4" app:layout_constraintTop_toBottomOf="@+id/textView4" /> <Button android:id="@+id/login" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="44dp" android:text="登录" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.498" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/textView7" /> <EditText android:id="@+id/accountEdit" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginEnd="16dp" app:layout_constraintBottom_toBottomOf="@+id/textView4" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@+id/textView4" app:layout_constraintTop_toTopOf="@+id/textView4" /> <EditText android:id="@+id/passwordEdit" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginEnd="16dp" app:layout_constraintBottom_toBottomOf="@+id/textView7" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@+id/textView7" app:layout_constraintTop_toTopOf="@+id/textView7" /> </androidx.constraintlayout.widget.ConstraintLayout>
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/addevelopment/892727.html