欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android 有效的解決內(nèi)存泄漏的問題實例詳解

 更新時間:2016年11月04日 10:45:49   作者:趙彥軍  
這篇文章主要介紹了Android 有效的解決內(nèi)存泄漏的問題的相關(guān)資料,這里舉例說明,如何實現(xiàn)解決內(nèi)存泄漏,需要的朋友可以參考下

Android 有效的解決內(nèi)存泄漏的問題

Android內(nèi)存泄漏,我想做Android 應(yīng)用的時候遇到的話很是頭疼,這里是我在網(wǎng)上找的不錯的資料,實例詳解這個問題的解決方案

前言:最近在研究Handler的知識,其中涉及到一個問題,如何避免Handler帶來的內(nèi)存溢出問題。在網(wǎng)上找了很多資料,有很多都是互相抄的,沒有實際的作用。

本文的內(nèi)存泄漏檢測工具是:LeakCanary  github地址:https://github.com/square/leakcanary

什么是內(nèi)存泄漏?

內(nèi)存泄漏是當(dāng)程序不再使用到的內(nèi)存時,釋放內(nèi)存失敗而產(chǎn)生了無用的內(nèi)存消耗。內(nèi)存泄漏并不是指物理上的內(nèi)存消失,這里的內(nèi)存泄漏是值由程序分配的內(nèi)存但是由于程序邏輯錯誤而導(dǎo)致程序失去了對該內(nèi)存的控制,使得內(nèi)存浪費。

 怎樣會導(dǎo)致內(nèi)存泄漏?

資源對象沒關(guān)閉造成的內(nèi)存泄漏,如查詢數(shù)據(jù)庫后沒有關(guān)閉游標(biāo)cursor

構(gòu)造Adapter時,沒有使用 convertView 重用

Bitmap對象不在使用時調(diào)用recycle()釋放內(nèi)存

對象被生命周期長的對象引用,如activity被靜態(tài)集合引用導(dǎo)致activity不能釋放

 內(nèi)存泄漏有什么危害?

內(nèi)存泄漏對于app沒有直接的危害,即使app有發(fā)生內(nèi)存泄漏的情況,也不一定會引起app崩潰,但是會增加app內(nèi)存的占用。內(nèi)存得不到釋放,慢慢的會造成app內(nèi)存溢出。所以我們解決內(nèi)存泄漏的目的就是防止app發(fā)生內(nèi)存溢出。

1、新建線程引起的Activity內(nèi)存泄漏

例子:

package rxnet.zyj.com.myapplication;
 
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
 
public class Activity6 extends AppCompatActivity {
 
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_6);
 
    findViewById( R.id.finish).setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        finish();
      }
    });
 
    new Thread(new Runnable() {
      @Override
      public void run() {
        try {<br>          //模擬耗時操作
          Thread.sleep( 15000 );
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    }).start();
 
  }
}

  運(yùn)行上面的代碼后,點擊finish按鈕,過一會兒發(fā)生了內(nèi)存泄漏的問題。

 為什么Activity6會發(fā)生內(nèi)存泄漏?

進(jìn)入Activity6 界面,然后點擊finish按鈕,Activity6銷毀,但是Activity6里面的線程還在運(yùn)行,匿名內(nèi)部類Runnable對象引用了Activity6的實例,導(dǎo)致Activity6所占用的內(nèi)存不能被GC及時回收。

 如何改進(jìn)?

Runnable改為靜態(tài)非匿名內(nèi)部類即可。

package rxnet.zyj.com.myapplication;
 
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
 
public class Activity6 extends AppCompatActivity {
 
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_6);
 
    findViewById( R.id.finish).setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        finish();
      }
    });
 
    new Thread( new MyRunnable()).start();
 
  }
 
  private static class MyRunnable implements Runnable {
 
    @Override
    public void run() {
      try {
        Thread.sleep( 15000 );
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
   
}

 2、Activity添加監(jiān)聽器造成Activity內(nèi)存泄漏

package rxnet.zyj.com.myapplication;
 
import android.app.Activity;
import android.os.Bundle;
 
public class LeakActivity extends Activity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    NastyManager.getInstance().addListener(this);
  }
}

  這個是在開發(fā)中經(jīng)常會犯的錯誤,NastyManager.getInstance() 是一個單例,當(dāng)我們通過 addListener(this) 將 Activity 作為 Listener 和 NastyManager 綁定起來的時候,不好的事情就發(fā)生了。

如何改進(jìn)?

想要修復(fù)這樣的 Bug,其實相當(dāng)簡單,就是在你的 Acitivity 被銷毀的時候,將他和 NastyManager 取消掉綁定就好了。

package rxnet.zyj.com.myapplication;
 
import android.app.Activity;
import android.os.Bundle;
 
public class LeakActivity extends Activity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    NastyManager.getInstance().addListener(this);
  }
 
  @Override
  protected void onDestroy() {
    super.onDestroy();
    NastyManager.getInstance().removeListener(this);
  }
}

  3、Handler 匿名內(nèi)部類造成內(nèi)存溢出?

先看著一段代碼

package rxnet.zyj.com.myapplication;
 
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
 
public class HandlerActivity extends AppCompatActivity {
 
  private final static int MESSAGECODE = 1 ;
 
  private final Handler handler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
      super.handleMessage(msg);
      Log.d("mmmmmmmm" , "handler " + msg.what ) ;
    }
  };
 
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_handler);
 
    findViewById( R.id.finish ).setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        finish();
      }
    });
 
    new Thread(new Runnable() {
      @Override
      public void run() {
        handler.sendEmptyMessage( MESSAGECODE ) ;
        try {
          Thread.sleep( 8000 );
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        handler.sendEmptyMessage( MESSAGECODE ) ;
      }
    }).start() ;
 
  }
}

  這段代碼運(yùn)行起來后,立即點擊 finish 按鈕,通過檢測,發(fā)現(xiàn) HandlerActivity 出現(xiàn)了內(nèi)存泄漏。當(dāng)Activity finish后,延時消息會繼續(xù)存在主線程消息隊列中8秒鐘,然后處理消息。而該消息引用了Activity的Handler對象,然后這個Handler又引用了這個Activity。這些引用對象會保持到該消息被處理完,這樣就導(dǎo)致該Activity對象無法被回收,從而導(dǎo)致了上面說的 Activity泄露。Handler 是個很常用也很有用的類,異步,線程安全等等。如果有下面這樣的代碼,會發(fā)生什么呢? handler.postDeslayed ,假設(shè) delay 時間是幾個小時… 這意味著什么?意味著只要 handler 的消息還沒有被處理結(jié)束,它就一直存活著,包含它的 Activity 就跟著活著。我們來想辦法修復(fù)它,修復(fù)的方案是 WeakReference ,也就是所謂的弱引用。垃圾回收器在回收的時候,是會忽視掉弱引用的,所以包含它的 Activity 會被正常清理掉。

如何避免

使用靜態(tài)內(nèi)部類

使用弱引用

修改后代碼是這樣的。

package rxnet.zyj.com.myapplication;
 
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
 
import java.lang.ref.WeakReference;
 
public class HandlerActivity extends AppCompatActivity {
 
  private final static int MESSAGECODE = 1 ;
  private static Handler handler ;
 
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_handler);
 
    findViewById( R.id.finish ).setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        finish();
      }
    });
 
    handler = new MyHandler( this ) ;
 
    new Thread(new Runnable() {
      @Override
      public void run() {
        handler.sendEmptyMessage( MESSAGECODE ) ;
        try {
          Thread.sleep( 8000 );
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        handler.sendEmptyMessage( MESSAGECODE ) ;
      }
    }).start() ;
 
  }
 
  private static class MyHandler extends Handler {
    WeakReference<HandlerActivity> weakReference ;
 
    public MyHandler(HandlerActivity activity ){
      weakReference = new WeakReference<HandlerActivity>( activity) ;
    }
 
    @Override
    public void handleMessage(Message msg) {
      super.handleMessage(msg);
      if ( weakReference.get() != null ){
        // update android ui
        Log.d("mmmmmmmm" , "handler " + msg.what ) ;
      }
    }
  }
}

  這個Handler已經(jīng)使用了靜態(tài)內(nèi)部類,并且使用了弱引用。但是這個并沒有完全解決 HandlerActivity 內(nèi)存泄漏的問題,罪魁禍?zhǔn)资蔷€程創(chuàng)建的方式出了問題,就像本文的第一個例子一樣。改進(jìn)的方式,是把Runnable類寫成靜態(tài)內(nèi)部類。

最終完整的代碼如下:

package rxnet.zyj.com.myapplication;
 
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
 
import java.lang.ref.WeakReference;
 
public class HandlerActivity extends AppCompatActivity {
 
  private final static int MESSAGECODE = 1 ;
  private static Handler handler ;
 
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_handler);
 
    findViewById( R.id.finish ).setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        finish();
      }
    });
 
    //創(chuàng)建Handler
    handler = new MyHandler( this ) ;
 
    //創(chuàng)建線程并且啟動線程
    new Thread( new MyRunnable() ).start();
  }
 
  private static class MyHandler extends Handler {
    WeakReference<HandlerActivity> weakReference ;
 
    public MyHandler(HandlerActivity activity ){
      weakReference = new WeakReference<HandlerActivity>( activity) ;
    }
 
    @Override
    public void handleMessage(Message msg) {
      super.handleMessage(msg);
      if ( weakReference.get() != null ){
        // update android ui
        Log.d("mmmmmmmm" , "handler " + msg.what ) ;
      }
    }
  }
 
  private static class MyRunnable implements Runnable {
 
    @Override
    public void run() {
      handler.sendEmptyMessage( MESSAGECODE ) ;
      try {
        Thread.sleep( 8000 );
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      handler.sendEmptyMessage( MESSAGECODE ) ;
    }
  }
}

  等等,還沒完呢?

上面這個代碼已經(jīng)有效的解決了Handler,Runnable 引用Activity實例從而導(dǎo)致內(nèi)存泄漏的問題,但是這不夠。因為內(nèi)存泄漏的核心原因就是這個某個對象應(yīng)該被系統(tǒng)回收內(nèi)存的時候,卻被其他對象引用,造成該內(nèi)存無法回收。所以我們在寫代碼的時候,要始終繃著這個弦。再回到上面這個問題,當(dāng)當(dāng)前Activity調(diào)用finish銷毀的時候,在這個Activity里面所有線程是不是應(yīng)該在OnDestory()方法里,取消線程。當(dāng)然是否取消異步任務(wù),要看項目具體的需求,比如在Activity銷毀的時候,啟動一個線程,異步寫log日志到本地磁盤,針對這個需求卻需要在OnDestory()方法里開啟線程。所以根據(jù)當(dāng)前環(huán)境做出選擇才是正解。

所以我們還可以修改代碼為:在onDestroy() 里面移除所有的callback 和 Message 。

package rxnet.zyj.com.myapplication;
 
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
 
import java.lang.ref.WeakReference;
 
public class HandlerActivity extends AppCompatActivity {
 
  private final static int MESSAGECODE = 1 ;
  private static Handler handler ;
 
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_handler);
 
    findViewById( R.id.finish ).setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        finish();
      }
    });
 
    //創(chuàng)建Handler
    handler = new MyHandler( this ) ;
 
    //創(chuàng)建線程并且啟動線程
    new Thread( new MyRunnable() ).start();
  }
 
  private static class MyHandler extends Handler {
    WeakReference<HandlerActivity> weakReference ;
 
    public MyHandler(HandlerActivity activity ){
      weakReference = new WeakReference<HandlerActivity>( activity) ;
    }
 
    @Override
    public void handleMessage(Message msg) {
      super.handleMessage(msg);
      if ( weakReference.get() != null ){
        // update android ui
        Log.d("mmmmmmmm" , "handler " + msg.what ) ;
      }
    }
  }
 
  private static class MyRunnable implements Runnable {
 
    @Override
    public void run() {
      handler.sendEmptyMessage( MESSAGECODE ) ;
      try {
        Thread.sleep( 8000 );
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      handler.sendEmptyMessage( MESSAGECODE ) ;
    }
  }
 
  @Override
  protected void onDestroy() {
    super.onDestroy();
 
    //如果參數(shù)為null的話,會將所有的Callbacks和Messages全部清除掉。
    handler.removeCallbacksAndMessages( null );
  }
}

 

 4、AsyncTask造成內(nèi)存泄漏

package rxnet.zyj.com.myapplication;
 
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
 
public class Activity2 extends AppCompatActivity {
 
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_2);
 
    findViewById( R.id.finish2).setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        finish();
      }
    });
 
 
    new AsyncTask<String,Integer,String>(){
 
      @Override
      protected String doInBackground(String... params) {
        try {
          Thread.sleep( 6000 );
        } catch (InterruptedException e) {
        }
        return "ssss";
      }
 
      @Override
      protected void onPostExecute(String s) {
        super.onPostExecute(s);
        Log.d( "mmmmmm activity2 " , "" + s ) ;
      }
 
    }.executeOnExecutor( AsyncTask.THREAD_POOL_EXECUTOR , "" ) ;
     
  }
}

  為什么?

上面代碼在activity中創(chuàng)建了一個匿名類AsyncTask,匿名類和非靜態(tài)內(nèi)部類相同,會持有外部類對象,這里也就是activity,因此如果你在Activity里聲明且實例化一個匿名的AsyncTask對象,則可能會發(fā)生內(nèi)存泄漏,如果這個線程在Activity銷毀后還一直在后臺執(zhí)行,那這個線程會繼續(xù)持有這個Activity的引用從而不會被GC回收,直到線程執(zhí)行完成。

   怎么解決?

 自定義靜態(tài)AsyncTask類A

syncTask的周期和Activity周期保持一致。也就是在Activity生命周期結(jié)束時要將AsyncTask cancel掉。

package rxnet.zyj.com.myapplication;
 
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
 
public class AsyncTaskActivity extends AppCompatActivity {
 
  private static MyTask myTask ;
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_asynctask);
 
    findViewById( R.id.finish).setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        finish();
      }
    });
 
    myTask = new MyTask() ;
    myTask.executeOnExecutor( AsyncTask.THREAD_POOL_EXECUTOR , "") ;
 
  }
 
  private static class MyTask extends AsyncTask{
 
    @Override
    protected Object doInBackground(Object[] params) {
      try {
        //模擬耗時操作
        Thread.sleep( 15000 );
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      return "";
    }
  }
 
  @Override
  protected void onDestroy() {
    super.onDestroy();
 
    //取消異步任務(wù)
    if ( myTask != null ){
      myTask.cancel(true ) ;
    }
  }
}

5、Timer Tasks 造成內(nèi)存泄漏

package rxnet.zyj.com.myapplication;
 
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
 
import java.util.Timer;
import java.util.TimerTask;
 
public class TimerActivity extends AppCompatActivity {
 
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_2);
 
    findViewById( R.id.finish2).setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        finish();
      }
    });
 
    //開始定時任務(wù)
    timer();
  }
 
  void timer(){
    new Timer().schedule(new TimerTask() {
      @Override
      public void run() {
        while(true);
      }
    },1000 ); // 1秒后啟動一個任務(wù)
  }
}

  為什么? 

這里內(nèi)存泄漏在于Timer和TimerTask沒有進(jìn)行Cancel,從而導(dǎo)致Timer和TimerTask一直引用外部類Activity。

  怎么解決? 

在適當(dāng)?shù)臅r機(jī)進(jìn)行Cancel。

TimerTask用靜態(tài)內(nèi)部類

   注意:在網(wǎng)上看到一些資料說,解決TimerTask內(nèi)存泄漏可以使用在適當(dāng)?shù)臅r機(jī)進(jìn)行Cancel。經(jīng)過測試,證明單單使用在適當(dāng)?shù)臅r機(jī)進(jìn)行Cancel , 還是有內(nèi)存泄漏的問題。所以一定要用靜態(tài)內(nèi)部類配合使用。

package rxnet.zyj.com.myapplication;
 
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
 
import java.util.Timer;
import java.util.TimerTask;
 
public class TimerActivity extends AppCompatActivity {
 
  private TimerTask timerTask ;
 
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_2);
 
    findViewById( R.id.finish2).setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        finish();
      }
    });
 
    //開始定時任務(wù)
    timer();
  }
 
  void timer(){
    timerTask = new MyTimerTask() ;
    new Timer().schedule( timerTask ,1000 ); // 1秒后啟動一個任務(wù)
  }
 
  private static class MyTimerTask extends TimerTask{
 
    @Override
    public void run() {
      while(true){
        Log.d( "ttttttttt" , "timerTask" ) ;
      }
    }
  }
 
  @Override
  protected void onDestroy() {
    super.onDestroy();
 
    //取消定時任務(wù)
    if ( timerTask != null ){
      timerTask.cancel() ;
    }
  }
}

感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!

相關(guān)文章

  • Android添加自定義下拉刷新布局阻尼滑動懸停彈動畫效果

    Android添加自定義下拉刷新布局阻尼滑動懸停彈動畫效果

    這篇文章主要為大家介紹了Android添加自定義下拉刷新布局阻尼滑動懸停彈動畫效果詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • Android開發(fā)中Listview動態(tài)加載數(shù)據(jù)的方法示例

    Android開發(fā)中Listview動態(tài)加載數(shù)據(jù)的方法示例

    這篇文章主要介紹了Android開發(fā)中Listview動態(tài)加載數(shù)據(jù)的方法,結(jié)合實例形式較為詳細(xì)的分析了Android操作ListView界面布局與數(shù)據(jù)動態(tài)更新相關(guān)操作技巧,需要的朋友可以參考下
    2017-10-10
  • Android用動畫顯示或隱藏視圖

    Android用動畫顯示或隱藏視圖

    大家好,本篇文章主要講的是Android用動畫顯示或隱藏視圖,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下
    2022-01-01
  • Android Doze模式啟用和恢復(fù)詳解

    Android Doze模式啟用和恢復(fù)詳解

    這篇文章主要介紹了Android Doze模式啟用和恢復(fù)功能,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2017-03-03
  • Android實現(xiàn)歡迎滑動頁面

    Android實現(xiàn)歡迎滑動頁面

    這篇文章主要為大家詳細(xì)介紹了Android實現(xiàn)歡迎滑動頁面,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • 總結(jié)Android App內(nèi)存優(yōu)化之圖片優(yōu)化

    總結(jié)Android App內(nèi)存優(yōu)化之圖片優(yōu)化

    網(wǎng)上有很多大拿分享的關(guān)于Android性能優(yōu)化的文章,主要是通過各種工具分析,使用合理的技巧優(yōu)化APP的體驗,提升APP的流暢度,但關(guān)于內(nèi)存優(yōu)化的文章很少有看到。下面是我在實踐過程中使用的一些方法,很多都是不太成熟的項目,只是將其作為一種處理方式分享給大家。
    2016-08-08
  • Android手機(jī)衛(wèi)士之確認(rèn)密碼對話框

    Android手機(jī)衛(wèi)士之確認(rèn)密碼對話框

    這篇文章主要為大家詳細(xì)介紹了Android手機(jī)衛(wèi)士之確認(rèn)密碼對話框,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-10-10
  • Android Timer使用的實例代碼

    Android Timer使用的實例代碼

    這篇文章介紹了Android Timer使用的實例代碼,有需要的朋友可以參考一下
    2013-10-10
  • Android側(cè)滑效果簡單實現(xiàn)代碼

    Android側(cè)滑效果簡單實現(xiàn)代碼

    這篇文章主要為大家詳細(xì)介紹了Android側(cè)滑效果簡單實現(xiàn)代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-11-11
  • Android音樂播放器簡單實現(xiàn)案例

    Android音樂播放器簡單實現(xiàn)案例

    我們平時長時間打代碼的時候肯定會感到疲憊和乏味,這個時候一邊播放自己喜歡的音樂,一邊繼續(xù)打代碼,心情自然也愉快很多。音樂帶給人的聽覺享受是無可比擬的,動聽的音樂可以愉悅?cè)说纳硇?,讓人更加積極地去熱愛生活,這篇文章主要介紹了Android音樂播放器簡單實現(xiàn)案例
    2022-12-12

最新評論