Android apk安装管理(PackageManagerService 分析)

2023-11-05

Android apk安装管理(PackageManagerService 分析)                                                           

 

本篇主要分析了系统启动阶段包管理服务的启动流程,其中的几个接口在apk安装时也会被调用。包管理服务启动时主要做的工作大致有如下几方面:

 

1.建立java层的installer与c层的installd的socket联接,使得在上层的install,remove,dexopt等功能最终由installd在底层实现

 

2.建立PackageHandler消息循环,用于处理外部的apk安装请求消息,如adb install,packageinstaller安装apk时会发送消息

 

3.解析/system/etc/permission下xml文件(framework/base/data/etc/),包括platform.xml和系统支持的各种硬件模块的feature.主要工作:

(1)建立底层user ids和group ids 同上层permissions之间的映射;可以指定一个权限与几个组ID对应。当一个APK被授予这个权限时,它也同时属于这几个组。

(2)给一些底层用户分配权限,如给shell授予各种permission权限;把一个权限赋予一个UID,当进程使用这个UID运行时,就具备了这个权限。

(3) library,系统增加的一些应用需要link的扩展jar库;

(4) feature,系统每增加一个硬件,都要添加相应的feature.将解析结果放入mSystemPermissions,mSharedLibraries,mSettings.mPermissions,mAvailableFeatures等几个集合中供系统查询和权限配置使用。

 

4.检查/data/system/packages.xml是否存在,这个文件是在解析apk时由

writeLP()创建的,里面记录了系统的permissions,以及每个apk的name,codePath,flags,ts,version,uesrid等信息,这些信息主要通过apk的

AndroidManifest.xml解析获取,解析完apk后将更新信息写入这个文件并保

存到flash,下次开机直接从里面读取相关信息添加到内存相关列表中。当有apk

升级,安装或删除时会更新这个文件。

 

5.检查BootClassPath,mSharedLibraries及/system/framework下的jar

是否需要dexopt,需要的则通过dexopt进行优化

 

6.启动AppDirObserver线程监测/system/framework,/system/app,/data/app,/data/

app-private目录的事件,主要监听add和remove事件。对于目录监听底层通过

inotify机制实现,inotify 是一种文件系统的变化通知机制,如文件增加、删除

等事件可以立刻让用户态得知,它为用户态监视文件系统的变化提供了强大的支持。

当有add event时调用scanPackageLI(File , int , int)处理;

当有remove event时调用removePackageLI()处理;

 

7.对于以上几个目录下的apk逐个解析,主要是解析每个apk的AndroidMa-

nifest.xml文件,处理asset/res等资源文件,建立起每个apk的配置结构信息,

并将每个apk的配置信息添加到全局列表进行管理。调用installer.install()进

行安装工作,检查apk里的dex文件是否需要再优化,如果需要优化则通过辅助工

具dexopt进行优化处理;将解析出的componet添加到pkg的对应列表里;

对apk进行签名和证书校验,进行完整性验证。

 

8.将解析的每个apk的信息保存到packages.xml和packages.list文件里,

packages.list记录了如下数据:pkgName,userId,debugFlag,dataPath(包的数据路径)

                                                     图1  主流程图


 

详细分析

 

在systemserver.java中启动包管理服务

pm=PackageManagerService.main(context,factoryTest!= SystemServer.FACTORY_TEST_OFF);

main函数主要功能是构造 PackageManagerService实例,然后添加到ServiceManager中。

   public static final IPackageManager main(Context context, booleanfactoryTest) {

       PackageManagerService m = new PackageManagerService(context,factoryTest);

       ServiceManager.addService("package", m);

       return m;

    }

 

PackageManagerService(context, factoryTest)是包管理服务的主进程。它完成了对/system/app,/data/app,/system/framework,/data/app-private下的apk文件的解析。详细流程如下:

 

初始化过程:

判断ro.build.type 是否等于eng;

创建系统显示像素实例mMetrics = new DisplayMetrics();

 

创建mSettings实例mSettings = new Settings(),Settings类是PackageManagerService的一个静态子类,它的作用主要是保持动态设置的信息,通过Settings()构造函数在/data/system下创建了三个文件名:packages.xml,packages-backup.xml(这个文件在mSettings.writeLP()里被删除了),packages.list。

 

mSettings增加android.uid.system,android.uid.phone,android.uid.log三个共享用户ID,同时授予其系统权限。

 

//建立installer与installd的socket联接

Installer installer = new Installer();

installer.ping() &&Process.supportsProcesses();

installd完成以下一些命令

struct cmdinfo cmds[] = {

    {"ping",                 0,do_ping },

    {"install",               3,do_install },

    {"dexopt",             3,do_dexopt },

    {"movedex",          2, do_move_dex },

    {"rmdex",              1,do_rm_dex },

    {"remove",            1,do_remove },

    {"rename",            2,do_rename },

    {"freecache",        1,do_free_cache },

    {"rmcache",          1,do_rm_cache },

    {"protect",            2,do_protect },

    {"getsize",            3,do_get_size },

    {"rmuserdata",     1,do_rm_user_data },

    {"movefiles",        0,do_movefiles },

};

 

//获取当前缺省的显示像素

WindowManagerwm=(WindowManager)context.getSystemService(Context.WINDOW_SERVICE);

Display d = wm.getDefaultDisplay();

d.getMetrics(mMetrics);

 

建立一个消息循环,用于处理apk安装时的请求消息处理(这些请求来自adb install/push,包安装器,android market下载安装apk时发送的)

mHandlerThread.start();

mHandler = newPackageHandler(mHandlerThread.getLooper());

这个消息循环处理的消息事件如下:

   static final int SEND_PENDING_BROADCAST = 1;

   static final int MCS_BOUND = 3;

   static final int END_COPY = 4;

   static final int INIT_COPY = 5;

   static final int MCS_UNBIND = 6;

   static final int START_CLEANING_PACKAGE = 7;

   static final int FIND_INSTALL_LOC = 8;

   static final int POST_INSTALL = 9;

   static final int MCS_RECONNECT = 10;

   static final int MCS_GIVE_UP = 11;

   static final int UPDATED_MEDIA_STATUS = 12;

   static final int WRITE_SETTINGS = 13;

 

//创建/data/data和/data/app-private目录

File dataDir =Environment.getDataDirectory();//获得/data目录

mAppDataDir = new File(dataDir,"data");

mDrmAppPrivateInstallDir = newFile(dataDir, "app-private");

 

// Read permissions from/system/etc/permission directory.

//这些文件在framework/base/data/etc

Void readPermissions()

{

      //解析/system/etc/permission/下的*.xml文件,获取权限信息 

      //最后解析该目录下的 platform.xml文件,使该文件里的权限在栈顶出现,以便预先处理

      //这个文件记录了系统级应用的uid及其拥有的权限

       File permFile = newFile(Environment.getRootDirectory(),"etc/permissions/platform.xml");

       readPermissionsFromXml(permFile);

       //该函数的功能如下:

      通过xml解析器解释*.xml文件,提取标签名“group”,"permission","assign-permission","library","feature"并进行相应处理。在platform.xml中对底层的系统用户和组ID(groupids)同上层的由平台管理的permission名字之间进行了关系映射,使它们关联起来。当一个应用被授予某个权限后,同时属于已知的组ID,这个应用就可以进行允许这个组的文件系统操作,如(read,write,execute)。这里记录了一些系统级的应用的 uid 对应的 permission

     //每个标签的含义:

group:安装到系统中的所有APK都具备的组ID。

permission:可以指定一个权限与几个组ID对应。当一个APK被授予这个权限时,它也同    时属于这几个组。

assign-permission:把一个权限赋予一个UID,当进程使用这个UID运行时,就具备了这个权限。

library:为系统添加一些扩展库用的。对应的.jar文件放在/system/framework/目录下。比如GoogleMap相关的库。

feature:每添加一个硬件,都要增加对应的feature。将以上解析的结果对应放入mGlobalGids,mSettings.mPermissions,mSystemPermissions,mSharedLibraries,,mAvailableFeatures等几个list中供系统查询和权限配置使用。

}

 

 

//readLP()会判断/data/system/packages.xml文件是否存在,如果不存在则返回false,如果存在则进行解析,在系统第一次启动时packages.xml文件是不存在的,由writeLP()创建该文件,并将该文件写到nand上,下次开机会直接读取并解析这个文件。解析的过程即是按照xml定义的标签,将对应的属性和值添加到全局列表中。packages.xml文件中记录了系统安装的所有apk的属性权限的信息,当系统中的apk安装,删除或升级时,改文件就会被更新。

<permissions>标签定义了目前系统中定义的所有权限。主要分为两类:系统定义的(package属性为Android)和APK定义的(package属性为APK的包名)

sharedUserId/userId:Android系统启动一个普通的APK时,会为这个APK分配一个独立的UID,这就是userId。如果APK要和系统中其它APK使用相同的UID的话,那就是sharedUserId。

perms:APK的AndroidManifest.xml文件中,每使用一个<uses-permission>标签,<perms>标签中就会增加一项。

<shared-user>代表一个共享UID,通常,共同实现一系列相似功能的APK共享一个UID。<perms>标签中的 权限代表了这个共享UID的权限,所有使用的同一个共享UID的APK运行在同一进程中,这个进程的UID就是这个共享UID,这些APK都具有这个共享 UID的权限。

name:共享UID的名字,在APK的Android:sharedUserId属性中使用。

userId:使用这个共享UID的所有APK运行时所在的进程的UID。

mRestoredSettings =mSettings.readLP();

 

 

//判断boot class path里的文件(jar文件)是否需要dexopt(判断标准是检查DvmGlobals.bootClassPath是否已经包含这个文件),如果需要先将文件添加到libFiles里,同时进行dexopt:由Installer通过socket将命令传给installd的run_dexopt,最终调用的是/system/bin/dexopt对jar包进行处理。如果已经进行了dexopt动作,则将/data/dalvik-cache下的以data开头的文件删除,后续重新建立。如果外部库mSharedLibraries列表存在,也要检查列表中的元素是否需要dexopt,如果需要则和boot class path进行相同处理。对于/system/framework下apk和jar文件检查是否需要dexopt.

 

String bootClassPath =System.getProperty("java.boot.class.path");

 if(bootClassPath != null) {

       String[] paths = splitString(bootClassPath, ':');

            for (int i=0; i<paths.length; i++) {

                try {

                           if(dalvik.system.DexFile.isDexOptNeeded(paths[i])) {//是否需要dexopt

                                libFiles.add(lib);//添加到libFiles列表

                                 mInstaller.dexopt(paths[i],Process.SYSTEM_UID, true);//进行dexopt

                             }

                        }

              }

  }

//对framework-res.apk不进行dexopt直接添加到libFiles

       libFiles.add(mFrameworkDir.getPath() + "/framework-res.apk");

 

//启动AppDirObserver线程监测/system/framework,/system/app,/data/app,/data/app-private几个目录的事件,主要监听的是add和remove事件。对于目录监听底层通过inotify机制实现,inotify 是在 2.6.13 中引入的新功能,它为用户态监视文件系统的变化提供了强大的支持;inotify是一种文件系统的变化通知机制,如文件增加、删除等事件可以立刻让用户态得知,当监测到事件发生时该线程做何处理呢?

MframeworkInstallObserver =

                 newAppDirObserver(mFrameworkDir.getPath(),OBSERVER_EVENTS, true);

mFrameworkInstallObserver.startWatching();

 

//调用scanDirLI解析以上目录下的apk文件,该函数是包管理服务的重要函数,在后面有详细的分析

   private void scanDirLI(File dir, int flags, int scanMode) {

       String[] files = dir.list();

       for (i=0; i<files.length; i++)

       {

           File file = new File(dir, files[i]);

           PackageParser.Package pkg =

                 scanPackageLI(file,flags|PackageParser.PARSE_MUST_BE_APK, scanMode);

           if (pkg == null && (flags & PackageParser.PARSE_IS_SYSTEM)== 0 &&

                    mLastScanError ==PackageManager.INSTALL_FAILED_INVALID_APK) {

                // Delete the apk

                file.delete();

           }

       }

    }

 

//对于不存在的system apk调用以下函数删除掉

  Iterator<PackageSetting> psit =mSettings.mPackages.values().iterator();

  PackageSetting ps = psit.next();

   if((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0

                      &&!mPackages.containsKey(ps.name)   

                      &&!mSettings.mDisabledSysPackages.containsKey(ps.name))

   {

         psit.remove();

         mInstaller.remove(ps.name);

   }

 

//在解析完以上目录下的apk后,更新应用的权限

      updatePermissionsLP(null, null, true, regrantPermissions,regrantPermissions);

              

//writeLP 会生成packages.xml和packages.list文件,packages.list的数据格式是:pkgName,userId,debugFlag,dataPath(包的数据路径),packages.xml保存了每个已经安装apk的详尽的信息

      mSettings.writeLP();

以上是包管理服务在系统启动时做的全部工作。

 

/

下面解析其中一个比较重要的函数scanDirLI:

 

private void scanDirLI(File dir, int flags,int scanMode) {

       String[] files = dir.list();

       for (i=0; i<files.length; i++)

       {

           File file = new File(dir, files[i]);

           PackageParser.Package pkg =

                     scanPackageLI(file,flags|PackageParser.PARSE_MUST_BE_APK, scanMode);

            if (pkg == null && (flags &PackageParser.PARSE_IS_SYSTEM) == 0 &&

                    mLastScanError ==PackageManager.INSTALL_FAILED_INVALID_APK) {

                // Delete the apk

                file.delete();

           }

       }

    }

这个函数结构比较简单,对监测的几个目录下的每一个apk文件继续通过scanPackageLI(file,flags|PackageParser.PARSE_MUST_BE_APK, scanMode)进行解析,对不存在且安装失败已经无效的非系统apk直接删除。

 

 

 

 

 

 

 


 


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

为pkgSettings添加新的时间戳:

pkgSetting.setTimeStamp(scanFileTime);

 

 

 


                                       图2 scanDirLI流程分析

 

scanPackageLI是一个重定义函数,它的作用是:用 PackageParser的两个重定义函数parsePackage解析package的asset,res,建立asset资源文件路径;解析AndroidManifest.xml文件,建立PackageParser.Package结构,这个结构保存了从AndroidManifest.xml解析出的package的信息。对package进行数字签名及完整性校验,

 

private PackageParser.PackagescanPackageLI(File scanFile, int parseFlags, int scanMode)

{

      //实例化一个 PackageParser对象

         PackageParser pp = new PackageParser(scanPath);

 

      // parsePackage也是一个重定义函数,它主要做了三件事,一个是解析apk中的asset下的文件,一个是解析res下的文件,关于asset与res区别请参考:http://blog.csdn.net/hshm20517/archive/2011/06/02/6461890.aspx。然后通过重定义函数parsePackage(Resourcesres, XmlResourceParser parser, int flags, String[] outError) 对apk的AndroidManifest.xml进行解析,将每个标签对应的信息添加到每个包的相关列表中,如将标签application下的activity通过pkg.activities.add(a)添加到package的activities列表,将service添加到owner.services.add(s)。

     PackageParser.Package pkg = pp.parsePackage(scanFile, scanPath,mMetrics, parseFlags);

 

    //检查这个package是否已经存在,以及是否重命名过,以及该系统package是否可以被更新,如果可以被更新,则对比系统分区和data分区的package版本,如果系统分区的package高于data分区的版本,则保留系统分区的package

    

     //对package进行签名认证,如果是system img里的,只是通过AndroidManifest.xml获得签名,对签名校验,不会对全部文件进行有效性检查;否则,就要结合META-INF/进行签名和有效性校验

     collectCertificatesLI(pp, ps, pkg, scanFile, parseFlags);

 

     //调用重定义函数继续进行解析,将每个apk解析出的标签信息添加到全局的列表里。如将每个apk的recervers列表里的元素pkg.receivers.get(i),通过mReceivers.addActivity(a, "receiver")添加到全局列表mReceivers里

     return scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE);

}

 

<补充知识>

res/raw和assets区别

 

*res/raw和assets的相同点:

1.两者目录下的文件在打包后会原封不动的保存在apk包中,不会被编译成二进制。


*res/raw和assets的不同点:
1.res/raw中的文件会被映射到R.java文件中,访问的时候直接使用资源ID即R.id.filename;assets文件夹下的文件不会被映射到R.java中,访问的时候需要AssetManager类。
2.res/raw不可以有目录结构,而assets则可以有目录结构,也就是assets目录下可以再建立文件夹

*读取文件资源:

1.读取res/raw下的文件资源,通过以下方式获取输入流来进行写操作

InputStream is =getResources().openRawResource(R.id.filename);  

2.读取assets下的文件资源,通过以下方式获取输入流来进行写操作

AssetManager am = null;  

am = getAssets();  

InputStream is =am.open("filename");  

(用于内置文件但不知道文件名称,需要筛选出想要的文件然后拷贝到目标目录中,推荐内置在assets文件夹中)
1.res/raw目录:
通过反射的方式得到R.java里面raw内部类里面所有的资源ID的名称,然后通过名称获取资源ID的值来读取我们想要的文件。
2.assets目录:
getAssets().list("");来获取assets目录下所有文件夹和文件的名称,再通过这些名称读取我们想要的文件。

另,在处理asset时,android限制最大的数据是1M,超出后会报错误。

</>

 

public Package parsePackage(FilesourceFile, String destCodePath, DisplayMetrics metrics, int flags)

{

           //解析/asset下的文件

           assmgr = new AssetManager();

           int cookie = assmgr.addAssetPath(mArchiveSourcePath);

           parser = assmgr.openXmlResourceParser(cookie,"AndroidManifest.xml");

 

           //解析/Res下的文件,通过 parsePackage函数解析AndroidManifest.xml文件

           Resources res = new Resources(assmgr, metrics, null);

           pkg = parsePackage(res, parser, flags, errorText);

 

           // 设置代码路径和资源路径

           pkg.mPath = destCodePath;

            pkg.mScanPath = mArchiveSourcePath;

}

 

Package parsePackage(Resources res,XmlResourceParser parser, int flags, String[] outError)

{

解析AndroidManifest.xml里的各个标签,并对pkg的mVersionCode,mSharedUserId,

mSharedUserLabel,installLocation等变量赋值。对于application,permission-group,permission,permission-tree,uses-permission,uses-configuration,uses-feature,uses-sdk,supports-screens,protected-broadcast,instrumentation,original-package,adopt-permissions,eat-comment等标签调用相关函数进行处理。解析出每个标签下的子标签的信息,然后将这些信息添加到每个package的对应列表中,如将application下的activity通过pkg.activities.add(a)添加到package的activities列表。

 

      //将pkg返回

      return pkg;

        

}

 

private PackageParser.PackagescanPackageLI(PackageParser.Package pkg, int parseFlags, int scanMode) {

 

       (1)Check all shared libraries and map to their actual file path.

       (2)check pkg.reqFeatures in mAvailableFeatures

       (3)Check and note if we are renaming from an original package name

       (4)Check if we are renaming from an original package name.

       对于original package不是太了解,还需要继续研究

 

       判断新装应用的content providers是否与已经安装应用的产生冲突。

       if (mPlatformPackage == pkg) {//如果包名以android开头的,则将应用的dataDir设为/data/system

           // The system package is special.

           dataPath = new File (Environment.getDataDirectory(),"system");

           pkg.applicationInfo.dataDir = dataPath.getPath();

       }else {

           // This is a normal package, need to make its data directory.

           dataPath = getDataPathForPackage(pkg);

       if (dataPath.exists()) {//如果路径存在

            使用FileUtils.getPermissions获取dataPath的权限,

           if (mOutPermissions[1] == pkg.applicationInfo.uid  || !Process.supportsProcesses())

                {

                    pkg.applicationInfo.dataDir= dataPath.getPath();

               } else {

                     mInstaller.remove(pkgName);//将该包删除

                     mInstaller.install(pkgName, pkg.applicationInfo.uid,pkg.applicationInfo.uid);

                       //重新安装该应用

                }

               pkg.applicationInfo.dataDir =dataPath.getPath();//dataDir重新赋值

       }

      else

       {        //如果路径不存在则直接install安装

                  mInstaller.install(pkgName,pkg.applicationInfo.uid, pkg.applicationInfo.uid);

        }

 

        // Perform shared library installation and dex validation and

       // optimization, if this is not a system app.

       performDexOptLI(pkg, forceDex);

 

         //如果新的应用已经安装,请求ActivityManager将旧的kill掉,以免使用时造成混乱

         if ((parseFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {

           killApplication(pkg.applicationInfo.packageName,pkg.applicationInfo.uid);

       }

          

           // Add the new setting to mSettings

           mSettings.insertPackageSettingLP(pkgSetting, pkg);

           // Add the new setting to mPackages

           mPackages.put(pkg.applicationInfo.packageName, pkg);

           // Make sure we don't accidentally delete its data.

           mSettings.mPackagesToBeCleaned.remove(pkgName);

 

           以下将每个包的provider,service,activity等信息添加到全局列表中

           mServices.addService(s);

           mReceivers.addActivity(a, "receiver");

           mActivities.addActivity(a, "activity");

           mPermissionGroups.put(pg.info.name, pg);

           permissionMap.put(p.info.name, bp);

            mInstrumentation.put(a.getComponentName(),a);

           mProtectedBroadcasts.add(pkg.protectedBroadcasts.get(i));

}

 

 

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android apk安装管理(PackageManagerService 分析) 的相关文章

  • View系列 (三) — Measure 流程详解

    Measure 流程详解 一 概述 二 单一 View 的测量流程 1 流程图 2 源码分析 三 ViewGroup 的测量流程 1 流程图 2 源码分析 一 概述 测量过程分为 View的measure过程 和 ViewGroup的mea
  • android之媒体硬解OMX的实现

    转自 http blog csdn net vincent blog article details 7578112 android的多媒体部分采用的编解码标准是OMX 当然这个标准是用于硬件编解码的 软件编解码在这里我就不说了 直接从st
  • android 启动过程分析

    Servicemanager需要先启动 zygote后面的service需要用到servicemanager的服务
  • 《Linux设备节点创建》用户空间ueventd创建设备节点规则

    转自 http blog csdn net tankai19880619 article details 11726371 说明 本文基于Android2 3和Linux2 6 其余版本仅供参考 一 devfs udev和sysfs是什么关
  • Activity启动流程源码分析-浅析生命周期函数

    源码分析 接着上一篇 Activity启动流程源码分析 setContentView源码阅读 的讲解 本节介绍一下Activity的生命周期函数何时被调用 要看Activity的生命周期函数何时被调用 不得不翻阅 ActivityThrea
  • android 系统级应用和服务的启动流程

    activityManagerService java 1 systemRaady 收到systemReady 通知 2 AppGlobals getPackageManager getPersistentApplications STOC
  • http://blog.csdn.net/haomcu/article/details/7267090

    转自 http blog csdn net haomcu article details 7267090 一 Android平台Wifi的基本代码路径 1 Wpa supplicant源码部分 external wpa supplicant
  • 为什么ViewGroup的onDraw()方法不执行

    问题 ViewGroup onDraw不执行的原因 怎么让ViewGroup onDraw执行 android代码一直在优化 我看了几个版本的源码 目前 我用的是API30的源码 再去看ViewGroup为什么不走onDraw 的时候 已经
  • 1-APP启动源码分析-1

    桌面app也就是我们认识的launcher app 点击app icon启动到app内部的过程分为2种情况 一种是冷启动 一种叫热启动 冷启动 系统没有创建过app的进程 也就是后台没有此app进程 所以冷启动系统会创建一个新的进程分配给a
  • Python+uiautomator2手机UI自动化测试实战 --1. 环境搭建

    转自 https blog csdn net ricky yangrui article details 81414870 一 简介 uiautomator2是一个python库 用于Android的UI自动化测试 其底层基于Google
  • Android apk安装管理(PackageManagerService 分析)

    Android apk安装管理 PackageManagerService 分析 本篇主要分析了系统启动阶段包管理服务的启动流程 其中的几个接口在apk安装时也会被调用 包管理服务启动时主要做的工作大致有如下几方面 1 建立java层的in
  • Activity启动流程

    简述 Activity 启动分为两种 1 Activity中通过startActivity 方法启动一个Activity 2 从桌面通过点击应用图标启动一个App然后显示Activity 我们通过第二点来分析 更全面一点 先走一波流程图 以
  • Android Display System --- Surface Flinger

    转自一醉千年大大 http blog csdn net yili xie archive 2009 11 12 4803527 aspx SurfaceFlinger 是Android multimedia 的一个部分 在Android 的
  • Android源码分析 - Service启动流程

    开篇 本篇以android 11 0 0 r25作为基础解析 在之前的文章中 我们已经分析过了四大组件中Activity和ContentProvider的启动流程 这次我们就来讲讲四大组件之一的Service是如何启动和绑定的 流程图 在查
  • MediaScanner生成及保存thumbnail的方式

    转自 http blog csdn net qikaibinglan article details 6130589 本文简单研究一下MediaScanner生成及保存thumbnail的方式 并给出代码快速查询图片的thumbnail 1
  • android MediaPlayer 中的JNI总结

    1 在android media MediaPlayer cpp 中 定义fields静态变量 里面有两个重要的成员变量 context 用来保存创建的mediaplayer post event 用来将JNI层的事件回调给JAVA层 实现
  • android input 机制源码分析

    具体文字说明请参考 http blog csdn net luoshengyang article details 6882903
  • Android TabLayout setupWithViewPager()方法绑定Viewpager不显示文字

    setupWithViewPager 做了什么事情 TabLayout tabLayout findViewById R id tabLayout ViewPager viewPager findViewById R id viewPage
  • Dalvik虚拟机简要介绍和学习计划

    通过修改 android framework base core jni AndroidRuntime cpp 中的 property get dalvik vm heapsize heapsizeOptsBuf 4 16m 来修改 dal
  • android 中的的 sp/wp/RefBase

    转自 http blog csdn net innost article details 6752443 5 1 概述 初次接触Android源码时 见到最多的一定是sp和wp 即使你只是沉迷于Java世界的编码 那么Looper和Hand

随机推荐

  • Map集合案例-统计投票人数

    需求 某个班级80名学生 现在需要组成秋游活动 班长提供了四个景点依次是 A B C D 每个学生只能选择一个景点 请统计出最终哪个景点想去的人数最多 利用Map集合进行统计 A06 HashMapDemo2 java package da
  • MySQL主从复制搭建步骤详解

    MySQL主从复制搭建步骤详解 1 简介 MySQL主从复制是一种数据库高可用性的解决方案 通过将数据从一个MySQL主服务器同步到一个或多个从服务器来提高数据库的可用性和性能 本文将详细介绍如何搭建MySQL主从复制环境 2 环境准备 在
  • linux下Nerdtree安装方法

    目录 1 下载Nerdtree 2 linux下安装 3 成功享受吧 1 下载Nerdtree 百度网盘下载 地址为链接 百度网盘 请输入提取码 提取码 07e3 来自百度网盘超级会员V4的分享 github方式下载 地址为 https g
  • 字符串 去掉空格 C++

    去掉空格 时间限制 1Sec 内存限制 128MB 提交 5807 解决 3117 题目描述 读入一些字符串 将其中的空格去掉 输入 输入为多行 每行为一个字符串 字符串只由字母 数字和空格组成 长度不超过80 输入以 End of fil
  • 【五、反向代理及其相关配置】

    文章目录 反向代理及其相关配置 1 反向代理 2 正向代理 3 网关 4 Nginx做反向代理的缺点 5 反向代理配置 1 跳转到外网网站上 2 跳转到本机服务器上 反向代理及其相关配置 1 反向代理 服务器提供的代理为反向代理 原理 当用
  • 网络安全(黑客)自学的误区

    一 自学网络安全学习的误区和陷阱 1 不要试图先成为一名程序员 以编程为基础的学习 再开始学习 我在之前的回答中 我都一再强调不要以编程为基础再开始学习网络安全 一般来说 学习编程不但学习周期长 而且实际向安全过渡后可用到的关键知识并不多
  • Trie树【数组实现】

    全文目录 Trie的表现形式 数组实现 Trie 树 代码 Trie的表现形式 Trie树主要用来实现字符串的存储和快速查找 其表现形式类似一颗多叉树 每个节点表示字符串的一个字符 由于可能会存在类似 abc 和 abcde 这样的数据 所
  • 大数据和人工智能到底是什么关系

    大数据和人工智能的关系 首先要说什么是大数据 这些年来 大数据先是被神化 继而又被妖魔化 到了今天 其实谁也不知道别人所谓的大数据指的是什么 有时候大数据的定义里既有平台 硬件 又有分析技术 但为了说清楚大数据和人工智能的关系 我们还是回归
  • 下一波加密浪潮:站在风口上的“NFT”!

    如果说2020是区块链的 Defi 大火年 流动性挖矿让不少用户和平台完美体验了一场红利盛宴 那么2021则杀出的黑马 NFT 升级为主角 拉开了表演的序幕 不少项目和用户目光纷纷投向这一赛道 试图寻找新的财富机遇 NFT定义 它究竟是什么
  • 特征脸EigenFace、Fisher脸FisherFace、LBP直方图LBPHFace

    在最新版的2 4 2中 文档的更新也是一大亮点 refrence manual扩充了200多页的内容 添加了contrib部分的文档 contrib就是指OpenCV中新添加的模块 但又不是很稳定 可以认为是一个雏形的部分 这次结合refm
  • 【NLP】使用 PyTorch 通过 Hugging Face 使用 BERT 和 Transformers 进行情感分析

    大家好 我是Sonhhxg 柒 希望你看完之后 能对你有所帮助 不足请指正 共同学习交流 个人主页 Sonhhxg 柒的博客 CSDN博客 欢迎各位 点赞 收藏 留言 系列专栏 机器学习 ML 自然语言处理 NLP 深度学习 DL fore
  • Android 项目必备(十二)--> APP 底部导航栏最佳实践

    文章目录 简介 TabLayout Fragment 1 效果图 2 布局文件 3 代码 BottomNavigationView Fragment 1 效果图 2 布局文件 3 main bottom navigation xml 4 代
  • Python实现拉格朗日插值法填补缺失值

    s为列向量 n为被插值的位置 k为取前后的数据个数 默认为5 from scipy interpolate import lagrange 导入拉格朗日插值函数 def ployinterp column s n k 5 y s iloc
  • 每日技巧(word条形图更改横坐标)

    问题 在工作中 遇到条形图需要更改纵坐标的数据 那该如何解决这个问题呢 解决方案 右键 选中编辑数据中的在Excel中编数据 D 点击完成后会出现 如下图的Excel的表格 数据编辑完成后 则横坐标则会改变 如下图 今日小技巧到此为止
  • 消息队列 RabbitMQ入门:Linux(Docker)中安装和卸载RabbitMQ服务

    文章目录 前言 一 Linux中安装RabbitMQ 下载Erlang 下载RabbitMQ 进入Linux进行安装 启动RabbitMQ 二 RabbitMQ Web管理页面 安装RabbitMQ Web管理页面 访问管理页面 三 使用D
  • 协同过滤算法_《推荐系统实践》3.基于物品的协同过滤算法

    基于物品的协同过滤算法 item based collaborative filtering 以下简称ItemCF 算法思想 给用户推荐那些和他们之前喜欢的物品相似的物品 不过 ItemCF算法并不利用物品的内容属性计算物品之间的相似度 它
  • android二级菜单ui,Android UI 之实现多级树形列表TreeView示例

    所谓TreeView就是在Windows中常见的多级列表树 在Android中系统只默认提供了ListView和ExpandableListView两种列表 最多只支持到二级列表的实现 所以如果想要实现三级和更多层次的列表 就需要我们自己来
  • python pandas使用pipe管道增强代码可读性

    pandas dataframe的pipe文档链接 https pandas pydata org docs reference api pandas DataFrame pipe html 使用pipe可以像水流一样 有顺序的执行data
  • 三子棋【C语言实现】

    三子棋 让我们一起用C语言来玩一场三子棋的游戏吧 文章目录 三子棋 前言 一 基本步骤 二 具体实现 1 菜单界面 2 创建棋盘 3 棋盘初始化 4 打印棋盘 5 玩家落子 6 电脑落子 7 判断输赢 8 运行演示 1 玩家获胜 2 电脑获
  • Android apk安装管理(PackageManagerService 分析)

    Android apk安装管理 PackageManagerService 分析 本篇主要分析了系统启动阶段包管理服务的启动流程 其中的几个接口在apk安装时也会被调用 包管理服务启动时主要做的工作大致有如下几方面 1 建立java层的in