zoukankan      html  css  js  c++  java
  • 深入浅出Android动态载入jar包技术

    在实际项目中。因为某些业务频繁变更而导致频繁升级client的弊病会造成较差的用户体验,而这也恰是Web App的优势,于是便衍生了一种思路。将核心的易于变更的业务封装在jar包里然后通过网络下载下来,再由android动态载入运行的方案。以改善频繁升级的毛病

      --前言



    该技术的详细实现步骤可參考农民伯伯的博客:http://www.cnblogs.com/over140/archive/2011/11/23/2259367.html
    本文以此为基础,扩展了一个简单的框架,教大家怎样使用该技术实现业务的动态变更升级

    上效果图先:

    再看看csdn code上project的项目结构



    FrameExample 就是我们的demo

    frame是给demo引用的链接库。也就是框架壳

    FrameCore 就是核心框架包。用于被android动态载入的jar包


    ---------------------------------------------------------------------------------------------------------------------------------

    test文件夹展开例如以下


    hfs2_3b287.rar   一个本地httpserver应用,用于存放測试用的apk和jar包
    des.jar           经过优化的可用于动态载入的jar包
    BtcMonitor.apk   測试用的apk


    ----------------------------------------------------------------------------------------------------------------------

    httpserver界面例如以下:




    demo通过frame库提供的api接口调用到framecore核心包里的详细实现代码
    这样当业务详细实现有变更时,仅仅需改动framecore里的内容。然后放到网络上
    framecore里能够实现一个自检測jar包版本号并自己主动更新下载jar包的功能,本例去掉了这个功能

    有兴趣的童鞋能够自己尝试一下,另外本例仅仅提供了下载的接口,其他接口依据框架模板定义自行加入就可以

    再贴一个核心类FrameInstance的实现:

    public class FrameInstance implements IFrame{
    
    	private static final CommonLog log = LogFactory.createLog();
    	
    	private static FrameInstance mInstance;
    	private boolean isFrameInit = false;
    	
    	private Context mContext;
    	
    	private Handler mJarHandler;
    	private DownloadJARProxy mJARProxy;
    	private ISimpleDownloadCallback mJARDownloadCallback;
    	
    
    
    	public static synchronized FrameInstance getInstance(Context context) {
    		if (mInstance == null){
    			mInstance  = new FrameInstance(context);
    		}
    		return mInstance;
    	}
    
    	private FrameInstance(Context context)
    	{	
    		mContext = context;
    		
    		mJarHandler = new Handler(){
    
    			@Override
    			public void handleMessage(Message msg) {		
    				switch(msg.what){
    					case IHandlerMsg.ON_START:
    
    						break;
    					case IHandlerMsg.ON_PROGRESS:
    					{					
    						int cur = msg.arg1;
    						int max = msg.arg2;
    
    						
    						double rate = cur * 1.0 / max;
    						int value = (int) (rate * 100);
    						String conString = String.valueOf(value) + "%";
    						log.e("download jar percent:" + conString);
    					}
    						break;
    					case IHandlerMsg.ON_SUCCESS:
    						if (mJARDownloadCallback != null){
    							mJARDownloadCallback.onDownload(true);
    						}
    						break;
    					case IHandlerMsg.ON_FAIL:
    						if (mJARDownloadCallback != null){
    							mJARDownloadCallback.onDownload(false);
    						}
    						break;
    					case IHandlerMsg.ON_CANCEL:
    						break;
    				}
    			}
    			
    		};
    		
    	}
    
    
    	@Override
    	public boolean startFramework() {
    
    		if (isFrameInit){
    			return true;
    		}	
    		isFrameInit = loadFrameCore();
    		log.e("startFramework ret = " + isFrameInit);
    		
    		if (mIFrameCore != null){
    			mIFrameCore.startEngine(mContext);
    		}
    		
    		return isFrameInit;
    	}
    
    	@Override
    	public boolean stopFramework() {
    
    		if (!isFrameInit){
    			return true;
    		}	
    		
    		if (mIFrameCore != null){
    			mIFrameCore.stopEngine(mContext);
    		}
    		
    		releaseFrameCore();
    		isFrameInit = false;		
    		log.e("stopFramework... ");
    		
    		return true;
    	}
    
    
    
    	@Override
    	public boolean isFrameworkInit() {
    		return isFrameInit;
    	}
    	
    	@Override
    	public boolean isFrameCoreExist() {
    
    		if (!FrameTool.hasSDCard())
    			return false;
    		
    		File file = new File(FrameTool.getJARSavePath());
    		if (!file.exists()){
    			return false;
    		}
    		
    		return true;
    	}
    
    	@Override
    	public boolean startDownloadFrameCore(ISimpleDownloadCallback callback) {
    
    		if (!FrameTool.hasSDCard()){
    			log.e("SDCard not exist!!!");
    			return false;
    		}
    
    		
    		if (mJARProxy != null){
    			if (mJARProxy.isTaskRunning()){
    				return true;
    			}
    		}
    		
    
    		mJARProxy = new DownloadJARProxy(mJarHandler);
    		mJARProxy.startDownloadTask(FrameTool.getJARURL(), FrameTool.getJARSavePath());
    		mJARDownloadCallback = callback;
    		log.e("startDownloadFrameCore...JAR_URL:" + FrameTool.getJARURL() + ", SAVE_PATH:" +  FrameTool.getJARSavePath());
    		
    		return true;
    	}
    
    	
    	
    	public boolean updateDownloadAPK(String url, String dstPath, IUpdateDownloadCallback callback){
    		if (mIUpdateTools == null){
    			log.e("mIUpdateTools = null!!!");
    			return false;
    		}
    		
    		if (TextUtils.isEmpty(url) || TextUtils.isEmpty(dstPath)){
    			return false;
    		}
    		
    		mIUpdateTools.updateDownloadAPK(url, dstPath, callback);
    		
    		return true;
    	}
    	
    	public boolean cancelDownloadAPK(){
    		if (mIUpdateTools == null){
    			log.e("mIUpdateTools = null!!!");
    			return false;
    		}
    		
    		mIUpdateTools.cancelDownloadAPK();
    		
    		return true;
    	}
    	
    	public boolean checkJARVersion(){
    		
    		
    		
    		
    		return true;
    	}
    	
    	
    	public boolean installAPK(String path){
    
    		if (TextUtils.isEmpty(path)){
    			return false;
    		}
    		
    	    Intent i = new Intent(Intent.ACTION_VIEW);
    	    i.setDataAndType(Uri.parse("file://" + path), "application/vnd.android.package-archive");
    	    i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    	    mContext.startActivity(i);
    		
    		
    		return true;
    	}
    	
    	
    	private IFrameCore mIFrameCore;
    	private IUpdateTools mIUpdateTools;
    	private boolean loadFrameCore(){
    		
    //		if (true){
    //			mIVersionTools = new VersionTools();
    //			mIUpdateTools = new UpdateTools();
    //			return true;
    //		}
    		
    		if (!isFrameCoreExist()){
    			return false;
    		}
    		
    		DexClassLoader classLoader = DexClassLoadTools.newDexClassLoader(mContext, FrameTool.getJARSavePath());
    		if (classLoader == null){
    			return false;
    		}
    		
    		mIFrameCore = ClassFactory.newFrameCore(classLoader);
    		if (mIFrameCore == null){
    			return false;
    		}
    		
    		mIUpdateTools = ClassFactory.newUpdateTools(classLoader);
    		return true;
    	}
    	
    	private void releaseFrameCore(){
    		mIFrameCore = null;
    		mIUpdateTools = null;
    	}
    	
    
    	private static class ClassFactory{
    		public static IFrameCore newFrameCore(DexClassLoader classLoader){
    			IFrameCore object = null;
    			Class cls = newFrameCoreClass(classLoader, "com.lance.framecore.externex.FrameCore");
    		    if (cls != null){
    		    	try {
    					object = (IFrameCore) cls.newInstance();
    				} catch (InstantiationException e) {
    					e.printStackTrace();
    				} catch (IllegalAccessException e) {
    					e.printStackTrace();
    				}
    		    }
    		    return object;
    		}
    		
    		public static IUpdateTools newUpdateTools(DexClassLoader classLoader){
    			IUpdateTools object = null;
    			Class cls = newFrameCoreClass(classLoader, "com.lance.framecore.externex.UpdateTools");
    		    if (cls != null){
    		    	try {
    					object = (IUpdateTools) cls.newInstance();
    				} catch (InstantiationException e) {
    					e.printStackTrace();
    				} catch (IllegalAccessException e) {
    					e.printStackTrace();
    				}
    		    }
    		    return object;
    		}
    		
    		
    		public static Class newFrameCoreClass(DexClassLoader classLoader, String className){
    			
    			   Class libProviderClazz = null;		       
    		       try {
    		           libProviderClazz = classLoader.loadClass(className);
    		       } catch (Exception exception) {
    		           exception.printStackTrace();
    		       }
    		       return libProviderClazz;
    		}
    		
    	}
    
    	
    	public static class FrameTool{
    		
    		private final static String JAR_URL = "http://192.168.1.101/jar/des.jar";
    		public static boolean hasSDCard() {
    			String status = Environment.getExternalStorageState();
    			if (!status.equals(Environment.MEDIA_MOUNTED)) {
    				return false;
    			} 
    			return true;
    		}
    		
    
    		public static String getRootFilePath() {
    			if (hasSDCard()) {
    				return Environment.getExternalStorageDirectory().getAbsolutePath() + "/";
    			} else {
    				return Environment.getDataDirectory().getAbsolutePath() + "/data/"; 
    			}
    		}
    		
    		public static String getJARURL(){
    			return JAR_URL;
    		}
    		
    		public static String getJARSavePath(){
    			return getRootFilePath() + "FrameCore.jar";
    		}
    		
    
    		
    	}
    	
    
    	
    	private static class DexClassLoadTools{
    		
    		public static DexClassLoader newDexClassLoader(Context context, String jarPath){
    			
    			final File optimizedDexOutputPath = new File(jarPath);  
    			if (!optimizedDexOutputPath.exists()){
    				return null;
    			}
    			
    
    			File file = context.getDir("osdk", 0);
    			log.e("getDir:" + file.getAbsolutePath());
    
    			DexClassLoader cl = new DexClassLoader(optimizedDexOutputPath.getAbsolutePath(),
    				   file.getAbsolutePath(),
    	               null,
    	               context.getClassLoader());
    		  
    		   return cl;
    		}
    	}
    
    
    
    	@Override
    	public boolean deleteFrameCore() {
    		log.e("deleteFrameCore");
    		
    		if (!isFrameCoreExist()){
    			log.e("framecore.jar is not exist:" + FrameTool.getJARSavePath());
    			return false;
    		}
    		
    		FileTools.deleteDirectory(FrameTool.getJARSavePath());
    		
    		return true;
    	}
    
    	@Override
    	public FrameCoreInfo getFrameCoreInfo() {
    
    		try {
    			if (mIFrameCore == null){
    				return new FrameCoreInfo();
    			}
    			
    			return mIFrameCore.getFrameCoreInfo(mContext);
    		} catch (Exception e) {
    			e.printStackTrace();
    			return new FrameCoreInfo();
    		}
    
    
    	}
    
    
    	
    	
    	
    }


    值得注意的是

    在导出framecore时无需导出com.lance.framecore.extern包下代码,否则载入时会出现反复定义错误

    同一时候要保持framecoreproject和frameproject该包代码的一致,在扩展接口时把对应接口写在这个包下就可以


    --------------------------------------------------------------------------------------------------------------------------------

    其他的没啥好说的了,自个儿download代码看吧
    以下附上code地址:https://code.csdn.net/geniuseoe2012/dynamicjar



    假设童鞋们认为本文实用,最好还是关注我的code主页:https://code.csdn.net/geniuseoe2012

    以后一些博文相关的demo会放在上面,这样大家就不用耗下载积分了。同一时候便于代码更正


    很多其它开源项目可关注我的github主页:https://github.com/geniusgithub得意


  • 相关阅读:
    济南学习D1T5__HEAP
    快速计算C(n,r)
    快速阶乘算法
    济南学习D2T1__折纸带
    济南学习D3T1__线性筛和阶乘质因数分解
    栈与队列:栈的链式储存结构
    线性表应用:建立一个随机数 链表获得中间结点
    栈与队列应用:二进制转十进制 八进制 十六进制(栈)
    线性表:单链表基本操作代码
    线性表应用:魔术师发牌与拉丁(Latin)方阵(循环链表)
  • 原文地址:https://www.cnblogs.com/mfrbuaa/p/5345975.html
Copyright © 2011-2022 走看看