安卓學習筆記之內存優化(一)
一個良好的App是經過嚴格的性能優化和內存優化給用戶帶來良好的操作。今天就說一下內存優化。
Java四種引用
Java的四種引用方式。
- 強引用 無論內存充足與否,系統都不會銷毀對象實例。
- 弱引用 只要產生了GC(垃圾回收器),弱引用實例對象容易被銷毀。
- 虛引用 檢測對象是否已經回收
- 軟引用 只要內存不足,就會被釋放
通過代碼來演示一下效果。
public static void main(String[] args){
//強引用 內存無論怎么樣系統都不會釋放
String str=new String("String");
//軟引用 只要內存不足就釋放掉
SoftReference<String> softReference=new SoftReference<String>(str);
//弱引用 只要系統產生了GC(垃圾回收)它引用的對象就會被釋放掉
WeakReference<String> weakReference=new WeakReference<String>(str);
//虛引用 判斷對象是否已被回收 很少用得上
PhantomReference phantomReference=new PhantomReference<String>(str)
System.out.println("強引用"+str);
System.out.println("軟引用"+softReference.get());
System.out.println("弱引用"+weakReference.get());
}
當點擊運行時候,輸出的結果。
強引用String
軟引用String
弱引用String
那么如何手動去銷毀其對象實例呢?
強引用可以手動將對象置為null。
弱引用和軟引用可以手動調用clear()的方法,弱引用也可以調用gc進行回收,代碼如下。
public static void main(String[] args){
//強引用 內存無論怎么樣系統都不會釋放
String str=new String("String");
//軟引用 內存不足就釋放掉
SoftReference<String> softReference=new SoftReference<String>(str);
//弱引用 只要系統產生了GC(垃圾回收)它引用的對象就會被釋放掉
WeakReference<String> weakReference=new WeakReference<String>(str);
//虛引用 判斷對象是否已被回收
PhantomReference phantomReference=new PhantomReference<String>(str)
str=null;
softReference.clear();
System.out.println("強引用"+str);
System.out.println("軟引用"+softReference.get());
System.gc();
//這種方式也是可以的
weakReference.clear();
System.out.println("弱引用"+weakReference.get());
}
運行輸出效果
強引用null
軟引用null
弱引用null
什么是內存泄漏?
舉一個例子,如果一個Activity啟動的時候執行了長時間的耗時操作,當該耗時操作并未完全結束時,用戶點擊了back回退,此時的系統產生的Activity頁面消失,后臺耗時操作仍然運行并持有Activity的對象實例,這樣就會導致內存泄漏。也就是說,無用對象仍然被引用而導致內存泄漏。
為了提高開發的穩定性,使用相關的工具進行查找內存泄漏的原因。
LeakCannary
這是第三方工具,可以方便得運用到項目中并快速的找到app是否存在內存泄漏的隱患。
1.首先是添加依賴
dependencies {
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5' releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5' testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
}
2.創建一個類繼承自Application,在onCreate()方法中添加如下代碼:
public class ExampleApplication extends Application {
@Override public void onCreate() {
super.onCreate();
if (LeakCanary.isInAnalyzerProcess(this)) {
// This process is dedicated to LeakCanary for heap analysis.
// You should not init your app in this process.
return;
}
LeakCanary.install(this);
// Normal app init code...
}
}
這樣就能使用了。貼代碼。
MainActivity類
public class MainActivity extends Activity implements View.OnClickListener {
private Button btn_press;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_press= (Button) findViewById(R.id.btn_press);
btn_press.setOnClickListener(this);
}
@Override
public void onClick(View view) {
MyThread myThread = new MyThread();
myThread.start();
}
public class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
MyApplication類
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
if (LeakCanary.isInAnalyzerProcess(this)) {
// This process is dedicated to LeakCanary for heap analysis.
// You should not init your app in this process.
return;
}
LeakCanary.install(this);
// Normal app init code...
}
}
添加一個按鈕
<Button
android:id="@+id/btn_press"
android:text="Press"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
運行效果。當點擊按鈕是按back回退到主界面。過一會在導航欄的位置就會出現相應的泄漏信息。
常見的內存泄漏
內部類隱式持有外部類的引用導致的內存泄漏。
好比上面演示的例子。當Activity創建的實例被系統銷毀時,創建的MyThread類依舊持有activity的對象所以報錯。
解決的辦法有三種,分別是在
- 將MyThread類變成靜態類,
- 在包下重新創建一個MyThread類。然后在MainActivity調用就行了。
- 采用弱引用的方式
這里演示一下弱引用的方式防止內存泄漏。貼上代碼。修改上面的代碼。
public class MainActivity extends Activity implements View.OnClickListener {
private Button btn_press;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_press= (Button) findViewById(R.id.btn_press);
btn_press.setOnClickListener(this);
}
@Override
public void onClick(View view) {
MyThread myThread = new MyThread(MainActivity.this);
myThread.start();
}
public static class MyThread extends Thread{
//創建一個弱引用對象
private WeakReference<MainActivity> mReference=null;
//在構造函數初始化弱引用的對象
public MyThread(MainActivity activity){
this.mReference=new WeakReference<MainActivity>(activity);
}
@Override
public void run() {
//取得弱引用的對象并和activity關聯
MainActivity activity = mReference.get();
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
這樣工具就不會提示出錯了。
Handler內存泄漏
Handler經常存在內存溢出的隱患,因為Handler發送消息過程中,可能存在長時間的耗時操作。為了避免這種情況,我們同樣適用弱引用來防止內存泄漏。
代碼演示一下,
public class MainActivity extends Activity implements View.OnClickListener {
private Button btn_press;
private Handler mhandler=new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 1:
btn_press.setText("接收消息,修改文本");
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_press= (Button) findViewById(R.id.btn_press);
btn_press.setOnClickListener(this);
}
@Override
public void onClick(View view) {
StartHandler();
}
private void StartHandler() {
Message message=Message.obtain();
message.what=1;
message.obj="發送延遲任務";
mhandler.sendMessageDelayed(message,20000);
}
}
雖然按鈕的文本確實改變了,但是工具依舊會報錯,這是因為,Handler中的Looper類,最后需要從MessageQueue中取出消息,如果我們在發送消息的工程共需要20s的延遲時間,那么Looper這個類就一直處于等待的狀態所以導致了內存泄漏。
同樣,解決的辦法是使用弱引用的方法。
貼代碼
public class MainActivity extends Activity implements View.OnClickListener {
private Button btn_press;
//將MainActivity 傳進去
private MyHandler myHandler=new MyHandler(MainActivity.this);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_press= (Button) findViewById(R.id.btn_press);
btn_press.setOnClickListener(this);
}
@Override
public void onClick(View view) {
StartHandler();
}
private void StartHandler() {
Message message=Message.obtain();
message.what=1;
message.obj="發送延遲任務";
mhandler.sendMessageDelayed(message,20000);
}
private static class MyHandler extends Handler{
private WeakReference<MainActivity> mReference;
public MyHandler(MainActivity activity){
this.mReference=new WeakReference<MainActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
MainActivity mainActivity = mReference.get();
if(mainActivity==null){
return ;
}
switch (msg.what){
case 1:
//通過軟引用中的對象可以拿到外部非靜態的Button對象
mainActivity.btn_press.setText("修改了按鈕文本");
break;
}
}
}
}
Fragment如何檢測內存泄漏?
對于Fragment的內存泄漏,它有特殊的方法。
需要在MyApplication創建一個RefWatcher觀察者對象。
public class MyApplication extends Application {
public static RefWatcher mRefWatcher;
@Override public void onCreate() {
super.onCreate();
//...
mRefWatcher = LeakCanary.install(this);
// Normal app init code...
}
}
在Fragment的onDestroy()方法中添加
MyApplication.mRefWatcher.watch(this);
同理方法案例和上面相同。讀者自己摸索。
智能推薦
安卓學習筆記——URI
一、URI:統一資源定位符,Universal Resource Identifier的簡稱 每一個ContentProvider都擁有一個公共的URI,這個URI用于表示ContentProvider提供的數據 二、URI的組成: 例: 三、URI 常用方法: 1、static Uri parse(String UriString):將一個字符串轉換為Uri 例如: 2、 List getPat...
內存優化之內存泄漏
我們能通過這篇文章學到什么?我們這篇文章主要通過兩點來講: 探討關于內存泄露的原因; 1.1 什么是內存泄漏 1.2 導致內存泄露的根本原因 實戰找出內存泄漏并解決; 2.1 怎么定位內存泄漏 2.2 怎么解決內存泄漏 什么是內存泄漏 內存泄漏是指對象不再被應用程序使用,但是垃圾回收器卻不能移除它們,因為它們正在被引用。為了方便大家理解,我用一個例子幫助大家理解垃圾回收機制; 大家去過食堂吃飯吧,...
SQLite數據庫存儲(一)【安卓學習筆記】
對于MODE_PRIVATE,MODE_APPEND兩種模式,對寫文件的影響有什么不同? MODE_PRIVATE:該文件只能被當前程序讀寫,會把原來的內容覆蓋掉 MODE_APPEND:該文件的內容可追加,不會把原來的內容覆蓋掉,新寫的內容追加在文件后面 但是對于修改文件中的部分內容,應該怎么做呢?這就需要我們用到SQLite數據庫 主要內容: 創建和打開一個SQLite 數據庫 SQLite數...
安卓專項測試之內存及PerfDog和SoloPi比較
這次繼續安卓專項測試之內存 Android系統中每個APP占內存會有私有和公共的兩部分:ShareDirty、PrivateDirty。“PrivateDirty”內存是其最重要的部分,因為只被自己的進程使用。它只在內存中存儲,因此不能做分頁存儲到外存(Android不支持swap)。所有分配的Dalvik堆和本地堆都是“private dirty”...
猜你喜歡
freemarker + ItextRender 根據模板生成PDF文件
1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...
電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!
Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...
requests實現全自動PPT模板
http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...
Linux C系統編程-線程互斥鎖(四)
互斥鎖 互斥鎖也是屬于線程之間處理同步互斥方式,有上鎖/解鎖兩種狀態。 互斥鎖函數接口 1)初始化互斥鎖 pthread_mutex_init() man 3 pthread_mutex_init (找不到的情況下首先 sudo apt-get install glibc-doc sudo apt-get install manpages-posix-dev) 動態初始化 int pthread_...