安卓Context上下文

目录

  • 前言
  • 一、Context简介
  • 二、Application Context
    • 2.1 Application Context的创建过程
    • 2.2 Application Context的获取过程
  • 三、Activity的Context创建过程
  • 四、Service的Context创建过程


前言

Context也就是上下文对象,是Android较为常用的类,但是对于Context,大多都停留在会用的阶段,本文会从源码角度来分析Context,从而更加深入的理解它。

一、Context简介

Context意为上下文或者场景,是一个应用程序环境信息的接口。
在开发中我们经常会使用Context,它的使用场景总的来说分为两大类,它们分别是:

  • 使用Context调用方法,比如:启动Activity、访问资源、调用系统级服务等。
  • 调用方法时传入Context,比如:弹出Toast、创建Dialog等。
    Activity、Service和Application都是间接的继承自Context的,因此,可以计算出一个应用程序进程中有多少个Context,这个数量等于Activity和Service的总个数加1,1指的是Application的数量。

Context是一个抽象类,它的内部定义了很多方法以及静态常量,它的具体实现类为ContextImpl。和Context相关联的类,除了ContextImpl还有ContextWrapper、ContextThemeWrapper和Activity等等,下面给出Context的关系图。
在这里插入图片描述

从图中我们可以看出,ContextImpl和ContextWrapper继承自Context,ContextThemeWrapper、Service和Application继承自ContextWrapper。ContextWrapper和ContextThemeWrapper都是Context的包装类,它们都含有Context类型的mBase对象,mBase具体指向的是ContextImpl,这样通过ContextWrapper和ContextThemeWrapper也可以使用Context的方法。ContextThemeWrapper中包含和主题相关的方法(比如: getTheme方法),因此,需要主题的Activity继承ContextThemeWrapper,而不需要主题的Service则继承ContextWrapper。


二、Application Context

2.1 Application Context的创建过程

我们通过调用getApplicationContext来获取应用程序的全局的Application Context,那么Application Context是如何创建的呢?
当一个应用程序启动完成后,应用程序就会有一个全局的Application Context。那么我们就从应用程序启动过程开始着手。

ActivityThread作为应用程序进程的核心类,它会调用它的内部类ApplicationThread的scheduleLaunchActivity方法来启动Activity,如下所示。

frameworks/base/core/java/android/app/ActivityThread.java

    private class ApplicationThread extends ApplicationThreadNative {
        
     ...
       @Override
        public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
                ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
                CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
                int procState, Bundle state, PersistableBundle persistentState,
                List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
                boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
                updateProcessState(procState, false);
                ActivityClientRecord r = new ActivityClientRecord();
                ...
                sendMessage(H.LAUNCH_ACTIVITY, r);
        }
     ...   
    }    

在ApplicationThread的scheduleLaunchActivity方法中向H类发送LAUNCH_ACTIVITY类型的消息,目的是将启动Activity的逻辑放在主线程中的消息队列中,这样启动Activity的逻辑会在主线程中执行。我们接着查看H类的handleMessage方法对LAUNCH_ACTIVITY类型的消息的处理。

frameworks/base/core/java/android/app/ActivityThread.java

private class H extends Handler {
        
      public static final int LAUNCH_ACTIVITY         = 100;
...
public void handleMessage(Message msg) {
          if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
          switch (msg.what) {
              case LAUNCH_ACTIVITY: {
                  Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                  final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
                  r.packageInfo = getPackageInfoNoCheck(
                          r.activityInfo.applicationInfo, r.compatInfo);//1
                  handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");//2
                  Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
              } break;

            ...
}

H继承自Handler ,是ActivityThread的内部类。在注释1处通过getPackageInfoNoCheck方法获得LoadedApk类型的对象,并将该对象赋值给ActivityClientRecord 的成员变量packageInfo,其中LoadedApk用来描述已加载的APK文件。在注释2处调用handleLaunchActivity方法,如下所示。

frameworks/base/core/java/android/app/ActivityThread.java

  private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
       ...
        Activity a = performLaunchActivity(r, customIntent);
       ...
    }

接着查看performLaunchActivity方法:
frameworks/base/core/java/android/app/ActivityThread.java

    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ...
        try {
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
           ...
        } 
        ...
        return activity;
    }

performLaunchActivity方法中有很多重要的逻辑,这里只保留了Application Context相关的逻辑,这里ActivityClientRecord 的成员变量packageInfo是LoadedApk类型的,接着来查看LoadedApk的makeApplication方法,如下所示。

frameworks/base/core/java/android/app/LoadedApk.java

public Application makeApplication(boolean forceDefaultAppClass,
        Instrumentation instrumentation) {
    if (mApplication != null) {
       //1
        return mApplication;
    }
    ...
    try {
      ...
       java.lang.ClassLoader cl = getClassLoader();
      ...
        ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);//2
        app = mActivityThread.mInstrumentation.newApplication(
                cl, appClass, appContext);//3
        appContext.setOuterContext(app);//4
    } catch (Exception e) {
       ...
    }
    mActivityThread.mAllApplications.add(app);
    mApplication = app;//5
    ...
    return app;
}

注释1处如果mApplication不为null则返回mApplication,这里假设是第一次启动应用程序,因此mApplication为null。
注释2处通过ContextImpl的createAppContext方法来创建ContextImpl。
注释3处的代码用来创建Application,在Instrumentation的newApplication方法中传入了ClassLoader类型的对象以及注释2处创建的ContextImpl 。
注释4处将Application赋值给ContextImpl的Context类型的成员变量mOuterContext。
注释5处将Application赋值给LoadedApk的成员变量mApplication,在Application Context的获取过程中我们会再次用到mApplication。
来查看注释3处的Application是如何创建的,Instrumentation的newApplication方法如下所示。
frameworks/base/core/java/android/app/Instrumentation.java

static public Application newApplication(Class<?> clazz, Context context)
        throws InstantiationException, IllegalAccessException, 
        ClassNotFoundException {
    Application app = (Application)clazz.newInstance();//1
    app.attach(context);
    return app;
}

Instrumentation中有两个newApplication重载方法,最终会调用上面这个重载方法。注释1处通过反射来创建Application,并调用了Application的attach方法,并将ContextImpl传进去:
frameworks/base/core/java/android/app/Application.java

/* package */ final void attach(Context context) {
    attachBaseContext(context);
    mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}

attach方法中调用了attachBaseContext方法,它的实现在Application的父类ContextWrapper中,代码如下所示。
frameworks/base/core/java/android/content/ContextWrapper.java

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

从上文得知,这个base指的是ContextImpl,将ContextImpl赋值给ContextWrapper的Context类型的成员变量mBase。


2.2 Application Context的获取过程

熟知了Application Context的创建过程,那么它的获取过程会非常好理解。我们通过调用getApplicationContext方法来获得Application Context,getApplicationContext方法的实现在ContextWrapper中,如下所示。
frameworks/base/core/java/android/content/ContextWrapper.java

    @Override
    public Context getApplicationContext() {
        return mBase.getApplicationContext();
    }

从上文得知,mBase指的是ContextImpl,我们来查看 ContextImpl的getApplicationContext方法:
frameworks/base/core/java/android/app/ContextImpl.java

Override
public Context getApplicationContext() {
    return (mPackageInfo != null) ?
            mPackageInfo.getApplication() : mMainThread.getApplication();
}

如果LoadedApk不为null,则调用LoadedApk的getApplication方法,否则调用AvtivityThread的getApplication方法。由于应用程序这时已经启动,因此LoadedApk不会为null,则会调用LoadedApk的getApplication方法:
frameworks/base/core/java/android/app/LoadedApk.java

   Application getApplication() {
        return mApplication;
    }

这里的mApplication我们应该很熟悉,它在上文LoadedApk的makeApplication方法的注释5处被赋值。这样我们通过getApplicationContext方法就获取到了Application Context。


三、Activity的Context创建过程

当我们在Activity中调用startActivity方法时,其实调用的是Context的startActivity方法,如果想要在Activity中使用Context提供的方法,务必要先创建Context。Activity的Context会在Activity的启动过程中被创建, ActivityThread是应用程序进程的核心类,它的内部类ApplicationThread会调用scheduleLaunchActivity方法来启动Activity,scheduleLaunchActivity方法如下所示。

frameworks/base/core/java/android/app/ActivityThread.java

Override
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
        ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
        CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
        int procState, Bundle state, PersistableBundle persistentState,
        List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
        boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
        updateProcessState(procState, false);
        ActivityClientRecord r = new ActivityClientRecord();
        r.token = token;
        ...
        sendMessage(H.LAUNCH_ACTIVITY, r);
}

scheduleLaunchActivity方法会将启动Activity的参数封装成ActivityClientRecord ,sendMessage方法向H类发送类型为LAUNCH_ACTIVITY的消息,并将ActivityClientRecord 传递过去。sendMessage方法的目的是将启动Activity的逻辑放在主线程中的消息队列中,这样启动Activity的逻辑就会在主线程中执行。
H类的handleMessage方法中会对LAUNCH_ACTIVITY类型的消息进行处理,其中调用了handleLaunchActivity方法,而handleLaunchActivity方法中又调用performLaunchActivity方法,来查看performLaunchActivity方法。
frameworks/base/core/java/android/app/ActivityThread.java

 private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ...
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);//1
             ...
            }
        } catch (Exception e) {
           ...
        }

        try {
          ...
            if (activity != null) {
                Context appContext = createBaseContextForActivity(r, activity);//2
                ...
                /**
                *3
                */
                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); 
                ...
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);//4
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
               ...
        }

        return activity;
    }

performLaunchActivity方法中有很多重要的逻辑,这里只保留了Activity的Context相关的逻辑。在注释1处用来创建Activity的实例。注释2处通过createBaseContextForActivity方法用来创建Activity的ContextImpl,并将ContextImpl传入注释3处的activity的attach方法中。在注释4处Instrumentation的callActivityOnCreate方法中会调用Activity的onCreate方法。
我们先来查看注释2出的createBaseContextForActivity方法:

frameworks/base/core/java/android/app/ActivityThread.java

 private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {
        ...
        ContextImpl appContext = ContextImpl.createActivityContext(
                this, r.packageInfo, r.token, displayId, r.overrideConfig);//1
        appContext.setOuterContext(activity);//2
        Context baseContext = appContext;
        ...
        return baseContext;
    }

在注释1处调用ContextImpl的createActivityContext方法来创建ContextImpl,注释2处调用了ContextImpl的setOuterContext方法,将此前创建的Activity 实例赋值给ContextImpl的成员变量mOuterContext,这样ContextImpl也可以访问Activity的变量和方法。
我们再回到ActivityThread的performLaunchActivity方法,查看注释3处的Activity的attach方法,如下所示。
frameworks/base/core/java/android/app/Activity.java

 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) {
        attachBaseContext(context);//1
        mFragments.attachHost(null /*parent*/);
        mWindow = new PhoneWindow(this, window);//2
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);//3
        mWindow.setOnWindowDismissedCallback(this);
        ...
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);//4
        if (mParent != null) {
            mWindow.setContainer(mParent.getWindow());
        }
        mWindowManager = mWindow.getWindowManager();//5
        mCurrentConfig = config;
    }

在注释2处创建PhoneWindow,它代表应用程序窗口。PhoneWindow在运行中会间接触发很多事件,比如点击事件、菜单弹出、屏幕焦点变化等事件,这些事件需要转发给与PhoneWindow关联的Actvity,转发操作通过Window.Callback接口实现,Actvity实现了这个接口,在注释3处将当前Activity通过Window的setCallback方法传递给PhoneWindow。
注释4处给PhoneWindow设置WindowManager,并在注释5处获取WindowManager并赋值给Activity的成员变量mWindowManager ,这样在Activity中就可以通过getWindowManager方法来获取WindowManager。
在注释1处调用了ContextThemeWrapper的attachBaseContext方法,如下所示。

frameworks/base/core/java/android/view/ContextThemeWrapper.java

Override
protected void attachBaseContext(Context newBase) {
    super.attachBaseContext(newBase);
}

attachBaseContext方法接着调用ContextThemeWrapper的父类ContextWrapper的attachBaseContext方法:

frameworks/base/core/java/android/content/ContextWrapper.java

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

注释1处的base指的是一路传递过来的Activity的ContextImpl,将它赋值给ContextWrapper的成员变量mBase。这样ContextWrapper的功能就可以交由ContextImpl处理,举个例子:
frameworks/base/core/java/android/content/ContextWrapper.java

@Override
public Resources.Theme getTheme() {
    return mBase.getTheme();
}

当调用ContextWrapper的getTheme方法,其实就是调用的ContextImpl的getTheme方法。
Activity的Context创建过程就讲到这里。 总结一下,在启动Activity的过程中创建ContextImpl,并赋值给ContextWrapper的成员变量mBase中。Activity继承自ContextWrapper的子类ContextThemeWrapper,这样在Activity中就可以使用ContextImpl了。


四、Service的Context创建过程

Service的Context创建过程与Activity的Context创建过程类似,也是在Service的启动过程中被创建 ActivityThread的内部类ApplicationThread会调用scheduleCreateService方法来启动Service,如下所示。
frameworks/base/core/java/android/app/ActivityThread.java

public final void scheduleCreateService(IBinder token,
         ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
     ...
     sendMessage(H.CREATE_SERVICE, s);
 }

sendMessage方法向H类发送CREATE_SERVICE类型的消息,H类的handleMessage方法中会对CREATE_SERVICE类型的消息进行处理,其中调用了handleCreateService方法:
frameworks/base/core/java/android/app/ActivityThread.java

 private void handleCreateService(CreateServiceData data) {
      ...
        try {
            if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);//1
            context.setOuterContext(service);
            Application app = packageInfo.makeApplication(false, mInstrumentation);
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManagerNative.getDefault());//2
            service.onCreate();
          ...
        } catch (Exception e) {
          ... 
        }
    }

在注释1处创建了ContextImpl ,并将该ContextImpl传入注释2处service的attach方法中:
frameworks/base/core/java/android/app/Service.java

 public final void attach(
            Context context,
            ActivityThread thread, String className, IBinder token,
            Application application, Object activityManager) {
        attachBaseContext(context);//1
        mThread = thread;           // NOTE:  unused - remove?
        mClassName = className;
        mToken = token;
        mApplication = application;
        mActivityManager = (IActivityManager)activityManager;
        mStartCompatibility = getApplicationInfo().targetSdkVersion
                < Build.VERSION_CODES.ECLAIR;
    }

注释1处调用了ContextWrapper的attachBaseContext方法。
frameworks/base/core/java/android/content/ContextWrapper.java

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

attachBaseContext方法在前文已经讲过,这里不再赘述。
Service的Context创建过程就讲解到这里,它和Activity的Context创建过程类似。


参考链接:
深度详解 Android 之 Context
Android Context完全解析,你所不知道的Context的各种细节

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/720629.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++ 70 之 类模版中的成员函数,在类外实现

#include <iostream> #include <string> using namespace std;template<class T1, class T2> class Students10{ public:T1 m_name;T2 m_age;Students10(T1 name, T2 age); // 类内声明 类外实现// {// this->m_name name;// this->m_age …

【PPT设计前沿】2024年PPT新趋势,让你的演示文稿引领潮流!

文章目录 一、简约风格的新诠释二、动态元素与交互性的深度融合三、个性化与定制化的独特展现四、大数据与可视化的创新应用五、绿色环保与可持续性的倡导《PPT完美设计入门与进阶/入门与进阶》图书特色内容简介目录前言/序言 获取方式 随着技术的不断革新和创意设计的蓬勃发展…

技术分析:开源大模型的兴起与热门项目推荐

技术分析&#xff1a;开源大模型的兴起与热门项目推荐 引言 随着人工智能&#xff08;AI&#xff09;技术的不断发展&#xff0c;开源大模型成为了许多程序员和研究人员关注的焦点。开源项目不仅促进了技术的快速迭代和普及&#xff0c;还为更多的人提供了学习和实践的机会。…

提取人脸——OpenCV

提取人脸 导入所需的库创建窗口显示原始图片显示检测到的人脸创建全局变量定义字体对象定义一个函数select_image定义了extract_faces函数设置按钮运行GUI主循环运行显示 导入所需的库 tkinter&#xff1a;用于创建图形用户界面。 filedialog&#xff1a;用于打开文件对话框。 …

PR软件视频抠图换背景

1 新建项目 2 新建序列 在项目的右下角有个图标&#xff0c;新建 序列 序列是视频的制作尺寸&#xff0c;根据自己的需要选择 3 新建颜色遮罩 在项目的右下角--新建颜色遮罩--选择黑色--确定 4 导入视频 把要导入视频的文件夹打开&#xff0c;把视频拖到 项目 里 把黑色遮罩拖…

苹果电脑下载vite包错

苹果电脑下载vite包错/Users/lili/.npm/_cacache/index-v5/c5/50/b451703d03b3802b9ee6b7ff2b0bde4de7f26830eb52c904d6911c137cf8包错解决方式 解决方式&#xff1a;sudo chown -R 501:20 "/Users/wangxin/.npm"

python连接数据库,相关数据处理

随机生成一千个数据插入large_db中 # 这是一个示例 Python 脚本。# 按 ShiftF10 执行或将其替换为您的代码。 # 按 双击 Shift 在所有地方搜索类、文件、工具窗口、操作和设置。 import pandas as pd from sqlalchemy import create_engine from faker import Faker# 初始化fa…

MySQL日志——redolog

redo log&#xff08;重做日志&#xff09; 为什么需要redo log&#xff1f; 在mysql提交一个事务后&#xff0c;这个事务所作的数据修改并不会直接保存到磁盘文件中&#xff0c;而是先保存在buffer pool缓冲区中&#xff0c;在需要读取数据时&#xff0c;先从缓冲区中找&…

图片怎么弄成黑白的?关于将图片改成黑白的几种方法

图片怎么弄成黑白的&#xff1f;黑白照片以其独特的艺术魅力和经典的视觉效果&#xff0c;依然在摄影和图像处理中占据重要地位。无论是为了追求怀旧的氛围&#xff0c;还是为了突出图像的构图和光影效果&#xff0c;许多人都希望将彩色图片转换成黑白图片。这不仅可以赋予图像…

Springboot 权限认证框架 -- SA-Token 简介(一)

引言 现今的软件开发中&#xff0c;权限认证与访问控制是每一个应用都必不可少的功能。SA-Token是一个简单、安全、易用的权限认证框架&#xff0c;它主要解决登录认证、权限认证、Session会话、单点登录等功能。SA-Token以其轻量级、零学习成本的特点&#xff0c;迅速赢得了开…

进程间通信以及线程的同步互斥机制

1.进程间通信机制 常用的六种通信机制&#xff1a; 管道、消息队列、共享内存、信号灯集、信号、Socket 管道&#xff08;Pipe&#xff09;和无名管道&#xff08;匿名管道&#xff09;&#xff1a; 管道是一种半双工的通信方式&#xff0c;数据只能单向流动&#xff0c;通常…

【python】python指南(四):typing静态类型注解综述

一、引言 对于算法工程师来说&#xff0c;语言从来都不是关键&#xff0c;关键是快速学习以及解决问题的能力。大学的时候参加ACM/ICPC一直使用的是C语言&#xff0c;实习的时候做一个算法策略后台用的是php&#xff0c;毕业后做策略算法开发&#xff0c;因为要用spark&#x…

整合JavaSSM框架【超详细】

在整合SSM之前我们首先要知道SSM框架指的是哪些框架&#xff1f; Java的SSM指的是Spring、Spring MVC、MyBatis这三个框架 Spring框架 什么是Spring&#xff1f; Spring是一个支持快速开发Java EE应用程序的框架。它提供了一系列底层容器和基础设施&#xff0c;并可以和大量常…

Linux-DNS域名解析服务01

BIND 域名服务基础 1、DNS&#xff08;Domain Name System&#xff09;系统的作用及类型 整个 Internet 大家庭中连接了数以亿计的服务器、个人主机&#xff0c;其中大部分的网站、邮件等服务器都使用了域名形式的地址&#xff0c;如 www.google.com、mail.163.com 等。很显然…

window下nginx命令报错 CreateFile() “xxx/logs/nginx.pid“ failed

参考文章&#xff1a; 《Windows下nginx报错解决&#xff1a;CreateFile() “xxx/logs/nginx.pid” failed 》 《Windows下Nginx的启动停止重启等命令操作过程》 解决过程 报错忘记截图了 错误详细信息&#xff1a;在nginx -s reload、nginx -s stop时出现 nginx: [error] C…

YOLOv8目标检测算法在地平线Bernoulli2架构BPU上高效部署参考(PTQ方案)30fps!

—— 以RDK X3为例&#xff0c;修改Head部分&#xff0c;8ms疾速Python后处理程序&#xff0c;30fps稳稳当当 本文在地平线对YOLOv8s的Backbone修改的基础上&#xff0c;提出一种在地平线Bernoulli2架构BPU上部署YOLOv8的后处理思路。使用640640分辨率&#xff0c;80类别基于C…

DSP——从入门到放弃系列2——PLL锁相环(持续更新)

1、概述 锁相环&#xff08;Phase Locked Loop,PLL&#xff09;是处理器的时钟源&#xff0c;控制着C6678处理器中C66x内核、各外围设备的时钟的时钟比、对准和选通功能。 2、功能描述 上图显示了PLL和PLL控制器的逻辑实现。PLL控制器提供通过软件可配置的分频器&#xff0…

关于glibc-all-in-one下载libc2.35以上报错问题

./download libc版本 下载2.35时报错&#xff1a;原因是缺少解压工具zstd sudo apt-get install zstd 下载后重新输命令就可以了 附加xclibc命令 xclibc -x ./pwn ./libc-版本 ldd pwn文件 xclibc -c libc版本

Git记录 上传至Gitee

1.GitHub拉去的代码需要上传至自己的Gitee需要清除原有remote服务器信息 查看原始远程服务器信息&#xff0c;后删除远程服务器信息 git remote -v git remote rm origin 2.Gitee新建软件仓库 法1&#xff09;不用初始化仓库&#xff0c;初始化会自动生成.git。如果本地.git…

【第18章】Vue实战篇之登录界面

文章目录 前言一、数据绑定1. 数据绑定2. 数据清空 二、表单校验1. 代码2. 展示 三、登录1.登录按钮2.user.js3. login 四、展示总结 前言 上一章完成用户注册&#xff0c;这一章主要做用户登录。 一、数据绑定 登录和注册使用相同的数据绑定 1. 数据绑定 <!-- 登录表单 -…