1. 程式人生 > >android裝置的記憶體空間(RAM)總空間和可用空間大小的獲取以及一些思考

android裝置的記憶體空間(RAM)總空間和可用空間大小的獲取以及一些思考

在專案中我們會遇到這樣的需求,那就是獲取android裝置可用記憶體(ram)空間的大小和總空間的大小.關於這個問題我們分為兩個部分探討.

一,通常情況下我們使用系統提供的api獲取可用記憶體空間和總記憶體空間的方法.

程式碼如下:

private void getMemoryInfo_1(){
  ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
  outInfo = new MemoryInfo();
  am.getMemoryInfo(outInfo );
  long availMem = outInfo.availMem;
  long totalMem = outInfo.totalMem;
  Log.i(TAG,"availMem = "+Formatter.formatFileSize(this, availMem));
  Log.i(TAG,"totalMem = "+Formatter.formatFileSize(this, totalMem));
 }


使用上面的方法獲取可用和總共記憶體空間的大小有一定的侷限,因為總記憶體空間的api是在api 16新增的,如果是低版本的是不能使用的,為了相容低版本,我們可以使用下面的方法獲取可用和總記憶體空間.

我們有時候需要檢視記憶體資訊,我們可以通過開啟命令列視窗 cmd------>adb shell --> cat proc/meminfo來檢視相信的記憶體資訊,這個檔案的第一行資訊存放的就是總記憶體資訊,那麼我們可以通過讀取這個檔案獲得總記憶體大小.程式碼如下:

private void getMemoryInfo_2(){
		ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
		outInfo = new MemoryInfo();
		am.getMemoryInfo(outInfo );
		long availMem = outInfo.availMem;//單位是位元組
		StringBuffer sb = new StringBuffer();
		try {
			BufferedReader burf = new BufferedReader(new InputStreamReader(new FileInputStream("/proc/meminfo")));
			String strInfo = burf.readLine();
			char[] chInfo = strInfo.toCharArray();
			int size = chInfo.length;
			for(int i = 0; i < size; i++){
				if(chInfo[i] <= '9' && chInfo[i] >= '0'){
					sb.append(chInfo[i]);
				}
			}
			burf.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		String totalMem = sb.toString();//單位是KB
		Log.i(TAG,"availMem = "+Formatter.formatFileSize(this, availMem));
		Log.i(TAG,"totalMem = "+totalMem );
	}


二, 通過上面程式碼得到的資訊和模擬器上點選"設定--->應用--->正在執行"最下面出現的可用記憶體資訊不一致,這是怎麼回事呢?

於是我們就需要檢視系統的程式碼,看看這個差別是怎麼產生的

1,首先根據這個介面的"可用","已用"和"RAM"三個欄位在android\packages\apps\Settings\res\values-zh-rCN\strings.xml檔案中找他們對應的資源id,在1154-1156行

得到結果如下:

<string name="service_background_processes" msgid="6844156253576174488">"可用:<xliff:g id="MEMORY">%1$s</xliff:g>"</string>
    <string name="service_foreground_processes" msgid="7583975676795574276">"已用:<xliff:g id="MEMORY">%1$s</xliff:g>"</string>
    <string name="memory" msgid="6609961111091483458">"RAM"</string>


2,根據得到的資源id,發現在android\packages\apps\Settings\src\com\android\settings\applications\RunningProcessView中的391和405行引用了該資源id

mBackgroundProcessText.setText(getResources().getString(
                        R.string.service_background_processes, sizeStr));
..................
 mForegroundProcessText.setText(getResources().getString(
                        R.string.service_foreground_processes, sizeStr));


3,下面我們就看看系統是怎麼計算可用的記憶體空間大小的

void refreshUi(boolean dataChanged) {
       ..............
        
        synchronized (mState.mLock) {
            if (mLastNumBackgroundProcesses != mState.mNumBackgroundProcesses
                    || mLastBackgroundProcessMemory != mState.mBackgroundProcessMemory
                    || mLastAvailMemory != availMem) {
               ........................
                String sizeStr = Formatter.formatShortFileSize(getContext(),
                        mLastAvailMemory + mLastBackgroundProcessMemory);
                mBackgroundProcessText.setText(getResources().getString(
                        R.string.service_background_processes, sizeStr));
            }
            if (mLastNumForegroundProcesses != mState.mNumForegroundProcesses
                    || mLastForegroundProcessMemory != mState.mForegroundProcessMemory
                    || mLastNumServiceProcesses != mState.mNumServiceProcesses
                    || mLastServiceProcessMemory != mState.mServiceProcessMemory) {
               ..........................................
                String sizeStr = Formatter.formatShortFileSize(getContext(),
                        mLastForegroundProcessMemory + mLastServiceProcessMemory);
                mForegroundProcessText.setText(getResources().getString(
                        R.string.service_foreground_processes, sizeStr));
            }
            
           ...............................
        }
    }


我們通過上面的程式碼可以發現已用空間的表示前臺程序和後臺服務佔用的空間,可用空間是可用的記憶體加上後臺程式佔用的空間.

那下面我們看看可用空間的mLastAvailMemory 和mLastBackgroundProcessMemory是怎麼計算得到的

A----mLastAvailMemory,在388行程式碼中可以看到mLastAvailMemory = availMem,那我們接下來看看availMem是怎麼計算的,是在377行long availMem = readAvailMem() -SECONDARY_SERVER_MEM;

  SECONDARY_SERVER_MEM =
            Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_MEM"))*PAGE_SIZE;

SECONDARY_SERVER_MEM是通過讀取JB\build\target\board\generic_x86\init.rc檔案獲得的,預設是16MB

我們再看看函式readAvailMem()

private long readAvailMem() {
        try {
            long memFree = 0;
            long memCached = 0;
            FileInputStream is = new FileInputStream("/proc/meminfo");
            int len = is.read(mBuffer);
            is.close();
            final int BUFLEN = mBuffer.length;
            for (int i=0; i<len && (memFree == 0 || memCached == 0); i++) {
                if (matchText(mBuffer, i, "MemFree")) {
                    i += 7;
                    memFree = extractMemValue(mBuffer, i);
                } else if (matchText(mBuffer, i, "Cached")) {
                    i += 6;
                    memCached = extractMemValue(mBuffer, i);
                }
                while (i < BUFLEN && mBuffer[i] != '\n') {
                    i++;
                }
            }
            return memFree + memCached;
        } catch (java.io.FileNotFoundException e) {
        } catch (java.io.IOException e) {
        }
        return 0;
    }


發現函式readAvailMem是讀取/proc/meminfo檔案中的MemFree和Cached兩個選項然後相加得到的

B--------mLastBackgroundProcessMemory   mLastBackgroundProcessMemory 是通過類RunningState的mBackgroundProcessMemory欄位獲得的,那下面我們看看RunningState是怎麼回事兒

我們在RunningState類的1280行發現了關鍵程式碼

for (int i=0; i<pids.length; i++) {
               ........
                        ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
                    backgroundProcessMemory += proc.mSize;
               ........
            }


通過程式碼發現後臺程序所佔的記憶體空間是統計了真正的後臺執行程序和空程序所佔用的空間.

到這裡我們就知道了系統的可用記憶體空間是怎麼計算的了,下面我們通過自己的程式碼實現一下

public class MainActivity extends Activity {

	private static final String TAG = "MainActivity";
	private ActivityManager am;
	private MemoryInfo outInfo;
	private int nativePssSum;
	private int otherPssSum;
	private static final long SECONDARY_SERVER_MEM = 4096 * 4;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
		getAvailMem();
	}
	private long getAvailMem() {
		long avail = getAvailMemFromInfo() + getBackgroundProcessMem() - SECONDARY_SERVER_MEM;
		Log.i(TAG,"avail = " + avail);
		return avail;
	}
	//從/proc/meminfo中得到MemFree和Cached所得到的記憶體空間
	private long getAvailMemFromInfo(){
		StringBuffer sb = new StringBuffer();
		try {
			BufferedReader burf = new BufferedReader(new InputStreamReader(new FileInputStream("/proc/meminfo")));
			String strInfo = burf.readLine();
			strInfo = burf.readLine();//讀取/proc/meminfo的MemInfo
			char[] chInfo = strInfo.toCharArray();
			int size = chInfo.length;
			for(int i = 0; i < size; i++){
				if(chInfo[i] <= '9' && chInfo[i] >= '0'){
					sb.append(chInfo[i]);
				}
			}
			long memFree = Long.parseLong(sb.toString());
			sb.delete(0, sb.length());
			strInfo = burf.readLine();
			strInfo = burf.readLine();//讀取/proc/meminfo的Cached
			chInfo = strInfo.toCharArray();
			size = chInfo.length;
			for(int i = 0; i < size; i++){
				if(chInfo[i] <= '9' && chInfo[i] >= '0'){
					sb.append(chInfo[i]);
				}
			}
			long cached = Long.parseLong(sb.toString());
			Log.i(TAG,"memFree = "+memFree);
			Log.i(TAG,"cached = "+cached);
			burf.close();
			return cached + memFree;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return 0;
	}
	//得到後臺程序和空程序所佔用的記憶體空間
	private int getBackgroundProcessMem(){
		List<RunningAppProcessInfo> runningProcessInfos = am.getRunningAppProcesses();
		
		for(RunningAppProcessInfo info : runningProcessInfos){
			if(info.importance >= RunningAppProcessInfo.IMPORTANCE_BACKGROUND){
				int pid = info.pid;
				android.os.Debug.MemoryInfo[] processMemoryInfo = am.getProcessMemoryInfo(new int[]{pid});
				nativePssSum += processMemoryInfo[0].nativePss;
				otherPssSum += processMemoryInfo[0].otherPss;
			}
		}
		Log.i(TAG,"nativePssSum = "+nativePssSum);
		Log.i(TAG,"otherPssSum = "+otherPssSum);
		return nativePssSum + otherPssSum;
	}
}


綜上所述:記憶體已用空間 = 前臺程序記憶體空間 + 服務程序空間   可用記憶體空間 = meminfo中的MemFree + memInfo中的Cached + 後臺和空程序的空間 - SECONDARY_SERVER_MEM(預設是16MB)

另:Android的“正在執行服務”中有關於“已用空間”和“可用空間”的統計,但它不是通常意義(傳統Linux)上的記憶體使用情況統計,而是基於Low Memory Killer和Android虛擬機器的Activity堆疊上的可用記憶體統計。因為在傳統程式中,程式退出後記憶體即釋放;但是在Android中,即使按back鍵返回後,為了使用快取提高效能,Acitivity例項還有可能儲存在堆疊上。基於這種設計理念,它的統計資料也不同於傳統意義上的統計資料