通过android的沙箱机制,可以在不同的应用间共享代码、数据、或资源文件。
阅读本文前需要了解android的沙箱机制

一、代码共享


Context packageContext = createPackageContext("com.example.signatureapp", Context.CONTEXT_INCLUDE_CODE|Context.CONTEXT_IGNORE_SECURITY);

Context.CONTEXT_INCLUDE_CODE:

把参数包名对应的应用中的代码包含到Context中,这样当前应用才能访问context应用中的类,如果context对应的应用不能安全的加载到当前应用中,会报出SecurityException,如果想要加载另一个应用,访问它的数据,则这个标记必须设置

CONTEXT_IGNORE_SECURITY:忽略加载context对应的应用这个过程中的安全限制,总是允许加载,和CONTEXT_INCLUDE_CODE配合使用,代表不管是否安全,都要加载,一般使用要小心。

由于应用处于同一进程中,那么B应用可以通过ClassLoader加载A应用的类,然后B应用可以通过反射调用A应用的方法,如:


public class Util {

public static void printStr(String str){

​ System.out.println("========str: "+str);

}

}

通过反射调用代码


Class clazz = packageContext.getClassLoader().loadClass("com.example.signatureapp.Util");

Method method = clazz.getDeclaredMethod("printStr",String.class);

method.invoke(null,"===========静态方法");

在这里有两个很有趣的东西:

1、两个应用的加载的class的hashcode是一样的

根据android的沙盒机制可以知道,在android中,当两个应用当uid和签名一致时,这时getClassLoader()加载的是应用A中的class,相当于创建了这个class的引用给引用B。

2、无法共享彼此间的静态数据,就是说A应用修改了变量,不会影响到B应用,反之亦然。

img

通过android的沙箱机制可以知道,每个应用都是一个独立的dvm进程,而两个应用uid相同,只是拥有了同样的资源访问权限,而静态变量是放在堆栈中的,每个进程的堆栈信息都是互相隔离的,因此就出现了这个现象。

二、数据库共享

使用packageContext可以打开彼此之间的数据库。

三、SharedPreferences共享


try {

​ Context context = createPackageContext("com.example.sandboxa", CONTEXT_INCLUDE_CODE | CONTEXT_IGNORE_SECURITY);

​ SharedPreferences sp = appAContext.getSharedPreferences("sandbox", MODE_PRIVATE);

​ } catch (PackageManager.NameNotFoundException e) {

​ e.printStackTrace();

​ }

这里有一点是需要注意的:

如果应用A、应用B都是新安装的应用;

应用B先于应用A启动,然后应用B获取了Context

再返回应用A,在应用B中将数据存到sp中,返回应用B,这时是应用B是获取不到Sp应用的。

img

四、私有文件共享

可以直接通过getFilesDir()这个接口获取到应用A的文件夹


String path = appAContext.getFilesDir().getPath() + "/private_file";

五、资源文件共享

要想获取到其它应用到资源文件,获取Context时需要设置CONTEXT_RESTRICTED标志。

然后使用getResources().getIdentifier(resName, resType, contextc的包名)


int id = appAContext.getResources().getIdentifier("hhh", "drawable", appAContext.getPackageName());

Log.d("TAG", "id = " + id);

img.setImageResource(id); // 错误的,不可以直接加载资源,只能通过appAContext.getResources()加载相应资源

img.setImageDrawable(appAContext.getResources().getDrawable(id));

这里需要注意的是,本应用不能直接使用该id加载资源,必须是使用appAContext.getResources()加载资源。

demo