Android开发4: Notification编程基础、Broadcast的使用及其静态注册、动态注册方式

前言

  啦啦啦~(博主每次开篇都要卖个萌,大家是不是都厌倦了呢~)

  本篇博文希望帮助大家掌握 Broadcast 编程基础,实现动态注册 Broadcast 和静态注册 Broadcast 的方式以及学会使用Notification。

基础知识

  BraodcastReceiver(广播接收器)是为了实现系统广播(Notification)而提供的一种组件, 它和事件处理机制类似,但是事件处理机制是程序组件级别的,而广播事件处理 机制是系统级别的。比如,我们可以发出一种广播来测试手机电量的变化,这时候就可以定义一个 BraodcastReceiver 来接受广播,当手机电量较低时提示用户。它是一个系统全局的监听器,用于监听系统全局的Broadcast消息,所以它可以很方便的进行系统组件之间的通信。

  BroadcastReceiver虽然是一个监听器,但是它和之前用到的OnXxxListener不同,那些只是程序级别的监听器,运行在指定程序的所在进程中,当程序退出的时候,OnXxxListener监听器也就随之关闭了,但是BroadcastReceiver属于系统级的监听器,它拥有自己的进程,只要存在与之匹配的Broadcast被以Intent的形式发送出来,BroadcastReceiver就会被激活。

  虽然同属Android的四大组件,BroadcastReceiver也有自己独立的声明周期,但是和Activity、Service又不同。当在系统注册一个BroadcastReceiver之后,每次系统以一个Intent的形式发布Broadcast的时候,系统都会创建与之对应的BroadcastReceiver广播接收者实例,并自动触发它的onReceive()方法,当onReceive()方法被执行完成之后,BroadcastReceiver的实例就会被销毁。虽然它独自享用一个单独的进程,但也不是没有限制的,如果BroadcastReceiver.onReceive()方法不能在10秒内执行完成,Android系统就会认为该BroadcastReceiver对象无响应,然后弹出ANR(Application No Response)对话框,所以不要在BroadcastReceiver.onReceive()方法内执行一些耗时的操作。

  如果需要根据广播内容完成一些耗时的操作,一般考虑通过Intent启动一个Service来完成该操作,而不应该在BroadcastReceiver中开启一个新线程完成耗时的操作,因为BroadcastReceiver本身的生命周期很短,可能出现的情况是子线程还没有结束,BroadcastReceiver就已经退出的情况,而如果BroadcastReceiver所在的进程结束了,该线程就会被标记为一个空线程,根据Android的内存管理策略,在系统内存紧张的时候,会按照优先级,结束优先级低的线程,而空线程无异是优先级最低的,这样就可能导致BroadcastReceiver启动的子线程不能执行完成。

  BroadcastReceiver本质上还是一个监听器,所以使用BroadcastReceiver的方法也是非常简单,只需要继承BroadcastReceiver,在其中重写onReceive(Context context,Intent intent)即可。一旦实现了BroadcastReceiver,并部署到系统中后,就可以在系统的任何位置,通过sendBroadcast、sendOrderedBroadcast方法发送Broadcast给这个BroadcastReceiver。

  我们既可以用 Intent 来启动一个组件,也可以用 sendBroadcast()方法发起一个系统级别的事件广播来传递消息。我们同样可以在自己的应用程序中实现 BroadcastReceiver 来监听和响应广播的Intent。在程序中使用 BraodcastReceiver 是比较简单的。首先要定义一个类继承 BraodcastReceiver,并且覆盖 onReceiver()方法来响应事件。然后注册在程序中 BraodcastReceiver。最后构建 Intent 对象调用 sendBroadcast()方法将广播发出。

  包含静态注册方式和动态注册方式:

1.静态广播部分

  静态广播部分首先需要后设置 setOnItemClickListener,利用 bundle 和 intent 将图片与文字内容发送出去。

  

  参考代码中的 STATICATION 为自己设定的广播名称。由于是静态注册所以需要在 AndroidMainfest.xml 中进行注册(右击New->other->Broadcast Receiver创建一个Receiver类后,AS将自动在AndroidMainfest.xml 中进行注册,我们只需要添加自己设定的广播名称):

  

  在静态广播类 StaticReceiver 中重写 onReceive 方法,当接收到对应广播时进行数据 处理,产生通知。 

  

2.动态注册部分

(1) 实现 BroadcastReceiver 子类(这里命名为 DynamicReceiver),并且重写 onReceive 方法,修改方法与静态广播类中类似。

 

(2)创建主页面,设置注册和注销按钮(set OnClickListener),使其能够注册或者注销广 播器。

 

 

注册关键代码:

 

注销关键代码: 

       其中 dynamicReceiver 为我们之前创建的 DynamicReceiver 类。用 registerReceiver 与 unregisterReceiver 分别对其进行注册与注销。

 

  (3)设置 Send 按钮点击事件,如果广播注册,则能够点击后发送广播。发送方法与静态注 册时一直,仅需修改广播名称即可。(使用 sendBroadcast(intent))

 

  (4)注意在 Android 主界面中将 launchMode 设置为 singleInstance,使得点击 Notification 后不会另外新建一个 MainActivity:

  

3.Notification 的使用

  Notification 可以提供持久的通知,位于手机最上层的状态通知栏中。用手指按下状 态栏,并从手机上方向下滑动,就可以打开状态栏查看提示消息。

  通知一般通过NotificationManager服务发送一个Notification对象来完成通知,NotificationManager是一个重要的系统级服务,该对象位于应用程序的框架层中,应用程序可以通过它向系统发送全局的通知。使用通知的时候,需要创建一个Notification对象用来承载通知的内容,但是一般不会直接通过Notification的构造方法来得到对象,而是使用它的内部类Notification.Builder来实例化一个Builder对象,并设置通知的各项属性,最后通过Notification.Builder.builder()方法得到一个Notification对象,当获得这个Notification对象之后,就可以使用NotificationManager.notify()方法发送通知。

 

  开发 Notification 主要 涉及以下 3 个类:

     (1)Notification.Builder:用于动态的设置 Notification 的一些属性。

  

  这里LargeIcon获取的参数通过以下方式来设定(获取图片资源的方式):

  

 

  虽然通知中提供了各种属性的设置,但是一个通知对象,有几个属性是必须要设置的,其他的属性均是可选的,必须设置的属性如下:

      • 小图标,使用setSamllIcon()方法设置。
      • 标题,使用setContentTitle()方法设置。
      • 文本内容,使用setContentText()方法设置。

    (2)NotificationManager:负责将 Notification 在状态显示出来和取消;

    

  NotificationManager类是一个通知管理器类,这个对象是由系统维护的服务,是以单例模式的方式获得,所以一般并不直接实例化这个对象。在Activity中,可以使用Activity.getSystemService(String)方法获取NotificationManager对象,Activity.getSystemService(String)方法可以通过Android系统级服务的句柄,返回对应的对象。在这里需要返回NotificationManager,所以直接传递Context.NOTIFICATION_SERVICE即可。

    (3)Notification:设置 Notification 的相关属性。

    

  (4)点击 notification,就可以跳转到我们 intent 中指定的 activity。主要使用到setContentIntent 与 PendingIntent。

关于 Notification,不同版本的 API 显示可能会有所不同。本次实验中必须实现的部 分是标题、大图标、内容、小图标。其中标题为静态广播或动态广播;大图标与广播发送的内容相关,为对应水果或者动态图 dynamic;内容为水果名称或动态广播发送的内容; 小图标与大图标内容一样。

       图片的使用方面请尽量使用 mipmap 目录下的 image asset。否则在某些 API 中可能会 出现 Icon 过大的情况。

 

实验内容

实现一个 Android 应用,实现静态广播、动态广播两种改变 Notification 内容的方法。 具体要求:

(1)该界面为应用启动后看到的界面。

(2)点击静态注册按钮,跳转至如下界面。

点击表单项目。如 orange。会有对应通知产生,点击通知返回主界面:

 

(3)点击动态注册按钮,跳转至如下界面。

            

             (未注册广播)                       (已注册广播)                  (已注册后点击发送)

实现以下功能:

a)可以编辑广播的信息,点击 Send 按钮发送广播。 b)设置一个按钮进行广播接收器的注册与注销。 c)广播接收器若已被注册,发送出的广播信息会产生一个对应通知。 d)点击 Notification 可以跳转回主界面。

 注:在设置按钮内容的时候注意大小写问题(使用android:textAllCaps="false"属性)。

 

实验步骤

1.XML 布局

  本实验初始界面的 XML 布局是比较简单的,包括初始界面的两个按钮、静态 注册布局中的 ListView 及其每一栏中的 ImageView TextView、动态布局中的 EditText Button。

 

2.静态注册部分

  在一个 xml 布局文件中写好一个 ListView(这样就创建了一个空的列表, 然后在.java 文件中再填充数据),在每一栏中的 ImageView TextView 使用线 性结构,分别用来存放图片背景及联系人的姓名。这里和第三次实验类似。  在 MainActivity.java 文件中,我们获取到初始界面的两个按钮并使用 Intent 设置相应的点击跳转事件。

  在 StaticActivity.java 文件中, 我们需要获取到静态注册布局中的 ListView 并使用 Adapter 为这个 ListView 填充数据。我们需要获取图片和名称 两个参数,与实验三不同的是每一栏中的图片背景都不同。首先,我们创建一个Fruit 类,来存储名称数据,初始化一些变量,并使用 get 和 Set 方法来获得或 者更改相应的数据。

    接下来在 StaticActivity.java 文件中为每一项名称数据创建一个对象,并 添加在 List 中,方便之后传递数据的操作。这里我们通过获取图片命名将图片 素材绑定在一个 int 类型的数组中: 

    

    首先构建好数据,把数据以 Map 键值对的形式添加到 list 中。然后创建Listview 的 Adapter。 

     

    在 ListView 单击事件中,利用 bundle 和 intent 将图片与文字内容发送出去:

    

  创建一个 Broadcast Receiver 类,并在 AndroidMainfest.xml 中进行注册。 然后在静态广播类 StaticReceiver 中重写 onReceive 方法,当接收到对应广 播时进行数据 处理,产生通知。

  这里我们获取到点击产生的图片和文字信息,并使用 Notification(可以 提供持久的通知,位于手机最上层的状态通知栏中。用手指按下状态栏,并从手 机上方向下滑动,就可以打开状态栏查看提示消息)。设置好 Notification 涉 及的 3 个类: Notification.Builder(用于动态的设置)、 NotificationManager (负责将 Notification 在状态显示出来和取消)、Notification(设置 Notification 的相关属性),然后使用 setContentIntent 与 PendingIntent 使得点击 notification,就可以跳转到我们 intent 中指定的 activity: 

  

3.动态注册部分:

  在这一部分中有两个按钮,Register Broadcast 按钮再按下后将会变换按 钮文本并实现一些按钮事件,并且再次按下时的事件也会有所改变。受实验三中 星星切换的启发,这里我们可以使用 tag,每个 View 都可以设置 tag,通过 tag 可以用来判断该 View 现在的状态。在初始化的时候,将 tag 设置为 0。然后在 相应位置完成注册和注销按钮功能的设置:

  

  在 send 按钮事件中,也要注意此时 Register Broadcast 按钮的状态问题, 所以也需要使用 tag 来进行判断(设置 Send 按钮点击事件,如果广播注册,则 能够点击后发送广播。发送方法与静态注册时一致,仅需修改广播名称即可。在 这里我们也需获取到 EditText 中获取到的文本,作为传递的参数之一):

相应的,创建一个 Broadcast Receiver 类,并在 AndroidMainfest.xml 中进行 注册。然后在动态广播类中重写 onReceive 方法,当接收到对应广播时进行数 据处理,产生通知。

完成实验~

此外:

  在 Android 主界面中将 launchMode 设置为 singleInstance,使得点击 Notification 后不会另外新建一个 MainActivity;

  为了防止缩图在 mipmap 里新建 image assert,将所有使用的图片都进行创 建。

 

实验关键代码

StaticActivity.java

package com.example.yanglh6.myapplication4;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.SimpleAdapter;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by lenovo on 2016/10/21.
 */

public class StaticActivity extends AppCompatActivity {

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

        final int[] stu_pic = {
                R.mipmap.apple,
                R.mipmap.banana,
                R.mipmap.cherry,
                R.mipmap.coco,
                R.mipmap.kiwi,
                R.mipmap.orange,
                R.mipmap.pear,
                R.mipmap.strawberry,
                R.mipmap.watermelon};

        final List<Map<String, Object>> data = new ArrayList<>();
        /*生成动态数组,加入数据*/
        final List<Fruit> ItemName = new ArrayList<Fruit>();
        ItemName.add(new Fruit("Apple"));
        ItemName.add(new Fruit("Banana"));
        ItemName.add(new Fruit("Cherry"));
        ItemName.add(new Fruit("Coco"));
        ItemName.add(new Fruit("Kiwi"));
        ItemName.add(new Fruit("Orange"));
        ItemName.add(new Fruit("Pear"));
        ItemName.add(new Fruit("Strawberry"));
        ItemName.add(new Fruit("Watermelon"));

        final String[] name = new String[ItemName.size()];
        for (int i = 0; i < ItemName.size(); i++) {
            String x = ItemName.get(i).getItemName();
            name[i] = x;
        }
        for (int i = 0; i < ItemName.size(); i++) {
            Map<String, Object> temp = new LinkedHashMap<>();
            temp.put("ItemImage", stu_pic[i]);
            temp.put("name", name[i]);
            data.add(temp);
        }

        ListView listView = (ListView) findViewById(R.id.staticStart);
        final SimpleAdapter simpleAdapter = new SimpleAdapter(this, data, R.layout.item,
                new String[]{"ItemImage", "name"}, new int[]{R.id.ItemImage, R.id.ItemName});
        listView.setAdapter(simpleAdapter);

        /*  ListView单击事件  */
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                Intent intent = new Intent("com.example.yanglh6.myapplication4.staticreceiver");
                Fruit temp = ItemName.get(i);
                intent.putExtra("ItemImage", stu_pic[i]);
                intent.putExtra("name", name[i]);
                sendBroadcast(intent);
            }
        });

    }
}

 

StaticReciver.java

package com.example.yanglh6.myapplication4;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;

public class StaticReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals("com.example.yanglh6.myapplication4.staticreceiver")) {
            Bundle bundle = intent.getExtras();
            Bitmap bitmap=BitmapFactory.decodeResource(context.getResources(),bundle.getInt("ItemImage"));
            int imageId = (int) bundle.get("ItemImage");

            NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
            Notification.Builder builder = new Notification.Builder(context);
            builder.setContentTitle("静态广播")
                    .setContentText(bundle.getString("name"))
                    .setLargeIcon(bitmap)
                    .setSmallIcon(imageId)
                    .setTicker("您有一条新消息")
                    .setAutoCancel(true);
            Intent Intent1 = new Intent(context, MainActivity.class);

            PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, Intent1, 0);
            builder.setContentIntent(pendingIntent);
            Notification notify = builder.build();
            notificationManager.notify(0, notify);
        }


    }
}

 

DynamicActivity.java

package com.example.yanglh6.myapplication4;

import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

/**
 * Created by lenovo on 2016/10/21.
 */

public class DynamicActivity extends AppCompatActivity {
    private boolean tag = false;
    private DynamicReceiver dynamicReceiver = null;

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

        final Button buttonRegister = (Button) findViewById(R.id.register);
        Button buttonSend = (Button) findViewById(R.id.send);

        buttonRegister.setOnClickListener(new Button.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (!tag) {
                    buttonRegister.setText("Unregister Broadcast");
                    tag = true;
                    dynamicReceiver = new DynamicReceiver();
                    IntentFilter dynamic_filter = new IntentFilter();
                    dynamic_filter.addAction("com.example.yanglh6.myapplication4.dynamicreceiver");
                    registerReceiver(dynamicReceiver, dynamic_filter);
                } else {
                    buttonRegister.setText("Register Broadcast");
                    tag = false;
                    unregisterReceiver(dynamicReceiver);
                }
            }
        });

        buttonSend.setOnClickListener(new Button.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (!tag) {

                } else {
                    Intent intent = new Intent("com.example.yanglh6.myapplication4.dynamicreceiver");
                    String str1 = "";
                    EditText editText = (EditText) findViewById(R.id.editText);
                    str1 = editText.getText().toString();
                    intent.putExtra("ItemImage", R.mipmap.dynamic);
                    intent.putExtra("name", str1);
                    sendBroadcast(intent);
                }
            }
        });
    }
}

 

DynamicReceiver.java

package com.example.yanglh6.myapplication4;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;

public class DynamicReceiver extends BroadcastReceiver {
    public DynamicReceiver() {
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals("com.example.yanglh6.myapplication4.dynamicreceiver")) {
            Bundle bundle = intent.getExtras();
            Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), bundle.getInt("ItemImage"));
            int imageId = (int) bundle.get("ItemImage");

            NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
            Notification.Builder builder = new Notification.Builder(context);
            builder.setContentTitle("动态广播")
                    .setContentText(bundle.getString("name"))
                    .setLargeIcon(bitmap)
                    .setSmallIcon(imageId)
                    .setTicker("您有一条新消息")
                    .setAutoCancel(true);
            Intent mIntent = new Intent(context, MainActivity.class);

            PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, mIntent, 0);
            builder.setContentIntent(pendingIntent);
            Notification notify = builder.build();
            notificationManager.notify(0, notify);
        }
    }
}

 

实验截图

  

  

  

  

 

总结

  BraodcastReceiver(广播接收器)是为了实现系统广播而提供的一种组件, 它和事件处理机制类似,但是事件处理机制是程序组件级别的,而广播事件处理 机制是系统级别的。比如,我们可以发出一种广播来测试手机电量的变化,这时 候就可以定义一个 BraodcastReceiver 来接受广播,当手机电量较低时提示用 户。我们既可以用 Intent 来启动一个组件,也可以用 sendBroadcast()方法发 起一个系统级别的事件广播来传递消息。我们同样可以在自己的应用程序中实现 BroadcastReceiver 来监听和响应广播的 Intent。

  在程序中使用 BraodcastReceiver 是比较简单的。首先要定义一个类继承 BraodcastReceiver,并且覆盖 onReceiver()方法来响应事件。然后注册在程序 中 BraodcastReceiver。最后构建 Intent 对象调用 sendBroadcast()方法将广播 发出。

    包含静态注册方式和动态注册方式:

  (1)静态注册方式

      静态注册方式是在 AndroidManifest.xml 的 application 里面定义 receiver 并设置要接收的 action。静态注册方式的特点:不管改应用程序是否 处于活动状态,都会进行监听,比如某个程序时监听内存的使用情况的,当 在手机上安装好后,不管改应用程序是处于什么状态,都会执行改监听方法中的内容。

  (2)动态注册方式

  在 activity 里面调用函数来注册,和静态的内容差不多。一个形参是 receiver, 另一个是 IntentFilter,其中里面是要接收的 action。动态注册方式特点:在 代码中进行注册后,当应用程序关闭后,就不再进行监听。当然,也可手工调用unregisterReceiver()进行销毁。

  (3)BroadcastReceiver 的生命周期

  一个BroadcastReceiver 对象只有在被调用onReceive(Context, Intent)的才有效的,当从该函数返回后,该对象就无效的了,结束生命周期。

  (4)Notification

  Notification 可以提供持久的通知,位于手机最上层的状态通知栏中。用手指按下状 态栏,并从手机上方向下滑动,就可以打开状态栏查看提示消息。

  通知一般通过NotificationManager服务发送一个Notification对象来完成通知,NotificationManager是一个重要的系统级服务,该对象位于应用程序的框架层中,应用程序可以通过它向系统发送全局的通知。使用通知的时候,需要创建一个Notification对象用来承载通知的内容,但是一般不会直接通过Notification的构造方法来得到对象,而是使用它的内部类Notification.Builder来实例化一个Builder对象,并设置通知的各项属性,最后通过Notification.Builder.builder()方法得到一个Notification对象,当获得这个Notification对象之后,就可以使用NotificationManager.notify()方法发送通知。

 

源码下载

源码下载点击这里~

1、本实验实验环境:

操作系统 Windows 10 

实验软件 Android Studio 2.2.1

虚拟设备:Nexus_6

API:19(其他高版本的API可能会将提示图表显示成一个白圆点,API19测试可正常显示)

2、贴代码的时候由于插入代码框的大小问题,代码格式不太严整,望见谅~

posted @ 2016-11-01 17:50  yanglh6  阅读(1664)  评论(0编辑  收藏  举报