APK 安装过程 及 原理 详解

安卓 APK 安装过程详解 在安卓设备上安装应用程序的过程中,APK(Android Package)文件是最常见的安装文件格式。为了顺利地在设备上安装APK,我们需要确保设备与计算机之间的ADB(Android Debug Bridge)连接正常,并且有适当的权限来执行各种文件操作。本篇文章将详细介绍从连接设备到成功安装APK的整个过程。通过以上步骤,您可以成功地在安卓设备上安装APK文件。这些操作不仅让您了解了ADB工具的基本使用方法,还教会您如何修改设备的文件权限以便进行各种安装操作。 阅读详情

APK为AndroidPackage的缩写

Android应用安装有如下四种方式:

1.系统应用安装――开机时完成,没有安装界面

2.网络下载应用安装――通过market应用完成,没有安装界面

3.ADB工具安装――没有安装界面。

4.第三方应用安装――通过SD卡里的APK文件安装,有安装界面,由 packageinstaller.apk 应用处理安装及卸载。

-------------------------------------

应用安装涉及到如下几个目录:        

system/app ---------------系统自带的应用程序,获得adb root权限才能删除

data/app  ---------------用户程序安装的目录。用户 安装时把apk文件 复制 到此目录
data/data ---------------存放应用程序的数据
data/dalvik-cache--------将apk中的dex文件安装到dalvik-cache目录下(dex文件是dalvik虚拟机的可执行文件,其大小约为原始apk文件大小的四分之一)

安装过程:

复制APK安装包到data/app目录下,解压并扫描安装包,把dex文件(Dalvik字节码)保存到dalvik-cache目录,并data/data目录下创建对应的应用数据目录。

卸载过程:

删除 安装过程中在上述三个目录下创建的文件及目录。

-------------------------

安装应用的过程解析

一.开机安装 
PackageManagerService处理各种 应用的安装,卸载,管理等工作,开机时 由systemServer启动此服务

(源文件路径:android\frameworks\base\services\java\com\android\server\PackageManagerService.java)

PackageManagerService服务 启动的流程:

1.首先 扫描安装“system\framework”目录下的jar包


  

 // Find base frameworks (resource packages without code).

            mFrameworkInstallObserver = new AppDirObserver(

                mFrameworkDir.getPath(), OBSERVER_EVENTS, true);

            mFrameworkInstallObserver.startWatching();

            scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM

                    | PackageParser.PARSE_IS_SYSTEM_DIR,

                    scanMode | SCAN_NO_DEX, 0);

2.扫描 安装系统system/app的应用程序


  

  // Collect all system packages.

            mSystemAppDir = new File(Environment.getRootDirectory(), "app");

            mSystemInstallObserver = new AppDirObserver(

                mSystemAppDir.getPath(), OBSERVER_EVENTS, true);

            mSystemInstallObserver.startWatching();

            scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM

                    | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);

3.制造商的目录下/vendor/app应用包


  

 // Collect all vendor packages.

            mVendorAppDir = new File("/vendor/app");

            mVendorInstallObserver = new AppDirObserver(

                mVendorAppDir.getPath(), OBSERVER_EVENTS, true);

            mVendorInstallObserver.startWatching();

            scanDirLI(mVendorAppDir, PackageParser.PARSE_IS_SYSTEM

                    | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);

4.扫描“data\app”目录,即用户安装的第三方应用


  

scanDirLI(mAppInstallDir, 0, scanMode, 0);

5.扫描" data\app-private"目录,即安装DRM保护的APK文件(一个受保护的歌曲或受保 护的视频是使用 DRM 保护的文件)


  

scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,

                    scanMode, 0);

扫描方法的代码清单


  

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

        String[] files = dir.list();

        if (files == null) {

            Log.d(TAG, "No files in app dir " + dir);

            return;

        }

        if (false) {

            Log.d(TAG, "Scanning app dir " + dir);

        }

        int i;

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

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

            if (!isPackageFilename(files[i])) {

                // Ignore entries which are not apk's

                continue;

            }

            PackageParser.Package pkg = scanPackageLI(file,

                    flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime);

            // Don't mess around with apps in system partition.

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

                    mLastScanError == PackageManager.INSTALL_FAILED_INVALID_APK) {

                // Delete the apk

                Slog.w(TAG, "Cleaning up failed install of " + file);

                file.delete();

            }

        }

    }

并且从该扫描方法中可以看出调用了scanPackageLI()

private PackageParser.Package scanPackageLI(File scanFile,

int parseFlags, int scanMode, long currentTime)

跟踪scanPackageLI()方法后发现,程序经过很多次的if else 的筛选,最后判定 可以安装apk后,调用了 mInstaller.install


  

if (mInstaller != null) {

                    int ret = mInstaller.install(pkgName, useEncryptedFSDir,  pkg.applicationInfo.uid,pkg.applicationInfo.uid);

                    if(ret < 0) {

                        // Error from installer

                        mLastScanError =    PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;

                        return null;

                    }

                }

mInstaller.install()  通过    

  LocalSocketAddress address = new LocalSocketAddress(

                "installd", LocalSocketAddress.Namespace.RESERVED);

指挥installd在C语言的文件中完成工作

PackageManagerService小节 :

1)从apk, xml中载入pacakge信息, 存储到内部成员变量中, 用于后面的查找. 关键的方法是scanPackageLI().
2)各种查询操作, 包括query Intent操作.
3)install package和delete package的操作. 还有后面的关键方法是installPackageLI().


二、从网络上下载应用:

下载完成后,会自动调用Packagemanager的安装方法installPackage()

   /* Called when a downloaded package installation has been confirmed by the user */

    由英文注释可见PackageManagerService 类的installPackage()函数为 安装程序 的入口。


  

 public void installPackage(

            final Uri packageURI, final IPackageInstallObserver observer, final int flags,

            final String installerPackageName) {

        mContext.enforceCallingOrSelfPermission(

                android.Manifest.permission.INSTALL_PACKAGES, null);

        Message msg = mHandler.obtainMessage(INIT_COPY);

        msg.obj = new InstallParams(packageURI, observer, flags,

                installerPackageName);

        mHandler.sendMessage(msg);

    }

其中是通过PackageHandler的实例mhandler.sendMessage(msg)把信息发给继承Handler的类HandleMessage()方法



  

class PackageHandler extends Handler{

                 

*****************省略若干********************

         public void handleMessage(Message msg) {

            try {

                doHandleMessage(msg);

            } finally {

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

            }

        }

   ******************省略若干**********************

 }

把信息发给doHandleMessage()方法,方法中用switch()语句进行判定传来Message


  

 void doHandleMessage(Message msg) {

            switch (msg.what) {

           

                case INIT_COPY: {

                    if (DEBUG_SD_INSTALL) Log.i(TAG, "init_copy");

                    HandlerParams params = (HandlerParams) msg.obj;

                    int idx = mPendingInstalls.size();

                    if (DEBUG_SD_INSTALL) Log.i(TAG, "idx=" + idx);

                    // If a bind was already initiated we dont really

                    // need to do anything. The pending install

                    // will be processed later on.

                    if (!mBound) {

                        // If this is the only one pending we might

                        // have to bind to the service again.

                        if (!connectToService()) {

                            Slog.e(TAG, "Failed to bind to media container service");

                            params.serviceError();

                            return;

                        } else {

                            // Once we bind to the service, the first

                            // pending request will be processed.

                            mPendingInstalls.add(idx, params);

                        }

                    } else {

                        mPendingInstalls.add(idx, params);

                        // Already bound to the service. Just make

                        // sure we trigger off processing the first request.

                        if (idx == 0) {

                            mHandler.sendEmptyMessage(MCS_BOUND);

                        }

                    }

                    break;

                }

                case MCS_BOUND: {

                    if (DEBUG_SD_INSTALL) Log.i(TAG, "mcs_bound");

                    if (msg.obj != null) {

                        mContainerService = (IMediaContainerService) msg.obj;

                    }

                    if (mContainerService == null) {

                        // Something seriously wrong. Bail out

                        Slog.e(TAG, "Cannot bind to media container service");

                        for (HandlerParams params : mPendingInstalls) {

                            mPendingInstalls.remove(0);

                            // Indicate service bind error

                            params.serviceError();

                        }

                        mPendingInstalls.clear();

                    } else if (mPendingInstalls.size() > 0) {

                        HandlerParams params = mPendingInstalls.get(0);

                        if (params != null) {

                            params.startCopy();

                        }

                    } else {

                        // Should never happen ideally.

                        Slog.w(TAG, "Empty queue");

                    }

                    break;

                }

            ****************省略若干**********************

}

}             

public final boolean sendMessage (Message msg)

public final boolean sendEmptyMessage (int what)

两者参数有别。

然后调用抽象类HandlerParams中的一个startCopy()方法

abstract class HandlerParams {

final void startCopy() {

   ***************若干if语句判定否这打回handler消息*******

handleReturnCode();

}
}

handleReturnCode()复写了两次其中有一次是删除时要调用的,只列出安装调用的一个方法


  

 @Override

        void handleReturnCode() {

            // If mArgs is null, then MCS couldn't be reached. When it

            // reconnects, it will try again to install. At that point, this

            // will succeed.

            if (mArgs != null) {

                processPendingInstall(mArgs, mRet);

            }

        }

这时可以清楚的看见 processPendingInstall()被调用。

其中run()方法如下


  

run(){

synchronized (mInstallLock) {

                        ************省略*****************

                        installPackageLI(args, true, res);

                   

 }

}

instaPacakgeLI()args,res参数分析

-----------------------------------------------------------------------------------------

//InstallArgs 是在PackageService定义的static abstract class InstallArgs 静态抽象类。


  

static abstract class InstallArgs {

*********************************************************************

其中定义了flag标志,packageURL,创建文件,拷贝apk,修改包名称,

                    还有一些删除文件的清理,释放存储函数。

    *********************************************************************

}

  class PackageInstalledInfo {

        String name;

        int uid;

        PackageParser.Package pkg;

        int returnCode;

        PackageRemovedInfo removedInfo;

 }

-----------------------------------------------------------------------------------------


  

private void installPackageLI(InstallArgs args, boolean newInstall, PackageInstalledInfo res) { int pFlags = args.flags; String installerPackageName = args.installerPackageName; File tmpPackageFile = new File(args.getCodePath()); boolean forwardLocked = ((pFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0); boolean onSd = ((pFlags & PackageManager.INSTALL_EXTERNAL) != 0); boolean replace = false; int scanMode = (onSd ? 0 : SCAN_MONITOR) | SCAN_FORCE_DEX | SCAN_UPDATE_SIGNATURE | (newInstall ? SCAN_NEW_INSTALL : 0); // Result object to be returned res.returnCode = PackageManager.INSTALL_SUCCEEDED; // Retrieve PackageSettings and parse package int parseFlags = PackageParser.PARSE_CHATTY | (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0) | (onSd ? PackageParser.PARSE_ON_SDCARD : 0); parseFlags |= mDefParseFlags; PackageParser pp = new PackageParser(tmpPackageFile.getPath()); pp.setSeparateProcesses(mSeparateProcesses); final PackageParser.Package pkg = pp.parsePackage(tmpPackageFile, null, mMetrics, parseFlags); if (pkg == null) { res.returnCode = pp.getParseError(); return; } String pkgName = res.name = pkg.packageName; if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_TEST_ONLY) != 0) { if ((pFlags&PackageManager.INSTALL_ALLOW_TEST) == 0) { res.returnCode = PackageManager.INSTALL_FAILED_TEST_ONLY; return; } } if (GET_CERTIFICATES && !pp.collectCertificates(pkg, parseFlags)) { res.returnCode = pp.getParseError(); return; } // Get rid of all references to package scan path via parser. pp = null; String oldCodePath = null; boolean systemApp = false; synchronized (mPackages) { // Check if installing already existing package if ((pFlags&PackageManager.INSTALL_REPLACE_EXISTING) != 0) { String oldName = mSettings.mRenamedPackages.get(pkgName); if (pkg.mOriginalPackages != null && pkg.mOriginalPackages.contains(oldName) && mPackages.containsKey(oldName)) { // This package is derived from an original package, // and this device has been updating from that original // name. We must continue using the original name, so // rename the new package here. pkg.setPackageName(oldName); pkgName = pkg.packageName; replace = true; } else if (mPackages.containsKey(pkgName)) { // This package, under its official name, already exists // on the device; we should replace it. replace = true; } } PackageSetting ps = mSettings.mPackages.get(pkgName); if (ps != null) { oldCodePath = mSettings.mPackages.get(pkgName).codePathString; if (ps.pkg != null && ps.pkg.applicationInfo != null) { systemApp = (ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; } } } if (systemApp && onSd) { // Disable updates to system apps on sdcard Slog.w(TAG, "Cannot install updates to system apps on sdcard"); res.returnCode = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; return; } if (!args.doRename(res.returnCode, pkgName, oldCodePath)) { res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; return; } // Set application objects path explicitly after the rename setApplicationInfoPaths(pkg, args.getCodePath(), args.getResourcePath()); pkg.applicationInfo.nativeLibraryDir = args.getNativeLibraryPath(); if (replace) { replacePackageLI(pkg, parseFlags, scanMode, installerPackageName, res); } else { installNewPackageLI(pkg, parseFlags, scanMode, installerPackageName,res); } }


最后判断 如果以前 不存在 那么调用installNewPackageLI()


  

private void installNewPackageLI(PackageParser.Package pkg, int parseFlags,int scanMode, String installerPackageName, PackageInstalledInfo res) { ***********************省略若干************************************************* PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode, System.currentTimeMillis()); ***********************省略若干************************************************** }


最后终于 回到了和 开机安装 一样的地方. 与开机方式安装 调用统一方法。

三、从ADB工具安装 

其 入口函数 源文件为pm.java 

(源文件路径:android\frameworks\base\cmds\pm\src\com\android\commands\pm\Pm.java)

其中\system\framework\pm.jar 包管理库

包管理脚本 \system\bin\pm 解析

Pm.java 文件里的 showUsage就是使用方法


  

private static void showUsage() { System.err.println("usage: pm [list|path|install|uninstall]"); System.err.println(" pm list packages [-f]"); System.err.println(" pm list permission-groups"); System.err.println(" pm list permissions [-g] [-f] [-d] [-u] [GROUP]"); System.err.println(" pm list instrumentation [-f] [TARGET-PACKAGE]"); System.err.println(" pm list features"); System.err.println(" pm path PACKAGE"); System.err.println(" pm install [-l] [-r] [-t] [-i INSTALLER_PACKAGE_NAME] [-s] [-f] PATH"); System.err.println(" pm uninstall [-k] PACKAGE"); System.err.println(" pm enable PACKAGE_OR_COMPONENT"); System.err.println(" pm disable PACKAGE_OR_COMPONENT"); System.err.println(" pm setInstallLocation [0/auto] [1/internal] [2/external]"); **********************省略************************** }


安装时候 会调用 runInstall()方法


  

private void runInstall() { int installFlags = 0; String installerPackageName = null; String opt; while ((opt=nextOption()) != null) { if (opt.equals("-l")) { installFlags |= PackageManager.INSTALL_FORWARD_LOCK; } else if (opt.equals("-r")) { installFlags |= PackageManager.INSTALL_REPLACE_EXISTING; } else if (opt.equals("-i")) { installerPackageName = nextOptionData(); if (installerPackageName == null) { System.err.println("Error: no value specified for -i"); showUsage(); return; } } else if (opt.equals("-t")) { installFlags |= PackageManager.INSTALL_ALLOW_TEST; } else if (opt.equals("-s")) { // Override if -s option is specified. installFlags |= PackageManager.INSTALL_EXTERNAL; } else if (opt.equals("-f")) { // Override if -s option is specified. installFlags |= PackageManager.INSTALL_INTERNAL; } else { System.err.println("Error: Unknown option: " + opt); showUsage(); return; } } String apkFilePath = nextArg(); System.err.println("\tpkg: " + apkFilePath); if (apkFilePath == null) { System.err.println("Error: no package specified"); showUsage(); return; } PackageInstallObserver obs = new PackageInstallObserver(); try { mPm.installPackage(Uri.fromFile(new File(apkFilePath)), obs, installFlags, installerPackageName); synchronized (obs) { while (!obs.finished) { try { obs.wait(); } catch (InterruptedException e) { } } if (obs.result == PackageManager.INSTALL_SUCCEEDED) { System.out.println("Success"); } else { System.err.println("Failure [" + installFailureToString(obs.result) + "]"); } } } catch (RemoteException e) { System.err.println(e.toString()); System.err.println(PM_NOT_RUNNING_ERR); } }


其中  

PackageInstallObserver obs = new PackageInstallObserver(); 

            mPm.installPackage(Uri.fromFile(new File(apkFilePath)), obs, installFlags,

                    installerPackageName);

如果 安装成功

obs.result == PackageManager.INSTALL_SUCCEEDED)

又因为有

IPackageManage mPm;

        mPm = IpackageManager.Stub.asInterface(ServiceManager.getService("package"));

Stub是接口IPackageManage的静态抽象类,asInterface是返回IPackageManager代理的静态方法。

因为class PackageManagerService extends IPackageManager.Stub

所以mPm.installPackage 调用 

    /* Called when a downloaded package installation has been confirmed by the user */

    public void installPackage(

            final Uri packageURI, final IPackageInstallObserver observer, final int flags,final String installerPackageName) 

这样  最后 就是 相当于 调用  从网络下载安装的 入口了。


四,从SD卡安装

系统调用 PackageInstallerActivity.java

源码路径:android/packages/apps/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java

进入这个Activity会判断信息是否有错,然后调用

      private void initiateInstall() 判断 是否 曾经有过 同名包的安装,或者包已经安装

通过后 执行private void startInstallConfirm() 点击OK按钮后经过一系列的安装信息的判断 Intent跳转到


  

public class InstallAppProgress extends Activity implements View.OnClickListener, OnCancelListener public void onCreate(Bundle icicle) { super.onCreate(icicle); Intent intent = getIntent(); mAppInfo = intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO); mPackageURI = intent.getData(); initView(); }


方法中调用了initView()方法


  

public void initView() { requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.op_progress); int installFlags = 0; PackageManager pm = getPackageManager(); try { PackageInfo pi = pm.getPackageInfo(mAppInfo.packageName, PackageManager.GET_UNINSTALLED_PACKAGES); if(pi != null) { installFlags |= PackageManager.INSTALL_REPLACE_EXISTING; } } catch (NameNotFoundException e) { } if((installFlags & PackageManager.INSTALL_REPLACE_EXISTING )!= 0) { Log.w(TAG, "Replacing package:" + mAppInfo.packageName); } PackageUtil.AppSnippet as = PackageUtil.getAppSnippet(this, mAppInfo, mPackageURI); mLabel = as.label; PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet); mStatusTextView = (TextView)findViewById(R.id.center_text); mStatusTextView.setText(R.string.installing); mProgressBar = (ProgressBar) findViewById(R.id.progress_bar); mProgressBar.setIndeterminate(true); // Hide button till progress is being displayed mOkPanel = (View)findViewById(R.id.buttons_panel); mDoneButton = (Button)findViewById(R.id.done_button); mLaunchButton = (Button)findViewById(R.id.launch_button); mOkPanel.setVisibility(View.INVISIBLE); String installerPackageName = getIntent().getStringExtra( Intent.EXTRA_INSTALLER_PACKAGE_NAME); PackageInstallObserver observer = new PackageInstallObserver(); pm.installPackage(mPackageURI, observer, installFlags, installerPackageName); }



方法最后 我们可以看到 再次调用安装接口 完成安装,相当于 又 调用了 网络安装。


---------------------------------------

总结:

1.系统应用安装――开机时完成

2.网络下载应用安装――最后 会 调用  系统应用安装 的 scanPackageLI 接口;

3.ADB工具安装――最后 会 调用   网络下载应用安装 的installPackage 接口,从而 相当于 走的 还是系统应用安装 的 路径;

4.第三方应用安装――由 packageinstaller.apk 应用处理安装及卸载,最后也会 调用   网络下载应用安装 的installPackage 接口,从而 相当于 走的 还是系统应用安装 的 路径。





使用 Python 获取实时港股行情数据 在本教程中,你将学习如何通过 Infoway API 获取港股实时行情数据,具体以腾讯控股(0700.HK)和汇丰控股(0005.HK)为例,使用 Python 进行数据请求和解析。 阅读详情

相关推荐

ANSYS Workbench接触分析实战:从算法选择到收敛难题破解

本文深入解析ANSYS Workbench接触分析的核心技术与实战技巧,涵盖四大接触算法选择、收敛难题破解秘籍及典型场景参数设置。通过实际案例演示如何优化接触刚度、pinball区域和非对称行为等关键参数,帮助工程师有效解决结构非线性问题,提升仿真效率和精度。

weixin_28705087的博客 360

一文带你搞懂APK安装过程

本篇从用户感受的APK安装讲起,分别详细梳理了 包管理器服务——PackageManagerService 的服务开启、初始化扫描过程;然后分析了对应原生系统的安装过程及页面;接着就是详细分析了安装流程,包括创建Session 发送APK、PMS中的APK拷贝操作、包括APK解析校验和dexOpt等具体安装流程;最后是安装结果的发送。内容确实很多,但是我们只需要重点了解 PMS的初始化、APK安装 的整体流程即可,无需关注繁杂的细节。

chuyouyinghe的专栏 7985

从Verilog到GDS:手把手教你搞定Calibre LVS物理验证(附常见报错解决)

本文详细介绍了从Verilog到GDS的Calibre LVS物理验证全流程,包括工具链配置、RTL到SPICE网表转换、LVS运行配置及高级技巧。重点解析了常见报错如网络不匹配、器件不匹配和电源引脚缺失的解决方案,帮助工程师高效完成芯片物理验证,确保设计正确性。

culing2941的博客 508

一文看懂Android APK安装的原理

Android APK 安装原理解析前言APK包的构成安装APK总结 前言 大家有没有想过一个应用的APK是怎么被安装到安卓手机上的,安装的本质是什么?我们知道,Windows应用程序的安装包是一个可执行的压缩包,安装的过程是把压缩包中的数据和依赖库拷贝到安装目录,再在桌面或启动栏创建快捷方式,那么安卓的APK安装过程是不是也是这样的呢?本文将为大家解答这些问题。 APK包的构成 首先,我看一下A...

好重好重的专栏 3867

编译原理实验二:LL(1)分析法(附C/C++完整代码)

本文讲述了LL(1)文法的原理及如何用C语言实现。并在给出的代码中实现了消除文法的左递归,First集,Follow集,select集的输出,构造预测分析表并输出,和对任意输入字符串的分析。程序总流程为:从文件读取文法 -> 输出文法信息 -> 消除左递归并输出消除后的文法 -> 计算First集并输出 -> 计算Follow集并输出 ->计算select集并输出 -> 判断是否是LL(1)文法 -> 构造预测分析表并输出 ->手动输入要分析的字符串 -> 输出分析过程和结果读取文件。

W_Wmwmwm的博客 6193

深入LSM6DSL的FIFO连续模式:基于STM32和MEMS库的数据流高效处理指南

本文深入探讨了LSM6DSL的FIFO连续模式,结合STM32硬件平台和ST官方MEMS库,提供了高效的数据流处理方案。通过优化I2C通信时序、中断驱动设计以及动态ODR调整,实现低功耗数据采集与处理,显著提升嵌入式传感器应用的性能和续航能力。

weixin_30387799的博客 350

APK安装过程及原理详解

应用程序包的安装是android的特点 APK为AndroidPackage的缩写 Android应用安装有如下四种方式: 1.系统应用安装――开机时完成,没有安装界面 2.网络下载应用安装――通过market应用完成,没有安装界面 3.ADB工具安装――没有安装界面。

Android 开发 5万+

从零开始:用君正T23ZN打造智能门锁双摄系统(含代码示例与调试技巧)

本文详细介绍了基于君正T23ZN视频处理芯片构建智能门锁双摄系统的完整实战方案。内容涵盖硬件选型与核心电路设计、软件开发环境搭建、双摄驱动框架、图像信号处理(ISP)调优、人脸识别算法集成与性能优化,以及系统调试与稳定性保障。文章提供了丰富的代码示例和调试技巧,旨在帮助嵌入式开发者高效实现具备人脸识别与监控功能的低功耗智能门锁视觉系统。

opencv7vision的博客 717

Android权限系统(一):开机获取权限信息

一.SystemConfig的整机权限信息   Android在SystemConfig的构造函数中会通过读取相关的文件来加载整机的权限信息。这些文件是{partition}/etc/permissions下面的文件。其中{partition}指代的分区包含了/system,/vendor,/odm,/oem/,/product/,/system_ext等目录。{partition}/etc/permissions目录下的文件以xml的形式存在以方便解析。 frameworks/base/core/java

Invoker123的博客 1万+

手把手教你学simulink(13.2)--构建一个单相并网逆变器Simulink仿真模型

基于Simulink的单相并网逆变器项目的详细仿真实例。这个项目将重点放在逆变器的详细建模、最大功率点跟踪(MPPT)控制策略、并网控制和仿真分析上。我们将使用MATLAB/Simulink来构建整个系统,并通过具体的代码和模块来实现各个功能。

MHD0815的博客 545

R语言中5种正态性检验的方法

统计学中的t检验和方差分析等方法的应用条件是样本都来自正态总体或近似正态总体,只有符合这个条件,才能用这些方法来检验各样本所属的总体参数的差异显著性。文本向大家介绍在R语言中检验正态性的几种方法: 1、Kolmogorov-Smirnov检验 K-S检验检验单一样本是否来自某一特定分布。比如检验一组数据是否为正态分布。它的检验方法是以样本数据的累积频数分布与特定理论分布比较,若两者间的差距很小,则推论该样本取自某特定分布族。K-S检验的原假设和备择假设为: H0:样本所来自的总体分布服从

Mrrunsen的博客 2万+

APK的安装过程及原理详解

应用程序包的安装是android的特点 APK为AndroidPackage的缩写 Android应用安装有如下四种方式: 1.系统应用安装――开机时完成,没有安装界面 2.网络下载应用安装――通过market应用完成,没有安装界面 3.ADB工具安装――没有安装界面。 4.第三方应用安装――通过SD卡里的APK文件安装,有安装界面,由         pack

sun_x_t的专栏 1073

[dprecated]第五章:TTM分析: 5.1 TTM设计目标和核心概念

TTM是Linux DRM框架下的通用显存管理模块,其复杂性源于需要满足多样化的显存管理需求。该文解析了TTM的核心结构体及其关系:ttm_device作为顶层管理者,包含多个ttm_resource_manager处理不同类型内存;ttm_buffer_object为核心对象,通过ttm_resource关联物理资源;其他组件如ttm_tt管理页表、ttm_pool优化分配效率。文章强调理解这些结构体关系是掌握TTM机制的基础,并建议国产芯片开发者参考这些功能需求。

图形/计算GPU的驱动与虚拟化技术分享园地 2128

APK安装过程及原理详解 【转】

本文转自: http://hi.baidu.com/android168/blog/item/06142a1ba1122774f624e4ed.html 原因:因为有涉及到挂载多张SD卡存储设备,安装到那张卡上?得了解下安装原理先。     来自华为内部资料  PackageInstaller 原理简述   应用安装是智能机的主要特点,即用户可以把各种应用(如游戏等)安装到手机上,并

ysy7116的专栏 364

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

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

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

上一篇: 如何把APK应用装到只读ROM
下一篇: PackageManagerService.java 构造函数的 分析
seableble
博客等级 码龄7年 137粉丝 700原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值