前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android内存泄漏:谨慎使用getSystemService

Android内存泄漏:谨慎使用getSystemService

作者头像
技术小黑屋
发布2018-09-05 09:53:55
1.3K0
发布2018-09-05 09:53:55
举报
文章被收录于专栏:技术小黑屋

Android中有很多服务,比如PowerManager,AlarmManager,NotificationManager等,通常使用起来也很方便,就是使用Context.getSystemService方法来获得。

一次在公司开发项目开发中,突然LeakCanary弹出了一个内存泄漏的通知栏,不好,内存泄漏发生了。原因竟是和getSystemService有关。

为了排除干扰因素,我们使用一个简单的示例代码

1 2 3 4 5 6 7 8 9

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的内存泄漏报告。如下图。

http://7jpolu.com1.z0.glb.clouddn.com/device-2016-11-04-081558_compressed.png
http://7jpolu.com1.z0.glb.clouddn.com/device-2016-11-04-081558_compressed.png

奇怪了,为什么PowerManager会持有Activity的实例呢,按照理解,PowerManager应该是持有Application的Context对象的。

因此,我们有必要对PowerManager的源码分析一下

1.PowerManager会持有一个Context实例,具体使用Activity还是Application的Context取决于调用者。

1 2 3 4 5 6 7 8 9 10 11 12

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文件中

1 2

// The system service cache for the system services that are cached per-ContextImpl. final Object[] mServiceCache = SystemServiceRegistry.createServiceCache();

而Activity通过ContextImpl提供的setOuterContext方法设置mOuterContext

1 2 3

final void setOuterContext(Context context) { mOuterContext = context; }

因此Activity与ContextImpl的关系如下图

http://7jpolu.com1.z0.glb.clouddn.com/QQ20161108-0.png
http://7jpolu.com1.z0.glb.clouddn.com/QQ20161108-0.png

SystemServiceRegistry.java中获取PowerManager的实现。

1 2 3 4 5 6 7 8 9 10 11 12

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也可以解决问题。

1

PowerManager powerManager = (PowerManager)getApplicationContext().getSystemService(Context.POWER_SERVICE);

是不是都要使用Application Context?

然而并非如此

以Activity为例,一些和UI相关的服务已经优先进行了处理

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

@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服务

1 2 3 4 5 6 7 8 9 10

@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时要慎重考虑这样的问题。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 如何解决
    • 不使用静态持有PowerManager
      • 使用ApplicationContext
      • 是不是都要使用Application Context?
      • 那到底该用哪个Context
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档