通过了解Application类的相关知识,掌握通过扩展Application类
实现维护应用全局状态
的方法。举个例子,微信里的用户名在多个组件中是共享的,那它就是一个全局状态。
1、Application类的简介
Application是维护应用全局状态的基类。Android系统会在启动应用进程时创建一个Application对象。下面是我们新建项目后自动生成
AndroidManifest.xml
(应用清单文件)的application。
<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/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
我们经常用到的activity,其name属性旨在告诉android在启动应用的时候用指定的类去实例化对象,而
aplication在不指定其name属性时,系统会使用framework层定义的Application类来实例化Application对象
。
下面我们来创建一个名为MyApp的Android项目,来看一下系统为我们实例化的Application对象。
只需修改一下MainActivity.java的代码:
我们再创建TAG标记时,可以输入logt然后按回车键,系统会快速生成所需代码
我们平常打印log时可以输入相应的logd、loge、logi及logwa等可以快速生成打印语句。
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity-app";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG, "onCreate: " + getApplication());
}
}
运行应用程序查看Logcat窗口打印日志,如下所示
如下图所示的继承关系,在清单文件中,
可以有多对activity标签,也可以有多对service等其他组件的标签,但是application标签只能有一个,并且整个应用只能有一个application对象。
如下图所示,appliation是context(上下文)的子类。其他组件可以通过context对象来访问application对象。
2、自定义Application类
Application这个功能虽好,但其却是系统定义的,我们无法去修改它,但是好在我们可以去扩展Application类,让Android系统使用自定义的Application类去创建Application对象。
我们要创建自定义的Application需要两个步骤:1、创建Application的子类;2、在清单文件中为application标签
添加android:name属性
接着我们上面创建好的项目继续编写代码,首先创建一个MyApplication类并继承Application类(此时先不写代码),紧接着,我们打开AndroidManifest.xml文件,为application标签添加name属性。
单击工具栏闪电样式的图标(
)
,查看打印的日志如下,此时创建的为com.demo.myapp.MyApplication@4f872d
我们自定义的Application对象
3、Application对象的生命周期
Application对象诞生于其他任何组件对象之前,并且一直存活,直至应用进程结束。
3.1、在不同的Activity中获取Application
下面我们来做个实验,思路如下:我们在主的Activity中添加一个按钮,点击这个按钮然后跳转到另外一个Activity,然后分别打印两个Activity得到的Application对象,然后对比两者是否一样。
1.打开默认布局文件activity_main.xml
,代码修改如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="@+id/btnStart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onClick"
android:text="Go to activity 2" />
</LinearLayout>
2.新建一个空的Activity(名字按默认即可),Main2Activity.java
代码如下:
public class Main2Activity extends AppCompatActivity {
private static final String TAG = "Main2Activity-app";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
setTitle("Main2Activity");
Log.d(TAG, "onCreate: " + getApplication() + "," + this);
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy: ");
}
}
3.然后修改MainActivity.java代码如下:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity-app";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setTitle("MainActivity");
Log.d(TAG, "onCreate: " + getApplication());
}
private void onClick(View view) {
switch (view.getId()) {
case R.id.btnStartActivity:
startActivity(new Intent(this, Main2Activity.class));
break;
default:
break;
}
}
}
4.运行程序,首先单击按钮进入Main2activity,单击返回按钮,再次单击按钮进入Main2activity,查看打印日志发现,从两个Activity中得到的Application对象是一致的,但是Main2activity从创建到销毁到再次被创建【观察末尾的哈希值
】。
3.2、在Service中获取Application
1.在主布局文件中再添加一个按钮,activity_main.xml代码如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="@+id/btnStartActivity"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onClick"
android:text="Go to activity 2" />
<Button
android:id="@+id/btnStartService"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onClick"
android:text="Start Service" />
</LinearLayout>
2.新建一个Service,修改让其继承IntentService,并实现其onHandleIntent方法,删除原来的onBind方法,代码如下:
public class MyService extends IntentService {
private static final String TAG = "MyService-app";
public MyService() {
super("MyService");
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
Log.d(TAG, "onHandleIntent: " + getApplication() + "," + this);
//休眠2秒
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy: ");
}
}
3.在MainActivity代码中的onClick方法中添加如下代码:
case R.id.btnStartService:
startService(new Intent(this, MyService.class));
break;
4.运行程序,单击Start Service
按钮,休眠2秒后服务自动销毁,再次单击按钮重复操作,打印日志如下所示,Application对象始终没有变化。至此,也就证明了Application的生命周期随着应用程序运行一直存活直至应用程序进程的结束而结束。
4、Application的回调函数
Application对象由Android系统管理,它的回调函数都运行于UI线程(即主线程)。常见的回调函数有:
- onCreate:Application被创建的时候调用此函数,在这里可以做一些初始化动作,但不要阻塞主线程。
- onConfigurationChanged:系统配置发生变更的时候被调用,比如屏幕方向的变化、系统语言的更改等。
- onLowMemory:系统内存吃紧的时候被调用,可以在这里释放内存。
1.继续修改MyApplication的代码,如下所示:
public class MyApplication extends Application {
private static final String TAG = "MyApplication-app";
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate: " + this);
//打印线程
Log.d(TAG, "onCreate: " + Thread.currentThread());
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
//输入logm按回车可以将方法和其参数都打印出来
Log.d(TAG, "onConfigurationChanged() called with: newConfig = [" + newConfig + "]");
}
@Override
public void onLowMemory() {
super.onLowMemory();
Log.d(TAG, "onLowMemory: ");
}
}
2.运行程序,按返回键,再次进入应用,然后改变屏幕方向,改变系统语言,打印log结果如下图所示:
说明:
- 序号123语句是在程序启动后打印出来的。
- 接着按返回键,然后再次进入应用,此时发现Application对象并没有改变,这里要说Android的一个机制,Android会尽量长地将应用留在内存中(后台),所以再次进入应用后,Application对象并没有改变(即并没有调用Application中
onCreate
方法);但是如果强制停止应用运行(在设置
→应用
点击应用后进入应用信息可以强制停止应用运行),则再次打开应用后,系统会再重新创建一个Application对象。- 序号4之后则是更改系统配置(这里进行了旋转屏幕和更改系统语言)调用了
onConfigurationChanged
函数的打印结果。
5、Application对象的作用
我们都知道,Android的组件之间都是松散耦合的,彼此之间通过Intent来传递数据。而Application对象全局可访问,且全程陪同应用进程所以特别适合完成以下任务:
- 共享全局状态,比如应用的用户名等。
- 初始化全应用所需的服务。
1.下面我们重新创建一个名为ApplicationFunction
项目,创建一个MyApplication
类并继承Appliction
类,代码如下:
public class MyApplication extends Application {
private String username;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public void onCreate() {
super.onCreate();
}
}
2.在AndroidManifest.xml
文件中注册,即添加application标签的name属性为android:name=".MyApplication"
3.修改activity_main.xml
布局文件中添加三个按钮为:
<Button
android:id="@+id/btnStartActivity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onClick"
android:text="Go to Activity 2" />
<Button
android:id="@+id/btnSetUsername"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onClick"
android:text="Set Username" />
<Button
android:id="@+id/btnGetUsername"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onClick"
android:text="Get Username" />
在MainActivity中添加代码如下:
public void onClick(View view) {
switch (view.getId()) {
case R.id.btnStartActivity:
startActivity(new Intent(this, Main2Activity.class));
break;
case R.id.btnSetUsername:
MyApplication app = (MyApplication) getApplication();
app.setUsername("chaixingsi");
Toast.makeText(MainActivity.this, "Set username to be " + app.getUsername(), Toast.LENGTH_SHORT).show();
break;
case R.id.btnGetUsername:
MyApplication app1 = (MyApplication) getApplication();
Toast.makeText(this, "username is " + app1.getUsername(), Toast.LENGTH_SHORT).show();
break;
}
}
4.新建空的Activity,在其中也添加一个按钮,用来获取用户名,并用Toast将信息弹出来,代码略。
5.运行程序,如下图所示。
6、Application对象vs.静态单例
静态单例也可以实现应用全局状态的共享,以共享otto(otto这里不会进行说明,后续会专门讲解)的Bus对象为例,我们分别:
- 用Application对象实现共享
- 用静态单例实现共享
思路:我们在主Activity中通过Application对象实现共享,在第二个Activity中使用静态单例实现共享。
.我们接着上面的项目ApplicationFunction
继续代码的编写,首先我们为otto添加依赖:
implementation 'com.squareup:otto:1.3.8'
6.1借助Application来实现共享
1.修改MyApplication
代码如下:
public class MyApplication extends Application {
private Bus bus;
public Bus getBus() {
return bus;
}
public void setBus(Bus bus) {
this.bus = bus;
}
@Override
public void onCreate() {
super.onCreate();
//在onCreate方法中创建Bus对象
bus = new Bus();
}
}
2.MainActivity
代码修改如下:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity-app";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onResume() {
super.onResume();
//注册
MyApplication app = (MyApplication) getApplication();
Bus bus = app.getBus();
bus.register(this);
Log.d(TAG, "onResume: " + bus);
}
@Override
protected void onPause() {
super.onPause();
//取消注册
MyApplication app = (MyApplication) getApplication();
Bus bus = app.getBus();
bus.unregister(this);
Log.d(TAG, "onPause: " + bus);
}
public void onClick(View view) {
switch (view.getId()) {
case R.id.btnStartActivity:
startActivity(new Intent(this, Main2Activity.class));
break;
}
}
}
6.2借助静态单例来实现共享
1.创建一个BusProvider的类,代码如下:
public class BusProvider {
public BusProvider() {
}
private static final Bus BUS = new Bus();
public static Bus getBus() {
return BUS;
}
}
2.Main2Activity的代码修改如下:
public class Main2Activity extends AppCompatActivity {
private static final String TAG = "Main2Activity-app";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume: " + BusProvider.getBus());
//注册
BusProvider.getBus().register(this);
}
@Override
protected void onPause() {
super.onPause();
Log.d(TAG, "onResume: " + BusProvider.getBus());
//取消注册
BusProvider.getBus().unregister(this);
}
}
6.3、运行结果
启动应用,点击按钮进入第二个Activity,日志打印结果如下所示:
6.4、对比
- 静态单例的模块化程度更好;
- Application本身就是一个context,所以有访问资源的能力;
- 我们可以给静态单例提供一个context参数,这样它也会有访问资源的能力;
- Application可以接收系统的回调,自动知悉系统环境变化;
- Application对象的生命周期有系统控制,静态单例则灵活的多,若是单例能实现需求就用单例。