1. 全局获取 Context 技巧
Android 提供了一个 Application 类,每当应用程序启动的时候,系统就会自动将这个类进行
初始化。而我们可以定制一个自己的 Application 类,以便于管理程序内一些全局的状态信
息,比如说全局 Context 。
public class MyApplication extends Application {
private static Context context;
@Override
public void onCreate() {
//获取一个程序级别的Context
context = getApplicationContext();
}
public static Context getContext() {
return context;
}
}
接下来我们需要告知系统,当程序启动的时候应该初始化 MyApplication 类,而不是默认的
Application 类。在 AndroidManifest.xml 文件的 标签下进行指定就可以了。
<application
android:name="com.example.networktest.MyApplication"
...>
2. 使用 Intent 传递对象
2.1 Serializable 方式
让类实现序列号接口
public class Person implements Serializable{
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
传输及获取
// FirstActivity 中
Person person = new Person();
person.setName("Tom");
person.setAge(20);
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
intent.putExtra("person_data", person);
startActivity(intent);
// SecondActivity
Person person = (Person) getIntent().getSerializableExtra("person_data");
2.2 Parcelable 方式
Parcelable 方式的实现原理是将一个完整的对象进行分解,而分解后的每一部分都是 Intent 所支持的数据类型,这样也就实现传递对象的功能了。
public class Person implements Parcelable {
private String name;
private int age;
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name); // 写出 name
dest.writeInt(age); // 写出 age
}
public static final Parcelable.Creator<Person> CREATOR=new Parcelable.Creator<Person>() {
@Override
public Person createFromParcel(Parcel source) {
Person person=new Person();
person.name=source.readString(); // 读取 name
person.age=source.readInt(); // 读取 age
return person;
}
@Override
public Person[] newArray(int size) {
return new Person[size];
}
};
}
- 首先我们让 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 作为数组大小就可以了。
传输及获取
// FirstActivity 中
Person person = new Person();
person.setName("Tom");
person.setAge(20);
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
intent.putExtra("person_data", person);
startActivity(intent);
// SecondActivity
Person person = (Person) getIntent().getParcelableExtra("person_data");
对比一下,Serializable 的方式较为简单,但由于会把整个对象进行序列化,因此效率方面会比 Parcelable 方式低一些,所以在通常情况下还是更加推荐使用 Parcelable 的方式来实现 Intent 传递对象的功能。
3. 定时任务
3.1 Timer 和 Alarm
Android 中的定时任务一般有两种实现方式,一种是使用 Java API 里提供的 Timer 类,一种是使用 Android 的 Alarm 机制。两种方式在大部分情况下都能实现类似的效果,但是 Timer 类有一个缺点,它并不适合用于那些需要长期在后台执行的定时任务。一般情况下,为了能让手机电池更加耐用,每种手机都有自己的休眠策略,Android 手机会在长时间不操作的情况下自动进入休眠状态,这就有可能导致 Timer 中的定时任务无法正确运行。而 Alarm 则具有唤醒 CPU 的功能 ,它可以保证在大多数情况下需要执行定时任务的时候 CPU都能正常工作。(唤醒 CPU 和唤醒屏幕并不是一个概念)
3.2 Alarm
Alarm 主要借助 AlarmManager 类来实现。
获取一个 AlarmManager 实例
AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
调用 AlarmManager 的 set()方法设置一个定时任务了,比如说想要设定一个任务在 10 秒钟后执行,就可以写成:
long triggerAtTime = SystemClock.elapsedRealtime() + 10 * 1000;
manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pendingIntent);
// 或者写成
long triggerAtTime = System.currentTimeMillis() + 10 * 1000;
manager.set(AlarmManager.RTC_WAKEUP, triggerAtTime, pendingIntent);
- 第一个参数是一个整型参数,用于指定 AlarmManager 的工作类型
- ELAPSED_REALTIME 表示让定时任务的触发时间从系统开机开始算起,但不会唤醒 CPU。
- ELAPSED_REALTIME_WAKEUP 同样表示让定时任务的触发时间从系统开机开始算起,但会唤醒 CPU。
- RTC 表示让定时任务的触发时间从 1970 年 1月 1 日 0 点开始算起,但不会唤醒 CPU。
- RTC_WAKEUP 同样表示让定时任务的触发时间从1970 年 1 月 1 日 0 点开始算起,但会唤醒 CPU。
- 第二个参数,定时任务触发的时间,以毫秒为单位。
- 三个参数是一个 PendingIntent.这里我们一般会调用 etBroadcast() 方法来获取一个能够执行广播的 PendingIntent。这样当定时任务被触发的时候,广播接收器的 onReceive() 方法就可以得到执行。
- 由于安卓官方为了 CUP 节能考虑,把相近时间唤醒 CPU 的服务都放到一期来启动,需要精确时间唤醒的可以把 set() 换成 setExact()
- 使用 SystemClock.elapsedRealtime() 方法可以获取到系统开机至今所经历时间的毫秒数,
- 使用 System.currentTimeMillis() 方法可以获取到 1970 年 1 月 1 日 0 点至今所经历时间的毫秒数。
实现定时启动的服务
创建服务,并定时发出广播
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
@Override
public void run() {
Log.d(TAG, "当前时间是"+ new Date().toString());
}
}).start();
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
int HowLong = 60 * 60 * 1000; //设定定时时间
long triggerAtTime = SystemClock.elapsedRealtime() + HowLong; //加上系统开机时间
Intent intent1 = new Intent(this, AlarmReceiver.class); //创建Intent指向广播接收器
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent1, 0);//调用 getBroadcast()方法来获取一个能够执行广播的 PendingIntent
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pendingIntent);//设置定时唤醒,并发出广播
return super.onStartCommand(intent, flags, startId);
}
在广播中执行开始下一次定时任务
public class AlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Intent AlarmIntent;
AlarmIntent = new Intent(context,LongRunningService.class);
context.startService(AlarmIntent);
}
}
在需要的地方首次启动服务
Intent intent = new Intent(this,LongRunningService.class);
startService(intent);