Repository: cundong/MemoryMonitor
Branch: master
Commit: d180d00a91dc
Files: 64
Total size: 85.8 KB
Directory structure:
gitextract_vdzligds/
├── .gitignore
├── README.md
├── app/
│ ├── build.gradle
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ └── com/
│ │ └── cundong/
│ │ └── memory/
│ │ ├── App.java
│ │ ├── Constants.java
│ │ ├── MainActivity.java
│ │ ├── demo/
│ │ │ ├── right/
│ │ │ │ ├── AsyncTaskOutOfMemoryActivity.java
│ │ │ │ ├── HandlerOutOfMemoryActivity.java
│ │ │ │ ├── OneStaticOutOfMemoryActivity.java
│ │ │ │ ├── OneThreadOutOfMemoryActivity.java
│ │ │ │ ├── TwoStaticOutOfMemoryActivity.java
│ │ │ │ └── TwoThreadOutOfMemoryActivity.java
│ │ │ └── wrong/
│ │ │ ├── AsyncTaskOutOfMemoryActivity.java
│ │ │ ├── HandlerOutOfMemoryActivity.java
│ │ │ ├── MemoryChurnActivity.java
│ │ │ ├── StaticOutOfMemoryActivity.java
│ │ │ └── ThreadOutOfMemoryActivity.java
│ │ ├── service/
│ │ │ ├── CoreService.java
│ │ │ └── EmptyService.java
│ │ └── util/
│ │ ├── BitmapUtils.java
│ │ ├── Logger.java
│ │ ├── MemoryUtil.java
│ │ └── ViewUtils.java
│ └── res/
│ ├── anim/
│ │ ├── slide_down.xml
│ │ └── slide_up.xml
│ ├── layout/
│ │ ├── activity_demo.xml
│ │ ├── activity_main.xml
│ │ ├── activity_outofmemory_list.xml
│ │ ├── activity_test.xml
│ │ └── float_view.xml
│ ├── menu/
│ │ └── main.xml
│ ├── values/
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ ├── values-v11/
│ │ └── styles.xml
│ ├── values-v14/
│ │ └── styles.xml
│ └── values-w820dp/
│ └── dimens.xml
├── build.gradle
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── magnet/
│ ├── .gitignore
│ ├── build.gradle
│ ├── gradle.properties
│ ├── proguard-rules.pro
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── com/
│ │ └── premnirmal/
│ │ └── Magnet/
│ │ └── ApplicationTest.java
│ └── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ └── com/
│ │ └── premnirmal/
│ │ └── Magnet/
│ │ ├── FlingListener.java
│ │ ├── IconCallback.java
│ │ ├── Magnet.java
│ │ ├── RemoveView.java
│ │ └── SimpleAnimator.java
│ └── res/
│ ├── anim/
│ │ ├── slide_down.xml
│ │ └── slide_up.xml
│ ├── drawable/
│ │ └── bottom_shadow.xml
│ ├── layout/
│ │ └── x_button_holder.xml
│ ├── menu/
│ │ └── paranormal.xml
│ └── values/
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
└── settings.gradle
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# built application files
*.ap_
# files for the dex VM
*.dex
# Java class files
*.class
# generated files
bin/
gen/
# Local configuration file (sdk path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# Ignore gradle files
.gradle/
build/
# Eclipse project files
.classpath
.project
.settings/
# Intellij project files
*.iml
*.ipr
*.iws
.idea/
# Mac system files
.DS_Store
================================================
FILE: README.md
================================================
# MemoryMonitor
一个给开发者使用的Android App内存清理、监控工具。
主要包括三部分内容:
* 内存清理
通过内存清理可以模拟系统内存不足时对进程的回收。
* Pss监控
通过内存监控可以监控指定应用程序使用的total Pss以及当前手机的内存使用情况,从而检测该应用是否存在内存泄漏。
* 内存优化
整理了一些关于内存优化的tips,以及一些可能导致内存溢出的场景示例,包含错误的写法和正确的写法。
## 1.内存清理
类似各种手机管家的 **加速球**,获取系统已用内存比率、可用内存大小,一键清理。
可以用于测试自己开发的Activity、Fragment健壮性,模拟Activity、Fragment被回收的场景,测试自己的程序是否完好的保存、恢复当前场景。
比如:打开你开发的某个Activity、Fragment,切到后台,清理一次内存,在将其切回前台后,看会不会出现空指针异常,以及程序状态是否被恢复。
## 2.Pss监控
Android 系统中的内存和Linux系统一样,存在着大量的共享内存。每个APP占内存会有私有和公共的两部分,我们可以通过App的Pss值,可以获取到这两部分内存。
Pss(Proportional Set Size):实际使用的物理内存,即:自身应用占有的内存+共享内存中比例分配给这个应用的内存。
通过该程序,每隔1秒,获取一次被监控App的Total Pss值。
使用某个功能(可能会导致OOM的那些都要试试),查看Pss是否飙升,或者使用过许久都没有降低。
如果使用后飙升并且长时间都降不下来,那就说明肯定会导致OOM(对象使用过之后还被引用着未释放),如果使用之后Total Pss飙升,但是使用过之后能降下来,也可能会导致OOM,我们还是需要去一点一点排查是什么原因导致的。
如果使用后飙升并且长时间都降不下来,我们就需要 [使用MAT来进一步分析问题所在](http://blog.csdn.net/xiaanming/article/details/42396507)。
此处提到的Pss,也可以使用adb命令
> adb shell dumpsys meminfo *your packageName*
查看:

## 3.内存优化
Android的虚拟机是基于寄存器的Dalvik,它的最大堆大小一般比较小(最低端的设备16M,后来出的设备变成了24M,48M等等),因此我们所能利用的内存空间是有限的。如果我们使用内存占用超过了一定的限额后就会出现OutOfMemory的错误。
可能会导致内存溢出的情况有以下几种:
### 对静态变量的错误使用
如果一个变量为static变量,它就属于整个类,而不是类的具体实例,所以static变量的生命周期是特别的长,如果static变量引用了一些资源耗费过多的实例,例如Context,就有内存溢出的危险。
[Google开发者博客,给出了一个例子](http://android-developers.blogspot.jp/2009/01/avoiding-memory-leaks.html),专门介绍长时间引用Context导致内存溢出的情况。
示例代码:
```java
private static Drawable sBackground;
@Override
protected void onCreate(Bundle state) {
super.onCreate(state);
TextView textView = new TextView(this);
textView.setText("Leaks are bad");
if (sBackground == null) {
sBackground = getResources().getDrawable(R.drawable.large_bitmap);
}
textView.setBackgroundDrawable(sBackground);
setContentView(textView);
}
```
这种情况下,静态的sBackground变量,虽然没有显式的持有Context的引用,但当我们执行`view.setBackgroundDrawable(Drawable drawable);`的时候,drawable 对象会将当前view设置为一个回调,通过 `View.setCallback(this)` 方法。
具体可见View类的源码:
```
public void setBackgroundDrawable(Drawable background) {
//...
if (mBackground == null || mBackground.getMinimumHeight() != background.getMinimumHeight() ||
mBackground.getMinimumWidth() != background.getMinimumWidth()) {
requestLayout = true;
}
background.setCallback(this);
if (background.isStateful()) {
background.setState(getDrawableState());
}
background.setVisible(getVisibility() == VISIBLE, false);
mBackground = background;
//...
}
```
`background.setCallback(this);` 代码块就是我们说的设置回调。
所以,这种情况就会存在这么一个隐式的引用链:Drawable持有View,而View持有Context,sBackground 是静态的,生命周期特别的长,于是就会导致了Context的溢出。
解决办法:
1.不用activity的context 而是用Application的Context;
```java
private static Drawable sBackground;
@Override
protected void onCreate(Bundle state) {
super.onCreate(state);
TextView textView = new TextView(this.getApplication());
textView.setText("Leaks are bad");
if (sBackground == null) {
sBackground = getResources().getDrawable(R.drawable.large_bitmap);
}
textView.setBackgroundDrawable(sBackground);
setContentView(textView);
}
```
2.在onDestroy()方法中,解除Activity与Drawable的绑定关系,从而去除Drawable对Activity的引用,使Context能够被回收;
```java
@Override
protected void onDestroy() {
super.onDestroy();
ViewUtils.unbindDrawables(findViewById(android.R.id.content));
System.gc();
}
public static void unbindDrawables(View view) {
if (view.getBackground() != null) {
view.getBackground().setCallback(null);
}
if (view instanceof ViewGroup) {
for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
unbindDrawables(((ViewGroup) view).getChildAt(i));
}
((ViewGroup) view).removeAllViews();
}
}
```
### 长周期内部类、匿名内部类长时间持有外部类引用导致相关资源无法释放
长周期内部类、匿名内部类,如Handler,Thread,AsyncTask等。
HandlerOutOfMemoryActivity所示的是Handler引发的内存溢出。
ThreadOutOfMemoryActivity所示的是Thread引发的内存溢出。
AsyncTaskOutOfMemoryActivity所示的时AsyncTask引发的内存溢出。
### Bitmap导致的内存溢出
一般是因为尝试加载过大的图片到内存,或者是内存中已经存在的过多的图片,从而导致内存溢出。
### 数据库Cursor未关闭
正常情况下,如果查询得到的数据量较小时不会有内存问题,而且虚拟机能够保证Cusor最终会被释放掉,如果Cursor的数据量特表大,特别是如果里面有Blob信息时,应该保证Cursor占用的内存被及时的释放掉,而不是等待GC来处理。
### 单例模式引用Context导致的内存泄露
如果在某个Activity中使用 `Singleton instance = Singleton.getInstance(this);` 就会造成该Activity一直被 `Singleton` 引用着,不能释放。这时候,正确的做法是使用 `getApplicationContext()` 来替代 `Activity的Context` ,这样就能避免内存泄露。
### 代码中一些细节
>* 尽量使用9path
>* Adapter要使用convertView
>* 各种监听,广播等,注册的同时要记得取消注册
>* 使用完对象要及时销毁,能使用局部变量的不要使用全局变量,功能用完成后要去掉对他的引用
>* 切勿在循环调用的地方去产生对象,比如在getview()里new OnClicklistener(),这样的话,拖动的时候会new大量的对象出来。
>* 使用Android推荐的数据结构,比如HashMap替换为SparseArray,避免使用枚举类型(在Android平台,枚举类型的内存消耗是Static常量的的2倍)
>* 使用lint工具优化工程
>* 字符串拼接使用StringBuilder或者StringBuffer
>* 尽量使用静态匿名内部类,如果需要对外部类的引用,使用弱引用
>* for循环的使用
用
`final int size = array.length; for(int i = 0; i< size;i++)`
来替代:
`for(int i =0;i < array.length;i++) `
最后,整理了一些开发中可能会导致内存溢出的场景,放在com.cundong.memory.demo.wrong中,并且给出了优化方法,放在com.cundong.memory.demo.right中。
## 4.截图
![截屏][1]
[1]: https://raw.githubusercontent.com/cundong/MemoryMonitor/master/screenshot/app.png
================================================
FILE: app/build.gradle
================================================
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
defaultConfig {
applicationId "com.cundong.memory"
minSdkVersion 14
targetSdkVersion 23
versionCode 2
versionName "1.2"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile project(':magnet')
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.1.1'
compile 'com.android.support:design:23.1.1'
compile 'com.jaredrummler:android-processes:1.0.3'
}
================================================
FILE: app/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.cundong.memory">
<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="23" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application
android:name=".App"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden|navigation|screenSize"
android:label="@string/app_name"
android:launchMode="singleTask"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".demo.wrong.HandlerOutOfMemoryActivity"
android:configChanges="orientation|keyboardHidden|navigation|screenSize"
android:label="@string/app_name"
android:screenOrientation="portrait" />
<activity
android:name=".demo.wrong.StaticOutOfMemoryActivity"
android:configChanges="orientation|keyboardHidden|navigation|screenSize"
android:label="@string/app_name"
android:screenOrientation="portrait" />
<activity
android:name=".demo.right.HandlerOutOfMemoryActivity"
android:configChanges="orientation|keyboardHidden|navigation|screenSize"
android:label="@string/app_name"
android:screenOrientation="portrait" />
<activity
android:name=".demo.right.OneStaticOutOfMemoryActivity"
android:configChanges="orientation|keyboardHidden|navigation|screenSize"
android:label="@string/app_name"
android:screenOrientation="portrait" />
<activity
android:name=".demo.right.TwoStaticOutOfMemoryActivity"
android:configChanges="orientation|keyboardHidden|navigation|screenSize"
android:label="@string/app_name"
android:screenOrientation="portrait" />
<activity
android:name=".demo.wrong.MemoryChurnActivity"
android:configChanges="orientation|keyboardHidden|navigation|screenSize"
android:label="@string/app_name"
android:screenOrientation="portrait" />
<service android:name=".service.CoreService" android:exported="false"/>
</application>
</manifest>
================================================
FILE: app/src/main/java/com/cundong/memory/App.java
================================================
package com.cundong.memory;
import android.app.Application;
import android.content.Context;
public class App extends Application {
private static Context mContext;
public static Context getAppContext() {
return mContext;
}
@Override
public void onCreate() {
super.onCreate();
mContext = this;
}
}
================================================
FILE: app/src/main/java/com/cundong/memory/Constants.java
================================================
package com.cundong.memory;
import java.util.ArrayList;
public class Constants {
public static final boolean SHOW_MEMORY_CLEAR = false;
// TODO
/**
* 此处,改为被监控 total Pss 的 processName 列表
* <p/>
* 微信:
* com.tencent.mm
* com.tencent.mm:TMAssistantDownloadSDKService
* com.tencent.mm:push
* com.tencent.mm:cuploader
* com.tencent.mm:nospace
* com.tencent.mm:tools
* com.tencent.mm:sandbox
* com.tencent.mm:exdevice
*/
public static final ArrayList<String> PROCESS_NAME_LIST = new ArrayList();
static {
PROCESS_NAME_LIST.add("com.tencent.mm");
PROCESS_NAME_LIST.add("com.tencent.mm:TMAssistantDownloadSDKService");
PROCESS_NAME_LIST.add("com.tencent.mm:push");
PROCESS_NAME_LIST.add("com.tencent.mm:cuploader");
PROCESS_NAME_LIST.add("com.tencent.mm:nospace");
PROCESS_NAME_LIST.add("com.tencent.mm:tools");
PROCESS_NAME_LIST.add("com.tencent.mm:sandbox");
PROCESS_NAME_LIST.add("com.tencent.mm:exdevice");
}
}
================================================
FILE: app/src/main/java/com/cundong/memory/MainActivity.java
================================================
package com.cundong.memory;
import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import com.cundong.memory.service.CoreService;
import com.cundong.memory.service.EmptyService;
import com.cundong.memory.util.MemoryUtil;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private static final int OVERLAY_PERMISSION_REQ_CODE = 1;
private Button mButton1, mButton2, mButton3, mButton4;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mButton1 = (Button) findViewById(R.id.button1);
mButton2 = (Button) findViewById(R.id.button2);
mButton3 = (Button) findViewById(R.id.button3);
mButton4 = (Button) findViewById(R.id.button4);
mButton3.setVisibility(Constants.SHOW_MEMORY_CLEAR ? View.VISIBLE : View.GONE);
mButton4.setVisibility(Constants.SHOW_MEMORY_CLEAR ? View.VISIBLE : View.GONE);
mButton1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try2StartMonitor();
}
});
mButton2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, CoreService.class);
intent.putExtra("action", 2);
startService(intent);
finish();
}
});
mButton3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(getApplicationContext(), "clearMemory", Toast.LENGTH_LONG).show();
MemoryUtil.clearMemory(getApplicationContext());
}
});
mButton4.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, EmptyService.class);
startService(intent);
}
});
}
@TargetApi(Build.VERSION_CODES.M)
private void try2StartMonitor() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {
Toast.makeText(this, R.string.permission_err, Toast.LENGTH_LONG).show();
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE);
} else {
Intent intent = new Intent(MainActivity.this, CoreService.class);
intent.putExtra("action", 1);
startService(intent);
finish();
}
}
@Override
protected void onResume() {
super.onResume();
boolean isServiceRunning = isServiceRunning(getPackageName() + ".service.CoreService");
mButton1.setVisibility(isServiceRunning ? View.GONE : View.VISIBLE);
mButton2.setVisibility(isServiceRunning ? View.VISIBLE : View.GONE);
}
@TargetApi(Build.VERSION_CODES.M)
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == OVERLAY_PERMISSION_REQ_CODE) {
if (!Settings.canDrawOverlays(this)) {
Toast.makeText(this, R.string.permission_err, Toast.LENGTH_SHORT).show();
} else {
Intent intent = new Intent(MainActivity.this, CoreService.class);
intent.putExtra("action", 1);
startService(intent);
finish();
}
}
}
/**
* 使用Application context来调用getSystemService,避免内存泄漏
*
* @param className
* @return
*/
private boolean isServiceRunning(String className) {
ActivityManager activityManager = (ActivityManager) App.getAppContext().getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningServiceInfo> serviceList = activityManager.getRunningServices(100);
if (serviceList == null || serviceList.size() <= 0) {
return false;
}
for (ActivityManager.RunningServiceInfo serviceInfo : serviceList) {
String serviceName = serviceInfo.service.getClassName();
if (serviceName.equals(className)) {
return true;
}
}
return false;
}
}
================================================
FILE: app/src/main/java/com/cundong/memory/demo/right/AsyncTaskOutOfMemoryActivity.java
================================================
package com.cundong.memory.demo.right;
import java.lang.ref.WeakReference;
import android.app.Activity;
import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.os.Bundle;
import android.widget.ImageView;
import com.cundong.memory.util.BitmapUtils;
import com.cundong.memory.R;
/**
* AsyncTask引发的内存溢出解决办法
*
* BitmapWorkerTask内部采用弱引用保存Context引用
*
*/
public class AsyncTaskOutOfMemoryActivity extends Activity {
private ImageView mImageView;
@Override
protected void onCreate(Bundle state) {
super.onCreate(state);
this.setContentView(R.layout.activity_demo);
mImageView = (ImageView) findViewById(R.id.image);
BitmapWorkerTask task = new BitmapWorkerTask(mImageView);
task.execute(R.drawable.large_bitmap);
}
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
private final WeakReference<ImageView> imageViewReference;
private int data = 0;
public BitmapWorkerTask(ImageView imageView) {
// Use a WeakReference to ensure the ImageView can be garbage collected
imageViewReference = new WeakReference<>(imageView);
}
// Decode image in background.
@Override
protected Bitmap doInBackground(Integer... params) {
data = params[0];
return BitmapUtils.decodeSampledBitmapFromResource(getResources(), data, 100, 100);
}
// Once complete, see if ImageView is still around and set bitmap.
@Override
protected void onPostExecute(Bitmap bitmap) {
if (imageViewReference != null && bitmap != null) {
final ImageView imageView = imageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
}
}
}
================================================
FILE: app/src/main/java/com/cundong/memory/demo/right/HandlerOutOfMemoryActivity.java
================================================
package com.cundong.memory.demo.right;
import java.lang.ref.WeakReference;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import com.cundong.memory.R;
/**
* 解决Handler引发的内存溢出
*
* 不使用非静态的内部类,改为使用静态内部类,当静态内部类中需要调用外部的Activity时,改用弱引用。
*
*/
public class HandlerOutOfMemoryActivity extends Activity {
private final MyHandler mHandler = new MyHandler(this);
private static class MyHandler extends Handler {
private final WeakReference<HandlerOutOfMemoryActivity> mActivity;
public MyHandler(HandlerOutOfMemoryActivity activity) {
mActivity = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
HandlerOutOfMemoryActivity activity = mActivity.get();
if (activity != null) {
/* ... */
}
}
}
private static final Runnable sRunnable = new Runnable() {
@Override
public void run() {
/* ... */
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.activity_outofmemory_list);
mHandler.postDelayed(sRunnable, 1000 * 60 * 10);
finish();
}
}
================================================
FILE: app/src/main/java/com/cundong/memory/demo/right/OneStaticOutOfMemoryActivity.java
================================================
package com.cundong.memory.demo.right;
import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.widget.TextView;
import com.cundong.memory.R;
/**
* 解决static变量引发的内存溢出
*
* 1.不用activity的context 而是用application的context
*
*/
public class OneStaticOutOfMemoryActivity extends Activity {
private static Drawable sBackground;
@Override
protected void onCreate(Bundle state) {
super.onCreate(state);
TextView label = new TextView(this.getApplication());
label.setText("Leaks are bad");
if (sBackground == null) {
sBackground = getResources().getDrawable(R.drawable.large_bitmap);
}
label.setBackgroundDrawable(sBackground);
setContentView(label);
}
}
================================================
FILE: app/src/main/java/com/cundong/memory/demo/right/OneThreadOutOfMemoryActivity.java
================================================
package com.cundong.memory.demo.right;
import java.lang.ref.WeakReference;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import com.cundong.memory.R;
/**
* 解决Thread引发的内存溢出
*
* 解决办法:
*
* 在线程内部采用弱引用保存Context引用
*
*/
public class OneThreadOutOfMemoryActivity extends Activity {
private Context mContext;
@Override
protected void onCreate(Bundle state) {
super.onCreate(state);
this.setContentView(R.layout.activity_demo);
mContext = this.getApplicationContext();
new MyThread().start();
}
private void releaseZip(WeakReference<Context> context){
/* do something */
}
private class MyThread extends Thread {
@Override
public void run() {
super.run();
try {
Thread.sleep(1000 * 60 * 2);
} catch (InterruptedException e) {
e.printStackTrace();
}
/* do something */
releaseZip(new WeakReference<Context>(mContext));
}
}
}
================================================
FILE: app/src/main/java/com/cundong/memory/demo/right/TwoStaticOutOfMemoryActivity.java
================================================
package com.cundong.memory.demo.right;
import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.widget.TextView;
import com.cundong.memory.util.ViewUtils;
import com.cundong.memory.R;
/**
* 解决static变量引发的内存溢出
*
* 2.在 onDestroy() 方法中,解除 Activity 和 biamap(drawble)的绑定关系,从而去除bitmap对activity 引用,让系统适时的去回收
*
*/
public class TwoStaticOutOfMemoryActivity extends Activity {
private static Drawable sBackground;
@Override
protected void onCreate(Bundle state) {
super.onCreate(state);
TextView label = new TextView(this);
label.setText("Leaks are bad");
if (sBackground == null) {
sBackground = getResources().getDrawable(R.drawable.large_bitmap);
}
label.setBackgroundDrawable(sBackground);
setContentView(label);
}
@Override
protected void onDestroy() {
super.onDestroy();
ViewUtils.unbindDrawables(findViewById(android.R.id.content));
System.gc();
}
}
================================================
FILE: app/src/main/java/com/cundong/memory/demo/right/TwoThreadOutOfMemoryActivity.java
================================================
package com.cundong.memory.demo.right;
import java.lang.ref.WeakReference;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import com.cundong.memory.R;
/**
* 解决Thread引发的内存溢出
*
* 1.MyThread改为静态内部类
*/
public class TwoThreadOutOfMemoryActivity extends Activity {
@Override
protected void onCreate(Bundle state) {
super.onCreate(state);
this.setContentView(R.layout.activity_demo);
new MyThread(this).start();
}
private static void releaseZip(Context context){
/* do something */
}
private static class MyThread extends Thread {
private final WeakReference<TwoThreadOutOfMemoryActivity> mActivity;
public MyThread(TwoThreadOutOfMemoryActivity activity) {
mActivity = new WeakReference<>(activity);
}
@Override
public void run() {
super.run();
try {
Thread.sleep(1000 * 60 * 2);
} catch (InterruptedException e) {
e.printStackTrace();
}
/* do something */
releaseZip(mActivity.get());
}
}
}
================================================
FILE: app/src/main/java/com/cundong/memory/demo/wrong/AsyncTaskOutOfMemoryActivity.java
================================================
package com.cundong.memory.demo.wrong;
import android.app.Activity;
import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.os.Bundle;
import android.widget.ImageView;
import com.cundong.memory.util.BitmapUtils;
import com.cundong.memory.R;
/**
* AsyncTask引发的内存溢出
*
* 当前情况:
*
* 1.
* 内部类BitmapWorkerTask,持有对外部AsyncTaskOutOfMemoryActivity的隐式引用
*
* 2.
* 如果我们切换横竖屏,默认就会销毁当前Activity,而这个Activity却被BitmapWorkerTask所持有
*
* 于是就出现了溢出。
*
*
* 解决办法:
*
* BitmapWorkerTask内部采用弱引用保存Context引用
*
*/
public class AsyncTaskOutOfMemoryActivity extends Activity {
private ImageView mImageView;
@Override
protected void onCreate(Bundle state) {
super.onCreate(state);
this.setContentView(R.layout.activity_demo);
mImageView = (ImageView) findViewById(R.id.image);
BitmapWorkerTask task = new BitmapWorkerTask();
task.execute(R.drawable.large_bitmap);
}
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
private int data = 0;
// Decode image in background.
@Override
protected Bitmap doInBackground(Integer... params) {
data = params[0];
return BitmapUtils.decodeSampledBitmapFromResource(getResources(), data, 100, 100);
}
// Once complete, see if ImageView is still around and set bitmap.
@Override
protected void onPostExecute(Bitmap bitmap) {
if (bitmap != null) {
mImageView.setImageBitmap(bitmap);
}
}
}
}
================================================
FILE: app/src/main/java/com/cundong/memory/demo/wrong/HandlerOutOfMemoryActivity.java
================================================
package com.cundong.memory.demo.wrong;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import com.cundong.memory.R;
/**
* Handler引发的内存溢出
*
* 1.
* 当一个Android应用启动的时候,会自动创建一个供主线程使用的Looper实例,这个Looper实例负责一个一个的处理消息队列中的消息对象,它的生命周期和当前应用
* 的生命周期是一样的。
*
* 2.
* 当Handler在主线程中初始化之后,发送一个消息(target为当前Handler)至消息队列,这个消息对象就已经包含了Handler实例的引用,只有这样,Looper在处理这条消息的时候,才可以
* 调用Handler的handlerMessage(Message)来完成消息的处理。
*
* 3.非静态的内部类和匿名内部类,都会隐式的持有一个外部类的引用。
*
* 由于这3个原因。
*
* 当Activity finish掉,被延迟的消息会存在消息队列中10分钟,这个消息中又包含了Handler的引用,Handler是一个匿名内部类,又隐式的持有外部Activity的引用,导致
* 其无法回收,进一步导致Activity持有的很多资源都无法回收,也就是内存泄露了。
*
* 正确示例:com.cundong.memory.right.HandlerOutOfMemoryActivity
*
*/
public class HandlerOutOfMemoryActivity extends Activity {
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
/* ... */
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.activity_outofmemory_list);
// Post a message and delay its execution for 10 minutes.
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
/* ... */
}
}, 1000 * 60 * 10);
finish();
}
}
================================================
FILE: app/src/main/java/com/cundong/memory/demo/wrong/MemoryChurnActivity.java
================================================
package com.cundong.memory.demo.wrong;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import com.cundong.memory.R;
import com.cundong.memory.util.MemoryUtil;
/**
* 内存抖动
*
* 在短时间内大量的对象被创建又马上被释放,瞬间产生大量的对象会严重占用Young Generation的内存区域,当达到阀值,剩余空间不够的时候,
* 会触发GC从而导致刚产生的对象又很快被回收。即使每次分配的对象占用了很少的内存,但是他们叠加在一起会增加Heap的压力,从而触发更多其他类型的GC。
*
* 这个操作有可能会影响到帧率,并使得用户感知到性能问题。
*
* Created by cundong on 2015/12/28.
*/
public class MemoryChurnActivity extends Activity {
private Button mButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.activity_test);
mButton = (Button) findViewById(R.id.button);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread() {
@Override
public void run() {
super.run();
for( int i=0; i<100; i++) {
Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.large_img);
int rowBytes = bmp.getRowBytes();
int height = bmp.getHeight();
long memSize = rowBytes * height;
Log.d("@Cundong", "memSize =" + memSize + "B =" + memSize * 1.0 / 1024 / 1024 +" M");
Log.d("@Cundong", "getUsedPercentValue:" + MemoryUtil.getUsedPercentValue());
}
/**
java.lang.OutOfMemoryError: Failed to allocate a 47 byte allocation with 0 free bytes and 3GB until OOM
at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:609)
at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:444)
at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:467)
at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:497)
at com.cundong.memory.demo.wrong.MemoryChurnActivity$1$1.run(MemoryChurnActivity.java:42)
*/
}
} .start();
}
});
}
}
================================================
FILE: app/src/main/java/com/cundong/memory/demo/wrong/StaticOutOfMemoryActivity.java
================================================
package com.cundong.memory.demo.wrong;
import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.widget.TextView;
import com.cundong.memory.R;
/**
* static变量引发的内存溢出
*
* 如果一个变量为static变量,它就属于整个类,而不是类的具体实例,所以static变量的生命周期是特别的长,如果static变量引用了一些资源耗费过多
* 的实例,例如Context,就有内存溢出的危险。
*
*
* Google开发者博客,给出了一个例子:http://android-developers.blogspot.jp/2009/01/avoiding-memory-leaks.html
* 专门介绍长时间的引用Context导致内存溢出的情况。
*
* 这种情况:
*
* 静态的sBackground变量,虽然没有显式的持有Context的引用,但是:
*
* 当我们执行view.setBackgroundDrawable(Drawable drawable);之后。
*
* Drawable会将View设置为一个回调(通过setCallback()方法),所以就会存在这么一个隐式的引用链:Drawable持有View,View持有Context
*
* sBackground是静态的,生命周期特别的长,就会导致了Context的溢出。
*
* 解决办法:
*
* 1.不用activity的context 而是用Application的Context
*
* 2.在onDestroy()方法中,解除Activity与Drawable的绑定关系,从而去除Drawable对Activity的引用,使Context能够被回收
*
*/
public class StaticOutOfMemoryActivity extends Activity {
private static Drawable sBackground;
@Override
protected void onCreate(Bundle state) {
super.onCreate(state);
TextView textView = new TextView(this);
textView.setText("Leaks are bad");
if (sBackground == null) {
sBackground = getResources().getDrawable(R.drawable.large_bitmap);
}
textView.setBackgroundDrawable(sBackground);
setContentView(textView);
}
}
================================================
FILE: app/src/main/java/com/cundong/memory/demo/wrong/ThreadOutOfMemoryActivity.java
================================================
package com.cundong.memory.demo.wrong;
import java.lang.ref.WeakReference;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import com.cundong.memory.R;
/**
* Thread引发的内存溢出
*
* 当前情况:
*
* 1.
* 内部类MyThread,持有对外部ThreadOutOfMemoryActivity的隐式引用
*
* 2.
* 如果我们切换横竖屏,默认就会销毁当前Activity,而这个Activity却被MyThread所持有
*
* 于是就出现了溢出。
*
*
* 解决办法:
*
* 1.MyThread改为静态内部类
*
* 2.在线程内部采用弱引用保存Context引用
*
*/
public class ThreadOutOfMemoryActivity extends Activity {
private Context mContext;
@Override
protected void onCreate(Bundle state) {
super.onCreate(state);
this.setContentView(R.layout.activity_demo);
mContext = this.getApplicationContext();
new MyThread().start();
}
private void releaseZip(WeakReference<Context> context){
/* do something */
}
private class MyThread extends Thread {
@Override
public void run() {
super.run();
try {
Thread.sleep(1000 * 60 * 2);
} catch (InterruptedException e) {
e.printStackTrace();
}
/* do something */
releaseZip(new WeakReference<>(mContext));
}
}
}
================================================
FILE: app/src/main/java/com/cundong/memory/service/CoreService.java
================================================
package com.cundong.memory.service;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageButton;
import android.widget.TextView;
import android.widget.Toast;
import com.cundong.memory.Constants;
import com.cundong.memory.MainActivity;
import com.cundong.memory.R;
import com.cundong.memory.util.MemoryUtil;
import com.premnirmal.Magnet.IconCallback;
import com.premnirmal.Magnet.Magnet;
import java.lang.ref.WeakReference;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
/**
* 类说明: 后台轮询service 每1秒钟更新一次通知栏 更新内存
*
* @date 2015-4-18
* @version 1.0
*/
public class CoreService extends Service implements IconCallback {
private static final String TAG = "CoreService";
private Timer mTimer;
private Magnet mMagnet;
private View mIconView = null;
private View mMemoryClearView;
private TextView mDescView;
private ImageButton mClearBtn, mSettingBtn;
private InnerHandler mHandler;
@Override
public void onCreate() {
super.onCreate();
mIconView = getIconView();
mDescView = (TextView) mIconView.findViewById(R.id.content);
mMemoryClearView = mIconView.findViewById(R.id.memory_clear_view);
mMemoryClearView.setVisibility(Constants.SHOW_MEMORY_CLEAR ? View.VISIBLE : View.GONE);
mClearBtn = (ImageButton) mIconView.findViewById(R.id.clear_btn);
mClearBtn.setOnClickListener( new OnClickListener(){
@Override
public void onClick(View v) {
Toast.makeText(getApplicationContext(), "clearMemory", Toast.LENGTH_SHORT).show();
MemoryUtil.clearMemory(getApplicationContext());
}
});
mSettingBtn = (ImageButton) mIconView.findViewById(R.id.setting_btn);
mSettingBtn.setOnClickListener( new OnClickListener(){
@Override
public void onClick(View v) {
Toast.makeText(getApplicationContext(), "test Setting", Toast.LENGTH_SHORT).show();
}
});
mMagnet = new Magnet.Builder(this)
.setIconView(mIconView)
.setIconCallback(this)
.setRemoveIconResId(R.drawable.trash)
.setRemoveIconShadow(R.drawable.bottom_shadow)
.setShouldFlingAway(true)
.setShouldStickToWall(true)
.setRemoveIconShouldBeResponsive(true)
.setInitialPosition(-100, -200)
.build();
mMagnet.show();
mHandler = new InnerHandler(this);
}
private View getIconView() {
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
return inflater.inflate(R.layout.float_view, null);
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (mTimer == null) {
mTimer = new Timer();
mTimer.scheduleAtFixedRate(new RefreshTask(), 0, 1000);
}
int action = intent != null ? intent.getIntExtra("action", 0) : 0;
if (action == 2) {
mMagnet.destroy();
this.stopSelf();
}
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
mTimer.cancel();
mTimer = null;
}
class RefreshTask extends TimerTask {
@Override
public void run() {
String usedPercentValue = MemoryUtil.getUsedPercentValue();
long availableMemory = MemoryUtil.getAvailableMemory();
HashMap<String, Long> totalPssMap = MemoryUtil.getTotalPss(Constants.PROCESS_NAME_LIST);
float memory = availableMemory / (float) 1024 / (float) 1024;
DecimalFormat decimalFormat = new DecimalFormat("##0.00");
final String[] content = new String[] {
getString(R.string.used_percent_value, usedPercentValue),
getString(R.string.available_memory, decimalFormat.format(memory)), getString(R.string.total_pss)};
StringBuffer sb = new StringBuffer();
sb.append(content[0]).append(",").append(content[1]).append("\r\n").append(content[2]);
Iterator iterator = totalPssMap.entrySet().iterator();
while(iterator.hasNext()) {
Map.Entry entry = (Map.Entry) iterator.next();
entry.getKey();
sb.append(entry.getKey()).append("=").append("\r\n").append(entry.getValue()).append("\r\n");
}
Bundle data = new Bundle();
data.putString("content", sb.toString());
Message message = mHandler.obtainMessage(1);
message.what = 1;
message.setData(data);
mHandler.sendMessage(message);
}
}
@Override
public void onFlingAway() {
Log.i(TAG, "onFlingAway");
}
@Override
public void onMove(float x, float y) {
Log.i(TAG, "onMove(" + x + "," + y + ")");
}
@Override
public void onIconClick(View icon, float iconXPose, float iconYPose) {
Log.i(TAG, "onIconClick(..)");
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
@Override
public void onIconDestroyed() {
Log.i(TAG, "onIconDestroyed()");
stopSelf();
}
private static class InnerHandler extends Handler {
private WeakReference<CoreService> ref;
public InnerHandler(CoreService service) {
ref = new WeakReference<>(service);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
CoreService service = ref.get();
if (service == null) {
return;
}
switch (msg.what) {
case 1:
Bundle data = msg.getData();
String content = data.getString("content");
service.mDescView.setText(content);
break;
}
}
}
}
================================================
FILE: app/src/main/java/com/cundong/memory/service/EmptyService.java
================================================
package com.cundong.memory.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
/**
* 一个空Service,仅仅用于测试一个空进程在dalvik、art上占用多少Total Pss
*/
public class EmptyService extends Service {
public EmptyService() {
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
}
}
================================================
FILE: app/src/main/java/com/cundong/memory/util/BitmapUtils.java
================================================
package com.cundong.memory.util;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
public class BitmapUtils {
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
}
================================================
FILE: app/src/main/java/com/cundong/memory/util/Logger.java
================================================
package com.cundong.memory.util;
import android.util.Log;
public class Logger {
/**
* log tag
*/
private String tag = "Logger";// application name
// TODO 配置Log打开或关闭
/**
* debug or not
*/
private static boolean debug = true;
private static Logger instance = new Logger();
private Logger() {
}
public static Logger getLogger() {
return instance;
}
private String getFunctionName() {
StackTraceElement[] sts = Thread.currentThread().getStackTrace();
if (sts == null) {
return null;
}
for (StackTraceElement st : sts) {
if (st.isNativeMethod()) {
continue;
}
if (st.getClassName().equals(Thread.class.getName())) {
continue;
}
if (st.getClassName().equals(this.getClass().getName())) {
continue;
}
return "[" + Thread.currentThread().getName() + "(" + Thread.currentThread().getId() + "): " + st.getFileName() + ":" + st.getLineNumber() + "]";
}
return null;
}
private String createMessage(String msg) {
String functionName = getFunctionName();
String message = (functionName == null ? msg : (functionName + " - " + msg));
return message;
}
/**
* log.i
*/
public void i(String msg) {
if (debug) {
String message = createMessage(msg);
Log.i(tag, message);
}
}
/**
* log.v
*/
public void v(String msg) {
if (debug) {
String message = createMessage(msg);
Log.v(tag, message);
}
}
/**
* log.d
*/
public void d(String msg) {
if (debug) {
String message = createMessage(msg);
Log.d(tag, message);
}
}
/**
* log.e
*/
public void e(String msg) {
// if (debug) {
String message = createMessage(msg);
Log.e(tag, message);
// }
}
/**
* log.error
*/
public void error(Exception e) {
if (debug) {
StringBuffer sb = new StringBuffer();
String name = getFunctionName();
StackTraceElement[] sts = e.getStackTrace();
if (name != null) {
sb.append(name + " - " + e + "\r\n");
} else {
sb.append(e + "\r\n");
}
if (sts != null && sts.length > 0) {
for (StackTraceElement st : sts) {
if (st != null) {
sb.append("[ " + st.getFileName() + ":" + st.getLineNumber() + " ]\r\n");
}
}
}
Log.e(tag, sb.toString());
}
}
/**
* log.d
*/
public void w(String msg) {
if (debug) {
String message = createMessage(msg);
Log.w(tag, message);
}
}
public void setTag(String tag) {
this.tag = tag;
}
/**
* set debug
*/
public static void setDebug(boolean d) {
debug = d;
}
public static boolean isDebug() {
return debug;
}
}
================================================
FILE: app/src/main/java/com/cundong/memory/util/MemoryUtil.java
================================================
package com.cundong.memory.util;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.content.Context;
import android.os.Build;
import android.os.Debug.MemoryInfo;
import android.text.TextUtils;
import android.util.Log;
import com.cundong.memory.App;
import com.cundong.memory.Constants;
import com.jaredrummler.android.processes.ProcessManager;
import com.jaredrummler.android.processes.models.AndroidAppProcess;
/**
* 类说明: 内存相关数据获取工具类
*
* @date 2015-4-18
* @version 1.0
*/
public class MemoryUtil {
/**
* getTotalPss,5.0+使用开源的android-processes解决方案,5.0以下使用系统api
*
* @param processNameList
* @return
*/
public static HashMap<String, Long> getTotalPss(ArrayList<String> processNameList) {
HashMap<String, Long> resultMap = new HashMap<>();
ActivityManager activityMgr = (ActivityManager) App.getAppContext().getSystemService(Context.ACTIVITY_SERVICE);
if (Build.VERSION.SDK_INT >= 21) {
List<AndroidAppProcess> list = ProcessManager.getRunningAppProcesses();
if (list != null) {
for (AndroidAppProcess processInfo : list) {
if (processNameList.contains(processInfo.name)) {
int pid = processInfo.pid;
MemoryInfo[] memoryInfos = activityMgr.getProcessMemoryInfo(new int[]{pid});
MemoryInfo memoryInfo = memoryInfos[0];
int totalPss = memoryInfo.getTotalPss();
resultMap.put(processInfo.name, new Long(totalPss));
}
}
}
} else {
List<RunningAppProcessInfo> list = activityMgr.getRunningAppProcesses();
if (list != null) {
for (RunningAppProcessInfo processInfo : list) {
if (Constants.PROCESS_NAME_LIST.contains(processInfo.processName)) {
int pid = processInfo.pid;
MemoryInfo[] memoryInfos = activityMgr.getProcessMemoryInfo(new int[] { pid });
MemoryInfo memoryInfo = memoryInfos[0];
int totalPss = memoryInfo.getTotalPss();
resultMap.put(processInfo.processName, new Long(totalPss));
}
}
}
}
return resultMap;
}
/**
* getTotalPss,5.0+使用开源的android-processes解决方案,5.0以下使用系统api
*
* @param processName
* @return
*/
public static long getTotalPss(String processName) {
ActivityManager activityMgr = (ActivityManager) App.getAppContext().getSystemService(Context.ACTIVITY_SERVICE);
if (Build.VERSION.SDK_INT >= 21) {
List<AndroidAppProcess> list = ProcessManager.getRunningAppProcesses();
if (list != null) {
for (AndroidAppProcess processInfo : list) {
if (processInfo.name.equals(processName)) {
int pid = processInfo.pid;
MemoryInfo[] memoryInfos = activityMgr.getProcessMemoryInfo(new int[]{pid});
MemoryInfo memoryInfo = memoryInfos[0];
int totalPss = memoryInfo.getTotalPss();
return totalPss;
}
}
}
} else {
List<RunningAppProcessInfo> list = activityMgr.getRunningAppProcesses();
if (list != null) {
for (RunningAppProcessInfo processInfo : list) {
if (processInfo.processName.equals(processName)) {
int pid = processInfo.pid;
MemoryInfo[] memoryInfos = activityMgr.getProcessMemoryInfo(new int[] { pid });
MemoryInfo memoryInfo = memoryInfos[0];
int totalPss = memoryInfo.getTotalPss();
return totalPss;
}
}
}
}
return -1;
}
/**
* 计算已使用内存的百分比
*
*/
public static String getUsedPercentValue() {
String dir = "/proc/meminfo";
try {
FileReader fr = new FileReader(dir);
BufferedReader br = new BufferedReader(fr, 2048);
String memoryLine = br.readLine();
String subMemoryLine = memoryLine.substring(memoryLine
.indexOf("MemTotal:"));
br.close();
long totalMemorySize = Integer.parseInt(subMemoryLine.replaceAll(
"\\D+", ""));
long availableSize = getAvailableMemory() / 1024;
int percent = (int) ((totalMemorySize - availableSize)
/ (float) totalMemorySize * 100);
return percent + "%";
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
/**
* 获取可用内存
*
*/
public static long getAvailableMemory() {
ActivityManager activityManager = (ActivityManager) App.getAppContext()
.getSystemService(Context.ACTIVITY_SERVICE);
ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo();
activityManager.getMemoryInfo(mi);
return mi.availMem;
}
public static void clearMemory(Context context) {
ActivityManager activityManger = (ActivityManager) App.getAppContext().getSystemService(Context.ACTIVITY_SERVICE);
if (Build.VERSION.SDK_INT >= 21) {
List<AndroidAppProcess> list = ProcessManager.getRunningAppProcesses();
if (list != null) {
for (AndroidAppProcess processInfo : list) {
int myPid = android.os.Process.myPid();
if(myPid == processInfo.pid) {
continue;
}
if (!processInfo.foreground) {
activityManger.killBackgroundProcesses(processInfo.getPackageName());
}
}
}
} else {
List<ActivityManager.RunningAppProcessInfo> list = activityManger.getRunningAppProcesses();
if (list != null) {
for (int i = 0; i < list.size(); i++) {
ActivityManager.RunningAppProcessInfo appProcessInfo = list.get(i);
String[] pkgList = appProcessInfo.pkgList;
if (appProcessInfo.importance >= ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
for (int j = 0; j < pkgList.length; j++) {
if (pkgList[j].equals(context.getPackageName())) {
continue;
}
activityManger.killBackgroundProcesses(pkgList[j]);
}
}
}
}
}
}
}
================================================
FILE: app/src/main/java/com/cundong/memory/util/ViewUtils.java
================================================
package com.cundong.memory.util;
import android.view.View;
import android.view.ViewGroup;
public class ViewUtils {
public static void unbindDrawables(View view) {
if (view.getBackground() != null) {
view.getBackground().setCallback(null);
}
if (view instanceof ViewGroup) {
for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
unbindDrawables(((ViewGroup) view).getChildAt(i));
}
((ViewGroup) view).removeAllViews();
}
}
}
================================================
FILE: app/src/main/res/anim/slide_down.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromYDelta="0%"
android:toYDelta="100%"
android:duration="@android:integer/config_shortAnimTime"/>
</set>
================================================
FILE: app/src/main/res/anim/slide_up.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromYDelta="100%"
android:toYDelta="0%"
android:duration="@android:integer/config_shortAnimTime"/>
</set>
================================================
FILE: app/src/main/res/layout/activity_demo.xml
================================================
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin" >
<TextView
android:id="@+id/desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dip"
android:text="demo"
android:textSize="18sp" />
<ImageView
android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/large_bitmap" />
</LinearLayout>
================================================
FILE: app/src/main/res/layout/activity_main.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.cundong.memory.MainActivity">
<Button
android:id="@+id/button1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/startmonitor" />
<Button
android:id="@+id/button2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/button1"
android:text="@string/stopmonitor" />
<Button
android:id="@+id/button3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/button2"
android:text="@string/clearmemory" />
<Button
android:id="@+id/button4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/button3"
android:text="Test EmptyProcess" />
</LinearLayout>
================================================
FILE: app/src/main/res/layout/activity_outofmemory_list.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.cundong.memory.MainActivity" >
<TextView
android:id="@+id/desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp" />
</RelativeLayout>
================================================
FILE: app/src/main/res/layout/activity_test.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin" >
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Start"
/>
</RelativeLayout>
================================================
FILE: app/src/main/res/layout/float_view.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/floating_layer_black"
android:gravity="center_vertical|left"
android:orientation="vertical"
android:padding="4dip">
<TextView
android:id="@+id/content"
android:layout_width="@dimen/float_width"
android:layout_height="wrap_content"
android:layout_marginTop="10dip"
android:lineSpacingMultiplier="1.2"
android:textColor="#000000"
android:textSize="12sp" />
<RelativeLayout
android:id="@+id/memory_clear_view"
android:layout_width="@dimen/float_width"
android:layout_height="wrap_content">
<ImageButton
android:id="@+id/clear_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerInParent="true"
android:background="@drawable/sillding_menu_mobile_clear" />
<ImageButton
android:id="@+id/setting_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerInParent="true"
android:background="@drawable/sliding_explorer_icon" />
</RelativeLayout>
</LinearLayout>
================================================
FILE: app/src/main/res/menu/main.xml
================================================
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.example.testmemo.MainActivity" >
<item
android:id="@+id/action_settings"
android:orderInCategory="100"
android:showAsAction="never"
android:title="@string/action_settings"/>
</menu>
================================================
FILE: app/src/main/res/values/colors.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
<color name="colorTransparent">#00000000</color>
</resources>
================================================
FILE: app/src/main/res/values/dimens.xml
================================================
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
<dimen name="float_width">160dip</dimen>
</resources>
================================================
FILE: app/src/main/res/values/strings.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">MemoryMonitor</string>
<string name="hello_world">Hello world!</string>
<string name="action_settings">Settings</string>
<string name="startmonitor">开始监控</string>
<string name="stopmonitor">停止监控</string>
<string name="clearmemory">清理内存</string>
<string name="used_percent_value">内存使用:%1$s</string>
<string name="available_memory">可用:%1$sM</string>
<string name="total_pss">Pss Total:\r\n</string>
<string name="permission_err">当前targets API level 23+, 必须手动设置“允许在其他应用上层显示”开启才行</string>
</resources>
================================================
FILE: app/src/main/res/values/styles.xml
================================================
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
<style name="Transparent" parent="AppTheme.NoActionBar">
<item name="android:windowBackground">@color/colorTransparent</item>
<item name="android:windowIsTranslucent">true</item>
</style>
</resources>
================================================
FILE: app/src/main/res/values-v11/styles.xml
================================================
<resources>
<!--
Base application theme for API 11+. This theme completely replaces
AppBaseTheme from res/values/styles.xml on API 11+ devices.
-->
<style name="AppBaseTheme" parent="android:Theme.Holo.Light">
<!-- API 11 theme customizations can go here. -->
</style>
</resources>
================================================
FILE: app/src/main/res/values-v14/styles.xml
================================================
<resources>
<!--
Base application theme for API 14+. This theme completely replaces
AppBaseTheme from BOTH res/values/styles.xml and
res/values-v11/styles.xml on API 14+ devices.
-->
<style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
<!-- API 14 theme customizations can go here. -->
</style>
</resources>
================================================
FILE: app/src/main/res/values-w820dp/dimens.xml
================================================
<resources>
<!--
Example customization of dimensions originally defined in res/values/dimens.xml
(such as screen margins) for screens with more than 820dp of available width. This
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively).
-->
<dimen name="activity_horizontal_margin">64dp</dimen>
</resources>
================================================
FILE: build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.5.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#Wed Oct 21 11:34:03 PDT 2015
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
================================================
FILE: gradlew
================================================
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&-
APP_HOME="`pwd -P`"
cd "$SAVED" >&-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: magnet/.gitignore
================================================
/build
================================================
FILE: magnet/build.gradle
================================================
apply plugin: 'com.android.library'
android {
compileSdkVersion 23
buildToolsVersion "22.0.1"
lintOptions {
abortOnError false
}
defaultConfig {
minSdkVersion 3
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
}
================================================
FILE: magnet/gradle.properties
================================================
POM_NAME=Magnet
POM_ARTIFACT_ID=library
POM_PACKAGING=aar
================================================
FILE: magnet/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/prem/Documents/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
================================================
FILE: magnet/src/androidTest/java/com/premnirmal/Magnet/ApplicationTest.java
================================================
package com.premnirmal.Magnet;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
*/
public class ApplicationTest extends ApplicationTestCase<Application> {
public ApplicationTest() {
super(Application.class);
}
}
================================================
FILE: magnet/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest
package="com.premnirmal.Magnet" >
<application/>
</manifest>
================================================
FILE: magnet/src/main/java/com/premnirmal/Magnet/FlingListener.java
================================================
package com.premnirmal.Magnet;
import android.view.GestureDetector;
import android.view.MotionEvent;
/**
* Created by prem on 7/20/14.
*/
class FlingListener extends GestureDetector.SimpleOnGestureListener {
private static final float FLING_THRESHOLD_VELOCITY = 50f;
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
return (Math.abs(e2.getX() - e1.getX()) < FLING_THRESHOLD_VELOCITY && e2.getY() - e1.getY() > FLING_THRESHOLD_VELOCITY);
}
}
================================================
FILE: magnet/src/main/java/com/premnirmal/Magnet/IconCallback.java
================================================
package com.premnirmal.Magnet;
import android.view.View;
/**
* Created by prem on 7/20/14.
* Desc: Interface that gives the user callbacks for when the MagnetIcon has been interacted with.
*/
public interface IconCallback {
/**
* Insert code for what to do when the icon has been flung away
*/
public void onFlingAway();
/**
* Callback for when the icon has been dragged by the user
* @param x x coordiante on the screen in pixels
* @param y y coordinate on the screen in pixels
*/
public void onMove(float x, float y);
/**
* Callback for when the icon has been clicked. Perform any action such as launch your app,
* or show a menu, etc.
* @param icon the view holding the icon. Get context from this view.
* @param iconXPose the x coordinate of the icon in pixels
* @param iconYPose the y coordiante of the icon in pixels
*/
public void onIconClick(View icon, float iconXPose, float iconYPose);
/**
* Callback for when the icon has been destroyed. Usually you should stop your service in this.
*/
public void onIconDestroyed();
}
================================================
FILE: magnet/src/main/java/com/premnirmal/Magnet/Magnet.java
================================================
package com.premnirmal.Magnet;
import android.content.Context;
import android.graphics.PixelFormat;
import android.os.Handler;
import android.os.Looper;
import android.util.DisplayMetrics;
import android.view.GestureDetector;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.RelativeLayout;
/**
* Created by prem on 7/20/14.
* Desc: Class holding the Magnet Icon, and performing touchEvents on the view.
*/
public class Magnet implements View.OnTouchListener {
protected static final int TOUCH_TIME_THRESHOLD = 200;
protected View mIconView;
protected RemoveView mRemoveView;
protected WindowManager mWindowManager;
protected WindowManager.LayoutParams mLayoutParams;
protected Context mContext;
protected GestureDetector mGestureDetector;
protected boolean shouldStickToWall = true;
protected boolean shouldFlingAway = true;
protected IconCallback mListener;
protected MoveAnimator mAnimator;
protected long lastTouchDown;
protected float lastXPose, lastYPose;
protected boolean isBeingDragged = false;
protected int mWidth, mHeight;
protected int mInitialX = -1, mInitialY = -1;
/**
* Builder class to create your {@link Magnet}
*/
public static class Builder {
Magnet magnet;
public Builder(Context context) {
magnet = new Magnet(context);
}
/**
* The Icon must have a view, provide a view or a layout using {@link #setIconView(int)}
* @param iconView the view representing the icon
* @return
*/
public Builder setIconView(View iconView) {
magnet.mIconView = iconView;
magnet.mIconView.setOnTouchListener(magnet);
return this;
}
/**
* Use an xml layout to provide the button view
* @param iconViewRes the layout id of the icon
* @return
*/
public Builder setIconView(int iconViewRes) {
magnet.mIconView = LayoutInflater.from(magnet.mContext).inflate(iconViewRes, null);
magnet.mIconView.setOnTouchListener(magnet);
return this;
}
/**
* whether your magnet sticks to the edge of your screen when you release it
* @param shouldStick
* @return
*/
public Builder setShouldStickToWall(boolean shouldStick) {
magnet.shouldStickToWall = shouldStick;
return this;
}
/**
* whether you can fling away your Magnet towards the bottom of the screen
* @param shoudlFling
* @return
*/
public Builder setShouldFlingAway(boolean shoudlFling) {
magnet.shouldFlingAway = shoudlFling;
return this;
}
/**
* Callback for when the icon moves, or when it isis flung away and destroyed
* @param callback
* @return
*/
public Builder setIconCallback(IconCallback callback) {
magnet.mListener = callback;
return this;
}
/**
*
* @param shouldBeResponsive
* @return
*/
public Builder setRemoveIconShouldBeResponsive(boolean shouldBeResponsive) {
magnet.mRemoveView.shouldBeResponsive = shouldBeResponsive;
return this;
}
/**
* you can set a custom remove icon or use the default one
* @param removeIconResId
* @return
*/
public Builder setRemoveIconResId(int removeIconResId) {
magnet.mRemoveView.setIconResId(removeIconResId);
return this;
}
/**
* you can set a custom remove icon shadow or use the default one
* @param shadow
* @return
*/
public Builder setRemoveIconShadow(int shadow) {
magnet.mRemoveView.setShadowBG(shadow);
return this;
}
/**
* Set the initial coordinates of the magnet
* @param x
* @param y
* @return
*/
public Builder setInitialPosition(int x, int y) {
magnet.mInitialX = x;
magnet.mInitialY = y;
return this;
}
public Magnet build() {
if(magnet.mIconView == null) {
throw new NullPointerException("Magnet view is null! Must set a view for the magnet!");
}
return magnet;
}
}
protected Magnet(Context context) {
mContext = context;
mGestureDetector = new GestureDetector(context, new FlingListener());
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
mAnimator = new MoveAnimator();
mRemoveView = new RemoveView(context);
}
/**
* Show the Magnet i.e. add it to the Window
*/
public void show() {
addToWindow(mIconView);
updateSize();
if(mInitialX != -1 || mInitialY != -1) {
setPosition(mInitialX, mInitialY, true);
} else {
goToWall();
}
}
protected void addToWindow(View icon) {
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
PixelFormat.TRANSLUCENT
);
try {
mWindowManager.addView(icon, mLayoutParams = params);
} catch(Exception e) {
// ignore
// if targetSdkVersion >=23 addView maybe crash here.
// http://developer.android.com/intl/zh-cn/reference/android/Manifest.permission.html
}
}
protected void updateSize() {
final DisplayMetrics metrics = new DisplayMetrics();
mWindowManager.getDefaultDisplay().getMetrics(metrics);
mWidth = (metrics.widthPixels - mIconView.getWidth()) / 2;
mHeight = (metrics.heightPixels - mIconView.getHeight()) / 2;
}
@Override
public boolean onTouch(View view, MotionEvent event) {
boolean eaten = false;
if (shouldFlingAway) {
eaten = mGestureDetector.onTouchEvent(event);
}
if (eaten) {
flingAway();
} else {
float x = event.getRawX();
float y = event.getRawY();
int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN) {
showRemoveView();
lastTouchDown = System.currentTimeMillis();
mAnimator.stop();
updateSize();
isBeingDragged = true;
} else if (action == MotionEvent.ACTION_UP) {
if (System.currentTimeMillis() - lastTouchDown < TOUCH_TIME_THRESHOLD) {
if (mListener != null) {
mListener.onIconClick(mIconView, x, y);
}
}
hideRemoveView();
isBeingDragged = false;
eaten = false;
goToWall();
} else if (action == MotionEvent.ACTION_MOVE) {
if (isBeingDragged) {
move(x - lastXPose, y - lastYPose);
}
}
lastXPose = x;
lastYPose = y;
}
return eaten;
}
protected void flingAway() {
if (shouldFlingAway) {
int y = mContext.getResources().getDisplayMetrics().heightPixels / 2;
int x = 0;
mAnimator.start(x, y);
if (mListener != null) {
mListener.onFlingAway();
}
destroy();
}
}
protected void showRemoveView() {
if (mRemoveView != null && shouldFlingAway) {
mRemoveView.show();
}
}
protected void hideRemoveView() {
if (mRemoveView != null && shouldFlingAway) {
mRemoveView.hide();
}
}
protected void goToWall() {
if (shouldStickToWall) {
float nearestXWall = mLayoutParams.x >= 0 ? mWidth : -mWidth;
float nearestYWall = mLayoutParams.y > 0 ? mHeight : -mHeight;
if (Math.abs(mLayoutParams.x - nearestXWall) < Math.abs(mLayoutParams.y - nearestYWall)) {
mAnimator.start(nearestXWall, mLayoutParams.y);
} else {
mAnimator.start(mLayoutParams.x, nearestYWall);
}
}
}
protected void move(float deltaX, float deltaY) {
mLayoutParams.x += deltaX;
mLayoutParams.y += deltaY;
if (mRemoveView != null && shouldFlingAway) {
mRemoveView.onMove(mLayoutParams.x, mLayoutParams.y);
}
mWindowManager.updateViewLayout(mIconView, mLayoutParams);
if (mListener != null) {
mListener.onMove(mLayoutParams.x, mLayoutParams.y);
}
if (shouldFlingAway && !isBeingDragged && Math.abs(mLayoutParams.x) < 50
&& Math.abs(mLayoutParams.y - (mContext.getResources().getDisplayMetrics().heightPixels / 2)) < 250) {
flingAway();
}
}
/**
* Destroys the magnet - removes the view from the WindowManager and calls
* {@link IconCallback#onIconDestroyed()}
*/
public void destroy() {
mWindowManager.removeView(mIconView);
if (mRemoveView != null) {
mRemoveView.destroy();
}
if (mListener != null) {
mListener.onIconDestroyed();
}
mContext = null;
}
/**
* Set the position of the Magnet.
* Note: must be called **after** {@link #show()} is called.
* This will call {@link IconCallback#onMove(float, float)} on your listener
* @param x the x position
* @param y the y position
* @param animate whether the Magnet should animate to that position. If false the Magnet
* will simply just set its coordinates to the given position
*/
public void setPosition(int x, int y, boolean animate) {
if(animate) {
mAnimator.start(x, y);
} else {
mLayoutParams.x = x;
mLayoutParams.y = y;
mWindowManager.updateViewLayout(mIconView, mLayoutParams);
if (mListener != null) {
mListener.onMove(mLayoutParams.x, mLayoutParams.y);
}
}
}
protected class MoveAnimator implements Runnable {
protected Handler handler = new Handler(Looper.getMainLooper());
protected float destinationX;
protected float destinationY;
protected long startingTime;
protected void start(float x, float y) {
this.destinationX = x;
this.destinationY = y;
startingTime = System.currentTimeMillis();
handler.post(this);
}
@Override
public void run() {
if (mIconView != null && mIconView.getParent() != null) {
float progress = Math.min(1, (System.currentTimeMillis() - startingTime) / 400f);
float deltaX = (destinationX - mLayoutParams.x) * progress;
float deltaY = (destinationY - mLayoutParams.y) * progress;
move(deltaX, deltaY);
if (progress < 1) {
handler.post(this);
}
}
}
protected void stop() {
handler.removeCallbacks(this);
}
}
}
================================================
FILE: magnet/src/main/java/com/premnirmal/Magnet/RemoveView.java
================================================
package com.premnirmal.Magnet;
import android.content.Context;
import android.graphics.PixelFormat;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.widget.ImageView;
import android.widget.RelativeLayout;
/**
* Created by prem on 7/20/14.
* ViewHolder for the remove Icon.
*/
class RemoveView {
View mLayout;
View mButton;
View mShadow;
ImageView mButtonImage;
private WindowManager mWindowManager;
private SimpleAnimator mShowAnim;
private SimpleAnimator mHideAnim;
private SimpleAnimator mShadowFadeOut;
private SimpleAnimator mShadowFadeIn;
private final int buttonBottomPadding;
boolean shouldBeResponsive = true;
RemoveView(Context context) {
mLayout = LayoutInflater.from(context).inflate(R.layout.x_button_holder, null);
mButton = mLayout.findViewById(R.id.xButton);
mButtonImage = (ImageView) mLayout.findViewById(R.id.xButtonImg);
mButtonImage.setImageResource(R.drawable.trash);
buttonBottomPadding = mButton.getPaddingBottom();
mShadow = mLayout.findViewById(R.id.shadow);
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
addToWindow(mLayout);
mShowAnim = new SimpleAnimator(mButton, R.anim.slide_up);
mHideAnim = new SimpleAnimator(mButton, R.anim.slide_down);
mShadowFadeIn = new SimpleAnimator(mShadow, android.R.anim.fade_in);
mShadowFadeOut = new SimpleAnimator(mShadow, android.R.anim.fade_out);
hide();
}
void setIconResId(int id) {
mButtonImage.setImageResource(id);
}
void setShadowBG(int shadowBG) {
mShadow.setBackgroundResource(shadowBG);
}
void show() {
if (mLayout != null && mLayout.getParent() == null) {
addToWindow(mLayout);
}
mShadowFadeIn.startAnimation();
mShowAnim.startAnimation();
}
void hide() {
mShadowFadeOut.startAnimation();
mHideAnim.startAnimation(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
if (mLayout != null && mLayout.getParent() != null) {
mWindowManager.removeView(mLayout);
}
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
}
void onMove(final float x, final float y) {
if (shouldBeResponsive) {
final int xTransformed = (int) Math.abs(x * 100 / (mButton.getContext().getResources().getDisplayMetrics().widthPixels / 2));
final int bottomPadding = buttonBottomPadding - (xTransformed / 5);
if (x < 0) {
mButton.setPadding(0, 0, xTransformed, bottomPadding);
} else {
mButton.setPadding(xTransformed, 0, 0, bottomPadding);
}
}
}
void destroy() {
if (mLayout != null && mLayout.getParent() != null) {
mWindowManager.removeView(mLayout);
}
mLayout = null;
mWindowManager = null;
}
private void addToWindow(View layout) {
// WindowManager.LayoutParams params = new WindowManager.LayoutParams(
// WindowManager.LayoutParams.MATCH_PARENT,
// WindowManager.LayoutParams.MATCH_PARENT,
// WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
// WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
// PixelFormat.TRANSLUCENT
// );
WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
mParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
mParams.width = RelativeLayout.LayoutParams.MATCH_PARENT;
mParams.height = RelativeLayout.LayoutParams.MATCH_PARENT;
mParams.gravity = Gravity.CENTER;
mParams.format = PixelFormat.TRANSLUCENT;
mParams.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
| WindowManager.LayoutParams.FLAG_DIM_BEHIND
| WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
mParams.dimAmount = 0.4f;
try {
mWindowManager.addView(layout, mParams);
} catch (Exception e) {
e.printStackTrace();
}
}
}
================================================
FILE: magnet/src/main/java/com/premnirmal/Magnet/SimpleAnimator.java
================================================
package com.premnirmal.Magnet;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import java.lang.ref.WeakReference;
/**
* Created by prem on 7/20/14.
* A class that takes care of animating a view in a simple way.
*/
class SimpleAnimator {
private WeakReference<View> mViewRef;
private int animation;
public SimpleAnimator(View view, int anim) {
this.animation = anim;
this.mViewRef = new WeakReference<View>(view);
}
public void startAnimation() {
startAnimation(null);
}
public void startAnimation(Animation.AnimationListener listener) {
mViewRef.get().clearAnimation();
Animation anim = AnimationUtils.loadAnimation(mViewRef.get().getContext(), animation);
if(listener != null) {
anim.setAnimationListener(listener);
}
anim.setFillAfter(true);
mViewRef.get().startAnimation(anim);
}
}
================================================
FILE: magnet/src/main/res/anim/slide_down.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromYDelta="0%"
android:toYDelta="100%"
android:duration="@android:integer/config_shortAnimTime"/>
</set>
================================================
FILE: magnet/src/main/res/anim/slide_up.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromYDelta="100%"
android:toYDelta="0%"
android:duration="@android:integer/config_shortAnimTime"/>
</set>
================================================
FILE: magnet/src/main/res/drawable/bottom_shadow.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<gradient android:startColor="#05000000"
android:endColor="#0096ff"
android:type="linear"
android:angle="270"
/>
<corners android:radius="2dp" />
</shape>
</item>
</selector>
================================================
FILE: magnet/src/main/res/layout/x_button_holder.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:background="@color/white"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:layout_width="match_parent"
android:layout_height="80dp"
android:id="@+id/shadow"
android:background="@drawable/bottom_shadow"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"/>
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/default_padding_bottom"
android:id="@+id/xButton"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true">
<ImageView
android:layout_width="60dp"
android:layout_height="60dp"
android:id="@+id/xButtonImg"/>
</FrameLayout>
</RelativeLayout>
================================================
FILE: magnet/src/main/res/menu/paranormal.xml
================================================
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".ParanormalActivity" >
<item android:id="@+id/action_settings"
android:title="@string/action_settings"
android:orderInCategory="100"
android:showAsAction="never" />
</menu>
================================================
FILE: magnet/src/main/res/values/dimens.xml
================================================
<resources>
<dimen name="default_padding_bottom">55dp</dimen>
</resources>
================================================
FILE: magnet/src/main/res/values/strings.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Magnet</string>
<string name="hello_world">Hello world!</string>
<string name="action_settings">Settings</string>
</resources>
================================================
FILE: magnet/src/main/res/values/styles.xml
================================================
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
<!-- Customize your theme here. -->
</style>
</resources>
================================================
FILE: settings.gradle
================================================
include ':app', ':magnet'
gitextract_vdzligds/ ├── .gitignore ├── README.md ├── app/ │ ├── build.gradle │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ ├── java/ │ │ └── com/ │ │ └── cundong/ │ │ └── memory/ │ │ ├── App.java │ │ ├── Constants.java │ │ ├── MainActivity.java │ │ ├── demo/ │ │ │ ├── right/ │ │ │ │ ├── AsyncTaskOutOfMemoryActivity.java │ │ │ │ ├── HandlerOutOfMemoryActivity.java │ │ │ │ ├── OneStaticOutOfMemoryActivity.java │ │ │ │ ├── OneThreadOutOfMemoryActivity.java │ │ │ │ ├── TwoStaticOutOfMemoryActivity.java │ │ │ │ └── TwoThreadOutOfMemoryActivity.java │ │ │ └── wrong/ │ │ │ ├── AsyncTaskOutOfMemoryActivity.java │ │ │ ├── HandlerOutOfMemoryActivity.java │ │ │ ├── MemoryChurnActivity.java │ │ │ ├── StaticOutOfMemoryActivity.java │ │ │ └── ThreadOutOfMemoryActivity.java │ │ ├── service/ │ │ │ ├── CoreService.java │ │ │ └── EmptyService.java │ │ └── util/ │ │ ├── BitmapUtils.java │ │ ├── Logger.java │ │ ├── MemoryUtil.java │ │ └── ViewUtils.java │ └── res/ │ ├── anim/ │ │ ├── slide_down.xml │ │ └── slide_up.xml │ ├── layout/ │ │ ├── activity_demo.xml │ │ ├── activity_main.xml │ │ ├── activity_outofmemory_list.xml │ │ ├── activity_test.xml │ │ └── float_view.xml │ ├── menu/ │ │ └── main.xml │ ├── values/ │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ ├── values-v11/ │ │ └── styles.xml │ ├── values-v14/ │ │ └── styles.xml │ └── values-w820dp/ │ └── dimens.xml ├── build.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── magnet/ │ ├── .gitignore │ ├── build.gradle │ ├── gradle.properties │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── com/ │ │ └── premnirmal/ │ │ └── Magnet/ │ │ └── ApplicationTest.java │ └── main/ │ ├── AndroidManifest.xml │ ├── java/ │ │ └── com/ │ │ └── premnirmal/ │ │ └── Magnet/ │ │ ├── FlingListener.java │ │ ├── IconCallback.java │ │ ├── Magnet.java │ │ ├── RemoveView.java │ │ └── SimpleAnimator.java │ └── res/ │ ├── anim/ │ │ ├── slide_down.xml │ │ └── slide_up.xml │ ├── drawable/ │ │ └── bottom_shadow.xml │ ├── layout/ │ │ └── x_button_holder.xml │ ├── menu/ │ │ └── paranormal.xml │ └── values/ │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml └── settings.gradle
SYMBOL INDEX (150 symbols across 26 files)
FILE: app/src/main/java/com/cundong/memory/App.java
class App (line 6) | public class App extends Application {
method getAppContext (line 10) | public static Context getAppContext() {
method onCreate (line 14) | @Override
FILE: app/src/main/java/com/cundong/memory/Constants.java
class Constants (line 5) | public class Constants {
FILE: app/src/main/java/com/cundong/memory/MainActivity.java
class MainActivity (line 22) | public class MainActivity extends AppCompatActivity {
method onCreate (line 28) | @Override
method try2StartMonitor (line 81) | @TargetApi(Build.VERSION_CODES.M)
method onResume (line 98) | @Override
method onActivityResult (line 108) | @TargetApi(Build.VERSION_CODES.M)
method isServiceRunning (line 130) | private boolean isServiceRunning(String className) {
FILE: app/src/main/java/com/cundong/memory/demo/right/AsyncTaskOutOfMemoryActivity.java
class AsyncTaskOutOfMemoryActivity (line 20) | public class AsyncTaskOutOfMemoryActivity extends Activity {
method onCreate (line 24) | @Override
class BitmapWorkerTask (line 35) | class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
method BitmapWorkerTask (line 39) | public BitmapWorkerTask(ImageView imageView) {
method doInBackground (line 45) | @Override
method onPostExecute (line 52) | @Override
FILE: app/src/main/java/com/cundong/memory/demo/right/HandlerOutOfMemoryActivity.java
class HandlerOutOfMemoryActivity (line 18) | public class HandlerOutOfMemoryActivity extends Activity {
class MyHandler (line 22) | private static class MyHandler extends Handler {
method MyHandler (line 25) | public MyHandler(HandlerOutOfMemoryActivity activity) {
method handleMessage (line 29) | @Override
method run (line 39) | @Override
method onCreate (line 45) | @Override
FILE: app/src/main/java/com/cundong/memory/demo/right/OneStaticOutOfMemoryActivity.java
class OneStaticOutOfMemoryActivity (line 16) | public class OneStaticOutOfMemoryActivity extends Activity {
method onCreate (line 20) | @Override
FILE: app/src/main/java/com/cundong/memory/demo/right/OneThreadOutOfMemoryActivity.java
class OneThreadOutOfMemoryActivity (line 19) | public class OneThreadOutOfMemoryActivity extends Activity {
method onCreate (line 23) | @Override
method releaseZip (line 33) | private void releaseZip(WeakReference<Context> context){
class MyThread (line 37) | private class MyThread extends Thread {
method run (line 38) | @Override
FILE: app/src/main/java/com/cundong/memory/demo/right/TwoStaticOutOfMemoryActivity.java
class TwoStaticOutOfMemoryActivity (line 17) | public class TwoStaticOutOfMemoryActivity extends Activity {
method onCreate (line 21) | @Override
method onDestroy (line 37) | @Override
FILE: app/src/main/java/com/cundong/memory/demo/right/TwoThreadOutOfMemoryActivity.java
class TwoThreadOutOfMemoryActivity (line 16) | public class TwoThreadOutOfMemoryActivity extends Activity {
method onCreate (line 18) | @Override
method releaseZip (line 26) | private static void releaseZip(Context context){
class MyThread (line 30) | private static class MyThread extends Thread {
method MyThread (line 34) | public MyThread(TwoThreadOutOfMemoryActivity activity) {
method run (line 38) | @Override
FILE: app/src/main/java/com/cundong/memory/demo/wrong/AsyncTaskOutOfMemoryActivity.java
class AsyncTaskOutOfMemoryActivity (line 31) | public class AsyncTaskOutOfMemoryActivity extends Activity {
method onCreate (line 35) | @Override
class BitmapWorkerTask (line 46) | class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
method doInBackground (line 51) | @Override
method onPostExecute (line 58) | @Override
FILE: app/src/main/java/com/cundong/memory/demo/wrong/HandlerOutOfMemoryActivity.java
class HandlerOutOfMemoryActivity (line 31) | public class HandlerOutOfMemoryActivity extends Activity {
method handleMessage (line 35) | @Override
method onCreate (line 41) | @Override
FILE: app/src/main/java/com/cundong/memory/demo/wrong/MemoryChurnActivity.java
class MemoryChurnActivity (line 24) | public class MemoryChurnActivity extends Activity {
method onCreate (line 28) | @Override
FILE: app/src/main/java/com/cundong/memory/demo/wrong/StaticOutOfMemoryActivity.java
class StaticOutOfMemoryActivity (line 37) | public class StaticOutOfMemoryActivity extends Activity {
method onCreate (line 41) | @Override
FILE: app/src/main/java/com/cundong/memory/demo/wrong/ThreadOutOfMemoryActivity.java
class ThreadOutOfMemoryActivity (line 32) | public class ThreadOutOfMemoryActivity extends Activity {
method onCreate (line 36) | @Override
method releaseZip (line 46) | private void releaseZip(WeakReference<Context> context){
class MyThread (line 50) | private class MyThread extends Thread {
method run (line 51) | @Override
FILE: app/src/main/java/com/cundong/memory/service/CoreService.java
class CoreService (line 39) | public class CoreService extends Service implements IconCallback {
method onCreate (line 55) | @Override
method getIconView (line 100) | private View getIconView() {
method onBind (line 105) | @Override
method onStartCommand (line 110) | @Override
method onDestroy (line 127) | @Override
class RefreshTask (line 136) | class RefreshTask extends TimerTask {
method run (line 138) | @Override
method onFlingAway (line 173) | @Override
method onMove (line 178) | @Override
method onIconClick (line 183) | @Override
method onIconDestroyed (line 192) | @Override
class InnerHandler (line 198) | private static class InnerHandler extends Handler {
method InnerHandler (line 202) | public InnerHandler(CoreService service) {
method handleMessage (line 206) | @Override
FILE: app/src/main/java/com/cundong/memory/service/EmptyService.java
class EmptyService (line 10) | public class EmptyService extends Service {
method EmptyService (line 12) | public EmptyService() {
method onBind (line 15) | @Override
method onCreate (line 20) | @Override
FILE: app/src/main/java/com/cundong/memory/util/BitmapUtils.java
class BitmapUtils (line 7) | public class BitmapUtils {
method decodeSampledBitmapFromResource (line 9) | public static Bitmap decodeSampledBitmapFromResource(Resources res, in...
method calculateInSampleSize (line 25) | public static int calculateInSampleSize(
FILE: app/src/main/java/com/cundong/memory/util/Logger.java
class Logger (line 5) | public class Logger {
method Logger (line 20) | private Logger() {
method getLogger (line 24) | public static Logger getLogger() {
method getFunctionName (line 28) | private String getFunctionName() {
method createMessage (line 54) | private String createMessage(String msg) {
method i (line 63) | public void i(String msg) {
method v (line 73) | public void v(String msg) {
method d (line 83) | public void d(String msg) {
method e (line 93) | public void e(String msg) {
method error (line 103) | public void error(Exception e) {
method w (line 129) | public void w(String msg) {
method setTag (line 136) | public void setTag(String tag) {
method setDebug (line 143) | public static void setDebug(boolean d) {
method isDebug (line 147) | public static boolean isDebug() {
FILE: app/src/main/java/com/cundong/memory/util/MemoryUtil.java
class MemoryUtil (line 29) | public class MemoryUtil {
method getTotalPss (line 37) | public static HashMap<String, Long> getTotalPss(ArrayList<String> proc...
method getTotalPss (line 86) | public static long getTotalPss(String processName) {
method getUsedPercentValue (line 131) | public static String getUsedPercentValue() {
method getAvailableMemory (line 156) | public static long getAvailableMemory() {
method clearMemory (line 167) | public static void clearMemory(Context context) {
FILE: app/src/main/java/com/cundong/memory/util/ViewUtils.java
class ViewUtils (line 6) | public class ViewUtils {
method unbindDrawables (line 8) | public static void unbindDrawables(View view) {
FILE: magnet/src/androidTest/java/com/premnirmal/Magnet/ApplicationTest.java
class ApplicationTest (line 9) | public class ApplicationTest extends ApplicationTestCase<Application> {
method ApplicationTest (line 10) | public ApplicationTest() {
FILE: magnet/src/main/java/com/premnirmal/Magnet/FlingListener.java
class FlingListener (line 9) | class FlingListener extends GestureDetector.SimpleOnGestureListener {
method onFling (line 13) | @Override
FILE: magnet/src/main/java/com/premnirmal/Magnet/IconCallback.java
type IconCallback (line 9) | public interface IconCallback {
method onFlingAway (line 14) | public void onFlingAway();
method onMove (line 21) | public void onMove(float x, float y);
method onIconClick (line 30) | public void onIconClick(View icon, float iconXPose, float iconYPose);
method onIconDestroyed (line 36) | public void onIconDestroyed();
FILE: magnet/src/main/java/com/premnirmal/Magnet/Magnet.java
class Magnet (line 20) | public class Magnet implements View.OnTouchListener {
class Builder (line 44) | public static class Builder {
method Builder (line 48) | public Builder(Context context) {
method setIconView (line 57) | public Builder setIconView(View iconView) {
method setIconView (line 68) | public Builder setIconView(int iconViewRes) {
method setShouldStickToWall (line 79) | public Builder setShouldStickToWall(boolean shouldStick) {
method setShouldFlingAway (line 89) | public Builder setShouldFlingAway(boolean shoudlFling) {
method setIconCallback (line 99) | public Builder setIconCallback(IconCallback callback) {
method setRemoveIconShouldBeResponsive (line 109) | public Builder setRemoveIconShouldBeResponsive(boolean shouldBeRespo...
method setRemoveIconResId (line 119) | public Builder setRemoveIconResId(int removeIconResId) {
method setRemoveIconShadow (line 129) | public Builder setRemoveIconShadow(int shadow) {
method setInitialPosition (line 140) | public Builder setInitialPosition(int x, int y) {
method build (line 146) | public Magnet build() {
method Magnet (line 155) | protected Magnet(Context context) {
method show (line 166) | public void show() {
method addToWindow (line 176) | protected void addToWindow(View icon) {
method updateSize (line 195) | protected void updateSize() {
method onTouch (line 202) | @Override
method flingAway (line 242) | protected void flingAway() {
method showRemoveView (line 254) | protected void showRemoveView() {
method hideRemoveView (line 260) | protected void hideRemoveView() {
method goToWall (line 266) | protected void goToWall() {
method move (line 278) | protected void move(float deltaX, float deltaY) {
method destroy (line 298) | public void destroy() {
method setPosition (line 318) | public void setPosition(int x, int y, boolean animate) {
class MoveAnimator (line 331) | protected class MoveAnimator implements Runnable {
method start (line 338) | protected void start(float x, float y) {
method run (line 345) | @Override
method stop (line 358) | protected void stop() {
FILE: magnet/src/main/java/com/premnirmal/Magnet/RemoveView.java
class RemoveView (line 18) | class RemoveView {
method RemoveView (line 35) | RemoveView(Context context) {
method setIconResId (line 51) | void setIconResId(int id) {
method setShadowBG (line 55) | void setShadowBG(int shadowBG) {
method show (line 59) | void show() {
method hide (line 67) | void hide() {
method onMove (line 89) | void onMove(final float x, final float y) {
method destroy (line 101) | void destroy() {
method addToWindow (line 109) | private void addToWindow(View layout) {
FILE: magnet/src/main/java/com/premnirmal/Magnet/SimpleAnimator.java
class SimpleAnimator (line 13) | class SimpleAnimator {
method SimpleAnimator (line 18) | public SimpleAnimator(View view, int anim) {
method startAnimation (line 23) | public void startAnimation() {
method startAnimation (line 27) | public void startAnimation(Animation.AnimationListener listener) {
Condensed preview — 64 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (104K chars).
[
{
"path": ".gitignore",
"chars": 405,
"preview": "# built application files\n*.ap_\n\n# files for the dex VM\n*.dex\n\n# Java class files\n*.class\n\n# generated files\nbin/\ngen/\n\n"
},
{
"path": "README.md",
"chars": 5155,
"preview": "# MemoryMonitor \n\n一个给开发者使用的Android App内存清理、监控工具。 \n\n主要包括三部分内容:\n\n* 内存清理 \n\n通过内存清理可以模拟系统内存不足时对进程的回收。\n\n* Pss监控 \n\n通过内存监控可以监"
},
{
"path": "app/build.gradle",
"chars": 753,
"preview": "apply plugin: 'com.android.application'\n\nandroid {\n compileSdkVersion 23\n buildToolsVersion \"23.0.2\"\n\n defaultC"
},
{
"path": "app/src/main/AndroidManifest.xml",
"chars": 3118,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n package="
},
{
"path": "app/src/main/java/com/cundong/memory/App.java",
"chars": 349,
"preview": "package com.cundong.memory;\n\nimport android.app.Application;\nimport android.content.Context;\n\npublic class App extends A"
},
{
"path": "app/src/main/java/com/cundong/memory/Constants.java",
"chars": 1058,
"preview": "package com.cundong.memory;\n\nimport java.util.ArrayList;\n\npublic class Constants {\n\n public static final boolean SHOW"
},
{
"path": "app/src/main/java/com/cundong/memory/MainActivity.java",
"chars": 4827,
"preview": "package com.cundong.memory;\n\nimport android.annotation.TargetApi;\nimport android.app.ActivityManager;\nimport android.con"
},
{
"path": "app/src/main/java/com/cundong/memory/demo/right/AsyncTaskOutOfMemoryActivity.java",
"chars": 1758,
"preview": "package com.cundong.memory.demo.right;\n\nimport java.lang.ref.WeakReference;\n\nimport android.app.Activity;\nimport android"
},
{
"path": "app/src/main/java/com/cundong/memory/demo/right/HandlerOutOfMemoryActivity.java",
"chars": 1173,
"preview": "package com.cundong.memory.demo.right;\n\nimport java.lang.ref.WeakReference;\n\nimport android.app.Activity;\nimport android"
},
{
"path": "app/src/main/java/com/cundong/memory/demo/right/OneStaticOutOfMemoryActivity.java",
"chars": 740,
"preview": "package com.cundong.memory.demo.right;\n\nimport android.app.Activity;\nimport android.graphics.drawable.Drawable;\nimport a"
},
{
"path": "app/src/main/java/com/cundong/memory/demo/right/OneThreadOutOfMemoryActivity.java",
"chars": 934,
"preview": "package com.cundong.memory.demo.right;\n\nimport java.lang.ref.WeakReference;\n\nimport android.app.Activity;\nimport android"
},
{
"path": "app/src/main/java/com/cundong/memory/demo/right/TwoStaticOutOfMemoryActivity.java",
"chars": 962,
"preview": "package com.cundong.memory.demo.right;\n\nimport android.app.Activity;\nimport android.graphics.drawable.Drawable;\nimport a"
},
{
"path": "app/src/main/java/com/cundong/memory/demo/right/TwoThreadOutOfMemoryActivity.java",
"chars": 1007,
"preview": "package com.cundong.memory.demo.right;\n\nimport java.lang.ref.WeakReference;\n\nimport android.app.Activity;\nimport android"
},
{
"path": "app/src/main/java/com/cundong/memory/demo/wrong/AsyncTaskOutOfMemoryActivity.java",
"chars": 1482,
"preview": "package com.cundong.memory.demo.wrong;\n\nimport android.app.Activity;\nimport android.graphics.Bitmap;\nimport android.os.A"
},
{
"path": "app/src/main/java/com/cundong/memory/demo/wrong/HandlerOutOfMemoryActivity.java",
"chars": 1301,
"preview": "package com.cundong.memory.demo.wrong;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.os.Handler"
},
{
"path": "app/src/main/java/com/cundong/memory/demo/wrong/MemoryChurnActivity.java",
"chars": 2596,
"preview": "package com.cundong.memory.demo.wrong;\n\nimport android.app.Activity;\nimport android.graphics.Bitmap;\nimport android.grap"
},
{
"path": "app/src/main/java/com/cundong/memory/demo/wrong/StaticOutOfMemoryActivity.java",
"chars": 1345,
"preview": "package com.cundong.memory.demo.wrong;\n\nimport android.app.Activity;\nimport android.graphics.drawable.Drawable;\nimport a"
},
{
"path": "app/src/main/java/com/cundong/memory/demo/wrong/ThreadOutOfMemoryActivity.java",
"chars": 1109,
"preview": "package com.cundong.memory.demo.wrong;\n\nimport java.lang.ref.WeakReference;\n\nimport android.app.Activity;\nimport android"
},
{
"path": "app/src/main/java/com/cundong/memory/service/CoreService.java",
"chars": 5699,
"preview": "package com.cundong.memory.service;\n\nimport android.app.Service;\nimport android.content.Context;\nimport android.content."
},
{
"path": "app/src/main/java/com/cundong/memory/service/EmptyService.java",
"chars": 424,
"preview": "package com.cundong.memory.service;\n\nimport android.app.Service;\nimport android.content.Intent;\nimport android.os.IBinde"
},
{
"path": "app/src/main/java/com/cundong/memory/util/BitmapUtils.java",
"chars": 1695,
"preview": "package com.cundong.memory.util;\n\nimport android.content.res.Resources;\nimport android.graphics.Bitmap;\nimport android.g"
},
{
"path": "app/src/main/java/com/cundong/memory/util/Logger.java",
"chars": 2558,
"preview": "package com.cundong.memory.util;\n\nimport android.util.Log;\n\npublic class Logger {\n\n\t/**\n\t * log tag\n\t */\n\tprivate String"
},
{
"path": "app/src/main/java/com/cundong/memory/util/MemoryUtil.java",
"chars": 5900,
"preview": "package com.cundong.memory.util;\r\n\r\nimport java.io.BufferedReader;\r\nimport java.io.FileReader;\r\nimport java.io.IOExcepti"
},
{
"path": "app/src/main/java/com/cundong/memory/util/ViewUtils.java",
"chars": 540,
"preview": "package com.cundong.memory.util;\n\nimport android.view.View;\nimport android.view.ViewGroup;\n\npublic class ViewUtils {\n\n "
},
{
"path": "app/src/main/res/anim/slide_down.xml",
"chars": 257,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <translate\n"
},
{
"path": "app/src/main/res/anim/slide_up.xml",
"chars": 257,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <translate\n"
},
{
"path": "app/src/main/res/layout/activity_demo.xml",
"chars": 920,
"preview": "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmlns:tools=\"http://schemas.android.com/too"
},
{
"path": "app/src/main/res/layout/activity_main.xml",
"chars": 1444,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmln"
},
{
"path": "app/src/main/res/layout/activity_outofmemory_list.xml",
"chars": 726,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xm"
},
{
"path": "app/src/main/res/layout/activity_test.xml",
"chars": 680,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xm"
},
{
"path": "app/src/main/res/layout/float_view.xml",
"chars": 1514,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n andr"
},
{
"path": "app/src/main/res/menu/main.xml",
"chars": 359,
"preview": "<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmlns:tools=\"http://schemas.android.com/tools\"\n "
},
{
"path": "app/src/main/res/values/colors.xml",
"chars": 262,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <color name=\"colorPrimary\">#3F51B5</color>\n <color name=\"color"
},
{
"path": "app/src/main/res/values/dimens.xml",
"chars": 258,
"preview": "<resources>\n\n <!-- Default screen margins, per the Android Design guidelines. -->\n <dimen name=\"activity_horizonta"
},
{
"path": "app/src/main/res/values/strings.xml",
"chars": 619,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n <string name=\"app_name\">MemoryMonitor</string>\n <string name="
},
{
"path": "app/src/main/res/values/styles.xml",
"chars": 920,
"preview": "<resources>\n\n <!-- Base application theme. -->\n <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar"
},
{
"path": "app/src/main/res/values-v11/styles.xml",
"chars": 324,
"preview": "<resources>\n\n <!--\n Base application theme for API 11+. This theme completely replaces\n AppBaseTheme fr"
},
{
"path": "app/src/main/res/values-v14/styles.xml",
"chars": 381,
"preview": "<resources>\n\n <!--\n Base application theme for API 14+. This theme completely replaces\n AppBaseTheme fr"
},
{
"path": "app/src/main/res/values-w820dp/dimens.xml",
"chars": 373,
"preview": "<resources>\n\n <!--\n Example customization of dimensions originally defined in res/values/dimens.xml\n "
},
{
"path": "build.gradle",
"chars": 498,
"preview": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n r"
},
{
"path": "gradle/wrapper/gradle-wrapper.properties",
"chars": 230,
"preview": "#Wed Oct 21 11:34:03 PDT 2015\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_"
},
{
"path": "gradlew",
"chars": 5080,
"preview": "#!/usr/bin/env bash\n\n##############################################################################\n##\n## Gradle start "
},
{
"path": "gradlew.bat",
"chars": 2404,
"preview": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@r"
},
{
"path": "magnet/.gitignore",
"chars": 7,
"preview": "/build\n"
},
{
"path": "magnet/build.gradle",
"chars": 290,
"preview": "apply plugin: 'com.android.library'\n\nandroid {\n compileSdkVersion 23\n buildToolsVersion \"22.0.1\"\n \n lintOpti"
},
{
"path": "magnet/gradle.properties",
"chars": 57,
"preview": "POM_NAME=Magnet\nPOM_ARTIFACT_ID=library\nPOM_PACKAGING=aar"
},
{
"path": "magnet/proguard-rules.pro",
"chars": 656,
"preview": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /U"
},
{
"path": "magnet/src/androidTest/java/com/premnirmal/Magnet/ApplicationTest.java",
"chars": 352,
"preview": "package com.premnirmal.Magnet;\n\nimport android.app.Application;\nimport android.test.ApplicationTestCase;\n\n/**\n * <a href"
},
{
"path": "magnet/src/main/AndroidManifest.xml",
"chars": 120,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest\n package=\"com.premnirmal.Magnet\" >\n\n <application/>\n\n</manifest>\n"
},
{
"path": "magnet/src/main/java/com/premnirmal/Magnet/FlingListener.java",
"chars": 523,
"preview": "package com.premnirmal.Magnet;\n\nimport android.view.GestureDetector;\nimport android.view.MotionEvent;\n\n/**\n * Created by"
},
{
"path": "magnet/src/main/java/com/premnirmal/Magnet/IconCallback.java",
"chars": 1143,
"preview": "package com.premnirmal.Magnet;\n\nimport android.view.View;\n\n/**\n * Created by prem on 7/20/14.\n * Desc: Interface that gi"
},
{
"path": "magnet/src/main/java/com/premnirmal/Magnet/Magnet.java",
"chars": 11824,
"preview": "package com.premnirmal.Magnet;\n\nimport android.content.Context;\nimport android.graphics.PixelFormat;\nimport android.os.H"
},
{
"path": "magnet/src/main/java/com/premnirmal/Magnet/RemoveView.java",
"chars": 4617,
"preview": "package com.premnirmal.Magnet;\n\nimport android.content.Context;\nimport android.graphics.PixelFormat;\nimport android.util"
},
{
"path": "magnet/src/main/java/com/premnirmal/Magnet/SimpleAnimator.java",
"chars": 978,
"preview": "package com.premnirmal.Magnet;\n\nimport android.view.View;\nimport android.view.animation.Animation;\nimport android.view.a"
},
{
"path": "magnet/src/main/res/anim/slide_down.xml",
"chars": 257,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <translate\n"
},
{
"path": "magnet/src/main/res/anim/slide_up.xml",
"chars": 257,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <translate\n"
},
{
"path": "magnet/src/main/res/drawable/bottom_shadow.xml",
"chars": 436,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <item>\n"
},
{
"path": "magnet/src/main/res/layout/x_button_holder.xml",
"chars": 1135,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n "
},
{
"path": "magnet/src/main/res/menu/paranormal.xml",
"chars": 336,
"preview": "<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmlns:tools=\"http://schemas.android.com/tools\"\n "
},
{
"path": "magnet/src/main/res/values/dimens.xml",
"chars": 79,
"preview": "<resources>\n <dimen name=\"default_padding_bottom\">55dp</dimen>\n</resources>\n"
},
{
"path": "magnet/src/main/res/values/strings.xml",
"chars": 216,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n <string name=\"app_name\">Magnet</string>\n <string name=\"hello_"
},
{
"path": "magnet/src/main/res/values/styles.xml",
"chars": 197,
"preview": "<resources>\n\n <!-- Base application theme. -->\n <style name=\"AppTheme\" parent=\"android:Theme.Holo.Light.DarkAction"
},
{
"path": "settings.gradle",
"chars": 26,
"preview": "include ':app', ':magnet'\n"
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the cundong/MemoryMonitor GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 64 files (85.8 KB), approximately 24.4k tokens, and a symbol index with 150 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.