Android性能优化系列——网络和电量优化

640?wx_fmt=gif

热文导读 | 点击标题阅读

如何进阶成为Java和Android架构师?

阿里技术专家带你使用Spring框架快速搭建Web工程项目

Kotlin 或将取代 Java —— 《Java 编程思想》作者 Bruce Eckel


来源:http://www.apkbus.com/blog-955387-77980.html(文末附github源码地址

之前分享了Android优化系列——代码、图片和布局优化,今天继续分享网络和电量优化部分。

网络优化. 移动端对额App几乎都是联网的,网络延迟等会对App的性能产生较大的影响,网络优化可以节约网络流量和电量
  • DNS域名的系统,主要的功能根据应用请求所用的域名URL去网络上面映射表中查相对应的IP地址,这个过程有可能会消耗上百毫秒,而且可能存在着DNS劫持的危险,可以替换为Ip直接连接的方式来代替域名访问的方法,从而达到更快的网络请求,但是使用Ip地址不够灵活,当后台变换了Ip地址的话,会出现访问不了,前段的App需要发包,解决方法是增加Ip地址动态更新的能力,或者是在IP地址访问失败了,切换到域名的访问.

Demo--->ping 一个地址,不正确的话,切换到备用的地址

  boolean ping = ping("wwww.baidu.com");
/**
    * 测试主域名是否可用
    *
    * @param ip
    * @return
    */

   private final int PING_TIME_OUT = 1000; // ping 超时时间
   private boolean ping(String ip) {
       try {
           Integer status = executeCommandIp( ip, PING_TIME_OUT );
           if ( status != null && status == 0 ) {
               return true;
           } else {
               return false;
           }
       } catch (IOException e) {
           e.printStackTrace();
       } catch (InterruptedException e) {
           e.printStackTrace();
       } catch (TimeoutException e) {
           e.printStackTrace();
       }
       return false;
   }
  /**
    * 执行域名是否可通
    * @param command
    * @param timeout
    * @return
    * @throws IOException
    * @throws InterruptedException
    * @throws TimeoutException
    */

   private int executeCommandIp( final String command, final long timeout )
           throws IOException, InterruptedException, TimeoutException
{
       Process process = Runtime.getRuntime().exec(
               "ping -c 1 -w 100 " + command);
       mWorker = new PingWorker(process);
       mWorker.start();
       try {
           mWorker.join(timeout);
           if (mWorker.exit != null) {
               return mWorker.exit;
           } else {
               //throw new TimeoutException();
               return -1;
           }
       } catch (InterruptedException ex) {
           mWorker.interrupt();
           Thread.currentThread().interrupt();
           throw ex;
       } finally {
           process.destroy();
       }
   }

PingWorker 类

 class PingWorker extends Thread {
       private final Process process;
       private Integer exit;
       private String ip;
       public PingWorker(Process process) {
           this.process = process;
       }
       @Override
       public void run() {
           try {
               exit = process.waitFor();
               if (exit == 0) {
                   BufferedReader buf = new BufferedReader(new InputStreamReader(process.getInputStream()));
                   String str = new String();
                   StringBuffer ipInfo = new StringBuffer();
                   //读出所有信息并显示
                   while((str=buf.readLine())!=null) {
                       ipInfo.append(str);
                   }
                   /*
                   PING sni1st.dtwscache.ourwebcdn.com (14.215.228.4) 56(84) bytes of data.64 bytes from 14.215.228.4: icmp_seq=1 ttl=57 time=16.6 ms--- sni1st.dtwscache.ourwebcdn.com ping statistics ---1 packets transmitted, 1 received, 0% packet loss, time 0msrtt min/avg/max/mdev = 16.656/16.656/16.656/0.000 ms
                    */

                   System.out.println("shiming ipInfo----->"+ipInfo);
                   Pattern mPattern = Pattern.compile("\\((.*?)\\)");
                   Matcher matcher = mPattern.matcher(ipInfo.toString());
                   if ( matcher.find() ) {
                       ip = matcher.group( 1 );
                   }
               }
               else {
                   ip = " process.waitFor()==="+exit;
               }
           }
           catch (IOException e) {
               e.printStackTrace();
               ip="java.io.IOException: Stream closed";
               return;
           }
           catch (InterruptedException e) {
               ip="java.io.InterruptedException: Stream closed";
               return;
           }
       }
   }
  • 合并网络请求,一次完整的Http请求,首先进行的是DNS查找,通过TCP三次握手,从而建立连接,如果是https请求的话,还要经过TLS握手成功后才可以进行连接,对于网络请求,减少接口,能够合并的网络请求就尽量合并

  • SSL(Secure Sockets Layer 安全套接层),及其继任者传输层安全(Transport Layer Security,TLS)是为网络通信提供安全及数据完整性的一种安全协议。TLS与SSL在传输层对网络连接进行加密。

HTTPS和HTTP的区别主要为以下四点:

  • 一、https协议需要到ca申请证书,一般免费证书很少,需要交费。

  • 二、http是超文本传输协议,信息是明文传输,https 则是具有安全性的ssl加密传输协议。

  • 三、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。

  • 四、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

  • 预先获取数据能够将网络请求集中在一次,这样其他时间段手机就可以切换到空闲的时间,从而避免经常性的唤醒,从而节约用电

  • 避免轮询:如果说每个一段时间需要向服务器发起主动的网络请求,其实不建议在app端做这样的操作,可以使用推送,如果说在不得已的情况下,也要避免使用Thread.sleep()函数来循环等待,建议使用系统的AlarmManager来实现定时轮询,AlarmManager 可以保证在系统休眠的时候,CPU也可以得到休息,在下一次需要发起网络球球的时候才唤醒

  • 尽量避免网络请求失败时候,无限制的循环重试连接,在我第一篇简书博客有写过一个网络加载的框架 :MVP网络框架(Retorfit+Rxjava+Rxandroid)中有提到过

  //基于Rxjava 和 RxAndroid Retorfit
         o.subscribeOn(Schedulers.io())
               .retryWhen(new RetryWhenHandler(1, 5))
               .doOnSubscribe(new Action0() {
                   @Override
                   public void call() {
                       s.onBegin();
                   }
               })
               .subscribeOn(AndroidSchedulers.mainThread())
               .unsubscribeOn(Schedulers.io())
               .observeOn(AndroidSchedulers.mainThread())
               .subscribe(s);
  • 离线缓存,对于图片或者文件,内存缓存+磁盘缓存+网络缓存,一般我们本地需要做的是二级缓存,当缓存中存在图片或者是文件,直接从缓存中读取,不会走网络,下载图片,在Android中使用LruCache实现内存缓存,DiskLruCache实现本地缓存

   /**
    * 图片缓存的核心类
    */

   private LruCache<String, Bitmap> mLruCache;
   // 缓存大小
   private static final int CACHE_MAX_SIZE = 1024;
   /**
    * LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。
    */

   private void lruCacheDemo() {
       // 获取应用程序最大可用内存
       int maxMemory = (int) Runtime.getRuntime().maxMemory();
       //设置LruCache缓存的大小,一般为当前进程可用容量的1/8。
       int cacheSize = maxMemory / 8;
       mLruCache = new LruCache<String, Bitmap>(cacheSize) {
           //重写sizeOf方法,计算出要缓存的每张图片的大小
           //这个方法要特别注意,跟我们实例化 LruCache 的 maxSize 要呼应,怎么做到呼应呢,比如 maxSize 的大小为缓存的个数,这里就是 return 1就 ok,如果是内存的大小,如果5M,这个就不能是个数 了,这是应该是每个缓存 value 的 size 大小,如果是 Bitmap,这应该是 bitmap.getByteCount();
           @Override
           protected int sizeOf(String key, Bitmap value)
{
               return value.getRowBytes() * value.getHeight();
           }
           ////这里用户可以重写它,实现数据和内存回收操作
           @Override
           protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue)
{
               if (oldValue != newValue) {
                   oldValue.recycle();
               }
           }
       };
   }
   /**
    * 从LruCache中获取一张图片,如果不存在就返回null。
    */

   private Bitmap getBitmapFromLruCache(String key) {
       return mLruCache.get(key);
   }
   /**
    * 往LruCache中添加一张图片
    *
    * @param key
    * @param bitmap
    */

   private void addBitmapToLruCache(String key, Bitmap bitmap) {
       if (getBitmapFromLruCache(key) == null) {
           if (bitmap != null)
               mLruCache.put(key, bitmap);
       }
   }
  • 压缩数据的大小:可以对发送服务端数据进行gzip压缩,同时可以使用更优的数据传输格式,例如二进制的代替Json格式,这个比较牛逼,估计运用的很少,使用webp格式代替图片格式

  • 不同的网络环境使用不同的超时策略,常见的网络格式有 2g、3g、4g、wifi,实时的更新当前的网络状态,通过监听来获取最新的网络类型,并动态调整网络超时的时间

 private void netWorkDemo() {
       TextView netWork = findViewById(R.id.net_work);
       boolean networkConnected = NetworkUtils.isNetworkConnected(this);
       int networkType = NetworkUtils.getNetworkType(this);
       System.out.println("shiming 是否联网了"+networkConnected);
       switch (networkType){
           case TYPE_UNKNOWN:
               System.out.println("shiming 联网的类型---无网络连接");
               netWork.setText("是否联网了---》"+networkConnected+" 联网的类型---无网络连接");
               break;
           case TYPE_2G:
               System.out.println("shiming 联网的类型---2G");
               netWork.setText("是否联网了---》"+networkConnected+" 联网的类型---2G");
               break;
           case TYPE_3G:
               System.out.println("shiming 联网的类型---TYPE_3G");
               netWork.setText("是否联网了---》"+networkConnected+" 联网的类型---TYPE_3G");
               break;
           case TYPE_4G:
               System.out.println("shiming 联网的类型---TYPE_4G");
               netWork.setText("是否联网了---》"+networkConnected+" 联网的类型---TYPE_4G");
               break;
           case TYPE_WIFI:
               System.out.println("shiming 联网的类型---TYPE_WIFI");
               netWork.setText("是否联网了---》"+networkConnected+" 联网的类型---TYPE_WIFI");
               break;
       }
   }

NetworkUtils 类

package com.shiming.performanceoptimization.network_optimization;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.telephony.TelephonyManager;
/**
* author: Created by shiming on 2018/4/28 10:52
* mailbox:[email protected]
* des:网络连接工具类
*/

public class NetworkUtils {
   private static final String SUBTYPE_TD_SCDMA = "SCDMA";
   private static final String SUBTYPE_WCDMA = "WCDMA";
   private static final String SUBTYPE_CDMA2000 = "CDMA2000";
   /**
    * 判断是否已连接到网络.
    *
    * @param context Context
    * @return 是否已连接到网络
    */

   public static boolean isNetworkConnected(Context context) {
       ConnectivityManager connectivity = (ConnectivityManager) context.getSystemService(Context
               .CONNECTIVITY_SERVICE);
       if (connectivity != null) {
           NetworkInfo info = connectivity.getActiveNetworkInfo();
           if (info != null &amp;&amp; info.isConnected()) {
               if (info.getState() == NetworkInfo.State.CONNECTED) {
                   return true;
               }
           }
       }
       return false;
   }
   /**
    * 获取当前网络类型
    *
    * @param context Context
    * @return 当前网络类型(Unknown, 2G, 3G, 4G, WIFI)
    */

   public static int getNetworkType(Context context) {
       NetworkInfo info = ((ConnectivityManager) context.getSystemService(
               Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();
       if (info != null &amp;&amp; info.isConnected()) {
           if (info.getType() == ConnectivityManager.TYPE_WIFI) {
               return NetworkType.TYPE_WIFI;
           } else if (info.getType() == ConnectivityManager.TYPE_MOBILE) {
               switch (info.getSubtype()) {
                   case TelephonyManager.NETWORK_TYPE_GPRS:
                   case TelephonyManager.NETWORK_TYPE_EDGE:
                   case TelephonyManager.NETWORK_TYPE_CDMA:
                   case TelephonyManager.NETWORK_TYPE_1xRTT:
                   case TelephonyManager.NETWORK_TYPE_IDEN: //api<8 : replace by 11
                       return NetworkType.TYPE_2G;
                   case TelephonyManager.NETWORK_TYPE_UMTS:
                   case TelephonyManager.NETWORK_TYPE_EVDO_0:
                   case TelephonyManager.NETWORK_TYPE_EVDO_A:
                   case TelephonyManager.NETWORK_TYPE_HSDPA:
                   case TelephonyManager.NETWORK_TYPE_HSUPA:
                   case TelephonyManager.NETWORK_TYPE_HSPA:
                   case TelephonyManager.NETWORK_TYPE_EVDO_B: //api<9 : replace by 14
                   case TelephonyManager.NETWORK_TYPE_EHRPD:  //api<11 : replace by 12
                   case TelephonyManager.NETWORK_TYPE_HSPAP:  //api<13 : replace by 15
                       return NetworkType.TYPE_3G;
                   case TelephonyManager.NETWORK_TYPE_LTE:    //api<11 : replace by 13
                       return NetworkType.TYPE_4G;
                   default:
                       // http://baike.baidu.com/item/TD-SCDMA 中国移动 联通 电信 三种3G制式
                       String subtypeName = info.getSubtypeName();
                       if (SUBTYPE_TD_SCDMA.equalsIgnoreCase(subtypeName) ||
                               SUBTYPE_WCDMA.equalsIgnoreCase(subtypeName) ||
                               SUBTYPE_CDMA2000.equalsIgnoreCase(subtypeName)) {
                           return NetworkType.TYPE_3G;
                       } else {
                           return NetworkType.TYPE_UNKNOWN;
                       }
               }
           }
       }
       return NetworkType.TYPE_UNKNOWN;
   }
}

NetworkType类

package com.shiming.performanceoptimization.network_optimization;
/**
* author: Created by shiming on 2018/4/28 10:52
* mailbox:lamshiming@sina.com
* des:网络连接类型常量
*/

public class NetworkType {
   /**
    * 无网络连接
    */

   public static final int TYPE_UNKNOWN = -1;
   /**
    * 2G
    */

   public static final int TYPE_2G = 0;
   /**
    * 3G
    */

   public static final int TYPE_3G = 1;
   /**
    * 4G
    */

   public static final int TYPE_4G = 2;
   /**
    * WIFI
    */

   public static final int TYPE_WIFI = 3;
}
  • CDN的全称是Content Delivery Network,即内容分发网络。其基本思路是尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快、更稳定。通过在网络各处放置节点服务器所构成的在现有的互联网基础之上的一层智能虚拟网络,CDN系统能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上。其目的是使用户可就近取得所需内容,解决 Internet网络拥挤的状况,提高用户访问网站的响应速度。

电量优化
  • 1、(BroadCastReceiver)为了减少应用耗损的电量,我们在代码中尽量避免使用无用的操作代码,当应用退到后台了,一切页面的刷新都是没有意义的,并且浪费电,比如有个监听网络状态的广播并执行一些动作,弹窗或者是Toast,那么app需要在后台的时候,禁用掉这个功能,

  • 2、数据传输    蓝牙传输,Wi-Fi传输  移动网络传输  后台数据的管理:根据业务需求,接口尽量避免无效数据的传输  数据传输的频度问题:通过经验值或者是数据统计的方法确定好数据传输的频度,避免冗余重复的数据传输,数据传输过程中要压缩数据的大小,合并网络请求,避免轮询

  • 3、位置服务 正确的使用位置复位,是应用耗电的一个关键

需要注意以下的三点:

1、有没有及时注销位置监听器:和广播差不多

2、位置更新监听频率的设定;更加业务需求设置一个合理的更新频率值,

  • minTime:用来指定间更新通知的最小的时间间隔,单位是毫秒,看日志这里是1s更新的

  • minDistance:用来指定位置更新通知的最小的距离,单位是米

3、Android提供了三种定位

  • GPS定位,通过GPS实现定位,精度最高,通常在10米(火星坐标),但是GPS定位在时间和电量上消耗也是最高的

  • 网络定位,通过移动通信的基站信号差异来计算出手机所在的位置,精度比GPS差好多

  • 被动定位,最省电的定位服务,如果使用被动定位服务。说明它想知道位置更新信息但有不主动获取,等待手机中其他应用或者是服务或者是系统组件发出定位请求,并和这些组件的监听器一起接收位置的信息,实际的开发中,一般使用的是第三方的地图,高德,腾讯,百度,他们做了很好的封装,同时在地图上的表现上更加的优化

        /**
        *   //设置定位精确度 Criteria.ACCURACY_COARSE比较粗略,Criteria.ACCURACY_FINE则比较精细
        *         criteria.setAccuracy(Criteria.ACCURACY_FINE);
        *         //设置是否要求速度
        *         criteria.setSpeedRequired(false);
        *         // 设置是否允许运营商收费
        *         criteria.setCostAllowed(false);
        *         //设置是否需要方位信息
        *         criteria.setBearingRequired(false);
        *         //设置是否需要海拔信息
        *         criteria.setAltitudeRequired(false);
        *         // 设置对电源的需求
        *         criteria.setPowerRequirement(Criteria.POWER_LOW);
        */

       Criteria criteria = new Criteria();
       criteria.setAccuracy(Criteria.ACCURACY_FINE);
       criteria.setAltitudeRequired(false);
       criteria.setBearingRequired(false);
       criteria.setCostAllowed(true);
       criteria.setPowerRequirement(Criteria.POWER_LOW);
       String serviceName = Context.LOCATION_SERVICE;
       mLocationManager = (LocationManager) getSystemService(serviceName);
//        locationManager.setTestProviderEnabled("gps", true);
       // TODO: 2018/5/3 IllegalArgumentException 'Provider "gps" unknown"   https://www.cnblogs.com/ok-lanyan/archive/2011/10/12/2208378.html
       mLocationManager.addTestProvider(LocationManager.GPS_PROVIDER,
               "requiresNetwork" == "", "requiresSatellite" == "", "requiresCell" == "", "hasMonetaryCost" == "",
               "supportsAltitude" == "", "supportsSpeed" == "",
               "supportsBearing" == "", android.location.Criteria.POWER_LOW,
               android.location.Criteria.ACCURACY_FINE);
       mProvider = mLocationManager.getBestProvider(criteria, true);
       //获取纬度
       //获取经度
       mLlistener = new LocationListener() {
           @Override
           public void onLocationChanged(Location location)
{
               // thread is not runable, msg ignore, state:TIMED_WAITING, 这里的线程有可能ANR
               if (location != null) {
                   double lat = location.getLatitude();//获取纬度
                   double lng = location.getLongitude();//获取经度
                   System.out.println("shiming   lat+" + lat);
                   System.out.println("shiming   lng+" + lng);
                   String name = Thread.currentThread().getName();
                   mCount++;
                   System.out.println("当前线程的位置name---"+name+"i==="+mCount);
                   mTv_location.setText("位置信息是2s变动的,可以设置,我是第"+mCount+"次变动的--->"+"\n\r"+"lat===="+lat+"      lng----->"+lng);
               }
               if (mLocationManager!=null) {
                   mLocationManager.removeUpdates(this);
                   if (mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
                       mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 2000, 1000, mLlistener);
                   }else {
                       mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 2000, 1000, mLlistener);
                   }
//                     TODO: 2018/5/3  这里在报错了,我把他注释掉
//                mLocationManager.setTestProviderEnabled(mProvider, false);//   java.lang.IllegalArgumentException: Provider "network" unknown
               }
           }
           @Override
           public void onProviderDisabled(String provider)
{
           }
           @Override
           public void onProviderEnabled(String provider)
{
           }
           @Override
           public void onStatusChanged(String provider, int status,
                                       Bundle extras
)
{
           }
       };
       /**
        * minTime:用来指定间更新通知的最小的时间间隔,单位是毫秒,看日志这里是1s更新的
        * minDistance:用来指定位置更新通知的最小的距离,单位是米
        */

       mLocationManager.requestLocationUpdates(mProvider, 2000, (float) 1000.0, mLlistener);

在OnDestroy 变量手动值为null,我在测试过程中,只有在值为null的时候这个位置监听才会停止,有兴趣的小伙伴,可以好好看看值为null,底层会做什么操作

/**
    * 记得需要销毁这个监听,
    * todo 如果不手动置为null的话,其实您可以通过日记发现,这个监听还是一直在走的,所以说这里手动值为null的好处
    */

   protected void onDestroy() {
       if (mAm!=null){
           mAm.cancel(mPi);
           mAm=null;//变为null
       }
       if (null!=mTag){
           mTag.release();
           //释放唤醒锁锁
           mTag=null;
       }
       mLocationManager.removeUpdates(mLlistener);
       if (mLocationManager != null) {
           mLocationManager.removeUpdates(mLlistener);
           mLocationManager = null;//不用分配空间
       }
       if (mLlistener != null) {
           mLlistener = null;
       }
       //   mLocationManager.setTestProviderEnabled(mProvider, false);
       super.onDestroy();
   }
  • WakeLock 是为了保持设备的唤醒状态的API,组织用户长时间不用,仍然需要组织设备进入休眠的状态,比如用户在看电影的时候。使用wakelock 时,需要及时的释放锁,比如播放视屏的时候WakeLock保持屏幕的常亮,在暂停的时候就应该释放锁,而不是等到停止播放才释放。

 @SuppressLint("WakelockTimeout")
   private void wakeLockDemo() {
//        PowerManager.PARTIAL_WAKE_LOCK;//保持CPU正常运转,但屏幕和键盘灯有可能是关闭的
//        PowerManager.SCREEN_DIM_WAKE_LOCK://保持CPU正常运转,允许屏幕点亮但可能是置灰的,键盘灯可能是关闭的
//        PowerManager.SCREEN_BRIGHT_WAKE_LOCK;//保持CPU正常的运转,允许屏幕高亮显示,键盘灯可能是关闭的
//        PowerManager.FULL_WAKE_LOCK;//保持CPU正常运转,保持屏幕高亮显示,键盘灯也保持连读
//        PowerManager.ACQUIRE_CAUSES_WAKEUP;//强制屏幕和键盘灯亮起,这种锁针对必须通知用户的操作
//        PowerManager.ON_AFTER_RELEASE;//当WakeLock被释放了,继续保持屏幕和键盘灯开启一定的时间
       PowerManager powerManager = (PowerManager) this.getSystemService(Context.POWER_SERVICE);
       /**
        *   case PARTIAL_WAKE_LOCK:
        *             case SCREEN_DIM_WAKE_LOCK:
        *             case SCREEN_BRIGHT_WAKE_LOCK:
        *             case FULL_WAKE_LOCK:
        *             case PROXIMITY_SCREEN_OFF_WAKE_LOCK:
        *             case DOZE_WAKE_LOCK:
        *             case DRAW_WAKE_LOCK:
        */

       mTag = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Tag");
       if (null!= mTag){
           mTag.acquire();
       }
   }
  • AlarmManager 也是比较耗电的,通常情况下需要保证两次唤醒操作的时间间隔不要太短了,在不需要使用唤醒功能的情况下,尽早的取消唤醒功能,否则应用会一直消耗电量 AlarmManager 是SDK提供的一个唤醒的APi,是系统级别的服务,可以在特定的时刻广播一个指定的Intent,这个pendingIntent可以用来启动Activity、Service、BroadcastReceiver, app,在后台也会启动

  private void alarmManager() {
       //创建Intent对象,action为ELITOR_CLOCK,附加信息为字符串“你该打酱油了”
       Intent intent = new Intent("action");
       intent.putExtra("msg","重启---App ---Le  -- 回到前台");
//        intent.setClass(ElectricQuantityOptimizationActivity.this,MainActivity.class);
       //定义一个PendingIntent对象,PendingIntent.getBroadcast包含了sendBroadcast的动作。
       //也就是发送了action 为"action"的intent
       mPi = PendingIntent.getBroadcast(this,0,intent,0);
      //AlarmManager对象,注意这里并不是new一个对象,Alarmmanager为系统级服务
       mAm = (AlarmManager)getSystemService(ALARM_SERVICE);
      //设置闹钟从当前时间开始,每隔5s执行一次PendingIntent对象pi,注意第一个参数与第二个参数的关系
       // 5秒后通过PendingIntent pi对象发送广播
       assert mAm != null;
       /**
        * 频繁的报警对电池寿命不利。至于API 22,警报管理器将覆盖近期和高频报警请求,
        * 在未来至少延迟5秒的警报,并确保重复间隔至少为60秒,如果真的需要间隔很短的话,官方建议使用handler
        * 该方法用于设置重复闹钟,第一个参数表示闹钟类型,第二个参数表示闹钟首次执行时间,
        * 第三个参数表示闹钟两次执行的间隔时间,第三个参数表示闹钟响应动作。
        */

       mAm.setRepeating(AlarmManager.RTC_WAKEUP,System.currentTimeMillis(),1000, mPi);
       //该方法用于设置一次性闹钟,第一个参数表示闹钟类型,第二个参数表示闹钟执行时间,第三个参数表示闹钟响应动作。
       //am.set(AlarmManager.RTC_WAKEUP,100000,pi);
       /**
        * (1)int type: 闹钟的类型,常用的有5个值:AlarmManager.ELAPSED_REALTIME、 AlarmManager.ELAPSED_REALTIME_WAKEUP、AlarmManager.RTC、 AlarmManager.RTC_WAKEUP、AlarmManager.POWER_OFF_WAKEUP。AlarmManager.ELAPSED_REALTIME表示闹钟在手机睡眠状态下不可用,该状态下闹钟使用相对时间(相对于系统启动开始),状态值为3;
        * AlarmManager.ELAPSED_REALTIME_WAKEUP表示闹钟在睡眠状态下会唤醒系统并执行提示功能,该状态下闹钟也使用相对时间,状态值为2;
        *
        * AlarmManager.RTC表示闹钟在睡眠状态下不可用,该状态下闹钟使用绝对时间,即当前系统时间,状态值为1;
        *
        * AlarmManager.RTC_WAKEUP表示闹钟在睡眠状态下会唤醒系统并执行提示功能,该状态下闹钟使用绝对时间,状态值为0;
        *
        * AlarmManager.POWER_OFF_WAKEUP表示闹钟在手机关机状态下也能正常进行提示功能,所以是5个状态中用的最多的状态之一,该状态下闹钟也是用绝对时间,状态值为4;不过本状态好像受SDK版本影响,某些版本并不支持;
        */

       //该方法也用于设置重复闹钟,与第二个方法相似,不过其两个闹钟执行的间隔时间不是固定的而已。
       //基本上相似,只不过这个方法优化了很多,省电
       // am.setInexactRepeating(AlarmManager.RTC_WAKEUP,System.currentTimeMillis(),1000,pi);
   }
/**
* author: Created by shiming on 2018/5/3 14:28
* mailbox:[email protected]
*/

public class MyReceiver extends BroadcastReceiver {
   @Override
   public void onReceive(Context context, Intent intent) {
       String msg = intent.getStringExtra("msg");
       System.out.println("shiming "  +  msg) ;
       Toast.makeText(context,msg,Toast.LENGTH_SHORT).show();
//        ElectricQuantityOptimizationActivity context1 = (ElectricQuantityOptimizationActivity) context;
//        context1.startActivity(intent);
   }
}

提供一个Demo,当app崩溃了,通过AlarmManager来重启App的功能

/**
* author: Created by shiming on 2018/5/3 14:28
* mailbox:[email protected]
*/

public class CrashHandler implements Thread.UncaughtExceptionHandler {
   public static CrashHandler mAppCrashHandler;
   private Thread.UncaughtExceptionHandler mDefaultHandler;
   private MyApplication mAppContext;
   public void initCrashHandler(MyApplication application) {
       this.mAppContext = application;
       // 获取系统默认的UncaughtException处理器
       mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
       Thread.setDefaultUncaughtExceptionHandler(this);
   }
   public static CrashHandler getInstance() {
       if (mAppCrashHandler == null) {
           mAppCrashHandler = new CrashHandler();
       }
       return mAppCrashHandler;
   }
   @Override
   public void uncaughtException(Thread thread, Throwable ex) {
       ex.printStackTrace();
       AlarmManager mgr = (AlarmManager) mAppContext.getSystemService(Context.ALARM_SERVICE);
       Intent intent = new Intent(mAppContext, MainActivity.class);
       intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
       intent.putExtra("crash", true);
       System.out.println("shiming -----》重启应用了哦");
       PendingIntent restartIntent = PendingIntent.getActivity(mAppContext, 0, intent, PendingIntent.FLAG_ONE_SHOT);
       mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 5000, restartIntent); // 1秒钟后重启应用
       android.os.Process.killProcess(android.os.Process.myPid());
       System.exit(0);
       System.gc();
   }
}
  • 程序会重新启动,如果点击电量优化,App崩溃了,请给与全部权限,还要在开发者模式里面给与位置信息模拟的设置,如果崩溃了, 你也可以发现app会自动的重新启动,这是AlarmManager的应用,注意看MyApplication里面的代码,tks

/**
* author: Created by shiming on 2018/5/3 14:48
* mailbox:[email protected]
*/

public class MyApplication  extends Application {
   @Override
   public void onCreate() {
       super.onCreate();
       /**
        * 程序会重新启动,如果点击电量优化,App崩溃了,请给与全部权限,
        * 还要在开发者模式里面给与位置信息模拟的设置,如果崩溃了,
        * 你也可以发现app会自动的重新启动,这是AlarmManager的应用,注意看MyApplication里面的代码,tks
        */

      CrashHandler.getInstance().initCrashHandler(this);
   }
}

以上就是个人总结的基本,总结的不太全面,同时也不太详细,如果可以的话,还请给个小星星,表示鼓励,谢谢了。

git地址 https://github.com/Shimingli/PerformanceOptimizationForAndroid

你有好的文章想和大家分享欢迎投稿,直接向我投递文章链接即可


最后,欢迎大家加入我们的知识星球,这期是到2019年3月10日结束,所以越早加入越好,现在加入的球友快1000人了,到1000人时将大幅提价(还有最后几十个名额了),所以快上车!

640?wx_fmt=jpeg

微信扫描或者点击上方二维码领取Android\Python\AI\Java等高级进阶资源

更多学习资料点击下面的“阅读原文”获取

640?wx_fmt=gif

猜你喜欢

转载自blog.csdn.net/xj032w2j4ccjhow8s8/article/details/81107751
今日推荐