那些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模式的时间间隔也会越长
在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
    }
    ...
}
全部评论

相关推荐

点赞 收藏 评论
分享
牛客网
牛客企业服务