Context知识详解

建议配合context知识架构图食用。

一、什么是Context

贴一个官方解释:

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.

上面的意思:context是一个应用程序环境的全局信息的接口。这是一个抽象类,其实现由Android系统提供。它允许访问特定于应用程序的资源和类,以及对应用程序级操作(如启动活动,广播和接收意图等)的调用。

这个解释可能听起来比较抽象,我的理解是一些Android组件(如activity、service)的运行需要一定的“环境”,就好像我们工作一般都是在办公室 ,休息则是在家里,我们都是处在一定的“环境”下去工作、学习、休息的,Android组件也是类似,它们不能脱离“环境”去运转,而这个“环境”在Android中就是context。

二、Context子类以及其继承关系

先贴个图

由图我们可以看出context有两个子类ContextImpl和ContextWrapper。

ContextWrapper

我们先来看下ContextWrapper。

  1. */***
  2. * * Proxying implementation of Context that simply delegates all of its calls to*
  3. * * another Context. Can be subclassed to modify behavior without changing*
  4. * * the original Context.*
  5. * */*
  6. public class ContextWrapper extends Context {
  7. Context mBase;
  8. public ContextWrapper(Context base) {
  9. mBase = base;
  10. }
  11. */***
  12. * * Set the base context for this ContextWrapper. All calls will then be*
  13. * * delegated to the base context. Throws*
  14. * * IllegalStateException if a base context has already been set.*
  15. * * *
  16. * ****@param***base The new base context for this wrapper.*
  17. * */*
  18. protected void attachBaseContext(Context base) {
  19. if (mBase != null) {
  20. throw new IllegalStateException(“Base context already set”);
  21. }
  22. mBase = base;
  23. }
  24. */***
  25. * ****@return***the base context as set by the constructor or setBaseContext*
  26. * */*
  27. public Context getBaseContext() {
  28. return mBase;
  29. }
  30. @Override
  31. public AssetManager getAssets() {
  32. return mBase.getAssets();
  33. }
  34. @Override
  35. public Resources getResources() {
  36. return mBase.getResources();
  37. }
  38. @Override
  39. public PackageManager getPackageManager() {
  40. return mBase.getPackageManager();
  41. }
  42. @Override
  43. public ContentResolver getContentResolver() {
  44. return mBase.getContentResolver();
  45. }
  46. @Override
  47. public Looper getMainLooper() {
  48. return mBase.getMainLooper();
  49. }
  50. @Override
  51. public Context getApplicationContext() {
  52. return mBase.getApplicationContext();
  53. }
  54. @Override
  55. public void setTheme(int resid) {
  56. mBase.setTheme(resid);
  57. }
  58. */*****@hide****/*
  59. @Override
  60. public int getThemeResId() {
  61. return mBase.getThemeResId();
  62. }
  63. @Override
  64. public Resources.Theme getTheme() {
  65. return mBase.getTheme();
  66. }
  67. @Override
  68. public void startActivity(Intent intent) {
  69. mBase.startActivity(intent);
  70. }
  71. @Override
  72. public void sendBroadcast(Intent intent) {
  73. mBase.sendBroadcast(intent);
  74. }
  75. //...
  76. }

该类直接继承自Context,并实现了Context定义的抽象方法。不过我们看源码发现其实它并未实质的去实现Context定义的操作只是通过mBase调用对应的方法去执行。这个mBase也是一个Context类型的变量,它的赋值是通过attachBaseContext赋值的。我们还知道service和application都是ContextWrapper子类,所以service和application都是Context。

  1. public abstract class Service extends ContextWrapper implements ComponentCallbacks2 {
  2. //...
  3. }
  4. public class Application extends ContextWrapper implements ComponentCallbacks2 {
  5. //...
  6. }

ContextWrapper还有一个子类ContextThemeWrapper。

  1. public class ContextThemeWrapper extends ContextWrapper {
  2. private int mThemeResource;
  3. private Resources.Theme mTheme;
  4. private LayoutInflater mInflater;
  5. public ContextThemeWrapper(Context base, @StyleRes int themeResId) {
  6. super(base);
  7. mThemeResource = themeResId;
  8. }
  9. public ContextThemeWrapper(Context base, Resources.Theme theme) {
  10. super(base);
  11. mTheme = theme;
  12. }
  13. @Override
  14. public Resources getResources() {
  15. return getResourcesInternal();
  16. }
  17. private Resources getResourcesInternal() {
  18. if (mResources == null) {
  19. if (mOverrideConfiguration == null) {
  20. mResources = super.getResources();
  21. } else if (Build.VERSION.SDK_INT >= 17) {
  22. final Context resContext = createConfigurationContext(mOverrideConfiguration);
  23. mResources = resContext.getResources();
  24. }
  25. }
  26. return mResources;
  27. }
  28. @Override
  29. public void setTheme(int resid) {
  30. if (mThemeResource != resid) {
  31. mThemeResource = resid;
  32. initializeTheme();
  33. }
  34. }
  35. public int getThemeResId() {
  36. return mThemeResource;
  37. }
  38. @Override
  39. public Resources.Theme getTheme() {
  40. if (mTheme != null) {
  41. return mTheme;
  42. }
  43. if (mThemeResource == 0) {
  44. mThemeResource = R.style.Theme_AppCompat_Light;
  45. }
  46. initializeTheme();
  47. return mTheme;
  48. }
  49. private void initializeTheme() {
  50. final boolean first = mTheme == null;
  51. if (first) {
  52. mTheme = getResources().newTheme();
  53. Resources.Theme theme = getBaseContext().getTheme();
  54. if (theme != null) {
  55. mTheme.setTo(theme);
  56. }
  57. }
  58. onApplyThemeResource(mTheme, mThemeResource, first);
  59. }
  60. //...
  61. }

可以看出ContextThemeWrapper主要是包含了主题Theme相关的接口,即android:theme属性指定的。而activity则是继承自ContextThemeWrapper。

  1. public class Activity extends ContextThemeWrapper
  2. implements LayoutInflater.Factory2,
  3. Window.Callback, KeyEvent.Callback,
  4. OnCreateContextMenuListener, ComponentCallbacks2,
  5. Window.OnWindowDismissedCallback {
  6. //...
  7. }

ContextImpl

由ContextWrapper源码我们知道实际上它并没有实现Context定义的相关操作。那么Context的真实实现类到底是谁呢 答案就是ContextImpl。它是Android系统提供的唯一的Context真实 实现类。

  1. class ContextImpl extends Context {
  2. @Override
  3. public void startActivity(Intent intent) {
  4. warnIfCallingFromSystemProcess();
  5. startActivity(intent, null);
  6. }
  7. @Override
  8. public void sendBroadcast(Intent intent) {
  9. warnIfCallingFromSystemProcess();
  10. String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
  11. try {
  12. intent.prepareToLeaveProcess(this);
  13. ActivityManager.getService().broadcastIntent(
  14. mMainThread.getApplicationThread(), intent, resolvedType, null,
  15. Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
  16. getUserId());
  17. } catch (RemoteException e) {
  18. throw e.rethrowFromSystemServer();
  19. }
  20. }
  21. @Override
  22. public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
  23. return registerReceiver(receiver, filter, null, null);
  24. }
  25. @Override
  26. public ComponentName startService(Intent service) {
  27. warnIfCallingFromSystemProcess();
  28. return startServiceCommon(service, false, mUser);
  29. }
  30. //...
  31. }

由源码看出ContextImpl确是是真实的实现了Context。

三、一个应用Context个数

通过上面Context子类继承关系的分析,一个应用Context个数显而易见。

APP Context总数 = Application(1) + Activity个数+ Service个数;

四、不同的Context之间差异

我们知道Application的生命周期跟应用的生命周期是相同的,所以Application的Context生命周期与应用程序完全相同。同理

Activity或者Service的Context与他们各自类生命周期相同。

由此可知Context使用不当会引起内存泄漏,我们在使用Context时必须要注意其生命周期。

  • 尽量使用 Application 的 Context

  • 不要让生命周期长于 Activity 的对象持有其的引用

  • 尽量不要在 Activity 中使用非静态内部类,因为非静态内部类会隐式持有外部类示例的引用,如果使用静态内部类,将外部实例引用作为弱引用持有。

五、不同Context的应用场景

大家注意看到有一些NO上添加了一些数字,其实这些从能力上来说是YES,但是为什么说是NO呢?下面一个一个解释:

数字1:启动Activity在这些类中是可以的,但是需要创建一个新的task。一般情况不推荐。

数字2:在这些类中去layout inflate是合法的,但是会使用系统默认的主题样式,如果你自定义了某些样式可能不会被使用。

数字3:在receiver为null时允许,在4.2或以上的版本中,用于获取黏性广播的当前值。(可以无视)

注:ContentProvider、BroadcastReceiver之所以在上述表格中,是因为在其内部方法中都有一个context用于使用。

以上参考https://blog.csdn.net/lmj623565791/article/details/40481055

由表格我们可以归纳出这样一个结论:操作涉及UI的应该使用Activity做为Context,不涉及UI的Service,Activity,Application等实例都可以。

六、不同Context实例化过程

Activity 中Context实例化过程

在Activity的启动过程中,activity的创建是在ActivityThread.

performLaunchActivity方法中完成的。

  1. //ActivityThread.java
  2. private Activity performLaunchActivity(ActivityClientRecord r,Intent customIntent){
  3. //...
  4. ContextImpl appContext=createBaseContextForActivity(r);//1、创建ContextImpl实例
  5. Activity activity=null;
  6. try{
  7. java.lang.ClassLoader cl=appContext.getClassLoader();
  8. //...
  9. activity=mInstrumentation.newActivity(
  10. cl,component.getClassName(),r.intent);//2、创建Activity
  11. StrictMode.incrementExpectedActivityCount(activity.getClass());
  12. r.intent.setExtrasClassLoader(cl);
  13. r.intent.prepareToEnterProcess();
  14. if(r.state!=null){
  15. r.state.setClassLoader(cl);
  16. }
  17. }catch(Exception e){
  18. if(!mInstrumentation.onException(activity,e)){
  19. throw new RuntimeException(
  20. "Unable to instantiate activity "+component
  21. +": "+e.toString(),e);
  22. }
  23. }
  24. try{
  25. Application app=r.packageInfo.makeApplication(false,mInstrumentation);
  26. if(activity!=null){
  27. appContext.setOuterContext(activity);//3、调用setOuterContext
  28. activity.attach(appContext,this,getInstrumentation(),r.token,
  29. r.ident,app,r.intent,r.activityInfo,title,r.parent,
  30. r.embeddedID,r.lastNonConfigurationInstances,config,
  31. r.referrer,r.voiceInteractor,window,r.configCallback);//4、调用attach
  32. }
  33. //...
  34. }

首先通过createBaseContextForActivity创建ContextImpl实例,那我们看下具体是如何创建的

  1. private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
  2. final int displayId;
  3. try {
  4. displayId = ActivityManager.getService().getActivityDisplayId(r.token);
  5. } catch (RemoteException e) {
  6. throw e.rethrowFromSystemServer();
  7. }
  8. ContextImpl appContext = ContextImpl.createActivityContext(
  9. this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
  10. //...
  11. return appContext;
  12. }

可以看出是调用createActivityContext,那来看下createActivityContext

  1. static ContextImpl createActivityContext(ActivityThread mainThread,
  2. LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,
  3. Configuration overrideConfiguration) {
  4. //...
  5. ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName,
  6. activityToken, null, 0, classLoader);
  7. //...
  8. context.setResources(resourcesManager.createBaseActivityResources(activityToken,
  9. packageInfo.getResDir(),
  10. splitDirs,
  11. packageInfo.getOverlayDirs(),
  12. packageInfo.getApplicationInfo().sharedLibraryFiles,
  13. displayId,
  14. overrideConfiguration,
  15. compatInfo,
  16. classLoader));
  17. context.mDisplay = resourcesManager.getAdjustedDisplay(displayId,
  18. context.getResources());
  19. return context;
  20. }

可以看到是调用了ContextImpl得一个构造函数创建的ContextImpl实例然后还给该实例设置了setResources,至此ContextImpl创建完成。但是我们注意到在创建了ContextImpl实例(appContext)之后又调用了setOuterContext

并把当前activity传入,这又是为什么呢? 看下源码

  1. private Context mOuterContext;
  2. final void setOuterContext(Context context) {
  3. mOuterContext = context;
  4. }

setOuterContext只是简单的把传入的activity赋值给了mOuterContext,这是ContextImpl类中定义的一个变量。通过这个操作ContextImpl就可以持有activity的引用。

setOuterContext之后又调用了activity.attach并把appContext传入。

  1. final void attach(Context context, ActivityThread aThread,
  2. Instrumentation instr, IBinder token, int ident,
  3. Application application, Intent intent, ActivityInfo info,
  4. CharSequence title, Activity parent, String id,
  5. NonConfigurationInstances lastNonConfigurationInstances,
  6. Configuration config, String referrer, IVoiceInteractor voiceInteractor,
  7. Window window, ActivityConfigCallback activityConfigCallback) {
  8. attachBaseContext(context);
  9. //...
  10. }
  1. //Activity.java
  2. protected void attachBaseContext(Context newBase) {
  3. super.attachBaseContext(newBase);
  4. newBase.setAutofillClient(this);
  5. }

Activity的attach我们只关注跟context有关的 那就是调用attachBaseContext,在这个函数内部调用了super.attachBaseContext。我们知道Activity继承自 ContextThemeWrapper, ContextThemeWrapper

继承自 ContextWrapper,所以最终会调用ContextWrapper.attachBaseContext,到这里,ContextWrapper类就可以将它的功能交给ContextImpl类来具体实现。

  1. //ContextWrapper.java
  2. protected void attachBaseContext(Context base) {
  3. if (mBase != null) {
  4. throw new IllegalStateException("Base context already set");
  5. }
  6. mBase = base;
  7. }

Service中Context实例化过程

  1. private void handleCreateService(CreateServiceData data){
  2. //...
  3. Service service=null;
  4. try{
  5. java.lang.ClassLoader cl=packageInfo.getClassLoader();
  6. service=(Service)cl.loadClass(data.info.name).newInstance();//1、创建service
  7. }catch(Exception e){
  8. if(!mInstrumentation.onException(service,e)){
  9. throw new RuntimeException(
  10. "Unable to instantiate service "+data.info.name
  11. +": "+e.toString(),e);
  12. }
  13. }
  14. try{
  15. if(localLOGV)Slog.v(TAG,"Creating service "+data.info.name);
  16. ContextImpl context=ContextImpl.createAppContext(this,packageInfo);//2、创建ContextImpl实例
  17. context.setOuterContext(service);//3、设置OuterContext
  18. Application app=packageInfo.makeApplication(false,mInstrumentation);
  19. service.attach(context,this,data.info.name,data.token,app,
  20. ActivityManager.getService()); //4、调用attach
  21. service.onCreate();
  22. mServices.put(data.token,service);
  23. try{
  24. ActivityManager.getService().serviceDoneExecuting(
  25. data.token,SERVICE_DONE_EXECUTING_ANON,0,0);
  26. }catch(RemoteException e){
  27. throw e.rethrowFromSystemServer();
  28. }
  29. }catch(Exception e){
  30. if(!mInstrumentation.onException(service,e)){
  31. throw new RuntimeException(
  32. "Unable to create service "+data.info.name
  33. +": "+e.toString(),e);
  34. }
  35. }
  36. }

我们看到Service中Context实例的创建流程跟Activity基本是一样的,首先创建Service实例然后创建ContextImpl实例,之后调用setOuterContext最后是attach。

Service中ContextImpl实例是通过函数createAppContext创建的,其内部则是通过ContextImpl的构造函数来创建实例。

  1. static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
  2. if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
  3. ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0,
  4. null);
  5. context.setResources(packageInfo.getResources());
  6. return context;
  7. }

setOuterContext操作跟Activity是一样的都是把引用赋值给mOuterContext。

最后就是attch了,下面是service的attach,可以看到它也是调用attachBaseContext,下面的流程跟Activity是一样的最终都是ContextWrapper类将它的功能交给ContextImpl类来具体实现。

  1. public final void attach(
  2. Context context,
  3. ActivityThread thread, String className, IBinder token,
  4. Application application, Object activityManager) {
  5. attachBaseContext(context);//调用attachBaseContext
  6. mThread = thread; // NOTE: unused - remove?
  7. mClassName = className;
  8. mToken = token;
  9. mApplication = application;
  10. mActivityManager = (IActivityManager)activityManager;
  11. mStartCompatibility = getApplicationInfo().targetSdkVersion
  12. < Build.VERSION_CODES.ECLAIR;
  13. }

Application中的Context的实例化过程

Application 的创建是在LoadedApk.makeApplication中。

  1. //LoadedApk.Java
  2. public Application makeApplication(boolean forceDefaultAppClass,
  3. Instrumentation instrumentation) {
  4. if (mApplication != null) {
  5. return mApplication;
  6. }
  7. //...
  8. Application app = null;
  9. try {
  10. java.lang.ClassLoader cl = getClassLoader();
  11. if (!mPackageName.equals("android")) {
  12. Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
  13. "initializeJavaContextClassLoader");
  14. initializeJavaContextClassLoader();
  15. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
  16. }
  17. ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);//1、创建ContextImpl实例
  18. app = mActivityThread.mInstrumentation.newApplication(
  19. cl, appClass, appContext);//2、创建application
  20. appContext.setOuterContext(app);//3、设置mOuterContext
  21. } catch (Exception e) {
  22. if (!mActivityThread.mInstrumentation.onException(app, e)) {
  23. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
  24. throw new RuntimeException(
  25. "Unable to instantiate application " + appClass
  26. + ": " + e.toString(), e);
  27. }
  28. }
  29. mActivityThread.mAllApplications.add(app);
  30. mApplication = app;
  31. //...
  32. }

可以看到Application中是先创建了ContextImpl实例然后创建Application实例最后调用了setOuterContext。看上去跟Service和Activity相比缺少了attach,而我们知道attach是ContextWrapper类将它的功能交给ContextImpl类来具体实现的过程,Application缺少attach那它是如何实现ContextWrapper的代理过程的呢? 其实Application是有attach的 它在newApplication创建Application的过程中调用的。

  1. public Application newApplication(ClassLoader cl, String className, Context context)
  2. throws InstantiationException, IllegalAccessException,
  3. ClassNotFoundException {
  4. return newApplication(cl.loadClass(className), context);
  5. }
  6. static public Application newApplication(Class<?> clazz, Context context)
  7. throws InstantiationException, IllegalAccessException,
  8. ClassNotFoundException {
  9. Application app = (Application)clazz.newInstance();
  10. app.attach(context);//调用application的attach方法
  11. return app;
  12. }
  13. final void attach(Context context) {
  14. attachBaseContext(context); //调用attachBaseContext
  15. mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
  16. }

嗯,这样看application和Service还有Activity的流程基本上是一致的。

至此Application、Service、Activity中context的实例化过程都已分析完了。

七、其他

无侵入式获取全局Context

使用一个ContentProvider,ContentProvider的onCreate()方法调用时,调用getContext()即可获取到Context,再静态变量保存,后续直接获取即可。

  1. public class AppContextProvider extends ContentProvider {
  2. static Context mContext;
  3. @Override
  4. public boolean onCreate() {
  5. //mContext保存为静态变量
  6. mContext = getContext();
  7. return false;
  8. }
  9. //...
  10. }
  11. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  12. package="com.app.contextprovider">
  13. <application>
  14. <!-- 全局Context提供者 -->
  15. <provider
  16. android:name=".AppContextProvider"
  17. android:authorities="${applicationId}.contextprovider"
  18. android:exported="false" />
  19. </application>
  20. </manifest>

getApplication和getApplicationContext的区别

首先来看getApplication方法,它只有在Activity和Service中有实现

  1. Activity
  2. /** Return the application that owns this activity. */
  3. public final Application getApplication() {
  4. return mApplication;
  5. }
  6. Service
  7. /** Return the application that owns this service. */
  8. public final Application getApplication() {
  9. return mApplication;
  10. }

Activity和Service中getApplication返回的是一个application对象。

getApplicationContext是ContextWrapper提供的方法,由源码可知它调用的是mBase的getApplicationContext()。此处的mBase实际是一个ContextImpl,所以我们看下ContextImpl的getApplicationContext(),可以看到返回的是mPackageInfo.getApplication()(此处的mPackageInfo包含当前应用的包信息、比如包名、应用的安装目录等信息,一般不为空)。

  1. //ContextWrapper
  2. public Context getApplicationContext() {
  3. return mBase.getApplicationContext();
  4. }
  5. //ContextImpl
  6. public Context getApplicationContext() {
  7. return (mPackageInfo != null) ?
  8. mPackageInfo.getApplication() : mMainThread.getApplication();
  9. }

我们知道一个应用只有一个Application所以getApplication和getApplicationContext 实际上都是返回当前应用的Application,它们是同一个对象。这两个函数的区别就是getApplication只能在Activity和Service中调用,而getApplicationContext 的使用范围则要大一些,比如在广播中想要获取全局的Context则需要使用getApplicationContext 而不是getApplication。

以上就是Context相关知识点的整理解析。

本文所有源码基于Android-8.0.0_r1

Android

Context知识详解的更多相关文章

  1. Intent知识详解

    Intent知识详解 一.什么是Intent 贴一个官方解释: An intent is an abstract description of an operation to be performed ...

  2. RabbitMQ基础知识详解

    什么是MQ? MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法.MQ是消费-生产者模型的一个典型的代表,一端往消息队列中不断写入消息,而另一端则可以读取队列中 ...

  3. <context:component-scan>详解 转发 https://www.cnblogs.com/fightingcoding/p/component-scan.html

    <context:component-scan>详解   默认情况下,<context:component-scan>查找使用构造型(stereotype)注解所标注的类,如@ ...

  4. Cisco路由技术基础知识详解

    第一部分 请写出568A的线序(接触网络第一天就应该会的,只要你掐过,想都能想出来) .网卡MAC地址长度是(  )个二进制位(16进制与2进制的换算关系,只是换种方式问,不用你拿笔去算) A.12  ...

  5. L009文件属性知识详解小节

    本堂课分为5部分内容 1.linux下重要目录详解 2.PATH变量路径内容 3.linux系统中文件类型介绍 4.linux系统中文件属性详细介绍 5.linux系统文件属性inode与block知 ...

  6. RabbitMQ,Apache的ActiveMQ,阿里RocketMQ,Kafka,ZeroMQ,MetaMQ,Redis也可实现消息队列,RabbitMQ的应用场景以及基本原理介绍,RabbitMQ基础知识详解,RabbitMQ布曙

    消息队列及常见消息队列介绍 2017-10-10 09:35操作系统/客户端/人脸识别 一.消息队列(MQ)概述 消息队列(Message Queue),是分布式系统中重要的组件,其通用的使用场景可以 ...

  7. 浏览器对象模型(BOM)是什么?(体系结构+知识详解)(图片:结构)

    浏览器对象模型(BOM)是什么?(体系结构+知识详解)(图片:结构) 一.总结 1.BOM操作所有和浏览器相关的东西:网页文档dom,历史记录,浏览器屏幕,浏览器信息,文档的地址url,页面的框架集. ...

  8. Golang Context 包详解

    Golang Context 包详解 0. 引言 在 Go 语言编写的服务器程序中,服务器通常要为每个 HTTP 请求创建一个 goroutine 以并发地处理业务.同时,这个 goroutine 也 ...

  9. Python字符串切片操作知识详解

    Python字符串切片操作知识详解 这篇文章主要介绍了Python中字符串切片操作 的相关资料,需要的朋友可以参考下 一:取字符串中第几个字符 print "Hello"[0] 表 ...

随机推荐

  1. #在windows上使用ngix重定向目录访问远程服务器文件详细实例

    为了在开发环境保持于生产环境相同的访问远程服务器文件资源的目录配置,需要在开发环境(windows)在远程文件服务器使用nignx重定向文件目录,因为网上的资料大都是copy的,解释比较笼统,也没有具 ...

  2. 华为OSPF与ACL综合应用实例讲解

    OSPF与ACL综合应用实例讲解 项目案例要求: 1.企业内网运行OSPF路由协议,区域规划如图所示:2.财务和研发所在的区域不受其他区域链路不稳定性影响:3.R1.R2.R3只允许被IT登录管理:4 ...

  3. Idea 2016 激活码

    43B4A73YYJ-eyJsaWNlbnNlSWQiOiI0M0I0QTczWVlKIiwibGljZW5zZWVOYW1lIjoibGFuIHl1IiwiYXNzaWduZWVOYW1lIjoiI ...

  4. Unity3D for iOS初级教程:Part 3/3(下)

    转自:http://www.cnblogs.com/alongu3d/archive/2013/06/01/3111739.html 消息不会自动消除 你基本的游戏功能已经完成了,但是显示一些关于游戏 ...

  5. 【JS】308- 深入理解ESLint

    点击上方"前端自习课"关注,学习起来~ 本文来自于"自然醒"投稿至[前端早读课]. 小沈是一个刚刚开始工作的前端实习生,第一次进行团队开发,难免有些紧张.在导师 ...

  6. 【JS】307- 复习 Object.assign 原理及其实现

    点击上方"前端自习课"关注,学习起来~ }let b = {    name: "muyiy",    book: {        title: " ...

  7. java_冒泡排序

    public static void main(String[] args){ int[] arr= {321, 43, 45, 76, 8, 6, 9, 1, 3, 63, 43}; for(int ...

  8. 使用CleanWebpackPlugin插件报错原因:CleanWebpackPlugin is not a constructor

    // webpack版本:4.32.2 // 抛错原写法 const CleanWebpackPlugin = require("clean-webpack-plugin"); . ...

  9. 跟着文档学习gulp1.1安装入门

    Step1:检查是否已经安装了node,npm 和 npX是否正确安装 Step2:安装gulp命令行工具(全局安装gulp) npm install --global gulp-cli Step3: ...

  10. CCF-CSP题解 201903-2 二十四点

    可枚举. 写栈的主要思想是:一个数栈\(numSta\),一个运算符栈\(opSta\).遇到一个运算符,就把之前优先级\(equal\ or\ greater\ than\)它的运算符处理掉. #i ...