广播机制基础知识

广播机制

1.广播机制

1.1概述

1.1.1引入

1.广播的生活案例

  • 记得以前读书的时候,每个班级都会有一个挂在墙上的大喇叭,用来广播一些通知,比如,开学要去搬书,广播: "每个班级找几个同学教务处拿书",发出这个广播后,所有同学都会在同一时刻收到这条广播通知,收到,但不是每个同学都会去搬书,一般去搬书的都是班里的"大力士",这群"大力士"接到这条广播后就会动身去把书搬回可是!——好吧,上面这个就是一个广播传递的一个很形象的例子:
  • 大喇叭--> 发送广播 --> 所有学生都能收到广播 --> 大力士处理广播

2.生活中广播特点

(1)广播发送者

  • 发一个或者多个
  • 不关心谁接收
  • 实时性

(2)广播接收者

  • 接收一个或者多个
  • 执行相应操作
  • 不关心谁发的
  • 实时性

3.广播的好处

  • 广播的发送者和接收者事先是不需要知道对方的存在的,这样带来的好处便是:系统的各个组件可以松耦合地组织在一起,这样系统就具有高度的可扩展性,容易与其它系统进行集成。

1.1.2Android中的广播

1.安卓中的广播

  • 如:手机接到一条短信,就会产生一个收到短消息的事件。接到一个电话,就会产生一个接到电话的事件。拍摄照片后就会产生一个拍摄照片的事件。
  • 在Android中,有一些操作完成以后,会发送广播,比如说发出一条短信,或打出一个电话,比如电池的使用状态,电话的接收和短信的接收都会产生一个广播。Android系统内部产生这些事件后广播这些事件,至于广播接收对象是否关心这些事件,以及它们如何处理这些事件,都由广播接收对象自己决定。

广播机制基础知识_第1张图片

2.系统广播

  • 电池的状态(如:电量不足)
  • 短信的接收和发送
  • 电话的接听和拨打
  • 系统闹钟
  • 系统垃圾文件占用内存过多

广播机制基础知识_第2张图片

3.广播的原理

广播机制基础知识_第3张图片

4.广播机制

  • 检查注册时候的IntentFilter中的Action是不是和广播Intent中的action是一致的
  • 一致的话使用onReceive相匹配

广播机制基础知识_第4张图片

1.1.3广播接收器

1.BroadcastReceiver

  • 监听系统或者是应用广播

2.BroadcastReceiver介绍

  • 安卓中四大组件之一
  • 本质:全局的监听器(OnXxxListener是程序级别的事件监听器

3.如何接收广播?

  • 先注册,注册方式有多种

4.系统广播接收器创建步骤

关键是注册

  1. 创建BroadcaseReceiver的子类,重写onReceive方法
  2. 注册,编写intentfilter标签设置action的值
  3. 广播发出后,对比intentfilter中的action的值是不是一致的,一致就调用onReceive方法创建接收器对象否则不创建

总之,接收的事件处理逻辑放到onReceive方法中就可以。调用完onReceive方法之后就失效了。

5.发送本地广播的创建方式

  1. 创建BroadcaseReceiver的子类,重写onReceive方法
  2. 注册,编写intentfilter标签设置action的值
  3. 创建Intent,设置action属性,使用sendBroadcast(intent)发送广播
  4. 广播发出后,对比intentfilter中的action的值是不是一致的,一致就调用onReceive方法创建接收器对象否则不创建

1.1.4自定义广播接收器

  • Intent就相当于无线电的信号,里面有很多正要的信息。

//第一个广播 接收器
public class MyBroadcastReceiver1 extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
    }
}

1.1.5常见的系统权限

发短信要有发短信的权限
接收开机广播的权限


广播机制基础知识_第5张图片

1.1.6常见的广播Action

广播机制基础知识_第6张图片

1.1.7发送系统级广播

1.广播类型

  • 标准广播
    • 异步执行,不一定哪个先接收到,几乎是同时
  • 有序广播
    • 根据优先级和注册的顺序一个一个的接收,只有接受并处理完了才可以继续往下传播

2.创建方式

  • 标准广播:context的sendBroadcast(Intent)
  • 有序广播:context的sendOrderedBroadcast(Intent,str)
    • 元素的android:priority属性中设置
    • 在IntentFilter对象的setPriority()设置
    • 取值范围:-1000到1000,数越大,优先级越高
    • str,权限参数,如果为null则表示不要求接收者声明指定的权限,如果不为null,则表示接收者若要接收此广播,需声明指定权限。

广播机制基础知识_第7张图片

3.有序广播接收器

  • setResultExtras(Bundle)方法将一个Bundle对象设置为结果集对象,传给下一个接收者。
  • 下一个接收者通过getResultExtras()可以获取上一个接收者存入的数据

public final Bundle getResultExtras(boolean makeMap)

makeMap如果为true,如果当前的Bundle为null,将创建一个空的Bundle对象;否则需要准备好接收一个空Bundle对象。用的时候设置成true就行了

  • abortBroadcast(),阻断广播不往下传播

4.有序和无序的比较

标准

有序

异步

反之

不可以终止

反之

无法传递结果

反之

发送方式是sendBroadcast(intent)

反之

1.1.8本地广播(重点)

1.优缺点

  • 高效
  • 防止泄露
  • 安全性更高

2.如何创建

  • 只能采用动态的注册方式
  • 必须在一个包下

3.LocalBroadcastManager

  • 局部通知管理器,这种通知的好处是安全性高,效率也高,适合局部通信。

4.获取LocalBroadcastManager的实例对象

LocalBroadcastManager localBManager = 
		LocalBroadcastManager.getInstance( this ) ;

5.注册方式

  • LocalBroadcastManager对象的registerReceiver()注册广播

6.创建步骤(重点)

  1. 创建intent,设置action
  2. 可选,设置intent的数据
  3. 创建IntentFilter,设置action
  4. 创建自定义的BroadCastReceiver对象
  5. 获取LocalBroadcastManager对象
  6. 根据LocalBroadcastManager对象调用registerBroadcast(接收器对象,intentfilter)注册
  7. LocalBroadcastManager对象的sendBroadcast(intent)发送广播

注意:

  • 和一般的不同的就在于5,6,7步
  • 本地广播无法通过静态注册来接收
  • 本地广播相比系统全局广播更加高效

7.取消注册的方法

  1. LocalBroadcastManager对象的unregisterReceiver(localReceiver);方法

1.2注册方式

两种注册方式:

  • 动态注册:在代码里注册(万能)
  • 静态注册:在AndroidManifest.xml中注册;

1.2.1静态注册

1.注册方式

  • AndroidManifest.xml中注册。
  • action指定要接收的广播事件

 
            android:exported="true">
 
  android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
            
        

扩展:在顺序广播中可以指定优先级

广播机制基础知识_第8张图片

2.优缺点

优点:不用启动程序就可以

1.2.2动态注册

1.注册方式

  • 有两种代码的方式
  • IntentFilter设置要监听的广播
  • registerReceiver注册广播

广播机制基础知识_第9张图片

2.IntentFilter的方法

No

方法

类型

描述

1

public IntentFilter()

构造

创建一个空的IntentFilter对象

2

public IntentFilter(String action)

构造

创建一个IntentFilter对象,并指定Action

3

public final void addAction(String action)

普通

增加一个要过滤的Action

4

public final void addCategory(String category)

普通

增加一个要过滤的Category

5

public final boolean hasAction(String action)

普通

判断指定的Action是否存在

6

public final boolean hasCategory(String category)

普通

判断指定的Category是否存在

3.取消注册

  • 一定要设置取消注册,不然的话在Activity或者是Service销毁的时候没有取消注册会报错的。

广播机制基础知识_第10张图片

4.优缺点

优点:注册方式灵活,根据需要注册,常用于更新UI。

缺点:需要程序启动才可以接收广播,假如我们需要程序 没有启动,但是还是能接收广播的话,那么就需要注册静态广播了!

也就是广播接收器必须是启动着的。

1.2.3静态注册案例-接收开机广播

1.案例:接收开机的广播

  1. 创建自定义的BroadcastReceiver用于处理监听到的系统广播。

//接收系统开机的广播事件
public class BootCompleteReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "开机成功!", Toast.LENGTH_SHORT).show();
    }
}

  1. 注册广播接收器。

   
            android:exported="true">
            
                
            
        

  1. 开启权限。

   <!--接收开机广播的权限-->
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

  1. 重启手机。

广播机制基础知识_第11张图片

1.2.4静/动态注册案例-发送广播

1.动态注册的方式发送标准广播

  • 发送标准广播
  • 设置要发送的数据
  • 不带权限

(1)布局文件

    
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="发送标准广播"
        android:textSize="30dp"
        android:onClick="sendBroadcast" />

(2)Java代码

    //点击按钮发送标准广播
    public void sendBroadcast(View view) {
        //设置发送的数据
        Intent intent =new Intent();
        intent.setAction("com.lxz.app8");
        intent.putExtra("code","我是张三!");
        //动态注册
        IntentFilter filter=new IntentFilter();
        filter.addAction("com.lxz.app8");
        BroadcastReceiver1 receiver=new BroadcastReceiver1();
        registerReceiver(receiver,filter);
        //发送广播
        sendBroadcast(intent);

    }

(3)自定义广播接收器

//接收标准广播
public class BroadcastReceiver1 extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "接收信息:"+intent.getStringExtra("code"), Toast.LENGTH_SHORT).show();
    }
}

(4)效果图

广播机制基础知识_第12张图片

2.在1的基础上设置权限

(1)manifest中定义和设置权限

    
    
    
    

(2)布局文件

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="发送标准广播(带权限)"
        android:textSize="30dp"
        android:onClick="sendBroadcast2" />

(3)发送广播的Java代码

  //点击按钮发送标准广播(带权限)
    public void sendBroadcast2(View view) {
        //设置发送的数据
        Intent intent =new Intent();
        intent.setAction("com.lxz.app8");
        intent.putExtra("code","我是张三!有权限!");
        //动态注册
        IntentFilter filter=new IntentFilter();
        filter.addAction("com.lxz.app8");
        BroadcastReceiver1 receiver=new BroadcastReceiver1();
        registerReceiver(receiver,filter);

        //设置权限
        String str="com.lxz.app.permission";
        //发送广播
        sendBroadcast(intent,str);

    }

(4)广播接收器

//接收标准广播
public class BroadcastReceiver1 extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "接收信息:"+intent.getStringExtra("code"), Toast.LENGTH_SHORT).show();
    }
}

(5)效果图

广播机制基础知识_第13张图片

3.有序广播的测试

  • 采用静态注册的方式
  • 设置优先级

(1)布局文件


    
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="发送有序广播"
        android:textSize="30dp"
        android:onClick="sendOrderBroadcast1" />

(2)发送有序广播的方法

 //点击按钮发送有序广播
    public void sendOrderBroadcast1(View view) {
        //设置发送的数据
        Intent intent =new Intent();
        intent.setAction("com.lxz.app8");
        intent.putExtra("code","请依次报号!");

        //发送广播-参数2代表的是自定义的权限
        sendOrderedBroadcast(intent,null);

    }

(3)注册代码

    
        
            android:exported="true"
            >
            
                
            
        
        
            android:exported="true"
            >
            
                
            
        
        
            android:exported="true"
            >
            
                
            
        
        
            android:exported="true"
            >
            
                
            
        

(4)广播接收者

  • 注册顺序从上到下
  • 优先级从下到上
  • 响应的时候从下到上

广播机制基础知识_第14张图片

  • 代码从下到上依次为

public class BroadcastOrderReceiver3 extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "接收信息:"+intent.getStringExtra("code")+"我是3号", Toast.LENGTH_SHORT).show();
        Bundle bundle=getResultExtras(true);
        System.out.println("----第3个接收器---");
        System.out.println("附加信息---"+bundle.getString("other"));
        bundle.putString("other",bundle.getString("other")+",我是3号!");
        setResultExtras(bundle);

    }
}

public class BroadcastOrderReceiver2 extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "接收信息:"+intent.getStringExtra("code")+"我是2号", Toast.LENGTH_SHORT).show();
        Bundle bundle=getResultExtras(true);
        System.out.println("----第2个接收器---");
        System.out.println("附加信息---"+bundle.getString("other"));
        bundle.putString("other",bundle.getString("other")+",我是2号!");
        setResultExtras(bundle);


    }
}

public class BroadcastOrderReceiver1 extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "接收信息:"+intent.getStringExtra("code")+"我是1号", Toast.LENGTH_SHORT).show();
        Bundle bundle=getResultExtras(true);
        System.out.println("----第1个接收器---");
        System.out.println("附加信息---"+bundle.getString("other"));
        bundle.putString("other",bundle.getString("other")+",我是1号!");
        setResultExtras(bundle);
        //停止广播的传递
        abortBroadcast();
        System.out.println("阻断传播");
    }
}

public class BroadcastOrderReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "接收信息:"+intent.getStringExtra("code")+"我是0号", Toast.LENGTH_SHORT).show();
        Bundle bundle=getResultExtras(true);
        System.out.println("----第0个接收器---");
        System.out.println("附加信息---"+bundle.getString("other"));
    }
}

(5)效果图

广播机制基础知识_第15张图片

广播机制基础知识_第16张图片

1.2.5本地广播案例-简单的发送和接收

1.参考代码

(1)布局文件代码

 
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="发送本地广播"
        android:textSize="30dp"
        android:onClick="localBroadcast" />

(2)发送本地广播的Java代码

    //发送本地广播
    public void localBroadcast(View view) {
        //设置数据
        Intent intent=new Intent();
        intent.setAction("com.lxz.localapp");
        intent.putExtra("code","我是本地广播");
        //动态注册
        LocalBroadReceiver receiver=new LocalBroadReceiver();
        IntentFilter filter=new IntentFilter();
        filter.addAction("com.lxz.localapp");
        LocalBroadcastManager manager=LocalBroadcastManager.getInstance(getApplicationContext());
        manager.registerReceiver(receiver,filter);

        //发送广播
        manager.sendBroadcast(intent);
    }

(3)本地广播接收者的代码

//接收本地广播
public class LocalBroadReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "接收信息:"+intent.getStringExtra("code"), Toast.LENGTH_SHORT).show();
    }
}

(4)效果图

广播机制基础知识_第17张图片

1.2.6本地广播案例-仿qq下线

待实现

1.案例要求

  • 像QQ一样,正在运行的QQ,如果我们用别的手机再次登陆自己的账号,前面这个是会提醒账户在别的终端登录,然后把我们打开的app都关掉,然后回到登陆页面。LoginActivity.java
  • 注意:需要在模拟器中设置—应用-左上角— —打开应用信息—设置出现在其它应用上

2.参考代码

(1)项目目录结构

广播机制基础知识_第18张图片

(2)ActivityCtroller代码。

  • 用于管理所有的Activity

//Activity管理类
public class ActivityController {
    //保存Activity
   private static List<Activity> list=new ArrayList<>();
   //添加Activity
   public  static void addActivity(Activity activity){
        list.add(activity);
   }
    //删除Activity
    public  static void removeActivity(Activity activity){
        list.remove(activity);
    }
    //结束所有的Activity
    public static void finishAllActivity(){
        for (Activity a:list){
           if (!a.isFinishing()){
               a.finish();
           }
        }
    }
}

(3)BaseActivity,是登录成功界面的基类,可以注册BroadcastReceiver。

  • 注意必须添加
  • android.intent.category.DEFAULT

public class BaseActivity extends AppCompatActivity {
    private LoginOutReceiver receiver;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityController.addActivity(this);
    }

    @Override
    protected void  onResume() {
        super.onResume();
        //注册广播
        IntentFilter filter = new IntentFilter();
        filter.addAction("com.lxz.loginout");
        filter.addCategory("android.intent.category.DEFAULT");
        receiver = new LoginOutReceiver();
        registerReceiver(receiver, filter);


    }

    @Override
    protected void onPause() {
        super.onPause();

        if (receiver!=null){
            unregisterReceiver(receiver);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

    }
}

(4)登录界面的Activity。

//仿qq下线
public class LoginActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
    }

    //方法:登录
    public void login(View view) {
        EditText account=findViewById(R.id.account);
        EditText password=findViewById(R.id.password);
        System.out.println(account+","+password);
        if (account.getText().toString().equals("root")&&password.getText().toString().equals("123456")){
             Toast.makeText(this, "登录成功!", Toast.LENGTH_SHORT).show();
            Intent intent=new Intent();
            intent.setClass(getApplicationContext(),LoginSuccessActivity.class);
            startActivity(intent);
            finish();
        }
        else{
            Toast.makeText(this, "账号或密码错误!", Toast.LENGTH_SHORT).show();
        }


    }
}

(5)登录界面activity对应的布局文件。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".LoginActivity">

    <TableLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:stretchColumns="1">

        <TableRow>

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:inputType="text"
                android:text="账号:"
                android:textSize="30dp" />

            <EditText
                android:id="@+id/account"
                android:text="root"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="账号(root)"
                android:inputType="text"
                android:maxLines="1"
                android:textSize="30dp" />
        TableRow>

        <TableRow>

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:inputType="text"
                android:text="密码:"
                android:textSize="30dp" />

            <EditText
                android:id="@+id/password"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="密码(123456)"
                android:inputType="text"
                android:maxLines="1"
                android:text="123456"
                android:textSize="30dp" />
        TableRow>
    TableLayout>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="login"
        android:text="登录"
        android:textSize="30dp" />
LinearLayout>

(6)登录成功Activity,LoginSuccessActivity的代码。

  • 发送广播

//登录成功后的界面
public class LoginSuccessActivity extends BaseActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login_success);
        //当登录后的Activity添加到管理类中去
        ActivityController.addActivity(this);


    }

    //方法:强制下线(发布广播)
    public void outlogin(View view) {
            Intent intent=new Intent();
            intent.setAction("com.lxz.loginout");
            intent.addCategory("android.intent.category.DEFAULT");
           sendBroadcast(intent);
    }

}

(7)activity_login_success布局文件代码。

  • 有一个强制下线的按钮。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".LoginSuccessActivity">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="outlogin"
        android:text="强制下线"
        android:textSize="30dp" />
LinearLayout>

(8)LoginOutReceiver广播接收器代码。

  • 设置一个Dialog用于弹窗
  • 我目前测试了本地广播和系统广播发现本地广播的context会报错,可能是因为进程号不统一导致的。

//强制下线的界面
public class LoginOutReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        AlertDialog.Builder builder=new AlertDialog.Builder(context);
        builder.setTitle("Error")
                .setMessage("您的账号在另外一台设备登录,程序即将回到登录界面!")
                .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        ActivityController.finishAllActivity();
                        Toast.makeText(context, "退出成功!", Toast.LENGTH_SHORT).show();
                        Intent intent1=new Intent();
                        intent1.setClass(context,LoginActivity.class);
                        context.startActivity(intent1);
                    }
                })
                ;
                builder.create().show();    }
}

(9)效果图

广播机制基础知识_第19张图片

1.3生命周期

1.3.1生命周期图

广播机制基础知识_第20张图片

1.3.2onReceive注意事项

onReceive方法中不能加入比较耗时的操作,否则系统会认为程序无响应,不要在广播里添加过多逻辑或者进行任何耗时操作,因为在广播中是不允许开辟线程的, 当onReceiver( )方法运行较长时间(超过10秒)还没有结束的话,那么程序会报错(ANR), 广播更多的时候扮演的是一个打开其他组件的角色,比如启动Service,Notification提示, Activity等。如果需要完成一项比较耗时的工作,可以通过发送Intent给Activity或Service,由Activity或Service来完成。

你可能感兴趣的:(安卓软件开发,android,Receiver,广播接收器,系统广播,本地广播)