Android自定义控件CustomView,构造函数的参数context是怎么获取的

Android自定义控件CustomView,构造函数的参数context是怎么获取的,第1张

很明显,这个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怎样获取等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/web/9668648.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-04-30
下一篇2023-04-30

发表评论

登录后才能评论

评论列表(0条)