Android · 2017年4月25日 0

Android 中的CreatePackageContext()

  • /**
  •      * Return a new Context object for the given application name.  This
  •      * Context is the same as what the named application gets when it is
  •      * launched, containing the same resources and class loader.  Each call to
  •      * this method returns a new instance of a Context object; Context objects
  •      * are not shared, however they share common state (Resources, ClassLoader,
  •      * etc) so the Context instance itself is fairly lightweight.
  •      *
  •      * <p>Throws {@link PackageManager.NameNotFoundException} if there is no
  •      * application with the given package name.
  •      *
  •      * <p>Throws {@link java.lang.SecurityException} if the Context requested
  •      * can not be loaded into the caller’s process for security reasons (see
  •      * {@link #CONTEXT_INCLUDE_CODE} for more information}.
  •      *
  •      * @param packageName Name of the application’s package.
  •      * @param flags Option flags, one of {@link #CONTEXT_INCLUDE_CODE}
  •      *              or {@link #CONTEXT_IGNORE_SECURITY}.
  •      *
  •      * @return A Context for the application.
  •      *
  •      * @throws java.lang.SecurityException
  •      * @throws PackageManager.NameNotFoundException if there is no application with
  •      * the given package name
  •      */
  •     Override
  •     public Context createPackageContext(String packageName, int flags)
  •         throws PackageManager.NameNotFoundException {
  •         if (packageName.equals(“system”) || packageName.equals(“android”)) {
  •             final ContextImpl context = new ContextImpl(mMainThread.getSystemContext());
  •             context.mBasePackageName = mBasePackageName;
  •             return context;
  •         }
  •         LoadedApk pi =
  •             mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(), flags);
  •         if (pi != null) {
  •             ContextImpl c = new ContextImpl();
  •             c.mRestricted = (flags & CONTEXT_RESTRICTED) == CONTEXT_RESTRICTED;
  •             c.init(pi, null, mMainThread, mResources, mBasePackageName);
  •             if (c.mResources != null) {
  •                 return c;
  •             }
  •         }
  •         // Should be a better exception.
  •         throw new PackageManager.NameNotFoundException(
  •             “Application package “ + packageName + ” not found”);
  •     }

主要作用是:创建其它程序的Context,通过创建的这个Context,就可以访问该软件包的资源,甚至可以执行其它软件包的代码。

使用:

  1. try {
  2.      Context c = createPackageContext(“com.dolphin.demo”, Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
  3.  } catch (SecurityException e) {
  4.      e.printStackTrace();
  5.  } catch (Exception e) {
  6.      e.printStackTrace();
  7.  }

通常一个软件是不能创建其它程序的Context的,除非它们拥有相同的用户ID与签名。用户ID是一个字符串标识,在程序的AndroidManifest.xml文件的manifest标签中指定,格式为android:shareUserId=”**”。安装在设备中的每一个apk程序,Android系统会给其分配一个单独的用户空间,其中android:shareUserId就是对应一个Linux用户ID,并且为它创建一个沙箱,以防止与其它应用程序产生影响。用户ID 在应用程序被安装到设备中时分配。通过SharedUserid,拥有同一个Userid的多个APK可以配置成运行在同一个进程中,所以默认就是可以互相访问任意数据,也可以配置成运行在不同的进程中, 同时可以访问其APK的数据目录下的资源(图片,数据库和文件),就像访问本程序的数据一样。

经常的用途:
如经常一些应用提供的换皮肤功能,实现方法大致有两种:
1)把需要替换的资源图片打包好之后,放在客户端指定的目录下面,切换皮肤,查找相应的资源时,直接切换资源查找的路径到该打包文件中对应的资源路径即可。
2)把需要替换的资源,放在一个空的android 应用程序的drawable-**目录下面即可,编译,运行该apk,查找资源时,就需要用到createPackageContext获取对应包名的Context实例,然后通过context来查找对应的资源。查找时,也有两种方式:
2.1)通过ID来查找资源图片;

  1. Context context;
  2.        try {
  3.            context = createPackageContext(“com.dolphin.demo”, Context.CONTEXT_INCLUDE_CODE
  4.                    | Context.CONTEXT_IGNORE_SECURITY);
  5.            txvA.setText(context.getResources().getText(R.string.message));
  6.        } catch (NameNotFoundException e) {
  7.            e.printStackTrace();
  8.        }

2.2)通过资源Name,反推出资源ID,然后查找对应的资源图片;

  1. /***  
  2.     * @param clazz 目标资源的R.java  
  3.     * @param className R.java的内部类,如layout,string,drawable…  
  4.     * @param name 资源名称  
  5.     * @return  
  6.     */
  7.    private int getResourseIdByName(Class clazz, String className, String name) {
  8.        int id = 0;
  9.        try {
  10.            Class[] classes = clazz.getClasses(); // 获取R.java里的所有静态内部类  
  11.            Class desireClass = null;
  12.            for (int i = 0; i < classes.length; i++) {
  13.                if (classes[i].getName().split(“\\$”)[1].equals(className)) {
  14.                    desireClass = classes[i];
  15.                    break;
  16.                }
  17.            }
  18.            if (desireClass != null)
  19.                id = desireClass.getField(name).getInt(desireClass);
  20.        } catch (IllegalArgumentException e) {
  21.            e.printStackTrace();
  22.        } catch (SecurityException e) {
  23.            e.printStackTrace();
  24.        } catch (IllegalAccessException e) {
  25.            e.printStackTrace();
  26.        } catch (NoSuchFieldException e) {
  27.            e.printStackTrace();
  28.        }
  29.        return id;
  30.    }
  31.    Context context;
  32.    try {
  33.        context = createPackageContext(“com.dolphin.demo”, Context.CONTEXT_INCLUDE_CODE
  34.                | Context.CONTEXT_IGNORE_SECURITY);
  35.        Class cls = context.getClassLoader().loadClass(“com.dolphin.demo.R”); // 获得目标apk的R类  
  36.        txvA.setText(context.getResources().getText(getResourseIdByName(cls, “string”“message”)));
  37.    } catch (NameNotFoundException e) {
  38.        e.printStackTrace();
  39.    } catch (ClassNotFoundException e) {
  40.        e.printStackTrace();
  41.    }

Share this: