精华内容
下载资源
问答
  • 2016-10-19 17:04:38

    1.在工程gradle加上后面2句

    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        testCompile 'junit:junit:4.12'
        compile 'com.android.support:appcompat-v7:23.4.0'
        debugCompile 'com.squareup.leakcanary:leakcanary-android:1.4-beta2'
        releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2'
    }
    2.在自己定义的application这样写
    public void onCreate() {
        super.onCreate();
    if (LeakCanary.isInAnalyzerProcess(this)) {
        // This process is dedicated to LeakCanary for heap analysis.
        // You should not init your app in this process.
        return;
    }
    refWatcher = LeakCanary.install(this);}public static RefWatcher getRefWatcher(Context context) { MApplication application = (MApplication) context.getApplicationContext(); return application.refWatcher;}private RefWatcher refWatcher;
    3.在activity的ondestory里面这样写
    @Override
    protected void onDestroy() {
        super.onDestroy();
        RefWatcher refWatcher = MApplication.getRefWatcher(this);
        refWatcher.watch(this);
    }
    eclipse 看这里:
    http://blog.csdn.net/liudehuaii18/article/details/46287695
    

    更多相关内容
  • 今天来写一个关于LeakCanary-1.5.0的一些心得;我们移动端性能方面第一个就想到使用LeakCanary;但是很多使用LeakCanary无法解析,以及LeakCanary在不同手机上面也会有不同的一些提示;我这边自己抽了一些时间,对...

           今天来写一个关于LeakCanary-1.5.0的一些心得;我们移动端性能方面第一个就想到使用LeakCanary;但是很多使用LeakCanary无法解析,以及LeakCanary在不同手机上面也会有不同的一些提示;我这边自己抽了一些时间,对LeakCanary的架构进行分析,以及代码的实现过程进行分析;不知道一篇能不能写完:下面先上一个图;      

          这个是自己画的LeakCanary的uml图;仅仅画了初始化的地方;这边会从这个图一步步解析LeakCanary的作用;首先我们先看到LeakCanary的初始化

         1)install(Application application)初始化方法

    public static RefWatcher install(Application application) {
        return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
            .excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
            .buildAndInstall();
      }
     public static AndroidRefWatcherBuilder refWatcher(Context context) {
        return new AndroidRefWatcherBuilder(context);
      }

      其中new 一个AndroidRefWatcherBuilder对象,开始注册listenerServiceClass并且注册一个ExcludedRefs,最后进行组装;

    其中buildAndInstall方法里面涉及到了Application注册Activity的生命周期;主要在生命周期的里面的onActivityDestroyed里面进行触发一个方法ActivityRefWatcher.this.onActivityDestroyed(activity);

        2)我们开始分析,第一步new AndroidRefWatcherBuilder并且设置ApplicationContext;

        3)设置listenerServiceClass;其中传入了DisplayLeakService.class;(专门处理heapDump文件)

         DisplayLeakService的父类是AbstractAnalysisResultService,AbstractAnalysisResultService的父类是IntentService。其实走到这一步;大概能看出来做一个异步的耗时操作了;肯定是在onHandleIntent(Intent intent);并且sendResultToListener是进行发送耗时操作的方式;获取heapDump文件,进行分析;重写onHeapAnalyzed方法,具体在DisplayLeakService实现;(具体实现后面分析)

        4)  获取AndroidExcludedRefs;创建ExcludedRefs对象;里面更多包含的是配置

        先看一下createAndroidDefaults方法;其中里面默认实现了SOFT_REFERENCES, FINALIZER_WATCHDOG_DAEMON, MAIN, LEAK_CANARY_THREAD, EVENT_RECEIVER__MMESSAGE_QUEUE;

         A) SOFT_REFERENCES:

    a.注册软引用的类名

    b.注册弱引用的类名

    c.注册虚引用的类名

    d.注册Finalizer的类名(GC: 从一个对象变得不可达开始,到执行它的finalizer方法,时间可能任意长)

    f.注册FinalizerReference的类名(class类里面定义finalize方法,就会创建)

         B)FINALIZER_WATCHDOG_DAEMON:

    a.注册FinalizerWatchdogDaemon线程名称(回收的线程)

         C)MAIN

    a.注册主线程名称

         D)LEAK_CANARY_THREAD

    a.注册LeakCanary的工作线程名称

         E)EVENT_RECEIVER__MMESSAGE_QUEUE

    a.注册android.view.Choreographer$FrameDisplayEventReceiver以及mMessageQueue;应该是需要继承修改;后续在看

         F)剩下的其他和机型;Api相关。是针对某一个Api遇到内存泄露作出的指定捕获,或者刨除;这边也是后续抽一个例子细看

    这个类使用的是 EnumSet.allOf 该方法接受一个元素类型的参数elementType,并引用其元素将存储到集合中的类对象;

       5)buildAndInstall()方法

         调用这个方法的时候;判断是否是初始化默认的DISABLED;这个位子后续说;这边不是;所以走下面的代码;

     public RefWatcher buildAndInstall() {
        RefWatcher refWatcher = build();
        if (refWatcher != DISABLED) {
          LeakCanary.enableDisplayLeakActivity(context);
          ActivityRefWatcher.installOnIcsPlus((Application) context, refWatcher);
        }
        return refWatcher;
      }

    走到enableDisplayLeakActivity方法;

     public static void enableDisplayLeakActivity(Context context) {
        setEnabled(context, DisplayLeakActivity.class, true);
      }

    里面开启类一个单核心线程

    public static void setEnabled(Context context, final Class<?> componentClass,
          final boolean enabled) {
        final Context appContext = context.getApplicationContext();
        executeOnFileIoThread(new Runnable() {
          @Override public void run() {
            setEnabledBlocking(appContext, componentClass, enabled);
          }
        });
      }

    来处理setEnabledBlocking方法;

     public static void setEnabledBlocking(Context appContext, Class<?> componentClass,
          boolean enabled) {
        ComponentName component = new ComponentName(appContext, componentClass);
        PackageManager packageManager = appContext.getPackageManager();
        int newState = enabled ? COMPONENT_ENABLED_STATE_ENABLED : COMPONENT_ENABLED_STATE_DISABLED;
        // Blocks on IPC.
        packageManager.setComponentEnabledSetting(component, newState, DONT_KILL_APP);
      }

    其中packageManager.setComponentEnabledSetting(component, newState, DONT_KILL_APP);来控制启用 禁用 四大组件

    由上面代码里面 对DisplayLeakActivity进行设置成----可用状态,并且不杀死APP(明显是在处理Activity的

    下面在走 installOnIcsPlus 方法;

    其中SDK低于14的不进行初始化观察;

      public static void installOnIcsPlus(Application application, RefWatcher refWatcher) {
        if (SDK_INT < ICE_CREAM_SANDWICH) {
          // If you need to support Android < ICS, override onDestroy() in your base activity.
          return;
        }
        ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);
        activityRefWatcher.watchActivities();
      }

    其中使用Application进行注册Activity生命周期,所以Fragment的泄露希望是自己去调用

      private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
          new Application.ActivityLifecycleCallbacks() {
            @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
            }
    
            @Override public void onActivityStarted(Activity activity) {
            }
    
            @Override public void onActivityResumed(Activity activity) {
            }
    
            @Override public void onActivityPaused(Activity activity) {
            }
    
            @Override public void onActivityStopped(Activity activity) {
            }
    
            @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
            }
    
            @Override public void onActivityDestroyed(Activity activity) {
              ActivityRefWatcher.this.onActivityDestroyed(activity);
            }
          };
    

    在里面执行了onActivityDestroyed方法;

         刚刚在上面没有提到buildAndInstall方法里面的build;现在我们回过来讲一下;因为我们现在已经知道了,这个LeakCanary触发的时机;

     public final RefWatcher build() {
        if (isDisabled()) {
          return RefWatcher.DISABLED;
        }
        //1
        ExcludedRefs excludedRefs = this.excludedRefs;
        if (excludedRefs == null) {
          excludedRefs = defaultExcludedRefs();
        }
        //2
        HeapDump.Listener heapDumpListener = this.heapDumpListener;
        if (heapDumpListener == null) {
          heapDumpListener = defaultHeapDumpListener();
        }
        //3
        DebuggerControl debuggerControl = this.debuggerControl;
        if (debuggerControl == null) {
          debuggerControl = defaultDebuggerControl();
        }
        //4
        HeapDumper heapDumper = this.heapDumper;
        if (heapDumper == null) {
          heapDumper = defaultHeapDumper();
        }
        // 5
        WatchExecutor watchExecutor = this.watchExecutor;
        if (watchExecutor == null) {
          watchExecutor = defaultWatchExecutor();
        }
        // 6
        GcTrigger gcTrigger = this.gcTrigger;
        if (gcTrigger == null) {
          gcTrigger = defaultGcTrigger();
        }
    
        return new RefWatcher(watchExecutor, debuggerControl, gcTrigger, heapDumper, heapDumpListener,
            excludedRefs);
      }

       我们先看到 :

       1 是ExcludedRefs对象的赋值;从uml图里面可以看到子类AndroidRefWatcherBuilder和父类RefWatcherBuilder;在初始化的时候调用的就是对父类this.excludedRefs赋值;

       2是AndroidRefWatcherBuilder子类里面调用父类的heapDumpListener方法赋值this.heapDumpListener

       3是AndroidRefWatcherBuilder类里面重写子类的defaultDebuggerControl方法,里面获取是否是被调试状态:Debug.isDebuggerConnected()

      4 是AndroidRefWatcherBuilder类里面重写子类的defaultHeapDumper方法 (下面细讲)

      5 是AndroidRefWatcherBuilder类里面重写子类的defaultWatchExecutor方法,初始化AndroidWatchExecutor延迟5000毫秒

    (专门开了一个工作线程处理)

      6 是AndroidRefWatcherBuilder类里面 使用默认的GcTrigger(做GC操作的)

    然后组装到RefWatcher里面去;下面我们开始讲解一下整个工作的流程;以及重点的实现部分

    现在开走;实现的内存泄露检测流程

    当Activity涉及到destory时

      void onActivityDestroyed(Activity activity) {
        refWatcher.watch(activity);
      }

    调用watch方法

      public void watch(Object watchedReference, String referenceName) {
        if (this == DISABLED) {
          return;
        }
        checkNotNull(watchedReference, "watchedReference");//校验不为null
        checkNotNull(referenceName, "referenceName");//校验不为null
        final long watchStartNanoTime = System.nanoTime();//纳秒
        String key = UUID.randomUUID().toString();//唯一标识码
        retainedKeys.add(key);//缓存起来
        final KeyedWeakReference reference =
            new KeyedWeakReference(watchedReference, key, referenceName, queue);//生成weak;配置一个key和name(name看起来是空值),加到队列里面去,继承WeakReference

        ensureGoneAsync(watchStartNanoTime, reference);//实现runable的方法;
      }

      private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
        watchExecutor.execute(new Retryable() {//watchExecutor上面说的设置一个工作线程;
          //判断是否是主线程; 如果是的话,直接addIdleHandler(CPU空闲的时候会调用)然后执行run();(有重试机制)都是延时操作
          // 如果不是主线程,先post回主线程,addIdleHandler(CPU空闲的时候会调用)然后执行run();(有重试机制)都是延时操作

          @Override public Retryable.Result run() {
            return ensureGone(reference, watchStartNanoTime);//RETRY会进行重试
          }
        });
      }

      Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
        long gcStartNanoTime = System.nanoTime();//获取纳秒
        long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);//计算出来耗时多久

        removeWeaklyReachableReferences();//移除队列里面的值,以及对应的retainedKeys的name 重要1

        if (debuggerControl.isDebuggerAttached()) {//判断是否在调试,如果是调试的返回重试
          // The debugger can create false leaks.
          return RETRY;
        }
        if (gone(reference)) {//如果retainedKeys没有缓存的reference的话,不做操作
          return DONE;
        }
        gcTrigger.runGc();//表示有reference,进行GC操作  重要2
        //Runtime.getRuntime().gc(); 发起GC操作
        //Thread.sleep(100);//等待 100毫秒
        //System.runFinalization();//运行处于挂起终止状态的所有对象的终止方法。
        //调用该方法说明 Java 虚拟机做了一些努力运行已被丢弃对象的 finalize 方法,但是这些对象的 finalize 方法至今尚未运行。当控制权从方法调用中返回时,Java 虚拟机已经尽最大努力去完成所有未执行的终止方法

        removeWeaklyReachableReferences();//移除队列里面的值,以及对应的retainedKeys的name
        if (!gone(reference)) {//如果这个时候,队列里面的reference被移除了,但是retainedKeys还是包含reference,进行下一步内存泄露分析,如果没有表示被回收了;重要 3
          long startDumpHeap = System.nanoTime();//再来一个纳秒
          long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);//计算耗时

          File heapDumpFile = heapDumper.dumpHeap();//生成heap dump文件,并且保存起来;看 AndroidHeapDumper类 重要4
          if (heapDumpFile == RETRY_LATER) {//如果是重试,进行重试操作
            // Could not dump the heap.
            return RETRY;
          }
          long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);//计算耗时
          heapdumpListener.analyze(
              new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
                  gcDurationMs, heapDumpDurationMs));//开始分析,看ServiceHeapDumpListener 类 ->HeapAnalyzerService类 重要5
        }
        return DONE;
      }

    重点说明:(这个操作是线程空余状态进行)

    重点1:

    清空不存在的误差

    重点2:

    进行GC的操作,并且还是延迟了

    重点3:

    是利用缓存,以及队列的删除进行判断是否存在内存泄漏

    重点4:

    AndroidHeapDumper类:

     @Override public File dumpHeap() {
        File heapDumpFile = leakDirectoryProvider.newHeapDumpFile();//生成路径
        // 看下面的DefaultLeakDirectoryProvider类newHeapDumpFile();生成路径不多说明
    
        if (heapDumpFile == RETRY_LATER) {//如果稍后在进行重试;会返回一个标识
          return RETRY_LATER;
        }
    
        FutureResult<Toast> waitingForToast = new FutureResult<>();
        showToast(waitingForToast);//先进行弹框出现内存泄露标志,这个也是cpu空闲时,缓存该toast
    
        if (!waitingForToast.wait(5, SECONDS)) {//并发
          CanaryLog.d("Did not dump heap, too much time waiting for Toast.");
          return RETRY_LATER;
        }
    
        Toast toast = waitingForToast.get();//获取toat
        try {
          Debug.dumpHprofData(heapDumpFile.getAbsolutePath());//Debug.dumpHprofData 生成heap dump文件,后面是路径;//目标找到了,这个是生成文件
          cancelToast(toast);//取消弹窗
          return heapDumpFile;//返回写入的路径
        } catch (Exception e) {
          CanaryLog.d(e, "Could not dump heap");
          // Abort heap dump
          return RETRY_LATER;//失败了重试
        }
      }

    里面生成了真正的dump的对象,以及解析的类

    重点5:

    ServiceHeapDumpListener 类 ->HeapAnalyzerService类

      @Override protected void onHandleIntent(Intent intent) {
        if (intent == null) {
          CanaryLog.d("HeapAnalyzerService received a null intent, ignoring.");
          return;
        }
        String listenerClassName = intent.getStringExtra(LISTENER_CLASS_EXTRA);
        HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA);
    
        HeapAnalyzer heapAnalyzer = new HeapAnalyzer(heapDump.excludedRefs);//输入信息类
    
        AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey);//TODO 1 检测内存泄漏,以及生成result
        AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
        //TODO 2 DisplayLeakService 也是一样开启一个IntentService处理 onHeapAnalyzed方法里面
      }

    这个类操作之后,到DisplayLeakService 这个进行最后的数据小处理,以及弹通知操作

    下面细讲一下TODO:

    TODO 1:

    HeapAnalyzer类:这个是我们非常关键的类。因为会涉及到我们判断哪一些不需要写入;不需要生成泄漏文件;所以上面的配置类很重要

      public AnalysisResult checkForLeak(File heapDumpFile, String referenceKey) {//确定一下是否是内存泄露
        long analysisStartNanoTime = System.nanoTime();//纳秒
    
        if (!heapDumpFile.exists()) {//判断文件是否存在;不存在的话,进行返回失败操作
          Exception exception = new IllegalArgumentException("File does not exist: " + heapDumpFile);
          return failure(exception, since(analysisStartNanoTime));
        }
    
        try {
          HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile);//MemoryMappedFileBuffer 将heapDumpFile传入,生成HprofBuffer,这个到haha的那个库,后续分析
          HprofParser parser = new HprofParser(buffer);//转码,也是haha
          Snapshot snapshot = parser.parse();//这几个转化后续在看,上面就是将文件转化成可以识别的对象
          deduplicateGcRoots(snapshot);//生成GC root快照
    
          Instance leakingRef = findLeakingReference(referenceKey, snapshot);//这边开始查找泄漏Instance
    
          // False alarm, weak reference was cleared in between key check and heap dump.
          if (leakingRef == null) {//如果没找到,表示没有内存泄漏
            return noLeak(since(analysisStartNanoTime));
          }
    
          return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef);//有的话,开始继续查找
        } catch (Throwable e) {
          return failure(e, since(analysisStartNanoTime));
        }
      }
    
      /**
       * Pruning duplicates reduces memory pressure from hprof bloat added in Marshmallow.
       */
      void deduplicateGcRoots(Snapshot snapshot) {
        // THashMap has a smaller memory footprint than HashMap.
        final THashMap<String, RootObj> uniqueRootMap = new THashMap<>();
    
        final List<RootObj> gcRoots = (ArrayList) snapshot.getGCRoots();//获取根的gc root(可达性分析法)
        for (RootObj root : gcRoots) {//开始遍历
          String key = generateRootKey(root);//转化成字符串
          if (!uniqueRootMap.containsKey(key)) {//排重操作,如果没有的话,就加进去
            uniqueRootMap.put(key, root);
          }
        }
    
        // Repopulate snapshot with unique GC roots.
        gcRoots.clear();//清空
        uniqueRootMap.forEach(new TObjectProcedure<String>() {//haha里面的,这边应该是转化成字符串之类的,方便后续识别
          @Override public boolean execute(String key) {
            return gcRoots.add(uniqueRootMap.get(key));
          }
        });
      }
    
      private String generateRootKey(RootObj root) {
        return String.format("%s@0x%08x", root.getRootType().getName(), root.getId());
      }
    
      private Instance findLeakingReference(String key, Snapshot snapshot) {//返回找到的referenceKey的KeyedWeakReference泄漏
        ClassObj refClass = snapshot.findClass(KeyedWeakReference.class.getName());//看是否包含 KeyedWeakReference这个对象名字(弱引用);对象无法被销毁导致的
        List<String> keysFound = new ArrayList<>();
        for (Instance instance : refClass.getInstancesList()) {//haha里面,进行遍历
          List<ClassInstance.FieldValue> values = classInstanceValues(instance);//获取 List<FieldValue>
          String keyCandidate = asString(fieldValue(values, "key"));
          if (keyCandidate.equals(key)) {//看一下是否包含该key名称的,即referenceKey,有的话就是找到了该泄漏的
            return fieldValue(values, "referent");
          }
          keysFound.add(keyCandidate);
        }
        throw new IllegalStateException(
            "Could not find weak reference with key " + key + " in " + keysFound);
      }
    
      private AnalysisResult findLeakTrace(long analysisStartNanoTime, Snapshot snapshot,
          Instance leakingRef) {
    
        ShortestPathFinder pathFinder = new ShortestPathFinder(excludedRefs);//之前的配置类;
        ShortestPathFinder.Result result = pathFinder.findPath(snapshot, leakingRef);//ShortestPathFinder类
    
        // False alarm, no strong reference path to GC Roots.
        if (result.leakingNode == null) {
          return noLeak(since(analysisStartNanoTime));
        }
    
        LeakTrace leakTrace = buildLeakTrace(result.leakingNode);
    
        String className = leakingRef.getClassObj().getClassName();
    
        // Side effect: computes retained size.
        snapshot.computeDominators();
    
        Instance leakingInstance = result.leakingNode.instance;
    
        long retainedSize = leakingInstance.getTotalRetainedSize();
    
        retainedSize += computeIgnoredBitmapRetainedSize(snapshot, leakingInstance);
    
        return leakDetected(result.excludingKnownLeaks, className, leakTrace, retainedSize,
            since(analysisStartNanoTime));
      }

    TODO 2:

    DisplayLeakService类,进行发送通知

      @Override protected final void onHeapAnalyzed(HeapDump heapDump, AnalysisResult result) {
        String leakInfo = leakInfo(this, heapDump, result, true);
        CanaryLog.d(leakInfo);
    
        boolean resultSaved = false;
        boolean shouldSaveResult = result.leakFound || result.failure != null;
        if (shouldSaveResult) {
          heapDump = renameHeapdump(heapDump);//生成heapDump
          resultSaved = saveResult(heapDump, result);//保存起来。这个会让另一个页面去找到该路径
        }
    
        PendingIntent pendingIntent;
        String contentTitle;
        String contentText;
    
        if (!shouldSaveResult) {
          contentTitle = getString(R.string.leak_canary_no_leak_title);
          contentText = getString(R.string.leak_canary_no_leak_text);
          pendingIntent = null;
        } else if (resultSaved) {
          pendingIntent = DisplayLeakActivity.createPendingIntent(this, heapDump.referenceKey);
    
          if (result.failure == null) {
            String size = formatShortFileSize(this, result.retainedHeapSize);
            String className = classSimpleName(result.className);
            if (result.excludedLeak) {
              contentTitle = getString(R.string.leak_canary_leak_excluded, className, size);
            } else {
              contentTitle = getString(R.string.leak_canary_class_has_leaked, className, size);
            }
          } else {
            contentTitle = getString(R.string.leak_canary_analysis_failed);
          }
          contentText = getString(R.string.leak_canary_notification_message);
        } else {
          contentTitle = getString(R.string.leak_canary_could_not_save_title);
          contentText = getString(R.string.leak_canary_could_not_save_text);
          pendingIntent = null;
        }
        // New notification id every second.
        int notificationId = (int) (SystemClock.uptimeMillis() / 1000);
        showNotification(this, contentTitle, contentText, pendingIntent, notificationId);//发送Notification通知
        afterDefaultHandling(heapDump, result, leakInfo);
      }

    以上是我们使用LeakCanary的基本操作流程;所以这些是我需要了解;后续我们再去分析haha这个库

    展开全文
  • LeakCanary2的免写 初始化代码 原理

    千次阅读 2019-05-01 22:15:24
    最近LeakCanary做了升级,发布了2.0版本,带了了很多性能上的优化,不过一个很吸引我的点在于,他居然不像以前一样,需要手动初始化了。 按照以前的使用流程,一般我们都是在dependencies 加入依赖 dependencies { ...

    最近LeakCanary做了升级,发布了2.0版本,带了了很多性能上的优化,不过一个很吸引我的点在于,他居然不像以前一样,需要手动初始化了。

    按照以前的使用流程,一般我们都是在dependencies 加入依赖

    dependencies {
      debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.3'
      releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.3'
      // Optional, if you use support library fragments:
      debugImplementation 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.3'
    }	
    

    接着在我们的application里面加入初始化的逻辑。

    public class ExampleApplication extends Application {
    	
    	  @Override public void onCreate() {
    	    super.onCreate();
    	    if (LeakCanary.isInAnalyzerProcess(this)) {
    	      // This process is dedicated to LeakCanary for heap analysis.
    	      // You should not init your app in this process.
    	      return;
    	    }
    	    LeakCanary.install(this);
    	    // Normal app init code...
    	  }
    	}
    

    但是,新版本的 LeakCanary 2.0居然可以不再需要写这个操作,只需要在代码里面加入这么一句依赖就可以了

    dependencies {
      debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.0-alpha-1'
    }
    

    我有点怀疑自己的眼睛,重新看了下他们的Readme,是不是真的

    Getting started Add LeakCanary to your build.gradle:

    dependencies { debugImplementation com.squareup.leakcanary:leakcanary-android:2.0-alpha-1' }

    You’re good to go! LeakCanary will automatically show a notification when an activity or fragment memory leak is detected in
    your debug build.

    好吧,确实是这样,那么到底怎么做到的?很神奇啊,这怎么也会有一个地方会需要初始化的,到底换到那里去了?

    在经过对源码的解读后,发现了一个骚操作,感觉传开后,以后的sdk库都可能这么做,教坏小朋友了。

    ContentProvider

    在经过对源码的阅读后,发现其实人家是基于CP这个对于绝大数开发来说,基本没用到的四大组件之一来做的,真的是服了哈哈。查看他的leakcanary-leaksentry 模块的AndroidManifest.xml文件,可以看到下面的内容:

    <?xml version="1.0" encoding="utf-8"?>
    <manifest
        xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.squareup.leakcanary.leaksentry"
        >
    
      <application>
        <provider
            android:name="leakcanary.internal.LeakSentryInstaller"
            android:authorities="${applicationId}.leak-sentry-installer"
            android:exported="false"/>
      </application>
    </manifest>
    

    接着我们去看下那LeakSentryInstaller这个类到底做了什么。

    internal class LeakSentryInstaller : ContentProvider() {
    
      override fun onCreate(): Boolean {
        CanaryLog.logger = DefaultCanaryLog()
        val application = context!!.applicationContext as Application
        InternalLeakSentry.install(application)  
        //骚操作在这里,利用系统自动调用CP的onCreate方法来做初始化
        return true
      }
    
      override fun query(
        uri: Uri,
        strings: Array<String>?,
        s: String?,
        strings1: Array<String>?,
        s1: String?
      ): Cursor? {
        return null
      }
    
      override fun getType(uri: Uri): String? {
        return null
      }
    
      override fun insert(
        uri: Uri,
        contentValues: ContentValues?
      ): Uri? {
        return null
      }
    
      override fun delete(
        uri: Uri,
        s: String?,
        strings: Array<String>?
      ): Int {
        return 0
      }
    
      override fun update(
        uri: Uri,
        contentValues: ContentValues?,
        s: String?,
        strings: Array<String>?
      ): Int {
        return 0
      }
    }
    

    我们看到这个CP类,没做任何的CURD操作,全是空的,就纯粹利用系统会回调这个接口来做初始化,帮助开发偷懒,省去调用写初始化逻辑。

    个人看待这个,觉得得分两部门
    好处:确实带来了 "免侵入",不需要业务人员写任何代码。
    坏处:这有点把CP给用歪了。以后所有人都这么弄,接入的sdk都这么写的话,那就真的可爱了。

    ref

    1. https://github.com/square/leakcanary/wiki/Migrating-to-LeakCanary-2.0
    展开全文
  • Square公司出品的大名鼎鼎的LeakCanary,就是业界知名的内存泄露检测的利器。


    作为android进阶知识,性能优化不管是在社招面试还是在日常工作中都是相当实用的知识,并且也是区分中级和高级程序员的试金石。

    今天我们先学习内存优化中的一个小知识点,就是内存泄露的检测和解决。当然,如何解决是大多数中级工程师都要去学习的东西,网上也有大量的资料,所以我这里不会详解。而是主要着眼于内存泄露的检测。Square公司出品的大名鼎鼎的LeakCanary,就是业界知名的内存泄露检测的利器。

    在这里插入图片描述

    阅读本篇文章,预计需要20分钟,你将会学习到:

    • LeakCanary检测内存泄露的原理
    • 使用ContentProvider进行三方库初始化的方法

    原理概述

    关于LeakCanary的原理,官网上已经给出了详细的解释。翻译过来就是:

    1. LeakCanary使用ObjectWatcher来监控Android的生命周期。当Activity和Fragment被destroy以后,这些引用被传给ObjectWatcher以WeakReference的形式引用着。如果gc完5秒钟以后这些引用还没有被清除掉,那就是内存泄露了。
    2. 当被泄露掉的对象达到一个阈值,LeakCanary就会把java的堆栈信息dump到.hprof文件中。
    3. LeakCanary用Shark库来解析.hprof文件,找到无法被清理的引用的引用栈,然后再根据对Android系统的知识来判定是哪个实例导致的泄露。
    4. 通过泄露信息,LeakCanary会将一条完整的引用链缩减到一个小的引用链,其余的因为这个小的引用链导致的泄露链都会被聚合在一起。

    通过官网的介绍,我们很容易就抓住了学习LeakCanary这个库的重点:

    • LeakCanary是如何使用ObjectWatcher 监控生命周期的?
    • LeakCanary如何dump和分析.hprof文件的?

    看官方原理总是感觉不过瘾,下面我们从代码层面上来分析。本文基于LeakCanary 2.0 beta版。

    基本使用

    LeakCanary的使用相当的简单。只需要在module的build.gradle添加一行依赖,代码侵入少。

    dependencies {
      // debugImplementation because LeakCanary should only run in debug builds.
      debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.0-beta-3'
    }
    

    就这样,应用非常简单就接入了LeakCanary内存检测功能。当然还有一些更高级的用法,比如更改自定义config,增加监控项等,大家可以参考官网

    源码分析

    1. 初始化

    和之前的1.x版本相比,2.0甚至都不需要再在Application里面增加install的代码。可能很多的同学都会疑惑,LeakCanary是如何插入自己的初始化代码的呢? 其实这里LeakCanary是使用了ContentProvider来进行初始化。我之前在介绍Android插件化系列三:技术流派和四大组件支持的时候曾经介绍过ContentProvider的特点,即在打包的过程中来自不同module的ContentProvider最后都会merge到一个文件中,启动app的时候ContentProvider是自动安装,并且安装会比Application的onCreate还早。LeakCanary就是依据这个原理进行的设计。具体可以参考【译】你的Android库是否还在Application中初始化?

    我们可以查看LeakCanary源码,发现它在leakcanary-object-watcher-android的AndroidManifest.xml中有一个ContentProvider。

    <provider
            android:name="leakcanary.internal.AppWatcherInstaller$MainProcess"
            android:authorities="${applicationId}.leakcanary-installer"
            android:exported="false"/>
    

    然后我们查看AppWatcherInstaller的代码,发现内部是使用InternalAppWatcher进行的install。

      // AppWatcherInstaller
    override fun onCreate(): Boolean {
        val application = context!!.applicationContext as Application
        InternalAppWatcher.install(application)
        return true
    }
    
      // InternalAppWatcher
    fun install(application: Application) {
        // 省略部分代码
        checkMainThread()
        if (this::application.isInitialized) {
          return
        }
        InternalAppWatcher.application = application
    
        val configProvider = { AppWatcher.config }
        ActivityDestroyWatcher.install(application, objectWatcher, configProvider)
        FragmentDestroyWatcher.install(application, objectWatcher, configProvider)
        onAppWatcherInstalled(application)
    }
    

    可以看到这里主要把Activity和Fragment区分了开来,然后分别进行注册。Activity的生命周期监听是借助于Application.ActivityLifecycleCallbacks。

    private val lifecycleCallbacks =
        object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
          override fun onActivityDestroyed(activity: Activity) {
            if (configProvider().watchActivities) {
              objectWatcher.watch(activity)
            }
          }
        }
    
    application.registerActivityLifecycleCallbacks(activityDestroyWatcher.lifecycleCallbacks)
    

    而Fragment的生命周期监听是借助了Activity的ActivityLifecycleCallbacks生命周期回调,当Activity创建的时候去调用FragmentManager.registerFragmentLifecycleCallbacks方法注册Fragment的生命周期监听。

        override fun onFragmentViewDestroyed(
          fm: FragmentManager,
          fragment: Fragment
        ) {
          val view = fragment.view
          if (view != null && configProvider().watchFragmentViews) {
            objectWatcher.watch(view)
          }
        }
    
        override fun onFragmentDestroyed(
          fm: FragmentManager,
          fragment: Fragment
        ) {
          if (configProvider().watchFragments) {
            objectWatcher.watch(fragment)
          }
        }
      }
    

    最终,Activity和Fragment都将自己的引用传入了ObjectWatcher.watch()进行监控。从这里开始进入到LeakCanary的引用监测逻辑。

    题外话:LeakCanary 2.0版本和1.0版本相比,增加了Fragment的生命周期监听,每个类的职责也更加清晰。但是我个人觉得使用
    (Activty)->Unit
    这种lambda表达式作为类的写法不是很优雅,倒不如面向接口编程。完全可以设计成ActivityWatcher和FragmentWatcher都继承自某个接口,这样也方便后续扩展。

    2. 引用监控

    2.1 引用和GC

    1. 引用
      首先我们先介绍一点准备知识。大家都知道,java中存在四种引用:
    • 强引用:垃圾回收器绝不会回收它,当内存空间不足,Java虚拟机宁愿抛出OOM
    • 软引用:只有在内存不足的时候JVM才会回收仅有软引用指向的对象所占的空间
    • 弱引用:当JVM进行垃圾回收时,无论内存是否充足,都会回收仅被弱引用关联的对象。
    • 虚引用:和没有任何引用一样,在任何时候都可能被垃圾回收。

    一个对象在被gc的时候,如果发现还有软引用(或弱引用,或虚引用)指向它,就会在回收对象之前,把这个引用加入到与之关联的引用队列(ReferenceQueue)中去。如果一个软引用(或弱引用,或虚引用)对象本身在引用队列中,就说明该引用对象所指向的对象被回收了。

    当软引用(或弱引用,或虚引用)对象所指向的对象被回收了,那么这个引用对象本身就没有价值了,如果程序中存在大量的这类对象(注意,我们创建的软引用、弱引用、虚引用对象本身是个强引用,不会自动被gc回收),就会浪费内存。因此我们这就可以手动回收位于引用队列中的引用对象本身。

    比如我们经常看到这种用法

    WeakReference<ArrayList> weakReference = new WeakReference<ArrayList>(list);
    

    还有也有这样一种用法

    WeakReference<ArrayList> weakReference = new WeakReference<ArrayList>(list, new ReferenceQueue<WeakReference<ArrayList>>());
    

    这样就可以把对象和ReferenceQueue关联起来,进行对象是否gc的判断了。另外我们从弱引用的特征中看到,弱引用是不会影响到这个对象是否被gc的,很适合用来监控对象的gc情况。
    2. GC
    java中有两种手动调用GC的方式。

    System.gc();
    // 或者
    Runtime.getRuntime().gc();
    

    2.2 监控

    我们在第一节中提到,Activity和Fragment都依赖于响应的LifecycleCallback来回调销毁信息,然后调用了ObjectWatcher.watch添加了销毁后的监控。接下来我们看

    ObjectWatcher.watch做了什么操作。
      @Synchronized fun watch(
        watchedObject: Any,
        name: String
      ) {
        removeWeaklyReachableObjects()
        val key = UUID.randomUUID().toString()
        val watchUptimeMillis = clock.uptimeMillis()
        val reference =
          KeyedWeakReference(watchedObject, key, name, watchUptimeMillis, queue)
        watchedObjects[key] = reference
        checkRetainedExecutor.execute {
          moveToRetained(key)
        }
      }
    
      private fun removeWeaklyReachableObjects() {
        // WeakReferences are enqueued as soon as the object to which they point to becomes weakly
        // reachable. This is before finalization or garbage collection has actually happened.
        var ref: KeyedWeakReference?
        do {
          ref = queue.poll() as KeyedWeakReference?
          if (ref != null) {
            watchedObjects.remove(ref.key)
          }
        } while (ref != null)
      }
    
      @Synchronized private fun moveToRetained(key: String) {
        removeWeaklyReachableObjects()
        val retainedRef = watchedObjects[key]
        if (retainedRef != null) {
          retainedRef.retainedUptimeMillis = clock.uptimeMillis()
          onObjectRetainedListeners.forEach { it.onObjectRetained() }
        }
      }
    

    这里我们看到,有一个存储着KeyedWeakReference的ReferenceQueue对象。在每次增加watch object的时候,都会去把已经处于ReferenceQueue中的对象给从监控对象的map即watchObjects中清理掉,因为这些对象都已经被回收了。然后再去生成一个KeyedWeakReference,这个对象就是一个持有了key和监测开始时间的WeakReference对象。最后再去调用moveToRetained,相当于记录和回调给监控方这个对象正式开始监测的时间。

    那么我们现在已经拿到了需要监控的对象了,但是又是怎么去判断这个对象已经内存泄露的呢?这就要继续往下面看。我们主要到前面在讲解InternalAppWatcher的install方法的时候,除了install了Activity和Fragment的检测器,还调用了onAppWatcherInstalled(application)方法,看代码发现这个方法就是InternalLeakCanary的invoke方法。

      override fun invoke(application: Application) {
        this.application = application
    
        AppWatcher.objectWatcher.addOnObjectRetainedListener(this)
    
        val heapDumper = AndroidHeapDumper(application, leakDirectoryProvider)
    
        val gcTrigger = GcTrigger.Default
    
        val configProvider = { LeakCanary.config }
    
        val handlerThread = HandlerThread(LEAK_CANARY_THREAD_NAME)
        handlerThread.start()
        val backgroundHandler = Handler(handlerThread.looper)
    
        heapDumpTrigger = HeapDumpTrigger(
            application, backgroundHandler, AppWatcher.objectWatcher, gcTrigger, heapDumper,
            configProvider
        )
      }
    
      override fun onObjectRetained() {
        if (this::heapDumpTrigger.isInitialized) {
          heapDumpTrigger.onObjectRetained()
        }
      }
    

    我们看到首先是初始化了heapDumper,gcTrigger,heapDumpTrigger等对象用于gc和heapDump,同时还实现了OnObjectRetainedListener,并把自己添加到了上面的onObjectRetainedListeners中,以便每个对象moveToRetained的时候,InternalLeakCanary都能获取到onObjectRetained()的回调,回调里就只是回调了heapDumpTrigger.onObjectRetained()方法。看来都是依赖于HeapDumpTrigger这个类。
    HeapDumpTrigger主要的处理逻辑都在checkRetainedObjects方法中。

      private fun checkRetainedObjects(reason: String) {
        val config = configProvider()
        var retainedReferenceCount = objectWatcher.retainedObjectCount
    
        if (retainedReferenceCount > 0) {
          gcTrigger.runGc()  // 触发一次GC操作,只保留不能被回收的对象
          retainedReferenceCount = objectWatcher.retainedObjectCount
        }
    
        if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return
    
        if (!config.dumpHeapWhenDebugging && DebuggerControl.isDebuggerAttached) {
          showRetainedCountWithDebuggerAttached(retainedReferenceCount)
          scheduleRetainedObjectCheck("debugger was attached", WAIT_FOR_DEBUG_MILLIS)
          return
        }
    
        val heapDumpUptimeMillis = SystemClock.uptimeMillis()
        KeyedWeakReference.heapDumpUptimeMillis = heapDumpUptimeMillis
        dismissRetainedCountNotification()
        val heapDumpFile = heapDumper.dumpHeap()
        if (heapDumpFile == null) {
          scheduleRetainedObjectCheck("failed to dump heap", WAIT_AFTER_DUMP_FAILED_MILLIS)
          showRetainedCountWithHeapDumpFailed(retainedReferenceCount)
          return
        }
        lastDisplayedRetainedObjectCount = 0
        objectWatcher.clearObjectsWatchedBefore(heapDumpUptimeMillis)
    
        HeapAnalyzerService.runAnalysis(application, heapDumpFile)
      }
    

    那么HeapDumpTrigger具体做了些啥呢?我理了一下主要是下面几个功能:

    • 后台线程轮询当前还存活着的对象
    • 如果存活的对象大于0,那就触发一次GC操作,回收掉没有泄露的对象
    • GC完后,仍然存活着的对象数和预定的对象数相比较,如果多了就调用heapDumper.dumpHeap()方法把对象dump成文件,并交给HeapAnalyzerService去分析
    • 根据存活情况展示通知

    2.3 总结

    看到了这里,我们应该脑海中有概念了。Activity和Fragment通过注册系统的监听在onDestroy的时候把自己的引用放入ObjectWatcher进行监测,监测主要是通过HeapDumpTrigger类轮询进行,主要是调用AndroidHeapDumper来dump出文件来,然后依赖于HeapAnalyzerService来进行分析。后面一小节,我们将会聚焦于对象dump操作和HeapAnalyzerService的分析过程。

    3. dump对象及分析

    3.1 dump对象

    hprof是JDK提供的一种JVM TI Agent native工具。JVM TI,全拼是JVM Tool interface,是JVM提供的一套标准的C/C++编程接口,是实现Debugger、Profiler、Monitor、Thread Analyser等工具的统一基础,在主流Java虚拟机中都有实现。hprof工具事实上也是实现了这套接口,可以认为是一套简单的profiler agent工具。

    用过Android Studio Profiler工具的同学对hprof文件都不会陌生,当我们使用Memory Profiler工具的Dump Java heap图标的时候,profiler工具就会去捕获你的内存分配情况。但是捕获以后,只有在Memory Profiler正在运行的时候我们才能查看,那么我们要怎么样去保存当时的内存使用情况呢,又或者我想用别的工具来分析堆分配情况呢,这时候hprof文件就派上用场了。Android Studio可以把这些对象给export到hprof文件中去。

    LeakCanary也是使用的hprof文件进行对象存储。hprof文件比较简单,整体按照 前置信息 + 记录表的格式来组织的。但是记录的种类相当之多。具体种类可以查看HPROF Agent。

    同时,android中也提供了一个简便的方法Debug.dumpHprofData(filePath)可以把对象dump到指定路径下的hprof文件中。LeakCanary使用使用Shark库来解析Hprof文件中的各种record,比较高效,使用Shark中的HprofReader和HprofWriter来进行读写解析,获取我们需要的信息。大家可以关注一些比较重要的,比如:

    • Class Dump
    • Instance Dump
    • Object Array Dump
    • Primitive Array Dump
      dump具体的代码在AndroidHeapDumper类中。HprofReader和HprofWriter过于复杂,有兴趣的直接查看源码吧
      override fun dumpHeap(): File? {
        val heapDumpFile = leakDirectoryProvider.newHeapDumpFile() ?: return null
    
        return try {
          Debug.dumpHprofData(heapDumpFile.absolutePath)
          if (heapDumpFile.length() == 0L) {
            null
          } else {
            heapDumpFile
          }
        } catch (e: Exception) {
          null
        } finally {
        }
      }
    

    3.2 对象分析

    前面我们已经分析到了,HeapDumpTrigger主要是依赖于HeapAnalyzerService进行分析。那么这个HeapAnalyzerService究竟有什么玄机?让我们继续往下面看。可以看到HeapAnalyzerService其实是一个ForegroundService。在接收到分析的Intent后就会调用HeapAnalyzer的analyze方法。所以最终进行分析的地方就是HeapAnalyzer的analyze方法。
    核心代码如下

        try {
          listener.onAnalysisProgress(PARSING_HEAP_DUMP)
          Hprof.open(heapDumpFile)
              .use { hprof ->
               // 1.生成graph
                val graph = HprofHeapGraph.indexHprof(hprof, proguardMapping)
                // 2.寻找Leak
                val findLeakInput = FindLeakInput(
                    graph, leakFinders, referenceMatchers, computeRetainedHeapSize, objectInspectors
                )
                val (applicationLeaks, libraryLeaks) = findLeakInput.findLeaks()
                listener.onAnalysisProgress(REPORTING_HEAP_ANALYSIS)
                return HeapAnalysisSuccess(
                    heapDumpFile, System.currentTimeMillis(), since(analysisStartNanoTime),
                    applicationLeaks, libraryLeaks
                )
              }
        } catch (exception: Throwable) {
          listener.onAnalysisProgress(REPORTING_HEAP_ANALYSIS)
          return HeapAnalysisFailure(
              heapDumpFile, System.currentTimeMillis(), since(analysisStartNanoTime),
              HeapAnalysisException(exception)
          )
        }
      }
    

    这段代码中涉及到了专为LeakCanary设计的Shark库的用法,在这里就不多解释了。大概介绍一下每一步的作用:

    • 首先调用HprofHeapGraph.indexHprof方法,这个方法会把dump出来的各种实例instance,Class类对象和Array对象等都建立起查询的索引,以record的id作为key,把需要的信息都存储在Map中便于后续取用
    • 调用findLeakInput.findLeak方法,这个方法会从GC Root开始查询,找到最短的一条导致泄露的引用链,然后再根据这条引用链构建出LeakTrace。
    • 把查询出来的LeakTrace对外展示

    总结

    本篇文章分析了LeakCanary检测内存泄露的思路和一些代码的设计思想,但是限于篇幅不能面面俱到。接下来我们回答一下文章开头提出的问题。

    1. LeakCanary是如何使用ObjectWatcher 监控生命周期的?
      LeakCanary使用了Application的ActivityLifecycleCallbacks和FragmentManager的FragmentLifecycleCallbacks方法进行Activity和Fragment的生命周期检测,当Activity和Fragment被回调onDestroy以后就会被ObjectWatcher生成KeyedReference来检测,然后借助HeapDumpTrigger的轮询和触发gc的操作找到弹出提醒的时机。
    2. LeakCanary如何dump和分析.hprof文件的?
      使用Android平台自带的Debug.dumpHprofData方法获取到hprof文件,使用自建的Shark库进行解析,获取到LeakTrace

    参考

    展开全文
  • 1.LeakCanary 如何自动初始化 LeakCanary只需添加依赖就可以实现自动初始化LeakCanary是通过ContentProvider实现初始化的,在ContentProvider 的 onCreate方法中初始化LeakCanary。并且...
  • 一般我们在 Application 的 onCreate 中进行初始化操作。此时推荐一个小技巧:借助 ContentProvider 实现完全"无侵"的初始化, 让 SDK 更加易用。 ContentProvider 初始化原理 ContentProvider 的 onCreate 的调用...
  • 最近因为国内上架应用,检测出firebase有违规收集android id行为,明明在代码中延迟初始化了,但firebase依然有操作数据的行为,跟踪发现firebase采用了ContentProvider来初始化sdk,怪不得通过代码延时初始化并没有...
  • LeakCanary

    2022-02-15 11:57:52
    一、LeakCanary 二、BlockCanary 三、KOOM
  • 还有些就仅仅是依据初始化: public class DemoApplication extends Application { @Override public void onCreate() { super.onCreate(); LeakCanary.install(this);//像这样的。 APICloud.initialize(this); //...
  • 那么有些操作需要在application中初始化的工作我们完全可以放到ContentProvider中处理,leakCanary2.0就是这么做的,把 init 的逻辑放到库内部,让调用方完全不需要在 Application 里去进行初始化了,十分方便。...
  • 最近小明开源了一个 Android 三方库,接入流程很简单,开发者们只需要在应用的 Application 的 onCreate() 方法中去初始化它,然后就可以调用相应的库 API 了 public class App extends Application { @Override ...
  • ContentProvider比其他组件都更早初始化,可以在这里拿到一些配置信息,例如屏幕的宽高信息,也可以在这里初始化部分框架,例如Leakcanary、bugly都是在这里进行初始化的; =======================================...
  • LeakCanary 2.7 原理分析

    千次阅读 2021-06-16 15:07:29
    我们直接引入依赖即可,无需代码初始化LeakCanary 会在 app 初始化时自动初始化自身。 debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7' Application->attachBaseContext =====> ...
  • 很多第三方框架或SDK都会有个初始化操作,比如在Application的onCreate中调用XXX.init(),最近学习了LeakCanary的做法,就不再需要手动初始化了,那么自动初始化该怎么做呢? 首先定义一个InitProvider,它是继承于...
  • 如何使用 第一步:添加依赖 debugImplementation '...所以,这个 ContentProvider 就被初始化了。 AndroidManifes.xml 的路径为 :leakcanary-object-watcher-android/src/main
  • 但是我们在使用的时候也能正常使用,通过挖掘其源码,可以看出来他们一般使用的ContentProvider来进行SDK的初始化的,目前使用ContentProvider的知名SDK有:ButterKnife、Leakcanary、BlockCanary...等等。 这里补充...
  • LeakCanary实现原理简析

    2021-01-11 21:13:59
    LeakCanary初始化1.1 在ContentProvider中初始化LeakCanary1.2 初始化默认观察器1.3 跟踪观察被销毁的对象2. 观察对象的存活状态3. 分析泄漏对象4. 其它 原理这两个字,会让人觉得太深奥,其实只是简单了解下。 ...
  • leakcanary1.6.2aar和jar

    2018-11-19 16:56:29
    然后在Application中添加初始化代码 ``` import com.squareup.leakcanary.LeakCanary; #oncreate中 if (LeakCanary.isInAnalyzerProcess(this)) { // This process is dedicated to LeakCanary for heap analysis...
  • LeakCanary2.7源码分析

    2021-10-27 16:01:24
    文章目录简介1、LeakCanary的启动及初始化源码查看1.1 leakcanary-object-watcher-android AndroidManifest.xml中定义contentProvider1.2 AppWatcherInstaller.kt 中onCreate()2、内存泄漏检测怎么实现的2.1 ...
  • 分析LeakCanary原理

    2020-10-09 19:52:11
    引入包 debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.0' debug只在项目运行中出现,如果想要添加检测其他对象或者查看源码 引入包 implementation '...
  • 省略代码 } Leakcanary初始化 AppWatcherInstaller.onCreate调用Leakcanary的初始化 /** * contentProvider 创建时开始初始化Leakcanary */ override fun onCreate(): Boolean { val application = context!...
  • Android内存泄漏&leakcanary2.7

    千次阅读 2021-12-29 14:07:09
    4.1 自动监测Activity等 4.1.1 leackcanary初始化leakcanary1.x中,需要开发者在代码中调用leakcanary初始化方法才能实现自动监测: pulib class MyApplication extends Application { @Override public void ...
  • LeakCanary 的集成过程很简单,首先在 build.gradle 文件中添加依赖: dependencies { debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.1' releaseImplementation '...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,252
精华内容 1,300
关键字:

leakcanary初始化