精华内容
下载资源
问答
  • BroadcastReceiver详解

    2021-04-01 11:48:30
    BroadcastReceiver详解 使用方式 在Manifest.xml文件中注册 <receiver android:name=".test.broadcast.MyTestReceiver" android:enabled="true" android:exported="true"> <intent-filter> <...

    BroadcastReceiver详解

    使用方式

    在Manifest.xml文件中注册

    <receiver
      android:name=".test.broadcast.MyTestReceiver"
      android:enabled="true"
      android:exported="true">
      <intent-filter>
          <action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
          <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
          <action android:name="android.intent.action.SCREEN_ON" />
          <action android:name="android.intent.action.SCREEN_OFF" />
          <action android:name="android.intent.action.INPUT_METHOD_CHANGED" />
          <action android:name="android.intent.action.CONFIGURATION_CHANGED" />
          <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
          <action android:name="android.intent.action.USER_PRESENT" />
      </intent-filter>
    </receiver>
    

    在Manifesh.xml文件中注册自定义的BroadcastReceiver,当意图过滤器中的动作发生时,会回调BroadcastReceiver中的onRecevie方法.

    public class MyTestReceiver extends BroadcastReceiver {
        private static final String TAG = "MyTestReceiver";
    
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent != null) {
                String action = intent.getAction();
                Log.d(TAG, "onReceive: receive action is " + action);
            }
        }
    }
    

    代码中动态注册

    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
    
    registerReceiver(new BroadcastReceiver() {
      @Override
      public void onReceive(Context context, Intent intent) {
          String action = intent == null ? "" : intent.getAction();
          if (Intent.ACTION_SCREEN_OFF.equalsIgnoreCase(action)) {
              Log.i(TAG, "onReceive: 屏幕关闭");
          }
          context.unregisterReceiver(this);
      }
    }, intentFilter);
    

    在Activity上下文中调用registerReceiver方法,动态注册一个广播接收者,同时指定了意图过滤器。
    这里要注意注册的BroadcastReceiver最好是静态类,防止内存泄漏。否则要及时取消注册。

    原理

    注册通知

    静态注册

    通过PackageManagerService安装应用时,会解析安装包中的Manifest.xml文件,存储解析到Receiver标签对应的广播接收器。

    通过Unix Socket接收需要安装的Apk文件
    //android/os/FileBridge.java
    
    public class FileBridge extends Thread {
        private static final int MSG_LENGTH = 8;
        private final FileDescriptor mServer = new FileDescriptor();
        private final FileDescriptor mClient = new FileDescriptor();
        
        private FileDescriptor mTarget;
    
        //构造函数
        //创建Unix Domain Socket的客户端和服务端,然后会将代表客户端的文件描述符发送给客户端进程
        public FileBridge() {
            try {
                Os.socketpair(AF_UNIX, SOCK_STREAM, 0, mServer, mClient);
            } catch (ErrnoException e) {
                throw new RuntimeException("Failed to create bridge");
            }
        }
        
        //mTarget文件描述符用来存储客户端进程发送过来的Apk文件数据
        public void setTargetFile(FileDescriptor target) {
            mTarget = target;
        }
    
        public FileDescriptor getClientSocket() {
            return mClient;
        }
        
        //这是一个线程,不停监听Socket的Server端
        @Override
        public void run() {
            final byte[] temp = new byte[8192];//读缓冲区
            //读取Socket的Server端,如果没有数据会阻塞
            //每次先读取8字节,前4个字节代表数据类型,后4四个字节代表数据长度
            while (IoBridge.read(mServer, temp, 0, MSG_LENGTH) == MSG_LENGTH) {
                //按照大端序读取前4字节
                final int cmd = Memory.peekInt(temp, 0, ByteOrder.BIG_ENDIAN);
                //数据写入,将apk文件数据写入到mTraget代表的文件描述中
                if (cmd == CMD_WRITE) {
                    int len = Memory.peekInt(temp, 4, ByteOrder.BIG_ENDIAN);
                   while (len > 0) {
                       int n = IoBridge.read(mServer, temp, 0, Math.min(temp.length, len));
                       if (n == -1) {
                           throw new IOException(
                                   "Unexpected EOF; still expected " + len + " bytes");
                       }
                       IoBridge.write(mTarget, temp, 0, n);
                       len -= n;
                   }
                //数据同步,并回显给客户端确认
                } else if (cmd == CMD_FSYNC) {
                    Os.fsync(mTarget);
                    IoBridge.write(mServer, temp, 0, MSG_LENGTH);
                //关闭消息通道,并回显给客户端确认
                } else if (cmd == CMD_CLOSE) {
                    Os.fsync(mTarget);
                   Os.close(mTarget);
                   mClosed = true;
                   IoBridge.write(mServer, temp, 0, MSG_LENGTH);
                   break;
                }
            }
            
        }
    }
    
    验证APK合法性
    1. 包名、版本号
    2. 签名(V2+V1)sudo
    3. 其他应用验证package-verifier
    4. 应用申请的权限,需要用户确认
    5. 拷贝安装包到data/data/{包名}/目录下,并且复制Native库到lib目录下
    解析Apk文件
    1. 添加apk文件路径到AssetManager管理中
    2. 解析Manifest.xml文件信息到Package类中,区分是多个apk文件安装,还是单个apk文件安装
    3. 验证该安装包是否只用于测试
    4. 为Package类填充证书和签名信息
    5. 是否覆盖安装,需要验证签名是否一致、包配置是否一致等。还要替换应用资源
    //frameworks/base/core/java/android/content/pm/PackageParser.java
    
    private boolean parseBaseApplication(Package owner, Resources res,
                XmlResourceParser parser, int flags, String[] outError)
        ... 
        
        final int innerDepth = parser.getDepth();
        int type;
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
              && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth))     
              {
             String tagName = parser.getName();
              if (tagName.equals("activity")) {
                  Activity a = parseActivity(owner, res, parser, flags, outError, 
                                            false,owner.baseHardwareAccelerated);
                  owner.activities.add(a);
              } else if (tagName.equals("receiver")) {
                  Activity a = parseActivity(owner, res, parser, flags, outError, true, false);
                  owner.receivers.add(a);
            
              } else if (tagName.equals("service")) {
              ...
        }
    }
    

    从上面解析过程可以看出,Android系统把Activity和BroadcastReceiver都当做了Activity类型来存储的。
    parseActivity方法中,还会继续解析里面的intent-filter标签,以及其action/category/data等子标签。
    所有从Manifest.xml文件中解析出来的数据,会存储到Package类中。

     public final static class Package {
        public String packageName;
        public final ArrayList<Activity> activities = new ArrayList<Activity>(0);
        public final ArrayList<Activity> receivers = new ArrayList<Activity>(0);
        ...
     }
    
    保存组件信息

    将Package类中的四大组件信息以及整个Package信息保存到PackageManagerService中。同时还会保存到Settings类中,Settings类代表了系统的动态设置信息。

    //PackageManagerService.java
    
    //系统中所有程序信息
    final ArrayMap<String, PackageParser.Package> mPackages =
                new ArrayMap<String, PackageParser.Package>();
    
    // 系统中所有程序的四大组件信息
    final ActivityIntentResolver mActivities =
           new ActivityIntentResolver();
    final ActivityIntentResolver mReceivers =
           new ActivityIntentResolver();
    final ServiceIntentResolver mServices = new ServiceIntentResolver();
    final ProviderIntentResolver mProviders = new ProviderIntentResolver();
    
    //扫描包信息
    private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg,
                final int policyFlags, final int scanFlags, long currentTime, UserHandle user)
                throws PackageManagerException {
        // Add the new setting to mSettings
        mSettings.insertPackageSettingLPw(pkgSetting, pkg);
        // Add the new setting to mPackages
        mPackages.put(pkg.applicationInfo.packageName, pkg);
        
        
        ...
        
        //保存Package中的BroadcastReceiver信息
        N = pkg.receivers.size();
        r = null;
        for (i=0; i<N; i++) {
            //这里把Receiver当做Activity来处理
            PackageParser.Activity a = pkg.receivers.get(i);
            a.info.processName = fixProcessName(pkg.applicationInfo.processName,
                    a.info.processName, pkg.applicationInfo.uid);
            mReceivers.addActivity(a, "receiver");
            if ((policyFlags&PackageParser.PARSE_CHATTY) != 0) {
                if (r == null) {
                
                    r = new StringBuilder(256);
                } else {
                    r.append(' ');
                }
                r.append(a.info.name);
            }
        }
    }
    
    

    动态注册

    通过ActivityManagerServer注册
    //frameworks/base/core/java/android/app/ContextImpl.java
    
    private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
                IntentFilter filter, String broadcastPermission,
                Handler scheduler, Context context) {
            IIntentReceiver rd = null;
            if (receiver != null) {
                if (mPackageInfo != null && context != null) {
                    if (scheduler == null) {
                        scheduler = mMainThread.getHandler();
                    }
                    rd = mPackageInfo.getReceiverDispatcher(
                        receiver, context, scheduler,
                        mMainThread.getInstrumentation(), true);
                } else {
                    if (scheduler == null) {
                        scheduler = mMainThread.getHandler();
                    }
                    rd = new LoadedApk.ReceiverDispatcher(
                            receiver, context, scheduler, null, true).getIIntentReceiver();
                }
            }
            try {
                final Intent intent = ActivityManagerNative.getDefault().registerReceiver(
                        mMainThread.getApplicationThread(), mBasePackageName,
                        rd, filter, broadcastPermission, userId);
                if (intent != null) {
                    intent.setExtrasClassLoader(getClassLoader());
                    intent.prepareToEnterProcess();
                }
                return intent;
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
    

    我们在Context上下文中调用registerReceiver(BroadcastReceiver, IntentFilter)方法动态注册一个广播时,会先保存到ActivityManagerServer中。

    //frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
    
    final HashMap<IBinder, ReceiverList> mRegisteredReceivers = new HashMap<>();
    
    public Intent registerReceiver(IApplicationThread caller, String callerPackage,
                IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
        
        ...
        
        //如果调用者传入BroadcastReceiver是null,代表想要获得一个粘性广播
        //粘性广播直接通过registerReceiver方法的返回值返回给调用者。
        Intent sticky = allSticky != null ? allSticky.get(0) : null;
        if (receiver == null) {
          return sticky;
        }
    
        //将BroadcastReceiver存储到一个HashMap中
        ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
        if (rl == null) {
            //ReceiverList是一个ArrayList子类,用来存储BroadcastReceiver和对应的过滤器
        rl = new ReceiverList(this, callerApp, callingPid, callingUid,
           userId, receiver);
            if (rl.app != null) {
                rl.app.receivers.add(rl);
            } else {
                try {
                   receiver.asBinder().linkToDeath(rl, 0);
                } catch (RemoteException e) {
                   return sticky;
                }
                rl.linkedToDeath = true;
            }
            mRegisteredReceivers.put(receiver.asBinder(), rl);
        }
        //保存过滤器到IntentResolver中
        BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
                        permission, callingUid, userId);
        rl.add(bf);  
        mReceiverResolver.addFilter(bf);
    }
    

    发送、接收通知

    当应用程序或系统程序发出一个广播时,会调用到ActivityManagerService中,ActivityManagerService会从自己保存的BroadcastReceiver列表和PackageManagerService保存的BroadcastReceiver列表中找出符合意图过滤器的广播接收者,然后向其发送广播。

    //ActivityManagerService.java
    
    final int broadcastIntentLocked(ProcessRecord callerApp,
                String callerPackage, Intent intent, String resolvedType,
                IIntentReceiver resultTo, int resultCode, String resultData,
                Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
                boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {
                
        //1. 默认不给已经stopped停止的应用发送广播
        
        //2. 如果没有完成开机操作,例如正在升级系统,只允许注册广播,不能发送
        
        //3. 如果广播指定的目的用户,那么这个用户必须处于运行状态
        
        //4. 验证是否是系统应用发出的受保护的广播。受保护的广播只能由系统应用发出。
        //系统应用的判断标准是用户id:ROOT_UID/SYSTEM_UID/PHONE_UID/BLUETOOTH_UID/NFC_UID,或者是常驻应用
        
        //5. 窗口小部件的配置和更新广播也不能由应用发出。由于历史原因,它们并不是受保护的广播,需要单独判断
        
        //6. 对特殊广播的处理:由PackageManager发出的应用被删除或变更的广播,
        //需要ActivityManagerService作出对应的操作,例如从最近使用应用中删除它们的Activity。
        //特殊类型的广播处理完成后,就直接返回了
        
        //7. 从PackageManagerService中查询符合过滤器条件的广播接受者List<ResolveInfo>,这里就是应用安装时,解析出来的静态注册的广播接受者。
         List<ResolveInfo> newReceivers = AppGlobals.getPackageManager()
                            .queryIntentReceivers(intent, resolvedType, pmFlags,
                                                     user).getList();
        
        //8. 从ActivityManagerService的ContentResolver中查询符合条件的广播接受者,这里代表通过代码动态注册的广播接受者
         registeredReceivers = mReceiverResolver.queryIntent(intent,
                            resolvedType, false, userId);
       
        
        //9. 将两个列表按照优先级合并成一个列表
        
        //10. 构造一个BroadcastRecord,添加到队列BroadcastQueue中,然后开始调度执行。
    }
    
    
    //BroasdcastQueue.java
    
    public final class BroadcastQueue {
    
        final ArrayList<BroadcastRecord> mParallelBroadcasts = new ArrayList<>();
        final ArrayList<BroadcastRecord> mOrderedBroadcasts = new ArrayList<>();
    
        //11. BroadcastQueue中的广播被分为并发广播和顺序广播。并发广播会立即被执行,而不需要等待其他的广播执行完毕。顺序广播则会逐一执行。
        //我们在注册广播时可以指定是否是顺序广播,或者粘性广播被认为是并发广播
        
        
        final void processNextBroadcast(boolean fromMsg) {
            BroadcastRecord r;
            
            //12. 开始处理列表中的广播。先处理并发广播
            
            //13. 如果处理广播的进程不存在,还需要等待进程的创建
            
            //14. 处理动态注册的广播,在BroadcastQueue中以BroadcastFilter形式存储
            performReceiveLocked(filter.receiverList.app, 
                                filter.receiverList.receiver,
                            new Intent(r.intent), r.resultCode, r.resultData,
                            r.resultExtras, r.ordered, r.initialSticky, r.userId);
                            
            //15. 处理静态注册的广播,如果需要,还要提前唤起对应进程。如果目标进程开始运行后,再分发对应的广播
            String targetProcess = info.activityInfo.processName;
            ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
                            info.activityInfo.applicationInfo.uid, false);
            app.addPackage(info.activityInfo.packageName,
                                info.activityInfo.applicationInfo.versionCode, 
                                mService.mProcessStats);
            processCurBroadcastLocked(r, app);
            
            
        }
    }
    

    performReceiveLocked方法代码如下:

    void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
                Intent intent, int resultCode, String data, Bundle extras,
                boolean ordered, boolean sticky, int sendingUser) throws RemoteException {
        if (app != null) {
            app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
                                data, extras, ordered, sticky, sendingUser,
                                 app.repProcState);
        } else {
            receiver.performReceive(intent, resultCode, data, extras, ordered,
                        sticky, sendingUser);
        }
    }
    

    processCurBroadcastLocked方法如下:

    private final void processCurBroadcastLocked(BroadcastRecord r,
                ProcessRecord app) throws RemoteException {
                
        app.thread.scheduleReceiver(new Intent(r.intent), 
                                r.curReceiver,
                                mService.compatibilityInfoForPackageLocked(
                                r.curReceiver.applicationInfo),
                                r.resultCode, 
                                r.resultData, 
                                r.resultExtras, 
                                r.ordered, 
                                r.userId,
                                app.repProcState);
    }
    

    取消注册

    取消动态注册的广播接收器

    前面说过动态注册的广播接收器是存储在ActivityManagerService中的,那么取消注册的时候,也是通过跨进程通信,告知ActivityManagerService移除指定的广播接收器。

    //ActivityManagerService.java
    
    final HashMap<IBinder, ReceiverList> mRegisteredReceivers = new HashMap<>();
    
    public void unregisterReceiver(IIntentReceiver receiver) {
       final long origId = Binder.clearCallingIdentity();
       try {
           boolean doTrim = false;
    
           synchronized(this) {
               ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
               if (rl != null) {
                   final BroadcastRecord r = rl.curBroadcast;
                   if (r != null && r == r.queue.getMatchingOrderedReceiver(r)) {
                       final boolean doNext = r.queue.finishReceiverLocked(
                               r, r.resultCode, r.resultData, r.resultExtras,
                               r.resultAbort, false);
                       if (doNext) {
                           doTrim = true;
                           r.queue.processNextBroadcast(false);
                       }
                   }
    
                   if (rl.app != null) {
                       rl.app.receivers.remove(rl);
                   }
                   //从HashMap中移除BroadcastReceiver
                   //从IntentResolver中移除对应的过滤器
                   removeReceiverLocked(rl);
                   if (rl.linkedToDeath) {
                       rl.linkedToDeath = false;
                       rl.receiver.asBinder().unlinkToDeath(rl, 0);
                   }
               }
           }
    
           // If we actually concluded any broadcasts, we might now be able
           // to trim the recipients' apps from our working set
           if (doTrim) {
               trimApplications();
               return;
           }
    
       } finally {
           Binder.restoreCallingIdentity(origId);
       }
    }
    
    
    void removeReceiverLocked(ReceiverList rl) {
       mRegisteredReceivers.remove(rl.receiver.asBinder());
       for (int i = rl.size() - 1; i >= 0; i--) {
           mReceiverResolver.removeFilter(rl.get(i));
       }
    }
    

    取消静态注册的广播接收器

    静态注册的广播接收者理论上是无法取消注册的,因为它们是写死在Manifest.xml文件中,并在应用安装时就被记录到PackageManagerService中的,并且PackageManagerService类并没有提供移除Receiver信息的接口。

    那么静态注册的广播接收器就不能取消了么?
    当然不是,这里我们可以采用迂回的方式达到目的。
    在从PackageManagerService中查询符合条件的广播接收器列表时,IntentResolver会检查每个过滤器对应的BroadcastReceiver是否被禁用了,如果被禁用了就会忽略掉这个BroadcastReceiver,从而达到和取消注册同样的效果。

    下面是禁用或启动某个组件的代码:

    //禁用xxx.class代表的组件,如果是广播接收器,则不会再接收到广播
    ComponentName componentName = new ComponentName(this, xxx.class);
            getPackageManager().setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
    
    //启用xxx.class代表的组件
    ComponentName componentName = new ComponentName(this, xxx.class);
            getPackageManager().setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
    

    当我们通过PackageManagerService禁用某个组件时,PackageManagerService做了两件事:

    1. 前面在静态注册流程中提高,从Manifest.xml解析出来的组件也会存储到Setting中一份,标识了组件的状态。所以第一个要先修改Setting中的组件状态。如下面代码所示:
    //PackageManagerService.java
    
    private void setEnabledSetting(final String packageName, String className, int newState,
                final int flags, int userId, String callingPackage) {
        PackageSetting pkgSetting;
        pkgSetting = mSettings.mPackages.get(packageName);
        pkgSetting.setEnabled(newState, userId, callingPackage);
    }
    

    将某个组件的状态改为禁用后,当再次向PackageManagerService查询静态注册的组件时就会把禁用掉的组件过滤掉,不会返回给ActivityManagerService。

    //PackageManagerService.java
    
    //ActivityIntentResolver类的方法
    //根据ActivityIntentInfo创建一个ResolveInfo
    @Override
    protected ResolveInfo newResult(PackageParser.ActivityIntentInfo info,
          int match, int userId) {
          
          //向Setting类查询对应的组件是否被启用了
          if (!mSettings.isEnabledAndMatchLPr(info.activity.info, mFlags, userId)) {
               return null;
           }
    }
    
    1. 第二件事会调用通过ActivityManagerService发送一个Intent.ACTION_PACKAGE_CHANGED类型的广播,告知ActivityManagerService有个组件被禁用了,如果有正在排队发送的广播需要移除掉。

    这个流程和普通应用发送广播的流程是一样的,区别是ActivityManagerService在检查到此类型的广播时,会自己处理掉,而不会真正的发送出去,为你Intent.ACTION_PACKAGE_CHANGED广播属于受保护的广播,只能系统代码能发送。

    ActivityManagerService的处理逻辑如下:

    //ActivityManagerService.java
    
    private void cleanupDisabledPackageComponentsLocked(
                String packageName, int userId, boolean killProcess, String[] changedClasses) {
            
        //1. 遍历禁用的组件,从PackageManagerService中查询对应组件是否已经被禁用
        //找出还没有被禁用的组件,如果已经禁用了就不管了
        try {
              enabled = pm.getComponentEnabledSetting(
                      new ComponentName(packageName, changedClass),
                      (userId != UserHandle.USER_ALL) ? userId : UserHandle.USER_SYSTEM);
          } catch (Exception e) {
              // As above, probably racing with uninstall.
              return;
          }
          if (enabled != PackageManager.COMPONENT_ENABLED_STATE_ENABLED
                  && enabled != PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
              if (disabledClasses == null) {
                  disabledClasses = new ArraySet<>(changedClasses.length);
              }
              //disabledClasses代表了实际将要被禁用的组件
              disabledClasses.add(changedClass);
          }
          
    
      //遍历ActivityManagerService存储的广播接收器,查看是否有需要禁用的
       for (int i = mBroadcastQueues.length - 1; i >= 0; i--) {
           mBroadcastQueues[i].cleanupDisabledPackageReceiversLocked(
                   packageName, disabledClasses, userId, true);
       }
                   
    }
    
    

    通过以上两个步骤就实现了禁用静态注册的广播接收器的目的。

    与LocalBroadcastReceiver区别

    LocalBroadcastReceiver就比较简单了,就是一个单例类+消息转发。不需要与ActivityManagerService或PackageManagerService服务进行跨进程通信。
    LocalBroadcastReceiver是线程安全的,注册或发送广播时都是用Synchronized关键字包裹起来。

    获取LocalBroadcastReceiver单例

    @NonNull
    public static LocalBroadcastManager getInstance(@NonNull Context context) {
       synchronized (mLock) {
           if (mInstance == null) {
               mInstance = new LocalBroadcastManager(context.getApplicationContext());
           }
           return mInstance;
       }
    }
    
    private LocalBroadcastManager(Context context) {
       mAppContext = context;
       mHandler = new Handler(context.getMainLooper()) {
    
           @Override
           public void handleMessage(Message msg) {
               switch (msg.what) {
                   case MSG_EXEC_PENDING_BROADCASTS:
                       executePendingBroadcasts();
                       break;
                   default:
                       super.handleMessage(msg);
               }
           }
       };
    }
    

    在构造LocalBroadcastReceiver实例时,会创建一个Handler,用于在主线程发送通知。

    注册广播

    private final HashMap<BroadcastReceiver, ArrayList<ReceiverRecord>> mReceivers
                = new HashMap<>();
    private final HashMap<String, ArrayList<ReceiverRecord>> mActions = new HashMap<>();
    
    public void registerReceiver(@NonNull BroadcastReceiver receiver,
                @NonNull IntentFilter filter) {
       synchronized (mReceivers) {
            //使用ReceiverRecord记录一个BroadcastReceiver和IntentFilter
            //使用HashMap记录一个BroadcastReceiver与多个ReceiverRecord关系
            //因为对同一个BroadcastReceiver允许注册多次
           ReceiverRecord entry = new ReceiverRecord(filter, receiver);
           ArrayList<ReceiverRecord> filters = mReceivers.get(receiver);
           if (filters == null) {
               filters = new ArrayList<>(1);
               mReceivers.put(receiver, filters);
           }
           filters.add(entry);
           for (int i=0; i<filter.countActions(); i++) {
               String action = filter.getAction(i);
            //使用HashMap记录每个Action和多个ReceiverRecord的关系
            //便于发送广播时查找对应的Receiver
               ArrayList<ReceiverRecord> entries = mActions.get(action);
               if (entries == null) {
                   entries = new ArrayList<ReceiverRecord>(1);
                   mActions.put(action, entries);
               }
               entries.add(entry);
           }
       }
    }
    

    发送广播

    在主线程接收广播

    private final ArrayList<BroadcastRecord> mPendingBroadcasts = new ArrayList<>();
    
    public boolean sendBroadcast(@NonNull Intent intent) {
        
        ...
        
        //找出接受者,存储到列表mPendingBroadcasts中,等待被发送
        mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
        //向主线程发送一个消息,开始分发mPendingBroadcasts中的广播
        if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
            mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
        }
    }
    

    在当前调用线程接收广播

    public void sendBroadcastSync(@NonNull Intent intent) {
       if (sendBroadcast(intent)) {
            //找到符合条件的接受者后,立即在当前线程去执行分发逻辑,而不等待主线程分发
            //也就是此方法会BroadcastReceiver的onReceive方法执行后,才会返回
           executePendingBroadcasts();
       }
    }
    
    展开全文
  • BroadcastReceiver 详解

    2017-11-04 17:50:56
    一、定义BroadcastReceiver,广播接收器,是四大组件之一。是全局(一般情况下,应用内广播可认为是局 部广播,将exported属性设置为false)的监听器,Android 广播分为两个角色:广播发送者和广 播接收者。

    一、定义

    BroadcastReceiver,广播接收器,是四大组件之一。是全局(一般情况下,应用内广播可认为是局
    部广播,将exported属性设置为false)的监听器,Android 广播分为两个角色:广播发送者和广
    播接收者。

    二、应用场景

    a.不同组件之间的通信(包括应用内和应用之间)

    b.与Android系统在特定情况下的通信

    c.线程之间的通信

    三、生命周期

    当一个broadcast信息到达该receiver,Android调用它的onReceive()方法并将含有该广播信息

    的intent 对象传递它。Broadcast receiver仅仅在执行该方法时才被认为是活跃的。当

    onReceive()返回后,它又处于非活跃状态。也就是说,它的生命周期为从回调onReceive()方

    法开始到该方法返回结果后结束。最长只有10秒,所以我们不能再OnReceive()中做耗时的任

    务,可以将耗时的任务转移到服务中进行。

    四、实现原理

    Android 中的广播使用了设计模式中的观察者模式,基于消息的发布/订阅事件模型。

    五、广播的两种注册方式:静态注册和动态注册

    • 静态注册

    是在配置文件AndroidManifest.xml里通过**标签声明receiver 标签声明

    属性值说明
    
    <receiver 
    //这个属性用于定义系统是否能够实例化这个广播接收器,如果设置为true,则能够实例化,如果设置为false,则不能被实例化。默认值是true。
        android:enabled=["true" | "false"]
    //这个属性用于指示该广播接收器是否能够接收来自应用程序外部的消息,如果设置true,则能够接收,如果设置为false,则不能够接收。如果
    //设置为false,这该接收只能接收那些由相同应用程序组件或带有相同用户ID的应用程序所发出的消息。
    //默认值是由receiver中有无intent-filter决定的:如果有intent-filter,默认值为true,否则为false
        android:exported=["true" | "false"]
        android:icon="drawable resource"
        android:label="string resource"
    //继承BroadcastReceiver子类的类名
        android:name=".mBroadcastReceiver"
    //具有相应权限的广播发送者发送的广播才能被此BroadcastReceiver所接收;
        android:permission="string"
    //BroadcastReceiver运行所处的进程
    //默认为app的进程,可以指定独立的进程
    //注:Android四大基本组件都可以通过此属性指定自己的独立进程
        android:process="string" >
    
    //用于指定此广播接收器将接收的广播类型
    //本示例中给出的是用于接收网络状态改变时发出的广播
     <intent-filter>
    <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
        </intent-filter>
    </receiver>
    
    
    注册示例
    
    <receiver 
      //此广播接收者类是mBroadcastReceiver
      android:name=".mBroadcastReceiver" >
      //用于接收网络状态改变时发出的广播
      <intent-filter>
          <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
      </intent-filter>
    </receiver>
    
    当此App首次启动时,系统会自动实例化mBroadcastReceiver类,并注册到系统中。
    • 动态注册

    在代码中通过调用Context的registerReceiver()方法进行动态注册BroadcastReceiver,

    具体代码如下:

    @Override
      protected void onResume(){
          super.onResume();
    
        //实例化BroadcastReceiver子类 &  IntentFilter
         mBroadcastReceiver mBroadcastReceiver = new mBroadcastReceiver();
         IntentFilter intentFilter = new IntentFilter();
    
        //设置接收广播的类型
         intentFilter.addAction(android.net.conn.CONNECTIVITY_CHANGE);
    
        //调用Context的registerReceiver()方法进行动态注册
         registerReceiver(mBroadcastReceiver, intentFilter);
     }
    
    
    //注册广播后,要在相应位置记得销毁广播
    //即在onPause() 中unregisterReceiver(mBroadcastReceiver)
    //当此Activity实例化时,会动态将MyBroadcastReceiver注册到系统中
    //当此Activity销毁时,动态注册的MyBroadcastReceiver将不再接收到相应的广播。
     @Override
     protected void onPause() {
         super.onPause();
          //销毁在onResume()方法中的广播
         unregisterReceiver(mBroadcastReceiver);
         }
    }
    

    注意:动态注册的广播,在它依附的上下文销毁之前注销广播注册,否则会导致内存泄露。

    • 静态注册和动态注册的区别

    静态注册:常驻型广播;不受到任何组件的生命周期影响;应用场景是需要时刻监听的广播;

    动态注册:非常驻型广播;跟随注册它的组件的生命周期,使用灵活,在组件生命周期结束前

    必须注销广播;应用场景是在特定时刻需要监听的广播。

    六、广播的类型

    *普通广播
    *有序广播
    *粘性广播
    *系统广播
    *App应用内广播

    注意:

    1.粘性广播在Android5.0已经废弃,不建议再使用该广播。

    2.可以在onReceive()中通过isOrderdBroadcast()方法判断是否是有序广播,该方法是

    BroadcastReceiver类中提供的方法,返回boolean型值,用于判断是否是有序广播。

    展开全文
  • BroadCastReceiver详解

    2017-07-12 23:30:02
    五、LocalBroadCastManager详解 1、LocalBroadCastManager高效的原因主要是因为它内部通过Handler来实现的,它的sendBroadCast()方法含义并非和我们平时所用的一样,它的sendBroadCast()其实是通过Handler发送...

    参考http://www.jianshu.com/p/ca3d87a4cdf3

    一、广播

    1、广播的定义

    广播是安卓系统中不同应用之间传输信息的一种机制,要发送广播的内容是一个Intent,这个Intent就是我们要传输的数据,Android系统在产生某个事件时发送广播,应用程序使用广播接收者接收这个广播,就知道系统产生了什么事件。比较像Java中的观察者模式
    Android系统在运行的过程中,会产生很多事件,比如开机、电量改变、收发短信、拨打电话、屏幕解锁

    2、广播的场景

    A、同一个App内不同组件之间的通信

    B、不同App之间不同组件之间的通信

    3、广播的种类

    普通广播(Normal Broadcast)

    所有与广播中的action匹配的广播接收者都可以收到这条广播,并且是没有先后顺序,视为同时收到

    有序广播(Ordered Broadcast)

    所有与广播中的action匹配的广播接收者都可以收到这条广播,但是是有先后顺序的,按照广播接收者的优先级排序

    本地广播(Local Broadcast)

    粘性广播(Sticky Broadcast)

    二、实现广播

    1、广播定义

    继承自BroadcastReceivre基类,必须复写抽象方法onReceive()方法,广播接收器接收到相应广播后,会自动回调onReceive()方法

    一般情况下,onReceive方法会涉及与其他组件之间的交互,如发送Notification、启动service等

    默认情况下,广播接收器运行在UI线程,因此,onReceive方法不能执行耗时操作,否则将导致ANR。

    class MyBroadcastReceiver extends BroadcastReceiver {
    
      //接收到广播后自动调用该方法
      @Override
      public void onReceive(Context context, Intent intent) {
       //写入接收广播后的操作
        }
    }

    2、广播注册

    静态注册

    在AndroidManifest.xml里通过< receive >标签声明

    <receiver 
        android:enabled=["true" | "false"]
    //此broadcastReceiver能否接收其他App的发出的广播
    //默认值是由receiver中有无intent-filter决定的:如果有intent-filter,默认值为true,否则为false
        android:exported=["true" | "false"]
        android:icon="drawable resource"
        android:label="string resource"
    //继承BroadcastReceiver子类的类名
        android:name=".MyBroadcastReceiver"
    //具有相应权限的广播发送者发送的广播才能被此BroadcastReceiver所接收;
        android:permission="string"
    //BroadcastReceiver运行所处的进程
    //默认为app的进程,可以指定独立的进程
    //注:Android四大基本组件都可以通过此属性指定自己的独立进程
        android:process="string" >
    
    //用于指定此广播接收器将接收的广播类型
    //本示例中给出的是用于接收网络状态改变时发出的广播
     <intent-filter>
    <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
        </intent-filter>
    </receiver>
    

    注册结果:

    <receiver 
      //此广播接收者类是MyBroadcastReceiver
      android:name=".MyBroadcastReceiver" >
      //用于接收网络状态改变时发出的广播
      <intent-filter>
          <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
      </intent-filter>
    </receiver>

    动态注册

    在代码中通过调用Context的registerReceiver()方法进行动态注册

        //实例化BroadcastReceiver子类 &  IntentFilter
         mBroadcastReceiver mBroadcastReceiver = new mBroadcastReceiver();
         IntentFilter intentFilter = new IntentFilter();
    
        //设置接收广播的类型
         intentFilter.addAction(android.net.conn.CONNECTIVITY_CHANGE);
    
        //调用Context的registerReceiver()方法进行动态注册
         registerReceiver(mBroadcastReceiver, intentFilter);
    
    //注册广播后,要在相应位置记得销毁广播
    //即在onDestroy() 中unregisterReceiver(mBroadcastReceiver)
    //当此Activity实例化时,会动态将MyBroadcastReceiver注册到系统中
    //当此Activity销毁时,动态注册的MyBroadcastReceiver将不再接收到相应的广播。
     @Override
     protected void onDestroy() {
         super.onDestroy();
          //销毁在onDestroy()方法中的广播
         unregisterReceiver(mBroadcastReceiver);
         }
    }

    特别注意

    动态广播最好在Activity的onResume()注册、onPause()注销。

    在onResume()注册、onPause()注销是因为onPause()在App死亡前一定会被执行,从而保证广播在App死亡前一定会被注销,从而防止内存泄露。

    不在onCreate() & onDestory() 或 onStart() & onStop()注册、注销是因为:

    1、当系统因为内存不足(优先级更高的应用需要内存,请看上图红框)要回收Activity占用的资源时,Activity在执行完onPause()方法后就会被销毁,有些生命周期方法onStop(),onDestory()就不会执行。当再回到此Activity时,是从onCreate方法开始执行。

    2、假设我们将广播的注销放在onStop(),onDestory()方法里的话,有可能在Activity被销毁后还未执行onStop(),onDestory()方法,即广播仍还未注销,从而导致内存泄露。
    但是,onPause()一定会被执行,从而保证了广播在App死亡前一定会被注销,从而防止内存泄露。

    区别: 通过静态注册的广播能够在程序退出后依然接收动态注册的广播。程序退出后,广播不再接收

    三、广播种类

    1、普通广播(Normal Broadcast)

    即开发者自身定义intent的广播

    Intent intent = new Intent();
    //对应BroadcastReceiver中intentFilter的action
    intent.setAction(BROADCAST_ACTION);
    //发送广播
    sendBroadcast(intent);

    若被注册了的广播接收者中注册时intentFilter的action与上述匹配,则会接收此广播

    <receiver 
        //此广播接收者类是mBroadcastReceiver
        android:name=".mBroadcastReceiver" >
        //用于接收网络状态改变时发出的广播
        <intent-filter>
            <action android:name="BROADCAST_ACTION" />
        </intent-filter>
    </receiver>

    2、系统广播(System Broadcast)

    Android中内置了多个系统广播:只要涉及到手机的基本操作(如开机、网络状态变化、拍照等等),都会发出相应的广播,每个广播都有特定的Intent - Filter(包括具体的action)
    当使用系统广播时,只需要在注册广播接收者时定义相关的action即可,并不需要手动发送广播,当系统有相关操作时会自动进行系统广播

    3、有序广播(Ordered Broadcast)

    发送出去的广播被广播接收者按照先后顺序接收

    广播接受者接收广播的顺序规则(同时面向静态和动态注册的广播接受者)

    1、按照Priority属性值从大-小排序;
    2、Priority属性相同者,动态注册的广播优先;

    特点:

    1、接收广播按顺序接收
    2、先接收的广播接收者可以对广播进行截断,即后接收的广播接收者不再接收到此广播;
    3、先接收的广播接收者可以对广播进行修改,那么后接收的广播接收者将接收到被修改后的广播

    sendOrderedBroadcast(intent);

    4、粘性广播(Sticky Broadcast)

    由于在Android5.0 & API 21中已经失效,所以不建议使用

    5、App应用内广播(Local Broadcast)

    背景:

    Android中的广播可以跨App直接通信(exported对于有intent-filter情况下默认值为true)

    冲突

    1、其他App针对性发出与当前App intent-filter相匹配的广播,由此导致当前App不断接收广播并处理;
    2、其他App注册与当前App一致的intent-filter用于接收广播,获取广播具体信息;会出现安全性 & 效率性的问题。

    解决方案
    使用App应用内广播(Local Broadcast)

    1、App应用内广播可理解为一种局部广播,广播的发送者和接收者都同属于一个App。
    2、相比于全局广播(普通广播),App应用内广播优势体现在:安全性高 & 效率高

    具体使用1 - 将全局广播设置成局部广播

    1、注册广播时将exported属性设置为false,使得非本App内部发出的此广播不被接收;
    2、在广播发送和接收时,增设相应权限permission,用于权限验证;
    3、发送广播时指定该广播接收器所在的包名,此广播将只会发送到此包中的App内与之相匹配的有效广播接收器中。

    通过intent.setPackage(packageName)指定包名

    具体使用2 - 使用封装好的LocalBroadcastManager类

    使用方式上与全局广播几乎相同,只是注册/取消注册广播接收器和发送广播时将参数的context变成了LocalBroadcastManager的单一实例

    注:对于LocalBroadcastManager方式发送的应用内广播,只能通过LocalBroadcastManager动态注册,不能静态注册

    //注册应用内广播接收器
    //步骤1:实例化BroadcastReceiver子类 & IntentFilter mBroadcastReceiver 
    mBroadcastReceiver = new mBroadcastReceiver(); 
    IntentFilter intentFilter = new IntentFilter(); 
    
    //步骤2:实例化LocalBroadcastManager的实例
    localBroadcastManager = LocalBroadcastManager.getInstance(this);
    
    //步骤3:设置接收广播的类型 
    intentFilter.addAction(android.net.conn.CONNECTIVITY_CHANGE);
    
    //步骤4:调用LocalBroadcastManager单一实例的registerReceiver()方法进行动态注册 
    localBroadcastManager.registerReceiver(mBroadcastReceiver, intentFilter);
    
    //取消注册应用内广播接收器
    localBroadcastManager.unregisterReceiver(mBroadcastReceiver);
    
    //发送应用内广播
    Intent intent = new Intent();
    intent.setAction(BROADCAST_ACTION);
    localBroadcastManager.sendBroadcast(intent);

    四、广播实现机制

    Android中的广播使用了设计模式中的观察者模式:基于消息的发布/订阅事件模型。

    1、模型中有3个角色:

    消息订阅者(广播接收者)
    消息发布者(广播发布者)
    消息中心(AMS,即Activity Manager Service:贯穿Android系统四大组件的一个核心服务,负责四大组件的启动、创建)

    这里写图片描述

    2、原理描述:

    广播接收者 通过 Binder机制在 AMS 注册
    广播发送者 通过 Binder 机制向 AMS 发送广播
    AMS 根据 广播发送者 要求,在已注册列表中,寻找合适的广播接收者
    寻找依据:IntentFilter / Permission
    AMS将广播发送到合适的广播接收者相应的消息循环队列中;
    广播接收者通过 消息循环 拿到此广播,并回调 onReceive()

    特别注意:广播发送者 和 广播接收者的执行 是 异步 的,发出去的广播不会关心有无接收者接收,也不确定接收者到底是何时才能接收到;

    特别注意

    对于不同注册方式的广播接收器回调OnReceive(Context context,Intent intent)中的context返回值是不一样的:

    对于静态注册(全局+应用内广播),回调onReceive(context, intent)中的context返回值是:ReceiverRestrictedContext;

    对于全局广播的动态注册,回调onReceive(context, intent)中的context返回值是:Activity Context;

    对于应用内广播的动态注册(LocalBroadcastManager方式),回调onReceive(context, intent)中的context返回值是:Application Context。

    对于应用内广播的动态注册(非LocalBroadcastManager方式),回调onReceive(context, intent)中的context返回值是:Activity Context;

    五、LocalBroadCastManager详解

    1、LocalBroadCastManager高效的原因主要是因为它内部通过Handler来实现的,它的sendBroadCast()方法含义并非和我们平时所用的一样,它的sendBroadCast()其实是通过Handler发送Message来实现的

    2、既然它内部是通过Handler来发送广播的,那么相比系统广播通过Binder机制来发送广播肯定更高效,同时使用Handler,其它应用无法向我们的应用发送广播,我们应用发送的广播也不会脱离我们的应用

    展开全文
  • 今天小编就为大家分享一篇关于Android四大组件之BroadcastReceiver详解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
  • BroadcastReceiver详解BroadcastReceiver简介BroadcastReceiver简单实现以及参数详解 BroadcastReceiver简介 顾名思义,BroadcastReceiver就是广播接收器,属于Android四大组件之一。用于接收程序(系统内部的程序...

    BroadcastReceiver简介

    顾名思义,BroadcastReceiver就是广播接收器,属于Android四大组件之一。用于接收程序(系统内部的程序以及用户开发的程序)发出的Broadcast Intent来做出相应的操作广播的发送需要两步:

    • 创建需要启动的BroadcastReceiver的Intent。
    • 使用sendBroadcast()或者sendOrderBroadcast()来启动。

    注册广播

    BroadcastReceiver本质上是一个监听器,监听收到的广播来进行处理相应操作,要实现一个广播接收者方法如下:
    第一步:继承BroadcastReceiver,并重写onReceive(Context context, Intent intent)方法。

    public class NetworkReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
        }
    }
    

    第二步,订阅感兴趣的广播Intent,订阅方法有两种:
    先来了解两种注册广播方式区别:

    • 静态注册
      长时间在后台运行,不受应用程序生命周期的影响,即使程序关闭了,该广播接受者也可以正常接收广播,但是比较费电以及消耗内存
    • 动态注册
      跟随应用的生命周期,有注册必须要有取消注册,否则容易造成内存泄露。

    动态注册

    第一种代码中实现:

     private NetworkReceiver networkReceiver;
    		if(networkReceiver == null){
                networkReceiver = new NetworkReceiver();
            }
            IntentFilter intentFilter = new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE");
            registerReceiver(networkReceiver,intentFilter); //注册网络监听广播
    

    要注意在onStop()中取消注册

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

    静态注册

    第二种AndroidManifest文件中设置(静态注册):
    静态注册特点:无论该应用程序是否处于活动状态,都会进行监听。

     <receiver android:name=".broadcast.NetworkReceiver">
           <intent-filter>
               <action android:name="android.net.conn.CONNECTIVITY_CHANGE"></action>
           </intent-filter>
     </receiver>
    

    注意:

    1. 我们一般在onStart中注册BroadcastReceiver,在onStop中取消BroadcastReceiver(当然你也可以在onCreate中注册,onDestory取消注册,没有影响)。
    2. 在Android8.0以上,Google为了安全原因对广播进行了一些限制,比如上方监听网络变化的广播,你会发现在8.0以下,可以正常监听到网络广播,在8.0以上的系统则接收不到广播,这个时候我们需要使用动态注册的方法去监听网络广播。例如,我们可以新建一个BaseActivity继承自Activity,然后使其所有的活动继承自BaseActivity,在BaseActivity判断是否是Android8.0以上系统进行动态注册:
    //Android 8.0以上需要动态注册
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
       //实例化IntentFilter对象
       IntentFilter intentFilter = new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE");
       networkReceiver = new NetworkReceiver();
       //注册广播接收
       registerReceiver(networkReceiver, intentFilter);
     }
    
    1. 一个BroadcastReceiver 对象只有在被调用onReceive(Context, Intent)时才有效,当该函数返回后,该对象就无效的了,结束了生命周期。
    2. 当广播消息到来的时候,会执行BroadcastReceiver中的onReceive(Context, Intent),onReceive()执行完毕后,BroadcastReceiver实例会被销毁,但是如果onReceive()在10秒内还没有执行完毕,Android会认为程序无响应,所以在onReceive()中不能执行耗时的操作,如果想要执行耗时的操作,需要使用Intent发给Service,让Service来完成。有的会问,能不能开一个子线程进行操作呢,自然是不能的,因为BroadcastReceiver的生命周期很短,如果子线程还没有执行完BroadcastReceiver就结束了,如果BroadcastReceiver结束,BroadcastReceiver所在的线程会被系统优先杀死,当然处于次线程中的子线程也会被杀死,所以不能够使用BroadcastReceiver执行耗时的操作。当我们想要执行耗时操作的时候,需要使用如下方法:
    public class NetworkReceiver extends BroadcastReceiver {
    
        @Override
        public void onReceive(Context context, Intent intent) {
            //启动Intent服务,由Service来进行耗时的操作
            Intent service = new Intent(context,XxxxService.class);
            context.startService(service);
        }
    }
    

    发送广播

    给本应用中静态注册的广播接收者发送广播

    发送广播其实很简单,只需要调用Context的sendBroadcast()或者sendOrderBroadcast()即可,下面简单的介绍一下如何发送广播。
    第一步:
    新建MyReceive继承BroadcastReceiver

    public class MyReceiver extends BroadcastReceiver {
        private static String TAG = MyReceiver.class.getSimpleName();
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            String msg = intent.getStringExtra("msg");
            Log.e(TAG,"接收到的action为:" + action + "消息为:" + msg);
        }
    }
    

    第二步:
    在AndroidManifest.xml中静态注册广播

    <receiver android:name=".broadcast.MyReceiver">
           <intent-filter>
               <!--指定该BroadcastReceiver所响应的Action-->
               <action android:name="zheshiyigeceshi"></action>
           </intent-filter>
    </receiver>
    

    第三步:
    在Activity发送广播:

    Intent intent = new Intent();
    intent.setAction("zheshiyigeceshi");
    intent.putExtra("msg","这是测试广播");
    //要注意的是如果在8.0以上的系统需要加入下面这一句(否则无法接收到广播),反正自己发送广播的时候加上下面这一句就可以了,加上在8.0以下的系统也无影响
    intent.setComponent(new ComponentName(MainActivity.this,MyReceiver.class));//显示指定组件名称
    sendBroadcast(intent);
    

    给其它应用中静态注册的广播接收者发送广播

    下面演示一下给其它应用中静态注册的广播接收者发送广播,并启动该应用。
    第一步:
    新建项目,并创建MyReceive继承BroadcastReceiver

    public class MyReceive extends BroadcastReceiver {
        private static String TAG = MyReceive.class.getSimpleName();
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.e(TAG,"收到了其它应用发过来的广播:" + intent.getStringExtra("msg"));
            Intent intent1 = new Intent(context, MainActivity.class);
            intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(intent1);
        }
    }
    

    第二步:
    在AndroidManifest.xml中静态注册广播

      <receiver android:name=".broadcast.MyReceive">
                <intent-filter>
                    <action android:name="zheshilingyigeceshi"></action>
                </intent-filter>
            </receiver>
    

    第三步:
    在第一个项目中发送广播给第二个项目

    Intent intent = new Intent();
    intent.setAction("zheshiyigeceshi");
    intent.putExtra("msg","这是测试广播");
    //要注意的是如果在8.0以上的系统需要加入下面这一句(否则无法接收到广播),反正自己发送广播的时候加上下面这一句就可以了,加上在8.0以下的系统也无影响
    //两个参数分别为,要发给哪个应用包名,要发给哪个应用的广播接受者类名路径
    intent.setComponent(new ComponentName("com.example.dhd.broadcastreceiverdemo2","com.example.dhd.broadcastreceiverdemo2.broadcast.MyReceive"));
    sendBroadcast(intent);
    

    至此,第一个应用发给广播给第二个应用用来启动第二个应用。

    广播分类

    普通广播

    所谓普通广播就是我们自己定义的广播,上方发送广播(给本应用中静态注册的广播接收者发送广播与给其它应用中静态注册的广播接收者发送广播)都属于普通广播,一般来讲,这种广播是比较常用的,
    这里不再举例子说明。

    系统广播

    系统广播就是Android系统本身自带的广播,例如系统开机的时候会给所有安装的应用发送一条开机广播,网络监测广播,比如上方的(注册广播)的例子,这里不再举例子说明。

    无序广播

    无序广播是通过sendBroadcast()发送的,这种广播不能够终止,当发出后,所有的广播接收者都能够收到此广播,上方发送广播都是使用的无序广播,这里不再举例说明。
    经测试,如果静态注册无序广播(并且Android系统处于8.0以上),需要发给两个广播接收者,例如:

    intent.setComponent(new ComponentName("com.example.dhd.broadcastreceiverdemo3", "com.example.dhd.broadcastreceiverdemo3.broadcast.MyReceive"));
    intent.setComponent(new ComponentName("com.example.dhd.broadcastreceiverdemo2","com.example.dhd.broadcastreceiverdemo2.broadcast.MyReceive"));
    

    真正收到广播的只有最后一个设置要启动的应用名。

    如果是动态注册无序广播,则不需要指定上方代码,当动态注册之后,发送广播时,两个应用程序都可以收到广播。

    有序广播

    有序广播是通过sendOrderBroadcast()发送的,它是有优先级的,优先级设置比较高的广播接收者先接收到,可以终止广播和修改数据。
    要注意一点的是,谷歌在Android8.0的时候修改了广播机制,静态注册的自定义广播不会按照优先级向下传递,在发送自定义静态广播的时候需要指定对应的包名,而且在之后的广播接收者需要再次指定包名进行传递,基本上可以说已经废了。例子就类似于上面的无序广播,只是把无序广播使用sendBroadcast()发送改为 sendOrderedBroadcast(),这里就不演示了,有兴趣的可以尝试一下。
    在这里介绍一下,有序广播的优先级以及数据接收以及修改。

    新建broadcastreceivedemo项目:

    设置按钮发送有序广播:

    Intent intent = new Intent();
    intent.setAction("zheshilingyigeceshi");
    //sendOrderedBroadcast(Intent intent, 
    //发送广播的意图对象(可以携带数据)
    //String receiverPermission, //接收权限(如果为空,则不需要权限)
    //BroadcastReceiver resultReceiver, //广播接收者对象(自己创建的最终的广播接收者,可以无须在清单文件中配置,也会接收到广播 )
    //Handler scheduler,         //若传null,则默认是在主线程中
    //int initialCode,           //初始化的一个值。可默认:Activity.RESULT_OK
    //String initialData,        //可发送的初始化数据(相当于一条广播数据)。可为null
    //Bundle initialExtras)      //可绑定数据传递(Intent对象也可以,所以可为null)
    sendOrderedBroadcast(intent,null,null,null, Activity.RESULT_OK,"我是首条数据",null);
    

    新建broadcastreceiverdemo2项目:

    新建MyReceive:

    public class MyReceive extends BroadcastReceiver {
        private static String TAG = MyReceive.class.getSimpleName();
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.e(TAG,"收到了其它应用发过来的广播:" + getResultData());
            //abortBroadcast();
            setResultData("这是第二条数据");
        }
    }
    

    进行动态注册广播:

    private MyReceive myReceive;
    IntentFilter intentFilter = new IntentFilter("zheshilingyigeceshi");
    intentFilter.setPriority(1000);
    myReceive = new MyReceive();
    //注册广播接收
    registerReceiver(myReceive, intentFilter);
    

    新建broadcastreceiverdemo3项目:

    新建MyReceive:

    public class MyReceive extends BroadcastReceiver {
        private static String TAG = MyReceive.class.getSimpleName();
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.e(TAG,"收到了其它应用发过来的广播:" + getResultData());
        }
    }
    

    进行动态注册广播:

    IntentFilter intentFilter = new IntentFilter("zheshilingyigeceshi");
    myReceive = new MyReceive();
    intentFilter.setPriority(800);
    //注册广播接收
    registerReceiver(myReceive, intentFilter);
    

    运行程序发现:
    在这里插入图片描述

    当不想要广播向下传递的时候可以使用以下代码终止广播:

    abortBroadcast();
    

    静态注册设置优先级:
    使用android:priority="1000"设置优先级

    <receiver android:name=".broadcast.MyReceive">
           <intent-filter android:priority="1000">
               <action android:name="zheshilingyigeceshi"></action>
           </intent-filter>
    </receiver>
    

    动态注册设置优先级:
    使用intentFilter.setPriority(1000);设置优先级

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
           //实例化IntentFilter对象
           IntentFilter intentFilter = new IntentFilter("zheshilingyigeceshi");
           intentFilter.setPriority(1000);
           myReceive = new MyReceive();
           //注册广播接收
           registerReceiver(myReceive, intentFilter);
       }
    

    结束语

    BroadcastReceiver介绍先到这里了,有错误或者有疑问的可在下方留言,我看到后会尽快解决。

    展开全文
  • 博客《BroadcastReceiver详解》对应源码,博客地址:http://blog.csdn.net/harvic880925/article/details/38710901
  • Android开发之BroadcastReceiver详解 BroadcastReceiver,顾名思义就是“广播接收者”的意思,它是Android四大基本组件之一,这种组件本质上是一种全局的监听器,用于监听系统全局的广播消息。它可以接收来自...
  • 安卓四大控件之BroadcastReceiver详解

    万次阅读 多人点赞 2016-07-06 17:00:48
    BroadcastReceiver详解广播的概念Android:系统在产生某个事件时发送广播,应用程序使用广播接收者接收这个广播,就知道系统产生了什么事件。 Android系统在运行的过程中,会产生很多事件,比如开机、电量改变、收发...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,899
精华内容 2,359
关键字:

broadcastreceiver详解