亚洲视频二区_亚洲欧洲日本天天堂在线观看_日韩一区二区在线观看_中文字幕不卡一区

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.430618.com 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

【CSDN 編者按】對(duì)于眾多 Android 程序員而言,在需求與應(yīng)用性能之間,主要精力都會(huì)放到新需求的開發(fā)。隨著項(xiàng)目復(fù)雜度的增加,應(yīng)用性能越來越低,出現(xiàn)各種問題。程序員們奔波于各種“救火現(xiàn)場(chǎng)”,疲于奔命。本文作者 Aritra Roy 分享了自己在 Android 應(yīng)用程序開發(fā)過程中所遇以及思考,針對(duì)內(nèi)存泄漏提煉出一套可以應(yīng)用于開發(fā)中的方法論。也許會(huì)讓你的開發(fā)效率事半功倍。

作者 | Aritra Roy,Android 開發(fā)者

譯者 | 羅昭成,責(zé)編 | 唐小引

封圖 | CSDN 付費(fèi)下載自東方 IC

出品 | CSDN(ID:CSDNnews)

以下為譯文:

我們都知道,寫一個(gè) Android 的應(yīng)用很容易,但是要寫一個(gè)高性能的應(yīng)用可就不容易了。以我的個(gè)人經(jīng)驗(yàn)來說,在 App 的開發(fā)過程中,主要的精力都會(huì)放在新功能、新模塊、新組件的開發(fā)上。

開發(fā)過程中,看得見的 UI 比看不見的性能更能吸引我們的目光。所以我強(qiáng)制自己將“優(yōu)化應(yīng)用程序(如內(nèi)存泄漏)”的優(yōu)先級(jí)提高,并養(yǎng)成習(xí)慣。

長(zhǎng)期以來,不關(guān)注性能,帶來了很多的技術(shù)債。經(jīng)過一年多的努力調(diào)整, 比起一年前,我有很多的心得體會(huì)。

對(duì)于很多開發(fā)者來說,內(nèi)存泄漏都是一個(gè)老大難的問題。關(guān)于處理內(nèi)存泄漏,你有可能會(huì)覺得太難,又或是太費(fèi)時(shí),又或者是覺得完全沒有意義。但我要告訴你的是,事實(shí)并非如此。當(dāng)你開始處理這些問題的時(shí)候,你會(huì)發(fā)現(xiàn),這感覺超級(jí)棒。

在本篇文章中,我會(huì)以盡可能簡(jiǎn)單的方式講解這些問題,即使你是一個(gè)初學(xué)者,也可以學(xué)習(xí)到如何構(gòu)建一個(gè)高質(zhì)量、高性能的應(yīng)用。

1.垃圾回收

JAVA 是一個(gè)非常強(qiáng)大的語言。在 Android 中,我們幾乎不會(huì)像 C / C++ 那樣,手動(dòng)分配和釋放內(nèi)存。因?yàn)?Java 會(huì)自動(dòng)清理內(nèi)存。

讓我們來思考一個(gè)問題,如果 Java 內(nèi)建的垃圾回收系統(tǒng)可以在我們不需要的時(shí)候自動(dòng)回收內(nèi)存,那我們?yōu)槭裁催€需要關(guān)心內(nèi)存呢?是因?yàn)槔厥諜C(jī)制不夠完善嗎?

當(dāng)然不是,Java 垃圾回收機(jī)制當(dāng)然是完善的。垃圾回收機(jī)制是可以正常工作,但是,如果我們的應(yīng)用程序出現(xiàn) Bug, 導(dǎo)致垃圾回收器不能正常檢查出不需要的內(nèi)存塊,就會(huì)導(dǎo)致問題。

總體來說,是我們自己的代碼錯(cuò)誤導(dǎo)致垃圾回收不可用。不可否認(rèn),垃圾回收機(jī)制是 Java 最偉大的設(shè)計(jì)之一。

2.關(guān)于垃圾回收器

在處理內(nèi)存問題之前,你需要了解垃圾回收器的工作原理。它的概念非常簡(jiǎn)單,但在它背后,有著極其復(fù)雜的邏輯。但是你也別擔(dān)心,我們也只關(guān)心一些簡(jiǎn)單的概念。

火速收藏!Android 開發(fā)者必會(huì)的內(nèi)存泄漏指南

如圖所示,Android 或者 Java 應(yīng)用程序都有一個(gè)起點(diǎn),從對(duì)象的初始化,并且調(diào)用方法。我們可以認(rèn)為,這個(gè)點(diǎn)就是圖中的 "GC Roots"。有一些對(duì)象引用被 GC Roots 直接持有,剩下的則由它們自己創(chuàng)建并持有。

如此,整個(gè)引用鏈構(gòu)成了內(nèi)存樹。垃圾回收器從 GC roots 開始,直接或間接的鏈接著其它的對(duì)象。當(dāng)整個(gè)遍歷結(jié)束,還有一些不能被訪問到的對(duì)象,就是變成了垃圾,這些對(duì)象就會(huì)被垃圾回收器回收。

3.內(nèi)存泄漏

到現(xiàn)在,你已經(jīng)知道了垃圾回收的概念,也知道了垃圾回收在 Android 中是如何管理內(nèi)存的。下面,我們將深入研究一下內(nèi)存泄漏。

簡(jiǎn)單來說,內(nèi)存泄漏是指你的對(duì)象已經(jīng)使用結(jié)束,但是它卻不能被釋放掉。每個(gè)對(duì)象在完成它自己的生命周期過后,都需要從內(nèi)存中清理出來。但是如果一個(gè)對(duì)象被另一個(gè)對(duì)象直接或間接的持有,垃圾回收器不能檢查出它已經(jīng)使用結(jié)束。朋友們,這樣子就導(dǎo)致了內(nèi)存泄漏。

值得慶幸的是,我們并不需要太擔(dān)心所有的內(nèi)存泄漏,因?yàn)椴⒉皇撬械膬?nèi)存泄漏都是有害的。有一些內(nèi)存泄漏是無關(guān)痛癢(只泄漏幾 KB 的內(nèi)存),并且,在 Android Framwork 層也會(huì)有一些內(nèi)存泄漏,但是你并不需要去修復(fù),因?yàn)樗鼈儗?duì) App 的性能影響微乎其微,你可以忽略。

但是有一些會(huì)引起 App 崩潰, 或者嚴(yán)重卡頓。這些都是需要你時(shí)刻注意的。

4.為什么要解決內(nèi)存泄漏?

沒有人會(huì)想使用一個(gè)又慢又占內(nèi)存的應(yīng)用。如果使用一段時(shí)間就會(huì)崩潰,你的用戶也會(huì)“崩潰”掉,如果長(zhǎng)時(shí)間出現(xiàn)這樣子的問題,你的用戶會(huì)毫不猶豫的卸載掉你的應(yīng)用,并且再也不會(huì)使用它。

火速收藏!Android 開發(fā)者必會(huì)的內(nèi)存泄漏指南

如果你的應(yīng)用中存在內(nèi)存泄漏,垃圾回收器不能回收不使用的內(nèi)存,隨著用戶使用時(shí)間的增長(zhǎng),內(nèi)存的占用會(huì)越來越多。如此下去,當(dāng)系統(tǒng)不能在給它分配更多內(nèi)存的時(shí)候,就會(huì)導(dǎo)致 OutOfMemoryError, 然后應(yīng)用程序會(huì)崩潰掉。

垃圾回收有利有弊,垃圾回收是一龐大的系統(tǒng),在應(yīng)用中,盡可能少的讓垃圾回收器運(yùn)行,這樣對(duì)應(yīng)用體驗(yàn)會(huì)更好。

隨著你的應(yīng)用使用的堆內(nèi)存逐漸增加,Short GC 就會(huì)觸發(fā),來保證立即清理無用對(duì)象。現(xiàn)在這些快速清理內(nèi)存的 GC 運(yùn)行在不同的線程中,這些 GC 不會(huì)導(dǎo)致你的應(yīng)用變慢。

但是如果你的應(yīng)用中存在嚴(yán)重的內(nèi)存泄漏,Short GC 沒有辦法回收內(nèi)存,并且占用內(nèi)存持續(xù)增加,這將會(huì)導(dǎo)致 Larger GC 被觸發(fā)。它會(huì)將整個(gè)應(yīng)用程序掛起,阻塞大概 50~100ms,這會(huì)導(dǎo)致應(yīng)用程序變慢并且有可能不能使用。

修復(fù)內(nèi)存泄漏,減少對(duì) App 的影響,給用戶提供更好的體驗(yàn)。

5.如何發(fā)現(xiàn)內(nèi)存泄漏?

現(xiàn)在,你已經(jīng)認(rèn)識(shí)到,你需要修復(fù)隱藏在你 App 中的內(nèi)存泄漏。但是,我們?nèi)绾尾拍苷业剿鼈兡兀?/p>

Android Studio 為我們提供了一個(gè)非常強(qiáng)大的工具:Monitors。

通過它,你能看到網(wǎng)絡(luò)、CPU、GPU、內(nèi)存的使用情況。

火速收藏!Android 開發(fā)者必會(huì)的內(nèi)存泄漏指南

在調(diào)試運(yùn)行 App 的時(shí)候,要密切關(guān)注內(nèi)存監(jiān)視器。內(nèi)存泄漏的第一個(gè)現(xiàn)象就是,在使用的過程中,內(nèi)存一直增加,不能減少,即使你把 APP 退到后臺(tái)也不能釋放。內(nèi)存分配監(jiān)視器能夠清楚的看到不同對(duì)象所占用的內(nèi)存,可以清楚的知道哪個(gè)對(duì)象占用內(nèi)存較多,需要處理。

但是,它本身還不夠,它需要你指定時(shí)間,然后轉(zhuǎn)存出對(duì)應(yīng)的內(nèi)存堆。這是一個(gè)很無趣的工作。

幸運(yùn)的是,我們現(xiàn)在已經(jīng)有更好的方式來實(shí)現(xiàn)。LeakCanary, 一個(gè)和 App 一起運(yùn)行的庫,它會(huì)在內(nèi)存泄漏的時(shí)候,轉(zhuǎn)存出內(nèi)存信息,然后給我們發(fā)送一個(gè)通知并給我們一個(gè)有用的棧信息。

6.常見的內(nèi)存泄漏

從我的經(jīng)驗(yàn)來看,有很多相似且經(jīng)常出現(xiàn)內(nèi)存泄漏的問題,你在你每天的開發(fā)中,都有可能會(huì)遇到它們。一但你清楚了它們發(fā)生的時(shí)間、地點(diǎn)、原因 ,你就可以很輕松的修復(fù)它們。

  • 未取消的 Listener

很多時(shí)候,你在 Activity/Fragment 中注冊(cè)了一個(gè) Listener, 但是忘記取消注冊(cè)了。如果你的運(yùn)氣不好,它很可能會(huì)引起一個(gè)嚴(yán)重的內(nèi)存泄漏問題。一般來說,這些 Listener 的 注冊(cè)與取消注冊(cè)是同步出現(xiàn)的,在你使用的時(shí)候需要注冊(cè),在不使用的時(shí)候需要取消注冊(cè)。

舉個(gè)例子,當(dāng)我們的應(yīng)用程序需要獲取定位的時(shí)候,需要使用 LocationManager,你會(huì)從系統(tǒng)服務(wù)中拿到它,并且給其設(shè)置一個(gè)地理位置更新的回調(diào):

private void registerLocationUpdats {
mManager = (LocationManager) getSystemService(
Context.LOCATION_SERVICE);
mManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
TimeUnit.MINUTES.toMillis(1),
100,
this);
}

在代碼中,可以看出來,使用了 Activity 自己來實(shí)現(xiàn)了地理位置更新的回調(diào)。LocationManager 會(huì)持有這個(gè)回調(diào)的引用。當(dāng)你退出了這個(gè)頁面,Android 系統(tǒng)會(huì)調(diào)用 onDestory ,但是垃圾回收器并不能清理掉它,因?yàn)?LocationManager 持有它的強(qiáng)引用。

當(dāng)然,解決方案也很簡(jiǎn)單,就是在 onDestory 方法中,取消注冊(cè)就可以了。

@Override
public voidonDestroy {
super.onDestroy;
if (mManager != ) {
mManager.removeUpdates(this);
}
}
  • 內(nèi)部類

內(nèi)部類在 Java 和 Android 開發(fā)中經(jīng)常用到,非常簡(jiǎn)單,但是如果使用不當(dāng),也會(huì)造成嚴(yán)重的內(nèi)存泄漏。讓我們先來看一個(gè)簡(jiǎn)單的例子:

public class BadActivity extends Activity {
private TextView mMessageView;
@Override
protected voidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_bad_activity);
mMessageView = (TextView) findViewById(R.id.messageView);
new LongRunningTask.execute;
}
private class LongRunningTask extends AsyncTask<Void, Void, String> {
@Override
protected String doInBackground(Void... params) {
// 做一些耗時(shí)操作
return "Am finally done!";
}
@Override
protected voidonPostExecute(String result) {
mMessageView.setText(result);
}
}
}

這是一個(gè)很簡(jiǎn)單的 Activity 頁面,在頁面啟動(dòng)的時(shí)候,在后臺(tái)啟動(dòng)了一個(gè)耗時(shí)的任務(wù)(比如說,復(fù)雜的數(shù)據(jù)庫查詢或者是很慢的網(wǎng)絡(luò))。等到任務(wù)執(zhí)行結(jié)束,把拿到的結(jié)果顯示到頁面上。看起來,這樣做并沒有問題。事實(shí)上,非靜態(tài)的內(nèi)部類會(huì)隱式的持有外部類的引用(在這里,就是 Activity)。如果在耗時(shí)任務(wù)執(zhí)行完之前,你旋轉(zhuǎn)屏幕或者退出這個(gè)頁面,垃圾回收器就不能從內(nèi)存中清理掉 Activity 的實(shí)例。這個(gè)簡(jiǎn)單的問題會(huì)導(dǎo)致很嚴(yán)重的內(nèi)存泄漏問題。

當(dāng)然,解決方案也非常地簡(jiǎn)單,如下:

public class GoodActivity extends Activity {
private AsyncTask mLongRunningTask;
private TextView mMessageView;
@Override
protected voidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_good_activity);
mMessageView = (TextView) findViewById(R.id.messageView);
mLongRunningTask = new LongRunningTask(mMessageView).execute;
}
@Override
protected voidonDestroy {
super.onDestroy;
mLongRunningTask.cancel(true);
}
private static class LongRunningTask extends AsyncTask<Void, Void, String> {
private final WeakReference<TextView> messageViewReference;
publicLongRunningTask(TextView messageView) {
this.messageViewReference = new WeakReference<>(messageView);
}
@Override
protected String doInBackground(Void... params) {
String message = ;
if (!isCancelled) {
message = "I am finally done!";
}
return message;
}
@Override
protected voidonPostExecute(String result) {
TextView view = messageViewReference.get;
if (view != ) {
view.setText(result);
}
}
}
}

正如你看到的代碼,首先我將非靜態(tài)內(nèi)部類改成了靜態(tài)內(nèi)部類,這樣它就不會(huì)持有外部類的引用了。當(dāng)然,使用靜態(tài)的內(nèi)部類,非靜態(tài)的變量就不能訪問了。所以我們需要將 TextView 通過構(gòu)造方法把它傳過去。

在這里,我強(qiáng)烈推薦使用 WeakReference ,它能更好的避免引起內(nèi)存泄漏。你應(yīng)該去學(xué)習(xí) Java 中關(guān)于不同引用類型的知識(shí):

http://javarevisited.blogspot.in/2014/03/difference-between-weakreference-vs-softreference-phantom-strong-reference-java.html

  • 匿名內(nèi)部類

匿名內(nèi)部類也是在開發(fā)過程中經(jīng)常使用到的一個(gè)東西,它的定義和使用都非常的簡(jiǎn)潔。但以我的經(jīng)驗(yàn)來看,匿名內(nèi)部類造成了大量的內(nèi)存泄漏的問題。

匿名內(nèi)部類與非靜態(tài)內(nèi)部類相似,造成內(nèi)部類的原因也和上面說的一樣。你有可能在好多地方都使用了匿名內(nèi)部類,如果使用不當(dāng),會(huì)嚴(yán)重影響 App 的性能。

public class MoviesActivity extends Activity {
private TextView mNoOfMoviesThisWeek;
@Override
protected voidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_movies_activity);
mNoOfMoviesThisWeek = (TextView) findViewById(R.id.no_of_movies_text_view);
MoviesRepository repository = ((MoviesApp) getApplication).getRepository;
repository.getMoviesThisWeek
.enqueue(new Callback<List<Movie>> {

@Override
public voidonResponse(Call<List<Movie>> call,
Response<List<Movie>> response) {
int numberOfMovies = response.body.size;
mNoOfMoviesThisWeek.setText("No of movies this week: " + String.valueOf(numberOfMovies));
}
@Override
public voidonFailure(Call<List<Movie>> call, Throwable t) {
// Oops.
}
});
}
}

上面的例子中,我使用一個(gè)常用的網(wǎng)絡(luò)庫 Retrofit 發(fā)送了一個(gè)網(wǎng)絡(luò)請(qǐng)求,然后在 TextView 中顯示返回的結(jié)果。很明顯,那個(gè) Callback 對(duì)象持有 Activity 的引用。如果現(xiàn)在網(wǎng)絡(luò)很慢,在網(wǎng)絡(luò)響應(yīng)回來之前,頁面旋轉(zhuǎn)或者關(guān)閉,就會(huì)導(dǎo)致 Activity 泄漏。

我強(qiáng)烈建議,在需要的時(shí)候,盡量使用靜態(tài)的內(nèi)部類,而非匿名內(nèi)部類。當(dāng)然,我的意思不是不在使用匿名內(nèi)部類,如果你需要使用匿名內(nèi)部類,你需要注意引起內(nèi)存泄漏的問題,保證不會(huì)出現(xiàn)問題。

  • Bitmaps

在應(yīng)用中,你看到的所有圖片都是 Bitmap 對(duì)象,包含了所有的像素?cái)?shù)據(jù)。現(xiàn)在這些 Bitmap 數(shù)據(jù)非常的大,一個(gè)處理不好,就會(huì)引起 OOM, 造成 APP 崩潰。在 APP 中使用的圖片資源生成的 Bitmap 會(huì)由系統(tǒng)進(jìn)行管理,但是如果你需要自己處理 Bitmap ,要記住,使用完過后要調(diào)用 bitmap.recycle 來釋放資源。

在處理 Bitmap 時(shí),需要將一張大的圖縮放變小過后,在使用,多重用同一個(gè)圖片數(shù)據(jù)。google 官方有一個(gè)關(guān)于處理 Bitmap 內(nèi)存的文檔:

https://developer.android.com/training/displaying-bitmaps/manage-memory.html

  • Contexts

另一個(gè)是關(guān)于 Context 的濫用引起的內(nèi)存泄漏。Activity / Application / Service 都是繼承自 Context 并實(shí)現(xiàn)它們自己的功能,但是你也需要搞清楚它們之間的區(qū)別,什么是 activity 級(jí)別的 Context,什么是 application 級(jí)別的 Context,根據(jù)項(xiàng)目需求的場(chǎng)景去選擇使用哪一個(gè) Context 。錯(cuò)誤地使用 Activity Context,導(dǎo)致引用不能被釋放,就會(huì)引起內(nèi)存泄漏。

7.結(jié)語

現(xiàn)在,你知道了什么是垃圾回收器,什么是內(nèi)存泄漏,內(nèi)存泄漏給你帶來的影響。你也知道如何檢測(cè)和修復(fù)內(nèi)存泄漏。

從現(xiàn)在開始,構(gòu)建高質(zhì)量/高性能的應(yīng)用。處理內(nèi)存泄漏不僅能讓你的應(yīng)用有更好的用戶體驗(yàn),也能讓你成為更好的開發(fā)者。

原文:Everything You Need To Know About Memory Leaks In Android Apps

鏈接:https://blog.aritraroy.in/everything-you-need-to-know-about-memory-leaks-in-android-apps-655f191ca859

【END】

分享到:
標(biāo)簽:泄漏 內(nèi)存 Android
用戶無頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定