那些Android开发者必备的技能
这都0202年了,Android手机早已是人手一部,随着5G的推广,Android手机会出现新变化,这对Android开发者来说既是契机又是挑战。时代在变化,技术在更新,作为一名Android开发者,有些开发技巧必须掌握,这样能提高我们的开发效率
全局获取Context
当应用程序的架构逐渐复杂的时候,很多逻辑代码都将脱离Activity类,若某一个逻辑需要获取Context,那需要通过Application类来实现。
Android提供了一个Application类,每当应用程序启动的时候,系统就会自动将这个类进行初始化。开发者可以定制Application类来管理全局状态信息,例如Context。
public class MyApplication extends Application { private static Context context; @Override public void onCreate() { //获取应用Context context = getApplicationContext(); } //通过这个静态方法从非Activity类中获取Context public static Context getContext() { return context; } }
通过上述代码可以在其他类中获取Context,除此之外,还需要在AndroidManifest.xml文件更改默认的Application,这样程序启动时就会初始化自定义Application类了(每个项目只能配置一个Application):
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="cn.chenjianlink.android.test" android:versionCode="1" android:versionName="1.0" > ... <application android:name="cn.chenjianlink.android.MyApplication" ...> ... </application> </manifest>
用Intent传递对象
使用用Intent来传递对象通常有两种实现方式:Serializable和Parcelable
Serializable方式
Serializable表示将一个对象转换成可存储或可传输的状态。序列化后的对象可以在网络上进行传输,也可以存储到本地。实现Serializable序列化方式只需要一个类实现Serializable接口,然后为类中要序列化的字段添加get、set方法:
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; } }
将这个类的对象传入Intent对象中:
Person person = new Person(); person.setName("chenjian"); person.setAge(22); Intent intent = new Intent(MainActivity.this, MyActivity.class); intent.putExtra("person_data", person); startActivity(intent);
要从Intent中获取这个对象则通过如下代码:
Person person = (Person) getIntent().getSerializableExtra("person_data");
- getSerializableExtra():获取通过参数传递过来的序列化对象,只适用Serializable方式
Parcelable方式
Parcelable方式的实现原理是将一个完整的对象进行分解,而分解后的每一部分都是Intent所支持的数据类型,这样就能传递对象。
要通过Parcelable方式传递对象,则需要实现Parcelable接口,重写describeContents()和writeToParcel()这两个方法
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); dest.writeInt(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]; } }; }
- describeContents()方法一般直接返回0
- 在writeToParcel()方法中调用Parcel类的writeXxx()方法将要序列化的字段进行写出
- 必须提供一个名为CREATOR的常量,一般是Parcelable.Creator接口的一个实现。重写createFromParcel()和newArray()这两个方法
- 在createFromParcel()方法中调用readXxx()方法获取字段,读取顺序与writeToParcel()方法中写入顺序一致,最后返回类对象
- newArray()方法中需要new出一个类的数组,并使用方法中传入的size作为数组大小
定制日志工具
在开发过程中会经常用到日志工具,在项目中会有大量与日志相关的代码,项目上线以后这些日志会照常打印,有可能会泄露机密数据,但在项目上线前将这些日志代码删除也不现实,后期维护的时候仍需要这些日志,对付这种情况,能自由控制日志的打印是最好的方法,开发时打印日志,上线时屏蔽日志。
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; 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); } } }
通过设置level的数值来实现对日志的打印与屏蔽
定时任务
Android中的定时任务一般有两种实现方式,一种是使用Java API里提供的Timer类,一种是使用Android的Alarm机制。为了能让电池更加耐用,每种手机都会有自己的休眠策略,Android手机就会在长时间不操作的情况下自动让CPU进入到睡眠状态,这就有可能导致Timer中的定时任务无法正常运行。Alarm则具有唤醒CPU的功能,它可以保证在大多数情况下需要执行定时任务的时候CPU都能正常工作
Alarm机制
Alarm机制需要借助AlarmManager类来实现,通过调用Context的getSystemService()方法来获取实例:
AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
来调用AlarmManager的set()方法就可以设置一个定时任务:
long triggerAtTime = SystemClock.elapsedRealtime() + 10 * 1000; manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pendingIntent);
set()方法接收三个参数:
- 第一个参数是一个整型参数,用于指定AlarmManager 的工作类型:
- ELAPSED_REALTIME:让定时任务的触发时间从系统开机开始算起,不会唤醒CPU
- ELAPSED_REALTIME_WAKEUP:让定时任务的触发时间从系统开机开始算起,会唤醒CPU
- RTC:让定时任务的触发时间从1970年1月1日0点开始算起,不会唤醒CPU
- RTC_WAKEUP:让定时任务的触发时间从1970年1月1日0点开始算起,会唤醒CPU。
- 使用SystemClock.elapsedRealtime() 方法可以获取到系统开机至今所经历时间的毫秒数,使用System.currentTimeMillis() 方法可以获取到1970年1月1日0点至今所经历时间的毫秒数
- 第二个参数是定时任务触发的时间,以毫秒为单位。如果第一个参数使用的是ELAPSED_REALTIME或ELAPSED_REALTIME_WAKEUP,传入开机至今的时间再加上延迟执行的时间。如果第一个参数使用的是RTC或RTC_WAKEUP,则传入1970年1月1日0点至今的时间再加上延迟执行的时间。
- 第三个参数是PendingIntent对象
从Android 4.4系统开始,系统在耗电性方面进行的优化,会自动检测目前有多少Alarm任务存在,然后将触发时间相近的几个任务放在一起执行,大幅度地减少CPU被唤醒的次数,从而有效延长电池的使用时间,Alarm任务的触发时间将会变得不准确,有可能会延迟一段时间后任务才能得到执行。若要求Alarm任务的执行时间必须准确无误,使用AlarmManager的setExact()方法来替代set()方法
Doze模式
当用户的设备是Android 6.0或以上系统时,如果该设备未插接电源,处于静止状态(Android 7.0中删除了这一条件),且屏幕关闭了一段时间之后,就会进入到Doze模式。在Doze模式下,系统会对CPU 、网络、Alarm等活动进行限制,从而延长了电池的使用寿命。
系统并不会一直处于Doze模式,而是会间歇性地退出Doze模式一小段时间,在这段时间中,应用就可以去完成它们的同步操作、Alarm任务等等。
随着设备进入Doze模式的时间越长,间歇性地退出Doze模式的时间间隔也会越长
在Doze模式下,有这些功能会受到限制:
- 网络访问被禁止
- 系统忽略唤醒CPU或者屏幕操作
- 系统不再执行WIFI扫描
- 系统不再执行同步服务
- Alarm任务将会在下次退出Doze模式的时候执行(调用AlarmManager的setAndAllowWhileIdle()或setExactAndAllowWhileIdle()方法就能让定时任务即使在Doze模式下也能正常执行)
多窗口模式编程
Android 7.0系统中却引入了一个非常有特色的功能——多窗口模式,它允许用户在同一个屏幕中同时打开两个应用程序
进入多窗口模式
有两种方式可以进入多窗口模式:
- 在Overview列表界面长按任意一个活动的标题,将该活动拖动到屏幕突出显示的区域,则可以进入多窗口模式。
- 打开任意一个程序,长按Overview按钮,也可以进入多窗口模式。
多窗口模式下的生命周期
多窗口模式并不会改变活动原有的生命周期,只是会将用户最近交互过的那个活动设置为运行状态,而将多窗口模式下另外一个可见的活动设置为暂停状态。如果这时用户又去和暂停的活动进行交互,那么该活动就变成运行状态,之前处于运行状态的活动变成暂停状态。
禁用多窗口模式
当项目的targetSdkVersion指定成24或者更高时,要在AndroidManifest.xml的<application>或<activity>标签中加入如下属性:
android:resizeableActivity=["true" | "false"]
true 表示应用支持多窗口模式,false 表示应用不支持多窗口模式,如果不配置这个属性,那么默认值为true
当项目指定的targetSdkVersion低于24,并且活动是不允许横竖屏切换的,那么该应用也将不支持多窗口模式,想要让应用不允许横竖屏切换,那么就需要在AndroidManifest.xml的<activity>标签中加入如下配置:
android:screenOrientation=["portrait" | "landscape"]
portrait表示活动只支持竖屏,landscape表示活动只支持横屏
使用Java8新特性
想要在在Android项目中使用Java新特性,需要在app/build.gradle中添加如下配置:
android { ... defaultConfig { ... jackOptions.enabled = true } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } ... }