首页 > 代码库 > app备份与恢复研究

app备份与恢复研究

最近打算搞一个app备份与恢复的小应用,顾名思义,必然包括app的备份和app的恢复两部分。备份部分由可以分为apk的备份与data的备份两部分。需要注意的是备份与恢复都需要一堆的权限,尤其是恢复的时候。

app的备份

首先看看下面几个目录:

system/app 系统自带的应用程序apk目录,无法删除。
data/app 用户程序安装的目录,有删除权限。安装时把apk文件复制到此目录。
data/data 存放应用程序的数据。

data/dalvik-cache 将apk中的dex文件安装到dalvik-cache目录下(dex文件是dalvik虚拟机的可执行文件,其大小约为原始apk文件大小的四分之一)。

app的备份原理就是把system/app、data/app、data/data中的apk和数据存起来,可以存到sd卡也可以上传到网络。既然如此我们就可写代码了,下面代码只是我自己写来测试用的,不严谨,拷贝可能有问题。

apk的备份

 public String backupAppPackage(String packageInPath, String packageName)
    {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try
        {
            File in = new File(packageInPath);
            File out = new File(backupPath + packageName + ".apk");
            out.createNewFile();
            
            fis = new FileInputStream(in);
            fos = new FileOutputStream(out);
            int count;
            byte[] buffer = new byte[256 * 1024];
            while ((count = fis.read(buffer)) > 0)
            {
                fos.write(buffer, 0, count);
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
            return "IOException";
        }
        finally
        {
            if (fis != null)
            {
                try
                {
                    fis.close();
                }
                catch (IOException e)
                {
                    e.printStackTrace();
                }
            }
            
            if (fos != null)
            {
                try
                {
                    fos.flush();
                    fos.close();
                }
                catch (IOException e)
                {
                    e.printStackTrace();
                }
            }
        }
        return "OK";

    }

data的备份

    public String backupAppData(String dataInPath, String packageName)
    {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try
        {
            File in = new File(dataInPath);
            File out = new File(backupPath + packageName);
            out.createNewFile();
            
            fis = new FileInputStream(in);
            fos = new FileOutputStream(out);
            int count;
            byte[] buffer = new byte[256 * 1024];
            while ((count = fis.read(buffer)) > 0)
            {
                fos.write(buffer, 0, count);
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
            return "IOException";
        }
        finally
        {
            if (fis != null)
            {
                try
                {
                    fis.close();
                }
                catch (IOException e)
                {
                    e.printStackTrace();
                }
            }
            
            if (fos != null)
            {
                try
                {
                    fos.flush();
                    fos.close();
                }
                catch (IOException e)
                {
                    e.printStackTrace();
                }
            }
        }
        return "OK";

    }

app恢复

app恢复的时候就稍微有点纠结了,因为这就牵扯到apk的安装,而apk的安装的方法很多,需要根据自己的情况选择。

情况一

如果你搞的是一个系统级应用,可以考虑使用反射机制,但是最好是直接使用系统API,由于PackageManager的installPackage函数是隐藏的,所以在用eclipse开发编译的时候需要引入classes.jar。代码如下:

    public void installPackage(Uri apkFileUri) throws Exception
    {
        if (apkFileUri != null)
        {
            //使用放射机制得到PackageManager类的隐藏函数installPackage
            PackageManager pm = context.getPackageManager(); //得到pm对象   
            try
            {
                //通过反射机制获得该隐藏函数   
                //                Method installPackage = pm.getClass()
                //                        .getDeclaredMethod("installPackage",
                //                                Uri.class,
                //                                IPackageInstallObserver.class,
                //                                int.class,
                //                                String.class);
                
                //                //调用该函数,并且给其分配参数 ,待调用流程完成后会回调PkgSizeObserver类的函数   
                //                installPackage.invoke(pm,
                //                        apkFileUri,
                //                        new PkgInstallObserver(),
                //                        INSTALL_REPLACE_EXISTING,
                //                        null);
                


//直接通过API
                pm.installPackage(apkFileUri,
                        new PkgInstallObserver(),
                        INSTALL_REPLACE_EXISTING,
                        null);
                //W/System.err(24923): Caused by: java.lang.SecurityException: Neither user 10053 nor current process has android.permission.INSTALL_PACKAGES.
                
                System.out.println("invoke-->>>");
            }
            catch (Exception ex)
            {
                System.out.println("Exception-->>>");
                ex.printStackTrace();
                //Neither user 10053 nor current process has android.permission.GET_PACKAGE_SIZE.
                throw ex; // 抛出异常   
            }
        }
    }


IPackageInstallObserver是安装结果的回调,通过它监听安装的结果。
class PkgInstallObserver extends IPackageInstallObserver.Stub
    {
        public void packageInstalled(String packageName, int returnCode)
                throws RemoteException
        {
            System.out.println("packageInstalled-->>" + packageName);
        }

    }

情况二

如果搞的是一个普通应用则还有两种选择方案。

第一种是通过获取系统权限,执行命令静默安装,如下:
    public void installApp()
    {
        File dir = new File(backupPath);
        File file = dir.listFiles()[2];
        String apkPath = file.getAbsolutePath();
        String result = execRootCmd("pm install -r " + apkPath);
    }


  // 执行命令并且输出结果 
    public static String execRootCmd(String cmd)
    {
        String result = "";
        DataOutputStream dos = null;
        DataInputStream dis = null;
        
        try
        {
  // 经过Root处理的android系统即有su命令 
            Process p = Runtime.getRuntime().exec("su");
            dos = new DataOutputStream(p.getOutputStream());
            dis = new DataInputStream(p.getInputStream());
            
            Log.i(TAG, cmd);
            dos.writeBytes(cmd + "\n");
            dos.flush();
            dos.writeBytes("exit\n");
            dos.flush();
            String line = null;
            while ((line = dis.readLine()) != null)
            {
                Log.d("result", line);
                result += line;
            }
            p.waitFor();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (dos != null)
            {
                try
                {
                    dos.close();
                }
                catch (IOException e)
                {
                    e.printStackTrace();
                }
            }
            if (dis != null)
            {
                try
                {
                    dis.close();
                }
                catch (IOException e)
                {
                    e.printStackTrace();
                }
            }
        }
        return result;

    }

第二种最普通的安装是把安装的任务交给系统应用去做,如下:

public static void install(Context context, String filePath) {
    Intent i = new Intent(Intent.ACTION_VIEW);
    i.setDataAndType(Uri.parse("file://" + filePath), "application/vnd.android.package-archive");
    i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    context.startActivity(i);

}

需要滴权限

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.INSTALL_PACKAGES" />