概览
11. Android 进阶技巧
11.1 全局获取context
Application类,当程序启动,系统会自动将这个类进行初始化
流程:
- 自定义一个类继承自Application,并重写onCreate()方法,调用getApplicationContext()方法获取一个应用程序级别的context
- 创建一个静态方法将获取到的context返回
- 在Manifest文件中 application标签下配置
告知系统在程序启动时应该初始化 MyApplication类而不是默认的 Application类
android:name="com.example.intentadvancedtest.MyApplication"
- 在使用LitePal时也会配置application
这样会和MyApplication配置发生冲突,任何一个项目都只能配置一个Application
android:name="org.litepal.LitePalApplication"
- 在使用LitePal时,需要在MyApplication类中调用LitePal的初始化方法
LitePal.initialize(context);
代码示例:
public class MyApplication extends Application {
private static Context context;
@Override
public void onCreate() {
super.onCreate();
context = getApplicationContext();
//LitePal.initialize(context);
}
public static Context getContext(){
return context;
}
}
11.2 使用Intent传递对象
11.2.1 使用Serializable方式传递对象
流程:
- 创建模型类继承自Serializable
- 使用Intent传参:实例化对象并赋值, 使用intent.putExtra(“key”,对象名)传参
- 在目标活动中获取:使用getIntent().getSerializableExtra(“key”)获取,需要强转类型
代码示例:
//TODO: 步骤1,步骤2,传参
//在目标活动中,需要强转类型
getIntent().getSerializableExtra("key");
11.2.2 使用Parcelable方式传递对象:
- 创建模型类实现Parcelable接口,并实现方法,在writeToParcel()中使用 parcel.write()系列方法将数据写出
- 提供一个CREATOR的常量,在createFromParcel()方法中,实例化对象,使用in.read()系列方法读取数据并赋值给对象,最后将对象返回
- 使用Intent传参:实例化对象并赋值, 使用intent.putExtra(“key”,对象名)传参
- 在目标活动中获取:getIntent().getParcelableExtra(“key”)获取,需要强转类型
代码示例:
//创建模型类并实现Parcelable接口
public class Student implements Parcelable {
private String name;
private int age;
//TODO: get和set方法
public static final Creator<Student> CREATOR = new Creator<Student>() {
@Override
public Student createFromParcel(Parcel in) {
Student student = new Student();
//读取
student.name = in.readString();
student.age = in.readInt();
return student;
}
@Override
public Student[] newArray(int size) {
return new Student[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
//写出 name 和 age
parcel.writeString(name);
parcel.writeInt(age);
}
}
//
11.3 定制日志工具
基本固定写法:
public class LogUtil {
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;
//将level指定为VERBOSE就可以把所有日志都打印出来,指定为NOTHING后,将什么都不打印
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);
}
}
}
//TODO: 传参
//在目标活动中,需要强转类型
getIntent().getParcelableExtra("key");
11.4 创建定时任务
使用定时任务 Alarm机制,流程:
-
在服务中的onStartCommand()方法中
开启子线程去执行具体逻辑
使用getSystemService(ALARM_SERVICE)方法获取AlarmManager对象 -
使用manager.set()方法设置定时任务
- set()方法的3个参数:
- type:整形参数,用于指定AlarmManager的工作类型
- ELAPSED_REALTIME: 定时任务的触发时间从系统开机开始算起,但不会唤醒CPU
- ELAPSED_REALTIME_WAKEUP: 定时任务的触发时间从系统开机开始算起,但会唤醒CPU
- RTC:定时任务的触发时间从1970年1月1日0点开始算起,但不会唤醒CPU
- RTC_WAKEUP:定时任务的触发时间从1970年1月1日0点开始算起,但会唤醒CPU
- long:定时任务触发的时间
- pendingIntent:调用Service()或者getBroadcast()方法获取一个能够执行服务或广播的PendingIntent,当定时任务被触发时,服务的onStartCommand()方法或广播接收器的onReceive()方法就会执行
第一个参数type如果为前两个则使用 SystemClock.elapsedRealtime():获取到系统开机至今所经历时间的毫秒数
第一个参数type如果为后两个则使用 SystemClock.currentTimeMillis():获取1970年1月1日0点至今所经历时间的毫秒数
-
Alarm任务的触发时间不太准确,如果要求Alarm任务的执行时间必须准确无误,则使用setExact()方法
在Doze模式下要求Alarm任务能够正常执行调用以下两个方法,区别与set()、setExact()方法的区别一致- setAndAllowWhileIdle()
- setExactAndAllowWhileIdle()
代码示例:
public class LongRunningService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
public LongRunningService() {
}
@Override
public void onCreate() {
Log.d("LongRunningService","onCreate");
super.onCreate();
}
@Override
public void onDestroy() {
Log.d("LongRunningService","onDestroy");
super.onDestroy();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
LogUtil.d("LongRunningService","onStartCommand");
//Log.d("LongRunningService","onStartCommand");
new Thread(new Runnable() {
@Override
public void run() {
//开启子线程执行具体逻辑
// Log.d("LongRunningService","task");
LogUtil.d("LongRunningService","task");
}
}).start();
AlarmManager manager = (AlarmManager)getSystemService(ALARM_SERVICE);
int anHour = 1000;
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);
LogUtil.d("LongRunningService","task end");
return super.onStartCommand(intent, flags, startId);
}
}
11.5 多窗口模式
多窗口模式的生命周期:并不会改变原有生命周期,用户最近与哪个活动进行交互即设置为运行状态,将另外一个活动设置为暂停状态
进入到多窗口模式和横竖屏的切换默认是会重新创建活动
生命周期:
- 先启动一个活动,onCreate()、onStart()、onResume()方法依次执行
- 进入到多窗口模式 onPause()、onStop()、onDestroy()、onCreate()、onStart()、onResume()、onPause()
- 选中另外一个程序进入多窗口模式 另外一个活动 onCreate()、onStart()、onResume()方法依次执行
- 此时操作第一个活动,第一个活动执行 onResume()方法,另外一个活动执行 onPause()方法
如果不想在进入多窗口模式时或横竖屏切换活动被重新创建:
在Manifest文件中 activity标签中配置 android:configChanges=“orientation|keyboardHidden|screenSize|screenLayout”
禁用多窗口模式:在Manifest文件 application或activity标签中 配置 android:resizeableActivity=“false”