編輯:關於Android編程
最近由於項目太大了,導致編譯通不過(Android對一個應用中的方法個數貌似有限制),所以一直琢磨著能否將某些模塊的APK不用安裝,動態加載,通過在網上查找資料和網友的幫助,終於實現了APK的動態加載,網絡上介紹APK動態加載的文章非常多,但是我覺得寫得非常好的就是這位大牛的,我基本上就是使用他的這種方案,然後加入了自己的元素。這位大牛是通過Activity實現的,我稍作修改,通過Fragment實現動態加載,我個人認為使用Fragmnet更加簡單,因為使用Fragment實現不需要考慮Fragment的生命周期。
第一個問題是因為在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
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(); //動態加載apk的路徑 mDexPath=bundle.getString(DEX_PATH); } //在Fragment中啟動另外一個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); //是啟動Fragment還是啟動Fragment,這裡啟動的是Fragment intent.putExtra(START_TYPE, TYPE_FRAGMENT); //需要加載的fragment的類名 intent.putExtra(CLASS_NAME, name); this.startActivity(intent); } }
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) { //這裡其實可以直接使用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中動態加載另外一個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; } }); } }
最後需要注意的一點就是動態加載的apk不能和宿主應用包含相同的jar包,不然會報錯的。。。
當前比較成熟一點的應用基本上都會在進入應用之顯示一個啟動界面.這個啟動界面或簡單,或復雜,或簡陋,或華麗,用意不同,風格也不同.下面來觀摩幾個流行的應用的啟動界面.1.
在學習使用Scroller之前,需要明白scrollTo()、scrollBy()方法。一、View的scrollTo()、scrollBy()scrollTo、scro
紅米3S是小米在14號推出的新機,紅米3S配備金屬機身、指紋識別、八核主流配置、長續航等特點,性價比很高。隨著紅米3S開啟預約,不僅之後米粉用戶就可以用上這
Android手機話筒采樣有部分代碼是在網上找的,具體不記得了。 使用采樣結果以及是自己的app的功能代碼: 1. 在一部分app中會需要對話筒的音頻輸入獲取使用,比如之