目录
一 获取全局Context
活动本身就是一个 Context 对象。但当应用程序的架构逐渐开始复杂起来的时候,很多的逻辑代码都将脱离 Activity 类,但Android 提供了一个 Application 类,每当应用程序启动的时候,系统就会自动将这个类进行初始化。而我们可以定制一个自己的 Application 类,以便管理程序内一些全局的状态信息,比如说全局 Context。
public class MyApplication extends Application {
private static Context context;
@Override
public void onCreate() {
super.onCreate();
context =getApplicationContext();
}
public static Context getContext() {
return context;
}
}
二 使用Intent传递对象
2.1 Serializable方式
Serializable 是序列化的意思,表示将一个对象转换成可存储或可传输的状态。序列化后的对象可以在网络上进行传输,也可存储到本地。至于序列化的方法也很简单,只需要让一个类去实现 Serizable 这个接口就可以了。
public class Festival implements Serializable {
private String name;
private String data;
public Festival(String name,String data){
this.name = name;
this.data = data;
}
public String getName() {
return name;
}
public String getData() {
return data;
}
}
传数据:
Festival festival = new Festival("元旦节","1月1日");
Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
intent.putExtra("festival_object",festival);
startActivity(intent);
取数据:
Festival festival = (Festival)getIntent().getSerializableExtra("festival_object");
2.2 Parcelable方式
Parcelable 方式的实现原理是将一个完整的对象进行分解,而分解后的每一部分都是 Intent 所支持的数据类型,这样也就实现传递对象的功能了。
public class Person implements Parcelable {
private String name;
private int age;
//省略set和get方法
protected Person(Parcel in) {
name = in.readString();//读取name
age = in.readInt();//读取age
}
public static final Creator<Person> CREATOR = new Creator<Person>() {
@Override
public Person createFromParcel(Parcel in) {
return new Person(in);
}
@Override
public Person[] newArray(int size) {
return new Person[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);//写出name
dest.writeInt(age);//写入age
}
}
首先让 Person 类实现 Parcelable 接口,这样就必须重写 describeContents( )和 writeToParcel( )这两个方法。其中describeContents( )方法直接返回 0 就可以了,而 writeToParcel( )方法中需要调用 Parcel 的 writeXxx( )方法将 Person 类中的字段一一写出。
除此之外,还必须在 Person 类中提供一个名为 CREATOR 的常量,这里创建了 Parcelable.Creator 接口的一个实现,并将泛型指定为 Person。
接着需要重写 createFromParcel( )和 newArray( )这两个方法,在 createFromParcel( )方法中去读取刚才写出的 name 和 age 字段,并创建一个 Person 对象进行返回,其中 name和age都是调用 Parcel 的 readXxx( )方法读取到的,注意这里的读取顺序一定要和写出顺序完全相同。而newArray()方法中只需要new出一个Person数组,并使用方法中传入的 size 作为数组大小就可以了。
Person person = (Person)getIntent().getParcelableExtra("person_object");
三 日志工具
public class LogUtils {
public static final int VERBOSE = 1;
public static final int DEBUG = 2;
public static final int INFO = 3;
public static final int WARN = 4;
public static final int ERROR = 5;
public static final int NOTHING = 6;
public static int level = VERBOSE;
public static void v(String tag,String msg){
if (level <= VERBOSE){
Log.v(tag,msg);
}
}
public static void d(String tag,String msg){
if (level <= DEBUG){
Log.d(tag,msg);
}
}
public static void i(String tag,String msg){
if (level <= INFO){
Log.i(tag,msg);
}
}
public static void w(String tag,String msg){
if (level <= WARN){
Log.w(tag,msg);
}
}
public static void e(String tag,String msg){
if (level <= ERROR){
Log.e(tag,msg);
}
}
}
四 创建定时任务
4.1 Alarm 机制
Alarm 机制的用法不复杂,主要是借助 AlarmManager 类来实现的。比如想要设定一个任务在 10 秒钟后执行,可写成:
// 获取 AlarmManager 的实例
AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
// 设置触发时间
// SystemClock.elapsedRealtime() 获取系统开机至今所经历时间的毫秒数
// SystemClock.currentTimeMillis() 获取1970年1月1日0点至今所经历时间的毫秒数
long triggerAtTime = SystemClock.elapsedRealtime() + 10 * 1000;
// 3个参数:指定 AlarmManager 的工作类型、定时任务的触发时间、PendingIntent
// 其中AlarmManager 的工作类型有四种:
// ELAPSED_REALTIME 定时任务的触发时间从系统开机开始时算起,不会唤醒 CPU
// ELAPSED_REALTIME_WAKEUP 系统开机开始时算起,会唤醒 CPU
// RTC 从1970年1月1日0点开始算起,不会唤醒 CPU
// RTC_WAKEUP 从1970年1月1日0点开始算起,会唤醒 CPU
manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,triggerAtTime,pandingIntent);
举个例子,实现一个长时间在后台定时运行的服务,首先新建一个普通的服务 LongRunningService,将触发定时任务的代码写到 onStartCommand() 方法中,如下:
public class LongRunningService extends Service {
public LongRunningService() {
}
@Override
public IBinder onBind(Intent intent) {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
@Override
public void run() {
// 在这里执行具体的逻辑操作
}
}).start();
AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);
int anHour = 60 * 60 * 1000;//1小时的毫秒数
long triggerAtTime = SystemClock.elapsedRealtime() + anHour;
Intent i = new Intent(this,LongRunningService.class);
PendingIntent pi = PendingIntent.getService(this,0,i,0);
manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,triggerAtTime,pi);
return super.onStartCommand(intent, flags, startId);
}
}
然后,要启动定时服务时调用如下代码即可:
Intent intent = new Intent(context,LongRunningService.class);
context.startService(intent);
值得注意的是,从 Android 4.4开始,由于系统在耗电方面的优化,Alarm 任务的触发时间变得不准确,可能会延迟一段时间后再执行。当然,使用 AlarmManager 的 setExact() 方法来替代 set() 方法,基本上可以保证任务准时执行。
4.2 Doze模式
在 Android 6.0中,谷歌加入了一个全新的 Doze 模式,可以极大幅度地延长电池的使用寿命。在 6.0 及以上系统的设备上,若未插接电源,处于静止状态(7.0中删除了这一条件),且屏幕关闭了一段时间之后,就会进入到 Doze 模式。在 Doze 模式下,系统会对 CPU、网络、Alarm 等活动进行限制,从而延长电池的使用寿命。
当然,系统不会一直处于 Doze 模式,而是会间歇性的退出一小段时间,在这段时间应用可以去完成它们的同步操作、Alarm 任务等,其工作过程如下:
Doze 模式的工作过程
Doze 模式下受限的功能有:
(1)网络访问被禁止
(2)系统忽略唤醒CPU或屏幕操作
(3)系统不再执行WIFI扫描
(4)系统不再执行同步任务
(5)Alarm 任务将会在下次退出 Doze 模式时执行
特殊需求,要 Alarm 任务在 Doze 模式下也必须正常执行,则可以调用 AlarmManager 的 setAndAllowWhileIdle() 或 setExactAndAllowWhileIdle() 方法。
五 多窗口模式编程
5.1 进入多窗口模式
在 OverView 列表界面长按任意一个活动的标题,将该活动拖到屏幕突出显示的区域,则可以进入多窗口模式。打开任意一个程序,长按 OverView 按钮,也可以进入多窗口模式。
5.2 多窗口模式下的生命周期
这一块是比较重要的。多窗口模式并不会改变活动原有的生命周期,只是会将用户最近交互过的那个活动设置为运行状态,而将多窗口模式下另外一个可见活动设置为暂停状态。若这时用户又去和暂停的活动进行交互,那么该活动就变成运行状态,之前处于运行状态的活动变成暂停状态。
进入多窗口模式时活动会被重新创建,若要改变这一默认行为,可以在 AndroidManifest.xml 中对活动添加如下配置:
android:configChanges="orientation|keyboardHidden|screenSize|screenLayout"
添加这行配置后,不管是进入多窗口模式还是横竖屏切换,活动都不会被重新创建,而是会将屏幕发生变化的事件通知到 Activity 的 onConfigurationChanged() 方法中。因此,若要在屏幕发生变化时进行相应的逻辑处理,那么在活动中重写 onConfigurationChanged() 方法即可。
5.3 禁用多窗口模式
在 AndroidManifest.xml 中,只要在Activity 或者 application中添加 android:resizeableActivity="false" 即可。
六 Lambda 表达式
Lambda表达式本质上是一种一名方法,它既没有方法名,也没有访问修饰符和返回值类型,使用它来编写代码将会更加简介,也更加易读。
Android Studio 2.1.1之前,需要自己导入插件
dependencies {
classpath 'me.tatarka:gradle-retrolambda:3.2.0'
}
在 Module 的 build.gradle 中添加如下代码
// 应用插件
apply plugin: 'me.tatarka.retrolambda'
// 支持Java8
android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
Android Studio 2.1.1之后(支持lambda插件)
android {
defaultConfig {
jackOptions {
// 打开jack编译器
enabled true
}
}
// 编译支持Java8
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}