zoukankan      html  css  js  c++  java
  • 手机管理应用研究【2】—— 安装卸载篇

    欢迎转载,转载请注明:http://blog.csdn.net/zhgxhuaa


    说明

    Android通过PackageManagerService(后面简称Pms)进行包管理,其主要功能包含:用户ID分配、包解析、包的安装卸载等。

    本文不正确Pms进行分析。主要目的是探讨一下包安装。在本文中主要探讨包安装的相关操作,卸载作为安装的逆过程。实现类似,不再赘述。在应用安装/卸载这里主要有这么几个常见的功能点:

    A.     静默安装/卸载

    B.     秒装/秒卸载

    C.     卸载应用保存数据

    D.     系统内置应用卸载

    E.      卸载后清除残留数据

    以下就从这几个方面做一下分析介绍。


    Android中APK的安装方式

    在Android中APK的安装有三种方式:

    1、开机Pms初始化时,扫描包安装文件夹。

    @/frameworks/base/services/java/com/android/server/SystemServer.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public void initAndLoop() {
        ......
          
        IPackageManager pm = null;
          
        ......
          
        try {
            ......
              
            pm = PackageManagerService.main(context, installer,
                    factoryTest != SystemServer.FACTORY_TEST_OFF,
                    onlyCore);
            ......
        catch (RuntimeException e) {
            Slog.e("System""******************************************");
            Slog.e("System""************ Failure starting core service", e);
        }
        ......
    }

    @/frameworks/base/services/java/com/android/server/pm/PackageManagerService.java

    1
    2
    3
    4
    5
    6
    7
    public static final IPackageManager main(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
        PackageManagerService m = new PackageManagerService(context, installer,
                factoryTest, onlyCore);
        ServiceManager.addService("package", m);
        return m;
    }

    以下是Pms构造函数的实现:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    public PackageManagerService(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
        ......
          
        synchronized (mInstallLock) {
        // writer
        synchronized (mPackages) {
            ......
              
            File dataDir = Environment.getDataDirectory();
            mAppDataDir = new File(dataDir, "data");
            mAppInstallDir = new File(dataDir, "app");
            mAppLibInstallDir = new File(dataDir, "app-lib");
            mAsecInternalPath = new File(dataDir, "app-asec").getPath();
            mUserAppDataDir = new File(dataDir, "user");
            mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
      
            ......
              
            // Find base frameworks (resource packages without code).
            mFrameworkInstallObserver = new AppDirObserver(
                frameworkDir.getPath(), OBSERVER_EVENTS, truefalse);
            mFrameworkInstallObserver.startWatching();
            scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM
                    | PackageParser.PARSE_IS_SYSTEM_DIR,
                    scanMode | SCAN_NO_DEX, 0);
      
            // Collected privileged system packages.
            File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
            mPrivilegedInstallObserver = new AppDirObserver(
                    privilegedAppDir.getPath(), OBSERVER_EVENTS, truetrue);
            mPrivilegedInstallObserver.startWatching();
                scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM
                        | PackageParser.PARSE_IS_SYSTEM_DIR
                        | PackageParser.PARSE_IS_PRIVILEGED, scanMode, 0);
      
            // Collect ordinary system packages.
            File systemAppDir = new File(Environment.getRootDirectory(), "app");
            mSystemInstallObserver = new AppDirObserver(
                systemAppDir.getPath(), OBSERVER_EVENTS, truefalse);
            mSystemInstallObserver.startWatching();
            scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM
                    | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
      
            // Collect all vendor packages.
            File vendorAppDir = new File("/vendor/app");
            mVendorInstallObserver = new AppDirObserver(
                vendorAppDir.getPath(), OBSERVER_EVENTS, truefalse);
            mVendorInstallObserver.startWatching();
            scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM
                    | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
      
            ......
      
            if (!mOnlyCore) {
                EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
                        SystemClock.uptimeMillis());
                mAppInstallObserver = new AppDirObserver(
                    mAppInstallDir.getPath(), OBSERVER_EVENTS, falsefalse);
                mAppInstallObserver.startWatching();
                scanDirLI(mAppInstallDir, 0, scanMode, 0);
      
                mDrmAppInstallObserver = new AppDirObserver(
                    mDrmAppPrivateInstallDir.getPath(), OBSERVER_EVENTS, falsefalse);
                mDrmAppInstallObserver.startWatching();
                scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,
                        scanMode, 0);
                ......
        // synchronized (mPackages)
        // synchronized (mInstallLock)
    }

    通过Pms的构造函数能够看出,Pms在初始化时会扫描/system/app、vender/app、/data/app、/data/app-private四个应用安装文件夹,然后调用sanDirLI方法进行安装。

    Pms通过AppDirObserver对这四个应用安装文件夹进行监控。一旦发现APK格式的文件则会调用scanPackageLI进行安装。

    2、通过包安装器PackageInstaller安装

    Android提供了一个默认的包安装器。位于/package/app/PackageInstaller文件夹。

    通过其Manifest文件能够看出。PackageInstaller会对我们安装应用发出的Intent进行处理,这里PackageInstaller提供了两种处理方式,各自是:file方式和package方式。

    @/package/app/PackageInstaller/AndroidManifest.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <activity android:name=".PackageInstallerActivity"
            android:configChanges="orientation|keyboardHidden|screenSize"
            android:excludeFromRecents="true">
        <intent-filter>
            <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:mimeType="application/vnd.android.package-archive" />
        </intent-filter>
        <intent-filter>
            <action android:name="android.intent.action.INSTALL_PACKAGE" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:scheme="file" />
            <data android:scheme="package" />
        </intent-filter>
    </activity>

    @/package/app/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java

    1
    2
    3
    4
    5
    6
    7
    8
    @Override
    protected void onCreate(Bundle icicle) {
        super.onCreate(icicle);
      
        ......
          
        initiateInstall();
    }
    1
    2
    3
    4
    5
    private void initiateInstall() {
        ......
      
        startInstallConfirm();
    }

    startInstallConfirm方法中点击“确认”后。会发出一个Intent,接收者为InstallAppProgress。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public void onClick(View v) {
        if(v == mOk) {
            if (mOkCanInstall || mScrollView == null) {
                // Start subactivity to actually install the application
                mInstallFlowAnalytics.setInstallButtonClicked();
                Intent newIntent = new Intent();
                newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
                        mPkgInfo.applicationInfo);
                newIntent.setData(mPackageURI);
                newIntent.setClass(this, InstallAppProgress.class);
                ......
                  
                startActivity(newIntent);
                finish();
            else {
                mScrollView.pageScroll(View.FOCUS_DOWN);
            }
        else if(v == mCancel) {
            ......
        }
    }

    @/package/app/PackageInstaller/src/com/android/packageinstaller/InstallAppProgress.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    public void initView() {
        setContentView(R.layout.op_progress);
        int installFlags = 0;
        PackageManager pm = getPackageManager();
          
        ......
          
        String installerPackageName = getIntent().getStringExtra(
                Intent.EXTRA_INSTALLER_PACKAGE_NAME);
        Uri originatingURI = getIntent().getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
        Uri referrer = getIntent().getParcelableExtra(Intent.EXTRA_REFERRER);
        int originatingUid = getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID,
                VerificationParams.NO_UID);
        ManifestDigest manifestDigest = getIntent().getParcelableExtra(EXTRA_MANIFEST_DIGEST);
        VerificationParams verificationParams = new VerificationParams(null, originatingURI,
                referrer, originatingUid, manifestDigest);
        PackageInstallObserver observer = new PackageInstallObserver();
      
        if ("package".equals(mPackageURI.getScheme())) {
            try {
                pm.installExistingPackage(mAppInfo.packageName);
                observer.packageInstalled(mAppInfo.packageName,
                        PackageManager.INSTALL_SUCCEEDED);
            catch (PackageManager.NameNotFoundException e) {
                observer.packageInstalled(mAppInfo.packageName,
                        PackageManager.INSTALL_FAILED_INVALID_APK);
            }
        else {
            pm.installPackageWithVerificationAndEncryption(mPackageURI, observer, installFlags,
                    installerPackageName, verificationParams, null);
        }
    }

    InstallAppProgress即应用安装过程中的进度条界面。

    通过上面的代码能够看到在initView方法的最后会调用Pms的installPackageWithVerificationAndEncryption方法进行安装。

    3、通过adb命令安装

    adb命令pm是Pms的Shellclient,通过pm能够进行包相关的一些操作,包含安装和卸载。pm命令的使用方法例如以下:

    pm的代码实如今Pm.java中。例如以下:

    @/frameworks/base/cmds/pm/src/com/android/commands/pm/Pm.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public static void main(String[] args) {
        new Pm().run(args);
    }
      
    public void run(String[] args) {
        ......
          
        mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
          
        ......
      
        if ("install".equals(op)) {
            runInstall();
            return;
        }
      
        if ("uninstall".equals(op)) {
            runUninstall();
            return;
        }
        ......
    }

    在run方法中初始化了一个Pms的client代理对象mPm,兴许的相关操作将有mPm完毕。

    以下看一下Pm中负责安装的方法runInstall的代码实现:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    private void runInstall() {
        int installFlags = PackageManager.INSTALL_ALL_USERS;
          
        ......
          
        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");
                    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 if (opt.equals("-d")) {
                installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
      
            ......  
                  
        PackageInstallObserver obs = new PackageInstallObserver();
        try {
            VerificationParams verificationParams = new VerificationParams(verificationURI,
                    originatingURI, referrerURI, VerificationParams.NO_UID, null);
      
            mPm.installPackageWithVerificationAndEncryption(apkURI, obs, installFlags,
                    installerPackageName, verificationParams, encryptionParams);
      
            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);
        }
    }

    能够看出runInstall终于会调用Pms的installPackageWithVerificationAndEncryption方法进行安装。

    通过pm安装时,成功安装的返回信息为“Success”,安装失败的返回信息为”Failure[失败信息]"。

    静默安装实现

    在了解了Android中包安装的方式后,接下来探讨一些怎样实现”静默安装“。所谓静默安装即跳过安装界面和进度条。在不被用户察觉的情况下载后台安装。以下针对上面的三种安装方式分别来分析怎样实现静默安装。

    1、push安装包到应用安装文件夹的方式

    在Pms初始化时安装包的流程中。我们知道Pms会监控/system/app、vender/app、/data/app、/data/app-private这四个应用安装文件夹。

    因此假设可以将APK文件push进应用安装文件夹不就行触发AppDirObserver中的包安装逻辑了了吗?所以这样的思路理论上是行得通的,但有两个须要注意的点:

    • 第一点:例如以下图所看到的。/system/app的訪问权限为root。这就要求在push到/system/app文件夹时必须具有root权限。

      而/data/app的訪问权限为system。要获得system权限就要求使用这样的方式的应用程序必须签名为platform而且sharedUserId制定为“android.uid.system”。

    • 第二点:系统应用(/system/app)与普通应用(/data/app)的安装方式是不同的,对于系统应用。全部资源都包括在apk这个zip包中。并且其在/system/app不必以包名命名(理论上能够随便起名)。

      而对于普通应用安装后,它的dex、lib、资源文件(安装包)分别存放在不同的文件夹,而且安装后以packagename-x.apk的形式保存在/data/app文件夹下。


    那这样的安装方式是不是就没实用了呢?

    非也。

    网上有些电子市场或管家类软件实现的”秒装“功能应该就是安装这个思路实现的,当然这里仅仅是推測,须要进一步研究。

    2、调用Pm隐藏API

    Android实现了一个应用安装器的APK负责包的安装工作,在上面的分析中我们知道。PackageInstaller的工作实际上仅仅是安装界面、权限确认、进度显示等,真正的安装工作依旧是调用Pms实现的。到这里我们就有了另外一种思路,能不能绕过安装界面,直接调用Pms里面的对应方法呢?当然能够,PackageManager类中就提供了这个方案:

    @/frameworks/base/core/java/android/content/pm/PackageManager.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    /**
     * @hide
     *
     * Install a package. Since this may take a little while, the result will
     * be posted back to the given observer.  An installation will fail if the calling context
     * lacks the {@link android.Manifest.permission#INSTALL_PACKAGES} permission, if the
     * package named in the package file's manifest is already installed, or if there's no space
     * available on the device.
     *
     * @param packageURI The location of the package file to install.  This can be a 'file:' or a
     * 'content:' URI.
     * @param observer An observer callback to get notified when the package installation is
     * complete. {@link IPackageInstallObserver#packageInstalled(String, int)} will be
     * called when that happens.  observer may be null to indicate that no callback is desired.
     * @param flags - possible values: {@link #INSTALL_FORWARD_LOCK},
     * {@link #INSTALL_REPLACE_EXISTING}, {@link #INSTALL_ALLOW_TEST}.
     * @param installerPackageName Optional package name of the application that is performing the
     * installation. This identifies which market the package came from.
     */
    public abstract void installPackage(
            Uri packageURI, IPackageInstallObserver observer, int flags,
            String installerPackageName);

    能够看出,这种方法是hide的,因此在应用开发时假设要使用,必须通过反射。

    这里的IPackageInstallObserver是installPackage方法的一个回调接口通知,事实上如今IPackageInstallObserver.aidl中,例如以下:

    @/frameworks/base/core/java/com/android/content/pm/IPackageInstallObserver.aidl

    1
    2
    3
    4
    5
    6
    7
    8
    9
    package android.content.pm;
      
    /**
     * API for installation callbacks from the Package Manager.
     * @hide
     */
    oneway interface IPackageInstallObserver {
        void packageInstalled(in String packageName, int returnCode);
    }

    使用Android内置未公开API有两种方法:一种是通过反射的方式实现;还有一种是在project文件夹下建立与所引用系统类同样的类和方法,这里仅仅要求类和方法名同样。不须要实现,仅仅保证编译时不报错就能够了,依据Java的类载入机制,在执行时,会去载入系统类。

    以下是採用另外一种方法时的两段演示样例代码:

    实现接口回调的代码例如以下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    class MyPakcageInstallObserver extends IPackageInstallObserver.Stub {
            Context cxt;
            String appName;
            String filename;
            String pkname;
      
            public MyPakcageInstallObserver(Context c, String appName,
                     String filename,String packagename) {
                this.cxt = c;
                this.appName = appName;
                this.filename = filename;
                this.pkname = packagename;
            }
      
            @Override
            public void packageInstalled(String packageName, int returnCode) {
                Log.i(TAG, "returnCode = " + returnCode);// 返回1代表成功安装
                            if (returnCode == 1) {
                                //TODO
                            }
                Intent it = new Intent();
                it.setAction(CustomAction.INSTALL_ACTION);
                it.putExtra("install_returnCode", returnCode);
                it.putExtra("install_packageName", packageName);
                it.putExtra("install_appName", appName); cxt.sendBroadcast(it);
            }
        }

    调用PackageManager.java隐藏方法,代码例如以下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    /**
         * 静默安装
         * */
        public static void autoInstallApk(Context context, String fileName,
                String packageName, String APPName) {
            Log.d(TAG, "jing mo an zhuang:" + packageName + ",fileName:" + fileName);
            File file = new File(fileName);
            int installFlags = 0;
            if (!file.exists())
                return;
            installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
            if (hasSdcard()) {
                installFlags |= PackageManager.INSTALL_EXTERNAL;
            }
            PackageManager pm = context.getPackageManager();
            try {
                IPackageInstallObserver observer = new MyPakcageInstallObserver(
                        context, APPName, appId, fileName,packageName,type_name);
                Log.i(TAG, "########installFlags:" + installFlags+"packagename:"+packageName);
                pm.installPackage(Uri.fromFile(file), observer, installFlags,
                        packageName);
            catch (Exception e) {
                  
            }
      
        }

    这样的方法也有一定的限制:

    首先,要在AndroidManifest.xml中声明”android.permission.INSTALL_PACKAGES”权限;

    其次,应用须要system权限。


    3、调用pm命令进行安装

    在adb窗体通过pm install安装包本来就是没有安装界面的。这不正是我们想要的吗?通过pm的安装方式须要取得root或system权限。

    pm的安装方式有两种,一种须要root权限,演示样例代码例如以下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    new Thread() {
        public void run() {
        Process process = null;
        OutputStream out = null;
        InputStream in = null;
        try {
        // 请求root
        process = Runtime.getRuntime().exec("su");
        out = process.getOutputStream();
        // 调用安装
        out.write(("pm install -r " + currentTempFilePath + " ").getBytes());
        in = process.getInputStream();
        int len = 0;
        byte[] bs = new byte[256];
        while (-1 != (len = in.read(bs))) {
        String state = new String(bs, 0, len);
        if (state.equals("Success ")) {
           //成功安装后的操作
             }
           }
        catch (IOException e) {
            e.printStackTrace();
        catch (Exception e) {
            e.printStackTrace();
        finally {
            try {
                if (out != null) {
                    out.flush();
                    out.close();
                }
                if (in != null) {
                    in.close();
                }
            catch (IOException e) {
                e.printStackTrace();
            }
        }
      }
    }.start();

    还有一钟须要system权限,示比例如以下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    new Thread() {
        public void run() {
        Process process = null;
        InputStream in = null;
        try {
        // 请求root
        process = Runtime.getRuntime().exec("pm install -r " + currentTempFilePath + " ");
        in = process.getInputStream();
        int len = 0;
        byte[] bs = new byte[256];
        while (-1 != (len = in.read(bs))) {
        String state = new String(bs, 0, len);
        if (state.equals("Success ")) {
           //成功安装后的操作
             }
           }
        catch (IOException e) {
            e.printStackTrace();
        catch (Exception e) {
            e.printStackTrace();
        finally {
            try {
                if (in != null) {
                    in.close();
                }
            catch (IOException e) {
                e.printStackTrace();
            }
        }
      }
    }.start();

    关于system权限的获取在介绍push方式的安装时已做介绍。

    上面的代码仅仅给出了比較核心的部分,在实际实现中。对返回结果的处理相同重要。

    高速秒装实现

    发现市面上有些手机管家和电子市场类管理软件实现了所谓的”秒装“功能。这个功能本身的实现原理很easy,实现思路为:我们能够利用Android中第一种安装方式,不经过Android应用安装器,直接将应用push到/data/app文件夹中,此时会触发Pms中对/data/app的文件夹监控机制。触发安装。

    然而在实现次功能时有这么几点须要注意:

    • 第一点:须要获得root权限(或通过系统漏洞(MastKey等)绕过root,总之要提示权限。这里没有深入研究)。

    • 第二点:因为push的方式会绕过Android的一些验证机制,因此在push之前须要人为进行校验等保证,这里仅仅举两个比較普遍的样例:

      1)如今非常多应用都是用so库,可是大部分应用往往仅仅提供arm版本号的so库集成,那边在push之前。就须要校验当前手机平台是否存在相应的so库,假设不存在则不建议是用秒装。

      2)在对已存在的应用进行升级时,假设通过秒装进行升级,那么必需要人为校验保证新的apk与已安装apk的签名一致。

    好了,到这里,Android安装就介绍完了,欢迎大家交流讨论。


    删除系统内置应用

    Google定义的存放系统内置应用的位置有两个:/system/app和/vender/app,当中/system/app用于存放Android内置应用。/vender/app用于存放厂商内置应用。实际上在使用时,/vender/app文件夹往往不使用。以下我们看一下/system/app文件夹(以下是我小米2S手机/system/app文件夹的截图):


    能够看出/system/app文件夹下应用文件的权限为644,用户为root。我们通过PackageManager是无法卸载系统内置应用的。要想删除系统内置应用,须要在获得root权限的前提下,通过rm命令删除就可以。

    这样的卸载方式有一些须要注意的问题。请结合5中卸载应用保存数据一起来看。


    卸载应用保存数据

    在有些手机管理软件和电子市场(91手机助手)中有这么一个功能“删除应用保存数据”,那它是怎样做到的呢?这里显然不能走Android默认的卸载流程。

    于是,我们想到了是不是能够通过rm直接删除/system/app或者/data/app文件夹下的apk文件就能够了呢?以下对这个想法做一个验证。

    以应用宝为例:

    1)在/data/app文件夹下找到应用宝安装后的apk文件。例如以下图:


    2)通过rm命令将/data/app文件夹下的应用宝apk文件删除,删除后/data/data文件夹下的情况例如以下:


    3)        看到这里相信大部分人都会有这种疑问?

    A.       重新启动手机后/data/data文件夹下的应用宝数据文件会不会被系统删除?

    B.       又一次安装应用宝后。新的应用宝还能訪问上次安装未删除的数据文件吗?

    带着这些疑问,首先看一下第一个情况,在重新启动手机后/data/data文件夹例如以下:


    能够看到应用宝的数据文件夹依旧是存在的。

    4)在又一次安装应用宝后,我们发现也是能够使用之前的数据文件夹的。


    从上图能够看出,又一次安装后应用宝的user为app_18与上次安装一样。

    对于这里一些原理性的东西临时先不做介绍。

    5)这里有几个须要注意的问题。例如以下:

    A.   在通过这样的方式删除应用时。要注意第三方Launcher上快捷方式(图标)的处理。例如以下图所看到的,在删除应用宝后,在小米桌面上的图标并未一起删除。


    B. 在删除应用之前,最好先调用PackageManager中的foreStopPackage方法停止相关应用,然后再删除。否则可能会有不友好的系统提示,例如以下图:


    应用安装卸载相关的还有其它一些内容。比方说:清理应用卸载残留、应用锁、应用隐藏、应用安装位置、远程adb安装等等。

    这些内容临时不做介绍,放到其它篇目中研究。

    下一篇介绍系统垃圾清理。

    OK,应用安装卸载篇就到这里,欢迎大家讨论交流。




  • 相关阅读:
    Rraspberry Pi 4B python3 安装opencv
    如何用arduion制作智能 垃圾桶
    MySQL(二)表结构的管理
    MySQL(一)基础操作
    vc++绘图基础
    网站签~
    (转)Oracle 知识日常积累
    利用反射判断bean属性不为空(null和空串)
    (转)Oracle 单字段拆分成多行
    svn 解决树冲突
  • 原文地址:https://www.cnblogs.com/blfshiye/p/5079649.html
Copyright © 2011-2022 走看看