最近打算搞一个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" />
app备份与恢复研究