当前位置:   article > 正文

Android动态加载jar、apk的实现_android动态加载apk

android动态加载apk

        前段时间到阿里巴巴参加支付宝技术分享沙龙,看到支付宝在Android使用插件化的技术,挺好奇的。正好这几天看到了农民伯伯的相关文章,因此简单整理了下,有什么错误希望大神指正。

        

       核心类

      1.1      DexClassLoader类
   可以加载jar/apk/dex,可以从SD卡中加载未安装的apk。
   1.2      PathClassLoader类  
   只能加载已经安装到Android系统中的apk文件。


    一、正文

       1.1 动态加载jar

    类似于eclipse的插件化实现, 首先定义好接口, 用户实现接口功能后即可通过动态加载的方式载入jar文件, 以实现具体功能。注意, 这里的jar包需要经过android dx工具的处理 , 否则不能使用。

首先我们定义如下接口 :

  1. package com.example.interf;
  2. /**
  3. * @Title: ILoader.java
  4. * @Package com.example.loadjardemo
  5. * @Description: 通用接口, 需要用户实现
  6. * @version V1.0
  7. */
  8. public interface ILoader {
  9. public String sayHi();
  10. }
用户需实现,该接口, 并且将工程导出为jar包的形式。

示例如下 :

  1. public class JarLoader implements ILoader {
  2. public JarLoader() {
  3. }
  4. @Override
  5. public String sayHi() {
  6. return "I am jar loader.";
  7. }
  8. }

最后, 实现功能的代码打包成jar包 :

首先选中工程, 右键后选择“导出”, 然后选择“java”-----“jar文件”, 然后将你的具体功能实现类导出为jar,文件名为loader.jar,如下图所示 : 


将打包好的jar拷贝到SDK安装目录android-sdk-windows\platform-tools下,DOS进入这个目录,执行如下命令:

dx --dex --output=loader_dex.jar loader.jar

然后将loader_dex.jar放到android手机中, 这里我们放到SD卡根目录下。


动态加载代码 : 

  1. /**
  2. *
  3. * @Title: loadJar
  4. * @Description: 项目工程中必须定义接口, 而被引入的第三方jar包实现这些接口,然后进行动态加载 。
  5. * 相当于第三方按照接口协议来开发, 使得第三方应用可以以插件的形式动态加载到应用平台中。
  6. * @return void
  7. * @throws
  8. */
  9. private void loadJar(){
  10. final File optimizedDexOutputPath = new File(Environment.getExternalStorageDirectory().toString()
  11. + File.separator + "loader_dex.jar");
  12. BaseDexClassLoader cl = new BaseDexClassLoader(Environment.getExternalStorageDirectory().toString(),
  13. optimizedDexOutputPath, optimizedDexOutputPath.getAbsolutePath(), getClassLoader());
  14. Class libProviderClazz = null;
  15. try {
  16. // 载入JarLoader类, 并且通过反射构建JarLoader对象, 然后调用sayHi方法
  17. libProviderClazz = cl.loadClass("com.example.interf.JarLoader");
  18. ILoader loader = (ILoader)libProviderClazz.newInstance();
  19. Toast.makeText(MainActivity.this, loader.sayHi() , Toast.LENGTH_SHORT).show();
  20. } catch (Exception exception) {
  21. // Handle exception gracefully here.
  22. exception.printStackTrace();
  23. }
  24. }

效果如下图所示 : 
请等待...



1.2 加载未安装的apk

     首先新建一个Android项目, 定义如下接口 : 

    

  1. public interface ISayHello {
  2. public String sayHello() ;
  3. }
    

定义一个Activity实现该接口, 如下:

  1. package com.example.loaduninstallapkdemo;
  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. import android.view.Menu;
  5. import android.view.View;
  6. import android.view.View.OnClickListener;
  7. import android.widget.Toast;
  8. /**
  9. *
  10. * @ClassName: UninstallApkActivity
  11. * @Description: 这是被动态加载的Activity类
  12. *
  13. */
  14. public class UninstallApkActivity extends Activity implements ISayHello{
  15. private View mShowView = null ;
  16. @Override
  17. protected void onCreate(Bundle savedInstanceState) {
  18. super.onCreate(savedInstanceState);
  19. setContentView(R.layout.activity_main);
  20. mShowView = findViewById(R.id.show) ;
  21. mShowView.setOnClickListener(new OnClickListener() {
  22. @Override
  23. public void onClick(View v) {
  24. Toast.makeText(UninstallApkActivity.this, "这是已安装的apk被动态加载了", Toast.LENGTH_SHORT).show();
  25. }
  26. }) ;
  27. }
  28. @Override
  29. public boolean onCreateOptionsMenu(Menu menu) {
  30. // Inflate the menu; this adds items to the action bar if it is present.
  31. getMenuInflater().inflate(R.menu.activity_main, menu);
  32. return true;
  33. }
  34. @Override
  35. public String sayHello(){
  36. return "Hello, this apk is not installed";
  37. }
  38. }
然后将该编译生apk, 并且将该apk拷贝到SD卡根目录下。

动态加载未安装的apk

  1. /**
  2. *
  3. * @Title: loadUninstallApk
  4. * @Description: 动态加载未安装的apk
  5. * @return void
  6. * @throws
  7. */
  8. private void loadUninstallApk(){
  9. String path = Environment.getExternalStorageDirectory() + File.separator;
  10. String filename = "LoadUninstallApkDemo.apk";
  11. // 4.1以后不能够将optimizedDirectory设置到sd卡目录, 否则抛出异常.
  12. File optimizedDirectoryFile = getDir("dex", 0) ;
  13. DexClassLoader classLoader = new DexClassLoader(path + filename, optimizedDirectoryFile.getAbsolutePath(),
  14. null, getClassLoader());
  15. try {
  16. // 通过反射机制调用, 包名为com.example.loaduninstallapkdemo, 类名为UninstallApkActivity
  17. Class mLoadClass = classLoader.loadClass("com.example.loadunstallapkdemo.UninstallApkActivity");
  18. Constructor constructor = mLoadClass.getConstructor(new Class[] {});
  19. Object testActivity = constructor.newInstance(new Object[] {});
  20. // 获取sayHello方法
  21. Method helloMethod = mLoadClass.getMethod("sayHello", null);
  22. helloMethod.setAccessible(true);
  23. Object content = helloMethod.invoke(testActivity, null);
  24. Toast.makeText(MainActivity.this, content.toString(), Toast.LENGTH_LONG).show();
  25. } catch (Exception e) {
  26. e.printStackTrace();
  27. }
  28. }


DexClassLoader 注意点 :

A class loader that loads classes from .jar and .apk files containing a classes.dex entry. This can be used to execute code not installed as part of an application.

This class loader requires an application-private, writable directory to cache optimized classes. Use Context.getDir(String, int) to create such a directory:

   
  1. File dexOutputDir = context.getDir("dex", 0);
  2.  

Do not cache optimized classes on external storage. External storage does not provide access controls necessary to protect your application from code injection attacks.


效果如图 : 



1.3 加载已安装的apk

将1.2中的apk安装到手机中,我的例子中,该apk的包名为“com.example.loaduninstallapkdemo”,Activity名为"UninstallApkActivity". 加载代码如下 :

  1. /**
  2. *
  3. * @Title: loadInstalledApk
  4. * @Description: 动态加载已安装的apk
  5. * @return void
  6. * @throws
  7. */
  8. private void loadInstalledApk() {
  9. try {
  10. String pkgName = "com.example.loaduninstallapkdemo";
  11. Context context = createPackageContext(pkgName,
  12. Context.CONTEXT_IGNORE_SECURITY | Context.CONTEXT_INCLUDE_CODE) ;
  13. // 获取动态加载得到的资源
  14. Resources resources = context.getResources() ;
  15. // 过去该apk中的字符串资源"tips", 并且toast出来,apk换肤的实现就是这种原理
  16. String toast = resources.getString(resources.getIdentifier("tips", "string", pkgName) ) ;
  17. Toast.makeText(MainActivity.this, toast, Toast.LENGTH_SHORT).show() ;
  18. Class cls = context.getClassLoader().loadClass(pkgName + ".UninstallApkActivity") ;
  19. // 跳转到该Activity
  20. startActivity(new Intent(context, cls)) ;
  21. } catch (NameNotFoundException e) {
  22. e.printStackTrace();
  23. }catch (ClassNotFoundException e) {
  24. Log.d("", e.toString()) ;
  25. }
  26. }</span>


效果如图:

消息被Toast出来, 并且跳转到了目标Activity. 

相关资料 : 探秘腾讯Android手机游戏平台之不安装游戏apk直接启动法

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/861158
推荐阅读
相关标签
  

闽ICP备14008679号