首先我們需要明白,實(shí)現(xiàn)動(dòng)態(tài)加載就是要解決兩個(gè)問題:(如果使用Fragments實(shí)現(xiàn),則是一個(gè)問題)
1、Activity生命周期的管理。
2、動(dòng)態(tài)加載的apk的資源如何獲取。
第一個(gè)問題是因?yàn)樵?a target="_blank" href='http://tipsywinegypsy.com/fz/java/'>java中任何一個(gè)程序要運(yùn)行起來,必須通過類加載器將某個(gè)類加入內(nèi)存,當(dāng)我們通過一個(gè)類加載器將Activity加入內(nèi)存時(shí),其實(shí)這個(gè)Activity就是一個(gè)普通的類,它已經(jīng)沒有生命周期的概念了,在Android系統(tǒng)中,Activity的生命周期是通過ActivityManager來控制的,如果我們通過動(dòng)態(tài)加載的方式加載這個(gè)Activity,那么ActivityManager根本就不知道這個(gè)Activity的存在,所以我們必須處理好這個(gè)Activity的生命周期,至于第二個(gè)問題,在Android中,我們獲取資源都是通過Context拿到的,而動(dòng)態(tài)加載的APK是沒有Context的,所以我們不能和以前一樣那樣來拿。前面的兩篇文章推薦的方法已經(jīng)能夠很好的解決以上兩個(gè)問題,因此實(shí)現(xiàn)了APK的動(dòng)態(tài)加載。
我先來描述一下大牛博客中實(shí)現(xiàn)動(dòng)態(tài)加載的思路吧:
創(chuàng)建一個(gè)ProxyActivity,通過名字知道,它就是一個(gè)代理Activity,我們調(diào)用任何一個(gè)Activity都是通過調(diào)用ProxyActivity實(shí)現(xiàn)的,我只需要傳入動(dòng)態(tài)加載apk的路徑和需要?jiǎng)討B(tài)加載的類名,比如加載了一個(gè)Activity之后,通過反射機(jī)制讀取到Activity的所有的生命周期函數(shù)以及onActivityResult等函數(shù),并保存在一個(gè)列表中,在ProxyActivity的onCreate中通過反射調(diào)用動(dòng)態(tài)加載的Activity的onCreate,由于ProxyActivity是一個(gè)正常的Activity,它的生命周期是正常的,所以在ProxyActivity的生命周期函數(shù)中調(diào)用動(dòng)態(tài)加載Activity的生命周期函數(shù)就ok了,從而實(shí)現(xiàn)動(dòng)態(tài)加載的Activity也有生命周期了。同時(shí)盡然是代理,那么就代理徹底一點(diǎn),就干脆把動(dòng)態(tài)加載的Activity中的所有的邏輯都轉(zhuǎn)入到ProxyActivity中。那么這就要求被加載的Activity有一個(gè)ProxyActivity的引用,這個(gè)可以讓所有動(dòng)態(tài)加載的Activity繼承一個(gè)BaseActivity,這個(gè)BaseActivity中有一個(gè)setProxy方法,用來設(shè)置ProxyActivity。所以不是任何APK,都可以動(dòng)態(tài)加載的,一般只有動(dòng)態(tài)加載自己編寫的apk,動(dòng)態(tài)加載別人的apk不太現(xiàn)實(shí)。
看了上面的思路,是不是有點(diǎn)借腹生子的感覺,其實(shí)就是把動(dòng)態(tài)加載的Activity的邏輯轉(zhuǎn)移到了ProxyActivity
解決資源訪問的問題方法就是造ProxyActivity中重載者兩個(gè)函數(shù)
public abstract AssetManager getAssets();
public abstract Resources getResources();
至于為什么能解決資源的問題,我還是推薦幾篇文章大家去學(xué)習(xí)一下吧:
本人的另外一篇文章:http://blog.csdn.net/yuanzeyao/article/details/12955459
講解Android資源加載機(jī)制的一篇文章:http://blog.csdn.net/singwhatiwanna/article/details/24532419
好了,上面就是通過Activity實(shí)現(xiàn)的動(dòng)態(tài)加載apk,下面看看我是怎么通過Fragment來實(shí)現(xiàn)動(dòng)態(tài)加載的,如果熟悉Fragment的同學(xué)們應(yīng)該知道,F(xiàn)ragment就相當(dāng)于一個(gè)有生命周期的View,它的生命周期被所在的Activity的生命周期管理,即使我們通過類加載器把一個(gè)Fragment加入到內(nèi)存,它和以前我們使用的Fragment沒有什么兩樣,只要我們將這個(gè)Fragment加入到ProxyActivity,ProxyActivity就會(huì)自動(dòng)的管理好這個(gè)Fragment的生命周期。所以我們就不需要擔(dān)心Fragment的生命周期,下面就來看看代碼實(shí)現(xiàn)吧:
1、BaseFragment.java
[java] view plain copy
public class BaseFragment extends Fragment implements IConstant
{
private static final String TAG = "BaseFragment";
protected String mDexPath;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Bundle bundle=this.getArguments();
//動(dòng)態(tài)加載apk的路徑
mDexPath=bundle.getString(DEX_PATH);
}
//在Fragment中啟動(dòng)另外一個(gè)Fragment
protected void replaceFragmentByProxy(String name)
{
if(mDexPath==null)
return;
//PROXY_VIEW_ACTION 是ProxyActivity的action
Intent intent=new Intent(PROXY_VIEW_ACTION);
//傳遞apk路徑
intent.putExtra(DEX_PATH, mDexPath);
//是啟動(dòng)Fragment還是啟動(dòng)Fragment,這里啟動(dòng)的是Fragment
intent.putExtra(START_TYPE, TYPE_FRAGMENT);
//需要加載的fragment的類名
intent.putExtra(CLASS_NAME, name);
this.startActivity(intent);
}
}
所有需要?jiǎng)討B(tài)加載的Fragment都需要繼承這個(gè)BaseFragment,每次啟動(dòng)一個(gè)Fragment,只需要傳遞apk的路徑即可。
下面是我寫的一個(gè)MyFragment,用來使用BitmapFun加載網(wǎng)絡(luò)圖片的,這里僅僅是加載并顯示圖片,沒有考慮其他的,如果想深入了解BitmapFun的使用,請(qǐng)看我的另外一篇文章:
http://blog.csdn.net/yuanzeyao/article/details/38355719
[java] view plain copy
public class MyFragment extends BaseFragment
{
private static final String TAG = "MyFragment";
private static final String IMAGE_CACHE_DIR = "thumbs";
private ImageFetcher mImageFetcher;
private GridView mGridView;
private Context context;
private Button btn;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
ImageCacheParams cacheParams = new ImageCacheParams(getActivity(), IMAGE_CACHE_DIR);
cacheParams.setMemCacheSizePercent(0.25f); // Set memory cache to 25% of app memory
// The ImageFetcher takes care of loading images into our ImageView children asynchronously
mImageFetcher = new ImageFetcher(getActivity(), 200);
mImageFetcher.setLoadingImage(R.drawable.empty_photo);
mImageFetcher.addImageCache(getActivity().getSupportFragmentManager(), cacheParams);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
//這里其實(shí)可以直接使用R.layout.fragment
Resources mResources=this.getActivity().getResources();
return inflater.inflate(mResources.getIdentifier("fragment", "layout", "com.dl.client"), container,false);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState)
{
super.onViewCreated(view, savedInstanceState);
mGridView=(GridView) view.findViewById(R.id.gridView);
btn=(Button)view.findViewById(R.id.btn_fragment);
btn.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View arg0)
{
//在Fragment中動(dòng)態(tài)加載另外一個(gè)Fragment
replaceFragmentByProxy("com.dl.client.TempFragment");
}
});
context=this.getActivity();
mGridView.setAdapter(new BaseAdapter()
{
@Override
public View getView(int position, View contentView, ViewGroup arg2)
{
ImageView mImg;
if(contentView==null)
{
contentView=LayoutInflater.from(context).inflate(R.layout.item,null);
}
mImg=(ImageView)contentView.findViewById(R.id.img_11);
//mImg.setImageResource(R.drawable.empty_photo);
mImageFetcher.loadImage(Images.imageThumbUrls[position], mImg);
return contentView;
}
@Override
public long getItemId(int arg0)
{
return 0;
}
@Override
public Object getItem(int arg0)
{
return Images.imageThumbUrls[arg0];
}
@Override
public int getCount()
{
return Images.imageThumbUrls.length;
}
});
}
}
下面看看這個(gè)應(yīng)用的效果吧:
最后需要注意的一點(diǎn)就是動(dòng)態(tài)加載的apk不能和宿主應(yīng)用包含相同的jar包,不然會(huì)報(bào)錯(cuò)的。。。