對今日頭條android端的一次簡單分析(上)

該篇博文以前標題比較大,引發很多朋友反感,本該刪除,從新整理再發,可是考慮到,編輯後從新發,平臺會再次推薦給你們,更會耽誤老鐵們的時間,遂決定在原文簡單編輯。另外給你們道歉,以前有些標題黨,但願你們原諒。javascript

前言

閒來無事,正看着百度新聞,忽然靈感一閃,何不研究下一款新聞app,那就從今日頭條開始吧。css

第一步

今日頭條版本

1.一、輪廓分析

這裏使用壓縮軟件打開,主要看lib目錄及assets目錄。
html

壓縮工具打開

1.二、lib目錄

lib目錄主要是放一些庫或jar包,打開後發現,只有一個armeabi目錄,咱們知道x86/x86_64/armeabi-v7a/arm64-v8a設備都支持armeabi架構的.so文件,因此一個目錄也是能夠的,只是不能保證100%不crash。java

  • libandfix.so 阿里的一個熱更新框架
  • libcocklogic-1.1.3.so 阿里的一個推送
  • libdaemon.so 內存泄漏檢測
  • libgif.so、libgifimage.so字面意思可知是圖片顯示庫
  • libimagepipeline.so facebook出品的fresco框架,這個框架使用率正在被Glide追趕,畢竟Glide是谷歌出的。
  • libOcrEncryption.so 文字識別庫
  • libstatic-webp.so 支持主流webp格式圖片,這個格式圖片比jpg圖片壓縮率超高,幾兆的圖片均可能被壓縮到幾十k。
  • libweibosdkcore.so 微博sdk
    ……

其餘的不分析了,感興趣能夠自行百度。
jquery

1.三、assets目錄

assets目錄主要放置靜態文件,且爲只讀權限,不能修改。
打開以後,發現大量的html、js、css文件,不愧是混合開發啊,一些靜態文件都直接放assets裏,加載的時候都不須要去網絡讀取。這裏包含着jquery.js、rangy-core.js、TBAppLinkJsBridge.js(鏈接淘寶)、wpload.js、wpsave.js、android.js等。android

第二步:抓包

httpDNS

由圖可知,使用了httpdns服務,這種服務具備域名防劫持、精準調度的特性。尤爲是阿里推出的httpdns號稱 0ms解析延遲。事實上,剛查了阿里的httpdns服務,ip就是203.107.1.1,確認其使用阿里的httpdns服務。
隨便點擊一條新聞試試,獲得的地址是
m.haiwainet.cn/ttc/345621/…
能夠看到這個網站並不帶頭部及底部的,看來只是爲給今日頭條或者其餘第三方準備的,不信我在在該網站內隨便點其餘新聞
網址格式爲 m.haiwainet.cn/middle/3541…
ttc和moddile不同。這也說明頭條是採用和第三方合做的方式解決文章的版權問題,本身管理着評論接口,另外我app上看到的新聞頁面有些關鍵詞被加了熱點連接,能夠知道今日頭條經過js對網頁處理過,或者是加工過。
其餘地方不少都是 security.snssdk.com通訊,也沒什麼抓的。

第三步:反編譯


有圖可知共使用的服務有andfix、百度推送、極光推送、gson(我以爲fastjson更好用)、小米推送、華爲推送、個信、autolay自動佈局、友盟等等。
先分析下啓用頁SplashActivity

圖中有個地方調了finish,看下條件:
isSplashCreateAbOn 沒找到,不知道是幹嗎的,估計寫在so文件裏了。
isTaskRoot判斷是否在任務棧中的根Activity,即啓動頁面的第一個activity。
上面q、r方法代碼以下(註釋是猜想的意思):

private void q()
  {
    if (!com.ss.android.article.base.app.a.Q().cj()) {
      return;
    }
    ArticleBaseExtendManager.a().registDeviceManager(this);
    //註冊服務
  }

  private void r()
  {
    if (!this.D)
    {
      this.D = true;
      this.E = ArticleBaseExtendManager.a().hasDetailInfo(this);
      //獲得詳細信息
    }
  }複製代碼

下面去看這個Manager方法:web

package com.ss.android.article.base.feature.shrink.extend;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import com.bytedance.common.utility.Logger;
import com.bytedance.common.utility.k;
import com.ss.android.common.shrink.extend.CoreExtendAdapter;
import com.ss.android.common.shrink.extend.CoreExtendSdkManager;
import java.util.HashMap;
import java.util.Map;

public class ArticleBaseExtendManager extends CoreExtendSdkManager {
  private static CoreExtendSdkManager b = null;
  public Map<String, CoreExtendAdapter> a = new HashMap();

  private CoreExtendAdapter a(String paramString)
  {
    CoreExtendAdapter localCoreExtendAdapter2;
    if (k.a(paramString))
    {
      localCoreExtendAdapter2 = null;
      return localCoreExtendAdapter2;
    }
    for (;;)
    {
      try
      {
        localCoreExtendAdapter2 = (CoreExtendAdapter)this.a.get(paramString);
        if (localCoreExtendAdapter2 != null) {
          break;
        }
        Object localObject2 = Class.forName(paramString).newInstance();
        if (!(localObject2 instanceof CoreExtendAdapter)) {
          continue;
        }
        localCoreExtendAdapter1 = (CoreExtendAdapter)localObject2;
      }
      catch (Throwable localThrowable1)
      {
        Object localObject1 = localThrowable1;
        CoreExtendAdapter localCoreExtendAdapter1 = null;
        continue;
        localCoreExtendAdapter1 = null;
        continue;
      }
      try
      {
        this.a.put(paramString, localCoreExtendAdapter1);
        //加載完成。
        Logger.d("CoreExtendAdapter", "load ThirdExtendLibAdapter done: " + paramString);
        return localCoreExtendAdapter1;
      }
      catch (Throwable localThrowable2) {}
    }
    //加載異常時
    Logger.w("CoreExtendAdapter", "load " + paramString + " has exception: " + localThrowable2);
    return localCoreExtendAdapter1;
  }

  public static CoreExtendSdkManager a()
  {
    if (b == null) {}
    try
    {
      if (b == null) {
        b = new ArticleBaseExtendManager();
      }
      return b;
    }
    finally {}
  }

  public void baiduStatisticsEvent(Context paramContext, String paramString1, String paramString2)
  {
    CoreExtendAdapter localCoreExtendAdapter = a("com.ss.android.article.base.feature.shrink.extend.BaiduStatisticsAdapter");
    if (localCoreExtendAdapter == null) {
      return;
    }
    localCoreExtendAdapter.baiduStatisticsEvent(paramContext, paramString1, paramString2);
  }

  public Intent getJumpIntent(Context paramContext)
  {
    CoreExtendAdapter localCoreExtendAdapter = a("com.ss.android.article.base.feature.shrink.extend.UgrReadApkAdapter");
    if (localCoreExtendAdapter == null) {
      return super.getJumpIntent(paramContext);
    }
    return localCoreExtendAdapter.getJumpIntent(paramContext);
  }

  public boolean hasDetailInfo(Context paramContext)
  {
    CoreExtendAdapter localCoreExtendAdapter = a("com.ss.android.article.base.feature.shrink.extend.UgrReadApkAdapter");
    if (localCoreExtendAdapter == null) {
      return super.hasDetailInfo(paramContext);
    }
    return localCoreExtendAdapter.hasDetailInfo(paramContext);
  }

  public void initBaiduStatistics(Context paramContext)
  {
    CoreExtendAdapter localCoreExtendAdapter = a("com.ss.android.article.base.feature.shrink.extend.BaiduStatisticsAdapter");
    if (localCoreExtendAdapter == null) {
      return;
    }
    localCoreExtendAdapter.initBaiduStatistics(paramContext);
  }

  public void initGoogleAdSdk(Context paramContext)
  {
    CoreExtendAdapter localCoreExtendAdapter = a("com.ss.android.article.base.feature.shrink.extend.GoogleAdAdapter");
    if (localCoreExtendAdapter == null) {
      return;
    }
    localCoreExtendAdapter.initGoogleAdSdk(paramContext);
  }

  public void registDeviceManager(Activity paramActivity)
  {
    CoreExtendAdapter localCoreExtendAdapter = a("com.ss.android.article.base.feature.shrink.extend.RegistDeviceManagerAda");
    if (localCoreExtendAdapter == null) {
      return;
    }
    localCoreExtendAdapter.registDeviceManager(paramActivity);
  }

  public void startAppseStatistics(Context paramContext, String paramString)
  {
    CoreExtendAdapter localCoreExtendAdapter = a("com.ss.android.article.base.feature.shrink.extend.AppseeStatisticsAdapter");
    if (localCoreExtendAdapter == null) {
      return;
    }
    localCoreExtendAdapter.startAppseStatistics(paramContext, paramString);
  }
}複製代碼

由上段代碼可知:定義一個hash數組 public Map a = new HashMap();
若是加載成功,執行a.put(paramString, localCoreExtendAdapter1)方法。該manage內還包含一些初始化方法,如initBaidu、initGoogleAdSdk、registDeviceManager,入參都是字符串的包名。
打開是的b方法,兼容性打開,通常由編譯器生成。
面試

protected Intent b()
  {
    Intent localIntent;
    if (this.F != null) {
      localIntent = this.F;
    }
    do
    {
      return localIntent;
      localIntent = new Intent(this, MainActivity.class);
      if (Build.VERSION.SDK_INT >= 11) {
        localIntent.addFlags(32768);
      }
    } while (Build.VERSION.SDK_INT < 21);
    localIntent.addFlags(67108864);
    localIntent.addFlags(536870912);
    return localIntent;
  }複製代碼

再看看Main_Activity代碼json

public class MainActivity extends com.ss.android.article.base.feature.main.a {
  private static Set<WeakReference<MainActivity>> V = new HashSet();
  private WeakReference<MainActivity> W = new WeakReference(this);

  public void onCreate(Bundle paramBundle)
  {
    com.bytedance.c.a.m(this);
    k.a.a("MainActivity#onCreateStart");
    k.a.b();
    try
    {
      Iterator localIterator = V.iterator();
      while (localIterator.hasNext())
      {
        WeakReference localWeakReference = (WeakReference)localIterator.next();
        if (localWeakReference != null)
        {
          Activity localActivity = (Activity)localWeakReference.get();
          if ((localActivity != null) && (localActivity != this))
          {
            localActivity.finish();
            continue;
            super.onCreate(paramBundle);
          }
        }
      }
    }
    catch (Throwable localThrowable) {}
    for (;;)
    {
      k.a.a("MainActivity#onCreateEnd");
      com.bytedance.c.a.n(this);
      return;
      V.clear();
      V.add(this.W);
    }
  }

  protected void onDestroy()
  {
    super.onDestroy();
    try
    {
      V.remove(this.W);
      return;
    }
    catch (Throwable localThrowable) {}
  }

  protected void onResume()
  {
    com.bytedance.c.a.o(this);
    k.a.a("MainActivity#onResumeStart");
    super.onResume();
    k.a.a("MainActivity#onResumeEnd");
    com.bytedance.c.a.p(this);
  }
}複製代碼

使用了弱引用WeakReference,其主要特色是:一旦發現了只具備弱引用的對象,無論當前內存空間足夠與否,都會回收它的內存。這句話意思若是有activity頁面沒有被釋放,在進入Main_Activity時都要執行finish的方法,解決後面多頁面衝突的bug,其中,咱們注意到rivate static Set > V = new HashSet();的V是static修飾的,只會有一個對象,估計後面打開的每個頁面都會向v裏增長activity。 數組

總結

瞎折騰一通,沒多少技術含量,也許是瞎折騰。可是若是你須要去一家公司面試,先熟悉他所用的技術,提早作點功課、想好應對的策略,能夠大大增長面試經過的勝算。

補充:本文較爲基礎,更深層次文章,下次帶到。對以前所爲深感抱歉!

相關文章
相關標籤/搜索