动态注册广播接收器_ip广播系统软件v2.3说明书

动态注册广播接收器_ip广播系统软件v2.3说明书从registerReceiver(BroadcastReceiverreceiver,IntentFilterfilter)出发所经历的类和方法:registerReceiver(receiver,filter)–>ContextWrapper.java$registerReceiver(receiver,filter);@OverridepublicIntentregis…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

从registerReceiver(BroadcastReceiver receiver,IntentFilter filter)出发

所经历的类和方法:registerReceiver(receiver,filter)–>ContextWrapper.java$registerReceiver(receiver,filter);

@Override
public Intent registerReceiver(
    BroadcastReceiver receiver, IntentFilter filter) {
    return mBase.registerReceiver(receiver, filter);//Context mBase
}

Context是接口,其实现类ContextImpl,因此其具体实现是ContextImpl$reisterReceiver(receiver,filter);

@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
    return registerReceiver(receiver, filter, null, null);
}

@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
        int flags) {
    return registerReceiver(receiver, filter, null, null, flags);
}

@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
        String broadcastPermission, Handler scheduler) {
    return registerReceiverInternal(receiver, getUserId(),
            filter, broadcastPermission, scheduler, getOuterContext(), 0);
}

用以上代码可知调用registerReceiver以后其内部调用了其重载函数,其重载函数又进一步调了registerReceiverInternal()方法

private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
        IntentFilter filter, String broadcastPermission,
        Handler scheduler, Context context, int flags) {
    IIntentReceiver rd = null;
    if (receiver != null) {
        if (mPackageInfo != null && context != null) {
            if (scheduler == null) {
                scheduler = mMainThread.getHandler();       //1
            }
            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 = ActivityManager.getService().registerReceiver(
                mMainThread.getApplicationThread(), mBasePackageName, rd, filter,
                broadcastPermission, userId, flags);
        if (intent != null) {
            intent.setExtrasClassLoader(getClassLoader());
            intent.prepareToEnterProcess();
        }
        return intent;
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

IIntentReceiver :IIntentReceiver其实是一个AIDL接口,内部就一个方法performReceive(…);
mPackageInfo:ContextImpl导入的一个LoadedApk的实例,是用来处理广播接收的。

以上参数context通过getOuterContext获取注册广播时所在的context,方法走到//1处,scheduler = mMainThread.getHandler();该scheduler是一个Handler类型,mMainThread是ActivityThread类型,这里handler主要用来负责分发AMS发送过来的广播用的。

public final class ActivityThread {
......
final H mH = new H();
private final class H extends Handler {
	......
	public void handleMessage(Message msg) {
		......
		switch (msg.what) {
		......
		}
		......
	}
	.....
}

......
final Handler getHandler() {
	return mH;
}

获取完scheduler(Handler),执行 rd = mPackageInfo.getReceiverDispatcher(…),

public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,
        Context context, Handler handler,
        Instrumentation instrumentation, boolean registered) {
    synchronized (mReceivers) {
        LoadedApk.ReceiverDispatcher rd = null;
        ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null;
        if (registered) {
            map = mReceivers.get(context);
            if (map != null) {
                rd = map.get(r);
            }
        }
        if (rd == null) {
            rd = new ReceiverDispatcher(r, context, handler,
                    instrumentation, registered);
            if (registered) {
                if (map == null) {
                    map = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();
                    mReceivers.put(context, map);
                }
                map.put(r, rd);
            }
        } else {
            rd.validate(context, handler);
        }
        rd.mForgotten = false;
        return rd.getIIntentReceiver();
    }
}

首先这个函数的返回类型是IIntentReceiver,这里可以看做是帮我们跨进程通信的Binder对象。

mReceivers:private final ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers是通过这些数组和Map把注册广播所在的Context,receiver以及ReceiverDispatcher封装起来,逻辑是如果广播registered成立,以context为key获取broadcastReceiver和ReceiverDispatcher。

假设MainActivity为Key值保存在LoadedApk的成员变量mReceivers中,这样,只要给定一个Activity和BroadcastReceiver,就可以查看LoadedApk里面是否已经存在相应的广播接收发布器ReceiverDispatcher了。

看看ReceiverDispather这个类

 static final class ReceiverDispatcher {

    final static class InnerReceiver extends IIntentReceiver.Stub {
        final WeakReference<LoadedApk.ReceiverDispatcher> mDispatcher;
        final LoadedApk.ReceiverDispatcher mStrongRef;

        InnerReceiver(LoadedApk.ReceiverDispatcher rd, boolean strong) {
            mDispatcher = new WeakReference<LoadedApk.ReceiverDispatcher>(rd);
            mStrongRef = strong ? rd : null;
        }

InnerReceiver是静态内部类,继承IIntentReceiver.Stub,InnerReceiver就是Binder接口IIntentReceiver的实现类

return rd.getIIntentReceiver();-->return mIIntentReceiver
mIIntentReceiver = new InnerReceiver(this, !registered);

而通过以上rd.getIIntetReceiver可以看出rd就是InnerReceiver对象,也就是binder对象

在新建广播接收发布器ReceiverDispatcher时,会在构造函数里面创建一个InnerReceiver实例,这是一个Binder对象,实现了IIntentReceiver接口,可以通过ReceiverDispatcher.getIIntentReceiver函数来获得,获得后就会把它传给ActivityManagerService,以便接收广播。在ReceiverDispatcher类的构造函数中,还会把传进来的Handle类型的参数activityThread保存下来,以便后面在分发广播的时候使用。

接下来是 Intent intent = ActivityManager.getService().registerReceiver()

.....
Intent intent = ActivityManager.getService().registerReceiver()
...

最后进入到ActivityManagerService中的registerReceiver函数中去,ActivityMangerService$registerReceiver();看看它是怎么进去的

过往的版本貌似都是ActivityManagerNative.getDefault().registerReceiver()而不是ActivityManager.getService().registerReceiver(),看看两者的实现步骤都具体做了什么,有什么区别

<1> 我先定位到ActivityManger.getService()这个方法

  /**
 * @hide
 */
@UnsupportedAppUsage
public static IActivityManager getService() {
    return IActivityManagerSingleton.get();
}

接下来看看IActivityMangerSingletone()

 @UnsupportedAppUsage
private static final Singleton<IActivityManager> IActivityManagerSingleton =
        new Singleton<IActivityManager>() {
            @Override
            protected IActivityManager create() {
                final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                final IActivityManager am = IActivityManager.Stub.asInterface(b);
                return am;
            }
        };

在frameworks/base/core/java/android/util/Singleton.java

public abstract class Singleton<T> {
  @UnsupportedAppUsage
 private T mInstance;
  protected abstract T create();
  @UnsupportedAppUsage
  public final T get() {
      synchronized (this) {
          if (mInstance == null) {
              mInstance = create();
          }
         return mInstance;
     }
  }  
  }

单纯从代码分析以上两段代码,IActivityManagerSingleton是Singleton变量,而singleton是一个抽象类内部声明了一个抽象方法create(),而其get方法通过由一个继承了Singleton并复写函数create()来给mInstance赋值,回到上层IActivityManagerSingleton,new了Singleton并重写了函数create(),通过Context.ACTIVITY_SERVICE获取一个binder,,并通过该对象返回了IActivityManager对象,最后ActivityManager.getService().registerReceiver()等价于IActivityManager.registerReceiver()。

具体以上的代码,就是Singleton这么一个工具类通过单例模式创建了一个对象,而这个过程是这样的,先获取了关联了系统服务的ActivityManagerService的binder对象[ final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);],然后通过IActivityManager.Stub.asInterface(b)返回一个IActivityManager的代理对象,基于Binder机制,通过调用代理对象的方法,使得系统服务ActivityManagerService对应的方法被调用,因为ActivityManagerService继承了IActivityManager.Stub

public class ActivityManagerService extends IActivityManager.Stub
      implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
      ......
      }

<2>ActivityManagerNative.getDefault().registerReceiver()看看他是怎么个操作

static public IActivityManager getDefault(){
    return ActivityManager.getService();
}

我丢!一样啊,只是这个方法封装了一下而已,到头来还是ActivityManager.getService(),打扰了

ActivityManagerService.registerReceiver的处理

函数首先是获得调用registerReceiver函数的应用程序进程记录块:

public Intent registerReceiver(IApplicationThread caller, String callerPackage,IIntentReceiver receiver,
IntentFilter filter,String permission, int userId,int flags) {
... //初始化了一些需要的变量
synchronized(this) {
if (caller != null) {
callerApp = getRecordForAppLocked(caller);
}
if(cllerApp == null){
    ....//异常处理
}if (callerApp.info.uid != SYSTEM_UID &&
    !callerApp.pkgList.containsKey(callerPackage) &&
    !"android".equals(callerPackage)) {
    ...//抛异常
              }
    callingUid = callerApp.info.uid;//进程中第一个app的uid
    callingPid = callerApp.pid;该app应用的process id
    } else {
    callerPackage = null;
    callingUid = Binder.getCallingUid();
    callingPid = Binder.getCallingPid();
          }
instantApp = isInstantApp(callerApp, callerPackage, callingUid);

看看instantApp是什么,怎么获取的

 private boolean isInstantApp(ProcessRecord record, @Nullable String callerPackage, int uid) {
      if (UserHandle.getAppId(uid) < FIRST_APPLICATION_UID) {
          return false;
      }
      // Easy case -- we have the app's ProcessRecord.
      if (record != null) {
          return record.info.isInstantApp();
      }
      // Otherwise check with PackageManager.
      IPackageManager pm = AppGlobals.getPackageManager();
      try {
          if (callerPackage == null) {
              final String[] packageNames = pm.getPackagesForUid(uid);
              if (packageNames == null || packageNames.length == 0) {
                  throw new IllegalArgumentException("Unable to determine caller package name");
              }
              // Instant Apps can't use shared uids, so its safe to only check the first package.
              callerPackage = packageNames[0];
          }
          mAppOpsService.checkPackage(uid, callerPackage);
          return pm.isInstantApp(callerPackage, UserHandle.getUserId(uid));
      } catch (RemoteException e) {
          Slog.e(TAG, "Error looking up if " + callerPackage + " is an instant app.", e);
          return true;
      }
  }

在Process.java中FIRST_APPLICATION_UID=10000;规定应用Uid是10000-19999,因此if(UserHandle.getAppId(uid) < FIRST_APPLICATION_UID)这就是判断如果不是应用的uid就返回false.

然后通过PackageManager检查是不是instant app如果是返回ture;

以下是处理粘性广播的处理

有时候用户希望发送sticky广播,以便日后注册的receiver可以收到“错过”的sticky广播。要达到这个目的,系统必须在内部维护一张sticky广播表,在具体的实现中,AMS会把广播intent加入mStickyBroadcasts映射表中。mStickyBroadcasts是一张哈希映射表,其key值为intent的action字符串,value值为“与这个action对应的intent数组列表”的引用。当我们发送sticky广播时,新的广播intent要么替换掉intent数组列表中的某项,要么作为一个新项被添加进数组列表,以备日后使用。

 Iterator<String> actions = filter.actionsIterator();
 ...
 int[] userIds = { UserHandle.USER_ALL,UserHandle.getUserId(callingUid) };
 while (actions.hasNext()) {
 String action = actions.next();
    for (int id : userIds) {//mStickyBroadcasts保留着系统所有的粘性广播
        ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(id);
            if (stickies != null) {
                 ArrayList<Intent> intents = stickies.get(action);
                    if (intents != null) {
                        if (stickyIntents == null) {
                            stickyIntents = new ArrayList<Intent>();
                          stickyIntents.addAll(intents);
                      }
}

//到此为止stickyIntents是系统中所有的粘性的intent

    ArrayList<Intent> allSticky = null;
    if (stickyIntents != null) {
        final ContentResolver resolver = mContext.getContentResolver();
        // Look for any matching sticky broadcasts...
        for (int i = 0, N = stickyIntents.size(); i < N; i++) {
            Intent intent = stickyIntents.get(i);
            // If intent has scheme "content", it will need to acccess
            // provider that needs to lock mProviderMap in ActivityThread
            // and also it may need to wait application response, so we
            // cannot lock ActivityManagerService here.
            if (filter.match(resolver, intent, true, TAG) >= 0) {
                if (allSticky == null) {
                    allSticky = new ArrayList<Intent>();
                }
                allSticky.add(intent); 
            }
        }
    }
    //allSticky就是与本次注册intentfilter匹配的粘性广播

        if (allSticky != null) {
            ArrayList receivers = new ArrayList();
            receivers.add(bf);

            final int stickyCount = allSticky.size();
            for (int i = 0; i < stickyCount; i++) {
                Intent intent = allSticky.get(i);   //处理粘性广播
                BroadcastQueue queue = broadcastQueueForIntent(intent);
                BroadcastRecord r = new BroadcastRecord(queue, intent, null,
                        null, -1, -1, null, null, AppOpsManager.OP_NONE, null, receivers,
                        null, 0, null, null, false, true, true, -1);
                queue.enqueueParallelBroadcastLocked(r);
                queue.scheduleBroadcastsLocked();
            }
        }

        return sticky;
    }
}

1.mStickyBroadcasts,key是用户的id,data是广播intent数组。由此可见,粘性广播是分用户的,用户1发出的粘性广播,用户2可能收不到。

2.stickyIntents 保存的是系统所有粘性广播intent

3.allSticky 保存的是与本次registerReceiver(BroadcastReceiver receiver, IntentFilter intent)中intent匹配的粘性广播的intent

扩展:粘性消息:粘性消息在发送后就一直存在于系统的消息容器里面,等待对应的处理器去处理,如果暂时没有处理器处理这个消息则一直在消息容器里面处于等待状态,粘性广播的Receiver如果被销毁,那么下次重建时会自动接收到消息数据. 注意:普通广播和粘性消息不同被截获,而有序广播是可

如果不是用sendStickyBroadcat()发的 因此这里的sticky和allSticky都为null
会进入以下代码

  ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
          if (rl == null) {
              rl = new ReceiverList(this, callerApp, callingPid, callingUid,
                      userId, receiver);
              if (rl.app != null) {
                  final int totalReceiversForApp = rl.app.receivers.size();
                  if (totalReceiversForApp >= MAX_RECEIVERS_ALLOWED_PER_APP) {
                      throw new IllegalStateException("Too many receivers, total of "
                              + totalReceiversForApp + ", registered for pid: "
                              + rl.pid + ", callerPackage: " + callerPackage);
                  }
                  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);

ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());

其中的receiver参数类型是IIntentReceiver,也就是对应着之前的ReceiverDispatcher中那个binder实体。

下面是ReceiverList的代码片段:

class ReceiverList extends ArrayList<BroadcastFilter>implements IBinder.DeathRecipient {
final ActivityManagerService owner; 
public final IIntentReceiver receiver;    
public final ProcessRecord app;    
public final int pid;    
public final int uid;
BroadcastRecord curBroadcast = null;
boolean linkedToDeath = false;
String stringName;
. . . . . .

ReceiverList继承于ArrayList,而BroadcastFilter又继承于IntentFilter,所以ReceiverList可以被理解为一个IntentFilter数组列表。

就是把注册的receivers保存到这个ReceiverList表里,其中还判断了如果注册的receiver数量过多抛出异常,最后把这receivers表放在mRegisteredReceivers中,这些都是为了方便在收到广播时,快速找到对应的广播接收器的。其中rl.app可以看做是这个注册receiver的宿主app的进程。

 BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
                  permission, callingUid, userId, instantApp, visibleToInstantApps);
          if (rl.containsFilter(filter)) {
         ...
         mReceiverResolver.addFilter(bf);
         }

以上代码作用就是创建一个BroadcastFilter来把广播接收器列表rl和filter关联起来,然后保存在ActivityManagerService中的成员变量mReceiverResolver中去,因为之前只保存receivers没有和其filter发生关联

BroadCastFilter代码片段:

BroadcastFilter(IntentFilter _filter, ReceiverList _receiverList,
          String _packageName, String _requiredPermission, int _owningUid, int _userId,
          boolean _instantApp, boolean _visibleToInstantApp) {
      super(_filter);
      receiverList = _receiverList;
      packageName = _packageName;
      requiredPermission = _requiredPermission;
      owningUid = _owningUid;
      owningUserId = _userId;
      instantApp = _instantApp;
      visibleToInstantApp = _visibleToInstantApp;
  }
  ...

最后将BroadcastFilter添加到IntentResolver类型的mReceiverResolver中,这样当AMS接收到广播时,就可以从mReceiverResolver中直接找到对应的广播接收者,从而达到注册广播的目的。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/194686.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • centos搭建svn 服务器 并同步到web 目录(总结)

    centos搭建svn 服务器 并同步到web 目录(总结)

    2021年10月29日
  • Python 逐行读取txt 文件并生成列表

    前言开始1.载入文件2.读取数据流3.数据处理4.关闭文件前言我们在编写一些自动化脚本的时候,为了方便,经常需要以txt文件作为数据输入,今天就跟大家讨论一下如何对txt文件进行读取并生成对应的列表等程序可操作的数据载体。开始1.载入文件这步就大家比较熟悉,文件操作中最基本的了。因为我们只需要读取文件,并不需要写入文件,所以在这里指定mode=”r”为只读模式(默认)。f=open(“C:/foo.txt”,”r”,encoding=’utf-8′)此时就有了这.

  • idea 查看类图_idea查看方法的实现

    idea 查看类图_idea查看方法的实现以BeanFactory为例给大家展示在Idea中查看某个接口的所有的实现类图1.展示一下BeanFactory类2.选中接口右键->Diagrams(showDiagrams)3.出现一个接口类,然后点中接口右键showimplementations或者快捷键ctrl+alt+B4.出现了图然后全选或者快捷键ctrl+a,然后回车就出现了所有的关系图…

  • java中的getClass()方法的用法

    java中的getClass()方法的用法getClass的定义以及使用方法getClass()的作用getClass()使用方式简单代码演示其他相关用法1.getName()和getCanonicalName()2.getDeclaredMethod()3.还有很多对class对象进行操作的方法getClass()的作用getClass()返回此Object的运行时该对象的类.该方法返回一个Class对象,可以通过该对象可…

  • 人工与软件刷流量有什么区别,如何做刷流量效果才最好?

    人工与软件刷流量有什么区别,如何做刷流量效果才最好?大家好,对于流量这个关键词我相信有绝大部分的是了解的,无论是对于开网店的,还是做直播亦或是做其他事情的,都需要流量进行曝光、推广等,但是,一些网店因为流量不够,所能得到的流量池有限,所以就导致了许多开网店的朋友们进行刷流量,现在刷流量的软件也是千千万万,那么选择哪种会比较好一些呢,亦或是人工刷流量与软件刷流量有什么区别呢?商家都了解真实的人工访客流量是需要有店铺停留时间或者是商品详情页的停留时间的,而这个停留时间是根据商家的店铺权重而不同的,每一个等级的店铺所算的停留时间是不相同的,如果商品优.

  • opencv中resize函数怎么用(图像resize)

    opencv中的resize函数有多种用法:1,图像缩放opencv帮助文档中对resize函数的介绍:src输入图dst输出图,形态和输入图相同,当dsize不等于0,输出图尺寸会和dsize相同,当dsize等于0,输出图尺寸会由输入图尺寸、fx、fy计算而得dsize输出尺寸,当输入为0时,fx、fy皆不可为0,dsize=Size(round(fxsrc.cols),round(fysrc.rows))fx水平缩放比例,当输入为0时,fx=(do

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号