Android App安装弹窗显示流程

news2024/11/15 23:29:44

一、APP的安装

1、常见安装方式
系统应用和预制应用安装――开机时完成,没有安装界面,在PKMS的构造函数中完成安装

网络下载或第三方应用安装――调用PackageManager.installPackages(),有安装界面。

ADB工具安装――没有安装界面,它通过启动pm脚本的形式,然后调用com.android.commands.pm.Pm类,之后调用到PMS.installStage()完成安装。
在这里插入图片描述2、APK的签名校验理解

V1签名apk-signature-v1-location.png只是校验了apk资源,并没有约束zip,签名信息存储在zip/META-INF中。

v2签名是一个对全文件进行签名的方案,能提供更快的应用安装时间、对未授权APK文件的更改提供更多保护.

3、APK安装过程

开机后扫描应用安装目录和系统App目录,解析其中的apk文件将相关信息加载到PKMS中的数据结构中,同时对于没有对应数据目录的App生成对应的数据目录
注册包名App等信息、以及相关的四大组件到PMS中
将解析到的数据同步到/data/system/packages.xml中

4、App安装涉及的目录理解
系统App安装目录

1、 /system/app: Android系统App路径
2、/system/priv-app: 同上,但比/system/app权限优先级更高,可以拿到ApplicationInfo.PRIVATE_FLAG_PRIVILEGED特殊权限
3、/vendor/app: odm或者oem厂商预制系统App目录
4、/vendor/priva-app: 同上

普通应用App安装目录

/data/app:用户App程序安装的目录。安装时Apk会被拷贝至此目录

用户数据目录

/data/data:存放应用程序的数据,无论是系统App还是普通App,App产生的用户数据都存放在/data/data/包名/目录下。

App注册表目录

/data/system
1、packages.xml:
记录apk的permissions,flags,ts,version,uesrid等信息,这些信息主要通apk的AndroidManifest.xml解析获取,当系统进行程序安装、卸载和更新等操作时,均会更新该文件。
2、packages-backup.xml : 备份文件
3、packages-stopped.xml : 记录被用户强行停止的应用的Package信息
4、packages-stopped-backup.xml : pakcages-stoped.xml文件的备份
5、packages.list : 记录非系统自带的APK的数据信息,这些APK有变化时会更新该文件

5、package.xml文件解析

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<packages>
    <version sdkVersion="xxx" databaseVersion="xxx" fingerprint="xxx" />
    <version volumeUuid="xxx" sdkVersion="xxx" databaseVersion="xxx" fingerprint="xxx" />

    <permissions>
        <item name="android.permission.REAL_GET_TASKS" package="android" protection="18" />
        ...
    </permissions>   
  
    <package name="com.android.providers.telephony" codePath="/system/priv-app/TelephonyProvider" nativeLibraryPath="/system/priv-app/TelephonyProvider/lib" publicFlags="1007402501" privateFlags="8" ft="11e8dc5d800" it="11e8dc5d800" ut="11e8dc5d800" version="25" versionName="7.1.2" applicationName="电话和短信存储" sharedUserId="1001" isOrphaned="true">
        <sigs count="1">
            <cert index="1" key="xxx" />
        </sigs>
        <perms>
            <item name="android.permission.WRITE_SETTINGS" granted="true" flags="0" />
            ...
        </perms>
        <proper-signing-keyset identifier="1" />
    </package>  
    ...       
    
    <updated-package name="xxx.xxx.xxx" codePath="/system/app/xxx" ft="11e8dc5d800" it="11e8dc5d800" ut="11e8dc5d800" version="11" nativeLibraryPath="/system/app/xxx/lib" primaryCpuAbi="armeabi-v7a" sharedUserId="1000" />


    <shared-user name="android.media" userId="10005">
        <sigs count="1">
            <cert index="2" />
        </sigs>
        <perms>
            <item name="android.permission.ACCESS_CACHE_FILESYSTEM" granted="true" flags="0" />
            ...
        </perms>
    </shared-user>
    ...
</packages>   

package.xml对应的类图关系
在这里插入图片描述BasePermission

BasePermission对应packages.xml中permissions标签的子标签item,对于上述所定义的每一项权限都会生成一个BasePermission。
protection :等级分为四个
1、普通权限(normal)
2、运行时权限(dangerous)
3、签名权限(signature)
4、特殊权限(privileged)

    <permissions>
        <item name="android.permission.REAL_GET_TASKS" package="android" protection="18" />
        ...
    <permissions/>  

PermissionsState
在这里插入图片描述

PermissionState对应的是标签中的子标签标签中的内容

<package name="com.android.providers.telephony" codePath="/system/priv-app/TelephonyProvider" nativeLibraryPath="/system/priv-app/TelephonyProvider/lib" publicFlags="1007402501" privateFlags="8" ft="11e8dc5d800" it="11e8dc5d800" ut="11e8dc5d800" version="25" versionName="7.1.2" applicationName="电话和短信存储" sharedUserId="1001" isOrphaned="true">
        <perms>
            <item name="android.permission.WRITE_SETTINGS" granted="true" flags="0" />
            ...
        </perms>
</package>


PackageSignatures

PackageSignatures对应的是<package>标签中的子标签<sigs>标签中的内容
<package name="com.android.providers.telephony" codePath="/system/priv-app/TelephonyProvider" nativeLibraryPath="/system/priv-app/TelephonyProvider/lib" publicFlags="1007402501" privateFlags="8" ft="11e8dc5d800" it="11e8dc5d800" ut="11e8dc5d800" version="25" versionName="7.1.2" applicationName="电话和短信存储" sharedUserId="1001" isOrphaned="true">
        <sigs count="1">
            <cert index="1" key="xxx" />
        </sigs>
</package>


PackageSetting

PackageSetting这个数据结构类是packages.xml里面记录安装包信息标签相对应的类,可以看到PackageSetting继承了PackageSettingBase类,PackageSettingBase类继承自SettingBase类。应用的基本信息保存在PackageSettingBase类的成员变量中,签名则保存在PackageSignatures中,权限状态保存在父类的SettingBase的PermissionsState中。

 <package name="com.android.providers.telephony" codePath="/system/priv-app/TelephonyProvider" nativeLibraryPath="/system/priv-app/TelephonyProvider/lib" publicFlags="1007402501" privateFlags="8" ft="11e8dc5d800" it="11e8dc5d800" ut="11e8dc5d800" version="25" versionName="7.1.2" applicationName="电话和短信存储" sharedUserId="1001" isOrphaned="true">
        <sigs count="1">
            <cert index="1" key="xxx" />
        </sigs>
        <perms>
            <item name="android.permission.WRITE_SETTINGS" granted="true" flags="0" />
            ...
        </perms>
        <proper-signing-keyset identifier="1" />
</package>  

SharedUserSetting

SharedUserSetting这个数据结构类是packages.xml里面记录安装包信息标签相对应的类,它和PackageSetting有一个共同的父类即SettingBase,即都是通过父类的PermissionsState来保存权限信息。SharedUserSetting被设计的用途主要用来描述具有相同的sharedUserId的应用信息,它的成员变量packages保存了所有具有相同sharedUserId的应用信息引用,而成员变量userId则是记录多个APK共享的UID。共享用户的应用的签名是相同的,签名保存在成员变量signatures中(这里有一点需要注意,由于签名相同,Android运行时很容易检索到某个应用拥有相同的sharedUserId的其他应用)。

在这里插入图片描述

 <shared-user name="android.media" userId="10005">
        <sigs count="1">
            <cert index="2" />
        </sigs>
        <perms>
            <item name="android.permission.ACCESS_CACHE_FILESYSTEM" granted="true" flags="0" />
            ...
        </perms>
</shared-user>

Settings : package.xml 终极大管家类
在这里插入图片描述

二、APP安装整体流程

代码仓库:http://androidxref.com/9.0.0_r3/xref/packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallStart.java

1、安装APP代码入口

    <activity android:name=".InstallStart"
    android:exported="true"
    android:excludeFromRecents="true">
    <intent-filter android:priority="1">
        <action android:name="android.intent.action.VIEW"/>
        <action android:name="android.intent.action.INSTALL_PACKAGE"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:scheme="file"/>
        <data android:scheme="content"/>
        <data android:mimeType="application/vnd.android.package-archive"/>
    </intent-filter>
    <intent-filter android:priority="1">
        <action android:name="android.intent.action.INSTALL_PACKAGE"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:scheme="file"/>
        <data android:scheme="package"/>
        <data android:scheme="content"/>
    </intent-filter>
    <intent-filter android:priority="1">
        <action android:name="android.content.pm.action.CONFIRM_PERMISSIONS"/>
        <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
    </activity>

2、根据Uri的Scheme协议不同,跳转到不同的界面
content协议跳转到InstallStaging,package协议跳转到PackageInstallerActivity

 protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ......
        Intent nextActivity = new Intent(intent);
        nextActivity.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
        // The the installation source as the nextActivity thinks this activity is the source, hence
        // set the originating UID and sourceInfo explicitly
        nextActivity.putExtra(PackageInstallerActivity.EXTRA_CALLING_PACKAGE, callingPackage);
        nextActivity.putExtra(PackageInstallerActivity.EXTRA_ORIGINAL_SOURCE_INFO, sourceInfo);
        nextActivity.putExtra(Intent.EXTRA_ORIGINATING_UID, originatingUid);

        //1、content的Uri协议 : InstallStaging 
        //2、package的Url协议:PackageInstallerActivity 
        if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) {
            nextActivity.setClass(this, PackageInstallerActivity.class);
        } else {
            Uri packageUri = intent.getData();

            if (packageUri != null && (packageUri.getScheme().equals(ContentResolver.SCHEME_FILE)
                    || packageUri.getScheme().equals(ContentResolver.SCHEME_CONTENT))) {
                // Copy file to prevent it from being changed underneath this process
                   //1、content的Uri协议 : InstallStaging 
                nextActivity.setClass(this, InstallStaging.class);
            } else if (packageUri != null && packageUri.getScheme().equals(
                    PackageInstallerActivity.SCHEME_PACKAGE)) {
                //package的Url协议:PackageInstallerActivity 
                nextActivity.setClass(this, PackageInstallerActivity.class);
            } else {
                Intent result = new Intent();
                result.putExtra(Intent.EXTRA_INSTALL_RESULT,
                        PackageManager.INSTALL_FAILED_INVALID_URI);
                setResult(RESULT_FIRST_USER, result);

                nextActivity = null;
            }
        }
        
        if (nextActivity != null) {
            startActivity(nextActivity);
        }
        finish();
    }

3、InstallStaging类的介绍

主要内容:将content协议的Uri转换为package协议的Uri,然后通过IO形式写入到mStagedFile文件中
作用:主要起了转换的作用,将content协议的Uri转换为package协议,然后跳转到PackageInstallerActivity

@Override
  protected void onResume() {
      super.onResume();
      if (mStagingTask == null) {
          if (mStagedFile == null) {
              try {
                  mStagedFile = TemporaryFileManager.getStagedFile(this);
              } catch (IOException e) {
                  showError();
                  return;
              }
          }
          mStagingTask = new StagingAsyncTask();
          mStagingTask.execute(getIntent().getData());
      }
  }


 private final class StagingAsyncTask extends AsyncTask<Uri, Void, Boolean> {
        @Override
        protected Boolean doInBackground(Uri... params) {
            if (params == null || params.length <= 0) {
                return false;
            }
            Uri packageUri = params[0];
            try (InputStream in = getContentResolver().openInputStream(packageUri)) {
                if (in == null) {
                    return false;
                }
                try (OutputStream out = new FileOutputStream(mStagedFile)) {
                    byte[] buffer = new byte[4096];
                    int bytesRead;
                    while ((bytesRead = in.read(buffer)) >= 0) {
                        if (isCancelled()) {
                            return false;
                        }
                        out.write(buffer, 0, bytesRead);
                    }
                }
            } catch (IOException | SecurityException e) {
                Log.w(LOG_TAG, "Error staging apk from content URI", e);
                return false;
            }
            return true;
        }
        @Override
        protected void onPostExecute(Boolean success) {
           if (session != null) {
         Intent broadcastIntent = new Intent(BROADCAST_ACTION);
         broadcastIntent.setPackage(
                 getPackageManager().getPermissionControllerPackageName());
         broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId);
         PendingIntent pendingIntent = PendingIntent.getBroadcast(
                 InstallInstalling.this,
                 mInstallId,
                 broadcastIntent,
                 PendingIntent.FLAG_UPDATE_CURRENT);
        //APP安装的启动入口
         session.commit(pendingIntent.getIntentSender());
         mCancelButton.setEnabled(false);
         setFinishOnTouchOutside(false);
     } else {
         getPackageManager().getPackageInstaller().abandonSession(mSessionId);
         if (!isCancelled()) {
             launchFailure(PackageManager.INSTALL_FAILED_INVALID_APK, null);
         }
     }
}

4、PackageInstallerActivity类的介绍
它就是在安装应用显示弹窗的Activity

@Override
protected void onCreate(Bundle icicle) {
    super.onCreate(icicle);
    if (icicle != null) {
        mAllowUnknownSources = icicle.getBoolean(ALLOW_UNKNOWN_SOURCES_KEY);
    }
    mPm = getPackageManager();
    mIpm = AppGlobals.getPackageManager();
    mAppOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
    mInstaller = mPm.getPackageInstaller();
    mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
    ...
    //根据Uri的Scheme进行预处理
    boolean wasSetUp = processPackageUri(packageUri);
    if (!wasSetUp) {
        return;
    }
    bindUi(R.layout.install_confirm, false);
    //判断是否是未知来源的应用,如果开启允许安装未知来源选项则直接初始化安装
    checkIfAllowedAndInitiateInstall();
}

分别对content和package两种不同协议处理

private boolean processPackageUri(final Uri packageUri) {
     mPackageURI = packageUri;
     final String scheme = packageUri.getScheme();//1
     switch (scheme) {
         case SCHEME_PACKAGE: {
             try {
              ...
         } break;
         case SCHEME_FILE: {
             File sourceFile = new File(packageUri.getPath());
             //得到sourceFile的包信息
             PackageParser.Package parsed = PackageUtil.getPackageInfo(this, sourceFile);
             if (parsed == null) {
                 Log.w(TAG, "Parse error when parsing manifest. Discontinuing installation");
                 showDialogInner(DLG_PACKAGE_ERROR);
                 setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
                 return false;
             }
             //对parsed进行进一步处理得到包信息PackageInfo
             mPkgInfo = PackageParser.generatePackageInfo(parsed, null,
                     PackageManager.GET_PERMISSIONS, 0, 0, null,
                     new PackageUserState());//3
             mAppSnippet = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile);
         } break;
         default: {
             Log.w(TAG, "Unsupported scheme " + scheme);
             setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI);
             finish();
             return false;
         }
     }
     return true;
 }

弹窗上显示是否是非法安装的处理

private void checkIfAllowedAndInitiateInstall() {
       //判断如果允许安装未知来源或者根据Intent判断得出该APK不是未知来源
       if (mAllowUnknownSources || !isInstallRequestFromUnknownSource(getIntent())) {
           //初始化安装
           initiateInstall();
           return;
       }
       // 如果管理员限制来自未知源的安装, 就弹出提示Dialog或者跳转到设置界面
       if (isUnknownSourcesDisallowed()) {
           if ((mUserManager.getUserRestrictionSource(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
                   Process.myUserHandle()) & UserManager.RESTRICTION_SOURCE_SYSTEM) != 0) {    
               showDialogInner(DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER);
               return;
           } else {
               startActivity(new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS));
               finish();
           }
       } else {
           handleUnknownSources();
       }
   }

InstallStaging.java session.commit() 去执行系统framework层

 protected void onPostExecute(Boolean success) {
           if (session != null) {
         Intent broadcastIntent = new Intent(BROADCAST_ACTION);
         broadcastIntent.setPackage(
                 getPackageManager().getPermissionControllerPackageName());
         broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId);
         PendingIntent pendingIntent = PendingIntent.getBroadcast(
                 InstallInstalling.this,
                 mInstallId,
                 broadcastIntent,
                 PendingIntent.FLAG_UPDATE_CURRENT);
        //APP安装的启动入口
         session.commit(pendingIntent.getIntentSender());
         mCancelButton.setEnabled(false);
         setFinishOnTouchOutside(false);
     } else {
         getPackageManager().getPackageInstaller().abandonSession(mSessionId);
         if (!isCancelled()) {
             launchFailure(PackageManager.INSTALL_FAILED_INVALID_APK, null);
         }
     }

PackageInstaller.java 类

public void commit(@NonNull IntentSender statusReceiver) {
           try {
               mSession.commit(statusReceiver);
           } catch (RemoteException e) {
               throw e.rethrowFromSystemServer();
           }
       }

PackageInstallerSession.java类
PackageInstallObserverAdapter继承PackageInstallObserver : 监听安装APP的过程
mSessionId是安装包的会话id,mInstallId是等待的安装事件id

@Override
   public void commit(IntentSender statusReceiver) {
       Preconditions.checkNotNull(statusReceiver);
       ...
       mActiveCount.incrementAndGet();
       final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(mContext,
               statusReceiver, sessionId, mIsInstallerDeviceOwner, userId);
      //Handler发送一个类型为MSG_COMMIT的消息,通知PMS安装应用
       mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget();
   }

private final Handler.Callback mHandlerCallback = new Handler.Callback() {
      @Override
      public boolean handleMessage(Message msg) {
          final PackageInfo pkgInfo = mPm.getPackageInfo(
                  params.appPackageName, PackageManager.GET_SIGNATURES
                          | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
          final ApplicationInfo appInfo = mPm.getApplicationInfo(
                  params.appPackageName, 0, userId);
          synchronized (mLock) {
              if (msg.obj != null) {
                  mRemoteObserver = (IPackageInstallObserver2) msg.obj;
              }
              try {
                  //PMS开始安装应用
                  commitLocked(pkgInfo, appInfo);
              } catch (PackageManagerException e) {
                  final String completeMsg = ExceptionUtils.getCompleteMessage(e);
                  Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
                  destroyInternal();
                   //安装时候出现异常问题
                  dispatchSessionFinished(e.error, completeMsg, null);
              }
              return true;
          }
      }
  };

private void commitLocked(PackageInfo pkgInfo, ApplicationInfo appInfo)
          throws PackageManagerException {
     ...
    //通知 PMS开始安装应用
      mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params,
              installerPackageName, installerUid, user, mCertificates);
  }

总结:

根据Uri的Scheme协议不同,跳转到不同的界面,content协议跳转到InstallStaging,package跳转到PackageInstallerActivity。
InstallStaging将content协议的Uri转换为File协议,然后跳转到PackageInstallerActivity。
PackageInstallerActivity会分别对package协议和file协议的Uri进行处理,如果是file协议会解析APK文件得到包信息PackageInfo。
PackageInstallerActivity中会对未知来源进行处理,如果允许安装未知来源或者根据Intent判断得出该APK不是未知来源,就会初始化安装确认界面,如果管理员限制来自未知源的安装, 就弹出提示Dialog或者跳转到设置界面。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

社交登陆成功回调

1. 点击跳转至第三方授权 2. 这是使用gitee作为第三方授权进行验证 3. 授权成功则跳转至 redirect_url 4. 社交登陆回调逻辑 一、根据第三方授权提供的方式获取token &#xff08;1&#xff09;发送请求获取code码&#xff08;每次发送请求&#xff0c;code码会改变&#xff…

构建安全架构的 Azure 云:深入了解零信任体系结构

文章目录 前言一、零信任安全模型的概念以及背景介绍二、传统安全模型&#xff08;边界模型&#xff09;三、零信任模型&#xff08;现阶段主流云厂商策略&#xff09;四、Azure 中的零信任体系结构&#xff08;本文重点&#xff09;4.1 基础知识点&#xff08;必须了解&#x…

File 类,InputStream, OutputStream 的用法

目录 一.File类 关于名字和路径的操作 关于创建和销毁的操作 创建文件夹(多级目录) InputStream 第一种:字节流读取 第二种: 字符流读取(Reader) OutputStream 第一种:字节流写入 第二种方式:字符流输入 一.File类 File翻译过来"文件" 那么File类的操作实际…

【初识 Docker | 中级篇】 Docker 安装 Redis

文章目录 前言一、安装 docker1、安装docker2、安装docker-compose 二、redis 单机安装1.创建配置文件1.1.创建目录1.2.创建redis.conf1.3.创建docker-compose.yml 2.启动redis容器 总结 前言 可以按照以下步骤在 Docker 中安装 Redis docker pull redis 拉取Redis镜像 docker…

CSS3-定位

网页常见布局方式 1 标准流 1 块级元素独占一行 → 垂直布局 2 行内元素/行内块元素一行显示多个 → 水平布局 2 浮动 可以让原本垂直布局的 块级元素变成水平布局 3 定位 1 可以让元素自由的摆放在网…

软件项目管理 第五章 软件项目的成本管理 课后习题参考答案——主编:李冰、张桥珍、刘玉娥

第五章 软件项目的成本管理 课后习题参考答案 1.选择题 (1)&#xff08;A&#xff09;是用系统的功能数量来测量其规模,与实现产品所使用的语言和技术是没有关系的。 A.功能点 B.对象点 C.代码行 D.用例点 (2)如果你是某项目的项目经理,你已经估…

easyX库文字输出相关函数(注释版)

您好这里是limou3434的博文系列&#xff0c;感兴趣的话可以看看我的其他系列。 本次我给您带来的是easyX库的字符输出系列函数&#xff0c;祝您看得开心。 0.文字输出函数概览 函数或数据类型描述LOGFONT文字样式的结构体。settextcolor设置当前文字颜色。settextstyle设置当…

Altium Designer VS CADENCE 颜色配置

最近公司要求用CADENCE画图&#xff0c;对于我这个用了10年以上AD的老玩家来说&#xff0c;真的是很不想接受&#xff0c;虽然AD有版权问题&#xff0c;据说也容易收到律师函&#xff0c;但还是不想更换&#xff0c;毕竟用了10年了&#xff0c;感情、熟练程度摆在那里。没办法&…

继承的基本内容

在面向对象部分提到过&#xff0c;面向对象三大特性&#xff08;不是只有三个特性&#xff0c;而是指存在感最强的三个特性&#xff09;&#xff1a;封装&#xff0c;继承&#xff0c;多态。 封装&#xff1a;对比C语言&#xff0c;将数据和处理数据的方法放入一个类中&#xf…

【算法总结】——组合型回溯

文章目录 组合型回溯例题1——组合从输入考虑模板从答案考虑模板 例题2——括号生成解法一解法二 剪枝分析回溯时间复杂度的通用方法 组合型回溯 组合型和子集型之间的差异在哪里呢&#xff1f; 相比子集问题&#xff0c;组合问题是可以做一些额外的优化的&#xff08;因为只…

Linux模块文件编译到内核与独立编译成.ko文件的方法

很多粉丝在群里提问&#xff0c;如何把一个模块文件编译到内核中或者独立变异成ko文件。本文给大家详解讲解。 1. 内核目录 Linux内核源代码非常庞大&#xff0c;随着版本的发展不断增加。它使用目录树结构&#xff0c;并且使用Makefile组织配置、编译。 初次接触Linux内核&…

Visual Studio 2022写Windows程序造成CPU占用率过高故障排除

我是荔园微风&#xff0c;作为一名在IT界整整25年的老兵&#xff0c;今天针对Visual Studio 2022写Windows程序造成CPU占用率过高故障进行排除。 下面是一个标准的Windows程序&#xff0c;也可以说是经典程序了&#xff0c;但是这个程序一运行&#xff0c;WinMain.exe的CPU占用…

Android 13(T) - binder阅读(2)- ServiceManager的启动与获取

1 ServiceManager的启动 1.1 服务的启动与注册 上一篇笔记中有说到&#xff0c;ServiceManager是一个特殊的binder service&#xff0c;所以它和普通的service一样需要打开binder驱动&#xff0c;在驱动中创建一个属于ServiceManager进程的binder_proc。 int main(int argc,…

django中发送get post请求并获得数据

django中发送get post请求并获得数据 项目结构如下注册路由 urls.py在处理函数中处理请求 views.py进行 get的请求01浏览器 get请求传参数02服务器django get参数解析获取01浏览器 post的发送浏览器get 请求 获取页面返回的 form 发送post请求 带参数 02服务器django的post请求…

【Unity3D】平面光罩特效

1 前言 屏幕深度和法线纹理简介中对深度和法线纹理的来源、使用及推导过程进行了讲解&#xff0c;激光雷达特效中讲述了一种重构屏幕像素点世界坐标的方法&#xff0c;本文将沿用激光雷达特效中重构像素点世界坐标的方法&#xff0c;实现平面光罩特效。 假设平面光罩的高度为 s…

SpringCloud Alibaba入门7之引入服务网关Gateway

我们需要在客户端和服务端之间加一个统一的入口&#xff0c;来作为请求的统一接入&#xff0c;而在微服务的体系中&#xff0c;承担这个角色的就是网关。我们只需要将网关的机器IP配置到DNS,或者接入负载&#xff0c;那么客户端的服务最终通过我们的网关&#xff0c;再转发到对…

GEE:欧几里得距离——计算目标图像中每个像素到目标像素的距离

作者:CSDN @ _养乐多_ 利用欧几里得距离计算目标图像中每个像素到目标像素的距离,以量化像素与目标的接近程度。 结果如下图所示, 文章目录 一、欧几里得距离简介二、代码一、欧几里得距离简介 欧几里得距离(Euclidean distance)是在数学中常用的一种距离度量方式,用于…

Android PMS APP安装流程

仓库网址&#xff1a;http://androidxref.com/9.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java 一、PMS安装APP流程图 二、文件复制 PMS处理安装HandlerParams安装参数流程图 PackageManagerService.java#installStage…

职场求生记|唐朝打工人如何绝地求生

&#x1f4da;书名&#xff1a;《长安的荔枝》 ✏️作者&#xff1a;马伯庸 作为“见微”系列神作&#xff0c;其在微信读书总榜的第一名位置持续一段时间了&#xff0c;其讲述的内容和每个人都息息相关&#xff0c;更是能引起职场人的无限共鸣&#xff0c;值得深思。 ⭐故事…

使用networkx查看某一个节点的一阶/二阶/三阶邻居

文章目录 前言手动高级 前言 一般情况下&#xff0c;貌似这些图之类的包&#xff0c;只提供查询一个节点的一阶邻居&#xff0c;但是有的时候我们需要二阶甚至三阶&#xff0c;那么该如何做呢&#xff1f; 注意一下&#xff0c;本文的方法仅可以针对二阶或者三阶&#xff0c;…