主頁 > 移動端開發 > Android開發最近新聞和功能都在這里了面試必備

Android開發最近新聞和功能都在這里了面試必備

2021-06-14 07:22:43 移動端開發

阿里面試

自我介紹

首先要介紹自己的專案經驗和個人的擅長點,因為面試官主要考察你的表達能力和語言精簡能力,

簡歷的撰寫其實這里可能需要注意幾點;
1.標題的直接按照姓名-幾年作業經驗-應聘崗位格式來填寫,例如 黃銘——四年作業經驗——Android開發高級工程師,因為要知道,HR篩選簡歷是非常快的,你要在第一時間就讓他看到他想要看的資訊,HR一開始最關心的就是作業年限和崗位,你直接在標題欄用最大的字體寫出來會比很多人介紹了半天專案最后寫年限要好得多,

2.個人資訊寫最前面, 個人資訊欄其實不用寫很多東西,最主要的就是姓名、聯系方式、性別、如果學校是名校那么就寫上,如果本科學的是計算機專業也可以在這里寫上,如果都不是就最好放到最后去寫,因為最前面的東西永遠是最先看到的內容,也是你最有競爭力和賣弄的資本,

####1.使用
LeakCancary 2.0使用,只需要配置如下代碼,便可以進行使用,比LeakCanary1.0不知道高到哪里去了~

debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.0-alpha-2'

####2.原始碼分析
閱讀原始碼后可以看到leakcancary-leaksentry模塊的Androidmanifest檔案,可以看到下面的內容:

<?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
    //利用系統自動呼叫ContentProvider的onCreate來進行安裝
    InternalLeakSentry.install(application)
    return true
  }
  ...

至于為什么系統會呼叫ContentProvider的onCreate方法,我們可以看看原始碼,在ActivityThread中的H中的handleMessage可以看到

public void handleMessage(Message msg) {
    if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
    switch (msg.what) {
        case BIND_APPLICATION:
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
            AppBindData data = (AppBindData)msg.obj;
            //關鍵方法
            handleBindApplication(data);
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            break;

然后在handleBindApplication中可以看到

// don't bring up providers in restricted mode; they may depend on the
// app's custom Application class
if (!data.restrictedBackupMode) {
    if (!ArrayUtils.isEmpty(data.providers)) {
        //contentprovider初始化,里面會呼叫onCreate方法
        installContentProviders(app, data.providers);
    }
}

// Do this after providers, since instrumentation tests generally start their
// test thread at this point, and we don't want that racing.
try {
    mInstrumentation.onCreate(data.instrumentationArgs);
}
catch (Exception e) {
    throw new RuntimeException(
        "Exception thrown in onCreate() of "
        + data.instrumentationName + ": " + e.toString(), e);
}
try {
    //app的onCreate方法呼叫
    mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {

具體呼叫contentprovider的onCreate代碼邏輯如下

@UnsupportedAppUsage
private void installContentProviders(
        Context context, List<ProviderInfo> providers) {
    final ArrayList<ContentProviderHolder> results = new ArrayList<>();

    for (ProviderInfo cpi : providers) {
        ···
        //installProvider方法
        ContentProviderHolder cph = installProvider(context, null, cpi,
                false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
        if (cph != null) {
            cph.noReleaseNeeded = true;
            results.add(cph);
        }
    }
  //installProvider方法,然后一步步跟進
  //1
  //XXX Need to create the correct context for this provider.
  localProvider.attachInfo(c, info);
  //2
	public void attachInfo(Context context, ProviderInfo info) {
        attachInfo(context, info, false);
   }
  //3
  private void attachInfo(Context context, ProviderInfo info, boolean testing) {
        mNoPerms = testing;
        mCallingPackage = new ThreadLocal<>();
        if (mContext == null) {
            ···
            ContentProvider.this.onCreate();
        }
    }

通過上面的分析,可以知道在我們引入依賴后,依賴包中的AndroidMainfest.xml檔案便會主動合并到主AndroidManifest.xml檔案中,然后在程式啟動程序中便會自動創建ContentProvider,然后進行InternalLeakSentry.install(application),接下來進行一些列的監控和dump操作等,

#####2.1 InternalLeakSentry.install(application)

下面來分析InternalLeakSentry.install(application)里面都做了一些什么,可以看到

fun install(application: Application) {
    CanaryLog.d("Installing LeakSentry")
    checkMainThread()
    if (this::application.isInitialized) {
      return
    }
    InternalLeakSentry.application = application

    val configProvider = { LeakSentry.config }
    // 1.監聽 Activity.onDestroy()
    ActivityDestroyWatcher.install(
        application, refWatcher, configProvider
    )
    // 2.監聽 Fragment.onDestroy()
    FragmentDestroyWatcher.install(
        application, refWatcher, configProvider
    )
    // 3.監聽完成后進行一些初始化作業
    listener.onLeakSentryInstalled(application)
  }

從命名上可以看到在Activity和Fragment進行destory的時候進行watch

  1. ActivityDestroyWatcher
internal class ActivityDestroyWatcher private constructor(
  private val refWatcher: RefWatcher,
  private val configProvider: () -> Config
) {

  private val lifecycleCallbacks = object : ActivityLifecycleCallbacksAdapter() {
    override fun onActivityDestroyed(activity: Activity) {
      if (configProvider().watchActivities) {
        // 監聽到 onDestroy() 之后,通過 refWatcher 監測 Activity
        refWatcher.watch(activity)
      }
    }
  }

  companion object {
    fun install(
      application: Application,
      refWatcher: RefWatcher,
      configProvider: () -> Config
    ) {
      val activityDestroyWatcher =
        ActivityDestroyWatcher(refWatcher, configProvider)
      application.registerActivityLifecycleCallbacks(activityDestroyWatcher.lifecycleCallbacks)
    }
  }
}
  1. FragmentDestroyWatcher
internal interface FragmentDestroyWatcher {

  fun watchFragments(activity: Activity)

  companion object {

    private const val SUPPORT_FRAGMENT_CLASS_NAME = "androidx.fragment.app.Fragment"

    fun install(
      application: Application,
      refWatcher: RefWatcher,
      configProvider: () -> LeakSentry.Config
    ) {
      val fragmentDestroyWatchers = mutableListOf<FragmentDestroyWatcher>()

      //大于等于android O  
      if (SDK_INT >= O) {
        fragmentDestroyWatchers.add(
            AndroidOFragmentDestroyWatcher(refWatcher, configProvider)
        )
      }

      if (classAvailable(
              SUPPORT_FRAGMENT_CLASS_NAME
          )
      ) {
        // androidx 使用 SupportFragmentDestroyWatcher
        fragmentDestroyWatchers.add(
            SupportFragmentDestroyWatcher(refWatcher, configProvider)
        )
      }

      if (fragmentDestroyWatchers.size == 0) {
        return
      }

      application.registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacksAdapter() {
        override fun onActivityCreated(
          activity: Activity,
          savedInstanceState: Bundle?
        ) {
          for (watcher in fragmentDestroyWatchers) {
            watcher.watchFragments(activity)
          }
        }
      })
    }

    private fun classAvailable(className: String): Boolean {
      return try {
        Class.forName(className)
        true
      } catch (e: ClassNotFoundException) {
        false
      }
    }
  }
}

Android O 及以后,androidx 都具備對 Fragment 生命周期的監聽功能,為什么不監聽Android O之前的呢???(待解決)
在版本為1.5.4之前是不支持Fragment記憶體泄漏監聽的,后面版本才加了進來,

  1. listener.onLeakSentryInstalled(application)

該listener的最終實作類是leakcanary-android-core中的InternalLeakCanary類

override fun onLeakSentryInstalled(application: Application) {
    this.application = application

    val heapDumper = AndroidHeapDumper(application, leakDirectoryProvider)
		//用于發現可能的記憶體泄漏之后手動呼叫 GC 確認是否真的為記憶體泄露
    val gcTrigger = GcTrigger.Default

    val configProvider = { LeakCanary.config }

    val handlerThread = HandlerThread(HeapDumpTrigger.LEAK_CANARY_THREAD_NAME)
    handlerThread.start()
    val backgroundHandler = Handler(handlerThread.looper)
		//用于確認記憶體泄漏之后進行 heap dump 作業,
    heapDumpTrigger = HeapDumpTrigger(
        application, backgroundHandler, LeakSentry.refWatcher, gcTrigger, heapDumper, configProvider
    )
    application.registerVisibilityListener { applicationVisible ->
      this.applicationVisible = applicationVisible
      heapDumpTrigger.onApplicationVisibilityChanged(applicationVisible)
    }
    addDynamicShortcut(application)
  }

這里有個關于GC回收的知識點,我們可以看看優秀的第三方框架都是怎么寫的

interface GcTrigger {
  fun runGc()
  object Default : GcTrigger {
    override fun runGc() {
      // Code taken from AOSP FinalizationTest:
      // https://android.googlesource.com/platform/libcore/+/master/support/src/test/java/libcore/
      // java/lang/ref/FinalizationTester.java
      // System.gc() does not garbage collect every time. Runtime.gc() is
      // more likely to perform a gc.
      Runtime.getRuntime()
          .gc()
      enqueueReferences()
      System.runFinalization()
    }
    private fun enqueueReferences() {
      // Hack. We don't have a programmatic way to wait for the reference queue daemon to move
      // references to the appropriate queues.
      try {
        Thread.sleep(100)
      } catch (e: InterruptedException) {
        throw AssertionError()
      }
    }
  }
}

可以看到,它使用了Runtime.getRuntime().gc()而不是System.gc(),進入System.gc原始碼一看

public static void gc() {
    boolean shouldRunGC;
    synchronized (LOCK) {
        shouldRunGC = justRanFinalization;
        if (shouldRunGC) {
            justRanFinalization = false;
        } else {
            runGC = true;
        }
    }
    if (shouldRunGC) {
        Runtime.getRuntime().gc();
    }
}

可以看到System.gc原始碼的還是最終實作是Runtime.getRuntime().gc();但是需要一系列的判斷條件,我們手動呼叫System.runFinalization()可以使gc方法中的justRanFinalizationw為true,從而保證Runtime.getRuntime().gc()會被執行,

####3.如何判斷物件可能泄露:ReferenceQueue含義及作用

在Activity/Fragment銷毀后,會進行一系列的物件回收,我們把這些物件分別和參考佇列進行關聯,當某個物件被回收時,**(弱參考一旦變成弱可達(可達性演算法分析),參考就會加到參考佇列中,然后再進行回收)**我們物件的參考就會被加入到參考佇列中,根據該原理進行一系列的操作,最終判斷是否記憶體泄漏,

#####3.1 參考佇列

通常我們將其ReferenceQueue翻譯為參考佇列,換言之就是存放參考的佇列,保存的是Reference物件,其作用在于Reference物件所參考的物件被GC回收時,該Reference物件將會被加入參考佇列中(ReferenceQueue)的佇列末尾,

ReferenceQueue常用的方法:

public Reference poll():從佇列中取出一個元素,佇列為空則回傳null;

public Reference remove():從佇列中出對一個元素,若沒有則阻塞至有可出隊元素;

public Reference remove(long timeout):從佇列中出對一個元素,若沒有則阻塞至有可出對元素或阻塞至超過timeout毫秒;

  1. 強參考

  2. 軟參考

  3. 弱參考

  4. 虛參考(Phantom Reference)

    虛引等同于沒有參考,這意味著在任何時候都可能被GC回收,設定虛參考的目的是為了被虛參考關聯的物件在被垃圾回收器回收時,能夠收到一個系統通知,(被用來跟蹤物件被GC回收的活動)虛參考和弱參考的區別在于:虛參考在使用時必須和參考佇列(ReferenceQueue)聯合使用,其在GC回收期間的活動如下:

    ReferenceQueue queue=new ReferenceQueue();

    PhantomReference pr=new PhantomReference(object,queue);

    也即是GC在回收一個物件時,如果發現該物件具有虛參考,那么在回收之前會首先該物件的虛參考加入到與之關聯的參考佇列中,程式可以通過判斷參考佇列中是否已經加入虛參考來了解被參考的物件是否被GC回收,

#####3.2 GC Root物件

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-23zFcSFz-1623419386976)(/Users/jackie/Library/Application Support/typora-user-images/image-20200723182524190.png)]

#####3.3 記憶體是否泄漏

知道參考佇列的原理后,先大概描述一下如何判斷是否泄漏,首先創建三個佇列

  /**
   * References passed to [watch] that haven't made it to [retainedReferences] yet.
   * watch() 方法傳進來的參考,尚未判定為泄露
   */
  private val watchedReferences = mutableMapOf<String, KeyedWeakReference>()
  /**
   * References passed to [watch] that we have determined to be retained longer than they should
   * have been.
   * watch() 方法傳進來的參考,已經被判定為泄露
   */
  private val retainedReferences = mutableMapOf<String, KeyedWeakReference>()
  private val queue = ReferenceQueue<Any>() // 參考佇列,配合弱參考使用
    
//KeyedWeakReference,物件和參考佇列進行弱參考關聯,所以這個物件一定會被回收    
class KeyedWeakReference(
  referent: Any,
  val key: String,
  val name: String,
  val watchUptimeMillis: Long,
  referenceQueue: ReferenceQueue<Any>
) : WeakReference<Any>(
    referent, referenceQueue
) {
  @Volatile
  var retainedUptimeMillis = -1L

  companion object {
    @Volatile
    @JvmStatic var heapDumpUptimeMillis = 0L
  }

}    

如果一個obj物件,它和佇列queue進行弱參考關聯,在進行垃圾收集時,發現該物件具有弱參考,會把參考加入到參考佇列中,我們如果在該佇列中拿到參考,則說明該物件被回收了,如果拿不到,則說明該物件還有強/軟參考未釋放,那么就說明物件還未回收,發生記憶體泄漏了,然后dump記憶體快照,使用第三方庫進行參考鏈分析

這里重點強調一點一個物件可能被多個參考持有,比如強參考,軟參考,弱參考,只要這個物件還有強參考/軟參考,與這個物件關聯的任意參考佇列就拿不到參考,參考佇列就相當于一個通知,多個參考佇列和一個物件關聯,物件被回收時,多個佇列都會受到通知

#####3.4 watch()

@Synchronized fun watch(
  watchedReference: Any,
  referenceName: String
) {
  if (!isEnabled()) {
    return
  }
  //移除佇列中將要被 GC 的參考
  removeWeaklyReachableReferences()
  val key = UUID.randomUUID()
      .toString()
  val watchUptimeMillis = clock.uptimeMillis()
  val reference = // 構建當前參考的弱參考物件,并關聯參考佇列 queue
    KeyedWeakReference(watchedReference, key, referenceName, watchUptimeMillis, queue)
  if (referenceName != "") {
    CanaryLog.d(
        "Watching instance of %s named %s with key %s", reference.className,
        referenceName, key
    )
  } else {
    CanaryLog.d(
        "Watching instance of %s with key %s", reference.className, key
    )
  }

  watchedReferences[key] = reference
  checkRetainedExecutor.execute {
    //如果參考未被移除,則可能存在記憶體泄漏
    moveToRetained(key)
  }
}

removeWeaklyReachableReferences()

  private fun removeWeaklyReachableReferences() {
    // 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.
    // 弱參考一旦變得弱可達,就會立即入隊,這將在 finalization 或者 GC 之前發生,
    var ref: KeyedWeakReference?
    do {
      ref = queue.poll() as KeyedWeakReference? // 佇列 queue 中的物件都是會被 GC 的
      if (ref != null) {
        val removedRef = watchedReferences.remove(ref.key)
        if (removedRef == null) {
          retainedReferences.remove(ref.key)
        }
        // 移除 watchedReferences 佇列中的會被 GC 的 ref 物件,剩下的就是可能泄露的物件
      }
    } while (ref != null)
  }

moveToRetained()

  @Synchronized private fun moveToRetained(key: String) {
    removeWeaklyReachableReferences() // 再次呼叫,防止遺漏
    val retainedRef = watchedReferences.remove(key)
    if (retainedRef != null) {
      retainedReferences[key] = retainedRef
      onReferenceRetained()
    }
  }

最后會回呼到InternalLeakCanary的onReferenceRetained()方法

override fun onReferenceRetained() {
  if (this::heapDumpTrigger.isInitialized) {
    heapDumpTrigger.onReferenceRetained()
  }
}

//1.HeapDumpTrigger 的 onReferenceRetained()
fun onReferenceRetained() {
  scheduleRetainedInstanceCheck("found new instance retained")
}

//2.scheduleRetainedInstanceCheck
private fun scheduleRetainedInstanceCheck(reason: String) {
  backgroundHandler.post {
    checkRetainedInstances(reason)
  }
}
  
//3.checkRetainedInstances
private fun checkRetainedInstances(reason: String) {
  CanaryLog.d("Checking retained instances because %s", reason)
    val config = configProvider()
    // A tick will be rescheduled when this is turned back on.
    if (!config.dumpHeap) {
      return
    }

  var retainedKeys = refWatcher.retainedKeys
		//當前泄露實體個數小于 5 個,不進行 heap dump
    if (checkRetainedCount(retainedKeys, config.retainedVisibleThreshold)) return

      if (!config.dumpHeapWhenDebugging && DebuggerControl.isDebuggerAttached) {
        showRetainedCountWithDebuggerAttached(retainedKeys.size)
          scheduleRetainedInstanceCheck("debugger was attached", WAIT_FOR_DEBUG_MILLIS)
          CanaryLog.d(
          "Not checking for leaks while the debugger is attached, will retry in %d ms",
          WAIT_FOR_DEBUG_MILLIS
        )
          return
      }
		// 可能存在被觀察的參考將要變得弱可達,但是還未入隊參考佇列,
    // 這時候應該主動呼叫一次 GC,可能可以避免一次 heap dump
  gcTrigger.runGc()

    retainedKeys = refWatcher.retainedKeys

    if (checkRetainedCount(retainedKeys, config.retainedVisibleThreshold)) return

      HeapDumpMemoryStore.setRetainedKeysForHeapDump(retainedKeys)

      CanaryLog.d("Found %d retained references, dumping the heap", retainedKeys.size)
      HeapDumpMemoryStore.heapDumpUptimeMillis = SystemClock.uptimeMillis()
      dismissNotification()
      val heapDumpFile = heapDumper.dumpHeap()
      if (heapDumpFile == null) {
        CanaryLog.d("Failed to dump heap, will retry in %d ms", WAIT_AFTER_DUMP_FAILED_MILLIS)
          scheduleRetainedInstanceCheck("failed to dump heap", WAIT_AFTER_DUMP_FAILED_MILLIS)
          showRetainedCountWithHeapDumpFailed(retainedKeys.size)
          return
      }

  refWatcher.removeRetainedKeys(retainedKeys)

    HeapAnalyzerService.runAnalysis(application, heapDumpFile)
}

一些細節可以看看代碼注釋,checkRetainedCount滿足個數的話,就要發起head dump,具體的邏輯在AndroidHeapDumper.dumpHeap()中:

  override fun dumpHeap(): File? {
    val heapDumpFile = leakDirectoryProvider.newHeapDumpFile() ?: return null
		···
    return try {
     //Dump出檔案
      Debug.dumpHprofData(heapDumpFile.absolutePath)
      heapDumpFile
    } catch (e: Exception) {
      CanaryLog.d(e, "Could not dump heap")
      // Abort heap dump
      null
    } finally {
      cancelToast(toast)
      notificationManager.cancel(R.id.leak_canary_notification_dumping_heap)
    }
  }

最后啟動一個前臺服務 HeapAnalyzerService 來分析 heap dump 檔案,老版本中是使用 Square 自己的 haha 庫來決議的,這個庫已經廢棄了,Square 完全重寫了決議庫,主要邏輯都在 moudle leakcanary-analyzer 中,這部分我還沒有閱讀,就不在這里分析了,對于新的決議器,官網是這樣介紹的:

Uses 90% less memory and 6 times faster than the prior heap parser.

減少了 90% 的記憶體占用,而且比原來快了 6 倍,后面有時間單獨來分析一下這個決議庫,

后面的程序就不再贅述了,通過決議庫找到最短 GC Roots 參考路徑,然后展示給用戶,

4.手動寫記憶體泄漏檢測

下面是參考Zero的Demo寫的記憶體泄漏檢測的一個例子,思路和LeakCanary一樣

fun main() {

    class MyKeyedWeakReference(
            referent: Any,
            val key: String,
            val name: String,
            referenceQueue: ReferenceQueue<Any>
    ) : WeakReference<Any>(
            referent, referenceQueue
    ) {
        val className: String = referent.javaClass.name
        override fun toString(): String {
            return "{key=$key,className=$className}"
        }
    }
    //需要觀察的物件
    val watchedReferences = mutableMapOf<String,MyKeyedWeakReference>()
    //如果最后retainedReferences還存在參考,說明泄漏了
    val retainedReferences = mutableMapOf<String,MyKeyedWeakReference>()
    //當與之關聯的弱參考中的實體被回收,則會加入到queue
    val gcQueue = ReferenceQueue<Any>()

    fun sleep(mills: Long){
        try {
            Thread.sleep(mills)
        }catch (e: Exception){
            e.printStackTrace()
        }
    }

    fun gc(){
        println("執行gc...")
        Runtime.getRuntime().gc()
        sleep(100)
        System.runFinalization()
    }

    fun removeWeaklyReachableReferences(){
        println("removeWeaklyReachableReferences")
        var ref: MyKeyedWeakReference?
        do {
            ref = gcQueue.poll() as MyKeyedWeakReference? //佇列queue中的物件都是會被GC的
            println("ref=$ref,如果ref為null,說明物件還有強參考")
            if (ref != null){ //說明被釋放了
                println("ref=$ref, 物件被釋放了,key=${ref.key}")
                val removedRef = watchedReferences.remove(ref.key)
                println("removedRef=$removedRef, 如果removedRef為null,說明已經不在watchedReferences了,key=${ref.key}")
                if (removedRef == null){
                    //不在watchedReferences則說明在retainedReferences
                    retainedReferences.remove(ref.key)
                }
            }
        }while (ref != null)
    }

    @Synchronized
    fun moveToRetained(key: String){
        println("5.moveToRetained,key=$key")
        removeWeaklyReachableReferences()
        val retainedRef = watchedReferences.remove(key)
        println("retainedRef =$retainedRef 如果還有值說明沒有被釋放")
        if (retainedRef != null){ //添加到retainedReferences
            retainedReferences[key] = retainedRef
        }

    }

    fun watch(
            obj: Any,
            referenceName: String = ""){
        println("2.watch...")
        removeWeaklyReachableReferences()
        val key = UUID.randomUUID().toString()
        println("3.key=$key")
        val reference = MyKeyedWeakReference(obj,key,referenceName,gcQueue)
        println("4.reference=$reference")
        //加入觀察串列
        watchedReferences[key] = reference
        //過段時間查看是否釋放
        thread(start = true){
            sleep(5000)
            moveToRetained(key)
        }

    }

    var obj : Any? = Object()
    println("1.創建一個物件obj=$obj")
    watch(obj!!,"")
    sleep(2000)
    obj = null
    if (obj == null){
        println("obj=$obj 釋放了")
    }
    gc()
    sleep(5000)
    println("watchedReferences=$watchedReferences")
    println("retainedReferences=$retainedReferences")
    println("執行完畢")

5. ContentProvider的優化

5.1 Content的初始化順序

? 通過ContentProvider來進行初始化確實能給使用者帶來便利,但是會影響啟動速度,如果有多個ContentProvider,如何控制這些ContentProvider初始化的順序呢,可以參考下面這篇文章https://sivanliu.github.io/2017/12/16/provider%E5%88%9D%E5%A7%8B%E5%8C%96/,如果一些第三方值只提供ContentProvider的初始化方式,我們又不想影響我們APP的啟動時間,該如何處理呢?

#####5.2

? 如果一些第三方庫只提供ContentProvider的初始化方式,我們又不想影響我們APP的啟動時間,該如何處理呢?我們可以使用AOP方式進行插樁,通過Gradle+Transform+ASM進行修改ContentProvider的onCreate方法,提前回傳,然后手動去呼叫初始化代碼,如果這些初始化代碼是私有的或者只限制包內使用的,也可以通過ASM去修改訪問權限,然后在我們想初始化的地方再進行初始化,這可能涉及到一個先后的問題,需要先修改完然后再在某個地方初始化,這里只是提供一個思路,如果一個庫初始化耗時很長,又在ContentProvider中進行初始化,ContentProvider中初始化的代碼又臭又長,又沒有提供其他初始化方法,這樣的垃圾庫你要它干嘛!

####6.總結

  1. 利用ContentProvider自動初始化,無需用戶手動初始化

  2. GC回收,參考佇列

  3. 1.5.4之后支持fragment,支持androidx

  4. 當泄露參考到達 5 個時才會發起 heap dump

  5. 全新的 heap parser,減少 90% 記憶體占用,提升 6 倍速度

  6. ContentProvider的優劣,以及優化方案

參考文章

https://sivanliu.github.io/2017/12/16/provider%E5%88%9D%E5%A7%8B%E5%8C%96/

https://juejin.im/post/5d1225546fb9a07ecd3d6b71

最后相關架構及資料領取方式:

點擊我的騰訊檔案免費領取獲取往期Android高級架構資料、原始碼、筆記、視頻,高級UI、性能優化、架構師課程、NDK、混合式開發(ReactNative+Weex)微信小程式、Flutter全方面的Android進階實踐技術,群內還有技術大牛一起討論交流解決問題,

然后再在某個地方初始化,這里只是提供一個思路,如果一個庫初始化耗時很長,又在ContentProvider中進行初始化,ContentProvider中初始化的代碼又臭又長,又沒有提供其他初始化方法,這樣的垃圾庫你要它干嘛!

####6.總結

  1. 利用ContentProvider自動初始化,無需用戶手動初始化

  2. GC回收,參考佇列

  3. 1.5.4之后支持fragment,支持androidx

  4. 當泄露參考到達 5 個時才會發起 heap dump

  5. 全新的 heap parser,減少 90% 記憶體占用,提升 6 倍速度

  6. ContentProvider的優劣,以及優化方案

參考文章

https://sivanliu.github.io/2017/12/16/provider%E5%88%9D%E5%A7%8B%E5%8C%96/

https://juejin.im/post/5d1225546fb9a07ecd3d6b71

最后相關架構及資料領取方式:

點擊我的騰訊檔案免費領取獲取往期Android高級架構資料、原始碼、筆記、視頻,高級UI、性能優化、架構師課程、NDK、混合式開發(ReactNative+Weex)微信小程式、Flutter全方面的Android進階實踐技術,群內還有技術大牛一起討論交流解決問題,

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/287171.html

標籤:其他

上一篇:2020年最全Android面試攻略好文推薦

下一篇:Activity常見問題

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more