zoukankan      html  css  js  c++  java
  • Android动态加载——加载已安装APK中的类

    前言
      
    Android动态加载——加载已安装APK中的类和资源。

    不错的帖子哦!

    Gridview用法大总结(牛年珍藏版)+源码
    http://www.eoeandroid.com/thread-190769-1-1.html

    Android朴素UI城市天气预报源码
    http://www.eoeandroid.com/thread-187228-1-1.html

    精美Android UI界面源码(有图有真相)
    http://www.eoeandroid.com/thread-187010-1-1.html

    正文
    一、目标

      注意:被调用的APK在Android系统中是已经安装的。
         从当前APK中调用另外一个已安装APK的字符串、颜色值、图片、布局文件资源以及Activity。

    二、实现

      2.1    被调用工程
      基本沿用上个工程的,添加了被调用的字符串、图片等,所以这里就不贴了,后面有下载工程的链接。
      2.2   调用工程代码

    public class TestAActivity extends Activity {
     
        /** TestB包名 */
        private static final String PACKAGE_TEST_B = "com.nmbb.b";
     
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            try {
                final Context ctxTestB = getTestBContext();
                Resources res = ctxTestB.getResources();
                // 获取字符串string
                String hello = res.getString(getId(res, "string", "hello"));
                ((TextView) findViewById(R.id.testb_string)).setText(hello);
     
                // 获取图片Drawable
                Drawable drawable = res
                        .getDrawable(getId(res, "drawable", "testb"));
                ((ImageView) findViewById(R.id.testb_drawable))
                        .setImageDrawable(drawable);
     
                // 获取颜色值
                int color = res.getColor(getId(res, "color", "white"));
                ((TextView) findViewById(R.id.testb_color))
                        .setBackgroundColor(color);
     
                // 获取布局文件
                View view = getView(ctxTestB, getId(res, "layout", "main"));
                LinearLayout layout = (LinearLayout) findViewById(R.id.testb_layout);
                layout.addView(view);
     
                // 启动TestB Activity
                findViewById(R.id.testb_activity).setOnClickListener(
                        new OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                try {
                                    @SuppressWarnings("rawtypes")
                                    Class cls = ctxTestB.getClassLoader()
                                            .loadClass("com.nmbb.TestBActivity");
                                    startActivity(new Intent(ctxTestB, cls));
                                } catch (ClassNotFoundException e) {
                                    e.printStackTrace();
                                }
                            }
                        });
            } catch (NameNotFoundException e) {
                e.printStackTrace();
            }
        }
     
        /**
         * 获取资源对应的编号
         * 
         * @param testb
         * @param resName
         * @param resType
         *            layout、drawable、string
         * @return
         */
        private int getId(Resources testb, String resType, String resName) {
            return testb.getIdentifier(resName, resType, PACKAGE_TEST_B);
        }
     
        /**
         * 获取视图
         * 
         * @param ctx
         * @param id
         * @return
         */
        public View getView(Context ctx, int id) {
            return ((LayoutInflater) ctx
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(id,
                    null);
        }
     
        /**
         * 获取TestB的Context
         * 
         * @return
         * @throws NameNotFoundException
         */
        private Context getTestBContext() throws NameNotFoundException {
            return createPackageContext(PACKAGE_TEST_B,
                    Context.CONTEXT_IGNORE_SECURITY | Context.CONTEXT_INCLUDE_CODE); 
        }

    代码说明:
      基本原理:通过package获取被调用应用的Context,通过Context获取相应的资源、类。
      注意:
      a).  网上许多文章是通过当前工程的R.id来调用被调用工程的资源 ,这是错误的,即使不报错那也是凑巧,因为R是自动生成的,两个应用的id是没有办法对应的,所以需要通过getIdentifier来查找。
      b).   Context.CONTEXT_INCLUDE_CODE一般情况下是不需要加的,如果layout里面包含了自定义控件,就需要加上。注意不能在当前工程强制转换获得这个自定义控件,因为这是在两个ClassLoader中,无法转换。
      c).    获取这些资源是不需要shareUserId的。

      三、总结
      与上篇文章相比,获取资源更加方便,但也存在一些限制:
      3.1  被调用的apk必须已经安装,降低用户体验。
      3.2  style是无法动态设置的,即使能够取到。
      3.3  从目前研究结果来看,被调用工程如果使用自定义控件,会受到比较大的限制,不能强制转换使用(原因前面已经讲过)。
      3.4  由于一个工程里面混入了两个Context,比较容易造成混淆,取资源也比较麻烦。这里分享一下批量隐射两个apk id的办法,可以通过反射获取两个apk的R类,一次获取每一个id和值,通过名称一一匹配上,这样就不用手工传入字符串了。

    @SuppressWarnings("rawtypes")
        private static HashMap<String, Integer> getR(Class cls) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
            HashMap<String, Integer> result = new HashMap<String, Integer>();
            for (Class r : cls.getClasses()) {
                if (!r.getName().endsWith("styleable")) {
                    Object owner = r.newInstance();
                    for (Field field : r.getFields()) {
                        result.put(field.getName(), field.getInt(owner));
                    }
                }
            }
            return result;
     
        } 
  • 相关阅读:
    Maven 集成Tomcat插件
    dubbo 序列化 问题 属性值 丢失 ArrayList 解决
    docker 中安装 FastDFS 总结
    docker 从容器中拷文件到宿主机器中
    db2 相关命令
    Webphere WAS 启动
    CKEDITOR 4.6.X 版本 插件 弹出对话框 Dialog中 表格 Table 自定义样式Style 问题
    SpringMVC JSONP JSON支持
    CKEDITOR 3.4.2中 按钮事件中 动态改变图标和title 获取按钮
    git回退到远程某个版本
  • 原文地址:https://www.cnblogs.com/vus520/p/2628160.html
Copyright © 2011-2022 走看看