zoukankan      html  css  js  c++  java
  • Broadcast 使用详解

    极力推荐文章:欢迎收藏
    Android 干货分享

    阅读五分钟,每日十点,和您一起终身学习,这里是程序员Android

    本篇文章主要介绍 Android 开发中的部分知识点,通过阅读本篇文章,您将收获以下内容:

    1. 广播的生命周期
    2. 四大组件之一,必须在Androidmainfest.xml中注册
    3. 广播的注册(静态广播、动态广播)
    4. 广播的发送(正常、有序、持续)
    5. 广播接收(系统广播、自定义广播)

    Broadcast Android 四大组件之一,是一种广泛运用在应用程序之间异步传输信息的机制。
    Broadcast 本质上是一个Intent 对象,差别在于Broadcast可以被多个 BroadcastReceiver 处理。BroadcastReceiver 是一个全局监听器,通过它的 onReceive() 可以过滤用户想要的广播,进而进行其它操作。

    1. BroadcastReceiver简介

    BroadcastReceiver继承关系

    BroadcastReceiver 默认是在主线程中执行,如果onReceiver() 方法处理事件超过10s,则应用将会发生ANR(Application Not Responding),此时,如果建立工作线程并不能解决此问题,因此建议:如处理耗时操作,请用 Service 代替。

    BroadcastReceiver继承关系 如下:

    java.lang.Object
       ↳    android.content.BroadcastReceiver
    

    BroadcastReceiver的主要声明周期方法onReceiver(),此方法执行完之后,BroadcastReceiver 实例将被销毁。

    2.四大组件之一,必须在Androidmainfest.xml中注册

            <receiver
                android:name="ReceiverMethod"
                android:enabled="true"
                android:exported="true">
                <intent-filter>
                    <action android:name="String....." />
                </intent-filter>
            </receiver>
    

    注意:
    如不注册,将导致无法接收处理广播消息

    3.广播的注册(静态注册、动态注册)

    广播的注册分两种,一种在ndroidMfest.xml中静态注册,另一种是在Java代码中动态注册。

    1.静态注册

    一些系统发送的广播需要在Androidmainfest.xml 中静态注册,例如 开机广播,apk状态改变广播,电量状态改变广播等。这些静态注册的广播,通常在Androidmainfest.xml 中拦截特定的字符串。

    静态注册广播的方法如下:

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.programandroid"
        android:versionCode="1"
        android:versionName="1.0" >
             ... ...
            <receiver
                android:name="com.programandroid.BroadcastReceiver.NotificationReceived"
                android:enabled="true"
                android:exported="true" >
                <intent-filter>
                    <action android:name="Notification_cancel" />
                    <action android:name="Notification_music_pre" />
                    <action android:name="Notification_music_play" />
                    <action android:name="Notification_music_next" />
                </intent-filter>
            </receiver>
            ... ...
    

    1.静态注册开机广播方法

    开机广播比较特殊,需要在Androidmainfest.xml 中添加权限。否则,无法获取开机广播。

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
    
    //静态注册广播的方法
            <receiver
                android:name=".component.BroadcastReceiver.BootReceiverMethod"
                android:enabled="true"
                android:exported="true">
                <intent-filter>
                    <action android:name="android.intent.action.BOOT_COMPLETED" />
                </intent-filter>
            </receiver>
    

    2.动态注册广播

    在Java中动态注册广播,通常格式如下:

      //动态注册广播
      registerReceiver(BroadcastReceiver, IntentFilter);
    

    动态注册 监听灭屏、点亮屏幕的广播

    在广播中动态注册广播请注意一定要使用context.getApplicationContext(),防止context 为空 ,引起空指针异常。

    public class ScreenOnOffReceiver {
    
        public static void ReceiverScreenOnOff(Context context) {
            IntentFilter screenOffFilter = new IntentFilter();
            screenOffFilter.addAction(Intent.ACTION_SCREEN_OFF);
            screenOffFilter.addAction(Intent.ACTION_SCREEN_ON);
            BroadcastReceiver mScreenOnOffReceiver = new BroadcastReceiver() {
    
                @Override
                public void onReceive(Context context, Intent intent) {
                    // TODO Auto-generated method stub
                    String action = intent.getAction();
                    if (action.equals(Intent.ACTION_SCREEN_OFF)) {
    
                        Toast.makeText(context, "接收屏幕熄灭广播", Toast.LENGTH_SHORT).show();
    
                    }
                    if (action.equals(Intent.ACTION_SCREEN_ON)) {
    
                        Toast.makeText(context, "接收屏幕点亮广播", Toast.LENGTH_SHORT).show();
                    }
    
    
                }
    
            };
            /**
             * context.getApplicationContext()
             * 在广播中注册广播时候需要注意,防止context 为空 ,引起空指针异常
             * **/
    // 2.动态注册广播的方法
            context.registerReceiver(mScreenOnOffReceiver, screenOffFilter);
    
        }
    }
    

    4.广播的发送(无序、有序、持续)

    1.发送无序广播的方法

    发送无序广播在Android 中很常见,是一种一对多的关系,主要通过 sendBroadcast(intent);发送广播。

    发送自定义广播案例

    广播说白了就是一个带Action等字符串标记的Intent。发送自定义广播举例如下:

            Intent customIntent=new Intent();
            customIntent.setAction("SendCustomBroadcast");
            sendBroadcast(customIntent);
    

    接收自定义广播的方法

    当用户对某些广播感兴趣的话,此时可以获取此广播,然后在onReceive方法中处理接收广播的一下操作。

    
    public class CustomBroadcast extends BroadcastReceiver {
        public CustomBroadcast() {
        }
    
        @Override
        public void onReceive(Context context, Intent intent) {
    
            if (intent.getAction().equals("SendCustomBroadcast")){
                Toast.makeText(context,"自定义广播接收成功:Action:SendCustomBroadcast",Toast.LENGTH_SHORT).show();
            }
        }
    }
    

    注意自定义广播是在Androidmanfest.xml 中静态注册的。

    2.发送有序广播

    广播在Android是有优先级的,优先级高的广播可以终止或修改广播内容。发送有序广播的方法如下sendOrderedBroadcast(intent,"str_receiver_permission");

    例如:发送自定义有序广播

            Intent customOrderIntent=new Intent();
            customOrderIntent.setAction("SendCustomOrderBroadcast");
            customOrderIntent.putExtra("str_order_broadcast","老板说:公司每人发 10 个 月饼");
            sendOrderedBroadcast(customOrderIntent,"android.permission.ORDERBROADCAST");
    

    广播属于四大组件,一定要在AndroidMainfest.xml 中注册。

    有序广播静态注册

    接收有序广播的静态注册方法如下:

           <receiver
                android:name=".component.BroadcastReceiver.CustomHightBrodcast"
                android:enabled="true"
                android:exported="true"
               >
                <intent-filter android:priority="1000">
                    <action android:name="SendCustomOrderBroadcast" />
                </intent-filter>
            </receiver>
    
            <receiver
                android:name=".component.BroadcastReceiver.CustomMiddleBroadcast"
                android:enabled="true"
                android:exported="true"
              >
                <intent-filter android:priority="500">
                    <action android:name="SendCustomOrderBroadcast" />
                </intent-filter>
            </receiver>
            <receiver
                android:name=".component.BroadcastReceiver.CustomLowerBroadcast"
                android:enabled="true"
                android:exported="true"
               >
                <intent-filter android:priority="100">
                    <action android:name="SendCustomOrderBroadcast" />
                </intent-filter>
            </receiver>
    
      1. 有序广播,高优先级广播可以优先处理
    public class CustomHightBrodcast extends BroadcastReceiver {
        public CustomHightBrodcast() {
        }
    
        @Override
        public void onReceive(Context context, Intent intent) {
    
            if (intent.getAction().equals("SendCustomOrderBroadcast")) {
                Toast.makeText(context, intent.getStringExtra("str_order_broadcast"), Toast.LENGTH_SHORT).show();
                Bundle bundle=new Bundle();
                bundle.putString("str_order_broadcast","经理说:公司每人发 5 个 月饼");
    //            修改广播传输数据
                setResultExtras(bundle);
            }
        }
    }
    
      1. 中优先级的广播后序处理
    public class CustomMiddleBroadcast extends BroadcastReceiver {
        public CustomMiddleBroadcast() {
        }
        @Override
        public void onReceive(Context context, Intent intent) {
    
            if (intent.getAction().equals("SendCustomOrderBroadcast")) {
                Toast.makeText(context, getResultExtras(true).getString("str_order_broadcast"), Toast.LENGTH_SHORT).show();
                Bundle bundle=new Bundle();
                bundle.putString("str_order_broadcast","主管说:公司每人发 2 个 月饼");
                setResultExtras(bundle);
            }
        }
    }
    
      1. 低优先级广播最后处理
    public class CustomLowerBroadcast extends BroadcastReceiver {
        public CustomLowerBroadcast() {
        }
    
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals("SendCustomOrderBroadcast")) {
                String notice= getResultExtras(true).getString("str_order_broadcast");
                Toast.makeText(context,notice, Toast.LENGTH_SHORT).show();
    //          终止广播继续传播下去
                abortBroadcast();
            }
        }
    }
    

    注意 :
    有序广播需要声明并使用权限

    • 1.声明使用权限
     <!-- 申请使用自定义 有序广播的权限 -->
     <uses-permission >   android:name="android.permission.ORDERBROADCAST" />
    
    • 2.声明权限
     <!-- 自定义 有序广播的权限 -->
     <permission>
    android:name="android.permission.ORDERBROADCAST"/>
    

    在有序广播中高优先级的广播接收广播,可以修改数据,然后传给低优先级的广播。

    3.发送持续广播(已经被弃用)

    粘性广播会在Android 系统中一直存在,不过随着 Android系统的不断更新,此方法逐渐被抛弃,使用方法如下:sendStickyBroadcast(intent);

    5.广播接收(系统广播、自定义广播)

    当广播发出后,如何接收广播呢,下面将介绍接收广播的方法。
    接受广播类 主要继承 BroadcastReceiver,然后在onReceive方法,过滤广播Action中携带的Intent,然后进行相关处理。

    接收开机广播的方法

    1. 实现BootReceiverMethod 继承 BroadcastReceiver

    p ublic class BootReceiverMethod extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            //   接收开机广播处理事情,比如启动服务
            Intent mStartIntent = new Intent(context, StartServiceMethods.class);
            context.startService(mStartIntent);
        }
    }
    

    2.在Androidmainfest.xml 声明组件信息,并过滤开机完成 Action

            <receiver
                android:name=".component.BroadcastReceiver.BootReceiverMethod"
                android:enabled="true"
                android:exported="true">
                <intent-filter>
                    <action android:name="android.intent.action.BOOT_COMPLETED" />
                </intent-filter>
            </receiver>
    

    3.声明接收开机广播完成的权限

       <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    

    极力推荐文章:欢迎收藏
    Android 干货分享

    阅读五分钟,每日十点,和您一起终身学习,这里是程序员Android

    本篇文章主要介绍 Android 开发中的部分知识点,通过阅读本篇文章,您将收获以下内容:

    1. Service 简介
    2. 四大组件之一,必须在Androidmainfest.xml 中注册
    3. 启动模式启动服务
    4. 绑定模式绑定服务
    5. 前台服务
    6. AIDL远程服务

    ServiceAndroid四大组件之一(Activity 活动,Service 服务,ContentProvider 内容提供者,BroadcastReceiver 广播),与Activity相比,Activity 是运行在前台,用户可以看得见,Service 则是运行在后台,无用户界面,用户无法看到。

    Service主要用于组件之间交互(例如:与ActivityContentProviderBroadcastReceiver进行交互)、后台执行耗时操作等(例如下载文件,播放音乐等,但Service在主线程运行时长不能超过20s,否则会出现ANR,耗时操作一般建议在子线程中进行操作)。

    1.Service 简介

    在了解Service 的生命周期的之前,我们先了解一下Service 的继承关系,方便我们更好的了解Service

    Service 继承关系如下:

    java.lang.Object
       ↳	android.content.Context
     	   ↳	android.content.ContextWrapper
     	 	   ↳	android.app.Service
    

    Service 的两种启动模式

    Service 有两种不同的启动模式 ,不同的启动模式对应不同生命周期.
    Service 启动模式主要分两种: 1. 启动模式。 2. 绑定模式。

    1.启动模式

    此模式通过 startService()方法启动,此服务可以在后台一直运行,不会随启动组件的消亡而消亡。只能执行单一操作,无法返回结果给调用方,常用于网络下载、上传文件,播放音乐等。

    2.绑定模式

    此模式 通过绑定组件(Activity等)调用 bindService() 启动,此服务随绑定组件的消亡而解除绑定。

    如果此时没有其它通过startService()启动,则此服务会随绑定组件的消亡而消亡。
    多个组件不仅可以同时绑定一个Service,而且可以通过进程间通信(IPC)执行跨进程操作等。

    3.两种服务可以同时运行

    启动模式与绑定模式的服务可以同时运行,在销毁服务时,只有两种模式都不在使用Service时候,才可以销毁服务,否则会引起异常。

    4. 两种 Service 模式的生命周期

    两种 Service 模式的生命周期如下:

    两种 Service 模式生命周期图

    2.四大组件之一,必须在Androidmainfest.xml 中注册

    Service 注册方法如下:

    <manifest ... >
      ...
      <application ... >
          <service android:name=".ServiceMethods" />
          ...
      </application>
    </manifest>
    

    注意:

    Service 如不注册 ,不会像Activity 那样会导致App CrashService 不注册 不会报异常信息,但是服务会起不来,如不注意很容易迷惑。

    3.启动模式

    通过启动模式启动的Service ,如不主动关闭,Service会一直在。

    启动模式启动服务的方法

            Intent  mBindIntent = new Intent(ServiceMethods.this, BindServiceMethods.class);
            startService(mStartIntent);
    

    启动模式启动服务的生命周期

    下面是验证启动模式启动服务的生命周期的方法,详细生命周期请查看上方Service的生命周期图。

    01-03 17:16:36.147 23789-23789/com.android.program.programandroid I/StartService wjwj:: ----onCreate----
    01-03 17:16:36.223 23789-23789/com.android.program.programandroid I/StartService wjwj:: ----onStartCommand----
    01-03 17:16:38.174 23789-23789/com.android.program.programandroid I/StartService wjwj:: ----onDestroy----
    

    启动模式 启动服务案例

    此案例功能:启动服务,在服务中创建通知

    	// Service 创建方法
    	@Override
    	public void onCreate() {
    		super.onCreate();
    		Log.i(TAG, "----onCreate----");
    	}
    	// Service  启动方法
    	@Override
    	public int onStartCommand(Intent intent, int flags, int startId) {
    		Log.i(TAG, "----onStartCommand----");
    		// 获取NotificationManager实例
    		notifyManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    		// 实例化NotificationCompat.Builder并设置相关属性
    		NotificationCompat.Builder builder = new NotificationCompat.Builder(
    				this)
    		// 设置小图标
    				.setSmallIcon(R.drawable.ic_launcher)
    				.setLargeIcon(
    						BitmapFactory.decodeResource(getResources(),
    								R.drawable.ic_launcher))
    				// 设置通知标题
    				.setContentTitle("我是通过StartService服务启动的通知")
    				// 设置通知不能自动取消
    				.setAutoCancel(false).setOngoing(true)
    				// 设置通知时间,默认为系统发出通知的时间,通常不用设置
    				// .setWhen(System.currentTimeMillis())
    				// 设置通知内容
    				.setContentText("请使用StopService 方法停止服务");
    
    		// 通过builder.build()方法生成Notification对象,并发送通知,id=1
    		notifyManager.notify(1, builder.build());
    
    		return super.onStartCommand(intent, flags, startId);
    	}
    	// Service  销毁方法
    	@Override
    	public void onDestroy() {
    		Log.i(TAG, "----onDestroy----");
    		notifyManager.cancelAll();
    		super.onDestroy();
    	}
    

    4. 绑定模式启动绑定服务

    绑定模式启动的服务会随着绑定逐渐的消亡而解除Service绑定,如果此时Service没有通过启动模式启动,则此服务将会被销毁。

    绑定模式启动绑定服务的方法

    绑定模式,是通过其他组件启动的Service

    启动绑定模式服务的方法

    	// 启动绑定服务处理方法
    	public void BtnStartBindService(View view) {
    		// 启动绑定服务处理方法
    		bindService(mBindIntent, serviceConnection, Context.BIND_AUTO_CREATE);
    		isBindService = true;
    		Toast.makeText(ServiceMethod.this, "启动 " + mBindCount + " 次绑定服务",
    				Toast.LENGTH_SHORT).show();
    	}
    
    	
    	public void BtnSopBindService(View view) {
    		if (isBindService) {
    			// 解除绑定服务处理方法
    			unbindService(serviceConnection);
    			Toast.makeText(ServiceMethod.this, "解除 " + mUnBindCount + " 次绑定服务",
    					Toast.LENGTH_SHORT).show();
    			isBindService = false;
    		}
    
    	}
    

    绑定服务 随绑定组件的消亡而消亡

    绑定模式 生命周期回调代码如下:

    	// Service 创建方法
    	@Override
    	public void onCreate() {
    		super.onCreate();
    		Log.i(TAG, "----onCreate----");
    	}
    
    	// Service 绑定方法
    	@Override
    	public IBinder onBind(Intent intent) {
    		Log.i(TAG, "----onBind----");
    
    		MyBinder myBinder = new MyBinder();
    		return myBinder;
    	}
    
    	// Service 解除绑定方法
    	@Override
    	public boolean onUnbind(Intent intent) {
    
    		Log.i(TAG, "----onUnbind----");
    		return super.onUnbind(intent);
    
    	}
    
    	// Service 销毁方法
    	@Override
    	public void onDestroy() {
    		Log.i(TAG, "----onDestroy----");
    		super.onDestroy();
    	}
    

    绑定服务的生命周期代码打印Log信息如下:

    01-03 20:32:59.422 13306-13306/com.android.program.programandroid I/BindService wjwj:: ----onCreate----
    01-03 20:32:59.423 13306-13306/com.android.program.programandroid I/BindService wjwj:: -----onBind-----
    01-03 20:33:09.265 13306-13306/com.android.program.programandroid I/BindService wjwj:: ----onUnbind----
    01-03 20:33:09.266 13306-13306/com.android.program.programandroid I/BindService wjwj:: ----onDestroy----
    

    绑定服务案例

    功能:获取绑定模式启动 绑定服务及解除绑定服务的次数

    绑定服务类

    package com.android.program.programandroid.component.Service;
    
    import android.app.Service;
    import android.content.Intent;
    import android.os.IBinder;
    import android.util.Log;
    
    public class BindServiceMethods extends Service {
        private static final String TAG = "BindService wjwj:";
    
        public BindServiceMethods() {
        }
    
        @Override
        public void onCreate() {
            super.onCreate();
            Log.i(TAG, "----onCreate----");
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            Log.i(TAG, "----onBind----");
    
            MyBinder myBinder = new MyBinder();
            return myBinder;
        }
    
    
        @Override
        public boolean onUnbind(Intent intent) {
    
            Log.i(TAG, "----onUnbind----");
            return super.onUnbind(intent);
    
        }
    
        @Override
        public void onDestroy() {
            Log.i(TAG, "----onDestroy----");
            super.onDestroy();
        }
    }
    
    • 组件与绑定服务类之间的交互
     //    启动绑定服务处理方法
        public void BtnStartBindService(View view) {
    
            bindService(mBindIntent, serviceConnection, Context.BIND_AUTO_CREATE);
            isBindService = true;
            Toast.makeText(ServiceMethods.this,"启动 "+mBindCount+" 次绑定服务",Toast.LENGTH_SHORT).show();
        }
    
        //    解除绑定服务处理方法
        public void BtnSopBindService(View view) {
            if (isBindService) {
                unbindService(serviceConnection);
                Toast.makeText(ServiceMethods.this,"解除 "+mUnBindCount+" 次绑定服务",Toast.LENGTH_SHORT).show();
                isBindService=false;
            }
    
        }
    
    • 组件之间交互所需的 Binder 接口类
    /**
    * 该类提供 绑定组件与绑定服务提供接口
    * */
    public class MyBinder extends Binder {
       private int count = 0;
    
        public int getBindCount() {
            return ++count;
        }
        public int getUnBindCount() {
            return count> 0 ? count-- : 0;
        }
    }
    

    5. 提高服务的优先级

    服务默认启动方式是后台服务,但是可以通过设置服务为前台服务,提高服务的优先级,进而避免手机内存紧张时,服务进程被杀掉。

    设置前台服务的两种方法

    1.设置为前台服务

    //设置为前台服务
    startForeground(int, Notification)
    

    2.取消前台服务

    //取消为前台服务
    stopForeground(true);
    

    startForeground 前台服务案例

    功能:前台服务绑定通知信息,提高服务进程优先级,否则取消通知信息

    package com.android.program.programandroid.component.Service;
    
    import android.app.NotificationManager;
    import android.app.Service;
    import android.content.Intent;
    import android.graphics.BitmapFactory;
    import android.os.IBinder;
    import android.support.v4.app.NotificationCompat;
    
    import com.android.program.programandroid.R;
    
    public class MyStartForcegroundService extends Service {
    
        public MyStartForcegroundService() {
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            // TODO: Return the communication channel to the service.
            throw new UnsupportedOperationException("Not yet implemented");
        }
    
        @Override
        public void onCreate() {
            super.onCreate();
        }
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
    
            if (intent.getAction().equals("start_forceground_service")) {
    
    //        获取NotificationManager实例
                NotificationManager notifyManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    //        实例化NotificationCompat.Builder并设置相关属性
                NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
    //                设置小图标
                        .setSmallIcon(R.mipmap.ic_launcher)
                        .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
    //                设置通知标题
                        .setContentTitle("我是通过startForeground 启动前台服务通知")
    //                设置通知不能自动取消
                        .setAutoCancel(false)
                        .setOngoing(true)
    //                设置通知时间,默认为系统发出通知的时间,通常不用设置
    //                .setWhen(System.currentTimeMillis())
    //               设置通知内容
                        .setContentText("请使用stopForeground 方法改为后台服务");
    
                //通过builder.build()方法生成Notification对象,并发送通知,id=1
    //        设置为前台服务
                startForeground(1, builder.build());
    
            } else if (intent.getAction().equals("stop_forceground_service")) {
                
                stopForeground(true);
            }
    
            return super.onStartCommand(intent, flags, startId);
        }
    }
    
    

    6. 使用AIDL接口实现远程绑定

    由于内容较多,后续另开一篇详细介绍。

    至此,本篇已结束,如有不对的地方,欢迎您的建议与指正。同时期待您的关注,感谢您的阅读,谢谢!

    微信关注公众号:  程序员Android,领福利

  • 相关阅读:
    css3学习笔记之效果
    css3学习笔记之2D转换
    css3学习笔记之文本效果
    css3学习笔记之渐变
    css3学习笔记之背景
    css3学习笔记之边框
    AngularJs 入门参考代码
    环形矩阵
    Qt编写串口通信程序全程图文讲解[转]
    Qt操作Sqlite数据库
  • 原文地址:https://www.cnblogs.com/wangjie1990/p/11310608.html
Copyright © 2011-2022 走看看