Android动态加载(上)——加载未安装APK中的类

前言

  画UI界面,写逻辑代码,实在繁琐无谓,最近迷恋上了dex的动态加载技术。然后在网上搜搜搜...有结果了,下面分享下学习记录。

一、目标

实现在手机存储空间或SD卡中动态加载apk中的类方法

二、被调用apk生成

public class TestBActivity  extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView tvContent = (TextView) findViewById(R.id.text);
        tvContent.setText(forTest());
    }

    public String forTest() {
        return "from TestBActivity--forTest()";
    }
}

代码很简单,就不做分析了。

需要注意的是:我使用的是android studio,而且使用的是最新版2.2.2,打开了Instant Run功能。那么问题来了,当我参考农民伯伯的博客写的时候(最后附有链接)。

当我们打开Instant Run这个强大的功能后,生成的apk目录包含的文件是这样的:

是不是感觉很奇怪?我们一般看到的目录应该是这样的:

为什么会多了一个dex文件呢?问题就在Instant Run这个功能,具体是为什么就不深究了。所以很好解决,我们把Instant Run这个功能关闭就好了,

如果实在不想关闭的话,生成一个签名包也是可以的。

最后,将生成后的app-debug.apk拷贝到SD卡的根目录下。


三、实现目标 

    先说下,在我按照农民伯伯的代码运行后,报错了!他运行是成功的,我运行就出错了:

  java.lang.IllegalArgumentException: Optimized data directory /storage/emulated/0 is not owned by the current user. 

这个路径不是当前用户所有?什么鬼!看起来像没有权限的样子。百度一查发现,原来是系统兼容性问题,很好解决:

newDexClassLoader(path + filename, path,null,getClassLoader());

也就是第二个参数不能传SD卡路径,我们可以传当前应用的缓存路径:

	File dexOutputDir = getDir("dex", 0);
    	classLoader = new DexClassLoader(path + filename, dexOutputDir.getAbsolutePath(),
            null, getClassLoader());

完成代码:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        String path = Environment.getExternalStorageDirectory() + "/";
        String filename = "app-debug.apk";
        DexClassLoader classLoader = null;
        File dexOutputDir = getDir("dex", 0);
        try {
            classLoader = new DexClassLoader(path + filename, dexOutputDir.getAbsolutePath(),
                    null, getClassLoader());
        } catch (Exception ex) {
            Log.e("MainActivity", ex.getMessage());
        }
        try {
            Class mLoadClass = classLoader.loadClass("com.victor.demo.TestBActivity");
            Constructor constructor = mLoadClass.getConstructor(new Class[] {});
            Object TestBActivity = constructor.newInstance(new Object[] {});

            Method getMoney = mLoadClass.getMethod("forTest", null);
            getMoney.setAccessible(true);
            Object money = getMoney.invoke(TestBActivity, null);
            Toast.makeText(this, money.toString(), Toast.LENGTH_LONG).show();

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

嗯,就是这么一点代码量。也没有太多的知识,就是一个classloader + 反射。

动态加载方面还可以搜索一下"Java动态加载"方面的资料,很有参考价值。可以发现比Android动态加载jar/dex使用起来方便得多。   

  四、注意

别忘了加上SDCARD的写权限: android.permission.READ_EXTERNAL_STORAGE

 

  五、扩展阅读

    探秘腾讯Android手机游戏平台之不安装游戏APK直接启动法

    (强烈推荐:QQ游戏动态调用Activity的方法:通过ClassLoader,loadClass Activity类,然后分别在主工程的onDestroy、onKeyDown、onPause、onRestart、onResume等生命周期方法中反射调用(Method、invoke)子工程的类方法来模拟实现整个生命周期。此外巧妙的通过解压缩APK文件来获取游戏的资源)

    Android中文Wiki:DexFile


  参考资料:http://www.cnblogs.com/over140/archive/2012/03/29/2423116.html

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值