Android学习(进阶技巧)

一、全局获取Context的技巧:

        当应用程序的架构逐渐复杂的时候,许多逻辑代码会脱离Activity类,此时Context的获取就不太方便。但Android提供了一个Application类,每当应用程序启动的时候,系统就会自动将这个类进行初始化,而我们可以定制一个自己的Application类,以便管理程序内的一些全局状态信息,比如说全局Context。

        自定义Application的步骤:

(1)首先创建一个MyApplication类继承自Application:

public class MyApplication extends Application {

    private static Context context;

    @Override
    public void onCreate() {
        super.onCreate();
        //获取context
        context = getApplicationContext(); //通过getApplicationContext()方法获取应用系统级别的Context
    }
    //创建一个静态的方法,以便获取context对象
    public static Context getContext(){
        return context;
    }
}

(2)告知系统当程序启动的时候应该初始化MyApplication类而不是默认的Application类。在AndroidManifest文件中的<application>标签下进行指定:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.servicetest">

    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

    <application
        android:name="com.example.<packagename>.MyApplication" //这里必须是完整的包名
        ...>
        ...
    </application>

</manifest>

(3)前2步实现了一种全局获取Context的机制,之后不管在任何地方使用Context,只需要调用一下MyApplication.getContext()方法即可。

二、使用Intent传递对象:

        Intent可以传递一些数据,但传递的数据类型是有限的,当我们想传递一些自定义对象的时候,就需要使用特殊的方式:

(1)Serializable方式:

        Serializable是序列化的意思,表示将一个对象转换成可存储或可传输的状态。序列化后的对象可以在网络上进行传输,也可以存储到本地。序列化的方法:

        a.定义一个Person类,包含name和age两个字段。

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;
    }
}

        b.在主活动代码里:

Person person = new Person();
person.setName("Tom");
person.setAge(20);
Intent intent = new Intent(FirstAcitivity.this,SecondActivity.class);
intent.putExtra("person_data",person); //将实例传入
startActivity(intent);

        c.在跳转的活动代码里:

Person person = (Person) getIntent().getSerializableExtra("person_data");

通过调用getSerializableExtra()方法来获取参数传递过来的序列化对象,接着再将它向下转型成Person对象。

(2)Parcelable方式:

        Parcelable方式的实现原理是将一个完整的对象进行分解,而分解后的每一部分都是Intent所支持的数据类型。

        a.定义一个Person类,包含name和age两个字段。

public class Person implements Parcelable{
    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;
    }

    @Override
    public int describeContents() {  //实现Parcelable接口必须重写该方法
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {  //实现Parcelable接口必须重写该方法
    dest.writeString(name); //写出name
    dest.writeInt(age); //写出age
    }

    public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>(){  //这里提供了一个名为CREATOR的常量,创建了Parcelable.Creator接口的一个实现,并将泛型指定为Person

        @Override
        public Person[] newArray(int size) {
            return new Person[size];
        }

        @Override
        public Stu createFromParcel(Parcel source) { //读取写入的字段,需要注意的是写入和读取的顺序需要一致
            Person person = new Person();
            person.name = source.readString(); //读取name
            person.age = source.readInt(); // 读取age
            return s;
        }
    };
}

          b.在主活动代码里:

Person person = new Person();
person.setName("Tom");
person.setAge(20);
Intent intent = new Intent(FirstAcitivity.this,SecondActivity.class);
intent.putExtra("person_data",person); //将实例传入
startActivity(intent);

        c.在跳转的活动代码里:

Person person = (Person)getIntent.getParcelableExtra("person_data");

(3)Serializable和Parcelable两种方式的对比:

        Serializable方式较为简单,但由于会把整个对象进行序列化,因此效率会比Parcelable的方式低一些,所以通常情况下更推荐使用Parcelable的方式实现Intent传递对象的功能。

三、定制自己的日志工具:

        打印日志为我们调试程序提供了便利,但项目完成后,之前打印的日志在项目上线后仍然会打印日志,这会降低程序运行的效率,还有可能将机密数据泄漏出去。

        理想情况是让日志在项目开发阶段可以打印,在项目发布上线后,不能打印日志。

实现方法如下:

(1)新建一个LogUtil类:

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 final 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.v(tag, msg);
        }
    }
}

(2)使用自定义日志打印:

        LogUtil.d("TAG","debug log");  //打印DEBUG级别的日志

        LogUtil.w("TAG","warn log");   //打印WARN级别的日志

        自定义日志打印只需要我们修改level变量的值,确定打印日志的级别,就可以自由控制日志的打印行为了。

四、创建定时任务:

         Android 中的定时任务一般有两种实现方式,一种是使用Java API 里提供的 Timer 类,一种是使用 Android 的Alarm 机制。

        Timer 类有一个缺点,它不适合用于那些需要长期在后台执行的定时任务。为了能让手机电池更加耐用,每种手机都有自己的休眠策略,Android 手机会在长时间不操作的情况下自动进入休眠状态,这就有可能导致 Timer 中的定时任务无法正确运行。

        Alarm 机制具有唤醒 CPU 的功能 ,它可以保证在大多数情况下需要执行定时任务的时候CPU都能正常工作。唤醒CPU与唤醒屏幕完全不是一个概念。

  • Alarm 机制借助AlarmManager类来实现,通过调用Context的getSystemService()方法来获取实例的,传入的参数是Context.ALARM_SERVICE:

       AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);

  • 接下来调用AlarmManager 的set()方法就可以设置一个定时任务了:
long triggerAtTime = SystemClock.elapsedRealtime()+10 * 1000;//时间10s
manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,triggerAtTime,pendingIntent)

set()方法共有三个参数:

(1)第一个参数用于指定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点至今所经历的的毫秒数。

(2)第二个参数指定任务触发的时间,以毫秒为单位。若第一个参数使用的是ELAPSED_REALTIME或ELAPSED_REALTIME_WAKEUP时,传入系统开机至今的时间再加上任务延迟执行的时间。若第一个参数使用的是RTC或RTC_WAKEUP,则传入1970年1月1日0点至今的时间再加上任务延迟执行的时间。

(3)第三个参数传入一个PendingIndent,一般会调用 getSevice()或getBroadCast()方法来获取一个能够执行服务或广播的PendingIntent。当任务被触发时,服务的onStartCommand()方法或广播接收器的onReceive()方法就可以得到执行。

  • 实现一个长时间在后台定时运行的服务:
public class LongRunningService extends Service {
    
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(final 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; //这是一小时的毫秒数
        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);

注:从Android4.4系统开始,Alarm任务的触发时间将变得不准确,有可能会延迟一段时间后任务才得到执行,这是系统在耗电性方面的优化。

——参考《Android第一行代码》整理上述笔记

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值