安卓內(nèi)存優(yōu)化是一個很重要的話題鸳君,有很多方面可以考慮,比如避免內(nèi)存泄漏泻红、減少內(nèi)存抖動夭禽、優(yōu)化圖片加載、使用緩存和對象池等谊路。下面我舉一些代碼案例讹躯,分別展示不合適的寫法和高性能的寫法。
歡迎評論區(qū)留言指正和補充缠劝。
11. 避免使用 AsyncTask
來執(zhí)行異步任務(wù)潮梯。
AsyncTask
的內(nèi)部實現(xiàn)是使用一個線程池和一個消息隊列來管理任務(wù),這會占用內(nèi)存空間惨恭,并可能導(dǎo)致內(nèi)存泄漏和并發(fā)問題秉馏。如果需要執(zhí)行異步任務(wù),可以使用 RxJava
或者 Coroutine
等庫來代替脱羡。例如:
// 不合適的寫法
private class MyTask extends AsyncTask<Void, Void, String> {
private WeakReference<Context> contextRef;
public MyTask(Context context) {
contextRef = new WeakReference<>(context);
}
@Override
protected String doInBackground(Void... params) {
// do some background work
return "result";
}
@Override
protected void onPostExecute(String result) {
Context context = contextRef.get();
if (context != null) {
// do something with result and context
}
}
}
// 高性能的寫法
private fun doAsyncTask(context: Context) {
CoroutineScope(Dispatchers.IO).launch {
// do some background work
val result = "result"
withContext(Dispatchers.Main) {
// do something with result and context
}
}
}
這樣做可以避免不必要的內(nèi)存分配和回收萝究,避免內(nèi)存泄漏和并發(fā)問題,并提高異步任務(wù)的管理和調(diào)度锉罐。
12. 避免使用 BitmapFactory
來加載圖片帆竹。
BitmapFactory
的內(nèi)部實現(xiàn)是使用 nativeDecodeStream
方法來解碼圖片,這會消耗較多的內(nèi)存空間氓鄙,并可能導(dǎo)致OOM馆揉。如果需要加載圖片,可以使用 Glide
或者 Picasso
等庫來代替抖拦。例如:
// 不合適的寫法
ImageView imageView = findViewById(R.id.image_view);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image); // 這會創(chuàng)建一個原始大小的位圖對象升酣,占用內(nèi)存空間,并可能導(dǎo)致OOM
imageView.setImageBitmap(bitmap);
// 高性能的寫法
ImageView imageView = findViewById(R.id.image_view);
Glide.with(this).load(R.drawable.image).into(imageView); // 這會根據(jù)視圖的大小和屏幕密度來加載合適大小的位圖對象态罪,節(jié)省內(nèi)存空間噩茄,并避免OOM
這樣做可以避免不必要的位圖對象的創(chuàng)建,節(jié)省內(nèi)存空間复颈,并提高圖片加載的效率和質(zhì)量绩聘。
13. 避免使用 Serializable
接口來實現(xiàn)序列化沥割。
Serializable
接口的內(nèi)部實現(xiàn)是使用反射機制來序列化和反序列化對象,這會消耗較多的CPU和內(nèi)存資源凿菩,并可能導(dǎo)致性能下降机杜。如果需要實現(xiàn)序列化,可以使用 Parcelable
接口或者 ProtoBuf
等庫來代替衅谷。例如:
// 不合適的寫法
public class User implements Serializable {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
// getter and setter methods
}
// 高性能的寫法
public class User implements Parcelable {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
// getter and setter methods
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(age);
}
public static final Parcelable.Creator<User> CREATOR = new Parcelable.Creator<User>() {
@Override
public User createFromParcel(Parcel source) {
return new User(source.readString(), source.readInt());
}
@Override
public User[] newArray(int size) {
return new User[size];
}
};
}
這樣做可以避免不必要的反射操作椒拗,節(jié)省CPU和內(nèi)存資源,并提高序列化和反序列化的效率获黔。
14. 避免使用 LinkedList
來存儲數(shù)據(jù)蚀苛。
LinkedList
的內(nèi)部實現(xiàn)是使用一個雙向鏈表來存儲數(shù)據(jù),這會占用較多的內(nèi)存空間玷氏,并且可能導(dǎo)致內(nèi)存碎片堵未。如果需要存儲數(shù)據(jù),可以使用 ArrayList
或者 ArrayDeque
來代替盏触。例如:
// 不合適的寫法
LinkedList<String> list = new LinkedList<>();
list.add("a");
list.add("b");
list.add("c");
// 高性能的寫法
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
這樣做可以節(jié)省內(nèi)存空間渗蟹,因為 ArrayList
和 ArrayDeque
的內(nèi)部實現(xiàn)是使用一個數(shù)組來存儲數(shù)據(jù),沒有額外的開銷耻陕。另外拙徽,它們還可以提供更快的隨機訪問和迭代的性能。
15. 避免使用 StringTokenizer
來分割字符串诗宣。
StringTokenizer
的內(nèi)部實現(xiàn)是使用一個字符數(shù)組來存儲字符串膘怕,并且每次調(diào)用 nextToken()
方法都會創(chuàng)建一個新的字符串對象,這會消耗較多的CPU和內(nèi)存資源召庞,并可能導(dǎo)致GC岛心。如果需要分割字符串,可以使用 split()
方法或者 Scanner
類來代替篮灼。例如:
// 不合適的寫法
StringTokenizer st = new StringTokenizer("Hello World!");
while (st.hasMoreTokens()) {
String token = st.nextToken(); // 每次調(diào)用都會創(chuàng)建一個新的字符串對象
// do something with token
}
// 高性能的寫法
String[] tokens = "Hello World!".split(" "); // 這只會創(chuàng)建一個字符串?dāng)?shù)組對象
for (String token : tokens) {
// do something with token
}
這樣做可以避免不必要的字符串對象的創(chuàng)建忘古,節(jié)省CPU和內(nèi)存資源,并提高字符串分割的效率诅诱。
16. 避免使用 SimpleDateFormat
來格式化日期和時間髓堪。
SimpleDateFormat
的內(nèi)部實現(xiàn)是使用一個 Calendar
對象來存儲日期和時間,并且每次調(diào)用 format()
方法都會創(chuàng)建一個新的 Date
對象娘荡,這會消耗較多的CPU和內(nèi)存資源干旁,并可能導(dǎo)致GC。如果需要格式化日期和時間炮沐,可以使用 DateTimeFormatter
或者 FastDateFormat
等庫來代替争群。例如:
// 不合適的寫法
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date();
String formattedDate = sdf.format(date); // 每次調(diào)用都會創(chuàng)建一個新的日期對象
// 高性能的寫法
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime date = LocalDateTime.now();
String formattedDate = dtf.format(date); // 這不會創(chuàng)建任何新的對象
這樣做可以避免不必要的日期對象的創(chuàng)建,節(jié)省CPU和內(nèi)存資源大年,并提高日期和時間格式化的效率换薄。
17. 避免使用 SparseIntArray
來存儲稀疏矩陣玉雾。
SparseIntArray
的內(nèi)部實現(xiàn)是使用兩個數(shù)組來存儲鍵和值,這會占用較多的內(nèi)存空間轻要,并且可能導(dǎo)致數(shù)組擴容和復(fù)制复旬。如果需要存儲稀疏矩陣,可以使用 SparseMatrix
或者 EJML
等庫來代替冲泥。例如:
// 不合適的寫法
SparseIntArray matrix = new SparseIntArray();
matrix.put(0, 1);
matrix.put(1, 2);
matrix.put(2, 3);
// 高性能的寫法
SparseMatrix matrix = new SparseMatrix(3, 3);
matrix.set(0, 0, 1);
matrix.set(1, 1, 2);
matrix.set(2, 2, 3);
這樣做可以節(jié)省內(nèi)存空間赢底,因為 SparseMatrix
和 EJML
的內(nèi)部實現(xiàn)是使用一個鏈表或者一個哈希表來存儲非零元素柏蘑,沒有額外的開銷粹庞。另外,它們還可以提供更快的矩陣運算和轉(zhuǎn)置的性能庞溜。
18. 避免使用 JSONObject
來解析JSON字符串革半。
JSONObject
的內(nèi)部實現(xiàn)是使用一個 HashMap
來存儲鍵值對,這會占用較多的內(nèi)存空間流码,并且可能導(dǎo)致哈希沖突和擴容。如果需要解析JSON字符串六敬,可以使用 Gson
或者 Moshi
等庫來代替。例如:
// 不合適的寫法
String json = "{\"name\":\"Alice\",\"age\":18}";
JSONObject jsonObject = new JSONObject(json); // 這會創(chuàng)建一個哈希表對象驾荣,占用內(nèi)存空間外构,并可能導(dǎo)致哈希沖突和擴容
String name = jsonObject.getString("name");
int age = jsonObject.getInt("age");
// 高性能的寫法
String json = "{\"name\":\"Alice\",\"age\":18}";
Gson gson = new Gson();
User user = gson.fromJson(json, User.class); // 這會直接創(chuàng)建一個用戶對象,節(jié)省內(nèi)存空間播掷,并提高JSON解析的效率
String name = user.getName();
int age = user.getAge();
這樣做可以避免不必要的哈希表對象的創(chuàng)建,節(jié)省內(nèi)存空間垒酬,并提高JSON解析的效率和質(zhì)量件炉。
19. 避免使用 Random
類來生成隨機數(shù)。
Random
類的內(nèi)部實現(xiàn)是使用一個線性同余發(fā)生器來生成隨機數(shù)妻率,這會導(dǎo)致隨機數(shù)的質(zhì)量不高,并且可能導(dǎo)致并發(fā)問題宫静。如果需要生成隨機數(shù)券时,可以使用 ThreadLocalRandom
或者 SecureRandom
等類來代替伏伯。例如:
// 不合適的寫法
Random random = new Random();
int n = random.nextInt(10); // 這會生成一個不太隨機的整數(shù)说搅,并且可能導(dǎo)致并發(fā)問題
// 高性能的寫法
int n = ThreadLocalRandom.current().nextInt(10); // 這會生成一個更隨機的整數(shù),并且避免并發(fā)問題
這樣做可以提高隨機數(shù)的質(zhì)量和安全性弄唧,并避免并發(fā)問題。
20. 避免使用 Log
類來打印日志侯养。
Log
類的內(nèi)部實現(xiàn)是使用一個 PrintStream
對象來輸出日志到控制臺或者文件澄干,這會消耗較多的CPU和內(nèi)存資源,并且可能導(dǎo)致IO阻塞和性能下降麸俘。如果需要打印日志,可以使用 Timber
或者 Logger
等庫來代替逞泄。例如:
// 不合適的寫法
Log.d("TAG", "Hello World!"); // 這會輸出一條日志到控制臺或者文件静檬,消耗CPU和內(nèi)存資源,并且可能導(dǎo)致IO阻塞和性能下降
// 高性能的寫法
Timber.d("Hello World!"); // 這會輸出一條日志到控制臺或者文件侮腹,節(jié)省CPU和內(nèi)存資源稻励,并提高日志輸出的效率和質(zhì)量
這樣做可以避免不必要的IO操作父阻,節(jié)省CPU和內(nèi)存資源望抽,并提高日志輸出的效率和質(zhì)量。