• 10、Android--技巧


     10.1、全局获取Context的技巧

    在实践中有很多的地方都可以使用到Context

    弹出Toast的时候需要,启动活动的时候需要、发送广播的时候需要、

    操作数据库的时候需要、使用通知的时候需要.......

    现在的开发中基本上都在使用Context而发愁过

    因为操作都是再活动中进行的,活动的本身就是一个Context对象

    当应用程序的架构逐渐开始复杂起来的时候

    很多的逻辑代码都将脱离Activity类

    但是此时由需要使用Context,也许这个时候会感到废脑子

    如封装好的网络操作:

    此时再方法中添加一个Context参数

    并且假设有一个isNetworkAvaiable()方法来判断网路是否可用

    虽说上述是一种解决方案,但是却有点推卸责任的嫌疑

    因为我们将获取Context的任务转移给sendHttpRequest()方法的调用方

    至于用什么方法能不能得到Context对象,那不是我们需要考虑的问题

    再某些情况下,获取Context并非是一件容易的事情

    有时候还是挺伤脑筋的。

    Android提供了一个Application类,每当应用程序启动的时候

    系统就会自动将这个类进行初始化

    我们可以指定一个自己的Application类

    以便于管理程序内一些全局状态的信息

    比如全局的Context。

    定义自己的Application其实不复杂

    首先创建一个MyApplication类继承Application

    public class MyApplication extends Application {
    
        private static Context context;
    
        @Override
        public void onCreate() {
            super.onCreate();
            context = getApplicationContext();
        }
    
        public  static Context getContext(){
            return context;
        }
    }

    重写父类的onCreate()方法

    通过调用getApplicationContext()方法得到一个应用级别的Context

    然后提供一个静态的getContext()方法,在这里将获取到的Context进行返回

    接下来需要告知系统,当程序启动的时候应该初始化MyApplication类

    而不是默认的Application类

    再AndroidManifest.xml文件中的<application>标签下进行注册

            <activity android:name="com.example.ccrr.material.MyApplication">
    
            </activity>

     指定的时候一定要加上完整的包名,不然系统将无法找到这个类

     这样就有一套全局获取Context的机制

    不管咋任何地放进行使用Context,只需要调用一些:MyAppcation.getContext()方法即可

    此时再sendHttpRequest()方法中优化如下:

    这里的sendHttpRequest()方法不需要传入参数的方式来得到Context对象

    而是调用MyApplication.getContext()方法即可

    之前使用的LitePal就能再内部自动获取到Context

    已经配置过Application是否会和LitePalApplication产生冲突

    任何一个项目都只能配置一个Application

    对于这种情况,LitePal提供了更加简单的使用方法

    如下:

    使用这种写法就相当于把全局的Context对象通过参数传给LitePal

    效果和AndroidManifest.xml配置LitePalApplication是一样的

    10.2、使用Intent传递对象

     Intent在之前的使用已经相当熟悉了

    可以借助他来启动活动、发送广播、启动服务等

    同时还可以在Intent中添加一些附带的数据,已达到传值的效果

    在FirstActivity中

    这调用Intent的putExtra()方法来添加要传递的数据

    之后再SecondActivity中

    但是putExtra()方法中所支持的数据类型是有限的

    对常用一些数据类型都是支持的

    若是定义一些自定义的对象的时候就会无从下手

    下方的有一些技巧

    10.2.1、Seriallizable

    serializable是序列化的意思

    表示将一个对象转换成可存储或者可传输的状态

    序列化后的对象可以在网络上进行传输,也可以存储再本地

    至于序列化的方法很简单,只需要让一个类实现Serializable这个接口即可

    实例:

    定义一个Person类,其中包含了name和age这两个字段,实现序列化实现接口即可

    其中get、set方法都是用于赋值和读取字段的数据

    最重要的一部分再第一行

    这里让Person类去实现Serializable接口

    这样所有的Person对象都是看可序列化的

    然后再FirstActivity中:

    这里新建的一个Personde实例

    然后就直接将他们传入到putExtra()方法

    由于再Person类实现了Serializable接口,所以是这个写法

    再SecondActivity中

    调用getSerializableExtra()方法获取通过参数传递过来的序列化对象

    接着将它转为Person对象

    此时成功实现了使用Intent来传递对象的功能

    10.2.2、Parcelable方式

     除了Serializable之外,使用Parcelable也可以实现相同的效果

    不过不同于将对象序列化

    Parceleable方式实现的原理是一个将完整的对象进行分家

    而后分解的每一部分都是Intent所支持的数据类型,这样就实现了传递对象的功能。

    修改Person中的代码

    Parcleable的实现方式稍微复杂一些

    首先让类实现Parcleable接口,因此必须实现describeContents()方法和writeToParcel()方法

    describeContents() 方法直接返回为0即可

    writeToParcel() 方法中需要调用Parcel的writeXxx()方法,将Person类中的字段一一写相互

    注意:根据数据类型进行调用,string、int....

    还需要再Person类中提供一个名为CREATOR的常量

    这里创建一个Paraelable.Creator接口的一个实现

    接着重写createFromParcel()和newArray()这两个方法

    createFromParcel()方法中读取刚刚写出的name、age字段,并且创建一个Person对象进行返回

    其中name个age都是需要调用Parcel的readXxx()方法读取到,这里也需要注意类型string、int,以及读顺序和写顺序相同

    再newArray()方法中只需要new一个Person数组,使用方法中传入size作为数组的大小即可

    再SecondActivity中:

    这里使用getParcelableExtra()方法来获取数据传递过来的对象

    其他地方一致

    10.3、定制自己的日志工具

    对于日志的使用

    并且再开发中经常使用做数据测试操作

    但是这种使用再日志的控制方面还是做的不是很好

    再大型项目中会再很多地方使用日志进行打印

    再项目完成时就是再代码中调式的日志问题

    在项目上线时还会进行打印,很容易降低程序的运行效率、还有可能将数据泄露出

    解决最简单的方式是能够自由地控制日志的打印

    当程序处于开发阶段就让日志打印出来

    程序上线之后舅把日志屏幕掉

    需要定制一个自己的日志工具:

    10.4、创建定时任务 

     定时任务由两种实现方式:

    1、使用Java Api里提供的Timer类

    2、使用Android的Alarm机制

    两种方式在多数情况下都能实现类似的效果

    但Timer有一个明显的短板

    不适用于那些长期在后台运行的定时任务

    为了能让电池更加耐用,每种手机都会由自己的休眠策略

    Android手机就会长期在不操作的情况下自动让CPU进入到休眠状态

    这就有可能导致Timer中的定时任务无法正常执行

    而Alarm具有唤醒CPU的功能

    可以保证在大多数情况下需要请求执行任务的时候CPU都能正常工作

    唤醒CPU和唤醒屏幕不是一个概念的问题

    10.4.1、Alarm机制

    主要借助了AlarmManager类来实现的

    这个类和NotificationManager有点类似

    都是通过调用Context的getSystemService()方法来获取实例

    这里需要传入的的参数是Context.ALARM_SERVICE

    因此获取一个AlarmManage的实例:

    接下来调用AlarmManager的set()方法就可以设置一个定时任务

    比如设置一个10秒中后执行,就可以写成:

     

    set()方法需要传入三个参数

    1、整形参数,用于指定AlarmManager的工作类型有四个可选值

      ELAPSED_REALTIME、ELAPSED_REALTIME_WARKUP、RTC和RTC_WAKEUP

      ELAPSED_REALTIME:表示让定时任务的触发时间从系统开机开始算起,但是不会唤醒CPU

      ELAPSED_REALTIME_WARKUP:表示让定时任务的触发时间从系统时间开始算起,但是不会唤醒CPU

      RTC:表示任务的触发时间是从1970年1月1日0点开始算起,但是会唤醒CPU

      RTC_WAKEUP:表示任务的触发时间是从1970年1月1日0点开始算起,但是会唤醒CPU

      使用SystemClock.elapsedRealtime()方法可以获取到系统开机至今所经历时间的毫秒数

      使用System.currentTimeMills()方法可以获取到1970年1月1日0点至今所经历的毫秒数

    2、这个参数是定时任务触发的事件,以毫秒为单位

      如果第一个参数使用的是ELAPSED_REALTIME、ELAPSED_REALTIME_WARKUP

      这里传入开机至今的事件加上延迟执行的时间

      如果第一个参数使用的是RTC、RTC_WAKEUP

      这里传入1970年1月1日0点至今的时间在加上延迟的时间

    3、此时的参数是一个PendingIntent,这里一般会调用getService()方法或者getBroadcast方法来获取一个能够

      执行服务或广播的PendingIntent,这样当任务被触发的时候,服务的onStratCommand()方法或广播接收器

      onReceive()方法就可以得到执行

    所以此时的set()方法,设定一个任务在10秒钟后执行可以:

    如果要实现一个长时间在后台定时运行的服务

    首先创建一个普通的服务

    再将触发的定时任务代码写在onStratCommand()方法

    首先在onStratCommand()方法中开启一个子线程

    在这里可以执行一些具体的操作

    之所以在子线程中执行逻辑操作

    是因为逻辑操作也需要耗时的

    在主线程中执行可能会对定时的准确性造成影响

    创建线程之后的代码就是Alarm机制的使用

    首先获取了AlarmManager的实例

    然后在定义任务的触发时间为一小时之后

    在使用PendingIntent执行处理定时任务的服务为LongRunningSevice

    最后调用set()方法完成设定

    这样就定义了一个 长时间在后台运行的服务实现

    因为一旦启动了LongRunningService

    就会在onStartCommand()方法里设定一个定时任务

    这样一个小时后将会再次启动LongRunningServie

    从而形成了一个永久的循环

    保证LongRuningService的onStratCommand()方法可以每隔一小时就执行一次

    注意的是:

    4.4系统开始,Alarm任务的触发时间将会变得不准确

    有时会延迟一段时间之后任务才能得到执行

    这不是一个bug,而是系统对耗电方面性能的优化

    系统会自动检测目前有多少个Alarm任务存在

    然后触发时间相近的几个任务放在一起执行

    这样就可以大幅度减少CPU被唤醒的次数,从而提高电池的使用时间

    如果要求Alarm任务的执行时间必须准确无误

    Android提供了解决方案

    使用AlarmManager的setExact()方法来代替set()方法

    基本上就可以保证任务能够准确的执行

    10.4.2、Doze模式

    虽然Android的每个版本都在手机电量方面的努力进行优化

    不过一直没有解决后台服务泛滥、手机电量消耗过快的问题

    于是在Android6.0系统中,谷歌加入了一个全新的Doze模式

    从而及大幅度的延长电池的使用寿命

    首先Doze模式:

    当用户设备是一个Android6.0或以上的系统时

    如果设备未插电源,处于静止状态,且屏幕关闭了一段时间之后,就会进入Doze模式

    早Doze模式下:系统会对CPU、网络、Alarm等活动进行限制,从而延长了电池的使用寿命

    系统并不会一直处于Doze模式,而是间接性的退出Doze模式一小段时间

    在这段时间中,应用可以完成他们的同部操作、Alarm任务等等

    随着设备进入Doze模式的时间越长,间接性的退出Doze模式的时间间隔也会越来越长

    因为如果设备长时间不使用的话,是没必要频繁的退出Doze模式来执行同部操作的

    Android在这些细节上的把控使得电池寿命进一步得到了延长

    Doze模式下会有一些功能会受到限制:

    最后一条:

    在Doze模式下,我们的Alarm任务将会变得不准时

    当然,这在大多情况下是合理的,因为当用户长时间不使用手机才会进入Doze模式

    通常这种情况下对ALarm任务的准确性要求没有那么高

    真的有特殊需求

    需求对Alarm任务模式即使在Doze模式情况下也能必须正常执行

    Android还提供了解决方案

    调用AlarmManage的setAndAllowWhileIdle()或setExactAndAllowWhileIdle()方法

    能够让定时任务即使在Doze模式下也能进行正常执行

    这两个方法之间的区别和set()、setExact()方法之间的区别一致。

  • 相关阅读:
    【BZOJ1486】最小圈(分数规划)
    【BZOJ4753】最佳团体(分数规划,动态规划)
    【POJ3621】【洛谷2868】Sightseeing Cows(分数规划)
    【POJ2728】Desert King(分数规划)
    【POJ2976】Dropping Tests(分数规划)
    【BZOJ4520】K远点对(KD-Tree)
    【BZOJ3991】寻宝游戏(动态规划)
    【BZOJ4066】简单题(KD-Tree)
    KD-Tree
    【BZOJ2648】SJY摆棋子(KD-Tree)
  • 原文地址:https://www.cnblogs.com/Mrchengs/p/10739664.html
Copyright © 2020-2023  润新知