精华内容
下载资源
问答
  • Android APP耗电优化

    2018-10-15 11:17:41
    可能造成耗电的一些原因 网络请求耗电,而且手机数据网络进行http请求比无线网进行http请求更加耗电,因为数据网络调用到一些底层的硬件模块,就如GPS一样,当手机打开GPS功能后,也是启动了一些硬件模块就会明显...

    可能造成耗电的一些原因


    网络请求耗电,而且手机数据网络进行http请求比无线网进行http请求更加耗电,因为数据网络调用到一些底层的硬件模块,就如GPS一样,当手机打开GPS功能后,也是启动了一些硬件模块就会明显增加耗电
    高频的刷新UI界面,刷新UI界面其实就是进行layout的绘制,如果一个Activity的布局嵌套太多层,那么每一层layout都会刷新一次,例如动画等等这些都会造成耗电
    数据库,SD卡文件操作,这些都是属于耗时操作,当操作次数很少的时候基本不会有耗电问题,但是当短时间内操作次数很多的话,也会明显的增加耗电,同时也有可能造成页面卡顿
    AlarmManager,例如一些推送的心跳包实现,AlarmManager会定时唤醒CPU去执行一些任务,也是造成耗电的一大源头
    手机网络环境不好的时候会频繁的切换网络,因为网络数据交互的时候,系统也是会被唤醒的,所以APP如果在监听了网络切换广播后做了大量的操作,一样会增加耗电
    针对一些任务队列的处理,如果队列堆积的任务太多,导致循环执行太久也会造成耗电,因为占用了CPU资源去执行代码,我们的log日志工具保存到文件就是用任务队列实现的,当压力测试SDK一次性接受1万条消息的时候,那内存就表上来了,跟了下发现日志保存队列里面积压了4千多个任务,这时候即使手机锁屏,也还会不断的把队列中的任务执行完然后CPU才会休眠下去的,同样会造成严重的耗电,耗内存,好在release版本的日志都是关闭的
    执行一些高运算量的代码,例如json数据解析,一些二进制协议的数据编码和解码
    接收系统的一分钟广播,然后做一些程序逻辑处理,其实接收一分钟广播不耗电,耗电的是一分钟执行一次程序处理
    Wake Lock使用不当导致没有及时的释放,Wake Lock可以阻止cpu进入休眠的,如果没有及时的release会造成cpu无法休眠,程序耗电严重
    如果程序中有定时任务,在cpu休眠之后,定时任务就会被挂起不执行,这时候并不会造成太大的耗电,但是如果这个定时任务的时间间隔很短,1秒执行一次,那么当手机app集成了推送,推送就会有心跳包通过AlarmManager来唤醒,每次唤醒的时候就会再去执行挂起的定时任务,虽然执行定时任务的耗电量可能比心跳包的耗电量少很多,不过还是需要注意的,积少成多
    其实android中的Log日志的打印也会耗电的,在日常开发中,我们可能不仅会把log打印到AndroidStudio里面,有可能还会保存起来,而且可能在打印对象信息数据的时候会用到json格式转换,这些都会增加耗电,但是在正式发布的apk包中日志管理一般都是关闭的
    在手机锁屏后,CPU会过一段时间才休眠,如果程序中有定时任务,在CPU休眠后会被挂起不执行,但是在CPU休眠之前,定时任务还是会一直的执行的,之前遇到过这么一个问题,我们采用Picasso库:Picasso.with(context)

     


    Picasso(Context context, Dispatcher dispatcher, Cache cache, Listener listener,
          RequestTransformer requestTransformer, List<RequestHandler> extraRequestHandlers, Stats stats,
          Bitmap.Config defaultBitmapConfig, boolean indicatorsEnabled, boolean loggingEnabled) {
        this.context = context;
        this.dispatcher = dispatcher;
        this.cache = cache;
        this.listener = listener;
        this.requestTransformer = requestTransformer;
        this.defaultBitmapConfig = defaultBitmapConfig;

        int builtInHandlers = 7; // Adjust this as internal handlers are added or removed.
        int extraCount = (extraRequestHandlers != null ? extraRequestHandlers.size() : 0);
        List<RequestHandler> allRequestHandlers =
            new ArrayList<RequestHandler>(builtInHandlers + extraCount);

        // ResourceRequestHandler needs to be the first in the list to avoid
        // forcing other RequestHandlers to perform null checks on request.uri
        // to cover the (request.resourceId != 0) case.
        allRequestHandlers.add(new ResourceRequestHandler(context));
        if (extraRequestHandlers != null) {
          allRequestHandlers.addAll(extraRequestHandlers);
        }
        allRequestHandlers.add(new ContactsPhotoRequestHandler(context));
        allRequestHandlers.add(new MediaStoreRequestHandler(context));
        allRequestHandlers.add(new ContentStreamRequestHandler(context));
        allRequestHandlers.add(new AssetRequestHandler(context));
        allRequestHandlers.add(new FileRequestHandler(context));
        allRequestHandlers.add(new NetworkRequestHandler(dispatcher.downloader, stats));
        requestHandlers = Collections.unmodifiableList(allRequestHandlers);

        this.stats = stats;
        this.targetToAction = new WeakHashMap<Object, Action>();
        this.targetToDeferredRequestCreator = new WeakHashMap<ImageView, DeferredRequestCreator>();
        this.indicatorsEnabled = indicatorsEnabled;
        this.loggingEnabled = loggingEnabled;
        this.referenceQueue = new ReferenceQueue<Object>();
        this.cleanupThread = new CleanupThread(referenceQueue, HANDLER);
        this.cleanupThread.start();
      }123456789101112131415161718192021222324252627282930313233343536373839

    在Picasso构造的过程中:启动了一个线程CleanupThread

     

    private static class CleanupThread extends Thread {
        private final ReferenceQueue<Object> referenceQueue;
        private final Handler handler;

        CleanupThread(ReferenceQueue<Object> referenceQueue, Handler handler) {
          this.referenceQueue = referenceQueue;
          this.handler = handler;
          setDaemon(true);
          setName(THREAD_PREFIX + "refQueue");
        }

        @Override public void run() {
          Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND);
          while (true) {
            try {
              // Prior to Android 5.0, even when there is no local variable, the result from
              // remove() & obtainMessage() is kept as a stack local variable.
              // We're forcing this reference to be cleared and replaced by looping every second
              // when there is nothing to do.
              // This behavior has been tested and reproduced with heap dumps.
              RequestWeakReference<?> remove =
                  (RequestWeakReference<?>) referenceQueue.remove(THREAD_LEAK_CLEANING_MS);
              Message message = handler.obtainMessage();
              if (remove != null) {
                message.what = REQUEST_GCED;
                message.obj = remove.action;
                handler.sendMessage(message);
              } else {
                message.recycle();
              }
            } catch (InterruptedException e) {
              break;
            } catch (final Exception e) {
              handler.post(new Runnable() {
                @Override public void run() {
                  throw new RuntimeException(e);
                }
              });
              break;
            }
          }
        }

        void shutdown() {
          interrupt();
        }
      }1234567891011121314151617181920212223242526272829303132333435363738394041424344454647

    CleanupThread线程里面是有一个while(true){}循环,这个就会在手机锁屏后持续运行一段时间才会被挂起的,这个过程就增加了耗电,然后打开查看CPU使用情况,发现app一直被占用一点点,正常情况下,app的退到后台后,cpu应该是不会一直被占用的,如下图: 
     
    包名com.xtc.watch在退到后台之后仍然在占用CPU的资源执行一些东西,然后打开AndroidStudio,查看monitor里面的cpu的方法跟踪功能(Start method tracing) 
    ,红色部分就表示cpu被占用的比例,过一段时间后再次点击Start Method Tracing按钮停止方法跟踪,然后AndroidStudio会自动生成一个后缀名为.trace的分析文件: 
     
    上图中Thread后面的是执行方法所在线程的名称,Wall Clock Time表示程序从运行到终止所花费的时间,Thread Time是线程执行的时长,CPU Time是CPU的运行时间,在Thread列表里面的就表示在方法跟踪期间有执行过的线程,下面有颜色的长条是线程执行的时间信息Wall clock time和cpu time,如果在程序退到后台,手机锁屏后,仍然不断有程序里面的线程在跑,那么就存在了耗电风险了,你想想,程序退到后台,手机锁屏了,这时候所有程序线程有在方法跟踪期间的县城都已经展示出来了,如果发现程序耗电,很可能就是由于线程还在不断的运行造成的 
    - 在Service里面执行一些循环代码,也 
    可能造成耗电;如果Service是前台Service,那Service的进程优先级会比较高,或者是一些后台常驻Service,在这些Service执行无限循环的代码,耗电耗到手机发烫:

     

    public class PushService extends Service {

        private static final String tag = PushService.class.getSimpleName();

        private ExecutorService executorService = Executors.newSingleThreadExecutor();

        private Queue<Runnable> taskQueue = new LinkedList<>();

        @Override
        public void onCreate() {
            super.onCreate();
            Log.i(tag, this.getPackageName() + ":" + this.getClass().getSimpleName() + " onCreate.");
            Runnable task = null;
            while (!executorService.isShutdown()) {
                if ((task = taskQueue.poll()) != null) {
                    task.run();
                }
            }
        }

        @Override
        public void onDestroy() {
            super.onDestroy();
            Log.i(tag, this.getPackageName() + ":" + this.getClass().getSimpleName() + " onDestroy.");
            executorService.shutdownNow();
        }
    }123456789101112131415161718192021222324252627

    while循环里面的poll()方法并不是阻阻塞的,因此会一直重复的跑这段代码导致CUP即使在手机锁屏的时候仍然高速的在运行着循环代码

     

    针对以上原因进行相应的优化


    针对Http请求: 
    对http请求数据做GZIP压缩,当前流行的http第三方看默认都支持GZIP压缩
    Http缓存,Http协议有一个Cache机制,当发出http请求的时候会先到指定目录下检查是否已经存在这个请求的数据,如果存在并且还没过时,那么就会直接返回;而一些第三方例如OkHttp也有有自己的缓存机制OKHTTP缓存
    合并Http请求来减少Http请求次数,因为Http底层也是TCP连接,对于每个Http请求,发出请求的时候都会创建TCP连接,请求结束后会断开TCP连接,那么当Http请求次数很多的时候就会频繁的创建和断开TCP连接,如果把当中一些请求进行合理的合并,那么就会减少Http请求次数
    制定合理的Http请求数据格式和返回数据格式,做到请求数据中没有冗余字段
    可以在Http请求数据格式里面加一个字段dataVersion代表本地已有数据的版本号,然后传到服务器,服务器的数据表中也有一个字段是dataVersion,当服务器数据被修改的时候,dataVersion就加一,当检测到客户端传上来的dataVersion小于服务器数据表中的dataVersion的时候就返回最新数据,否则可以直接返回空数据代表当前本地数据已经是最新数据,这样就不会每次请求http的时候都会返回大量数据,当数据没有被改变的时候直接返回空,减少了http请求过程中的数据交互(但是要考虑一点,只有一些请求数据量比较大的才适合,因为增加了dataVersion字段后无论给客户端还是服务端也都相应的增加了维护的成本)
    针对数据库,SD卡文件操作: 
    APP在对数据库或者SD卡文件操作的时候无非也是涉及到一些数据的转换,json转换,可以采用json解析效率高的第三方库,例如fast json,Jackson,gson
    可以把一些需要持久化到数据库或者文件中的数据先缓存在内存中,然后在一个时间点一起触发一同更新到数据库或者文件中;例如,在进入Activity的时候会首先从数据库中搜索出帐户信息并且展示在界面,然后会再去发Http请求服务器的帐户信息数据,再把服务器最新的帐户信息数据刷新到界面中,同时也存在一个内存对象中,这时候先不更新到数据库,当退出这个Activity界面的时候可以去检测帐户数据是否发生改变,如果改变了就更新到数据中;这么做的好处是:如果在Activity界面多次修改数据,那最新的数据都是只更新到内存中的,当Activity退出后才把最新的帐户信息数据更新到数据库中,没必要更改一次就同时把数据更新一次到数据库
    SD卡的文件读写操作比数据库要快,数据库也是属于文件,但是在写数据的时候还要经过sqlite的一些列数据库操作,但是SD卡在写数据的时候是直接写到文件中的,所以针对与简单数据,标志变量或者数据条数很少的数据,而且安全性要求也不高的数据可以直接存在文件中,例如SharedPreferences中,只有关系型的数据,数据安全性较高的,数据记录条数比较多的可以选择数据库存储,而且如果数据库进行加密后,对于数据库的读写操作会更慢了
    针对数据库操作尽量不要直接使用android里面的Sqlite来手写读写的那些sql语句,可以选择一些orm框架库,例如ormlite,GreenDao等等,我们选择的是ormlite,因为ormlite数据库加密已经有现成的库可以提供使用
     

    展开全文
  • Android APP耗电优化可能造成耗电的一些原因网络请求耗电,而且手机数据网络进行http请求比无线网进行http请求更加耗电,因为数据网络调用到一些底层的硬件模块,就如GPS一样,当手机打开GPS功能后,也是启动了一些...


    Android APP耗电优化

    可能造成耗电的一些原因

    • 网络请求耗电,而且手机数据网络进行http请求比无线网进行http请求更加耗电,因为数据网络调用到一些底层的硬件模块,就如GPS一样,当手机打开GPS功能后,也是启动了一些硬件模块就会明显增加耗电
    • 高频的刷新UI界面,刷新UI界面其实就是进行layout的绘制,如果一个Activity的布局嵌套太多层,那么每一层layout都会刷新一次,例如动画等等这些都会造成耗电
    • 数据库,SD卡文件操作,这些都是属于耗时操作,当操作次数很少的时候基本不会有耗电问题,但是当短时间内操作次数很多的话,也会明显的增加耗电,同时也有可能造成页面卡顿
    • AlarmManager,例如一些推送的心跳包实现,AlarmManager会定时唤醒CPU去执行一些任务,也是造成耗电的一大源头
    • 手机网络环境不好的时候会频繁的切换网络,因为网络数据交互的时候,系统也是会被唤醒的,所以APP如果在监听了网络切换广播后做了大量的操作,一样会增加耗电
    • 针对一些任务队列的处理,如果队列堆积的任务太多,导致循环执行太久也会造成耗电,因为占用了CPU资源去执行代码,我们的log日志工具保存到文件就是用任务队列实现的,当压力测试SDK一次性接受1万条消息的时候,那内存就表上来了,跟了下发现日志保存队列里面积压了4千多个任务,这时候即使手机锁屏,也还会不断的把队列中的任务执行完然后CPU才会休眠下去的,同样会造成严重的耗电,耗内存,好在release版本的日志都是关闭的
    • 执行一些高运算量的代码,例如json数据解析,一些二进制协议的数据编码和解码
    • 接收系统的一分钟广播,然后做一些程序逻辑处理,其实接收一分钟广播不耗电,耗电的是一分钟执行一次程序处理
    • Wake Lock使用不当导致没有及时的释放,Wake Lock可以阻止cpu进入休眠的,如果没有及时的release会造成cpu无法休眠,程序耗电严重
    • 如果程序中有定时任务,在cpu休眠之后,定时任务就会被挂起不执行,这时候并不会造成太大的耗电,但是如果这个定时任务的时间间隔很短,1秒执行一次,那么当手机app集成了推送,推送就会有心跳包通过AlarmManager来唤醒,每次唤醒的时候就会再去执行挂起的定时任务,虽然执行定时任务的耗电量可能比心跳包的耗电量少很多,不过还是需要注意的,积少成多
    • 其实android中的Log日志的打印也会耗电的,在日常开发中,我们可能不仅会把log打印到AndroidStudio里面,有可能还会保存起来,而且可能在打印对象信息数据的时候会用到json格式转换,这些都会增加耗电,但是在正式发布的apk包中日志管理一般都是关闭的
    • 在手机锁屏后,CPU会过一段时间才休眠,如果程序中有定时任务,在CPU休眠后会被挂起不执行,但是在CPU休眠之前,定时任务还是会一直的执行的,之前遇到过这么一个问题,我们采用Picasso库:Picasso.with(context)
    Picasso(Context context, Dispatcher dispatcher, Cache cache, Listener listener,
          RequestTransformer requestTransformer, List<RequestHandler> extraRequestHandlers, Stats stats,
          Bitmap.Config defaultBitmapConfig, boolean indicatorsEnabled, boolean loggingEnabled) {
        this.context = context;
        this.dispatcher = dispatcher;
        this.cache = cache;
        this.listener = listener;
        this.requestTransformer = requestTransformer;
        this.defaultBitmapConfig = defaultBitmapConfig;
    
        int builtInHandlers = 7; // Adjust this as internal handlers are added or removed.
        int extraCount = (extraRequestHandlers != null ? extraRequestHandlers.size() : 0);
        List<RequestHandler> allRequestHandlers =
            new ArrayList<RequestHandler>(builtInHandlers + extraCount);
    
        // ResourceRequestHandler needs to be the first in the list to avoid
        // forcing other RequestHandlers to perform null checks on request.uri
        // to cover the (request.resourceId != 0) case.
        allRequestHandlers.add(new ResourceRequestHandler(context));
        if (extraRequestHandlers != null) {
          allRequestHandlers.addAll(extraRequestHandlers);
        }
        allRequestHandlers.add(new ContactsPhotoRequestHandler(context));
        allRequestHandlers.add(new MediaStoreRequestHandler(context));
        allRequestHandlers.add(new ContentStreamRequestHandler(context));
        allRequestHandlers.add(new AssetRequestHandler(context));
        allRequestHandlers.add(new FileRequestHandler(context));
        allRequestHandlers.add(new NetworkRequestHandler(dispatcher.downloader, stats));
        requestHandlers = Collections.unmodifiableList(allRequestHandlers);
    
        this.stats = stats;
        this.targetToAction = new WeakHashMap<Object, Action>();
        this.targetToDeferredRequestCreator = new WeakHashMap<ImageView, DeferredRequestCreator>();
        this.indicatorsEnabled = indicatorsEnabled;
        this.loggingEnabled = loggingEnabled;
        this.referenceQueue = new ReferenceQueue<Object>();
        this.cleanupThread = new CleanupThread(referenceQueue, HANDLER);
        this.cleanupThread.start();
      }

    在Picasso构造的过程中:启动了一个线程CleanupThread

    private static class CleanupThread extends Thread {
        private final ReferenceQueue<Object> referenceQueue;
        private final Handler handler;
    
        CleanupThread(ReferenceQueue<Object> referenceQueue, Handler handler) {
          this.referenceQueue = referenceQueue;
          this.handler = handler;
          setDaemon(true);
          setName(THREAD_PREFIX + "refQueue");
        }
    
        @Override public void run() {
          Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND);
          while (true) {
            try {
              // Prior to Android 5.0, even when there is no local variable, the result from
              // remove() & obtainMessage() is kept as a stack local variable.
              // We're forcing this reference to be cleared and replaced by looping every second
              // when there is nothing to do.
              // This behavior has been tested and reproduced with heap dumps.
              RequestWeakReference<?> remove =
                  (RequestWeakReference<?>) referenceQueue.remove(THREAD_LEAK_CLEANING_MS);
              Message message = handler.obtainMessage();
              if (remove != null) {
                message.what = REQUEST_GCED;
                message.obj = remove.action;
                handler.sendMessage(message);
              } else {
                message.recycle();
              }
            } catch (InterruptedException e) {
              break;
            } catch (final Exception e) {
              handler.post(new Runnable() {
                @Override public void run() {
                  throw new RuntimeException(e);
                }
              });
              break;
            }
          }
        }
    
        void shutdown() {
          interrupt();
        }
      }

    • CleanupThread线程里面是有一个while(true){}循环,这个就会在手机锁屏后持续运行一段时间才会被挂起的,这个过程就增加了耗电,然后打开查看CPU使用情况,发现app一直被占用一点点,正常情况下,app的退到后台后,cpu应该是不会一直被占用的,如下图: 

    这里写图片描述 
    包名com.xtc.watch在退到后台之后仍然在占用CPU的资源执行一些东西,然后打开AndroidStudio,查看monitor里面的cpu的方法跟踪功能(Start method tracing) 
    方法跟踪功能,红色部分就表示cpu被占用的比例,过一段时间后再次点击Start Method Tracing按钮停止方法跟踪,然后AndroidStudio会自动生成一个后缀名为.trace的分析文件: 
    方法执行线程信息
    上图中Thread后面的是执行方法所在线程的名称,Wall Clock Time表示程序从运行到终止所花费的时间,Thread Time是线程执行的时长,CPU Time是CPU的运行时间,在Thread列表里面的就表示在方法跟踪期间有执行过的线程,下面有颜色的长条是线程执行的时间信息Wall clock time和cpu time,如果在程序退到后台,手机锁屏后,仍然不断有程序里面的线程在跑,那么就存在了耗电风险了,你想想,程序退到后台,手机锁屏了,这时候所有程序线程有在方法跟踪期间的县城都已经展示出来了,如果发现程序耗电,很可能就是由于线程还在不断的运行造成的 
    - 在Service里面执行一些循环代码,也 
    可能造成耗电;如果Service是前台Service,那Service的进程优先级会比较高,或者是一些后台常驻Service,在这些Service执行无限循环的代码,耗电耗到手机发烫:

    public class PushService extends Service {
    
        private static final String tag = PushService.class.getSimpleName();
    
        private ExecutorService executorService = Executors.newSingleThreadExecutor();
    
        private Queue<Runnable> taskQueue = new LinkedList<>();
    
        @Override
        public void onCreate() {
            super.onCreate();
            Log.i(tag, this.getPackageName() + ":" + this.getClass().getSimpleName() + " onCreate.");
            Runnable task = null;
            while (!executorService.isShutdown()) {
                if ((task = taskQueue.poll()) != null) {
                    task.run();
                }
            }
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            Log.i(tag, this.getPackageName() + ":" + this.getClass().getSimpleName() + " onDestroy.");
            executorService.shutdownNow();
        }
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    while循环里面的poll()方法并不是阻阻塞的,因此会一直重复的跑这段代码导致CUP即使在手机锁屏的时候仍然高速的在运行着循环代码

    针对以上原因进行相应的优化

    • 针对Http请求: 
      1. 对http请求数据做GZIP压缩,当前流行的http第三方看默认都支持GZIP压缩
      2. Http缓存,Http协议有一个Cache机制,当发出http请求的时候会先到指定目录下检查是否已经存在这个请求的数据,如果存在并且还没过时,那么就会直接返回;而一些第三方例如OkHttp也有有自己的缓存机制OKHTTP缓存
      3. 合并Http请求来减少Http请求次数,因为Http底层也是TCP连接,对于每个Http请求,发出请求的时候都会创建TCP连接,请求结束后会断开TCP连接,那么当Http请求次数很多的时候就会频繁的创建和断开TCP连接,如果把当中一些请求进行合理的合并,那么就会减少Http请求次数
      4. 制定合理的Http请求数据格式和返回数据格式,做到请求数据中没有冗余字段
      5. 可以在Http请求数据格式里面加一个字段dataVersion代表本地已有数据的版本号,然后传到服务器,服务器的数据表中也有一个字段是dataVersion,当服务器数据被修改的时候,dataVersion就加一,当检测到客户端传上来的dataVersion小于服务器数据表中的dataVersion的时候就返回最新数据,否则可以直接返回空数据代表当前本地数据已经是最新数据,这样就不会每次请求http的时候都会返回大量数据,当数据没有被改变的时候直接返回空,减少了http请求过程中的数据交互(但是要考虑一点,只有一些请求数据量比较大的才适合,因为增加了dataVersion字段后无论给客户端还是服务端也都相应的增加了维护的成本)
    • 针对数据库,SD卡文件操作: 
      1. APP在对数据库或者SD卡文件操作的时候无非也是涉及到一些数据的转换,json转换,可以采用json解析效率高的第三方库,例如fast json,Jackson,gson
      2. 可以把一些需要持久化到数据库或者文件中的数据先缓存在内存中,然后在一个时间点一起触发一同更新到数据库或者文件中;例如,在进入Activity的时候会首先从数据库中搜索出帐户信息并且展示在界面,然后会再去发Http请求服务器的帐户信息数据,再把服务器最新的帐户信息数据刷新到界面中,同时也存在一个内存对象中,这时候先不更新到数据库,当退出这个Activity界面的时候可以去检测帐户数据是否发生改变,如果改变了就更新到数据中;这么做的好处是:如果在Activity界面多次修改数据,那最新的数据都是只更新到内存中的,当Activity退出后才把最新的帐户信息数据更新到数据库中,没必要更改一次就同时把数据更新一次到数据库
      3. SD卡的文件读写操作比数据库要快,数据库也是属于文件,但是在写数据的时候还要经过sqlite的一些列数据库操作,但是SD卡在写数据的时候是直接写到文件中的,所以针对与简单数据,标志变量或者数据条数很少的数据,而且安全性要求也不高的数据可以直接存在文件中,例如SharedPreferences中,只有关系型的数据,数据安全性较高的,数据记录条数比较多的可以选择数据库存储,而且如果数据库进行加密后,对于数据库的读写操作会更慢了
      4. 针对数据库操作尽量不要直接使用android里面的Sqlite来手写读写的那些sql语句,可以选择一些orm框架库,例如ormlite,GreenDao等等,我们选择的是ormlite,因为ormlite数据库加密已经有现成的库可以提供使用

    Android APP耗电优化

    可能造成耗电的一些原因

    • 网络请求耗电,而且手机数据网络进行http请求比无线网进行http请求更加耗电,因为数据网络调用到一些底层的硬件模块,就如GPS一样,当手机打开GPS功能后,也是启动了一些硬件模块就会明显增加耗电
    • 高频的刷新UI界面,刷新UI界面其实就是进行layout的绘制,如果一个Activity的布局嵌套太多层,那么每一层layout都会刷新一次,例如动画等等这些都会造成耗电
    • 数据库,SD卡文件操作,这些都是属于耗时操作,当操作次数很少的时候基本不会有耗电问题,但是当短时间内操作次数很多的话,也会明显的增加耗电,同时也有可能造成页面卡顿
    • AlarmManager,例如一些推送的心跳包实现,AlarmManager会定时唤醒CPU去执行一些任务,也是造成耗电的一大源头
    • 手机网络环境不好的时候会频繁的切换网络,因为网络数据交互的时候,系统也是会被唤醒的,所以APP如果在监听了网络切换广播后做了大量的操作,一样会增加耗电
    • 针对一些任务队列的处理,如果队列堆积的任务太多,导致循环执行太久也会造成耗电,因为占用了CPU资源去执行代码,我们的log日志工具保存到文件就是用任务队列实现的,当压力测试SDK一次性接受1万条消息的时候,那内存就表上来了,跟了下发现日志保存队列里面积压了4千多个任务,这时候即使手机锁屏,也还会不断的把队列中的任务执行完然后CPU才会休眠下去的,同样会造成严重的耗电,耗内存,好在release版本的日志都是关闭的
    • 执行一些高运算量的代码,例如json数据解析,一些二进制协议的数据编码和解码
    • 接收系统的一分钟广播,然后做一些程序逻辑处理,其实接收一分钟广播不耗电,耗电的是一分钟执行一次程序处理
    • Wake Lock使用不当导致没有及时的释放,Wake Lock可以阻止cpu进入休眠的,如果没有及时的release会造成cpu无法休眠,程序耗电严重
    • 如果程序中有定时任务,在cpu休眠之后,定时任务就会被挂起不执行,这时候并不会造成太大的耗电,但是如果这个定时任务的时间间隔很短,1秒执行一次,那么当手机app集成了推送,推送就会有心跳包通过AlarmManager来唤醒,每次唤醒的时候就会再去执行挂起的定时任务,虽然执行定时任务的耗电量可能比心跳包的耗电量少很多,不过还是需要注意的,积少成多
    • 其实android中的Log日志的打印也会耗电的,在日常开发中,我们可能不仅会把log打印到AndroidStudio里面,有可能还会保存起来,而且可能在打印对象信息数据的时候会用到json格式转换,这些都会增加耗电,但是在正式发布的apk包中日志管理一般都是关闭的
    • 在手机锁屏后,CPU会过一段时间才休眠,如果程序中有定时任务,在CPU休眠后会被挂起不执行,但是在CPU休眠之前,定时任务还是会一直的执行的,之前遇到过这么一个问题,我们采用Picasso库:Picasso.with(context)
    Picasso(Context context, Dispatcher dispatcher, Cache cache, Listener listener,
          RequestTransformer requestTransformer, List<RequestHandler> extraRequestHandlers, Stats stats,
          Bitmap.Config defaultBitmapConfig, boolean indicatorsEnabled, boolean loggingEnabled) {
        this.context = context;
        this.dispatcher = dispatcher;
        this.cache = cache;
        this.listener = listener;
        this.requestTransformer = requestTransformer;
        this.defaultBitmapConfig = defaultBitmapConfig;
    
        int builtInHandlers = 7; // Adjust this as internal handlers are added or removed.
        int extraCount = (extraRequestHandlers != null ? extraRequestHandlers.size() : 0);
        List<RequestHandler> allRequestHandlers =
            new ArrayList<RequestHandler>(builtInHandlers + extraCount);
    
        // ResourceRequestHandler needs to be the first in the list to avoid
        // forcing other RequestHandlers to perform null checks on request.uri
        // to cover the (request.resourceId != 0) case.
        allRequestHandlers.add(new ResourceRequestHandler(context));
        if (extraRequestHandlers != null) {
          allRequestHandlers.addAll(extraRequestHandlers);
        }
        allRequestHandlers.add(new ContactsPhotoRequestHandler(context));
        allRequestHandlers.add(new MediaStoreRequestHandler(context));
        allRequestHandlers.add(new ContentStreamRequestHandler(context));
        allRequestHandlers.add(new AssetRequestHandler(context));
        allRequestHandlers.add(new FileRequestHandler(context));
        allRequestHandlers.add(new NetworkRequestHandler(dispatcher.downloader, stats));
        requestHandlers = Collections.unmodifiableList(allRequestHandlers);
    
        this.stats = stats;
        this.targetToAction = new WeakHashMap<Object, Action>();
        this.targetToDeferredRequestCreator = new WeakHashMap<ImageView, DeferredRequestCreator>();
        this.indicatorsEnabled = indicatorsEnabled;
        this.loggingEnabled = loggingEnabled;
        this.referenceQueue = new ReferenceQueue<Object>();
        this.cleanupThread = new CleanupThread(referenceQueue, HANDLER);
        this.cleanupThread.start();
      }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    在Picasso构造的过程中:启动了一个线程CleanupThread

    private static class CleanupThread extends Thread {
        private final ReferenceQueue<Object> referenceQueue;
        private final Handler handler;
    
        CleanupThread(ReferenceQueue<Object> referenceQueue, Handler handler) {
          this.referenceQueue = referenceQueue;
          this.handler = handler;
          setDaemon(true);
          setName(THREAD_PREFIX + "refQueue");
        }
    
        @Override public void run() {
          Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND);
          while (true) {
            try {
              // Prior to Android 5.0, even when there is no local variable, the result from
              // remove() & obtainMessage() is kept as a stack local variable.
              // We're forcing this reference to be cleared and replaced by looping every second
              // when there is nothing to do.
              // This behavior has been tested and reproduced with heap dumps.
              RequestWeakReference<?> remove =
                  (RequestWeakReference<?>) referenceQueue.remove(THREAD_LEAK_CLEANING_MS);
              Message message = handler.obtainMessage();
              if (remove != null) {
                message.what = REQUEST_GCED;
                message.obj = remove.action;
                handler.sendMessage(message);
              } else {
                message.recycle();
              }
            } catch (InterruptedException e) {
              break;
            } catch (final Exception e) {
              handler.post(new Runnable() {
                @Override public void run() {
                  throw new RuntimeException(e);
                }
              });
              break;
            }
          }
        }
    
        void shutdown() {
          interrupt();
        }
      }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    CleanupThread线程里面是有一个while(true){}循环,这个就会在手机锁屏后持续运行一段时间才会被挂起的,这个过程就增加了耗电,然后打开查看CPU使用情况,发现app一直被占用一点点,正常情况下,app的退到后台后,cpu应该是不会一直被占用的,如下图: 
    这里写图片描述 
    包名com.xtc.watch在退到后台之后仍然在占用CPU的资源执行一些东西,然后打开AndroidStudio,查看monitor里面的cpu的方法跟踪功能(Start method tracing) 
    方法跟踪功能,红色部分就表示cpu被占用的比例,过一段时间后再次点击Start Method Tracing按钮停止方法跟踪,然后AndroidStudio会自动生成一个后缀名为.trace的分析文件: 
    方法执行线程信息
    上图中Thread后面的是执行方法所在线程的名称,Wall Clock Time表示程序从运行到终止所花费的时间,Thread Time是线程执行的时长,CPU Time是CPU的运行时间,在Thread列表里面的就表示在方法跟踪期间有执行过的线程,下面有颜色的长条是线程执行的时间信息Wall clock time和cpu time,如果在程序退到后台,手机锁屏后,仍然不断有程序里面的线程在跑,那么就存在了耗电风险了,你想想,程序退到后台,手机锁屏了,这时候所有程序线程有在方法跟踪期间的县城都已经展示出来了,如果发现程序耗电,很可能就是由于线程还在不断的运行造成的 
    - 在Service里面执行一些循环代码,也 
    可能造成耗电;如果Service是前台Service,那Service的进程优先级会比较高,或者是一些后台常驻Service,在这些Service执行无限循环的代码,耗电耗到手机发烫:

    public class PushService extends Service {
    
        private static final String tag = PushService.class.getSimpleName();
    
        private ExecutorService executorService = Executors.newSingleThreadExecutor();
    
        private Queue<Runnable> taskQueue = new LinkedList<>();
    
        @Override
        public void onCreate() {
            super.onCreate();
            Log.i(tag, this.getPackageName() + ":" + this.getClass().getSimpleName() + " onCreate.");
            Runnable task = null;
            while (!executorService.isShutdown()) {
                if ((task = taskQueue.poll()) != null) {
                    task.run();
                }
            }
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            Log.i(tag, this.getPackageName() + ":" + this.getClass().getSimpleName() + " onDestroy.");
            executorService.shutdownNow();
        }
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    while循环里面的poll()方法并不是阻阻塞的,因此会一直重复的跑这段代码导致CUP即使在手机锁屏的时候仍然高速的在运行着循环代码

    针对以上原因进行相应的优化

    • 针对Http请求: 
      1. 对http请求数据做GZIP压缩,当前流行的http第三方看默认都支持GZIP压缩
      2. Http缓存,Http协议有一个Cache机制,当发出http请求的时候会先到指定目录下检查是否已经存在这个请求的数据,如果存在并且还没过时,那么就会直接返回;而一些第三方例如OkHttp也有有自己的缓存机制OKHTTP缓存
      3. 合并Http请求来减少Http请求次数,因为Http底层也是TCP连接,对于每个Http请求,发出请求的时候都会创建TCP连接,请求结束后会断开TCP连接,那么当Http请求次数很多的时候就会频繁的创建和断开TCP连接,如果把当中一些请求进行合理的合并,那么就会减少Http请求次数
      4. 制定合理的Http请求数据格式和返回数据格式,做到请求数据中没有冗余字段
      5. 可以在Http请求数据格式里面加一个字段dataVersion代表本地已有数据的版本号,然后传到服务器,服务器的数据表中也有一个字段是dataVersion,当服务器数据被修改的时候,dataVersion就加一,当检测到客户端传上来的dataVersion小于服务器数据表中的dataVersion的时候就返回最新数据,否则可以直接返回空数据代表当前本地数据已经是最新数据,这样就不会每次请求http的时候都会返回大量数据,当数据没有被改变的时候直接返回空,减少了http请求过程中的数据交互(但是要考虑一点,只有一些请求数据量比较大的才适合,因为增加了dataVersion字段后无论给客户端还是服务端也都相应的增加了维护的成本)
    • 针对数据库,SD卡文件操作: 
      1. APP在对数据库或者SD卡文件操作的时候无非也是涉及到一些数据的转换,json转换,可以采用json解析效率高的第三方库,例如fast json,Jackson,gson
      2. 可以把一些需要持久化到数据库或者文件中的数据先缓存在内存中,然后在一个时间点一起触发一同更新到数据库或者文件中;例如,在进入Activity的时候会首先从数据库中搜索出帐户信息并且展示在界面,然后会再去发Http请求服务器的帐户信息数据,再把服务器最新的帐户信息数据刷新到界面中,同时也存在一个内存对象中,这时候先不更新到数据库,当退出这个Activity界面的时候可以去检测帐户数据是否发生改变,如果改变了就更新到数据中;这么做的好处是:如果在Activity界面多次修改数据,那最新的数据都是只更新到内存中的,当Activity退出后才把最新的帐户信息数据更新到数据库中,没必要更改一次就同时把数据更新一次到数据库
      3. SD卡的文件读写操作比数据库要快,数据库也是属于文件,但是在写数据的时候还要经过sqlite的一些列数据库操作,但是SD卡在写数据的时候是直接写到文件中的,所以针对与简单数据,标志变量或者数据条数很少的数据,而且安全性要求也不高的数据可以直接存在文件中,例如SharedPreferences中,只有关系型的数据,数据安全性较高的,数据记录条数比较多的可以选择数据库存储,而且如果数据库进行加密后,对于数据库的读写操作会更慢了
      4. 针对数据库操作尽量不要直接使用android里面的Sqlite来手写读写的那些sql语句,可以选择一些orm框架库,例如ormlite,GreenDao等等,我们选择的是ormlite,因为ormlite数据库加密已经有现成的库可以提供使用
    展开全文
  • Android APP耗电优化的一些经验

    万次阅读 2016-12-04 01:56:24
    APP怎么样才算是正常,怎么样才算是过于耗电,需要优化?测试方法 使用电流计测量APP运行时手机实时电流大小并与标准APP(一般是仅包含空白Activity的APP)进行比较 因为CPU的运行时间与耗电量是成正比的,所以我们...

    前言

    之前限于表达能力和时间,所以写得挺乱。今天一看这篇文章,已经有600多的阅读量了,也就是说我可能已经坑了600多的读者了,心里想想还是过意不去,所以今天把此文更新一下,聊表心意,希望能带给大家一些真正有用的经验。

    动机

    之前之所以想写这篇关于耗电优化的文章,是因为我参与开发的APP碰到了高耗电的问题,经常被用户投诉耗电很大、耗电排行第一,虽然是第一,但是想来并不是什么光荣的事情,所以我就很开心的被老大分配了优化耗电问题的任务,后来我也解决了这个问题,并再没有出现过用户投诉,所以想借此宝地跟大家分享一下经验。

    经验结论

    耗电的大小跟CPU的使用率成正比

    我是将物理的手机电流数据跟CPU的使用率对比得出此结论的,并且之后的分析都是基于这个结论的。问题解决之后,我更加相信这个结论是正确的。

    触屏时耗电相当高

    如果你的APP跟用户交互得比较频繁,那你可能要考虑一下是不是交互频繁导致的高耗电。因为触控屏也算是传感器,设计到传感器的操作都是耗电相当大的。游戏耗电大想必也有触屏的一部分功劳。大家可以使用下面的方法验证看看。

    警惕动画

    动画的连续变化是由View的不断重绘产生的效果,代价就是CPU或者GPU不断的计算从而导致耗电高,如果可以,不要做长时间的动画或者及时关闭动画,比如转圈的加载动画、后台界面的轮播图。特别需要警惕的是WebView的js动画和css动画,因为WebView加载的H5页面中的动画导致的是整个WebView的重绘,对CPU或GPU的消耗比普通View的动画要大很多。我这次碰到的一个问题就是H5页面最底部一直显示转圈的加载动画而页面滑到底部又会加载新的信息所以一直没有注意到这个问题,后来分析发现这个动画导致整个WebView一秒重绘几十次!这样耗电不高才怪!

    如何检测

    既然CPU的使用率过高是耗电的罪魁祸首,那我们怎么检测APP的使用率呢?

    top命令

    在adb shell中使用top命令可以列出系统中的所有进程及其CPU使用率,当APP进程的CPU使用率超过1%的时候,都是耗电比较大的了,正常情况下应该处于0%的值,实际上应该是0点几。对于这个命令,我经常使用的是top -m 5,只要列出前5个使用率最高的进程就够了,一般只有前5个才会有超过%1的CPU占用率。当APP进程的CPU占用率一直低于1%的时候,或者你想定位问题所在的时候,下面更精细的方法可以帮你捕捉到问题的所在。

    DDMS工具Update Thread功能

    当我们在DDMS工具中针对某个APP进程使用Update Thread功能的时候,会弹出一个窗口,这个窗口上显示了进程中所有线程的列表,还有CPU在其用户空间运行时间utime和系统空间运行时间stime,这个列表每隔几秒就会更新一次。我们要做的就是观察哪个线程的utime和stime的数据增长得快,增长得快表示这个线程占用了比较多的CPU资源,我们就可以具体查看是哪些代码在运行导致耗电高。一点经验:RenderThread增长得快一般是动画导致的。

    dumpsys batterystats

    这里新增一个分析方法,是我后来才学到的,大家可以进这个链接了解下Android App耗电发热分析

    展开全文
  • Android App耗电发热分析

    千次阅读 2017-04-12 21:18:07
    如题,这是一篇分享如何分析Android平台App耗电问题的文章,今天学到的新技能,算是之前写过的一篇文章Android APP耗电优化的一些经验的续集吧,O(∩_∩)O。 首先,上一个adb命令: adb shell dumpsys ...

    如题,这是一篇分享如何分析Android平台App耗电问题的文章,今天学到的新技能,算是之前写过的一篇文章Android APP耗电优化的一些经验的续集吧,O(∩_∩)O。
    首先,上一个adb命令:

    adb shell dumpsys batterystats

    这个命令可以打印出于电池消耗相关的信息,可以通过

    adb shell dumpsys batterystats > D:/batterystats.txt

    命令将内容输出到文本文件中。文件大致内容如下:

    这里写图片描述

    1. 左边的方框内是时间线,在这个文件的记录中,电池消耗是以时间为线索的。
    2. 中间的方框是剩余电池电量,在正常使用情况下,会随着时间减少。
    3. 右边的方框表示在该时间发生了什么事,一般都是与耗电相关的事件的重要节点。比如+wake_lock是某个应用获取了唤醒锁,-wake_lock是释放了唤醒锁。+running则表示CPU被某段程序占用,-running则表示该段程序运行完了等等,意思都是比较字面化的。而u0a78等符号则表示应用id,一个id对应一个应用,应用的id列表可以在这个文件内找到。(以上都是我的猜测,但是经过我一天学习应该基本正确。。。内谁,你先把砖头放下,有话我们慢慢说)

    那有了这个报告之后该怎么分析耗电高,手机发热的原因呢?我们用notepad++打开该文件然后搜索“temp=”,会得出下面的结果:
    这里写图片描述
    看到没有,电量有了,手机温度也有了,我们只要分析温度升高还有电量下降特别快的时间里发生了什么事情就行了。我的手机使用比较正常所以这里没有太特别的地方。

    最后,该上轮子了。google已经做了工具用于可视化的显示这个报告:battery-historian
    我们只要跟着下面几个步骤做就可以查看可视化电池报告啦:
    1.下载上面链接中的battery-historian工具并解压。
    2.打开解压文件夹中的script文件夹。
    3.在该路径下打开命令行窗口。
    4.输入historian.py D:/batterystats.txt > D:/battery.html。这里需要安装python环境,我装的是python2.7,可以正常使用。
    5.用chrome浏览器打开battery.html文件。
    做完以上步骤我们就可以看到以下界面,开始分析吧少年:
    这里写图片描述

    展开全文
  • 电量和硬件1.1 App 通过使用硬件模块消耗相应的电能1.2 资源调度机制是厂商功耗优化最重要的手段2. 电量和应用程序2.1 评估不同应用程序的耗电情况结论:把电量的测量转化为功能模块的使用时间或者次数2.2 尽可能...
  • Android App耗电分析

    千次阅读 2016-01-20 10:33:47
    项目中用到了一个比较坑爹的问题,就是App耗电问题,发现自己开发的时候没有注意这个问题,一发布给用户就暴露了这个我平常不注意的问题挺蛋疼,平常开发我都觉得耗电是很正常的,再耗电也在可接受范围内,但是...
  • SDK-tools版本低于25的参看另外一篇文章:Android App 耗电量分析-1 默认配置了git环境,没有的自行配置 配置Go语言环境 下载 https://golang.org/doc/install#testing 配置,主要是下载下载Battery Historian...
  • android app耗电分析方法

    千次阅读 2018-10-10 16:23:41
    这是一篇讲述应用耗电的文章,围绕 Android 电量采集机制及第二代 Battery Historian 分析工具讲述。文从数据采集、导出、环境搭建、解读报告的角度出发,从细节讲解整个流程。和大谈概念的文章不同,这里将进行实际...
  • Android App 耗电量分析-1

    千次阅读 2018-12-07 19:29:41
    Android App 电量分析  耗电操作主要分为下面几种 高频通信 CPU密集型的计算 传感器  频繁唤醒系统 解决方案 -减少:您的应用可以裁剪多少操作?例如,它可以缓存下载的数据,而不是重复唤醒无线电重新下载...
  • 深入浅出Android App耗电量统计

    千次阅读 2016-08-13 10:03:50
    Android统计App耗电量比较麻烦,直至Android 4.4,它仍没公开“电量统计”API或文档……额,是的,仅没有公开,并不是没有。平时在手机“设置- 电量”看到的数据就是系统调用内部API的统计结果。
  • android网络耗电优化

    千次阅读 2017-11-21 11:45:13
    一,减小网络功耗应用程序的网络请求是造成电池耗尽的主要原因,因为他们打开耗电的移动数据或Wi-Fi无线电。除了发送和接收数据包所需的功率之外,这些无线电只要打开并保持清醒,就会消耗额外的功率。 每15秒一个...
  • android app性能优化

    2017-09-23 17:43:35
    android app性能优化
  • Android App性能优化系列 关于Android App优化,从第一篇的计划开始,到内存优化的系列文结束,不知不觉近三个月的时间,写了十五六篇相关的博文,算是对自己的知识的一个系统化,也希望能给大家一些帮助。 在此...
  • Android App 性能优化实践 首页分类标签留言关于订阅2015-01-29 | 分类 android | 标签 Optimization  本文记录了Android App优化需要用到的工具和以及在实践中的Tips。也算对我这半年来部分工作的总结。
  • Android APP性能优化

    2019-06-26 11:34:30
    Android APP性能优化(最新总结) 导语 安卓大军浩浩荡荡,发展已近十个年头,技术优化日异月新,如今Android 8.0 Oreo都发布了,Android系统性能已经非常流畅了。但是,到了各大厂商手里,改源码自定系统,使得...
  • Android耗电优化

    2020-01-23 16:57:29
    1.为什么要进行耗电优化 如果一个app使用的很少,但是app的耗电量却很高,这时候用户肯定想直接卸载这个app。那么如何降低自己app的耗电量就是一个很重要的事情了。 2.耗电的因素有那些呢? (1)、Alarm Manager ...
  • 考虑到既然是优化性能,那首先要获取App的相关性能指标,比如: 内存占用、CPU利用率、App启动时间、App耗电量等情况,获取以上各指标后,才能针对性分析确定是否存在优化的可能。 查看App启动时间(冷启动、热启动...
  • 关于Android App优化, 从第一篇的计划开始, 到内存优化的系列文结束, 不知不觉近三个月的时间, 写了十五六篇相关的博文, 算是对自己的知识的一个系统化, 也希望能给大家一些帮助. 在此有对此做一个总结.路线 ...
  • 原文链接:https://www.androidperformance.com/2019/11/18/Android-App-Lunch-Optimize/ 本文参考了目前大部分 Android 应用启动优化的方案,将大家的方案做一个汇总,如果你有这方面的需求,只需要对照这篇文章,...
  • Android App 性能优化

    2018-10-09 17:06:16
    快、稳、省、小 流程的操作体验 稳定 省电、省流量 安装包小 不好的体验 卡顿 内存泄露、崩溃 代码质量和逻辑差导致耗流量、耗电 安装包过大 ...Android 显示过程可以简单概括为:Android 应用程序把...
  • Android App耗电发热分析2017年04月12日 21:18:07阅读数:4217如题,这是一篇分享如何分析Android平台App耗电问题的文章,今天学到的新技能,算是之前写过的一篇文章Android APP耗电优化的一些经验的续集吧,O(∩_∩...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 6,206
精华内容 2,482
关键字:

androidapp耗电优化