android Context家族及详解

Android全面解析之Context机制 前言 很高兴遇见你~ 欢迎阅读我的文章。 在文章Android全面解析之由浅及深Handler消息机制中讨论到,Handler可以: 避免我们自己去手动写 死循环和输入阻塞 来不断获取用户的输入以及避免线程直接结束,而是采用事务驱动型设计,使用Handler消息机制,让AMS可以控制整个程序的运行逻辑。 这是关于android程序在设计上更加重要的一部分,不太了解的读者可以前往阅读了解一下。而当我们知道android程序的程序是通过main方法跑起来的,然后通过handler机制来控制程序的运行,那么四 阅读详情


Context大家太熟悉了,大家都会用。比如启动组件、获取系统服务、获取权限相关、包管理、类加载、文件管理和资源相关等功能。
这里先讲讲Context的类关系:

家族关系

开局一张图、内容全靠编:
在这里插入图片描述
其实Activity、Service、Application其实是亲戚关系。
其中Activity被包裹了一层ContextThemeWrapper,相比service多处理了关于Theme的。比如setTheme() initializeTheme()等方法。
ContextWrapper是一个包装类, ContextImpl是具体的实现类,比如startActivity,startService等方法的具体实现。所以这是一个典型的装饰者模式。

何时创建

根据前面所说,Context中所有实现均由ContextImpl来实现的,那么ContextImpl又何时创建的了?所以ContextWrapper中的Context引用指向的是一个ContextImpl实例。
知道android启动流程的,最后会走ActivityThread的main()方法,然后会初始化Looper巴拉巴拉之类的,然后会走到performLaunchActivity方法这里:

  private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");

        ActivityInfo aInfo = r.activityInfo;
        if (r.packageInfo == null) {
            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                    Context.CONTEXT_INCLUDE_CODE);
        }

        ComponentName component = r.intent.getComponent();
        if (component == null) {
            component = r.intent.resolveActivity(
                mInitialApplication.getPackageManager());
            r.intent.setComponent(component);
        }

        if (r.activityInfo.targetActivity != null) {
            component = new ComponentName(r.activityInfo.packageName,
                    r.activityInfo.targetActivity);
        }
		//获取上下文 关于activity的
        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
        try {
        	//反射创建activity
            java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to instantiate activity " + component
                    + ": " + e.toString(), e);
            }
        }

        try {
        	//创建Application,里面会创建Application的上下文
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);

            if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
            if (localLOGV) Slog.v(
                    TAG, r + ": app=" + app
                    + ", appName=" + app.getPackageName()
                    + ", pkg=" + r.packageInfo.getPackageName()
                    + ", comp=" + r.intent.getComponent().toShortString()
                    + ", dir=" + r.packageInfo.getAppDir());

            if (activity != null) {
                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                Configuration config = new Configuration(mCompatConfiguration);
                if (r.overrideConfig != null) {
                    config.updateFrom(r.overrideConfig);
                }
                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
                        + r.activityInfo.name + " with config " + config);
                Window window = null;
                if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
                    window = r.mPendingRemoveWindow;
                    r.mPendingRemoveWindow = null;
                    r.mPendingRemoveWindowManager = null;
                }
                appContext.setOuterContext(activity);
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback);

                if (customIntent != null) {
                    activity.mIntent = customIntent;
                }
                r.lastNonConfigurationInstances = null;
                checkAndBlockForNetworkAccess();
                activity.mStartedActivity = false;
                int theme = r.activityInfo.getThemeResource();
                if (theme != 0) {
                    activity.setTheme(theme);
                }

                activity.mCalled = false;
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
                if (!activity.mCalled) {
                    throw new SuperNotCalledException(
                        "Activity " + r.intent.getComponent().toShortString() +
                        " did not call through to super.onCreate()");
                }
                r.activity = activity;
                r.stopped = true;
                if (!r.activity.mFinished) {
                    activity.performStart();
                    r.stopped = false;
                }
                if (!r.activity.mFinished) {
                    if (r.isPersistable()) {
                        if (r.state != null || r.persistentState != null) {
                            mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
                                    r.persistentState);
                        }
                    } else if (r.state != null) {
                        mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
                    }
                }
                if (!r.activity.mFinished) {
                    activity.mCalled = false;
                    if (r.isPersistable()) {
                        mInstrumentation.callActivityOnPostCreate(activity, r.state,
                                r.persistentState);
                    } else {
                        mInstrumentation.callActivityOnPostCreate(activity, r.state);
                    }
                    if (!activity.mCalled) {
                        throw new SuperNotCalledException(
                            "Activity " + r.intent.getComponent().toShortString() +
                            " did not call through to super.onPostCreate()");
                    }
                }
            }
            r.paused = true;

            mActivities.put(r.token, r);

        } catch (SuperNotCalledException e) {
            throw e;

        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to start activity " + component
                    + ": " + e.toString(), e);
            }
        }

        return activity;
    }

Activity对应

截取上面Activity相关

	//创建ContextImpl
   ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
        try {
        	//反射创建activity,
            java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to instantiate activity " + component
                    + ": " + e.toString(), e);
            }
        }
        ...
         appContext.setOuterContext(activity);
         //这里也要看绑定attach()方法
        activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback);
		...

主要看createBaseContextForActivity方法,会创建ContextImpl

  private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
        final int displayId;
        try {
            displayId = ActivityManager.getService().getActivityDisplayId(r.token);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
		//ContextImpl 内部创建
        ContextImpl appContext = ContextImpl.createActivityContext(
                this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);

        final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
        // For debugging purposes, if the activity's package name contains the value of
        // the "debug.use-second-display" system property as a substring, then show
        // its content on a secondary display if there is one.
        String pkgName = SystemProperties.get("debug.second-display.pkg");
        if (pkgName != null && !pkgName.isEmpty()
                && r.packageInfo.mPackageName.contains(pkgName)) {
            for (int id : dm.getDisplayIds()) {
                if (id != Display.DEFAULT_DISPLAY) {
                    Display display =
                            dm.getCompatibleDisplay(id, appContext.getResources());
                    appContext = (ContextImpl) appContext.createDisplayContext(display);
                    break;
                }
            }
        }
        return appContext;
    }

通过构造器创建ContextImpl

static ContextImpl createActivityContext(ActivityThread mainThread,
            LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,
            Configuration overrideConfiguration) {
        if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
		...
        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName,
                activityToken, null, 0, classLoader);
		...
        return context;
    }

创建好ContextImpl了,再回到上面的,当中会执行Application的创建和绑定(后面会讲),看这行activity.attach() 方法:

 final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback) {
        ...    
        //这里的context 就是  ContextImpl
        attachBaseContext(context);
        ...
    }

先走Activity自身的attachBaseContext()方法

 @Override
    protected void attachBaseContext(Context newBase) {
    	//这里的父类 是 ContextThemeWrapper
        super.attachBaseContext(newBase);
        newBase.setAutofillClient(this);
    }

跟进ContextThemeWrapper中的attachBaseContext()方法:

 @Override
    protected void attachBaseContext(Context newBase) {
    	//这里的父类是 ContextWrapper
        super.attachBaseContext(newBase);
    }

跟进ContextWrapper中的attachBaseContext()方法:

 protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }

到这了,已经将ContextImpl的引用传递到了ContextWrapper中,而mBase就是这个引用,我们使用的类似startActivity()等方法最终是由ContextImpl中执行的,比如:

  @Override
    public void startActivity(Intent intent) {
        mBase.startActivity(intent);
    }

Application对应

在回到上面performLaunchActivity方法的代码,这里把Application相关抽取下来:

	//其实就一行哈哈
	...
  Application app = r.packageInfo.makeApplication(false, mInstrumentation);
	...

看Application的创建, 在LoadedApk的makeApplication()方法:

  public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
      ...
	  Application app = null
	  //创建ContextImpl
      ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
     //这里由仪表盘创建Application 
      app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
      appContext.setOuterContext(app);
      ...
      return app;
    }

先看ContextImpl的创建:

   static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
        if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0,
                null);
        context.setResources(packageInfo.getResources());
        return context;
    }

还要接着看Instrumentation的newApplication()方法:

//Instrumentation中newApplication
  public Application newApplication(ClassLoader cl, String className, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
            //反射创建Application
        Application app = getFactory(context.getPackageName())
                .instantiateApplication(cl, className);
        //这里走了Application的attach()方法了
        app.attach(context);
        return app;
    }
//AppComponentFactory中instantiateApplication
 public @NonNull Application instantiateApplication(@NonNull ClassLoader cl,
            @NonNull String className)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        return (Application) cl.loadClass(className).newInstance();
    }

看到上面attach()方法,传入的是ContextImpl的引用,看Application的attach

 /* package */ final void attach(Context context) {
 		//自己没实现这个方法,由父类实现了 ,就是ContextWrapper
        attachBaseContext(context);
        mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
    }

到这里又跟上面Activity的绑定一样了:

 protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }

到这了,已经将ContextImpl的引用传递到了ContextWrapper中,而mBase就是这个引用,完成了与Application的相关了。

Service对应

具体的Service启动流程不说了,最后还是会走到ActivityThread 的 handleCreateService() 方法中:

   private void handleCreateService(CreateServiceData data) {
        // If we are getting ready to gc after going to the background, well
        // we are back active so skip it.
        unscheduleGcIdler();

        LoadedApk packageInfo = getPackageInfoNoCheck(
                data.info.applicationInfo, data.compatInfo);
        Service service = null;
        try {
            java.lang.ClassLoader cl = packageInfo.getClassLoader();
            service = (Service) cl.loadClass(data.info.name).newInstance();
        } catch (Exception e) {
            if (!mInstrumentation.onException(service, e)) {
                throw new RuntimeException(
                    "Unable to instantiate service " + data.info.name
                    + ": " + e.toString(), e);
            }
        }

        try {
            if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
			//创建ContextImpl
            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
            context.setOuterContext(service);

            Application app = packageInfo.makeApplication(false, mInstrumentation);
            //绑定方法
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManager.getService());
            service.onCreate();
            mServices.put(data.token, service);
            try {
                ActivityManager.getService().serviceDoneExecuting(
                        data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(service, e)) {
                throw new RuntimeException(
                    "Unable to create service " + data.info.name
                    + ": " + e.toString(), e);
            }
        }
    }

这里创建ContextImpl就不看了,主要看service.attach()方法:

 public final void attach(
            Context context,
            ActivityThread thread, String className, IBinder token,
            Application application, Object activityManager) {
         //自己没有实现,调用父类方法 ContextWrapper
        attachBaseContext(context);
        mThread = thread;           // NOTE:  unused - remove?
        mClassName = className;
        mToken = token;
        mApplication = application;
        mActivityManager = (IActivityManager)activityManager;
        mStartCompatibility = getApplicationInfo().targetSdkVersion
                < Build.VERSION_CODES.ECLAIR;
    }

到这了,已经将ContextImpl的引用传递到了ContextWrapper中,而mBase就是这个引用,完成了与Service的相关了。

面试点

一个应用包含多少个Context个数?

看了前面的分析得到:
Context个数 = Activity个数 + Application(只有一个) + Service个数

Application能启动一个Activity嘛?

要区分来看,对于不同android版本也不同。
在android 9.0 执行如下代码:

Intent intent=new Intent();
intent.setClass(context, LoginActivity.class);
//intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);

这里会报错的:

 ("Calling startActivity() from outside of an Activity "
                            + " context requires the FLAG_ACTIVITY_NEW_TASK flag."
                            + " Is this really what you want?")

但是对于android 6,android 7,android 8 却不会报错,而对比源码如下

 @Override
    public void startActivity(Intent intent, Bundle options) {
        warnIfCallingFromSystemProcess();

        // Calling start activity from outside an activity without FLAG_ACTIVITY_NEW_TASK is
        // generally not allowed, except if the caller specifies the task id the activity should
        // be launched in.
        if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0
                && options != null && ActivityOptions.fromBundle(options).getLaunchTaskId() == -1) {
            throw new AndroidRuntimeException(
                    "Calling startActivity() from outside of an Activity "
                    + " context requires the FLAG_ACTIVITY_NEW_TASK flag."
                    + " Is this really what you want?");
        }
        mMainThread.getInstrumentation().execStartActivity(
                getOuterContext(), mMainThread.getApplicationThread(), null,
                (Activity) null, intent, -1, options);
    }

由于options为空,直接跳过if判断,所以不会报错。
对于9.0代码如下:

@Override
    public void startActivity(Intent intent, Bundle options) {
        warnIfCallingFromSystemProcess();

        // Calling start activity from outside an activity without FLAG_ACTIVITY_NEW_TASK is
        // generally not allowed, except if the caller specifies the task id the activity should
        // be launched in. A bug was existed between N and O-MR1 which allowed this to work. We
        // maintain this for backwards compatibility.
        final int targetSdkVersion = getApplicationInfo().targetSdkVersion;

        if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == 0
                && (targetSdkVersion < Build.VERSION_CODES.N
                        || targetSdkVersion >= Build.VERSION_CODES.P)
                && (options == null
                        || ActivityOptions.fromBundle(options).getLaunchTaskId() == -1)) {
            throw new AndroidRuntimeException(
                    "Calling startActivity() from outside of an Activity "
                            + " context requires the FLAG_ACTIVITY_NEW_TASK flag."
                            + " Is this really what you want?");
        }
        mMainThread.getInstrumentation().execStartActivity(
                getOuterContext(), mMainThread.getApplicationThread(), null,
                (Activity) null, intent, -1, options);
    }

多判断了ActivityOptions.fromBundle(options).getLaunchTaskId() == -1这个,条件还是会成立的,就会抛出报错信息。

综上所述:对于版本不同,还要不同处理,针对版本判断,或者统一加上FLAG_ACTIVITY_NEW_TASK标签,但是这会新增一个task。所以真正解决问题的是:万不得已,不要用Application去启动一个Activity。

附图一张,抄来的:
数字1:启动Activity在这些类中是可以的,但是可能需要创建一个新的task。一般情况不推荐。
数字2:在这些类中去layout inflate是合法的,但是会使用系统默认的主题样式,如果你自定义了某些样式可能不会被使用。
数字3:在receiver为null时允许,在4.2或以上的版本中,用于获取黏性广播的当前值。(可以无视)
在这里插入图片描述

Context为何会发生泄漏,怎么解决?

单例里引用一个Context,而且这个Context刚好是Activity所对应的。当Activity销毁,但是单例中的Context还持有Activity的引用,就会造成内存泄漏。
解决:换成Application所对于的Context就行了,自身生命周期同引用一样,不存在泄漏。

ContentProvider的中的Context那来的?

这里由事关android的启动流程了,下次再讲这个流程,还是挺复杂的。只需知道ActivityThread的attach()方法会一步步调用到handleBindApplication方法:

 private void handleBindApplication(AppBindData data) {
            app = data.info.makeApplication(data.restrictedBackupMode, null);
            // Propagate autofill compat state
            app.setAutofillCompatibilityEnabled(data.autofillCompatibilityEnabled);
            //这里就是Application 
            mInitialApplication = app;

            // don't bring up providers in restricted mode; they may depend on the
            // app's custom Application class
            if (!data.restrictedBackupMode) {
                if (!ArrayUtils.isEmpty(data.providers)) {
                	//传入的是Application
                    installContentProviders(app, data.providers);
                    // For process that contains content providers, we want to
                    // ensure that the JIT is enabled "at some point".
                    mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
                }
            }    
    }

所以ContentProvider 的 Context 是在 Applicaiton 创建之后,被ContentProvider再利用了。

总结

其实所有的这些,只需弄懂android的启动流程,再看这些就不说很复杂了。源码的设计模式运用也非常优雅,值得多看。

从 Context 家谱来聊一聊 Context 家族使用的设计模式? Context Context 被翻译成上下文,是应用程序环境的全局信息接口,它是一个抽象类,其实现由 Android 系统提供的 ContextImpl 类。我们通过 Context 可以非常方便的访问应用程序的资源和类,以及可以进行应用程序级的各种操作,比如启动 Activity 和 Service、注册和发送广播、权限操作、主题操作、存储对象操作等等。它简直是一个万能类!难怪我们需要经常用到它。 Context 家族 Context 是一个抽象类,它的具体实现是在子类中进行的,它们共同构成了 Con 阅读详情

相关推荐

基于PWM的汽车仪表步进电机微步驱动优化实践

本文深入探讨了基于PWM的微步驱动技术,用于优化汽车仪表步进电机的运行性能。通过将传统的分步驱动升级为PWM微步细分驱动,利用正弦/余弦查表法合成旋转磁场,有效消除了指针抖动与噪声,实现了指针的平滑、精准运动。文章以STM32和VID2905电机为例,提供了详细的硬件连接、代码实现及高级优化技巧,为提升汽车仪表体验提供了实用方案。

weixin_29169505的博客 512

Android之LayoutInflater的解析过程和使用方法

获取LayoutInflater 获取LayoutInflater的方法也有三种 LayoutInflater inflater1 = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE) LayoutInflater inflater2 = LayoutInflater.from(this); LayoutInflater inflater3 = getLayoutInflater(); LayoutInflater的解析

jignjingfan的博客 781

人工智能100问☞第5问:什么是AI三要素?

人工智能的核心三要素是​​数据、算法和算力​​,分别作为AI的“燃料”、“大脑”和“动力”,三者协同驱动智能系统的学习、推理与决策能力。

bjdx_001的博客 1995

Android ContextThemeWrapper应用

前言在Android obtainStyledAttributes获取属性值(注:在后文中称此文为前文)文中最后的例子处,提出了一个问题——如何对单个元素设置Theme而不影响到整个Activity。该问题的实际意义是对于有些属性我们只能从Theme中获取而不能通过内联的方式嵌入在单个元素标签内(具体分析可参见前文),因此要想修改这个属性值,只能在Activity使用的Theme中指定。于是,该修改

FengShanChuiChui的博客 4947

Glue)《Multi-omics single-cell data integration and regulatory inference with graph-linked embedding》

论文引入GLUE框架,通过知识引导图(guidance graph)显式建模跨层调控交互,桥接特征空间,实现准确、鲁棒、可扩展的整合。近年来,单细胞测序技术(如scATAC-seq用于染色质可及性、snmC-seq用于DNA甲基化、scRNA-seq用于转录组)允许同时探测多个组学层,揭示细胞异质性和调控机制。GLUE优于现有方法:桥接特征空间,避免信息丢失,支持多组学和调控推断。用于整合未配对的单细胞多组学数据(如scRNA-seq、scATAC-seq和snmC-seq),并同时进行调控交互推断。

m0_58293236的博客 1152

WSL2下用QEMU模拟ARM开发板:从编译uboot到调试全流程(附常见错误解决)

本文详细介绍了在WSL2环境下使用QEMU模拟ARM开发板的完整流程,包括uboot编译、内核调试等关键步骤。通过QEMU模拟vexpress-a9开发板,开发者可以高效搭建嵌入式开发环境,解决常见错误并提升开发效率。

solidity8miner的博客 286

Android的Context详解 - 揭开Context的神秘面纱

这篇文章是基于我四年前的一篇文章进行更正和深入探究。背景是,2019年4月份我在找工作,看到一个问题,问this,getBaseContext()、getApplication()、getApplicationContext()的区别。当时我写了简单的demo验证,得出了跟网上答案一致的结论。但就在昨天,我发现,这个问题或许还有其他的答案。

zhaojun的博客 1902

STM32芯片解锁读写保护:更改Option Byte [Error已解决]

解锁读保护:STLink更改Option Byte 变Level从1变为0,点击Apply即可。(程序执行读保护,其实是将Level从0变为1) 默认情况下该保护功能是不开启的,若要开启该功能,需要改写内部FLASH选项字节(Option Bytes)中的配置。 RDP读保护级别 级别0,无保护, 级别1,使能读保护, 这种保护下,若使用调试功能(使用下载器、仿真器)或者从内部SRAM自举时都不能对内部FLASH及备份SRAM作任何访问(读写、擦除都被禁止); 当芯片处于级别1的时候,可以把选项字节

HowieXue 薛永浩的博客 2万+

Android Context简介

Context在整个系统里的核心表现形式如下图: 简单来讲,Context是一个抽象类,定义了一系列的抽象方法,它的具体实现由ContextImpl来完成。ContextWrapper虽然也继承了Context,但是它的方法的具体实现是由mBase(即ContextImpl实例)来完成的,故ContextWrapper也是一个代理类。Application、Service和ContextThemeWrapper都是继承于ContextWrapper。ContextThemeWrapper与ContextW

P_edestrian的专栏 413

【25届数字IC秋招总结】面试经验13——豪威科技面试

25届数字ic面试记录———豪威科技

lalala0635的博客 363

高频注入是否需要对齐和强拖操作?

高频注入技术显著优化了传统FOC启动流程,但仍需部分对齐操作。传统启动必须包含位置检测、对齐和强拖等步骤,而高频注入可实现零速闭环,简化或跳过部分环节。高频注入下的对齐主要用于验证有效性、初始化观测器和建立初始磁链,形式可大幅简化。虽然高频注入基本消除了传统强拖需求,但在重载、弱凸极等特殊情况下仍需辅助。实际应用中可采用自适应混合策略,根据负载和电机类型选择最优启动方案。高频注入使对齐和强拖从"必须"变为"可选优化项",是无传感器控制的重要进步。

汽车软件开发 205

Android应用Context详解及源码解析,android游戏开发入门视频

/** Proxying implementation of Context that simply delegates all of its calls to another Context. Can be subclassed to modify behavior without changing the original Context. */ public class ContextWrapper extends Context { Context mBase; public Co

m0_65146205的博客 2309

Gitlab通过Webhook自动触发Jenkins构建任务并在更新完毕后发送钉钉消息通知

Gitlab通过Webhook自动触发Jenkins构建任务并在更新完毕后发送钉钉消息通知 文章目录Gitlab通过Webhook自动触发Jenkins构建任务并在更新完毕后发送钉钉消息通知1.实现思路2.新增钉钉机器人并测试消息的发送2.1.新增钉钉机器人2.2.测试发送消息到钉钉3.在Jenkins中新建一个构建任务3.1.新建任务3.2.配置任务时增加参数化构建过程3.3.配置任务添加git地址3.4.配置构建触发器生成gitlab webhook使用的token3.5.配置任务构建过程中执行的She

江晓龙的博客 3809

Android 插件化开发——基础底层知识(Context家族史,Activity启动流程)

通过上一篇Binder以及AIDL的学习,博客最后写了实际上Activity与底层通信模式可以看成是AIDL。 不了解Binder以及AIDL的,移步: Android 插件化开发——基础底层知识(Binder,AIDL) 这篇博客接着为插件化开发作准备,讲述一下Context家族史以及Activity启动流程 Context家族史 关于Context,相信并不陌生,但是估计很多初学者都不...

lmq121210的博客 584

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

Android全面解析之Context机制(一) :初识Android context

回想一下最初学习Android开发的时候,第一用到context是什么时候?如果你跟我一样是通过郭霖的《第一行代码》来入门android,那么一般是Toast。Toast.makeText(this, "我是toast", Toast.LENGTH_SHORT).show()当初也不知道什么是Context,只知道他需要一个context类型,把activity对象传进去即可。从此context贯穿在我开发过程的方方面面,但我始终不知道这个context到底有什么用?为什么要这个对象?/***/

hirezy 958

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

Android开发中Context家族:this、getContext()、getApplicationContext()等核心方法的选择与避坑指南

本文深入解析Android开发中Context家族的核心方法,包括this、getContext()、getApplicationContext()等的正确使用场景与选择策略。重点探讨如何避免常见的内存泄漏和空指针异常问题,提供实用的避坑指南,帮助开发者编写更健壮的Android应用代码。掌握这些Context的使用技巧对提升应用稳定性至关重要。

kotlin6android的博客 396

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

Android中Context用法详解

Android中Context用法详解   本文我们一起来探讨一下关于Android中Context的作用以及Context的详细用法,这对我们学习Android的资源访问有很大的帮助,文章中也贴出了一些关于Android Context使用的示例代码,非常不错,以下是原文:   Context基本概念   Context是什么?   1) Context是一个抽象类,其通用实

连长的博客 3268

源码分析 --- Context

1.1 Context是什么?     1) Context是一个抽象类,其通用实现在ContextImpl类中。     2) Context:是一个访问application环境全局信息的接口,通过它可以访问application的资源和相关的类,其主要功能如下:         • 启动Activity         • 启动和停止Service         • 发送广播消息

u010852160的博客 361

Android -- Context

Context基本概念 Context是什么? 1) Con...

weixin_30745641的博客 71

Android 常用的Context详解

1.Context概述 Context是一个抽象类,其通用实现在ContextImpl类中。它的主要作用是一个访问application环境全局信息的接口,通过它可以访问application的资源和相关的类,其主要功能如下: 启动Activity 启动和停止Service 发送广播 消息 注册广播消息接受者 访问APK中各种资源 访问Package的相关信息 APK的各...

fanleiym的专栏 388

深入理解 Android 中的各种 Context

转自:https://juejin.im/post/5c1fab7d5188254eb05fbe48 Context 家族 Context 本身是一个抽象类,主要实现类为 ContextImpl,另外有子类 ContextWrapper 和 ContextThemeWrapper,这两个子类都是 Context 的代理类,主要区别是 ContextThemeWrapper 有自己的主题资源。它...

想飞的企鹅 548
上一篇: Android OkHttp 全面详解
下一篇: android CrashHandler处理异常
HUandroid
博客等级 码龄10年 35粉丝 113原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值