深入浅出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得意





本文转自mfrbuaa博客园博客,原文链接:http://www.cnblogs.com/mfrbuaa/p/5345975.html,如需转载请自行联系原作者

相关文章
|
1月前
|
Java 开发工具 Windows
Windows环境下面启动jar包,输出的日志出现乱码的解决办法
Windows环境下面启动jar包,输出的日志出现乱码的解决办法
|
1月前
|
网络协议 Java Nacos
Nacos报错问题之jar 包启动就报错误如何解决
Nacos是一个开源的、易于部署的动态服务发现、配置管理和服务管理平台,旨在帮助微服务架构下的应用进行快速配置更新和服务治理;在实际运用中,用户可能会遇到各种报错,本合集将常见的Nacos报错问题进行归纳和解答,以便使用者能够快速定位和解决这些问题。
|
1月前
|
Java Shell Docker
Docker启动后怎样运行jar包文件
Docker启动后怎样运行jar包文件
|
26天前
|
Java Maven 微服务
springboot项目开启远程调试-jar包
springboot项目开启远程调试-jar包
20 0
|
1天前
|
移动开发 Java Unix
Android系统 自动加载自定义JAR文件
Android系统 自动加载自定义JAR文件
15 1
|
1天前
|
Shell Android开发 开发者
Android系统 自定义动态修改init.custom.rc
Android系统 自定义动态修改init.custom.rc
13 0
|
1天前
|
测试技术 Android开发 开发者
RK3568 Android系统客制化动态替换ro任意属性
RK3568 Android系统客制化动态替换ro任意属性
11 1
|
1天前
|
存储 Linux Android开发
RK3568 Android/Linux 系统动态更换 U-Boot/Kernel Logo
RK3568 Android/Linux 系统动态更换 U-Boot/Kernel Logo
14 0
|
2天前
|
Android开发
Android RIL 动态切换 4G 模块适配
Android RIL 动态切换 4G 模块适配
5 0
|
2天前
|
Android开发
Android 动态修改参数配置
Android 动态修改参数配置
11 0