天天看點

Android性能優化的一些總結1.布局優化2.繪制優化3.記憶體洩漏優化4.響應速度優化5.線程優化6.記憶體洩漏分析工具

1.布局優化

1.1.盡量要減少布局檔案的層級數量

單個

ViewGroup

的時候使用

LinearLayout

,因為

RelativeLayout

功能複雜,要花費更多的CPU時間,而

LinearLayout

FrameLayout

是簡單高效的ViewGroup。而當出現嵌套的布局時候,盡量使用

RelativeLayout

布局優化還可以采用

<include>

<merge>

ViewStub

來控制

<include>

—– 可以将一個指定的布局檔案加載到目前布局中,例如:

代碼:

<include layout="@layout/title" />
           

<merge>

—– 用于層級包含的時候去除多餘的層級,比如上述的内層布局

title

中,如果也是

LinearLayout

,則可以直接用

<merge>

代替,預設布局是

FrameLayout

,例如:

代碼:

<merge xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="我是button3" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="我是button2" />

</merge>
           

ViewStub

—– 繼承

View

,不參與繪制過程,在按需加載的時候才顯示出來,比如網絡異常出錯的時候的頁面,不需要在初始化的時候就加載,用

ViewStub

就可以在使用的時候加載出來,提高初始化頁面的效率。例如。

代碼:

<ViewStub
    android:id="@+id/stub"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:inflatedId="@+id/root"
    android:layout="@layout/net_error" />
           

在需要加載的時候使用:

ViewStub stub = (ViewStub) findViewById(R.id.stub);
stub.setVisibility(View.VISIBLE);
           

或者使用

inflate

:

View view = stub.inflate();
           

2.繪制優化

在自定義View中的

onDraw()

方法裡,避免建立新的對象,因為

onDraw()

會頻繁被調用,降低執行效率。而且也不要有太多的循環操作。

3.記憶體洩漏優化

  1. 避免寫出記憶體洩漏的代碼,這需要靠豐富的程式設計經驗和對語言的熟練掌握。
  2. 使用記憶體洩漏工具MAT來分析

幾個記憶體洩漏的場景:

3.1. 靜态變量導緻的記憶體洩漏

代碼:

public class AndroidTestActivity extends Activity {

    private static View view;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_common);

        view = new View(this);
    }
}
           

靜态變量生命周期是整個

Application

的生命周期,而其内部持有目前

Activity

,是以

Activity

無法正常釋放,造成記憶體洩漏。

3.2. 單利模式造成的記憶體洩漏

單例的靜态特性使得單例的生命周期和應用的生命周期一樣長,若一個對象已經不需要使用了,而單例對象還持有該對象的引用,那麼這個對象将不能被正常回收,就導緻了記憶體洩漏。例如:

代碼:

public class AppManager {

    private static AppManager appManager;
    private Context context;

    private AppManager(Context context) {
        this.context = context;
    }

    public static AppManager getInstance(Context context) {
        if (appManager == null) {
            appManager = new AppManager(context);
        }
        return appManager;
    }
}
           

然後在

Avtivity

中使用該單例

代碼:

public class AndroidTestActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_common);

        AppManager appManager = AppManager.getInstance(this);//傳入的是activity
    }
}
           

Activity

退出的時候,單例還持有該

Activity

的引用,就造成了記憶體洩漏。

可将

this.context = context;
           

修改為

this.context = context.getApplicationContext();
           

這樣外加不論傳入什麼

context

,生命周期都一樣長,就不會存在記憶體洩漏。

3.3. 非靜态内部類導緻記憶體洩漏

非靜态内部類(包括匿名内部類)會持有外部類的引用(靜态的内部類不會持有外部類的引用),當非靜态内部類對象的生命周期比外部類對象的生命周期長時,就會導緻記憶體洩露。

平時經常寫的

Handler

的代碼

代碼:

public class AndroidTestActivity extends Activity {

    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //邏輯處理
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_common);

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep( * );
                    Message message = Message.obtain();
                    message.what = ;
                    handler.sendMessage(message);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}
           

如果

Activity

關閉了,但線程仍在運作,之後發送消息,這時候該線程持有

Handler

的引用,

Handler

又持有

Activity

的引用,就導緻了

Activity

無法回收。可以使用靜态内部類加弱引用的方式,或者直接在

Activity

結束的時候直接清除

Handler

代碼:(靜态内部類+弱引用(因為handler不再持有外部activity的引用,是以無法直接操作activity的資料))

private MyHandler myHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_common);
    myHandler = new MyHandler(this);
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                Thread.sleep( * );
                Message message = Message.obtain();
                message.what = ;
                myHandler.sendMessage(message);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }).start();
}
private static class MyHandler extends Handler {
    private WeakReference<AndroidTestActivity> activityWeakReference;
    public MyHandler(AndroidTestActivity activity) {
        activityWeakReference = new WeakReference<AndroidTestActivity>(activity);
    }
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        AndroidTestActivity androidTestActivity = activityWeakReference.get();
        if (activityWeakReference != null) {
            if (msg.what == ) {
                //邏輯操作
            }
        }
    }
}
           

或者直接在結束的時候銷毀handler的回調和消息。

代碼:

@Override
protected void onDestroy() {
    super.onDestroy();
    handler.removeCallbacksAndMessages(null);
}
           

線程造成的記憶體洩漏還有

Thread

AsyncTask

上述中的

Runnable

就是匿名内部類,也需要改成靜态内部類的方式。

3.4. 資源未關閉導緻記憶體洩漏

對于使用了

BraodcastReceiver

ContentObserver

File

Cursor

Stream

Bitmap

IO

等資源的代碼,應該在Activity銷毀時及時關閉或者登出,否則這些資源将不會被回收,造成記憶體洩漏。

3.5. 集合中的資源未及時清除

使用集合的時候,若某個已經加入的對象不再使用,而集合引用還在使用,則會造成記憶體洩漏,要及時清除掉不使用的集合對象。

remove

或者

clear

3.6. 屬性動畫造成的記憶體洩漏

ACtivity

中播放屬性動畫,如果動畫沒有播放完整就退出

Activity

,雖然無法再看到動畫,但是動畫的控件

View

引用了

Activity

,即

Activity

并沒有銷毀。可在

Activity

銷毀的時候同時清除動畫。

代碼:

@Override
protected void onDestroy() {
    super.onDestroy();
    animator.cancel();
}
           

3.7. WebView造成的記憶體洩漏

銷毀WebView之前需要先将WebView從父容器中移除,然後再銷毀WebView

@Override
protected void onDestroy() {
    super.onDestroy();
    // 先從父控件中移除WebView
    webViewContainer.removeView(mWebView);
    //一系列關閉操作
    webView.stopLoading();
    webView.getSettings().setJavaScriptEnabled(false);
    webView.clearHistory();
    webView.removeAllViews();
    //最後銷毀
    webView.destroy();
}
           

4.響應速度優化

Activity

如果5秒沒有螢幕觸摸事件或者鍵盤輸入事件,

BroadcastReceiver

如果10秒内還未執行完操作,都會出現

ANR

5.線程優化

盡量采用線程池,既可以重用内部線程,也避免了建立和銷毀線程帶來性能影響,還可以控制最大并發數。

6.記憶體洩漏分析工具

6.1.LeakCanary

6.2.MAT