Android动态加载jar、apk的实现

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

        

       核心类

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

 

    一、正文

       1.1 动态加载jar

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

首先我们定义如下接口

[java] view plaincopy

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.  }  

11.      

用户需实现,该接口,并且将工程导出为jar包的形式。

示例如下

[java] view plaincopy

1.    public class JarLoader implements ILoader {  

2.      

3.        public JarLoader() {  

4.        }  

5.      

6.        @Override  

7.        public String sayHi() {  

8.            return "I am jar loader.";  

9.        }  

10.    

11.  }  


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

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

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

[java] view plaincopy

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

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

 

动态加载代码 

[java] view plaincopy

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.               

13.             BaseDexClassLoader cl = new BaseDexClassLoader(Environment.getExternalStorageDirectory().toString(),  

14.                  optimizedDexOutputPath, optimizedDexOutputPath.getAbsolutePath(), getClassLoader());  

15.             Class libProviderClazz = null;  

16.               

17.             try {  

18.                 // 载入JarLoader类, 并且通过反射构建JarLoader对象, 然后调用sayHi方法  

19.                 libProviderClazz = cl.loadClass("com.example.interf.JarLoader");  

20.                 ILoader loader = (ILoader)libProviderClazz.newInstance();  

21.                 Toast.makeText(MainActivity.this, loader.sayHi() , Toast.LENGTH_SHORT).show();  

22.             } catch (Exception exception) {  

23.                 // Handle exception gracefully here.  

24.                 exception.printStackTrace();  

25.             }  

26.  }  


效果如下图所示 


 

 

1.2 加载未安装的apk

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

    

[java] view plaincopy

1.    public interface ISayHello {  

2.        public String sayHello()  ;  

3.    }  

    

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

[java] view plaincopy

1.    package com.example.loaduninstallapkdemo;  

2.      

3.    import android.app.Activity;  

4.    import android.os.Bundle;  

5.    import android.view.Menu;  

6.    import android.view.View;  

7.    import android.view.View.OnClickListener;  

8.    import android.widget.Toast;  

9.      

10.  /** 

11.   *  

12.   * @ClassName: UninstallApkActivity  

13.   * @Description: 这是被动态加载Activity 

14.   * 

15.   */  

16.  public class UninstallApkActivity extends Activity implements ISayHello{  

17.    

18.      private View mShowView = null ;  

19.        

20.      @Override  

21.      protected void onCreate(Bundle savedInstanceState) {  

22.          super.onCreate(savedInstanceState);  

23.          setContentView(R.layout.activity_main);  

24.            

25.          mShowView = findViewById(R.id.show) ;  

26.          mShowView.setOnClickListener(new OnClickListener() {  

27.                

28.              @Override  

29.              public void onClick(View v) {  

30.                  Toast.makeText(UninstallApkActivity.this"这是已安装的apk动态加载", Toast.LENGTH_SHORT).show();  

31.              }  

32.          }) ;  

33.      }  

34.    

35.      @Override  

36.      public boolean onCreateOptionsMenu(Menu menu) {  

37.          // Inflate the menu; this adds items to the action bar if it is present.  

38.          getMenuInflater().inflate(R.menu.activity_main, menu);  

39.          return true;  

40.      }  

41.    

42.      @Override  

43.      public String sayHello(){  

44.          return "Hello, this apk is not installed";  

45.      }  

46.  }  

然后将该编译生apk,并且将该apk拷贝到SD卡根目录下。

动态加载未安装的apk

[java] view plaincopy

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.    

12.         // 4.1以后不能够将optimizedDirectory设置到sd卡目录, 否则抛出异常.  

13.         File optimizedDirectoryFile = getDir("dex"0) ;  

14.         DexClassLoader classLoader = new DexClassLoader(path + filename, optimizedDirectoryFile.getAbsolutePath(),  

15.                                                          null, getClassLoader());  

16.    

17.         try {  

18.          // 通过反射机制调用, 包名为com.example.loaduninstallapkdemo, 类名为UninstallApkActivity  

19.             Class mLoadClass = classLoader.loadClass("com.example.loadunstallapkdemo.UninstallApkActivity");  

20.             Constructor constructor = mLoadClass.getConstructor(new Class[] {});  

21.             Object testActivity = constructor.newInstance(new Object[] {});  

22.               

23.             // 获取sayHello方法  

24.             Method helloMethod = mLoadClass.getMethod("sayHello"null);  

25.             helloMethod.setAccessible(true);  

26.             Object content = helloMethod.invoke(testActivity, null);  

27.             Toast.makeText(MainActivity.this, content.toString(), Toast.LENGTH_LONG).show();  

28.               

29.         } catch (Exception e) {  

30.             e.printStackTrace();  

31.         }  

32.  }  

 

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 anapplication.

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

   FiledexOutputDir = context.getDir("dex", 0);

 

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


效果如图

 

1.3 加载已安装的apk

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

[java] view plaincopy

1.          /** 

2.     

3.    * @Title: loadInstalledApk  

4.    * @Description: 动态加载已安装的apk     

5.    * @return void     

6.    * @throws 

7.    */  

8.    rivate 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.      // 获取动态加载得到的资源  

15.      Resources resources = context.getResources() ;  

16.      // 过去该apk中的字符串资源"tips" 并且toast出来,apk换肤的实现就是这种原理  

17.      String toast = resources.getString(resources.getIdentifier("tips""string", pkgName) ) ;  

18.      Toast.makeText(MainActivity.this, toast, Toast.LENGTH_SHORT).show() ;  

19.        

20.      Class cls = context.getClassLoader().loadClass(pkgName + ".UninstallApkActivity") ;  

21.      // 跳转到该Activity  

22.      startActivity(new Intent(context, cls)) ;  

23.  catch (NameNotFoundException e) {  

24.      e.printStackTrace();  

25.  }catch (ClassNotFoundException e) {  

26.      Log.d("", e.toString()) ;  

27.  }  

28.  </span>  




效果如图:

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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值