手機管理應用研究【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的Shell客戶端,通過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的客戶端代理對象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有兩種方法:一種是通過反射的方式實現;另一種是在工程目錄下建立與所引用系統類相同的類和方法,這裏只要求類和方法名相同,不需要實現,只保證編譯時不報錯就可以了,根據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 + "\n").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\n")) {
       //安裝成功後的操作
         }
       }
    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 + "\n");
    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\n")) {
       //安裝成功後的操作
         }
       }
    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方式的安裝時已做介紹。上面的代碼只給出了比較核心的部分,在實際實現中,對返回結果的處理同樣重要。

快速秒裝實現

發現市面上有些手機管家和電子市場類管理軟件實現了所謂的”秒裝“功能。這個功能本身的實現原理非常簡單,實現思路爲:我們可以利用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,應用安裝卸載篇就到這裏,歡迎大家討論交流。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章