实现Android 动态加载APK(Fragment or Activity实现)

来源:互联网 发布:在线学编程的网站 编辑:程序博客网 时间:2024/05/29 11:27

最近由于项目太大了,导致编译通不过(Android对一个应用中的方法个数貌似有限制),所以一直琢磨着能否将某些模块的APK不用安装,动态加载,通过在网上查找资料和网友的帮助,终于实现了APK的动态加载,网络上介绍APK动态加载的文章非常多,但是我觉得写得非常好的就是这位大牛的,我基本上就是使用他的这种方案,然后加入了自己的元素。这位大牛是通过Activity实现的,我稍作修改,通过Fragment实现动态加载,我个人认为使用Fragmnet更加简单,因为使用Fragment实现不需要考虑Fragment的生命周期。

文章地址:http://blog.csdn.NET/singwhatiwanna/article/details/22597587
          http://blog.csdn.Net/singwhatiwanna/article/details/23387079
一定要读了这两篇文章之后再来读我这篇,因为我是借鉴了这篇文章的思想的。

首先我们需要明白,实现动态加载就是要解决两个问题:(如果使用Fragments实现,则是一个问题)

1、Activity生命周期的管理。

2、动态加载的apk的资源如何获取。

第一个问题是因为在Java中任何一个程序要运行起来,必须通过类加载器将某个类加入内存,当我们通过一个类加载器将Activity加入内存时,其实这个Activity就是一个普通的类,它已经没有生命周期的概念了,在Android系统中,Activity的生命周期是通过ActivityManager来控制的,如果我们通过动态加载的方式加载这个Activity,那么ActivityManager根本就不知道这个Activity的存在,所以我们必须处理好这个Activity的生命周期,至于第二个问题,在Android中,我们获取资源都是通过Context拿到的,而动态加载的APK是没有Context的,所以我们不能和以前一样那样来拿。前面的两篇文章推荐的方法已经能够很好的解决以上两个问题,因此实现了APK的动态加载。
我先来描述一下大牛博客中实现动态加载的思路吧:
创建一个ProxyActivity,通过名字知道,它就是一个代理Activity,我们调用任何一个Activity都是通过调用ProxyActivity实现的,我只需要传入动态加载apk的路径和需要动态加载的类名,比如加载了一个Activity之后,通过反射机制读取到Activity的所有的生命周期函数以及onActivityResult等函数,并保存在一个列表中,在ProxyActivity的onCreate中通过反射调用动态加载的Activity的onCreate,由于ProxyActivity是一个正常的Activity,它的生命周期是正常的,所以在ProxyActivity的生命周期函数中调用动态加载Activity的生命周期函数就ok了,从而实现动态加载的Activity也有生命周期了。同时尽然是代理,那么就代理彻底一点,就干脆把动态加载的Activity中的所有的逻辑都转入到ProxyActivity中。那么这就要求被加载的Activity有一个ProxyActivity的引用,这个可以让所有动态加载的Activity继承一个BaseActivity,这个BaseActivity中有一个setProxy方法,用来设置ProxyActivity。所以不是任何APK,都可以动态加载的,一般只有动态加载自己编写的apk,动态加载别人的apk不太现实。
看了上面的思路,是不是有点借腹生子的感觉,其实就是把动态加载的Activity的逻辑转移到了ProxyActivity

解决资源访问的问题方法就是造ProxyActivity中重载者两个函数
public abstract AssetManager getAssets();
public abstract Resources getResources();
至于为什么能解决资源的问题,我还是推荐几篇文章大家去学习一下吧:
本人的另外一篇文章:http://blog.csdn.net/yuanzeyao/article/details/12955459
讲解Android资源加载机制的一篇文章:http://blog.csdn.net/singwhatiwanna/article/details/24532419
 
好了,上面就是通过Activity实现的动态加载apk,下面看看我是怎么通过Fragment来实现动态加载的,如果熟悉Fragment的同学们应该知道,Fragment就相当于一个有生命周期的View,它的生命周期被所在的Activity的生命周期管理,即使我们通过类加载器把一个Fragment加入到内存,它和以前我们使用的Fragment没有什么两样,只要我们将这个Fragment加入到ProxyActivity,ProxyActivity就会自动的管理好这个Fragment的生命周期。所以我们就不需要担心Fragment的生命周期,下面就来看看代码实现吧:

1、BaseFragment.java

[java] view plain copy print?
  1. public class BaseFragment extends Fragment implements IConstant  
  2. {  
  3.   private static final String TAG = "BaseFragment";  
  4.   protected String mDexPath;  
  5.   @Override  
  6.   public void onCreate(Bundle savedInstanceState)  
  7.   {  
  8.     super.onCreate(savedInstanceState);  
  9.     Bundle bundle=this.getArguments();  
  10.     //动态加载apk的路径  
  11.     mDexPath=bundle.getString(DEX_PATH);  
  12.   }  
  13.     
  14.   //在Fragment中启动另外一个Fragment  
  15.   protected void replaceFragmentByProxy(String name)  
  16.   {  
  17.     if(mDexPath==null)  
  18.       return;  
  19.     //PROXY_VIEW_ACTION 是ProxyActivity的action  
  20.     Intent intent=new Intent(PROXY_VIEW_ACTION);  
  21.     //传递apk路径  
  22.     intent.putExtra(DEX_PATH, mDexPath);  
  23.     //是启动Fragment还是启动Fragment,这里启动的是Fragment  
  24.     intent.putExtra(START_TYPE, TYPE_FRAGMENT);  
  25.     //需要加载的fragment的类名  
  26.     intent.putExtra(CLASS_NAME, name);  
  27.     this.startActivity(intent);  
  28.       
  29.   }  
  30. }  

所有需要动态加载的Fragment都需要继承这个BaseFragment,每次启动一个Fragment,只需要传递apk的路径即可。
下面是我写的一个MyFragment,用来使用BitmapFun加载网络图片的,这里仅仅是加载并显示图片,没有考虑其他的,如果想深入了解BitmapFun的使用,请看我的另外一篇文章:
http://blog.csdn.net/yuanzeyao/article/details/38355719

[java] view plain copy print?
  1. public class MyFragment extends BaseFragment  
  2. {  
  3.   private static final String TAG = "MyFragment";  
  4.     
  5.   
  6.   private static final String IMAGE_CACHE_DIR = "thumbs";  
  7.   private ImageFetcher mImageFetcher;  
  8.   
  9.   private GridView mGridView;  
  10.   private Context context;  
  11.   private Button btn;  
  12.     
  13.   @Override  
  14.   public void onCreate(Bundle savedInstanceState)  
  15.   {  
  16.     super.onCreate(savedInstanceState);  
  17.     ImageCacheParams cacheParams = new ImageCacheParams(getActivity(), IMAGE_CACHE_DIR);  
  18.   
  19.     cacheParams.setMemCacheSizePercent(0.25f); // Set memory cache to 25% of app memory  
  20.   
  21.     // The ImageFetcher takes care of loading images into our ImageView children asynchronously  
  22.     mImageFetcher = new ImageFetcher(getActivity(), 200);  
  23.     mImageFetcher.setLoadingImage(R.drawable.empty_photo);  
  24.     mImageFetcher.addImageCache(getActivity().getSupportFragmentManager(), cacheParams);  
  25.   }  
  26.     
  27.   @Override  
  28.   public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)  
  29.   {  
  30.     //这里其实可以直接使用R.layout.fragment  
  31.     Resources mResources=this.getActivity().getResources();  
  32.     return inflater.inflate(mResources.getIdentifier("fragment""layout""com.dl.client"), container,false);  
  33.   }  
  34.     
  35.   @Override  
  36.   public void onViewCreated(View view, Bundle savedInstanceState)  
  37.   {  
  38.     super.onViewCreated(view, savedInstanceState);  
  39.     mGridView=(GridView) view.findViewById(R.id.gridView);  
  40.     btn=(Button)view.findViewById(R.id.btn_fragment);  
  41.     btn.setOnClickListener(new View.OnClickListener()  
  42.     {  
  43.         
  44.       @Override  
  45.       public void onClick(View arg0)  
  46.       {  
  47.         //在Fragment中动态加载另外一个Fragment  
  48.         replaceFragmentByProxy("com.dl.client.TempFragment");  
  49.       }  
  50.     });  
  51.     context=this.getActivity();  
  52.     mGridView.setAdapter(new BaseAdapter()  
  53.     {  
  54.         
  55.       @Override  
  56.       public View getView(int position, View contentView, ViewGroup arg2)  
  57.       {  
  58.         ImageView mImg;  
  59.         if(contentView==null)  
  60.         {  
  61.           contentView=LayoutInflater.from(context).inflate(R.layout.item,null);  
  62.         }  
  63.         mImg=(ImageView)contentView.findViewById(R.id.img_11);  
  64.         //mImg.setImageResource(R.drawable.empty_photo);  
  65.         mImageFetcher.loadImage(Images.imageThumbUrls[position], mImg);  
  66.         return contentView;  
  67.       }  
  68.         
  69.       @Override  
  70.       public long getItemId(int arg0)  
  71.       {  
  72.         return 0;  
  73.       }  
  74.         
  75.       @Override  
  76.       public Object getItem(int arg0)  
  77.       {  
  78.         return Images.imageThumbUrls[arg0];  
  79.       }  
  80.         
  81.       @Override  
  82.       public int getCount()  
  83.       {  
  84.         return Images.imageThumbUrls.length;  
  85.       }  
  86.     });  
  87.   }  
  88. }  

下面看看这个应用的效果吧:


最后需要注意的一点就是动态加载的apk不能和宿主应用包含相同的jar包,不然会报错的。。。

文章转至:点击打开链接http://blog.csdn.NET/yuanzeyao/article/details/41809423