首页 > 系统相关 >Android内存泄漏:谨慎使用getSystemService

Android内存泄漏:谨慎使用getSystemService

时间:2022-11-09 23:35:46浏览次数:70  
标签:PowerManager SERVICE 内存 Context Activity 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的内存泄漏报告。如下图。

Android内存泄漏:谨慎使用getSystemService_内存泄漏

奇怪了,为什么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的关系如下图

Android内存泄漏:谨慎使用getSystemService_内存泄漏_02

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

如需了解更多关于Context的内存泄漏,请阅读

所以,当我们再次使用getSystemService时要慎重考虑这样的问题。


标签:PowerManager,SERVICE,内存,Context,Activity,Android,getSystemService
From: https://blog.51cto.com/u_3987305/5838975

相关文章

  • 记一场 Android 技术答疑
    之前在Stuq的Android课程中有幸分享了一些关于优化的问题,后期又处理了一些来自网友的问题,这里简单以文字形式做个整理.网络IO应该在哪种形式的线程中执行首先网络IO一般耗......
  • 在 Android 中如何确定 App(Activity) 的启动者
    最近在帮忙定位一个问题,涉及到某个应用自动启动了,为了确定是谁调用的,使用如下的日志进行查看(注:为了简单考虑,下面的启动者为launcher)(pre_release|✔)%adblogcat|grep......
  • Android WebView 诊断与排查问题的方法和技巧
    WebView,是安卓中很重要的一个组件,我们的应用中集成WebView后,可能会遇到各种各样的问题,这里简单介绍一些AndroidWebView诊断与排查问题的方法,希望对于大家有这方面的问题的......
  • java内部类 内存泄露
    Java语言中,非静态内部类的主要作用有两个:当内部类只在外部类中使用时,匿名内部类可以让外部不知道它的存在,从而减少了代码的维护工作。当内部类持有外部类时,它就可以直......
  • Android开发 Ripple涟漪效果
    前言此博客讲解Android5.0版本之后的涟漪效果的使用 简单的使用例子ripple_ic_bg.xml<?xmlversion="1.0"encoding="utf-8"?><ripplexmlns:android="http://......
  • android studio 升级 Android Studio Dolphin | 2021.3.1 Patch 1
    androidstudio升级AndroidStudioDolphin|2021.3.1Patch1后,xml布局预览界面报错一开始以为是那些警告导致的,有很多黄色的xml警告,比如命名的名字不是英文,或者设置......
  • Spring Boot 引起的 “堆外内存泄漏”,太坑了,快看看你什么版本!
    作者:纪兵,2015年加入美团,目前主要从事酒店C端相关的工作。原文:https://tech.meituan.com/2019/01/03/spring-boot-native-memory-leak.html背景为了更好地实现对项目的管......
  • 直播平台怎么搭建,Android与Js互调之传递图片
    直播平台怎么搭建,Android与Js互调之传递图片添加addJavascriptInterface注解方法H5VerificationJavascriptInterface对象映射 publicclassH5VerificationJavascrip......
  • Java JVM的内存使用
    内存总览堆:运行时数据区域,所有类实例和数组的内存均从此处分配,堆是在Java虚拟机启动时创建的;非堆:非堆就是JVM留给自己用的,所有方法区、JVM内部处理或优化所需的内......
  • 深入剖析Android应用开发--视频
    ​​深入剖析Android应用开发​​​​http://v.51work6.com/courseInfoRedirect.do?action=courseInfo&courseId=240568​​Android作为一款为移动终端打造的开源手机操作平......