
很明显,这个context是在调用构造函数的时候传递进来的。
以两个参数的构造函数为例,这个一般是在xml使用该控件后,解析xml时会调用构造方法。
那么这个时候传的context怎么来的呢?从父类传过来的,父类呢,也是从父类的父类传过来的,顶层父类是Decorview,那么看看Decorview的context怎么来的。
上图PhoneWindowjava
可以看出,Decorview是在generateDecor方法中创建的。而这个方法是Activity生命周期走到onResume时调用的一个方法。从方法里发现,context有两种。一种DecorContext,一种getContext()。
翻看源码,我们发现在activity中拿到的是Decorcontext。而它为什么要这么做呢?直接getcontext它不香么。看注释,它说这么做是为了不依附activity。因为我们看见Decorview里传递的第一个参数是applicationcontext
最后看看getContext的context是怎么来的。
上图ActivityThreadjava里的一个方法。
创建activity实例之前,我们会先创建context,而这个context实际上就是new 了一个ContextImpl。然后和activity绑定。所以getContext实际上就是一个ContextImpl实例。
对于Activity的启动流程,我们已经有了几个版本的分析了。这里我们分析一个更容易一些的,四大组件中最简单的Broadcast Receiver。
关于Broadcast,有几点需要了解。首先是广播的类型,然后是广播的发送方法,最后是广播是如何被接收的。这三者相辅相承的,比如普通广播和有序广播只有在详细了解了广播的接收过程了之后,才能真正明白它的含义。
普通的广播是不在意顺序的,最简单的理解是同时可以收到这个广播。如果应用是动态注册这个广播的,且广播发送时这个进程还活着,那么当然可以并发的把广播尽快地传送出去是最好的。
但是,如果是通过AndroidManifestxml静态注册的情况,也就是说这个广播首先要把一个进程启动起来,这时并发启动很多进程就是个问题了。Android目前的做法是,对这种静态的广播接收者,自动按有序广播的方式来串行处理。但是这对应用是透明的,应用不能假设系统已经把静态的无序广播当成有序广播来处理。
这个时候讲粘性广播有福了,因为从Android 50(API 21)开始,因为安全性的问题,官方已经正式废弃了粘性广播。
Context类提供两个方法可以用于发送普通广播:
差别是第二个设置权限。
发给特定的用户:
有序广播因为要处理消息的处理结果,所以要复杂一些。
如果只是想让广播可以按优先级来收取,并不在意处理的结果,可以用下面的版本:
同样,在多用户环境下,也可以选择给哪个用户发广播:
不管是普通的还是有序的广播都对应有粘性的版本:
以上的API都是定义于Context类中: >
嗯 channel实际就是一个客户端和server的一个抽象的管道 ,netty封装了网络的底层 所以 你不必太多去掀开一些它封装的东西来处理 对于还不熟悉的开发者来讲的 话;你可以这样处理 在连接上来的时候 你创建一个session会话来持有这个channel ,每一个session有一个ID ,那么 你在业务层就可以通过这个ID拿到session 从而将这个数据发送出去,你这里 其实在服务器端就是sessionA sessionB ,A,B两个客户端连接服务器了 ,那么 就创建sessionA sessionB ,并产生一个ID (ID 保持唯一就可以了),A向B发送,那么实际就是通过服务器来转发A的消息到B,那么你必然拿到B的ID,几在A的消息中发送B的ID,这样就可以拿到sessionB ,然后channelwrite(); 消息的转发 与消息的推送 关键就在与知道sessionID,顺利得到相应的session 这样就可以解决问题了;
创建session的位置在channelActive(ChannelHandlerContext ctx);标记channel的方式很多 ,上面的和你描述的一样 只是 封装了一个session来持有channel罢了;
谢谢你的回复。
你的回复里面没有提到如何绑定用户和channel的对应关系。我对此的大致想法是这样的:客户端与服务端建立连接的时候,也就是在channelActive(ChannelHandlerContext ctx),服务端用RSA算法生成一对公私钥,并把公钥返回给客户端;客户端使用公钥加密登录信息发送到服务器,服务器解密后,将用户信息与channel对应起来,记录在链表里面。然后当A发送信息给B的时候,服务器从链表里面找到B的信息,并通过B对应的channel传送信息给B。
不知我这样的连接流程有没有问题,请指教。
ID--session--channel 它们是一一对应的, map<ID,session>;
class session{
ID;//session持有ID
channel;//session持有channel
}
任何一个客户端链接上来,你就把服务器现在在线的ID同步给链接上来的客户端,这样就好像是qq,向谁发送? 只需 ID(目标)+message; 额 你这个是可以的 变相的其实本质一样 ,ID 就是一个纽带;
Context在我们开发中经常用到,不管是Framework提供给我们的四大组件,还是应用级别的Application,还是负责表现层的View相关类,甚至连我们很多时候创建的实体类都会需要持有一个Context的引用。那么Context到底是什么呢?
建议看这个: > Context,中文直译为“上下文”,SDK中对其说明如下: Interface to global information about an application environment This is an abstract class whose implementation is provided by the Android system It allows access to application-specific resources and classes, as well as up-calls for application-level operations such as launching activities, broadcasting and receiving intents, etc 从上可知一下三点,即: 1、它描述的是一个应用程序环境的信息,即上下文。 2、该类是一个抽象(abstract class)类,Android提供了该抽象类的具体实现类(后面我们会讲到是ContextIml类)。 3、通过它我们可以获取应用程序的资源和类,也包括一些应用级别 *** 作,例如:启动一个Activity,发送广播,接受Intent信息等。 Broadcast (广播)是一种广泛运用的应用程序之间传输信息的机制,而 BroadcastReceiver (广播接收器)则是用于接收来自系统和应用的广播对并对其进行响应的组件,Android中我们要发送的广播内容是一个 Intent ,这个 Intent 中可以携带我们要传送的数据 创建一个广播接收器非常简单,只需要继承 BroadcastReceiver ,并重写 onReceive() 即可 BroadcastReceiver 也是四大组件之一,所以我们也需要对 BroadcastReceiver 进行注册,不同于其他四大组件, BroadcastReceiver 有两种注册方式,分别是 静态注册 和 动态注册 静态注册 当我们的应用首次启动的时候,系统会自动实例化我们静态注册的 BroadcastReceiver ,然后将这个 BroadcastReceiver 注册到系统中,系统接收到广播之后,就会做出相应的判断,调用 onReceive() 方法。通过这种方式注册的广播,即使我们的应用被销毁,依然能收到广播。 这里要注意的是,我们的应用一定要被启动过 ,如果没有被启动可能就无法接收到广播,可以参考文章 Android应用在未启动的情况下无法收到指定广播的问题总结 正是因为静态注册耗电、占内存、不受程序生命周期影响,所以Google在Android 80上禁止大部分广播的静态注册,可以参考官文文档 Android 80 功能和 API 动态注册 通过动态注册的广播, BroadcastReceiver 的生命周期跟随Activity的生命周期 注意: 要在 Activity 的 onPause() 中 unRegeisterReceiver() ,否则会引起内存泄漏。比较推荐 onResume() 中去注册广播,在 onPause() 中去注销广播。因为在内存资源比较吃紧的情况下,可能我们的 Activity 执行完 onPause() 之后就被销毁,这时候 Activity 的 onStop() 和 onDestory() 方法就不会执行了 BroadcastReceiver注册完之后,这个 BroadcastReceiver 就能够接收响应的广播,下面我们来说说如何发送一条广播 普通广播(Normal Broadcast) 普通广播完全是异步的,通过 contextsendBroadcast() 方法发送,消息传递效率比较高,但所有接收器的执行顺序不确定。缺点是接收者不能将处理结果传递给下一个接收者,并且无法终止广播的传播 有序广播(Ordered Broadcast) 有序广播是通过 contextsendOrderedBroadcast() 方法发送,所有的广播者按照优先级依次执行,广播接收器的优先级通过 receiver 的 intent-filter 中的 android:priority 属性来设置,数值越大优先级越高。当广播接收器接收到广播后,可以使用 setResult() 方法把结果传递给下一个接收者,通过 getResult() 方法获取上一个接收者传递过来的结果,并可以通过 abortBroadcast() 方法丢弃该广播,使该广播不再传递给下一个接收者 粘性广播(Sticky Broadcast) 粘性广播通过 contextsendStickBroadcast() 方法来发送,用此方法发送的广播会一直滞留,当有匹配此广播的接收器被注册后,该广播接收器就会收到此广播。使用此广播时,需要获得 BROADCAST_STICKY 权限 由于在Android50 & API 21中已经失效,所以不建议使用。 本地广播(Local Broadcast) 前三种广播都是全局广播,所有应用都可以接收到,这样就带来安全隐患,而本地广播只在进程内传播,可以起到保护数据安全的作用 其实,本地广播的使用与其十分类似,之前的步骤均是一样的,调用者不同而已,本地广播调用的是 LocalBroadcastManager 相关方法,全局广播调用的是 Context 的相关方法,其方法名都是一样的 这里需要说一下, 使用本地广播并没有静态注册的方法 ,因为静态注册主要是为了让程序在未启动的情况下也能收到广播,而发动本地广播的时候,我们的程序已经是启动了,所以,自然是没有静态注册这个方法 Android中内置了多个系统广播,当使用系统广播时,只需要在注册广播接收者时定义相关的 action 即可,并不需要手动发送广播,当系统有相关 *** 作(如开机、网络状态变化、拍照等等)时会自动进行系统广播 Android系统广播 action 如下: 本文介绍了 BroadcastReceiver 的两种注册方式(动态注册、静态注册),四种发送方式(普通广播、有序广播、粘性广播(API21废弃)、本地广播),以及系统广播的用法。几乎涵盖了 BoradcastReceiver 在应用开发过程中的所有知识,对于BroadcastReceiver原理感兴趣的可以继续看 BroadcastReceiver详解(原理篇)
广播(Broadcast)机制用于进程/线程间通信,广播分为广播发送和广播接收两个过程,其中广播接收者BroadcastReceiver便是Android四大组件之一。
BroadcastReceiver分为两类:
从广播发送方式可分为三类:
广播在系统中以BroadcastRecord对象来记录, 该对象有几个时间相关的成员变量
广播注册,对于应用开发来说,往往是在Activity/Service中调用 registerReceiver() 方法,而Activity或Service都间接继承于Context抽象类,真正干活是交给ContextImpl类。另外调用getOuterContext()可获取最外层的调用者Activity或Service。
[ContextImpljava]
其中broadcastPermission拥有广播的权限控制,scheduler用于指定接收到广播时onRecive执行线程,当scheduler=null则默认代表在主线程中执行,这也是最常见的用法
[ContextImpljava]
ActivityManagerNativegetDefault()返回的是ActivityManagerProxy对象,简称AMP
该方法中参数有mMainThreadgetApplicationThread()返回的是ApplicationThread,这是Binder的Bn端,用于system_server进程与该进程的通信。
[-> LoadedApkjava]
不妨令 以BroadcastReceiver(广播接收者)为key,LoadedApkReceiverDispatcher(分发者)为value的ArrayMap 记为 A 。此处 mReceivers 是一个以 Context 为key,以 A 为value的ArrayMap。对于ReceiverDispatcher(广播分发者),当不存在时则创建一个。
此处mActivityThread便是前面传递过来的当前主线程的Handler
ReceiverDispatcher(广播分发者)有一个内部类 InnerReceiver ,该类继承于 IIntentReceiverStub 。显然,这是一个Binder服务端,广播分发者通过rdgetIIntentReceiver()可获取该Binder服务端对象 InnerReceiver ,用于Binder IPC通信。
[-> ActivityManagerNativejava]
这里有两个Binder服务端对象 caller 和 receiver ,都代表执行注册广播动作所在的进程 AMP通过Binder驱动将这些信息发送给system_server进程中的AMS对象,接下来进入AMSregisterReceiver。
[-> ActivityManagerServicejava]
其中 mRegisteredReceivers 记录着所有已注册的广播,以receiver IBinder为key, ReceiverList为value为HashMap。
在BroadcastQueue中有两个广播队列mParallelBroadcasts,mOrderedBroadcasts,数据类型都为ArrayList<broadcastrecord style="box-sizing: border-box;">:</broadcastrecord>
mLruProcesses数据类型为 ArrayList<ProcessRecord> ,而ProcessRecord对象有一个IApplicationThread字段,根据该字段查找出满足条件的ProcessRecord对象。
该方法用于匹配发起的Intent数据是否匹配成功,匹配项共有4项action, type, data, category,任何一项匹配不成功都会失败。
broadcastQueueForIntent(Intent intent)通过判断intentgetFlags()是否包含FLAG_RECEIVER_FOREGROUND 来决定是前台或后台广播,进而返回相应的广播队列mFgBroadcastQueue或者mBgBroadcastQueue。
注册广播:
另外,当注册的是Sticky广播:
广播注册完, 另一个 *** 作便是在广播发送过程
发送广播是在Activity或Service中调用 sendBroadcast() 方法,而Activity或Service都间接继承于Context抽象类,真正干活是交给ContextImpl类。
[ContextImpljava]
[-> ActivityManagerNativejava]
[-> ActivityManagerServicejava]
broadcastIntent()方法有两个布尔参数serialized和sticky来共同决定是普通广播,有序广播,还是Sticky广播,参数如下:
broadcastIntentLocked方法比较长,这里划分为8个部分来分别说明。
这个过程最重要的工作是:
BroadcastReceiver还有其他flag,位于Intentjava常量:
主要功能:
这个过主要处于系统相关的10类广播,这里不就展开讲解了
这个过程主要是将sticky广播增加到list,并放入mStickyBroadcasts里面。
其他说明:
AMScollectReceiverComponents :
广播队列中有一个成员变量 mParallelBroadcasts ,类型为ArrayList<broadcastrecord style="box-sizing: border-box;">,记录着所有的并行广播。</broadcastrecord>
动态注册的registeredReceivers,全部合并都receivers,再统一按串行方式处理。
广播队列中有一个成员变量 mOrderedBroadcasts ,类型为ArrayList<broadcastrecord style="box-sizing: border-box;">,记录着所有的有序广播。</broadcastrecord>
发送广播过程:
处理方式:
可见不管哪种广播方式,都是通过broadcastQueueForIntent()来根据intent的flag来判断前台队列或者后台队列,然后再调用对应广播队列的scheduleBroadcastsLocked方法来处理广播;
在发送广播过程中会执行 scheduleBroadcastsLocked 方法来处理相关的广播
[-> BroadcastQueuejava]
在BroadcastQueue对象创建时,mHandler=new BroadcastHandler(handlergetLooper());那么此处交由mHandler的handleMessage来处理:
由此可见BroadcastHandler采用的是”ActivityManager”线程的Looper
[-> BroadcastQueuejava]
此处mService为AMS,整个流程还是比较长的,全程持有AMS锁,所以广播效率低的情况下,直接会严重影响这个手机的性能与流畅度,这里应该考虑细化同步锁的粒度。
以上就是关于Android自定义控件CustomView,构造函数的参数context是怎么获取的全部的内容,包括:Android自定义控件CustomView,构造函数的参数context是怎么获取的、说说Android的广播(1)、netty channelgroup的广播信息,channel怎样获取等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)