Android第三方庫核心原理

JsBridge

Bridge基本原理:

Js通知Native

1)API注入。通過webview.addJavaInterface()的方法實現
2)攔截Js的alert/confirm/prompt/console等事件。由于prompt事件在js中很少使用,所以一般是攔截該事件。這些事件在WebChromeClient都有對應的方法回調(onConsoleMessage,onJsPrompt,onJsAlert,onJsConfirm)
3)url跳轉攔截,對應WebViewClient的shouldOverrideUrlLoading()方法。
第一種方法,由于webview在4.2以下的安全問題,所以有版本兼容問題。后兩種方法原理本質上是一樣的,都是通過對webview信息冒泡傳遞的攔截,通過定制協議-攔截協議-解析方法名參數-執行方法-回調。

Native通知Js

webView.loadUrl();
webView.evaluateJavascript()
webview可以通過loadUrl()的方法直接調用。在4.4以上還可以通過evaluateJava()方法獲取js方法的返回值。
4.4以前,如果想獲取方法的返回值,就需要通過上面的對webview信息冒泡傳遞攔截的方式來實現。

https://github.com/lzyzsd/JsBridge源碼分析

參考:[https://my.oschina.net/JiangTun/blog/1612700

在 Js 和 WebView 交互的過程中,主要實現兩個方向可以通信即可。
WebView 向 Js 傳遞數據是通過 WebView.loadUrl(String url) 實現的。
在 WebView 中接收 Js 傳遞的數據是通過 WebViewClient 中的 shouldOverrideUrlLoading(WebView view, String url) 攔截加載鏈接 String url 參數實現的。
重點:修改iframe的src會調用webview的shouldOverrideUrlLoading方法

js調用java

1)創建一個隱藏的messagingIframe,.更換iFrame的src,觸發BridgeWebViewClient的shouldOverrideUrlLoading方法。更換src,前綴為yy://QUEUE_MESSAGE/
2)創建sendMessageQueue數組,并為每個消息設置一個唯一的id并push到數組中
3)匹配到shouldOverrideUrlLoading,進入到BridgeWebView的flushMessageQueue方法
4)flushMessageQueue通過主要調用到了loadUrl方法
5)出發js的_fetchQueue方法,并再次更換iFrame的src協議頭,觸發shouldOverrideUrlLoading方法。

Retrofit2

參考:https://juejin.im/post/5c0fbcf6518825642650b875

image

image

核心原理:
通過動態代理返回具體的接口實例對象,調用接口方法時會調用到InvocationHandler的invoke方法。
然后根據每個方法的注解先解析出注解信息然后將注解信息進行封裝成okhttp所需要的網絡請求信息。


image

Okhttp3

整體流程圖


image

攔截器重試機制


image

默認攔截器

1)RetryAndFollowUpInterceptor


image

2)CacheInterceptor

3)ConnectInterceptor
image

4)CallServerInterceptor

OKHttp最終把拿到網絡請求連接給到CallServerInterceptor攔截器進行網絡請求和服務器通信

image.png
Response getResponseWithInterceptorChain() throws IOException {
    List<Interceptor> interceptors = new ArrayList<>();
    //添加開發者應用層自定義的Interceptor
    interceptors.addAll(client.interceptors());
    //這個Interceptor是處理請求失敗的重試,重定向
    interceptors.add(retryAndFollowUpInterceptor);
    //這個Interceptor工作是添加一些請求的頭部或其他信息
    //并對返回的Response做一些友好的處理(有一些信息你可能并不需要)
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    //這個Interceptor的職責是判斷緩存是否存在,讀取緩存,更新緩存等等
    interceptors.add(new CacheInterceptor(client.internalCache()));
    //這個Interceptor的職責是建立客戶端和服務器的連接
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
        //添加開發者自定義的網絡層攔截器
      interceptors.addAll(client.networkInterceptors());
    }
    //這個Interceptor的職責是向服務器發送數據,
    //并且接收服務器返回的Response
    interceptors.add(new CallServerInterceptor(forWebSocket));

    //一個包裹這request的chain
    Interceptor.Chain chain = new RealInterceptorChain(
        interceptors, null, null, null, 0, originalRequest);
    //把chain傳遞到第一個Interceptor手中
    return chain.proceed(originalRequest);
  }

ConnectionPool

在它內部持有一個線程池和一個緩存連接的雙向列表,連接中最多只能存在5個空閑連接,空閑連接最多只能存活5分鐘,空閑連接到期之后定時清理。


image

重點

OkHttp3的最底層是Socket,而不是URLConnection,它通過Platform的Class.forName()反射獲得當前Runtime使用的socket庫,利用Okio生產source和sink。

private void connectSocket(int connectTimeout, int readTimeout) throws IOException {
    Proxy proxy = route.proxy();
    Address address = route.address();

    rawSocket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP
        ? address.socketFactory().createSocket()
        : new Socket(proxy);

    rawSocket.setSoTimeout(readTimeout);
    ...
      //用Okio生產
      source = Okio.buffer(Okio.source(rawSocket));
      sink = Okio.buffer(Okio.sink(rawSocket));
    ...
  }

參考:
使用Socket進行HTTP請求與報文講解

OKHttp源碼解析(九):OKHTTP連接中三個"核心"RealConnection、ConnectionPool、StreamAllocation

Okio的source是socket.inputStream,sink是socket.outputStream。
所以,真正在傳輸數據時,就是用Okio的sink去傳socket,用source去取socket,底層其實也是socket操作。

Okhttp3的其他特性

1.返回數據閱后即焚
在OkHttp中,如果要攔截ResponseBody的數據內容(比如寫日志),會發現該數據讀過一次就會被情況,相當于是“閱后即焚”:

  //ResponseBody源碼
  public final String string() throws IOException { //底層不能自己消化異常,應該向上層拋出異常
    BufferedSource source = source();
    try {
      Charset charset = Util.bomAwareCharset(source, charset());
      return source.readString(charset);
    //不做catch,異常全部拋出給上層
    } finally { //確保原始字節數據得到處理
      Util.closeQuietly(source); //閱后即焚,這樣可以迅速騰出內存空間來
    }
  }

如果一定要攔截出數據內容,我們就不能直接讀ResponseBody中的source,需要copy一個副本才行:

BufferedSource sr = response.body().source();
sr.request(Long.MAX_VALUE);
Buffer buf = sr.buffer().clone();//copy副本讀取,不能讀取原文
String content = buf.readString(Charset.forName("UTF-8"));
buf.clear();

Response也提供了專門獲取ResponsBody數據的函數peekBody,實現原理也是copy:

//Response源碼
  public ResponseBody peekBody(long byteCount) throws IOException {
    BufferedSource source = body.source();
    source.request(byteCount);
    Buffer copy = source.buffer().clone();
    ...
    return ResponseBody.create(body.contentType(), result.size(), result);
  }

Okio

參考:
https://blog.piasy.com/2016/08/04/Understand-Okio/index.html
https://juejin.im/post/5856680c8e450a006c6474bd

image.png

Retrofit,OkHttp,Okio 進行網絡 IO 的流程圖


image.png

Demo

  // 寫入數據
 String fileName="test.txt";
        String path= Environment.getExternalStorageDirectory().getPath();
        File file=null;
        BufferedSink bufferSink=null;
        try{
            file=new File(path,fileName);
            if (!file.exists()){
                file.createNewFile();
            }
            bufferSink=Okio.buffer(Okio.sink(file));
            bufferSink.writeString("this is some thing import \n", Charset.forName("utf-8"));
            bufferSink.writeString("this is also some thing import \n", Charset.forName("utf-8"));
            bufferSink.close();
        }catch(Exception e){
          
        }


    //讀取數據
     try {
            BufferedSource bufferedSource=Okio.buffer(Okio.source(file));
            String str=bufferedSource.readByteString().string(Charset.forName("utf-8"));
            Log.e("TAG","--->"+str);
        } catch (Exception e) {
            e.printStackTrace();
        }

RecycleView&Listview

參考:
Android ListView 與 RecyclerView 對比淺析

緩存差異:

ListView(兩級緩存):


image

RecyclerView(四級緩存)


image

1)RecyclerView從mCacheViews(屏幕外)獲取緩存時,是通過匹配pos獲取目標位置的緩存,這樣做的好處是,當數據源數據不變的情況下,無須重新bindView,直接返回holder。
而同樣是離屏緩存,ListView從mScrapViews根據pos獲取相應的緩存,但是并沒有直接使用,而是重新getView(即必定會重新bindView)
      //AbsListView源碼
      //通過匹配pos從mScrapView中獲取緩存
        final View scrapView = mRecycler.getScrapView(position);
        //無論是否成功都直接調用getView,導致必定會調用createView
        final View child = mAdapter.getView(position, scrapView, this);
        if (scrapView != null) {
            if (child != scrapView) {
                mRecycler.addScrapView(scrapView, position);
            } else {
                ...
            }
        }

2)ListView中通過pos獲取的是view,即pos-->view;
RecyclerView中通過pos獲取的是viewholder,即pos --> (view,viewHolder,flag)引用;
從流程圖中可以看出,標志flag的作用是判斷view是否需要重新bindView,這也是RecyclerView實現局部刷新的一個核心

局部刷新

以RecyclerView中notifyItemRemoved(1)為例,最終會調用requestLayout(),使整個RecyclerView重新繪制,過程為:onMeasure()-->onLayout()-->onDraw()
其中,onLayout()為重點,分為三步:
dispathLayoutStep1():記錄RecyclerView刷新前列表項ItemView的各種信息,如Top,Left,Bottom,Right,用于動畫的相關計算;
dispathLayoutStep2():真正測量布局大小,位置,核心函數為layoutChildren();
dispathLayoutStep3():計算布局前后各個ItemView的狀態,如Remove,Add,Move,Update等,如有必要執行相應的動畫。
layoutChildren()流程圖:


image
image

當調用notifyItemRemoved時,會對屏幕內ItemView做預處理,修改ItemView相應的pos以及flag(流程圖中紅色部分):


image

當調用fill()中RecyclerView.getViewForPosition(pos)時,RecyclerView通過對pos和flag的預處理,使得bindview只調用一次.

需要指出,ListView和RecyclerView最大的區別在于數據源改變時的緩存的處理邏輯,ListView是"一鍋端",將所有的mActiveViews都移入了二級緩存mScrapViews,而RecyclerView則是更加靈活地對每個View修改標志位,區分是否重新bindView。

RecycleView源碼分析

參考:RecyclerView 源碼解析

image

RecyclerView的職責就是將Datas中的數據以一定的規則展示在它的上面,但說破天RecyclerView只是一個ViewGroup,它只認識View,不清楚Data數據的具體結構,所以兩個陌生人之間想構建通話,我們很容易想到 適配器模式 ,因此,RecyclerView需要一個Adapter(適配器模式)來與Datas進行交流

image.png

如上如所示,RecyclerView表示只會和ViewHolder進行接觸,而Adapter的工作就是將Data轉換為RecyclerView認識的ViewHolder,因此RecyclerView就間接地認識了Datas。
事情雖然進展愉快,但RecyclerView是個很懶惰的人,盡管Adapter已經將Datas轉換為RecyclerView所熟知的View,但RecyclerView并不想自己管理些子View,因此,它雇傭了一個叫做LayoutManager的大祭司來幫其完成布局(橋接模式),現在,圖示變成下面這樣

image.png

如上圖所示,LayoutManager協助RecyclerView來完成布局。但LayoutManager這個大祭司也有弱點,就是它只知道如何將一個一個的View布局在RecyclerView上,但它并不懂得如何管理這些View,如果大祭司肆無忌憚的玩弄View的話肯定會出事情,所以,必須有個管理View的護法,它就是Recycler,LayoutManager在需要View的時候回向護法進行索取,當LayoutManager不需要View(試圖滑出)的時候,就直接將廢棄的View丟給Recycler,圖示如下:

image.png

到了這里,有負責翻譯數據的Adapter,有負責布局的LayoutManager,有負責管理View的Recycler,一切都很完美,但RecyclerView乃何等神也,它下令說當子View變動的時候姿態要優雅(動畫),所以用雇傭了一個舞者ItemAnimator(觀察者模式),因此,舞者也進入了這個圖示:

image.png

自定義LayoutManager

參考:自定義LayoutManager實現最美應用列表
Android自定義LayoutManager第十一式之飛龍在天

效果:https://github.com/DingMouRen/LayoutManagerGroup
LayoutManager詳解及使用
LinearLayoutManager繼承RecycleView.LayoutManager。
LayoutManager#onLayoutChildren會調用到recycleView相應的操作view的方法。

ORMLite&GreenDao&Room

Android流行ORM框架性能對比及Room踩坑總結

Android Jetpack架構組件解析

帶你領略Android Jetpack組件的魅力

ViewModel

![image](https://upload-images.jianshu.io/upload_images/723726-21aa623cbf24485f?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

ViewModel生命周期
參考:ViewModel相關生命周期的原理分析

image
sdkversion<28

從Activity的生命周期狀態中觸發,調用FragmentController->FragmentManager從而將設置了setRetainInstance(true)的fragment的相關fragment保存起來;這樣config change時fragment不會執行到onDestroy; 對于HolderFragment而言,不會執行到其onDestory方法,ViewModelStore及其對應ViewModel均會得以保留

sdkversion>=28

ensureActivityConfiguration->relaunchActivityLocked-----binder call----------->handleRelaunchActivity (監聽到config變化,走relaunch邏輯,將mChangingConfigurations置為true,這樣Activity的ViewModelStore不會clear)
r.activity.mChangingConfigurations = true

總結

本問總結了ViewModel在config change時保持生命周期的相關原理;分為兩種情況:

1)before androidx support activity/fragment
利用無View的HolderFragment,使用setRetainInstance(true)保證其在config change時不被銷毀;這個思路值得我們借鑒
androidx support activity/fragment
2)在系統層進行了適配,無需HolderFragment接入了,其原理是config change正常執行銷毀工作,只不過在再次relaunch時還原;

Gson

參考:https://blog.csdn.net/chunqiuwei/article/category/5881669
gson有2種方式將json字符串轉化為成對應的Object
1)JsonParser
2)new Gson().from()

fastjson

參考:fastjson內幕
核心技術:
1、自行編寫類似StringBuilder的工具類SerializeWriter。
2、使用ThreadLocal來緩存buf
這個辦法能夠減少對象分配和gc,從而提升性能
3、使用asm避免反射
獲取java bean的屬性值,需要調用反射,fastjson引入了asm的來避免反射導致的開銷。fastjson內置的asm是基于objectweb asm 3.3.1改造的,只保留必要的部分,fastjson asm部分不到1000行代碼,引入了asm的同時不導致大小變大太多。
4、使用一個特殊的IdentityHashMap優化性能
5、缺省啟用sort field輸出
6、集成jdk實現的一些優化算法

react-nantive、weex、flutter

RxJava2

RxJava2.+創建流程源碼分析
RxJava2.+線程切換源碼分析

Glide

![image](https://upload-images.jianshu.io/upload_images/723726-98740656055cb823.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

參考:
Glide 架構設計藝術

image

基本用法:

Glide.with(this).load("xxx").circleCrop().into(ImageView(this))

Glide.with(this).load("xxx")返回RequestBuilder<Drawable>對象
其中RequestBuilder<Drawable>繼承BaseRequestOptions


image.png

BaseRequestOptions是用來保存配置信息的,即load()方法后面的各種操作符都是通過枚舉的方式保存在BaseRequestOptions的 private Options options = new Options();對象中。其中Options的內部是通過ArrayMap來實現的。


image.png

ok,基本上load()方法后面的所有操作符基本上都是用來保存配置信息的。真正的request構建和發起其實是發生在into()方法里的。
RequestBuilder#into
private <Y extends Target<TranscodeType>> Y into(
      @NonNull Y target,
      @Nullable RequestListener<TranscodeType> targetListener,
      BaseRequestOptions<?> options,
      Executor callbackExecutor) {
    Preconditions.checkNotNull(target);
    if (!isModelSet) {
      throw new IllegalArgumentException("You must call #load() before calling #into()");
    }
    //構建網絡請求對象
    Request request = buildRequest(target, targetListener, options, callbackExecutor);

    Request previous = target.getRequest();
    if (request.isEquivalentTo(previous)
        && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
      request.recycle();
      // If the request is completed, beginning again will ensure the result is re-delivered,
      // triggering RequestListeners and Targets. If the request is failed, beginning again will
      // restart the request, giving it another chance to complete. If the request is already
      // running, we can let it continue running without interruption.
      if (!Preconditions.checkNotNull(previous).isRunning()) {
        // Use the previous request rather than the new one to allow for optimizations like skipping
        // setting placeholders, tracking and un-tracking Targets, and obtaining View dimensions
        // that are done in the individual Request.
        previous.begin();
      }
      return target;
    }

    requestManager.clear(target);
    target.setRequest(request);
    //注意這是真正發去網絡請求的地方
    requestManager.track(target, request);

    return target;
  }

RequestManager#track

 synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
    targetTracker.track(target);
    requestTracker.runRequest(request);
  }

RequestTracker#runRequest
public void runRequest(@NonNull Request request) {
    requests.add(request);
    if (!isPaused) {
      request.begin();
    } else {
      request.clear();
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        Log.v(TAG, "Paused, delaying request");
      }
      pendingRequests.add(request);
    }
  }

request為SingleRequest

@Override
  public synchronized void begin() {
    assertNotCallingCallbacks();
    stateVerifier.throwIfRecycled();
    startTime = LogTime.getLogTime();
    if (model == null) {
      if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
        width = overrideWidth;
        height = overrideHeight;
      }
      // Only log at more verbose log levels if the user has set a fallback drawable, because
      // fallback Drawables indicate the user expects null models occasionally.
      int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG;
      //錯誤回調
      onLoadFailed(new GlideException("Received null model"), logLevel);
      return;
    }

    if (status == Status.RUNNING) {
      throw new IllegalArgumentException("Cannot restart a running request");
    }

    // If we're restarted after we're complete (usually via something like a notifyDataSetChanged
    // that starts an identical request into the same Target or View), we can simply use the
    // resource and size we retrieved the last time around and skip obtaining a new size, starting a
    // new load etc. This does mean that users who want to restart a load because they expect that
    // the view size has changed will need to explicitly clear the View or Target before starting
    // the new load.
    if (status == Status.COMPLETE) {
      onResourceReady(resource, DataSource.MEMORY_CACHE);
      return;
    }

    // Restarts for requests that are neither complete nor running can be treated as new requests
    // and can run again from the beginning.

    status = Status.WAITING_FOR_SIZE;
    if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
      //重點方法
      onSizeReady(overrideWidth, overrideHeight);
    } else {
      target.getSize(this);
    }

    if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
        && canNotifyStatusChanged()) {
      target.onLoadStarted(getPlaceholderDrawable());
    }
    if (IS_VERBOSE_LOGGABLE) {
      logV("finished run method in " + LogTime.getElapsedMillis(startTime));
    }
  }

從Request到Data的設計——資源加載模塊設計

image

image

每個ModelLoader中包含一個對應的DataFetcher,真正的網絡請求實現是DataFetcher老完成的。

從Data到Resource的設計——解碼和轉碼模塊設計

image

從Resource到Resource的設計——資源變換操作

image
從Resource到顯示在Target上的設計——資源顯示操作
image

RePlugin

參考:
Android全面插件化RePlugin流程與源碼解析

ClassLoaer

1、RePluginClassLoader: 宿主App中的Loader,繼承PathClassLoader,也是唯一Hook住系統的Loader
2、PluginDexClassLoader: 加載插件的Loader,繼承DexClassLoader。用來做一些“更高級”的特性

v-layout

參考:
Android開源庫V - Layout:淘寶、天貓都在用的UI框架,趕緊用起來吧!

image

最外層大的Adapter通過setAdapters方法將每個綁定helper的adapter放到mAdapters中,遍歷出helper列表添加到LayoutManager中。
其中內部的每個adapter通過Pair對象跟AdapterDataObserver綁定。
重點方法:

@Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        if (mHasConsistItemType) {
            Adapter adapter = mItemTypeAry.get(viewType);
            if (adapter != null) {
                return adapter.onCreateViewHolder(parent, viewType);
            }

            return null;
        }


        // reverse Cantor Function
        Cantor.reverseCantor(viewType, cantorReverse);

        int index = (int)cantorReverse[1];
        int subItemType = (int)cantorReverse[0];

        Adapter adapter = findAdapterByIndex(index);
        if (adapter == null) {
            return null;
        }

        return adapter.onCreateViewHolder(parent, subItemType);
    }

public void onBindViewHolder(RecyclerView.ViewHolder holder, int position, List<Object> payloads) {
        super.onBindViewHolder(holder, position, payloads);
        Pair<AdapterDataObserver, Adapter> pair = findAdapterByPosition(position);
        if (pair == null) {
            return;
        }
        pair.second.onBindViewHolder(holder, position - pair.first.mStartPosition, payloads);
        pair.second.onBindViewHolderWithOffset(holder, position - pair.first.mStartPosition, position, payloads);

    }
調用最外層Adapter時會回調內部每個小的adapter的onBindViewHolder方法從而改變內部布局。
hepler中持有mLayoutView
mLayoutView賦值:
 mLayoutView = layoutManager.generateLayoutView();

LeakCanary

java內存相關基礎:
這里以Java虛擬機為例,將運行時內存區分為不同的區域,每個區域承擔著不同的功能。
方法區
用戶存儲已被虛擬機加載的類信息,常量,靜態常量,即時編譯器編譯后的代碼等數據。異常狀態 OutOfMemoryError,其中包含常量池和用戶存放編譯器生成的各種字面量和符號引用。

是JVM所管理的內存中最大的一塊。唯一目的就是存放實例對象,幾乎所有的對象實例都在這里分配。Java堆是垃圾收集器管理的主要區域,因此很多時候也被稱為“GC堆”。異常狀態 OutOfMemoryError。
虛擬機棧
描述的是java方法執行的內存模型,每個方法在執行時都會創建一個棧幀,用戶存儲局部變量表,操作數棧,動態連接,方法出口等信息。每一個方法從調用直至完成的過程,就對應著一個棧幀在虛擬機棧中入棧到出棧的過程。 對這個區域定義了兩種異常狀態 OutOfMemoryError、StackOverflowError。
本地方法棧
虛擬機棧為虛擬機執行java方法,而本地方法棧為虛擬機使用到的Native方法服務。異常狀態StackOverFlowError、OutOfMemoryError。
程序計數器
一塊較小的內存,當前線程所執行的字節碼的行號指示器。字節碼解釋器工作時,就是通過改變這個計數器的值來選取下一條需要執行的字節碼指令。
內存模型
Java內存模型規定了所有的變量都存儲在主內存中。每條線程中還有自己的工作內存,線程的工作內存中保存了被該線程所使用到的變量,這些變量是從主內存中拷貝而來。線程對變量的所有操作(讀,寫)都必須在工作內存中進行。不同線程之間也無法直接訪問對方工作內存中的變量,線程間變量值的傳遞均需要通過主內存來完成。
為了保證內存可見性,常常利用volatile關鍵子特性來保證變量的可見性(并不能保證并發時原子性)

1、強引用,只要強引用還存在,垃圾收集器永遠不會回收掉被引用的對象。
2、軟引用,在系統將要發生內存溢出異常之前,將會把這些對象列進回收范圍進行二次回收。如果這次回收還沒有足夠的內存,才會拋出內存溢出異常。SoftReference表示軟引用。
3、弱引用,只要有GC,無論當前內存是否足夠,都會回收掉只被弱引用關聯的對象。WeakReference表示弱引用。
4、虛引用,這個引用存在的唯一目的就是在這個對象被收集器回收時收到一個系統通知,被虛引用關聯的對象,和其生存時間完全沒關系。PhantomReference表示虛引用,需要搭配ReferenceQueue使用,檢測對象回收情況。

leakCanary原理:
主要是在Activity的&onDestroy方法中,手動調用 GC,然后利用ReferenceQueue+WeakReference,來判斷是否有釋放不掉的引用,然后結合dump memory的hpof文件, 用HaHa分析出泄漏地方。
具體監聽的原理在于 Application 的registerActivityLifecycleCallbacks方法,該方法可以對應用內所有 Activity 的生命周期做監聽, LeakCanary只監聽了Destroy方法。GC會造成主線程阻塞卡頓,需要在空閑時間內進行GC,根據IdleHandler,當主線程空閑時,會回調queueidle函數。

知識點:
1,用ActivityLifecycleCallbacks接口來檢測Activity生命周期
2,WeakReference + ReferenceQueue 來監聽對象回收情況
3,Apolication中可通過processName判斷是否是任務執行進程
4,MessageQueue中加入一個IdleHandler來得到主線程空閑回調
5,LeakCanary檢測只針對Activiy里的相關對象。其他類無法使用,還得用MAT原始方法

EventBus

黏性事件:發送事件之后再訂閱該事件也能收到該事件,跟黏性廣播類似。

RN

參考:
https://juejin.im/post/57d4e67fda2f600059f48e11
那么Java和Js之間想要能聽懂對方的話,有兩個必備條件:
雙方的信息要能夠傳達到對方那里去,就是,先不管聽不聽的懂 ,你首先要把話傳過去
信息傳達前需要經過翻譯,才能被接受方正確理解。

第一個條件的解決方案是通過C++來做這個傳話筒,Java通過JNI來call到c++層,然后c++層再把信息傳到js,反之亦然;第二個條件的解決方案就是通過在初始化的時候構造兩本“詞典”,約定好以后說話只說對方寫好的“詞典”上的單詞,保證對方能聽懂。

NativeModuleRegistry.Builder nativeRegistryBuilder = new NativeModuleRegistry.Builder();
JavaScriptModuleRegistry.Builder jsModulesBuilder = new JavaScriptModuleRegistry.Builder();

rn開發中遇到的坑
1)flex css屬性支持不完全
2)debug比較困難,前端人員不理解native層面的報錯,native人員對于js的報錯也不理解。
3)原生組件Android和ios不同步,無法保證兩邊統一,并且有些控件Android和IOS調用不統一,需要做一層封裝,方便統一調用。
優點:
不需要發版,可以熱更新。但是涉及到原生功能支持的還是需要發版。只是減少發版的頻率而已。

WEEX

基本上很少有公司采用,社區也不活躍。核心原理與RN類似,js層采用vue。

Flutter

參考:https://zhuanlan.zhihu.com/p/43163159
理解Platform Channel工作原理
Flutter定義了三種不同類型的Channel,它們分別是
BasicMessageChannel:用于傳遞字符串和半結構化的信息。
MethodChannel:用于傳遞方法調用(method invocation)。
EventChannel: 用于數據流(event streams)的通信。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,622評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,716評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,746評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,991評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,706評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,036評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,029評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,203評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,725評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,451評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,677評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,161評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,857評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,266評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,606評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,407評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,643評論 2 380

推薦閱讀更多精彩內容