列表的顯示需要三個元素:
-
ListVeiw: 用來展示列表的View。
-
適配器 : 用來把數據映射到ListView上
-
數據: 具體的將被映射的字符串,圖片,或者基本組件。
ListView的工作原理如下:
下面簡單說下上圖的原理:
- 如果你有幾千幾萬甚至更多的選項(item)時,其中只有可見的項目存在內存(內存內存哦,說的優化就是說在內存中的優化!!!)中,其他的在Recycler中
- ListView先請求一個type1視圖(getView)然後請求其他可見的項目。convertView在getView中是空(null)的
- 當item1滾出屏幕,並且一個新的項目從屏幕低端上來時,ListView再請求一個type1視圖。convertView此時不是空值了,它的值是item1。你只需設定新的數據然後返回convertView,不必重新創建一個視圖
- public class MultipleItemsList extends ListActivity {
- private MyCustomAdapter mAdapter;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mAdapter = new MyCustomAdapter();
- for (int i = 0; i < 50; i++) {
- mAdapter.addItem("item " + i);
- }
- setListAdapter(mAdapter);
- }
- private class MyCustomAdapter extends BaseAdapter {
- private ArrayList mData = new ArrayList();
- private LayoutInflater mInflater;
- public MyCustomAdapter() {
- mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- }
- public void addItem(final String item) {
- mData.add(item);
- notifyDataSetChanged();
- }
- @Override
- public int getCount() {
- return mData.size();
- }
- @Override
- public String getItem(int position) {
- return mData.get(position);
- }
- @Override
- public long getItemId(int position) {
- return position;
- }
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- System.out.println("getView " + position + " " + convertView);
- ViewHolder holder = null;
- if (convertView == null) {
- convertView = mInflater.inflate(R.layout.item1, null);
- holder = new ViewHolder();
- holder.textView = (TextView)convertView.findViewById(R.id.text);
- convertView.setTag(holder);
- } else {
- holder = (ViewHolder)convertView.getTag();
- }
- holder.textView.setText(mData.get(position));
- return convertView;
- }
- }
- public static class ViewHolder {
- public TextView textView;
- }
- }
- if(!bmp.isRecycle() ){
- bmp.recycle() //回收圖片所佔的內存
- system.gc() //提醒系統及時回收
- }
下面來列舉下真正意義上的優化吧:
- ViewHolder Tag 必不可少,這個不多說!
-
如果自定義Item中有涉及到圖片等等的,一定要狠狠的處理圖片,圖片佔的內存是ListView項中最噁心的,處理圖片的方法大致有以下幾種:
2.1:不要直接拿個路徑就去循環decodeFile();這是找死….用Option保存圖片大小、不要加載圖片到內存去;
2.2: 拿到的圖片一定要經過邊界壓縮
2.3:在ListView中取圖片時也不要直接拿個路徑去取圖片,而是以WeakReference(使用WeakReference代替強引用。比如可以使 用WeakReference<Context> mContextRef)、SoftReference、WeakHashMap等的來存儲圖片信息,是圖片信息不是圖片哦!
2.4:在getView中做圖片轉換時,產生的中間變量一定及時釋放,用以下形式: - 儘量避免在BaseAdapter中使用static 來定義全局靜態變量,我以爲這個沒影響 ,這個影響很大,static是Java中的一個關鍵字,當用它來修飾成員變量時,那麼該變量就屬於該類,而不是該類的實例。所以用static修飾的變量,它的生命週期是很長的,如果用它來引用一些資源耗費過多的實例(比如Context的情況最多),這時就要儘量避免使用了..
- 如果爲了滿足需求下必須使用Context的話:Context儘量使用Application Context,因爲Application的Context的生命週期比較長,引用它不會出現內存泄露的問題
- 儘量避免在ListView適配器中使用線程,因爲線程產生內存泄露的主要原因在於線程生命週期的不可控制
-
記下小馬自己的錯誤:
之前使用的自定義ListView中適配數據時使用AsyncTask自行開啓線程的,這個比用Thread更危險,因爲Thread只有在run函數不 結束時纔出現這種內存泄露問題,然而AsyncTask內部的實現機制是運用了線程執行池(ThreadPoolExcutor,要想了解這個類的話大家加下我們的Android開發羣五號,因爲其它羣的存儲空間快滿了,所以只上傳到五羣裏了,看下小馬上傳的Gallery源碼,你會對線程執行池、軟、弱、強引用有個更深入的認識),這個類產生的Thread對象的生命週期是不確定的,是應用程序無法控制的,因此如果AsyncTask作爲Activity的內部類,就更容易出現內存泄露的問題。這個問題的解決辦法小馬當時網上查到了記在txt裏了,如下:
6.1:將線程的內部類,改爲靜態內部類。
6.2:在線程內部採用弱引用保存Context引用
示例代碼如下:
- public abstract class WeakAsyncTask<Params, Progress, Result, WeakTarget> extends
- AsyncTask<Params, Progress, Result> {
- protected WeakReference<WeakTarget> mTarget;
- public WeakAsyncTask(WeakTarget target) {
- mTarget = new WeakReference<WeakTarget>(target);
- }
- /** {@inheritDoc} */
- @Override
- protected final void onPreExecute() {
- final WeakTarget target = mTarget.get();
- if (target != null) {
- this.onPreExecute(target);
- }
- }
- /** {@inheritDoc} */
- @Override
- protected final Result doInBackground(Params... params) {
- final WeakTarget target = mTarget.get();
- if (target != null) {
- return this.doInBackground(target, params);
- } else {
- return null;
- }
- }
- /** {@inheritDoc} */
- @Override
- protected final void onPostExecute(Result result) {
- final WeakTarget target = mTarget.get();
- if (target != null) {
- this.onPostExecute(target, result);
- }
- }
- protected void onPreExecute(WeakTarget target) {
- // No default action
- }
- protected abstract Result doInBackground(WeakTarget target, Params... params);
- protected void onPostExecute(WeakTarget target, Result result) {
- // No default action
- }
- }