Android内存泄漏:谨慎使用getSystemService
Posted 初一十五啊
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android内存泄漏:谨慎使用getSystemService相关的知识,希望对你有一定的参考价值。
android中有很多服务,比如PowerManager,AlarmManager,NotificationManager等,通常使用起来也很方便,就是使用Context.getSystemService方法来获得。
一次在公司开发项目开发中,突然LeakCanary弹出了一个内存泄漏的通知栏,不好,内存泄漏发生了。原因竟是和getSystemService有关。
为了排除干扰因素,我们使用一个简单的示例代码
public class MainActivity extends AppCompatActivity
private static PowerManager powerManager;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
powerManager = (PowerManager)getSystemService(Context.POWER_SERVICE);
当退出MainActivity时,得到了LeakCanary的内存泄漏报告。如下图。
奇怪了,为什么PowerManager会持有Activity的实例呢,按照理解,PowerManager应该是持有Application的Context对象的。
因此,我们有必要对PowerManager的源码分析一下
1.PowerManager会持有一个Context实例,具体使用Activity还是Application的Context取决于调用者。
final Context mContext;
final IPowerManager mService;
final Handler mHandler;
/**
* @hide
*/
public PowerManager(Context context, IPowerManager service, Handler handler)
mContext = context;
mService = service;
mHandler = handler;
2.负责缓存服务的实现在ContextImpl.java文件中
// The system service cache for the system services that are cached per-ContextImpl.
final Object[] mServiceCache = SystemServiceRegistry.createServiceCache();
而Activity通过ContextImpl提供的setOuterContext方法设置mOuterContext
final void setOuterContext(Context context)
mOuterContext = context;
因此Activity与ContextImpl的关系如下图
SystemServiceRegistry.java中获取PowerManager的实现。
registerService(Context.POWER_SERVICE, PowerManager.class,
new CachedServiceFetcher<PowerManager>()
@Override
public PowerManager createService(ContextImpl ctx)
IBinder b = ServiceManager.getService(Context.POWER_SERVICE);
IPowerManager service = IPowerManager.Stub.asInterface(b);
if (service == null)
Log.wtf(TAG, "Failed to get power manager service.");
return new PowerManager(ctx.getOuterContext(),
service, ctx.mMainThread.getHandler());
);
创建具体的服务的实现为core/java/android/app/SystemServiceRegistry.java
如何解决
不使用静态持有PowerManager
因为static是一个很容易和内存泄漏产生关联的因素
- static变量与类的生命周期相同
- 类的生命周期等同于类加载器
- 类加载器通常和进程的生命周期一致
所以通过去除static可以保证变量周期和Activity实例相同。这样就不会产生内存泄漏问题。
使用ApplicationContext
除了上面的方法之外,传入Application的Context而不是Activity Context也可以解决问题。
PowerManager powerManager = (PowerManager)getApplicationContext().getSystemService(Context.POWER_SERVICE);
是不是都要使用Application Context?
然而并非如此
以Activity为例,一些和UI相关的服务已经优先进行了处理
@Override
public Object getSystemService(@ServiceName @NonNull String name)
if (getBaseContext() == null)
throw new IllegalStateException(
"System services not available to Activities before onCreate()");
if (WINDOW_SERVICE.equals(name))
return mWindowManager;
else if (SEARCH_SERVICE.equals(name))
ensureSearchManager();
return mSearchManager;
return super.getSystemService(name);
ContextThemeWrapper也优先处理了LayoutManager服务
@Override
public Object getSystemService(String name)
if (LAYOUT_INFLATER_SERVICE.equals(name))
if (mInflater == null)
mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
return mInflater;
return getBaseContext().getSystemService(name);
那到底该用哪个Context
- 如果服务和UI相关,则用Activity
- 如果是类似ALARM_SERVICE,CONNECTIVITY_SERVICE建议有限选用Application Context
- 如果出现出现了内存泄漏,排除问题,可以考虑使用Application Context
Android 知识点归整
Android 性能调优系列:https://0a.fit/dNHYY
Android 车载学习指南:https://0a.fit/jdVoy
Android Framework核心知识点笔记:https://0a.fit/acnLL
Android 音视频学习笔记:https://0a.fit/BzPVh
Jetpack全家桶(含Compose):https://0a.fit/GQJSl
Kotlin 入门到精进:https://0a.fit/kdfWR
Flutter 基础到进阶实战:https://0a.fit/xvcHV
Android 八大知识体系:https://0a.fit/mieWJ
Android 中高级面试题锦:https://0a.fit/YXwVq
后续如有新知识点,将会持续更新,尽请期待……
以上是关于Android内存泄漏:谨慎使用getSystemService的主要内容,如果未能解决你的问题,请参考以下文章
android 内存泄漏检测工具 LeakCanary 泄漏金丝雀