android设备的内存空间(RAM)总空间和可用空间大小的获取以及一些思考

时间:2021-12-30 19:56:26

在项目中我们会遇到这样的需求,那就是获取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实例还有可能保存在堆栈上。基于这种设计理念,它的统计数据也不同于传统意义上的统计数据