├── magnet
├── .gitignore
├── gradle.properties
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── dimens.xml
│ │ │ │ ├── styles.xml
│ │ │ │ └── strings.xml
│ │ │ ├── drawable-hdpi
│ │ │ │ ├── trash.png
│ │ │ │ └── ic_launcher.png
│ │ │ ├── drawable-mdpi
│ │ │ │ ├── trash.png
│ │ │ │ └── ic_launcher.png
│ │ │ ├── drawable-xhdpi
│ │ │ │ ├── trash.png
│ │ │ │ └── ic_launcher.png
│ │ │ ├── drawable-xxhdpi
│ │ │ │ ├── trash.png
│ │ │ │ └── ic_launcher.png
│ │ │ ├── anim
│ │ │ │ ├── slide_up.xml
│ │ │ │ └── slide_down.xml
│ │ │ ├── menu
│ │ │ │ └── paranormal.xml
│ │ │ ├── drawable
│ │ │ │ └── bottom_shadow.xml
│ │ │ └── layout
│ │ │ │ └── x_button_holder.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── premnirmal
│ │ │ └── Magnet
│ │ │ ├── FlingListener.java
│ │ │ ├── SimpleAnimator.java
│ │ │ ├── IconCallback.java
│ │ │ ├── RemoveView.java
│ │ │ └── Magnet.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── premnirmal
│ │ └── Magnet
│ │ └── ApplicationTest.java
├── build.gradle
└── proguard-rules.pro
├── settings.gradle
├── screenshot
├── app.png
└── total Pss.png
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── app
├── src
│ └── main
│ │ ├── res
│ │ ├── drawable-hdpi
│ │ │ ├── accept.png
│ │ │ ├── cancel.png
│ │ │ ├── download.png
│ │ │ ├── refresh.png
│ │ │ ├── large_bitmap.jpg
│ │ │ └── floating_layer_black.9.png
│ │ ├── drawable-xhdpi
│ │ │ ├── accept.png
│ │ │ ├── cancel.png
│ │ │ ├── refresh.png
│ │ │ ├── download.png
│ │ │ ├── large_img.jpg
│ │ │ ├── small_img.jpg
│ │ │ ├── sliding_explorer_icon.png
│ │ │ └── sillding_menu_mobile_clear.png
│ │ ├── drawable-xxhdpi
│ │ │ ├── accept.png
│ │ │ ├── cancel.png
│ │ │ ├── download.png
│ │ │ └── refresh.png
│ │ ├── mipmap-hdpi
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-mdpi
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-xhdpi
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-xxhdpi
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-xxxhdpi
│ │ │ └── ic_launcher.png
│ │ ├── anim
│ │ │ ├── slide_down.xml
│ │ │ └── slide_up.xml
│ │ ├── values
│ │ │ ├── colors.xml
│ │ │ ├── dimens.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ ├── values-v11
│ │ │ └── styles.xml
│ │ ├── menu
│ │ │ └── main.xml
│ │ ├── values-w820dp
│ │ │ └── dimens.xml
│ │ ├── values-v14
│ │ │ └── styles.xml
│ │ └── layout
│ │ │ ├── activity_test.xml
│ │ │ ├── activity_outofmemory_list.xml
│ │ │ ├── activity_demo.xml
│ │ │ ├── activity_main.xml
│ │ │ └── float_view.xml
│ │ ├── java
│ │ └── com
│ │ │ └── cundong
│ │ │ └── memory
│ │ │ ├── App.java
│ │ │ ├── service
│ │ │ ├── EmptyService.java
│ │ │ └── CoreService.java
│ │ │ ├── util
│ │ │ ├── ViewUtils.java
│ │ │ ├── BitmapUtils.java
│ │ │ ├── Logger.java
│ │ │ └── MemoryUtil.java
│ │ │ ├── demo
│ │ │ ├── right
│ │ │ │ ├── OneStaticOutOfMemoryActivity.java
│ │ │ │ ├── TwoStaticOutOfMemoryActivity.java
│ │ │ │ ├── OneThreadOutOfMemoryActivity.java
│ │ │ │ ├── TwoThreadOutOfMemoryActivity.java
│ │ │ │ ├── HandlerOutOfMemoryActivity.java
│ │ │ │ └── AsyncTaskOutOfMemoryActivity.java
│ │ │ └── wrong
│ │ │ │ ├── ThreadOutOfMemoryActivity.java
│ │ │ │ ├── HandlerOutOfMemoryActivity.java
│ │ │ │ ├── StaticOutOfMemoryActivity.java
│ │ │ │ ├── AsyncTaskOutOfMemoryActivity.java
│ │ │ │ └── MemoryChurnActivity.java
│ │ │ ├── Constants.java
│ │ │ └── MainActivity.java
│ │ └── AndroidManifest.xml
└── build.gradle
├── .gitignore
├── MemoryMonitor.iml
├── gradlew.bat
├── gradlew
└── README.md
/magnet/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':magnet'
2 |
--------------------------------------------------------------------------------
/magnet/gradle.properties:
--------------------------------------------------------------------------------
1 | POM_NAME=Magnet
2 | POM_ARTIFACT_ID=library
3 | POM_PACKAGING=aar
--------------------------------------------------------------------------------
/screenshot/app.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cundong/MemoryMonitor/HEAD/screenshot/app.png
--------------------------------------------------------------------------------
/screenshot/total Pss.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cundong/MemoryMonitor/HEAD/screenshot/total Pss.png
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cundong/MemoryMonitor/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/magnet/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 55dp
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/accept.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cundong/MemoryMonitor/HEAD/app/src/main/res/drawable-hdpi/accept.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/cancel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cundong/MemoryMonitor/HEAD/app/src/main/res/drawable-hdpi/cancel.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/download.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cundong/MemoryMonitor/HEAD/app/src/main/res/drawable-hdpi/download.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/refresh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cundong/MemoryMonitor/HEAD/app/src/main/res/drawable-hdpi/refresh.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/accept.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cundong/MemoryMonitor/HEAD/app/src/main/res/drawable-xhdpi/accept.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/cancel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cundong/MemoryMonitor/HEAD/app/src/main/res/drawable-xhdpi/cancel.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/refresh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cundong/MemoryMonitor/HEAD/app/src/main/res/drawable-xhdpi/refresh.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/accept.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cundong/MemoryMonitor/HEAD/app/src/main/res/drawable-xxhdpi/accept.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/cancel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cundong/MemoryMonitor/HEAD/app/src/main/res/drawable-xxhdpi/cancel.png
--------------------------------------------------------------------------------
/magnet/src/main/res/drawable-hdpi/trash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cundong/MemoryMonitor/HEAD/magnet/src/main/res/drawable-hdpi/trash.png
--------------------------------------------------------------------------------
/magnet/src/main/res/drawable-mdpi/trash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cundong/MemoryMonitor/HEAD/magnet/src/main/res/drawable-mdpi/trash.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/download.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cundong/MemoryMonitor/HEAD/app/src/main/res/drawable-xhdpi/download.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/large_img.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cundong/MemoryMonitor/HEAD/app/src/main/res/drawable-xhdpi/large_img.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/small_img.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cundong/MemoryMonitor/HEAD/app/src/main/res/drawable-xhdpi/small_img.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/download.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cundong/MemoryMonitor/HEAD/app/src/main/res/drawable-xxhdpi/download.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/refresh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cundong/MemoryMonitor/HEAD/app/src/main/res/drawable-xxhdpi/refresh.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cundong/MemoryMonitor/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cundong/MemoryMonitor/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cundong/MemoryMonitor/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/magnet/src/main/res/drawable-xhdpi/trash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cundong/MemoryMonitor/HEAD/magnet/src/main/res/drawable-xhdpi/trash.png
--------------------------------------------------------------------------------
/magnet/src/main/res/drawable-xxhdpi/trash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cundong/MemoryMonitor/HEAD/magnet/src/main/res/drawable-xxhdpi/trash.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/large_bitmap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cundong/MemoryMonitor/HEAD/app/src/main/res/drawable-hdpi/large_bitmap.jpg
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cundong/MemoryMonitor/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cundong/MemoryMonitor/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/magnet/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cundong/MemoryMonitor/HEAD/magnet/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/magnet/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cundong/MemoryMonitor/HEAD/magnet/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/magnet/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cundong/MemoryMonitor/HEAD/magnet/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/magnet/src/main/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cundong/MemoryMonitor/HEAD/magnet/src/main/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/floating_layer_black.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cundong/MemoryMonitor/HEAD/app/src/main/res/drawable-hdpi/floating_layer_black.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/sliding_explorer_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cundong/MemoryMonitor/HEAD/app/src/main/res/drawable-xhdpi/sliding_explorer_icon.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/sillding_menu_mobile_clear.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cundong/MemoryMonitor/HEAD/app/src/main/res/drawable-xhdpi/sillding_menu_mobile_clear.png
--------------------------------------------------------------------------------
/magnet/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/magnet/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/magnet/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Magnet
5 | Hello world!
6 | Settings
7 |
8 |
9 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Oct 21 11:34:03 PDT 2015
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
7 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/slide_down.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/slide_up.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
--------------------------------------------------------------------------------
/magnet/src/main/res/anim/slide_up.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 | #00000000
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 16dp
5 | 16dp
6 |
7 | 160dip
8 |
9 |
--------------------------------------------------------------------------------
/magnet/src/main/res/anim/slide_down.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
--------------------------------------------------------------------------------
/magnet/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 23
5 | buildToolsVersion "22.0.1"
6 |
7 | lintOptions {
8 | abortOnError false
9 | }
10 |
11 | defaultConfig {
12 | minSdkVersion 3
13 | targetSdkVersion 22
14 | versionCode 1
15 | versionName "1.0"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/magnet/src/main/res/menu/paranormal.xml:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values-v11/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/main.xml:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 | 64dp
9 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/values-v14/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/magnet/src/androidTest/java/com/premnirmal/Magnet/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.premnirmal.Magnet;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cundong/memory/App.java:
--------------------------------------------------------------------------------
1 | package com.cundong.memory;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 |
6 | public class App extends Application {
7 |
8 | private static Context mContext;
9 |
10 | public static Context getAppContext() {
11 | return mContext;
12 | }
13 |
14 | @Override
15 | public void onCreate() {
16 | super.onCreate();
17 | mContext = this;
18 | }
19 | }
--------------------------------------------------------------------------------
/magnet/src/main/res/drawable/bottom_shadow.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # built application files
2 | *.ap_
3 |
4 | # files for the dex VM
5 | *.dex
6 |
7 | # Java class files
8 | *.class
9 |
10 | # generated files
11 | bin/
12 | gen/
13 |
14 | # Local configuration file (sdk path, etc)
15 | local.properties
16 |
17 | # Proguard folder generated by Eclipse
18 | proguard/
19 |
20 | # Ignore gradle files
21 | .gradle/
22 | build/
23 |
24 | # Eclipse project files
25 | .classpath
26 | .project
27 | .settings/
28 |
29 | # Intellij project files
30 | *.iml
31 | *.ipr
32 | *.iws
33 | .idea/
34 |
35 | # Mac system files
36 | .DS_Store
--------------------------------------------------------------------------------
/app/src/main/java/com/cundong/memory/service/EmptyService.java:
--------------------------------------------------------------------------------
1 | package com.cundong.memory.service;
2 |
3 | import android.app.Service;
4 | import android.content.Intent;
5 | import android.os.IBinder;
6 |
7 | /**
8 | * 一个空Service,仅仅用于测试一个空进程在dalvik、art上占用多少Total Pss
9 | */
10 | public class EmptyService extends Service {
11 |
12 | public EmptyService() {
13 | }
14 |
15 | @Override
16 | public IBinder onBind(Intent intent) {
17 | return null;
18 | }
19 |
20 | @Override
21 | public void onCreate() {
22 | super.onCreate();
23 | }
24 | }
--------------------------------------------------------------------------------
/magnet/src/main/java/com/premnirmal/Magnet/FlingListener.java:
--------------------------------------------------------------------------------
1 | package com.premnirmal.Magnet;
2 |
3 | import android.view.GestureDetector;
4 | import android.view.MotionEvent;
5 |
6 | /**
7 | * Created by prem on 7/20/14.
8 | */
9 | class FlingListener extends GestureDetector.SimpleOnGestureListener {
10 |
11 | private static final float FLING_THRESHOLD_VELOCITY = 50f;
12 |
13 | @Override
14 | public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
15 | return (Math.abs(e2.getX() - e1.getX()) < FLING_THRESHOLD_VELOCITY && e2.getY() - e1.getY() > FLING_THRESHOLD_VELOCITY);
16 | }
17 |
18 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cundong/memory/util/ViewUtils.java:
--------------------------------------------------------------------------------
1 | package com.cundong.memory.util;
2 |
3 | import android.view.View;
4 | import android.view.ViewGroup;
5 |
6 | public class ViewUtils {
7 |
8 | public static void unbindDrawables(View view) {
9 | if (view.getBackground() != null) {
10 | view.getBackground().setCallback(null);
11 | }
12 |
13 | if (view instanceof ViewGroup) {
14 | for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
15 | unbindDrawables(((ViewGroup) view).getChildAt(i));
16 | }
17 | ((ViewGroup) view).removeAllViews();
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | MemoryMonitor
5 | Hello world!
6 | Settings
7 |
8 | 开始监控
9 | 停止监控
10 | 清理内存
11 | 内存使用:%1$s
12 | 可用:%1$sM
13 | Pss Total:\r\n
14 |
15 | 当前targets API level 23+, 必须手动设置“允许在其他应用上层显示”开启才行
16 |
--------------------------------------------------------------------------------
/magnet/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/prem/Documents/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_test.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_outofmemory_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
17 |
18 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 23
5 | buildToolsVersion "23.0.2"
6 |
7 | defaultConfig {
8 | applicationId "com.cundong.memory"
9 | minSdkVersion 14
10 | targetSdkVersion 23
11 | versionCode 2
12 | versionName "1.2"
13 | }
14 |
15 | buildTypes {
16 | release {
17 | minifyEnabled false
18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
19 | }
20 | }
21 | }
22 |
23 | dependencies {
24 | compile fileTree(dir: 'libs', include: ['*.jar'])
25 | compile project(':magnet')
26 | testCompile 'junit:junit:4.12'
27 | compile 'com.android.support:appcompat-v7:23.1.1'
28 | compile 'com.android.support:design:23.1.1'
29 | compile 'com.jaredrummler:android-processes:1.0.3'
30 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cundong/memory/demo/right/OneStaticOutOfMemoryActivity.java:
--------------------------------------------------------------------------------
1 | package com.cundong.memory.demo.right;
2 |
3 | import android.app.Activity;
4 | import android.graphics.drawable.Drawable;
5 | import android.os.Bundle;
6 | import android.widget.TextView;
7 |
8 | import com.cundong.memory.R;
9 |
10 | /**
11 | * 解决static变量引发的内存溢出
12 | *
13 | * 1.不用activity的context 而是用application的context
14 | *
15 | */
16 | public class OneStaticOutOfMemoryActivity extends Activity {
17 |
18 | private static Drawable sBackground;
19 |
20 | @Override
21 | protected void onCreate(Bundle state) {
22 | super.onCreate(state);
23 |
24 | TextView label = new TextView(this.getApplication());
25 | label.setText("Leaks are bad");
26 |
27 | if (sBackground == null) {
28 | sBackground = getResources().getDrawable(R.drawable.large_bitmap);
29 | }
30 |
31 | label.setBackgroundDrawable(sBackground);
32 |
33 | setContentView(label);
34 | }
35 | }
--------------------------------------------------------------------------------
/MemoryMonitor.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
15 |
16 |
17 |
18 |
19 |
20 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_demo.xml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
18 |
19 |
24 |
25 |
--------------------------------------------------------------------------------
/magnet/src/main/java/com/premnirmal/Magnet/SimpleAnimator.java:
--------------------------------------------------------------------------------
1 | package com.premnirmal.Magnet;
2 |
3 | import android.view.View;
4 | import android.view.animation.Animation;
5 | import android.view.animation.AnimationUtils;
6 |
7 | import java.lang.ref.WeakReference;
8 |
9 | /**
10 | * Created by prem on 7/20/14.
11 | * A class that takes care of animating a view in a simple way.
12 | */
13 | class SimpleAnimator {
14 |
15 | private WeakReference mViewRef;
16 | private int animation;
17 |
18 | public SimpleAnimator(View view, int anim) {
19 | this.animation = anim;
20 | this.mViewRef = new WeakReference(view);
21 | }
22 |
23 | public void startAnimation() {
24 | startAnimation(null);
25 | }
26 |
27 | public void startAnimation(Animation.AnimationListener listener) {
28 | mViewRef.get().clearAnimation();
29 | Animation anim = AnimationUtils.loadAnimation(mViewRef.get().getContext(), animation);
30 | if(listener != null) {
31 | anim.setAnimationListener(listener);
32 | }
33 | anim.setFillAfter(true);
34 | mViewRef.get().startAnimation(anim);
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/java/com/cundong/memory/demo/right/TwoStaticOutOfMemoryActivity.java:
--------------------------------------------------------------------------------
1 | package com.cundong.memory.demo.right;
2 |
3 | import android.app.Activity;
4 | import android.graphics.drawable.Drawable;
5 | import android.os.Bundle;
6 | import android.widget.TextView;
7 |
8 | import com.cundong.memory.util.ViewUtils;
9 | import com.cundong.memory.R;
10 |
11 | /**
12 | * 解决static变量引发的内存溢出
13 | *
14 | * 2.在 onDestroy() 方法中,解除 Activity 和 biamap(drawble)的绑定关系,从而去除bitmap对activity 引用,让系统适时的去回收
15 | *
16 | */
17 | public class TwoStaticOutOfMemoryActivity extends Activity {
18 |
19 | private static Drawable sBackground;
20 |
21 | @Override
22 | protected void onCreate(Bundle state) {
23 | super.onCreate(state);
24 |
25 | TextView label = new TextView(this);
26 | label.setText("Leaks are bad");
27 |
28 | if (sBackground == null) {
29 | sBackground = getResources().getDrawable(R.drawable.large_bitmap);
30 | }
31 |
32 | label.setBackgroundDrawable(sBackground);
33 |
34 | setContentView(label);
35 | }
36 |
37 | @Override
38 | protected void onDestroy() {
39 | super.onDestroy();
40 |
41 | ViewUtils.unbindDrawables(findViewById(android.R.id.content));
42 |
43 | System.gc();
44 | }
45 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cundong/memory/demo/right/OneThreadOutOfMemoryActivity.java:
--------------------------------------------------------------------------------
1 | package com.cundong.memory.demo.right;
2 |
3 | import java.lang.ref.WeakReference;
4 |
5 | import android.app.Activity;
6 | import android.content.Context;
7 | import android.os.Bundle;
8 |
9 | import com.cundong.memory.R;
10 |
11 | /**
12 | * 解决Thread引发的内存溢出
13 | *
14 | * 解决办法:
15 | *
16 | * 在线程内部采用弱引用保存Context引用
17 | *
18 | */
19 | public class OneThreadOutOfMemoryActivity extends Activity {
20 |
21 | private Context mContext;
22 |
23 | @Override
24 | protected void onCreate(Bundle state) {
25 | super.onCreate(state);
26 | this.setContentView(R.layout.activity_demo);
27 |
28 | mContext = this.getApplicationContext();
29 |
30 | new MyThread().start();
31 | }
32 |
33 | private void releaseZip(WeakReference context){
34 | /* do something */
35 | }
36 |
37 | private class MyThread extends Thread {
38 | @Override
39 | public void run() {
40 | super.run();
41 |
42 | try {
43 | Thread.sleep(1000 * 60 * 2);
44 | } catch (InterruptedException e) {
45 | e.printStackTrace();
46 | }
47 |
48 | /* do something */
49 | releaseZip(new WeakReference(mContext));
50 | }
51 | }
52 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cundong/memory/Constants.java:
--------------------------------------------------------------------------------
1 | package com.cundong.memory;
2 |
3 | import java.util.ArrayList;
4 |
5 | public class Constants {
6 |
7 | public static final boolean SHOW_MEMORY_CLEAR = false;
8 |
9 | // TODO
10 | /**
11 | * 此处,改为被监控 total Pss 的 processName 列表
12 | *
13 | * 微信:
14 | * com.tencent.mm
15 | * com.tencent.mm:TMAssistantDownloadSDKService
16 | * com.tencent.mm:push
17 | * com.tencent.mm:cuploader
18 | * com.tencent.mm:nospace
19 | * com.tencent.mm:tools
20 | * com.tencent.mm:sandbox
21 | * com.tencent.mm:exdevice
22 | */
23 | public static final ArrayList PROCESS_NAME_LIST = new ArrayList();
24 |
25 | static {
26 | PROCESS_NAME_LIST.add("com.tencent.mm");
27 | PROCESS_NAME_LIST.add("com.tencent.mm:TMAssistantDownloadSDKService");
28 | PROCESS_NAME_LIST.add("com.tencent.mm:push");
29 | PROCESS_NAME_LIST.add("com.tencent.mm:cuploader");
30 | PROCESS_NAME_LIST.add("com.tencent.mm:nospace");
31 | PROCESS_NAME_LIST.add("com.tencent.mm:tools");
32 | PROCESS_NAME_LIST.add("com.tencent.mm:sandbox");
33 | PROCESS_NAME_LIST.add("com.tencent.mm:exdevice");
34 | }
35 | }
--------------------------------------------------------------------------------
/magnet/src/main/res/layout/x_button_holder.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
18 |
19 |
20 |
27 |
28 |
32 |
33 |
--------------------------------------------------------------------------------
/app/src/main/java/com/cundong/memory/demo/right/TwoThreadOutOfMemoryActivity.java:
--------------------------------------------------------------------------------
1 | package com.cundong.memory.demo.right;
2 |
3 | import java.lang.ref.WeakReference;
4 |
5 | import android.app.Activity;
6 | import android.content.Context;
7 | import android.os.Bundle;
8 |
9 | import com.cundong.memory.R;
10 |
11 | /**
12 | * 解决Thread引发的内存溢出
13 | *
14 | * 1.MyThread改为静态内部类
15 | */
16 | public class TwoThreadOutOfMemoryActivity extends Activity {
17 |
18 | @Override
19 | protected void onCreate(Bundle state) {
20 | super.onCreate(state);
21 | this.setContentView(R.layout.activity_demo);
22 |
23 | new MyThread(this).start();
24 | }
25 |
26 | private static void releaseZip(Context context){
27 | /* do something */
28 | }
29 |
30 | private static class MyThread extends Thread {
31 |
32 | private final WeakReference mActivity;
33 |
34 | public MyThread(TwoThreadOutOfMemoryActivity activity) {
35 | mActivity = new WeakReference<>(activity);
36 | }
37 |
38 | @Override
39 | public void run() {
40 | super.run();
41 |
42 | try {
43 | Thread.sleep(1000 * 60 * 2);
44 | } catch (InterruptedException e) {
45 | e.printStackTrace();
46 | }
47 |
48 | /* do something */
49 | releaseZip(mActivity.get());
50 | }
51 | }
52 | }
--------------------------------------------------------------------------------
/magnet/src/main/java/com/premnirmal/Magnet/IconCallback.java:
--------------------------------------------------------------------------------
1 | package com.premnirmal.Magnet;
2 |
3 | import android.view.View;
4 |
5 | /**
6 | * Created by prem on 7/20/14.
7 | * Desc: Interface that gives the user callbacks for when the MagnetIcon has been interacted with.
8 | */
9 | public interface IconCallback {
10 |
11 | /**
12 | * Insert code for what to do when the icon has been flung away
13 | */
14 | public void onFlingAway();
15 |
16 | /**
17 | * Callback for when the icon has been dragged by the user
18 | * @param x x coordiante on the screen in pixels
19 | * @param y y coordinate on the screen in pixels
20 | */
21 | public void onMove(float x, float y);
22 |
23 | /**
24 | * Callback for when the icon has been clicked. Perform any action such as launch your app,
25 | * or show a menu, etc.
26 | * @param icon the view holding the icon. Get context from this view.
27 | * @param iconXPose the x coordinate of the icon in pixels
28 | * @param iconYPose the y coordiante of the icon in pixels
29 | */
30 | public void onIconClick(View icon, float iconXPose, float iconYPose);
31 |
32 |
33 | /**
34 | * Callback for when the icon has been destroyed. Usually you should stop your service in this.
35 | */
36 | public void onIconDestroyed();
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/java/com/cundong/memory/demo/wrong/ThreadOutOfMemoryActivity.java:
--------------------------------------------------------------------------------
1 | package com.cundong.memory.demo.wrong;
2 |
3 | import java.lang.ref.WeakReference;
4 |
5 | import android.app.Activity;
6 | import android.content.Context;
7 | import android.os.Bundle;
8 |
9 | import com.cundong.memory.R;
10 |
11 | /**
12 | * Thread引发的内存溢出
13 | *
14 | * 当前情况:
15 | *
16 | * 1.
17 | * 内部类MyThread,持有对外部ThreadOutOfMemoryActivity的隐式引用
18 | *
19 | * 2.
20 | * 如果我们切换横竖屏,默认就会销毁当前Activity,而这个Activity却被MyThread所持有
21 | *
22 | * 于是就出现了溢出。
23 | *
24 | *
25 | * 解决办法:
26 | *
27 | * 1.MyThread改为静态内部类
28 | *
29 | * 2.在线程内部采用弱引用保存Context引用
30 | *
31 | */
32 | public class ThreadOutOfMemoryActivity extends Activity {
33 |
34 | private Context mContext;
35 |
36 | @Override
37 | protected void onCreate(Bundle state) {
38 | super.onCreate(state);
39 | this.setContentView(R.layout.activity_demo);
40 |
41 | mContext = this.getApplicationContext();
42 |
43 | new MyThread().start();
44 | }
45 |
46 | private void releaseZip(WeakReference context){
47 | /* do something */
48 | }
49 |
50 | private class MyThread extends Thread {
51 | @Override
52 | public void run() {
53 | super.run();
54 |
55 | try {
56 | Thread.sleep(1000 * 60 * 2);
57 | } catch (InterruptedException e) {
58 | e.printStackTrace();
59 | }
60 |
61 | /* do something */
62 | releaseZip(new WeakReference<>(mContext));
63 | }
64 | }
65 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cundong/memory/demo/right/HandlerOutOfMemoryActivity.java:
--------------------------------------------------------------------------------
1 | package com.cundong.memory.demo.right;
2 |
3 | import java.lang.ref.WeakReference;
4 |
5 | import android.app.Activity;
6 | import android.os.Bundle;
7 | import android.os.Handler;
8 | import android.os.Message;
9 |
10 | import com.cundong.memory.R;
11 |
12 | /**
13 | * 解决Handler引发的内存溢出
14 | *
15 | * 不使用非静态的内部类,改为使用静态内部类,当静态内部类中需要调用外部的Activity时,改用弱引用。
16 | *
17 | */
18 | public class HandlerOutOfMemoryActivity extends Activity {
19 |
20 | private final MyHandler mHandler = new MyHandler(this);
21 |
22 | private static class MyHandler extends Handler {
23 | private final WeakReference mActivity;
24 |
25 | public MyHandler(HandlerOutOfMemoryActivity activity) {
26 | mActivity = new WeakReference<>(activity);
27 | }
28 |
29 | @Override
30 | public void handleMessage(Message msg) {
31 | HandlerOutOfMemoryActivity activity = mActivity.get();
32 | if (activity != null) {
33 | /* ... */
34 | }
35 | }
36 | }
37 |
38 | private static final Runnable sRunnable = new Runnable() {
39 | @Override
40 | public void run() {
41 | /* ... */
42 | }
43 | };
44 |
45 | @Override
46 | protected void onCreate(Bundle savedInstanceState) {
47 | super.onCreate(savedInstanceState);
48 | this.setContentView(R.layout.activity_outofmemory_list);
49 |
50 | mHandler.postDelayed(sRunnable, 1000 * 60 * 10);
51 |
52 | finish();
53 | }
54 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cundong/memory/demo/wrong/HandlerOutOfMemoryActivity.java:
--------------------------------------------------------------------------------
1 | package com.cundong.memory.demo.wrong;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 | import android.os.Handler;
6 | import android.os.Message;
7 |
8 | import com.cundong.memory.R;
9 |
10 | /**
11 | * Handler引发的内存溢出
12 | *
13 | * 1.
14 | * 当一个Android应用启动的时候,会自动创建一个供主线程使用的Looper实例,这个Looper实例负责一个一个的处理消息队列中的消息对象,它的生命周期和当前应用
15 | * 的生命周期是一样的。
16 | *
17 | * 2.
18 | * 当Handler在主线程中初始化之后,发送一个消息(target为当前Handler)至消息队列,这个消息对象就已经包含了Handler实例的引用,只有这样,Looper在处理这条消息的时候,才可以
19 | * 调用Handler的handlerMessage(Message)来完成消息的处理。
20 | *
21 | * 3.非静态的内部类和匿名内部类,都会隐式的持有一个外部类的引用。
22 | *
23 | * 由于这3个原因。
24 | *
25 | * 当Activity finish掉,被延迟的消息会存在消息队列中10分钟,这个消息中又包含了Handler的引用,Handler是一个匿名内部类,又隐式的持有外部Activity的引用,导致
26 | * 其无法回收,进一步导致Activity持有的很多资源都无法回收,也就是内存泄露了。
27 | *
28 | * 正确示例:com.cundong.memory.right.HandlerOutOfMemoryActivity
29 | *
30 | */
31 | public class HandlerOutOfMemoryActivity extends Activity {
32 |
33 | private final Handler mHandler = new Handler() {
34 |
35 | @Override
36 | public void handleMessage(Message msg) {
37 | /* ... */
38 | }
39 | };
40 |
41 | @Override
42 | protected void onCreate(Bundle savedInstanceState) {
43 | super.onCreate(savedInstanceState);
44 | this.setContentView(R.layout.activity_outofmemory_list);
45 |
46 | // Post a message and delay its execution for 10 minutes.
47 | mHandler.postDelayed(new Runnable() {
48 | @Override
49 | public void run() {
50 |
51 | /* ... */
52 | }
53 | }, 1000 * 60 * 10);
54 |
55 | finish();
56 | }
57 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
18 |
19 |
25 |
26 |
32 |
33 |
39 |
--------------------------------------------------------------------------------
/app/src/main/java/com/cundong/memory/demo/wrong/StaticOutOfMemoryActivity.java:
--------------------------------------------------------------------------------
1 | package com.cundong.memory.demo.wrong;
2 |
3 | import android.app.Activity;
4 | import android.graphics.drawable.Drawable;
5 | import android.os.Bundle;
6 | import android.widget.TextView;
7 |
8 | import com.cundong.memory.R;
9 |
10 | /**
11 | * static变量引发的内存溢出
12 | *
13 | * 如果一个变量为static变量,它就属于整个类,而不是类的具体实例,所以static变量的生命周期是特别的长,如果static变量引用了一些资源耗费过多
14 | * 的实例,例如Context,就有内存溢出的危险。
15 | *
16 | *
17 | * Google开发者博客,给出了一个例子:http://android-developers.blogspot.jp/2009/01/avoiding-memory-leaks.html
18 | * 专门介绍长时间的引用Context导致内存溢出的情况。
19 | *
20 | * 这种情况:
21 | *
22 | * 静态的sBackground变量,虽然没有显式的持有Context的引用,但是:
23 | *
24 | * 当我们执行view.setBackgroundDrawable(Drawable drawable);之后。
25 | *
26 | * Drawable会将View设置为一个回调(通过setCallback()方法),所以就会存在这么一个隐式的引用链:Drawable持有View,View持有Context
27 | *
28 | * sBackground是静态的,生命周期特别的长,就会导致了Context的溢出。
29 | *
30 | * 解决办法:
31 | *
32 | * 1.不用activity的context 而是用Application的Context
33 | *
34 | * 2.在onDestroy()方法中,解除Activity与Drawable的绑定关系,从而去除Drawable对Activity的引用,使Context能够被回收
35 | *
36 | */
37 | public class StaticOutOfMemoryActivity extends Activity {
38 |
39 | private static Drawable sBackground;
40 |
41 | @Override
42 | protected void onCreate(Bundle state) {
43 | super.onCreate(state);
44 |
45 | TextView textView = new TextView(this);
46 | textView.setText("Leaks are bad");
47 |
48 | if (sBackground == null) {
49 | sBackground = getResources().getDrawable(R.drawable.large_bitmap);
50 | }
51 |
52 | textView.setBackgroundDrawable(sBackground);
53 |
54 | setContentView(textView);
55 | }
56 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/float_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
18 |
19 |
23 |
24 |
31 |
32 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/app/src/main/java/com/cundong/memory/demo/wrong/AsyncTaskOutOfMemoryActivity.java:
--------------------------------------------------------------------------------
1 | package com.cundong.memory.demo.wrong;
2 |
3 | import android.app.Activity;
4 | import android.graphics.Bitmap;
5 | import android.os.AsyncTask;
6 | import android.os.Bundle;
7 | import android.widget.ImageView;
8 |
9 | import com.cundong.memory.util.BitmapUtils;
10 | import com.cundong.memory.R;
11 |
12 | /**
13 | * AsyncTask引发的内存溢出
14 | *
15 | * 当前情况:
16 | *
17 | * 1.
18 | * 内部类BitmapWorkerTask,持有对外部AsyncTaskOutOfMemoryActivity的隐式引用
19 | *
20 | * 2.
21 | * 如果我们切换横竖屏,默认就会销毁当前Activity,而这个Activity却被BitmapWorkerTask所持有
22 | *
23 | * 于是就出现了溢出。
24 | *
25 | *
26 | * 解决办法:
27 | *
28 | * BitmapWorkerTask内部采用弱引用保存Context引用
29 | *
30 | */
31 | public class AsyncTaskOutOfMemoryActivity extends Activity {
32 |
33 | private ImageView mImageView;
34 |
35 | @Override
36 | protected void onCreate(Bundle state) {
37 | super.onCreate(state);
38 | this.setContentView(R.layout.activity_demo);
39 |
40 | mImageView = (ImageView) findViewById(R.id.image);
41 |
42 | BitmapWorkerTask task = new BitmapWorkerTask();
43 | task.execute(R.drawable.large_bitmap);
44 | }
45 |
46 | class BitmapWorkerTask extends AsyncTask {
47 |
48 | private int data = 0;
49 |
50 | // Decode image in background.
51 | @Override
52 | protected Bitmap doInBackground(Integer... params) {
53 | data = params[0];
54 | return BitmapUtils.decodeSampledBitmapFromResource(getResources(), data, 100, 100);
55 | }
56 |
57 | // Once complete, see if ImageView is still around and set bitmap.
58 | @Override
59 | protected void onPostExecute(Bitmap bitmap) {
60 | if (bitmap != null) {
61 | mImageView.setImageBitmap(bitmap);
62 | }
63 | }
64 | }
65 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cundong/memory/util/BitmapUtils.java:
--------------------------------------------------------------------------------
1 | package com.cundong.memory.util;
2 |
3 | import android.content.res.Resources;
4 | import android.graphics.Bitmap;
5 | import android.graphics.BitmapFactory;
6 |
7 | public class BitmapUtils {
8 |
9 | public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
10 | int reqWidth, int reqHeight) {
11 |
12 | // First decode with inJustDecodeBounds=true to check dimensions
13 | final BitmapFactory.Options options = new BitmapFactory.Options();
14 | options.inJustDecodeBounds = true;
15 | BitmapFactory.decodeResource(res, resId, options);
16 |
17 | // Calculate inSampleSize
18 | options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
19 |
20 | // Decode bitmap with inSampleSize set
21 | options.inJustDecodeBounds = false;
22 | return BitmapFactory.decodeResource(res, resId, options);
23 | }
24 |
25 | public static int calculateInSampleSize(
26 | BitmapFactory.Options options, int reqWidth, int reqHeight) {
27 | // Raw height and width of image
28 | final int height = options.outHeight;
29 | final int width = options.outWidth;
30 | int inSampleSize = 1;
31 |
32 | if (height > reqHeight || width > reqWidth) {
33 |
34 | final int halfHeight = height / 2;
35 | final int halfWidth = width / 2;
36 |
37 | // Calculate the largest inSampleSize value that is a power of 2 and keeps both
38 | // height and width larger than the requested height and width.
39 | while ((halfHeight / inSampleSize) > reqHeight
40 | && (halfWidth / inSampleSize) > reqWidth) {
41 | inSampleSize *= 2;
42 | }
43 | }
44 |
45 | return inSampleSize;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/app/src/main/java/com/cundong/memory/demo/right/AsyncTaskOutOfMemoryActivity.java:
--------------------------------------------------------------------------------
1 | package com.cundong.memory.demo.right;
2 |
3 | import java.lang.ref.WeakReference;
4 |
5 | import android.app.Activity;
6 | import android.graphics.Bitmap;
7 | import android.os.AsyncTask;
8 | import android.os.Bundle;
9 | import android.widget.ImageView;
10 |
11 | import com.cundong.memory.util.BitmapUtils;
12 | import com.cundong.memory.R;
13 |
14 | /**
15 | * AsyncTask引发的内存溢出解决办法
16 | *
17 | * BitmapWorkerTask内部采用弱引用保存Context引用
18 | *
19 | */
20 | public class AsyncTaskOutOfMemoryActivity extends Activity {
21 |
22 | private ImageView mImageView;
23 |
24 | @Override
25 | protected void onCreate(Bundle state) {
26 | super.onCreate(state);
27 | this.setContentView(R.layout.activity_demo);
28 |
29 | mImageView = (ImageView) findViewById(R.id.image);
30 |
31 | BitmapWorkerTask task = new BitmapWorkerTask(mImageView);
32 | task.execute(R.drawable.large_bitmap);
33 | }
34 |
35 | class BitmapWorkerTask extends AsyncTask {
36 | private final WeakReference imageViewReference;
37 | private int data = 0;
38 |
39 | public BitmapWorkerTask(ImageView imageView) {
40 | // Use a WeakReference to ensure the ImageView can be garbage collected
41 | imageViewReference = new WeakReference<>(imageView);
42 | }
43 |
44 | // Decode image in background.
45 | @Override
46 | protected Bitmap doInBackground(Integer... params) {
47 | data = params[0];
48 | return BitmapUtils.decodeSampledBitmapFromResource(getResources(), data, 100, 100);
49 | }
50 |
51 | // Once complete, see if ImageView is still around and set bitmap.
52 | @Override
53 | protected void onPostExecute(Bitmap bitmap) {
54 | if (imageViewReference != null && bitmap != null) {
55 | final ImageView imageView = imageViewReference.get();
56 | if (imageView != null) {
57 | imageView.setImageBitmap(bitmap);
58 | }
59 | }
60 | }
61 | }
62 | }
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/app/src/main/java/com/cundong/memory/demo/wrong/MemoryChurnActivity.java:
--------------------------------------------------------------------------------
1 | package com.cundong.memory.demo.wrong;
2 |
3 | import android.app.Activity;
4 | import android.graphics.Bitmap;
5 | import android.graphics.BitmapFactory;
6 | import android.os.Bundle;
7 | import android.util.Log;
8 | import android.view.View;
9 | import android.widget.Button;
10 |
11 | import com.cundong.memory.R;
12 | import com.cundong.memory.util.MemoryUtil;
13 |
14 | /**
15 | * 内存抖动
16 | *
17 | * 在短时间内大量的对象被创建又马上被释放,瞬间产生大量的对象会严重占用Young Generation的内存区域,当达到阀值,剩余空间不够的时候,
18 | * 会触发GC从而导致刚产生的对象又很快被回收。即使每次分配的对象占用了很少的内存,但是他们叠加在一起会增加Heap的压力,从而触发更多其他类型的GC。
19 | *
20 | * 这个操作有可能会影响到帧率,并使得用户感知到性能问题。
21 | *
22 | * Created by cundong on 2015/12/28.
23 | */
24 | public class MemoryChurnActivity extends Activity {
25 |
26 | private Button mButton;
27 |
28 | @Override
29 | protected void onCreate(Bundle savedInstanceState) {
30 | super.onCreate(savedInstanceState);
31 | this.setContentView(R.layout.activity_test);
32 |
33 | mButton = (Button) findViewById(R.id.button);
34 | mButton.setOnClickListener(new View.OnClickListener() {
35 | @Override
36 | public void onClick(View v) {
37 | new Thread() {
38 | @Override
39 | public void run() {
40 | super.run();
41 |
42 | for( int i=0; i<100; i++) {
43 | Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.large_img);
44 |
45 | int rowBytes = bmp.getRowBytes();
46 | int height = bmp.getHeight();
47 |
48 | long memSize = rowBytes * height;
49 | Log.d("@Cundong", "memSize =" + memSize + "B =" + memSize * 1.0 / 1024 / 1024 +" M");
50 | Log.d("@Cundong", "getUsedPercentValue:" + MemoryUtil.getUsedPercentValue());
51 | }
52 |
53 | /**
54 | java.lang.OutOfMemoryError: Failed to allocate a 47 byte allocation with 0 free bytes and 3GB until OOM
55 | at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
56 | at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:609)
57 | at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:444)
58 | at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:467)
59 | at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:497)
60 | at com.cundong.memory.demo.wrong.MemoryChurnActivity$1$1.run(MemoryChurnActivity.java:42)
61 | */
62 | }
63 | } .start();
64 | }
65 | });
66 | }
67 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
23 |
29 |
30 |
31 |
32 |
33 |
34 |
39 |
44 |
49 |
54 |
59 |
64 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/app/src/main/java/com/cundong/memory/util/Logger.java:
--------------------------------------------------------------------------------
1 | package com.cundong.memory.util;
2 |
3 | import android.util.Log;
4 |
5 | public class Logger {
6 |
7 | /**
8 | * log tag
9 | */
10 | private String tag = "Logger";// application name
11 |
12 | // TODO 配置Log打开或关闭
13 | /**
14 | * debug or not
15 | */
16 | private static boolean debug = true;
17 |
18 | private static Logger instance = new Logger();
19 |
20 | private Logger() {
21 |
22 | }
23 |
24 | public static Logger getLogger() {
25 | return instance;
26 | }
27 |
28 | private String getFunctionName() {
29 | StackTraceElement[] sts = Thread.currentThread().getStackTrace();
30 |
31 | if (sts == null) {
32 | return null;
33 | }
34 |
35 | for (StackTraceElement st : sts) {
36 | if (st.isNativeMethod()) {
37 | continue;
38 | }
39 |
40 | if (st.getClassName().equals(Thread.class.getName())) {
41 | continue;
42 | }
43 |
44 | if (st.getClassName().equals(this.getClass().getName())) {
45 | continue;
46 | }
47 |
48 | return "[" + Thread.currentThread().getName() + "(" + Thread.currentThread().getId() + "): " + st.getFileName() + ":" + st.getLineNumber() + "]";
49 | }
50 |
51 | return null;
52 | }
53 |
54 | private String createMessage(String msg) {
55 | String functionName = getFunctionName();
56 | String message = (functionName == null ? msg : (functionName + " - " + msg));
57 | return message;
58 | }
59 |
60 | /**
61 | * log.i
62 | */
63 | public void i(String msg) {
64 | if (debug) {
65 | String message = createMessage(msg);
66 | Log.i(tag, message);
67 | }
68 | }
69 |
70 | /**
71 | * log.v
72 | */
73 | public void v(String msg) {
74 | if (debug) {
75 | String message = createMessage(msg);
76 | Log.v(tag, message);
77 | }
78 | }
79 |
80 | /**
81 | * log.d
82 | */
83 | public void d(String msg) {
84 | if (debug) {
85 | String message = createMessage(msg);
86 | Log.d(tag, message);
87 | }
88 | }
89 |
90 | /**
91 | * log.e
92 | */
93 | public void e(String msg) {
94 | // if (debug) {
95 | String message = createMessage(msg);
96 | Log.e(tag, message);
97 | // }
98 | }
99 |
100 | /**
101 | * log.error
102 | */
103 | public void error(Exception e) {
104 | if (debug) {
105 | StringBuffer sb = new StringBuffer();
106 | String name = getFunctionName();
107 | StackTraceElement[] sts = e.getStackTrace();
108 |
109 | if (name != null) {
110 | sb.append(name + " - " + e + "\r\n");
111 | } else {
112 | sb.append(e + "\r\n");
113 | }
114 | if (sts != null && sts.length > 0) {
115 | for (StackTraceElement st : sts) {
116 | if (st != null) {
117 | sb.append("[ " + st.getFileName() + ":" + st.getLineNumber() + " ]\r\n");
118 | }
119 | }
120 | }
121 |
122 | Log.e(tag, sb.toString());
123 | }
124 | }
125 |
126 | /**
127 | * log.d
128 | */
129 | public void w(String msg) {
130 | if (debug) {
131 | String message = createMessage(msg);
132 | Log.w(tag, message);
133 | }
134 | }
135 |
136 | public void setTag(String tag) {
137 | this.tag = tag;
138 | }
139 |
140 | /**
141 | * set debug
142 | */
143 | public static void setDebug(boolean d) {
144 | debug = d;
145 | }
146 |
147 | public static boolean isDebug() {
148 | return debug;
149 | }
150 | }
--------------------------------------------------------------------------------
/magnet/src/main/java/com/premnirmal/Magnet/RemoveView.java:
--------------------------------------------------------------------------------
1 | package com.premnirmal.Magnet;
2 |
3 | import android.content.Context;
4 | import android.graphics.PixelFormat;
5 | import android.util.Log;
6 | import android.view.Gravity;
7 | import android.view.LayoutInflater;
8 | import android.view.View;
9 | import android.view.WindowManager;
10 | import android.view.animation.Animation;
11 | import android.widget.ImageView;
12 | import android.widget.RelativeLayout;
13 |
14 | /**
15 | * Created by prem on 7/20/14.
16 | * ViewHolder for the remove Icon.
17 | */
18 | class RemoveView {
19 |
20 | View mLayout;
21 | View mButton;
22 | View mShadow;
23 | ImageView mButtonImage;
24 | private WindowManager mWindowManager;
25 | private SimpleAnimator mShowAnim;
26 | private SimpleAnimator mHideAnim;
27 |
28 | private SimpleAnimator mShadowFadeOut;
29 | private SimpleAnimator mShadowFadeIn;
30 |
31 | private final int buttonBottomPadding;
32 |
33 | boolean shouldBeResponsive = true;
34 |
35 | RemoveView(Context context) {
36 | mLayout = LayoutInflater.from(context).inflate(R.layout.x_button_holder, null);
37 | mButton = mLayout.findViewById(R.id.xButton);
38 | mButtonImage = (ImageView) mLayout.findViewById(R.id.xButtonImg);
39 | mButtonImage.setImageResource(R.drawable.trash);
40 | buttonBottomPadding = mButton.getPaddingBottom();
41 | mShadow = mLayout.findViewById(R.id.shadow);
42 | mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
43 | addToWindow(mLayout);
44 | mShowAnim = new SimpleAnimator(mButton, R.anim.slide_up);
45 | mHideAnim = new SimpleAnimator(mButton, R.anim.slide_down);
46 | mShadowFadeIn = new SimpleAnimator(mShadow, android.R.anim.fade_in);
47 | mShadowFadeOut = new SimpleAnimator(mShadow, android.R.anim.fade_out);
48 | hide();
49 | }
50 |
51 | void setIconResId(int id) {
52 | mButtonImage.setImageResource(id);
53 | }
54 |
55 | void setShadowBG(int shadowBG) {
56 | mShadow.setBackgroundResource(shadowBG);
57 | }
58 |
59 | void show() {
60 | if (mLayout != null && mLayout.getParent() == null) {
61 | addToWindow(mLayout);
62 | }
63 | mShadowFadeIn.startAnimation();
64 | mShowAnim.startAnimation();
65 | }
66 |
67 | void hide() {
68 | mShadowFadeOut.startAnimation();
69 | mHideAnim.startAnimation(new Animation.AnimationListener() {
70 | @Override
71 | public void onAnimationStart(Animation animation) {
72 |
73 | }
74 |
75 | @Override
76 | public void onAnimationEnd(Animation animation) {
77 | if (mLayout != null && mLayout.getParent() != null) {
78 | mWindowManager.removeView(mLayout);
79 | }
80 | }
81 |
82 | @Override
83 | public void onAnimationRepeat(Animation animation) {
84 |
85 | }
86 | });
87 | }
88 |
89 | void onMove(final float x, final float y) {
90 | if (shouldBeResponsive) {
91 | final int xTransformed = (int) Math.abs(x * 100 / (mButton.getContext().getResources().getDisplayMetrics().widthPixels / 2));
92 | final int bottomPadding = buttonBottomPadding - (xTransformed / 5);
93 | if (x < 0) {
94 | mButton.setPadding(0, 0, xTransformed, bottomPadding);
95 | } else {
96 | mButton.setPadding(xTransformed, 0, 0, bottomPadding);
97 | }
98 | }
99 | }
100 |
101 | void destroy() {
102 | if (mLayout != null && mLayout.getParent() != null) {
103 | mWindowManager.removeView(mLayout);
104 | }
105 | mLayout = null;
106 | mWindowManager = null;
107 | }
108 |
109 | private void addToWindow(View layout) {
110 | // WindowManager.LayoutParams params = new WindowManager.LayoutParams(
111 | // WindowManager.LayoutParams.MATCH_PARENT,
112 | // WindowManager.LayoutParams.MATCH_PARENT,
113 | // WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
114 | // WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
115 | // PixelFormat.TRANSLUCENT
116 | // );
117 |
118 | WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
119 | mParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
120 | mParams.width = RelativeLayout.LayoutParams.MATCH_PARENT;
121 | mParams.height = RelativeLayout.LayoutParams.MATCH_PARENT;
122 | mParams.gravity = Gravity.CENTER;
123 |
124 | mParams.format = PixelFormat.TRANSLUCENT;
125 | mParams.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
126 | | WindowManager.LayoutParams.FLAG_DIM_BEHIND
127 | | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
128 | mParams.dimAmount = 0.4f;
129 |
130 | try {
131 | mWindowManager.addView(layout, mParams);
132 | } catch (Exception e) {
133 | e.printStackTrace();
134 | }
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/app/src/main/java/com/cundong/memory/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.cundong.memory;
2 |
3 | import android.annotation.TargetApi;
4 | import android.app.ActivityManager;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.net.Uri;
8 | import android.os.Build;
9 | import android.os.Bundle;
10 | import android.provider.Settings;
11 | import android.support.v7.app.AppCompatActivity;
12 | import android.view.View;
13 | import android.widget.Button;
14 | import android.widget.Toast;
15 |
16 | import com.cundong.memory.service.CoreService;
17 | import com.cundong.memory.service.EmptyService;
18 | import com.cundong.memory.util.MemoryUtil;
19 |
20 | import java.util.List;
21 |
22 | public class MainActivity extends AppCompatActivity {
23 |
24 | private static final int OVERLAY_PERMISSION_REQ_CODE = 1;
25 |
26 | private Button mButton1, mButton2, mButton3, mButton4;
27 |
28 | @Override
29 | protected void onCreate(Bundle savedInstanceState) {
30 | super.onCreate(savedInstanceState);
31 | setContentView(R.layout.activity_main);
32 |
33 | mButton1 = (Button) findViewById(R.id.button1);
34 | mButton2 = (Button) findViewById(R.id.button2);
35 | mButton3 = (Button) findViewById(R.id.button3);
36 | mButton4 = (Button) findViewById(R.id.button4);
37 |
38 | mButton3.setVisibility(Constants.SHOW_MEMORY_CLEAR ? View.VISIBLE : View.GONE);
39 | mButton4.setVisibility(Constants.SHOW_MEMORY_CLEAR ? View.VISIBLE : View.GONE);
40 |
41 | mButton1.setOnClickListener(new View.OnClickListener() {
42 |
43 | @Override
44 | public void onClick(View v) {
45 |
46 | try2StartMonitor();
47 | }
48 | });
49 |
50 | mButton2.setOnClickListener(new View.OnClickListener() {
51 |
52 | @Override
53 | public void onClick(View v) {
54 | Intent intent = new Intent(MainActivity.this, CoreService.class);
55 | intent.putExtra("action", 2);
56 | startService(intent);
57 |
58 | finish();
59 | }
60 | });
61 |
62 | mButton3.setOnClickListener(new View.OnClickListener() {
63 |
64 | @Override
65 | public void onClick(View v) {
66 | Toast.makeText(getApplicationContext(), "clearMemory", Toast.LENGTH_LONG).show();
67 | MemoryUtil.clearMemory(getApplicationContext());
68 | }
69 | });
70 |
71 | mButton4.setOnClickListener(new View.OnClickListener() {
72 |
73 | @Override
74 | public void onClick(View v) {
75 | Intent intent = new Intent(MainActivity.this, EmptyService.class);
76 | startService(intent);
77 | }
78 | });
79 | }
80 |
81 | @TargetApi(Build.VERSION_CODES.M)
82 | private void try2StartMonitor() {
83 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {
84 |
85 | Toast.makeText(this, R.string.permission_err, Toast.LENGTH_LONG).show();
86 |
87 | Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
88 | startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE);
89 | } else {
90 | Intent intent = new Intent(MainActivity.this, CoreService.class);
91 | intent.putExtra("action", 1);
92 | startService(intent);
93 |
94 | finish();
95 | }
96 | }
97 |
98 | @Override
99 | protected void onResume() {
100 | super.onResume();
101 |
102 | boolean isServiceRunning = isServiceRunning(getPackageName() + ".service.CoreService");
103 |
104 | mButton1.setVisibility(isServiceRunning ? View.GONE : View.VISIBLE);
105 | mButton2.setVisibility(isServiceRunning ? View.VISIBLE : View.GONE);
106 | }
107 |
108 | @TargetApi(Build.VERSION_CODES.M)
109 | @Override
110 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
111 | if (requestCode == OVERLAY_PERMISSION_REQ_CODE) {
112 | if (!Settings.canDrawOverlays(this)) {
113 | Toast.makeText(this, R.string.permission_err, Toast.LENGTH_SHORT).show();
114 | } else {
115 | Intent intent = new Intent(MainActivity.this, CoreService.class);
116 | intent.putExtra("action", 1);
117 | startService(intent);
118 |
119 | finish();
120 | }
121 | }
122 | }
123 |
124 | /**
125 | * 使用Application context来调用getSystemService,避免内存泄漏
126 | *
127 | * @param className
128 | * @return
129 | */
130 | private boolean isServiceRunning(String className) {
131 |
132 | ActivityManager activityManager = (ActivityManager) App.getAppContext().getSystemService(Context.ACTIVITY_SERVICE);
133 | List serviceList = activityManager.getRunningServices(100);
134 |
135 | if (serviceList == null || serviceList.size() <= 0) {
136 | return false;
137 | }
138 |
139 | for (ActivityManager.RunningServiceInfo serviceInfo : serviceList) {
140 |
141 | String serviceName = serviceInfo.service.getClassName();
142 |
143 | if (serviceName.equals(className)) {
144 | return true;
145 | }
146 | }
147 |
148 | return false;
149 | }
150 | }
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MemoryMonitor
2 |
3 | 一个给开发者使用的Android App内存清理、监控工具。
4 |
5 | 主要包括三部分内容:
6 |
7 | * 内存清理
8 |
9 | 通过内存清理可以模拟系统内存不足时对进程的回收。
10 |
11 | * Pss监控
12 |
13 | 通过内存监控可以监控指定应用程序使用的total Pss以及当前手机的内存使用情况,从而检测该应用是否存在内存泄漏。
14 |
15 | * 内存优化
16 |
17 | 整理了一些关于内存优化的tips,以及一些可能导致内存溢出的场景示例,包含错误的写法和正确的写法。
18 |
19 | ## 1.内存清理
20 |
21 | 类似各种手机管家的 **加速球**,获取系统已用内存比率、可用内存大小,一键清理。
22 |
23 | 可以用于测试自己开发的Activity、Fragment健壮性,模拟Activity、Fragment被回收的场景,测试自己的程序是否完好的保存、恢复当前场景。
24 |
25 | 比如:打开你开发的某个Activity、Fragment,切到后台,清理一次内存,在将其切回前台后,看会不会出现空指针异常,以及程序状态是否被恢复。
26 |
27 | ## 2.Pss监控
28 |
29 | Android 系统中的内存和Linux系统一样,存在着大量的共享内存。每个APP占内存会有私有和公共的两部分,我们可以通过App的Pss值,可以获取到这两部分内存。
30 |
31 | Pss(Proportional Set Size):实际使用的物理内存,即:自身应用占有的内存+共享内存中比例分配给这个应用的内存。
32 |
33 | 通过该程序,每隔1秒,获取一次被监控App的Total Pss值。
34 |
35 | 使用某个功能(可能会导致OOM的那些都要试试),查看Pss是否飙升,或者使用过许久都没有降低。
36 |
37 | 如果使用后飙升并且长时间都降不下来,那就说明肯定会导致OOM(对象使用过之后还被引用着未释放),如果使用之后Total Pss飙升,但是使用过之后能降下来,也可能会导致OOM,我们还是需要去一点一点排查是什么原因导致的。
38 |
39 | 如果使用后飙升并且长时间都降不下来,我们就需要 [使用MAT来进一步分析问题所在](http://blog.csdn.net/xiaanming/article/details/42396507)。
40 |
41 | 此处提到的Pss,也可以使用adb命令
42 |
43 | > adb shell dumpsys meminfo *your packageName*
44 |
45 | 查看:
46 |
47 | 
48 |
49 | ## 3.内存优化
50 |
51 | Android的虚拟机是基于寄存器的Dalvik,它的最大堆大小一般比较小(最低端的设备16M,后来出的设备变成了24M,48M等等),因此我们所能利用的内存空间是有限的。如果我们使用内存占用超过了一定的限额后就会出现OutOfMemory的错误。
52 |
53 | 可能会导致内存溢出的情况有以下几种:
54 |
55 | ### 对静态变量的错误使用
56 |
57 | 如果一个变量为static变量,它就属于整个类,而不是类的具体实例,所以static变量的生命周期是特别的长,如果static变量引用了一些资源耗费过多的实例,例如Context,就有内存溢出的危险。
58 |
59 | [Google开发者博客,给出了一个例子](http://android-developers.blogspot.jp/2009/01/avoiding-memory-leaks.html),专门介绍长时间引用Context导致内存溢出的情况。
60 |
61 | 示例代码:
62 |
63 | ```java
64 | private static Drawable sBackground;
65 |
66 | @Override
67 | protected void onCreate(Bundle state) {
68 | super.onCreate(state);
69 |
70 | TextView textView = new TextView(this);
71 | textView.setText("Leaks are bad");
72 |
73 | if (sBackground == null) {
74 | sBackground = getResources().getDrawable(R.drawable.large_bitmap);
75 | }
76 |
77 | textView.setBackgroundDrawable(sBackground);
78 |
79 | setContentView(textView);
80 | }
81 | ```
82 |
83 | 这种情况下,静态的sBackground变量,虽然没有显式的持有Context的引用,但当我们执行`view.setBackgroundDrawable(Drawable drawable);`的时候,drawable 对象会将当前view设置为一个回调,通过 `View.setCallback(this)` 方法。
84 |
85 | 具体可见View类的源码:
86 | ```
87 | public void setBackgroundDrawable(Drawable background) {
88 | //...
89 |
90 | if (mBackground == null || mBackground.getMinimumHeight() != background.getMinimumHeight() ||
91 | mBackground.getMinimumWidth() != background.getMinimumWidth()) {
92 | requestLayout = true;
93 | }
94 |
95 | background.setCallback(this);
96 | if (background.isStateful()) {
97 | background.setState(getDrawableState());
98 | }
99 |
100 | background.setVisible(getVisibility() == VISIBLE, false);
101 | mBackground = background;
102 |
103 | //...
104 | }
105 | ```
106 | `background.setCallback(this);` 代码块就是我们说的设置回调。
107 |
108 | 所以,这种情况就会存在这么一个隐式的引用链:Drawable持有View,而View持有Context,sBackground 是静态的,生命周期特别的长,于是就会导致了Context的溢出。
109 |
110 | 解决办法:
111 |
112 | 1.不用activity的context 而是用Application的Context;
113 |
114 | ```java
115 | private static Drawable sBackground;
116 |
117 | @Override
118 | protected void onCreate(Bundle state) {
119 | super.onCreate(state);
120 |
121 | TextView textView = new TextView(this.getApplication());
122 | textView.setText("Leaks are bad");
123 |
124 | if (sBackground == null) {
125 | sBackground = getResources().getDrawable(R.drawable.large_bitmap);
126 | }
127 |
128 | textView.setBackgroundDrawable(sBackground);
129 |
130 | setContentView(textView);
131 | }
132 | ```
133 | 2.在onDestroy()方法中,解除Activity与Drawable的绑定关系,从而去除Drawable对Activity的引用,使Context能够被回收;
134 |
135 | ```java
136 | @Override
137 | protected void onDestroy() {
138 | super.onDestroy();
139 |
140 | ViewUtils.unbindDrawables(findViewById(android.R.id.content));
141 |
142 | System.gc();
143 | }
144 |
145 | public static void unbindDrawables(View view) {
146 | if (view.getBackground() != null) {
147 | view.getBackground().setCallback(null);
148 | }
149 |
150 | if (view instanceof ViewGroup) {
151 | for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
152 | unbindDrawables(((ViewGroup) view).getChildAt(i));
153 | }
154 | ((ViewGroup) view).removeAllViews();
155 | }
156 | }
157 | ```
158 |
159 | ### 长周期内部类、匿名内部类长时间持有外部类引用导致相关资源无法释放
160 |
161 | 长周期内部类、匿名内部类,如Handler,Thread,AsyncTask等。
162 |
163 | HandlerOutOfMemoryActivity所示的是Handler引发的内存溢出。
164 |
165 | ThreadOutOfMemoryActivity所示的是Thread引发的内存溢出。
166 |
167 | AsyncTaskOutOfMemoryActivity所示的时AsyncTask引发的内存溢出。
168 |
169 | ### Bitmap导致的内存溢出
170 |
171 | 一般是因为尝试加载过大的图片到内存,或者是内存中已经存在的过多的图片,从而导致内存溢出。
172 |
173 | ### 数据库Cursor未关闭
174 |
175 | 正常情况下,如果查询得到的数据量较小时不会有内存问题,而且虚拟机能够保证Cusor最终会被释放掉,如果Cursor的数据量特表大,特别是如果里面有Blob信息时,应该保证Cursor占用的内存被及时的释放掉,而不是等待GC来处理。
176 |
177 | ### 单例模式引用Context导致的内存泄露
178 |
179 | 如果在某个Activity中使用 `Singleton instance = Singleton.getInstance(this);` 就会造成该Activity一直被 `Singleton` 引用着,不能释放。这时候,正确的做法是使用 `getApplicationContext()` 来替代 `Activity的Context` ,这样就能避免内存泄露。
180 |
181 | ### 代码中一些细节
182 |
183 | >* 尽量使用9path
184 | >* Adapter要使用convertView
185 | >* 各种监听,广播等,注册的同时要记得取消注册
186 | >* 使用完对象要及时销毁,能使用局部变量的不要使用全局变量,功能用完成后要去掉对他的引用
187 | >* 切勿在循环调用的地方去产生对象,比如在getview()里new OnClicklistener(),这样的话,拖动的时候会new大量的对象出来。
188 | >* 使用Android推荐的数据结构,比如HashMap替换为SparseArray,避免使用枚举类型(在Android平台,枚举类型的内存消耗是Static常量的的2倍)
189 | >* 使用lint工具优化工程
190 | >* 字符串拼接使用StringBuilder或者StringBuffer
191 | >* 尽量使用静态匿名内部类,如果需要对外部类的引用,使用弱引用
192 | >* for循环的使用
193 | 用
194 | `final int size = array.length; for(int i = 0; i< size;i++)`
195 | 来替代:
196 | `for(int i =0;i < array.length;i++) `
197 |
198 | 最后,整理了一些开发中可能会导致内存溢出的场景,放在com.cundong.memory.demo.wrong中,并且给出了优化方法,放在com.cundong.memory.demo.right中。
199 |
200 | ## 4.截图
201 |
202 | ![截屏][1]
203 |
204 | [1]: https://raw.githubusercontent.com/cundong/MemoryMonitor/master/screenshot/app.png
205 |
--------------------------------------------------------------------------------
/app/src/main/java/com/cundong/memory/service/CoreService.java:
--------------------------------------------------------------------------------
1 | package com.cundong.memory.service;
2 |
3 | import android.app.Service;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.os.Bundle;
7 | import android.os.Handler;
8 | import android.os.IBinder;
9 | import android.os.Message;
10 | import android.util.Log;
11 | import android.view.LayoutInflater;
12 | import android.view.View;
13 | import android.view.View.OnClickListener;
14 | import android.widget.ImageButton;
15 | import android.widget.TextView;
16 | import android.widget.Toast;
17 |
18 | import com.cundong.memory.Constants;
19 | import com.cundong.memory.MainActivity;
20 | import com.cundong.memory.R;
21 | import com.cundong.memory.util.MemoryUtil;
22 | import com.premnirmal.Magnet.IconCallback;
23 | import com.premnirmal.Magnet.Magnet;
24 |
25 | import java.lang.ref.WeakReference;
26 | import java.text.DecimalFormat;
27 | import java.util.HashMap;
28 | import java.util.Iterator;
29 | import java.util.Map;
30 | import java.util.Timer;
31 | import java.util.TimerTask;
32 |
33 | /**
34 | * 类说明: 后台轮询service 每1秒钟更新一次通知栏 更新内存
35 | *
36 | * @date 2015-4-18
37 | * @version 1.0
38 | */
39 | public class CoreService extends Service implements IconCallback {
40 |
41 | private static final String TAG = "CoreService";
42 |
43 | private Timer mTimer;
44 |
45 | private Magnet mMagnet;
46 |
47 | private View mIconView = null;
48 | private View mMemoryClearView;
49 |
50 | private TextView mDescView;
51 | private ImageButton mClearBtn, mSettingBtn;
52 |
53 | private InnerHandler mHandler;
54 |
55 | @Override
56 | public void onCreate() {
57 | super.onCreate();
58 |
59 | mIconView = getIconView();
60 | mDescView = (TextView) mIconView.findViewById(R.id.content);
61 | mMemoryClearView = mIconView.findViewById(R.id.memory_clear_view);
62 | mMemoryClearView.setVisibility(Constants.SHOW_MEMORY_CLEAR ? View.VISIBLE : View.GONE);
63 |
64 | mClearBtn = (ImageButton) mIconView.findViewById(R.id.clear_btn);
65 | mClearBtn.setOnClickListener( new OnClickListener(){
66 |
67 | @Override
68 | public void onClick(View v) {
69 |
70 | Toast.makeText(getApplicationContext(), "clearMemory", Toast.LENGTH_SHORT).show();
71 | MemoryUtil.clearMemory(getApplicationContext());
72 | }
73 | });
74 |
75 | mSettingBtn = (ImageButton) mIconView.findViewById(R.id.setting_btn);
76 | mSettingBtn.setOnClickListener( new OnClickListener(){
77 |
78 | @Override
79 | public void onClick(View v) {
80 | Toast.makeText(getApplicationContext(), "test Setting", Toast.LENGTH_SHORT).show();
81 | }
82 | });
83 |
84 | mMagnet = new Magnet.Builder(this)
85 | .setIconView(mIconView)
86 | .setIconCallback(this)
87 | .setRemoveIconResId(R.drawable.trash)
88 | .setRemoveIconShadow(R.drawable.bottom_shadow)
89 | .setShouldFlingAway(true)
90 | .setShouldStickToWall(true)
91 | .setRemoveIconShouldBeResponsive(true)
92 | .setInitialPosition(-100, -200)
93 | .build();
94 |
95 | mMagnet.show();
96 |
97 | mHandler = new InnerHandler(this);
98 | }
99 |
100 | private View getIconView() {
101 | LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
102 | return inflater.inflate(R.layout.float_view, null);
103 | }
104 |
105 | @Override
106 | public IBinder onBind(Intent intent) {
107 | return null;
108 | }
109 |
110 | @Override
111 | public int onStartCommand(Intent intent, int flags, int startId) {
112 |
113 | if (mTimer == null) {
114 | mTimer = new Timer();
115 | mTimer.scheduleAtFixedRate(new RefreshTask(), 0, 1000);
116 | }
117 |
118 | int action = intent != null ? intent.getIntExtra("action", 0) : 0;
119 | if (action == 2) {
120 | mMagnet.destroy();
121 | this.stopSelf();
122 | }
123 |
124 | return super.onStartCommand(intent, flags, startId);
125 | }
126 |
127 | @Override
128 | public void onDestroy() {
129 |
130 | super.onDestroy();
131 |
132 | mTimer.cancel();
133 | mTimer = null;
134 | }
135 |
136 | class RefreshTask extends TimerTask {
137 |
138 | @Override
139 | public void run() {
140 |
141 | String usedPercentValue = MemoryUtil.getUsedPercentValue();
142 | long availableMemory = MemoryUtil.getAvailableMemory();
143 | HashMap totalPssMap = MemoryUtil.getTotalPss(Constants.PROCESS_NAME_LIST);
144 |
145 | float memory = availableMemory / (float) 1024 / (float) 1024;
146 | DecimalFormat decimalFormat = new DecimalFormat("##0.00");
147 |
148 | final String[] content = new String[] {
149 | getString(R.string.used_percent_value, usedPercentValue),
150 | getString(R.string.available_memory, decimalFormat.format(memory)), getString(R.string.total_pss)};
151 |
152 | StringBuffer sb = new StringBuffer();
153 | sb.append(content[0]).append(",").append(content[1]).append("\r\n").append(content[2]);
154 |
155 | Iterator iterator = totalPssMap.entrySet().iterator();
156 | while(iterator.hasNext()) {
157 | Map.Entry entry = (Map.Entry) iterator.next();
158 | entry.getKey();
159 | sb.append(entry.getKey()).append("=").append("\r\n").append(entry.getValue()).append("\r\n");
160 | }
161 |
162 | Bundle data = new Bundle();
163 | data.putString("content", sb.toString());
164 |
165 | Message message = mHandler.obtainMessage(1);
166 | message.what = 1;
167 | message.setData(data);
168 |
169 | mHandler.sendMessage(message);
170 | }
171 | }
172 |
173 | @Override
174 | public void onFlingAway() {
175 | Log.i(TAG, "onFlingAway");
176 | }
177 |
178 | @Override
179 | public void onMove(float x, float y) {
180 | Log.i(TAG, "onMove(" + x + "," + y + ")");
181 | }
182 |
183 | @Override
184 | public void onIconClick(View icon, float iconXPose, float iconYPose) {
185 | Log.i(TAG, "onIconClick(..)");
186 |
187 | Intent intent = new Intent(this, MainActivity.class);
188 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
189 | startActivity(intent);
190 | }
191 |
192 | @Override
193 | public void onIconDestroyed() {
194 | Log.i(TAG, "onIconDestroyed()");
195 | stopSelf();
196 | }
197 |
198 | private static class InnerHandler extends Handler {
199 |
200 | private WeakReference ref;
201 |
202 | public InnerHandler(CoreService service) {
203 | ref = new WeakReference<>(service);
204 | }
205 |
206 | @Override
207 | public void handleMessage(Message msg) {
208 | super.handleMessage(msg);
209 |
210 | CoreService service = ref.get();
211 | if (service == null) {
212 | return;
213 | }
214 |
215 | switch (msg.what) {
216 | case 1:
217 | Bundle data = msg.getData();
218 | String content = data.getString("content");
219 | service.mDescView.setText(content);
220 |
221 | break;
222 | }
223 | }
224 | }
225 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cundong/memory/util/MemoryUtil.java:
--------------------------------------------------------------------------------
1 | package com.cundong.memory.util;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.FileReader;
5 | import java.io.IOException;
6 | import java.util.ArrayList;
7 | import java.util.HashMap;
8 | import java.util.List;
9 |
10 | import android.app.ActivityManager;
11 | import android.app.ActivityManager.RunningAppProcessInfo;
12 | import android.content.Context;
13 | import android.os.Build;
14 | import android.os.Debug.MemoryInfo;
15 | import android.text.TextUtils;
16 | import android.util.Log;
17 |
18 | import com.cundong.memory.App;
19 | import com.cundong.memory.Constants;
20 | import com.jaredrummler.android.processes.ProcessManager;
21 | import com.jaredrummler.android.processes.models.AndroidAppProcess;
22 |
23 | /**
24 | * 类说明: 内存相关数据获取工具类
25 | *
26 | * @date 2015-4-18
27 | * @version 1.0
28 | */
29 | public class MemoryUtil {
30 |
31 | /**
32 | * getTotalPss,5.0+使用开源的android-processes解决方案,5.0以下使用系统api
33 | *
34 | * @param processNameList
35 | * @return
36 | */
37 | public static HashMap getTotalPss(ArrayList processNameList) {
38 |
39 | HashMap resultMap = new HashMap<>();
40 | ActivityManager activityMgr = (ActivityManager) App.getAppContext().getSystemService(Context.ACTIVITY_SERVICE);
41 |
42 | if (Build.VERSION.SDK_INT >= 21) {
43 | List list = ProcessManager.getRunningAppProcesses();
44 |
45 | if (list != null) {
46 | for (AndroidAppProcess processInfo : list) {
47 |
48 | if (processNameList.contains(processInfo.name)) {
49 | int pid = processInfo.pid;
50 | MemoryInfo[] memoryInfos = activityMgr.getProcessMemoryInfo(new int[]{pid});
51 |
52 | MemoryInfo memoryInfo = memoryInfos[0];
53 | int totalPss = memoryInfo.getTotalPss();
54 |
55 | resultMap.put(processInfo.name, new Long(totalPss));
56 | }
57 | }
58 | }
59 | } else {
60 | List list = activityMgr.getRunningAppProcesses();
61 | if (list != null) {
62 | for (RunningAppProcessInfo processInfo : list) {
63 |
64 | if (Constants.PROCESS_NAME_LIST.contains(processInfo.processName)) {
65 | int pid = processInfo.pid;
66 | MemoryInfo[] memoryInfos = activityMgr.getProcessMemoryInfo(new int[] { pid });
67 |
68 | MemoryInfo memoryInfo = memoryInfos[0];
69 | int totalPss = memoryInfo.getTotalPss();
70 |
71 | resultMap.put(processInfo.processName, new Long(totalPss));
72 | }
73 | }
74 | }
75 | }
76 |
77 | return resultMap;
78 | }
79 |
80 | /**
81 | * getTotalPss,5.0+使用开源的android-processes解决方案,5.0以下使用系统api
82 | *
83 | * @param processName
84 | * @return
85 | */
86 | public static long getTotalPss(String processName) {
87 |
88 | ActivityManager activityMgr = (ActivityManager) App.getAppContext().getSystemService(Context.ACTIVITY_SERVICE);
89 |
90 | if (Build.VERSION.SDK_INT >= 21) {
91 | List list = ProcessManager.getRunningAppProcesses();
92 |
93 | if (list != null) {
94 | for (AndroidAppProcess processInfo : list) {
95 | if (processInfo.name.equals(processName)) {
96 | int pid = processInfo.pid;
97 | MemoryInfo[] memoryInfos = activityMgr.getProcessMemoryInfo(new int[]{pid});
98 |
99 | MemoryInfo memoryInfo = memoryInfos[0];
100 | int totalPss = memoryInfo.getTotalPss();
101 |
102 | return totalPss;
103 | }
104 | }
105 | }
106 | } else {
107 | List list = activityMgr.getRunningAppProcesses();
108 | if (list != null) {
109 | for (RunningAppProcessInfo processInfo : list) {
110 |
111 | if (processInfo.processName.equals(processName)) {
112 | int pid = processInfo.pid;
113 | MemoryInfo[] memoryInfos = activityMgr.getProcessMemoryInfo(new int[] { pid });
114 |
115 | MemoryInfo memoryInfo = memoryInfos[0];
116 | int totalPss = memoryInfo.getTotalPss();
117 |
118 | return totalPss;
119 | }
120 | }
121 | }
122 | }
123 |
124 | return -1;
125 | }
126 |
127 | /**
128 | * 计算已使用内存的百分比
129 | *
130 | */
131 | public static String getUsedPercentValue() {
132 | String dir = "/proc/meminfo";
133 | try {
134 | FileReader fr = new FileReader(dir);
135 | BufferedReader br = new BufferedReader(fr, 2048);
136 | String memoryLine = br.readLine();
137 | String subMemoryLine = memoryLine.substring(memoryLine
138 | .indexOf("MemTotal:"));
139 | br.close();
140 | long totalMemorySize = Integer.parseInt(subMemoryLine.replaceAll(
141 | "\\D+", ""));
142 | long availableSize = getAvailableMemory() / 1024;
143 | int percent = (int) ((totalMemorySize - availableSize)
144 | / (float) totalMemorySize * 100);
145 | return percent + "%";
146 | } catch (IOException e) {
147 | e.printStackTrace();
148 | }
149 | return "";
150 | }
151 |
152 | /**
153 | * 获取可用内存
154 | *
155 | */
156 | public static long getAvailableMemory() {
157 |
158 | ActivityManager activityManager = (ActivityManager) App.getAppContext()
159 | .getSystemService(Context.ACTIVITY_SERVICE);
160 |
161 | ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo();
162 | activityManager.getMemoryInfo(mi);
163 |
164 | return mi.availMem;
165 | }
166 |
167 | public static void clearMemory(Context context) {
168 | ActivityManager activityManger = (ActivityManager) App.getAppContext().getSystemService(Context.ACTIVITY_SERVICE);
169 |
170 |
171 | if (Build.VERSION.SDK_INT >= 21) {
172 | List list = ProcessManager.getRunningAppProcesses();
173 |
174 | if (list != null) {
175 | for (AndroidAppProcess processInfo : list) {
176 |
177 | int myPid = android.os.Process.myPid();
178 |
179 | if(myPid == processInfo.pid) {
180 | continue;
181 | }
182 |
183 | if (!processInfo.foreground) {
184 | activityManger.killBackgroundProcesses(processInfo.getPackageName());
185 | }
186 | }
187 | }
188 | } else {
189 | List list = activityManger.getRunningAppProcesses();
190 |
191 | if (list != null) {
192 | for (int i = 0; i < list.size(); i++) {
193 | ActivityManager.RunningAppProcessInfo appProcessInfo = list.get(i);
194 |
195 | String[] pkgList = appProcessInfo.pkgList;
196 |
197 | if (appProcessInfo.importance >= ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
198 |
199 | for (int j = 0; j < pkgList.length; j++) {
200 |
201 | if (pkgList[j].equals(context.getPackageName())) {
202 | continue;
203 | }
204 |
205 | activityManger.killBackgroundProcesses(pkgList[j]);
206 | }
207 | }
208 | }
209 | }
210 | }
211 | }
212 | }
--------------------------------------------------------------------------------
/magnet/src/main/java/com/premnirmal/Magnet/Magnet.java:
--------------------------------------------------------------------------------
1 | package com.premnirmal.Magnet;
2 |
3 | import android.content.Context;
4 | import android.graphics.PixelFormat;
5 | import android.os.Handler;
6 | import android.os.Looper;
7 | import android.util.DisplayMetrics;
8 | import android.view.GestureDetector;
9 | import android.view.Gravity;
10 | import android.view.LayoutInflater;
11 | import android.view.MotionEvent;
12 | import android.view.View;
13 | import android.view.WindowManager;
14 | import android.widget.RelativeLayout;
15 |
16 | /**
17 | * Created by prem on 7/20/14.
18 | * Desc: Class holding the Magnet Icon, and performing touchEvents on the view.
19 | */
20 | public class Magnet implements View.OnTouchListener {
21 |
22 | protected static final int TOUCH_TIME_THRESHOLD = 200;
23 |
24 | protected View mIconView;
25 | protected RemoveView mRemoveView;
26 | protected WindowManager mWindowManager;
27 | protected WindowManager.LayoutParams mLayoutParams;
28 | protected Context mContext;
29 | protected GestureDetector mGestureDetector;
30 | protected boolean shouldStickToWall = true;
31 | protected boolean shouldFlingAway = true;
32 | protected IconCallback mListener;
33 | protected MoveAnimator mAnimator;
34 |
35 | protected long lastTouchDown;
36 | protected float lastXPose, lastYPose;
37 | protected boolean isBeingDragged = false;
38 | protected int mWidth, mHeight;
39 |
40 | protected int mInitialX = -1, mInitialY = -1;
41 | /**
42 | * Builder class to create your {@link Magnet}
43 | */
44 | public static class Builder {
45 |
46 | Magnet magnet;
47 |
48 | public Builder(Context context) {
49 | magnet = new Magnet(context);
50 | }
51 |
52 | /**
53 | * The Icon must have a view, provide a view or a layout using {@link #setIconView(int)}
54 | * @param iconView the view representing the icon
55 | * @return
56 | */
57 | public Builder setIconView(View iconView) {
58 | magnet.mIconView = iconView;
59 | magnet.mIconView.setOnTouchListener(magnet);
60 | return this;
61 | }
62 |
63 | /**
64 | * Use an xml layout to provide the button view
65 | * @param iconViewRes the layout id of the icon
66 | * @return
67 | */
68 | public Builder setIconView(int iconViewRes) {
69 | magnet.mIconView = LayoutInflater.from(magnet.mContext).inflate(iconViewRes, null);
70 | magnet.mIconView.setOnTouchListener(magnet);
71 | return this;
72 | }
73 |
74 | /**
75 | * whether your magnet sticks to the edge of your screen when you release it
76 | * @param shouldStick
77 | * @return
78 | */
79 | public Builder setShouldStickToWall(boolean shouldStick) {
80 | magnet.shouldStickToWall = shouldStick;
81 | return this;
82 | }
83 |
84 | /**
85 | * whether you can fling away your Magnet towards the bottom of the screen
86 | * @param shoudlFling
87 | * @return
88 | */
89 | public Builder setShouldFlingAway(boolean shoudlFling) {
90 | magnet.shouldFlingAway = shoudlFling;
91 | return this;
92 | }
93 |
94 | /**
95 | * Callback for when the icon moves, or when it isis flung away and destroyed
96 | * @param callback
97 | * @return
98 | */
99 | public Builder setIconCallback(IconCallback callback) {
100 | magnet.mListener = callback;
101 | return this;
102 | }
103 |
104 | /**
105 | *
106 | * @param shouldBeResponsive
107 | * @return
108 | */
109 | public Builder setRemoveIconShouldBeResponsive(boolean shouldBeResponsive) {
110 | magnet.mRemoveView.shouldBeResponsive = shouldBeResponsive;
111 | return this;
112 | }
113 |
114 | /**
115 | * you can set a custom remove icon or use the default one
116 | * @param removeIconResId
117 | * @return
118 | */
119 | public Builder setRemoveIconResId(int removeIconResId) {
120 | magnet.mRemoveView.setIconResId(removeIconResId);
121 | return this;
122 | }
123 |
124 | /**
125 | * you can set a custom remove icon shadow or use the default one
126 | * @param shadow
127 | * @return
128 | */
129 | public Builder setRemoveIconShadow(int shadow) {
130 | magnet.mRemoveView.setShadowBG(shadow);
131 | return this;
132 | }
133 |
134 | /**
135 | * Set the initial coordinates of the magnet
136 | * @param x
137 | * @param y
138 | * @return
139 | */
140 | public Builder setInitialPosition(int x, int y) {
141 | magnet.mInitialX = x;
142 | magnet.mInitialY = y;
143 | return this;
144 | }
145 |
146 | public Magnet build() {
147 | if(magnet.mIconView == null) {
148 | throw new NullPointerException("Magnet view is null! Must set a view for the magnet!");
149 | }
150 | return magnet;
151 | }
152 | }
153 |
154 |
155 | protected Magnet(Context context) {
156 | mContext = context;
157 | mGestureDetector = new GestureDetector(context, new FlingListener());
158 | mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
159 | mAnimator = new MoveAnimator();
160 | mRemoveView = new RemoveView(context);
161 | }
162 |
163 | /**
164 | * Show the Magnet i.e. add it to the Window
165 | */
166 | public void show() {
167 | addToWindow(mIconView);
168 | updateSize();
169 | if(mInitialX != -1 || mInitialY != -1) {
170 | setPosition(mInitialX, mInitialY, true);
171 | } else {
172 | goToWall();
173 | }
174 | }
175 |
176 | protected void addToWindow(View icon) {
177 | WindowManager.LayoutParams params = new WindowManager.LayoutParams(
178 | WindowManager.LayoutParams.WRAP_CONTENT,
179 | WindowManager.LayoutParams.WRAP_CONTENT,
180 | WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
181 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
182 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
183 | PixelFormat.TRANSLUCENT
184 | );
185 |
186 | try {
187 | mWindowManager.addView(icon, mLayoutParams = params);
188 | } catch(Exception e) {
189 | // ignore
190 | // if targetSdkVersion >=23 addView maybe crash here.
191 | // http://developer.android.com/intl/zh-cn/reference/android/Manifest.permission.html
192 | }
193 | }
194 |
195 | protected void updateSize() {
196 | final DisplayMetrics metrics = new DisplayMetrics();
197 | mWindowManager.getDefaultDisplay().getMetrics(metrics);
198 | mWidth = (metrics.widthPixels - mIconView.getWidth()) / 2;
199 | mHeight = (metrics.heightPixels - mIconView.getHeight()) / 2;
200 | }
201 |
202 | @Override
203 | public boolean onTouch(View view, MotionEvent event) {
204 | boolean eaten = false;
205 | if (shouldFlingAway) {
206 | eaten = mGestureDetector.onTouchEvent(event);
207 | }
208 | if (eaten) {
209 | flingAway();
210 | } else {
211 | float x = event.getRawX();
212 | float y = event.getRawY();
213 | int action = event.getAction();
214 | if (action == MotionEvent.ACTION_DOWN) {
215 | showRemoveView();
216 | lastTouchDown = System.currentTimeMillis();
217 | mAnimator.stop();
218 | updateSize();
219 | isBeingDragged = true;
220 | } else if (action == MotionEvent.ACTION_UP) {
221 | if (System.currentTimeMillis() - lastTouchDown < TOUCH_TIME_THRESHOLD) {
222 | if (mListener != null) {
223 | mListener.onIconClick(mIconView, x, y);
224 | }
225 | }
226 | hideRemoveView();
227 | isBeingDragged = false;
228 | eaten = false;
229 | goToWall();
230 | } else if (action == MotionEvent.ACTION_MOVE) {
231 | if (isBeingDragged) {
232 | move(x - lastXPose, y - lastYPose);
233 | }
234 | }
235 |
236 | lastXPose = x;
237 | lastYPose = y;
238 | }
239 | return eaten;
240 | }
241 |
242 | protected void flingAway() {
243 | if (shouldFlingAway) {
244 | int y = mContext.getResources().getDisplayMetrics().heightPixels / 2;
245 | int x = 0;
246 | mAnimator.start(x, y);
247 | if (mListener != null) {
248 | mListener.onFlingAway();
249 | }
250 | destroy();
251 | }
252 | }
253 |
254 | protected void showRemoveView() {
255 | if (mRemoveView != null && shouldFlingAway) {
256 | mRemoveView.show();
257 | }
258 | }
259 |
260 | protected void hideRemoveView() {
261 | if (mRemoveView != null && shouldFlingAway) {
262 | mRemoveView.hide();
263 | }
264 | }
265 |
266 | protected void goToWall() {
267 | if (shouldStickToWall) {
268 | float nearestXWall = mLayoutParams.x >= 0 ? mWidth : -mWidth;
269 | float nearestYWall = mLayoutParams.y > 0 ? mHeight : -mHeight;
270 | if (Math.abs(mLayoutParams.x - nearestXWall) < Math.abs(mLayoutParams.y - nearestYWall)) {
271 | mAnimator.start(nearestXWall, mLayoutParams.y);
272 | } else {
273 | mAnimator.start(mLayoutParams.x, nearestYWall);
274 | }
275 | }
276 | }
277 |
278 | protected void move(float deltaX, float deltaY) {
279 | mLayoutParams.x += deltaX;
280 | mLayoutParams.y += deltaY;
281 | if (mRemoveView != null && shouldFlingAway) {
282 | mRemoveView.onMove(mLayoutParams.x, mLayoutParams.y);
283 | }
284 | mWindowManager.updateViewLayout(mIconView, mLayoutParams);
285 | if (mListener != null) {
286 | mListener.onMove(mLayoutParams.x, mLayoutParams.y);
287 | }
288 | if (shouldFlingAway && !isBeingDragged && Math.abs(mLayoutParams.x) < 50
289 | && Math.abs(mLayoutParams.y - (mContext.getResources().getDisplayMetrics().heightPixels / 2)) < 250) {
290 | flingAway();
291 | }
292 | }
293 |
294 | /**
295 | * Destroys the magnet - removes the view from the WindowManager and calls
296 | * {@link IconCallback#onIconDestroyed()}
297 | */
298 | public void destroy() {
299 | mWindowManager.removeView(mIconView);
300 | if (mRemoveView != null) {
301 | mRemoveView.destroy();
302 | }
303 | if (mListener != null) {
304 | mListener.onIconDestroyed();
305 | }
306 | mContext = null;
307 | }
308 |
309 | /**
310 | * Set the position of the Magnet.
311 | * Note: must be called **after** {@link #show()} is called.
312 | * This will call {@link IconCallback#onMove(float, float)} on your listener
313 | * @param x the x position
314 | * @param y the y position
315 | * @param animate whether the Magnet should animate to that position. If false the Magnet
316 | * will simply just set its coordinates to the given position
317 | */
318 | public void setPosition(int x, int y, boolean animate) {
319 | if(animate) {
320 | mAnimator.start(x, y);
321 | } else {
322 | mLayoutParams.x = x;
323 | mLayoutParams.y = y;
324 | mWindowManager.updateViewLayout(mIconView, mLayoutParams);
325 | if (mListener != null) {
326 | mListener.onMove(mLayoutParams.x, mLayoutParams.y);
327 | }
328 | }
329 | }
330 |
331 | protected class MoveAnimator implements Runnable {
332 |
333 | protected Handler handler = new Handler(Looper.getMainLooper());
334 | protected float destinationX;
335 | protected float destinationY;
336 | protected long startingTime;
337 |
338 | protected void start(float x, float y) {
339 | this.destinationX = x;
340 | this.destinationY = y;
341 | startingTime = System.currentTimeMillis();
342 | handler.post(this);
343 | }
344 |
345 | @Override
346 | public void run() {
347 | if (mIconView != null && mIconView.getParent() != null) {
348 | float progress = Math.min(1, (System.currentTimeMillis() - startingTime) / 400f);
349 | float deltaX = (destinationX - mLayoutParams.x) * progress;
350 | float deltaY = (destinationY - mLayoutParams.y) * progress;
351 | move(deltaX, deltaY);
352 | if (progress < 1) {
353 | handler.post(this);
354 | }
355 | }
356 | }
357 |
358 | protected void stop() {
359 | handler.removeCallbacks(this);
360 | }
361 |
362 | }
363 | }
364 |
--------------------------------------------------------------------------------