I'm currently using Android Studio to develop live wallpapers using the wallpaper service.
As a result of the verification, it was found that a memory leak occurs and disappear(?) when the following operations are executed, but I do not know the solution.
How can I resolve a memory leak?
- Run activity (If there is no Activity with android.intent.category.LAUNCHER set, in my case Leak Canary will start.) , and close.
- Set my livewallpaper to Home screen and Lock Screen , and close.(the background will be black.)
- Start new profiler session in Android Studio to check memory.
- Set other wallpaper(ex. Solid colors) to Home screen and Lock Screen , and close.(the background will change)
Leak Canary detects and notifies you of leaks.(The data dumped at this point will be described later) You can also check that Memory remains in Profiler.
But if I ignore the notification, start Leak Canary and just close it, the memory profiler goes to 0 and stops.
If you select the previously displayed Leak Canary notification, you will see the following text: 「All retained objects were garbage collected Tap to dismiss」
Wallpaper.java
package Wallpaper;
import android.service.wallpaper.WallpaperService;
import android.util.Log;
public class Wallpaper extends WallpaperService{
@Override
public Engine onCreateEngine() {
return new Engine();
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.wallpapertest">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.WallpaperTest">
<!-- <activity-->
<!-- android:name=".MainActivity"-->
<!-- android:label="@string/app_name"-->
<!-- android:theme="@style/Theme.WallpaperTest.NoActionBar">-->
<!-- <intent-filter>-->
<!-- <action android:name="android.intent.action.MAIN" />-->
<!-- <category android:name="android.intent.category.LAUNCHER" />-->
<!-- </intent-filter>-->
<!-- </activity>-->
<service
android:name="Wallpaper"
android:label="TestWallpaper"
android:permission="android.permission.BIND_WALLPAPER">
<intent-filter>
<action android:name="android.service.wallpaper.WallpaperService" />
</intent-filter>
<meta-data
android:name="android.service.wallpaper"
android:resource="@xml/wallpaper" />
</service>
</application>
</manifest>
dumped by Leak Canary
┬───
│ GC Root: Global variable in native code
│
├─ android.service.wallpaper.WallpaperService$IWallpaperEngineWrapper instance
│ Leaking: UNKNOWN
│ Retaining 614 B in 3 objects
│ this$0 instance of com.example.wallpapertest.Wallpaper
│ ↓ WallpaperService$IWallpaperEngineWrapper.this$0
│ ~~~~~~
╰→ com.example.wallpapertest.Wallpaper instance
Leaking: YES (ObjectWatcher was watching this because com.example.
wallpapertest.Wallpaper received Service#onDestroy() callback)
Retaining 1.4 kB in 16 objects
key = 258eebec-e068-4a18-8b80-f4044dd74ca4
watchDurationMillis = 17673
retainedDurationMillis = 11652
mApplication instance of android.app.Application
mBase instance of android.app.ContextImpl
METADATA
Build.VERSION.SDK_INT: 30
Build.MANUFACTURER: Google
LeakCanary version: 2.6
App process name: com.example.wallpapertest
Stats: LruCache[maxSize=3000,hits=1850,misses=54308,hitRate=3%]
RandomAccess[bytes=2722909,reads=54308,travel=19446709706,range=16694011,size=22
214598]
Heap dump reason: user request
Analysis duration: 634134 ms
build.gradle(:app)
plugins {
id 'com.android.application'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "com.example.wallpapertest"
minSdkVersion 28
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
//for LeakCanary
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
testInstrumentationRunnerArgument "listener", "leakcanary.FailTestOnLeakRunListener"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
buildFeatures {
viewBinding true
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.navigation:navigation-fragment:2.3.5'
implementation 'androidx.navigation:navigation-ui:2.3.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
//for LeakCanary
def leakcanary_version = '2.6'
debugImplementation "com.squareup.leakcanary:leakcanary-android:$leakcanary_version"
androidTestImplementation "com.squareup.leakcanary:leakcanary-android-instrumentation:$leakcanary_version"
}
Other
- Android Studio 4.2.1
- AVD Pixel3a API30(Android11)
- Clicking on the profiler's memory graph causes an IDE internal error, so I can't see the details.
- % java --version
openjdk 11.0.8 2020-07-14
OpenJDK Runtime Environment (build 11.0.8+10-b944.6916264)
OpenJDK 64-Bit Server VM (build 11.0.8+10-b944.6916264, mixed mode)
I suspect the Wallpaper Service has some reference. I created another activity as a trial, executed 1-4, checked for a memory leak, and then opened and closed the created activity, and the leak disappeared.
I'm beginner so ,would very much like to hear what you have to say. Any ideas or suggestions would be welcome.
Profiler Image [1]: https://i.stack.imgur.com/wQvts.png
Add: Apparently, starting an activity other than LeakCanary resolves the leak. This is an Android spec and may not be a memory leak.