刚刚学android或者js等,都会看见这个频繁的字眼——Context。
意为”上下文“。
本文主要记述,Context到底是什么、如何理解Context、一个APP可以有几个Context、Context能干啥、Context的作用域、获取Context、全局获取Context技巧。
Java:万物皆对象。Flutter:万物皆组件。
俗语:”没对象吗?自己new一个啊~“
既然大多数情况可以new一个实例,那么,我们在android中的Activity实例怎么获取呢?Activity.instance可以获取activity。既然Activity也大致归属于一个类,那么可不可以用 Activity activity=new Activity(); 呢?安卓不像Java程序一样,随便创建一个类,写个main()方法就能运行,**Android应用模型是基于组件的应用设计模式,组件的运行要有一个完整的Android工程环境。在这个环境下,Activity、Service等系统组件才能正常工作,而这些组件不能采用普通的java对象创建方式,new一下是不能创建实例的,而是要有它们各自的上下文环境,也就是Context.
所以说,Context是维持android各组件能够正常工作的一个核心功能类。
(本图为沙拉查词给出的中文翻译)
有点晦涩难懂。但在程序中,我们可理解为当前对象在程序中所处的一个环境,一个与系统交互的过程。 比如QQ和你们自己的女朋友聊天时(没有grilfriend的可自己跳过举例),此时的context是指的聊天界面以及相关的数据请求与传输,Context在加载资源、启动Activity、获取系统服务、创建View等操作都要参与。
所以,一个Activity就是一个Context(getActivity()==getContext),一个Service也是一个Context。Android把场景抽象为Context类,用户和操作系统的每一次交互都是一个场景,比如:打电话、发短信等,都有activity,还有一些我们肉眼看不见的后台服务。一个应用程序可以认为是一个工作环境,用户在这个环境中切换到不同的场景,这就像服务员,客户可能是外卖小哥、也可能是农民工等,这些就是不同的场景,而服务员就是一个应用程序。
Context理解为”上下文“/”场景“,可能还是很抽象。那么我们可以做一个比喻:
一个APP是仙剑奇侠传3电视剧,Activity、Service、BroadcastReceiver、ContentProvider这四大组件就是电视剧的主角。它们是导演(系统)一开始就确定好试镜成功的人。换言之, 不是我们每个人都能被导演认可的。有了演员,就要有镜头啊,这个镜头便是(Context)。通过镜头,我们才能看见帅气 的胡歌。演员们都是在镜头(Context环境)下表演的。那么Button这些组件子类型就是配角,它们没有那么重要,随便一个组件都能参与演出(即随便new 一个实例),但是它们也需要参与镜头,不然一部戏只有主角多没意思,魔尊重楼还是要的,魔尊也要露面(工作在Context环境下),所以可以用代码new Button();或者xml布局定义一个button。
打开AndroidStudio,输入Context,然后ctrl+鼠标左键追朔其源码(看源码一般都先看注释便于理解): import android.content.Context;
看注释,TMD,是English,那么笔者这里就用小学生英语水平来翻译一哈哈:
Context提供了关于应用环境全局信息的接口。它是一个abstract类,它的执行被Android系统提供,允许获取以应用为特征的资源和类型,是一个统领一些资源APP环境变量等的上下文。通过它可以获取应用程序的资源和类(包括应用级别操作,如启动Activity,发广播,接收intent等)。abstract会有它的实现类。在源码中,我们可以通过AndroidStudio去查看它的子类,得到以下关系:
它有2个具体实现子类:ContextImpl、ContextWrapper。
关键在于对COntext的理解。从上面提到的实现子类可以看出,在APP中,Context的具体实现子类是Acitivity、Service、Applicaiton。所以Context's number=Activity's number + Service's number+1(1个APP只有一个Application)。为啥不是4大组件,上面不是说四大组件也是主角吗?看看BroadcastReceiver和ContentProvider的源码可以知道它们并不是Context的子类,它们持有的Context都是其他地方传递过去的(比如我们发送广播intent中的context就是外部传递过来的),所以不计数它们。
Context哪里会用到它。刚开始了解Android的时候不知道它是个啥玩意儿,但是久了发现有些地方就不得不传这个参数。
比如Toast、启动Activity、启动Service、发送广播、操作数据库等等都需要传Context参数,具体例子就不说了。详细可以看后文将提到的如何获取它。
不是随便获取一个Context实例就可以的,它的使用有一些规则和限制。因为Context的具体实例是由ContextImpl类去实现的,因此,Activity、Service、Application3种类型的Context都是等价的。但是,需要注意的是,,有些场景,比如启动Activity、弹出Dialog等。为了安全,Android不允许Activity或者Dialog凭空出现,一个Activity的启动肯定是由另一个Activity负责的,也就是以此形成的返回栈(具体可以看看任主席的《Android开发艺术探索》)而Dialog则必须是在一个Activity上弹出(系统Alert类型的Dialog除外),这种情况下, 我们只能用Activity类型的Context,否则报错。
Context作用域 | Application | Activity | Service |
---|---|---|---|
Show a Dialog | No | Yes | No |
Start an Activity | 不推荐 | Yes | 不推荐 |
Layout Inflation | 不推荐 | Yes | 不推荐 |
Start a Service | Yes | Yes | Yes |
Send a Broadcast | Yes | Yes | Yes |
Register Broadcast Receiver | Yes | Yes | Yes |
Load Resource Values | Yes | Yes | Yes |
Activity继承自ContextThemeWrapper,而Application和Service继承ContextWrapper,所以ContextThemeWrapper在ContextWrapper的基础上作了一些操作,使得Activity更加厉害。
1.如果用ApplicationContext去启动一个LaunchMode为standard的Activity的时候会报错: androud,util.AndroidRuntimeException:Calling startActivity from outside of an Activity context require the FLAG_ACTIVITY_NEW_TASK flag。Is this really what you want?
翻译一下,并了解这个FLAG的都知道,此时的非Activity类型的Context并没有所谓的返回栈,因此带启动的Activity就找不到栈。它还给我们明确之处了FLAG的解决办法,这样启动的时候就为它创建一个新的任务栈,而此时Activity是以Single Task模式启动的。所以这种用Application Context启动Activity的方式不推荐,Service同理。
2.在Application和Service中去layout inflate也是合法的,但是会使用系统默认的主题样式,如果自定义了某些样式可能不会被使用,所以也不推荐。
注:和UI相关的,都应该使用Activity Context来处理。其他的一些操作,Service、Activity、Application等实例都是可以的。同时要注意Context的引用持有,防止内存泄漏。可在被销毁的时候,置Context为null。
1.View.getContext():返回当前View对象的Context对象。通常是当前正在展示的Activity对象。
1.Activity,getApplicationContext()[后文会详细介绍这个方法]:获取当前Activity所在应用进程的Context对象,通常我们使用3.Context对象时,要优先考虑这个全局的进程Context。
ContextWrapper.getBaseContext():用来获取一个ContextWrapper进行装饰之前的Context。实际开发很少用,也不建议使用。
4.Activity.this:返回当前Activity的实例,如果的UI控件需要使用Activity作为Context对象,但默认的Toast实际上使用的ApplicationContext也可以。
实现View.OnClick监听方法中,写Toast,不要用this,因为this,在onClick(View view)指的是view对象而不是Activity实例,所以在这个方法中,应该使用”当前的Activity名.this“,这是入门者比较容易混淆的地方。
getApplication()和getApplicationContext():
获取当前Application对象用getApplicationContext.但是getApplication又是什么。
我们可以自己写代码打印一下:
Application app=(Application)getApplication(); Log.e(TAG,"getApplication is "+app); Context context=getApplicationContext(); Log.e(TAG,"getApplicationContext is "+ context);
运行后看logcat,效果图就不贴了(电脑卡)。从打印结果可以看出它们2个的内存地址是相同的,即它们是同一个对象。 因为Application本来就是一个Context,那么这里获取的getApplicationContext()自然也是Application本身的实例了。那这2个相同方法存在的意义是啥?(双胞胎?)实际上这2个方法在作用域上有比较大的区别。 getApplication()一看就知道是用来获取Application实例的(道理可以联想getActivity())。但getApplication()只有在Activity和Service中才能调用的到。 对于比如BroadcastReceiver等中也想要获取Application实例,这时就需要getApplicationContext()方法。
//继承BroadcastReceiver并重写onReceive()方法 @Override public void onReceive(Context context.Intent intent){ Application app=(Application)context.getApplicationContext(); }
我们经常会遇到内存泄漏,比如Activity销毁了,但是Context还持有该Activity的引用,造成了内存泄漏。(经常遇到)
2种典型的错误引用方式:
public class Singleton{ private static Singleton instancel private Context context; private Singleton(Context context){ this.context=context; } public static Singleton getInstance(Context context){ if(instance == null ){ instance=new Singleton(context); } return instance; } }
熟悉单例模式的都知道,这是一个非线程安全的单例模式,instance作为静态对象,其生命周期要长于普通的对象(单例直到APP退出后台才销毁),其中也包含了Activity。比如Activity A去getInstance()得到instance对象,传入this,常驻内存的Singleton保存了我们传入的A对象,并一直持有,即使Activity被销毁掉,但因为它的引用还存在于一个Singleton中,就不可能被GC掉,这样就导致了内存泄漏。比如典型的数据库操作,存储数据,需要重复的去索取数据,用单例保持数据和拿到Activity持有context引用,因为单例可以看作是上帝,它帮我们保存数据。所以即使Activity被finish掉,还有它的引用在Singleton中。
public class MainActivity extend Activity{ private static Drawable mDrawable; @Override protected void onCreate(Bundle saveInstanceState){ super.onCreate(); setContentView(R.layout.activity_main); ImageView imageview=new ImageView(this);//通过代码动态的创建组件,而不是传统的xml配置组件,这里的ImageView持有当前Activity的引用。 mDrawable=getResources().getDrawable(R.drawable.ic_launcher); imageview.setImageDrawable(mDrawable); } }
上述代码中,有一个static的Drawable对象。当ImageView设置这个Drawable的时候,ImageView保存了这个mDrawable的引用,而ImageView初始化的时候又传入了this,此处的this是指MainActivity的context。因为被static修饰的mDrawable是常驻内存的(比类还要早加载)。MainActivity是它的间接引用了,当MainActivity被销毁的时候,也不能被GC掉,就造成了内存泄漏。
大量的地方都需要使用Context,我们常常会因为不知道怎么得到这个Context而苦恼。那么,全局获取Context无疑是最好的解决方案。
很多时候,我们也不是经常为得不到Context而发愁,毕竟我们很多的操作都是在活动中进行的,而活动本身就是一个Context对象。但APP架构复杂后,很多逻辑代码都脱离了Activity类,此时又需要使用Context,所以我们需要采取全局获取Context的方法。
举例, 我们平常经常会写网络工具类,比如下面的这些代码:
public calss HttpUtil{ public static void sendHttpRequest(final String address,final HttpCallbackListener listener){ new Thread(new Runnable()){ @Override public void run(){ HttpURLConnection connection=null; try{ URL url =new URL(address); connection=(HttpURLConnection)url.openConnection(); connection.setRequestMethod("GET"); connection.setConnectTimeout(8000); connection.setReadTimeout(8000); connection.setDoInput(true); connection.setDoOutput(true); InputStream in =connection.getInputStream(); BufferedReader reader=new BufferedReader(new InputStreamReader(in)); StringBuilder response=new StringBuilder(); String line; while((line=reader.readLine())!=nulll){ response.append(line); } if(listener!=null){ //回调onFinish() listener.onFinish(response.toString); } }catch(Execption e){ if(listener!=null){ //回调onError() listener.onError(e); } }finally{ if(connection!=null){ connection.disconnect(); } } }}.start(); } }
上述代码中使用sendHttpRequest()方法来发送HTTP请求显然没问题。并且还可以在回调方法中处理服务器返回的数据。但是这个方法还可以被优化。当检测不到网络存在的时候就给用户一个Toast,并不再执行后面的代码。问题来了,Toast需要一个Context参数,但是在本来没有可以传递的Context对象。。。
一般思路:在方法中添加一个COntext参数:
public static void sendHttpRequest(final String address,final HttpCallbackListener listener,final Context context){ if(!isNetWorkAvailable()){ Toast.makeText(context,……); …… } ……
看似可以,但是有点甩锅。我们将获取Context的任务转移到了sendHttpRequest()方法的调用方。至于调用方能不能得到COntext对象就不是我们要考虑的问题了。
甩锅不一定是通用的解决方案。于是这里介绍哈如何获取全局Context的步骤:,通过它在项目的任何地方都能轻松的获取到Context。:
Android提供了一个Application类,每当APP启动的时候,系统就会自动将这个类进行初始化。我们可以定制一个自己的Application类,以便管理程序内一些全局的状态信息,比如说全局Context。
定制一个自己的Application并不复杂,首先, 需要创建一个MyApplication类继承自系统的Application:
public calss MyApplication extends Application{ private static Context context; @Overrride public void onCreate(){ context=getApplicationContext(); } public static Context getContext(){ return context; } }
代码很简单,容易理解。重写了父类的onCreate()方法,并通过调用getApplicationContext()方法得到一个应用程序级别的Context,然后又提供了一个静态的getContext()方法,在这里将刚才获取到的COntext进行返回。
接下来,我们需要告诉系统,当程序启动的时候应该初始化MyApplication类,而不是系统默认的Application类。这一步需要在清单文件里面实现,找到清单文件的<application>标签下进行指定就可以了:
<manifest …… ……> <application android :name="com.example.myContext.MyApplication" //这里输入.MyApplication也可以,或者输入MyApplication根据AS提示自动补全包名 ..> </application>
注意:这里一定要加上完整的包名,不然系统将无法找到这个类。
以上就是实现了一种全局获取Context的机制,在这个项目的任何地方使用Context,只需要调用MyApplication.getContext()就可以了。
自定义需要在清单文件写出android.name="……"。而为了让LitePal可以正常工作,也需要在清单文件下,配置:
android:name="org.litepal.LitePalApplication"
道理也是一样的,这样配置后,LitePal就能在内部自动获取到Context了。
问题:当都已经配置过自定义的Application怎么办?岂不是和LitePalApplication冲突了?
解答:任何一个项目都只能配置一个Application. 对于这种情况,LitePalApplication给出了很简单的解决方案,在自定义的Application中去调用LitePal的初始化方法就可以了:
public calss MyApplication extends Application{ private static Context context; @Overrride public void onCreate(){ context=getApplicationContext(); LitePalApplication.initialize(context); } public static Context getContext(){ return context; } }
这种写法就相当于我们把全局Context对象通过参数传递给了LitePal,效果和在清单文件配置LitePalApplication是一样的。
一般Context造成的内存泄漏,几乎都是当Context销毁的时候,因为被引用导致销毁失败。而Application的Context对象可以简单的理解为伴随着进程存在的(它的生命周期也很长,毕竟APP加载的时候先加载Application,我们可以自定义Application然后继承系统的Application)。
正确使用:
当Applicatin的Context能搞定的情况下,并且生命周期长的对象,优先使用Application的Context;
不要让生命周期长于Activity的对象持有Activity的引用。
尽量不要在Activity中使用非静态内部类。非静态内部类会隐式持有外部类实例的引用。如果使用静态内部类,将外部实例引用作为弱引用持有。
ActivityThread是主进程的入口,它的currentApplication返回值是application.
import android.app.Application; import java.lang.reflect.InvocationTargetException; /** * 这种方式获取全局的Application 是一种拓展思路。 * <p> * 对于组件化项目,不可能把项目实际的Application下沉到Base,而且各个module也不需要知道Application真实名字 * <p> * 这种一次反射就能获取全局Application对象的方式相比于在Application#OnCreate保存一份的方式显示更加通用了 */ public class AppGlobals { private static Application sApplication; public static Application getApplication() { if (sApplication == null) { try { sApplication = (Application) Class.forName("android.app.ActivityThread") .getMethod("currentApplication") .invoke(null, (Object[]) null); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } return sApplication; } }
到此这篇关于Context的详细介绍和实例分析的文章就介绍到这了,更多相关Context的详细介绍内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!
时间:2020-07-14
概念 代表整个web应用,可以和程序的容器(服务器)来通信 获取 1.通过request对象获取 request.getServletContext(); 2.通过HttpServlet获取 this.getServletContext(); 功能 1.获取MIME类型: MIME类型: 在互联网通信过程中定义的一种文件数据类型 格式: 大类型/小类型 text/html image/jpeg 获取: String getMimeType(String file) 2.域对象:共享数据 Serv
前言 今天介绍一下大家常见的一个单词 context 应该怎么去理解,正确的理解它有助于我们学习 spring 以及计算机系统中的其他知识. 1. context 是什么 我们经常在编程中见到 context 这个单词,当然每个人有每个人的理解,它被理解为:上下文.容器等等.我想说的是,context 理解为上下文最为合适.为什么呢?我以一个在计算机系统的例子来解释一下. 在计算机系统中,进程执行时有进程上下文,如果进程在执行的过程中遇到了中断,CPU 会从用户态切换为内核态(当然这个过程用户进
Hi,大家好,我是明哥. 在自己学习 Golang 的这段时间里,我写了详细的学习笔记放在我的个人微信公众号 <Go编程时光>,对于 Go 语言,我也算是个初学者,因此写的东西应该会比较适合刚接触的同学,如果你也是刚学习 Go 语言,不防关注一下,一起学习,一起成长. 我的在线博客:http://golang.iswbm.com 我的 Github:github.com/iswbm/GolangCodingTime 1. 什么是 Context? 在 Go 1.7 版本之前,context 还
这篇文章主要介绍了Java基于ServletContextListener实现UDP监听,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 使用spring boot实现项目启动时的监听, UDPListener import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.DatagramPacket; import java.ne
ServletContext对象: ServletContext类似字节码文件对象,在web创建的时候就自动生成了,并且是唯一的,跟随着项目和服务器共存亡了.通过这个对象,我们可以向里面存数据(键值对),也可以通过别的Servlet来获取这个数据:也可以根据相对(服务器)路径继来获取绝对路径. ServletContext代表是一个web应用的环境(上下文)对象,ServletContext对象内部封装是该web应用的信息,ServletContext对象一个web应用只有一个. 一个web应用
这篇文章主要介绍了JavaWeb pageContext对象原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一.JSP九大内置对象 1.out:用来向客户端发送文本数据 2.config:相当Servlet中的ServletConfig 3.page:当前JSP页面对象,相当在页面中的this,不过this能用的方法更多 4.pageContext:JSP的上下文对象,一般说到上下对象立马想到四个字:共享数据,没错这个就是JSP四大域中
RequestContextHolder的作用是: 在Service层获取获取request和response信息 代码示例: ServletRequestAttributes attrs = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attrs.getRequest(); 源码分析: 定义了两个ThreadLocal变量用来存储Reque
jquery是js的类库,js本身不能操作header,因为js是在浏览器加载页面过程中才开始执行的header需要服务器端执行操作 如果是ajax,是可以设置header $.ajax({ url: "", data: {}, type: "GET", beforeSend: function(xhr){xhr.setRequestHeader('X-Test-Header', 'test-value');},//这里设置header success: funct
express中常见的路由规则 主要使用的路由规则是get和post两种,即 var express = require('express'); var app = express(); app.get(); // get和post两种请求方式 app.post(); app.get()和app.post()的第一个参数为请求路径,第二个参数为处理请求的回调函数:回调函数有两个参数,分别为req和res,代表请求信息和响应信息. 获取请求路径和请求体中的各种参数 路径请求及对应获取请求路径的形式
使用方法 mysql>show table status; mysql>show table status like 'esf_seller_history'/G; mysql>show table status like 'esf_%'/G; 样例: mysql>show table status like 'esf_seller_history'/G; 1.Name 表名称 2.Engine: 表的存储引擎 3.Version: 版本 4.Row_format 行格式.对于My
本文实例讲述了Android开发获取系统中已安装程序信息的方法.分享给大家供大家参考,具体如下: public class AppInfoParser { private static String tag = "AppInfoParser"; public static List<AppInfo> getAppInfos(Context context){ //首先获取到包的管理者 PackageManager packageManager = context.getPa
本文实例讲述了Vue 实现从文件中获取文本信息的方法.分享给大家供大家参考,具体如下: 最近在使用vue做项目的时候,遇到一个需求,界面中需要显示大量的说明文字,为了保持界面的整洁和赶紧,决定采用单独的文件来存储显示信息,然后通过文件读取的方式显示到界面上. 刚开始我使用的是File和FileReader对象获取,但是比较气人的是这两个对象是IE浏览器特有的属性,chrome不支持,而且为了安全起见,现在浏览器是不推崇这种做法的,因为很容易造成文件被外部恶意删除或增加内容,安全性太低.无奈之下,
本文实例讲述了PHP版微信第三方实现一键登录及获取用户信息的方法.分享给大家供大家参考,具体如下: 注意,要使用微信在第三方网页登录是需要"服务号"才可以哦,所以必须到官方申请. 一开始你需要进入微信公众平台开启开发模式,并且填写oauth2的回调地址,地址填写你项目的域名就可以了.比如:www.baidu.com或zhidao.baidu.com.如果你的项目在二级域名就写二级域名 前端url授权地址,在url中填写appid与你项目中方法中的oauth的地址,具体在下面的代码中可以
本文实例为大家分享了SpringMVC中controller接收json数据的方法,供大家参考,具体内容如下 1.jsp页面发送ajax的post请求: function postJson(){ var json = {"username" : "imp", "password" : "123456"}; $.ajax({ type : "post", url : "<%=basePath
本文实例讲述了Android实现获取SERIAL信息的方法.分享给大家供大家参考.具体如下: android.os.build.serial 在API 9 中引入,如果低于9的系统可以通过这个方法获取这个信息值 java代码如下: public static String getDeviceSerial() { String serial = "unknown"; try { Class clazz = Class.forName("android.os.Build"
Zuul是Netflix出品的一个基于JVM路由和服务端的负载均衡器. Zuul功能: 认证 压力测试 金丝雀测试 动态路由 负载削减 安全 静态响应处理 主动/主动交换管理 Zuul的规则引擎允许通过任何JVM语言来编写规则和过滤器, 支持基于Java和Groovy的构建. 配置属性 zuul.max.host.connections 已经被两个新的配置属性替代, zuul.host.maxTotalConnections (总连接数)和 zuul.host.maxPerRouteConnec
本文实例讲述了Go语言扫描目录并获取相关信息的方法.分享给大家供大家参考.具体分析如下: 前言:最近看到Go里面有一个func很容易就可以扫描整个目录,并且可以得到相应的目录和文件信息,所以我将其进行了封装,拿到file info的所有信息 这样就可以方便的做其它用途了. 直接上代码,代码基于Go version 1 复制代码 代码如下: package main import ( "path/filepath" "os" "fla