广播机制
android中每个应用程序都可以对自己感兴趣的广播进行注册,这些广播可能是来源于系统也可能来源于其他应用程序,接收广播的方法则需要引入一个新概念——广播接收器 Broadcast Receiver
广播类型
1、标准广播
在广播发出后所有的广播接收器几乎同时接收到这条广播消息,因此没有先后之分,这种广播效率比较高也就意味着它是无法被截断的。
2、有序广播
广播发出后同一时刻只有一个广播接收器可以接收到这条广播消息,当这个广播接收器的逻辑执行完毕后,广播才会继续传播,优先级高的广播接收器就可以先接收到广播消息,而且还可以截断正在传播的广播,而后面的广播则不能收到广播消息。
静态注册实现开机
new->Other->Broadcast Receiver
public class BootCompleteReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // TODO: This method is called when the BroadcastReceiver is receiving // an Intent broadcast. Toast.makeText(context,"Boot Complete",Toast.LENGTH_SHORT).show(); } }添加
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
与
<intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED"/> </intent-filter>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.wangkangli.broadcast"> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <receiver android:name=".BootCompleteReceiver" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED"/> </intent-filter> </receiver> </application> </manifest>重启即可启动
发送自定义广播
1、发送标准广播
先定义一个广播接收器准备接收此广播
public class MyBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // TODO: This method is called when the BroadcastReceiver is receiving // an Intent broadcast. Toast.makeText(context,"receiver in MyBroadcastReceiver",Toast.LENGTH_SHORT).show(); } }在AndroidManifest.xml中修改广播接收器
<receiver android:name=".MyBroadcastReceiver" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="com.wangkangli.broadcast.MY_BROADCAST" /> </intent-filter> </receiver>设置点击事件发送广播
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button button = findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent("com.wangkangli.broadcast.MY_BROADCAST"); sendBroadcast(intent); } }); } }2、发送有序广播
新建project->新建AnotherBroadcastReceiver
public class AnotherBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // TODO: This method is called when the BroadcastReceiver is receiving // an Intent broadcast. Toast.makeText(context,"received in AnotherBroadcastReceiver",Toast.LENGTH_SHORT).show(); } }
<receiver android:name=".AnotherBroadcastReceiver" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="com.wangkangli.broadcast.MY_BROADCAST" /> </intent-filter> </receiver>
现在还是标准广播可以收到两个Toast提示
现在更改为有序广播更改MainActivity
button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent("com.wangkangli.broadcast.MY_BROADCAST"); sendOrderedBroadcast(intent,null); } });
使用sendOrderedBroadcast
如何设定先后顺序呢,在注册时
<receiver android:name=".MyBroadcastReceiver" android:enabled="true" android:exported="true"> <intent-filter android:priority="100"> <action android:name="com.wangkangli.broadcast.MY_BROADCAST" /> </intent-filter> </receiver>
priority = "100"优先级即比其他接收器高
现在截断这条广播使用abortBroadcast()
public class MyBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // TODO: This method is called when the BroadcastReceiver is receiving // an Intent broadcast. Toast.makeText(context,"receiver in MyBroadcastReceiver",Toast.LENGTH_SHORT).show(); abortBroadcast(); } }
3、使用本地广播
前面发送和接收的都属于系统全局广播,关键性数据广播会被截断,其他程序会不断的向我们发送垃圾广播,这样就需要使用本地广播处理这些安全问题 LocalBroadcast对广播进行管理
public class MainActivity extends AppCompatActivity { private IntentFilter intentFilter; private LocalBroadcastManager localBroadcastManager; private LocalReceiver localReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); localBroadcastManager = LocalBroadcastManager.getInstance(this); //获取实例 Button button = findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent("com.wangkangli.broadcast.LOCAL_BROADCAST"); localBroadcastManager.sendBroadcast(intent); //发送本地广播 } }); intentFilter = new IntentFilter(); intentFilter.addAction("com.wangkangli.broadcast.LOCAL_BROADCAST"); localReceiver = new LocalReceiver(); localBroadcastManager.registerReceiver(localReceiver,intentFilter); } @Override protected void onDestroy() { super.onDestroy(); localBroadcastManager.unregisterReceiver(localReceiver); } } class LocalReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context,"received local broadcast",Toast.LENGTH_SHORT).show(); } }
4、广播的最佳实践-实现强制下线功能
创建活动管理器
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(); } } } }
创建继承AppCompatActivity的BaseActivity
public class BaseActivity extends AppCompatActivity { private ForceOfflineReceiver receiver; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityCollector.addActivity(this); } @Override protected void onDestroy() { super.onDestroy(); ActivityCollector.removeActivity(this); } }登陆界面
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" > <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="60dp"> <TextView android:layout_width="90dp" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:textSize="18sp" android:text="Account:"/> <EditText android:id="@+id/account" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:layout_gravity="center_vertical"/> </LinearLayout> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="60dp"> <TextView android:layout_width="90dp" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:textSize="18sp" android:text="Password:"/> <EditText android:id="@+id/password" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:layout_gravity="center_vertical" android:inputType="textPassword"/> </LinearLayout> <Button android:id="@+id/login" android:text="login" android:layout_width="match_parent" android:layout_height="60dp" /> </LinearLayout>接收输入信息
public class LoginActivity extends BaseActivity { private EditText accountEdit,passwordEdit; private Button login; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); accountEdit = findViewById(R.id.account); passwordEdit = findViewById(R.id.password); login = findViewById(R.id.login); login.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String account = accountEdit.getText().toString(); String password = passwordEdit.getText().toString(); if (account.equals("admin")&&password.equals("123456")){ Intent intent = new Intent(LoginActivity.this,MainActivity.class); startActivity(intent); finish(); }else{ Toast.makeText(LoginActivity.this,"account or password is invalid",Toast.LENGTH_SHORT).show(); } } }); } }修改MainActivity
public class MainActivity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button forceOffine = findViewById(R.id.forcew_offline); forceOffine.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent("com.wangkangli.mylogicreceiver.FORCE_OFFLINE"); sendBroadcast(intent); } }); } }修改BaseActivity
public class BaseActivity extends AppCompatActivity { private ForceOfflineReceiver receiver; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityCollector.addActivity(this); } @Override protected void onResume() { super.onResume(); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction("com.wangkangli.mylogicreceiver.FORCE_OFFLINE"); receiver = new ForceOfflineReceiver(); registerReceiver(receiver,intentFilter); } @Override protected void onPause() { super.onPause(); if (receiver!=null){ unregisterReceiver(receiver); receiver = null; } } @Override protected void onDestroy() { super.onDestroy(); ActivityCollector.removeActivity(this); } } class ForceOfflineReceiver extends BroadcastReceiver{ @Override public void onReceive(final Context context, Intent intent) { AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle("warning"); builder.setMessage("you are forced to be offline.Please try to login again."); builder.setCancelable(false); builder.setPositiveButton("ok", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { ActivityCollector.finishAll(); Intent intent = new Intent(context,LoginActivity.class); context.startActivity(intent); } }); builder.show(); } }
修改AndroidManifest.xml
<activity android:name=".LoginActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>