1. 程式人生 > >實現Android 動態載入APK(Fragment or Activity實現)

實現Android 動態載入APK(Fragment or Activity實現)

最近由於專案太大了,導致編譯通不過(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. publicclass BaseFragment extends Fragment implements IConstant  
  2. {  
  3.   privatestaticfinal String TAG = "BaseFragment";  
  4.   protected String mDexPath;  
  5.   @Override
  6.   publicvoid onCreate(Bundle savedInstanceState)  
  7.   {  
  8.     super.onCreate(savedInstanceState);  
  9.     Bundle bundle=this.getArguments();  
  10.     //動態載入apk的路徑
  11.     mDexPath=bundle.getString(DEX_PATH);  
  12.   }  
  13.   //在Fragment中啟動另外一個Fragment
  14.   protectedvoid replaceFragmentByProxy(String name)  
  15.   {  
  16.     if(mDexPath==null)  
  17.       return;  
  18.     //PROXY_VIEW_ACTION 是ProxyActivity的action
  19.     Intent intent=new Intent(PROXY_VIEW_ACTION);  
  20.     //傳遞apk路徑
  21.     intent.putExtra(DEX_PATH, mDexPath);  
  22.     //是啟動Fragment還是啟動Fragment,這裡啟動的是Fragment
  23.     intent.putExtra(START_TYPE, TYPE_FRAGMENT);  
  24.     //需要載入的fragment的類名
  25.     intent.putExtra(CLASS_NAME, name);  
  26.     this.startActivity(intent);  
  27.   }  
  28. }  

所有需要動態載入的Fragment都需要繼承這個BaseFragment,每次啟動一個Fragment,只需要傳遞apk的路徑即可。
下面是我寫的一個MyFragment,用來使用BitmapFun載入網路圖片的,這裡僅僅是載入並顯示圖片,沒有考慮其他的,如果想深入瞭解BitmapFun的使用,請看我的另外一篇文章:
http://blog.csdn.net/yuanzeyao/article/details/38355719
[java] view plain copy print?
  1. publicclass MyFragment extends BaseFragment  
  2. {  
  3.   privatestaticfinal String TAG = "MyFragment";  
  4.   privatestaticfinal String IMAGE_CACHE_DIR = "thumbs";  
  5.   private ImageFetcher mImageFetcher;  
  6.   private GridView mGridView;  
  7.   private Context context;  
  8.   private Button btn;  
  9.   @Override
  10.   publicvoid onCreate(Bundle savedInstanceState)  
  11.   {  
  12.     super.onCreate(savedInstanceState);  
  13.     ImageCacheParams cacheParams = new ImageCacheParams(getActivity(), IMAGE_CACHE_DIR);  
  14.     cacheParams.setMemCacheSizePercent(0.25f); // Set memory cache to 25% of app memory
  15.     // The ImageFetcher takes care of loading images into our ImageView children asynchronously
  16.     mImageFetcher = new ImageFetcher(getActivity(), 200);  
  17.     mImageFetcher.setLoadingImage(R.drawable.empty_photo);  
  18.     mImageFetcher.addImageCache(getActivity().getSupportFragmentManager(), cacheParams);  
  19.   }  
  20.   @Override
  21.   public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)  
  22.   {  
  23.     //這裡其實可以直接使用R.layout.fragment
  24.     Resources mResources=this.getActivity().getResources();  
  25.     return inflater.inflate(mResources.getIdentifier("fragment""layout""com.dl.client"), container,false);  
  26.   }  
  27.   @Override
  28.   publicvoid onViewCreated(View view, Bundle savedInstanceState)  
  29.   {  
  30.     super.onViewCreated(view, savedInstanceState);  
  31.     mGridView=(GridView) view.findViewById(R.id.gridView);  
  32.     btn=(Button)view.findViewById(R.id.btn_fragment);  
  33.     btn.setOnClickListener(new View.OnClickListener()  
  34.     {  
  35.       @Override
  36.       publicvoid onClick(View arg0)  
  37.       {  
  38.         //在Fragment中動態載入另外一個Fragment
  39.         replaceFragmentByProxy("com.dl.client.TempFragment");  
  40.       }  
  41.     });  
  42.     context=this.getActivity();  
  43.     mGridView.setAdapter(new BaseAdapter()  
  44.     {  
  45.       @Override
  46.       public View getView(int position, View contentView, ViewGroup arg2)  
  47.       {  
  48.         ImageView mImg;  
  49.         if(contentView==null)  
  50.         {  
  51.           contentView=LayoutInflater.from(context).inflate(R.layout.item,null);  
  52.         }  
  53.         mImg=(ImageView)contentView.findViewById(R.id.img_11);  
  54.         //mImg.setImageResource(R.drawable.empty_photo);
  55.         mImageFetcher.loadImage(Images.imageThumbUrls[position], mImg);  
  56.         return contentView;  
  57.       }  
  58.       @Override
  59.       publiclong getItemId(int arg0)  
  60.       {  
  61.         return0;  
  62.       }  
  63.       @Override
  64.       public Object getItem(int arg0)  
  65.       {  
  66.         return Images.imageThumbUrls[arg0];  
  67.       }  
  68.       @Override
  69.       publicint getCount()  
  70.       {  
  71.         return Images.imageThumbUrls.length;  
  72.       }  
  73.     });  
  74.   }  
  75. }  

下面看看這個應用的效果吧:


最後需要注意的一點就是動態載入的apk不能和宿主應用包含相同的jar包,不然會報錯的。。。

文章轉至:點選開啟連結http://blog.csdn.NET/yuanzeyao/article/details/41809423