全局获取Context的技巧|青训营笔记

201 阅读3分钟

这是我参与「第四届青训营 」笔记创作活动的第12天

全局获取Context的技巧

  • 有很多地方都需要用到Context,弹出Toast的时候 需要,启动Activity的时候需要,发送广播的时候需要,操作数据库的时候需要,使用通知的时 候需要……

  • 而Activity本身就是一个Context对象。但是,当应用程序的架构逐渐开始复杂起来的时候, 很多逻辑代码将脱离Activity类,但此时又恰恰需要使用Context 例如,一个Toast.kt文件,并在这里对Toast的用法进行 了封装,代码如下所示:

fun String.showToast(context: Context, duration: Int = Toast.LENGTH_SHORT) { 
 Toast.makeText(context, this, duration).show() 
} 
 
fun Int.showToast(context: Context, duration: Int = Toast.LENGTH_SHORT) { 
 Toast.makeText(context, this, duration).show() 
} 
  • 可以看到,由于Toast的makeText()方法要求传入一个Context参数,但是当前代码既不在 Activity当中,也不在Service当中,是没有办法直接获取Context对象的。于是这里我们只好 给showToast()方法添加了一个Context参数,让调用showToast()方法的人传递一个 Context对象进来。
  • 下面我们就来学习一种技巧,让你在项目的任何地方都能够轻松获取 Context。
  • Android提供了一个Application类,每当应用程序启动的时候,系统就会自动将这个类进行 初始化。而我们可以定制一个自己的Application类,以便于管理程序内一些全局的状态信 息,比如全局Context。 定制一个自己的Application其实并不复杂,首先需要创建一个MyApplication类继承自 Application,代码如下所示:
class MyApplication : Application() { 
 
 companion object { 
 lateinit var context: Context 
 } 
 
 override fun onCreate() { 
 super.onCreate() 
 context = applicationContext 
 } 
 
} 

可以看到,MyApplication中的代码非常简单。这里我们在companion object中定义了一 个context变量,然后重写父类的onCreate()方法,并将调用 getApplicationContext()方法得到的返回值赋值给context变量,这样我们就可以以静 态变量的形式获取Context对象了。

  • 需要注意的是,将Context设置成静态变量很容易会产生内存泄漏的问题,所以这是一种有风 险的做法。 但是由于这里获取的不是Activity或Service中的Context,而是Application中的Context, 它全局只会存在一份实例,并且在整个应用程序的生命周期内都不会回收,因此是不存在内存 泄漏风险的。那么我们可以使用如下注解,让Android Studio忽略上述警告提示:
class MyApplication : Application() { 
 
 companion object { 
 @SuppressLint("StaticFieldLeak") 
 lateinit var context: Context 
 } 
 ... 
} 

接下来我们还需要告知系统,当程序启动的时候应该初始化MyApplication类,而不是默认的 Application类。这一步也很简单,在AndroidManifest.xml文件的标签下 进行指定就可以了,代码如下所示:

<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
 package="com.example.materialtest"> 
 <application 
 android:name=".MyApplication" 
 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/AppTheme"> 
 ... 

 </application> 
</manifest>·

这样我们就实现了一种全局获取Context的机制,之后不管你想在项目的任何地方使用 Context,只需要调用一下MyApplication.context就可以了。

  • 那么接下来我们再对showToast()方法进行优化,代码如下所示:
fun String.showToast(duration: Int = Toast.LENGTH_SHORT) { 
Toast.makeText(MyApplication.context, this, duration).show() 
} 

fun Int.showToast(duration: Int = Toast.LENGTH_SHORT) { 
Toast.makeText(MyApplication.context, this, duration).show() 
} 

可以看到,showToast()方法不需要再通过传递参数的方式得到Context对象,而是调用一下 MyApplication.context就可以了。
这样showToast()方法的用法也得到了进一步的精 简,现在只需要使用如下写法就能弹出一段文字提示: "This is Toast".showToast()