闲来无事,正看着百度新闻,突然灵感一闪,何不研究下一款新闻app,那就从今日头条开始吧。
这里使用压缩软件打开,主要看lib目录及assets目录。
lib目录主要是放一些库或jar包,打开后发现,只有一个armeabi目录,我们知道x86/x86_64/armeabi-v7a/arm64-v8a设备都支持armeabi架构的.so文件,所以一个目录也是可以的,只是不能保证100%不crash。
* 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
……
其他的不分析了,感兴趣可以自行百度。
assets目录主要放置静态文件,且为只读权限,不能修改。
打开之后,发现大量的html、js、css文件,不愧是混合开发啊,一些静态文件都直接放assets里,加载的时候都不需要去网络读取。这里包含着jquery.js、rangy-core.js、TBAppLinkJsBridge.js(连接淘宝)、wpload.js、wpsave.js、android.js等。
由图可知,使用了httpdns服务,这种服务具有域名防劫持、精准调度的特性。尤其是阿里推出的httpdns号称0ms解析延迟
。事实上,刚查了阿里的httpdns服务,ip就是203.107.1.1,确认其使用阿里的httpdns服务。
随便点击一条新闻试试,得到的地址是
http://m.haiwainet.cn/ttc/345621/2017/0510/content_30905168_1.html
可以看到这个网站并不带头部及底部的,看来只是为给今日头条或者其他第三方准备的,不信我在在该网站内随便点其他新闻
网址格式为http://m.haiwainet.cn/middle/3541093/2017/0510/content_30904904_1.html
ttc和moddile不一样。这也说明头条是采用和第三方合作的方式解决文章的版权问题,自己管理着评论接口,另外我app上看到的新闻页面有些关键词被加了热点链接,可以知道今日头条通过js对网页处理过,或者是加工过。
其他地方很多都是https://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方法:
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 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
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代码
public class MainActivity
extends com.ss.android.article.base.feature.main.a
{
private static Set> V = new HashSet();
private WeakReference 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,其中,我们注意到private static Set
的V是static修饰的,只会有一个对象,估计后面打开的每一个页面都会向v里增加activity。
瞎折腾一通,没多少技术含量,也许是瞎折腾。但是如果你需要去一家公司面试,先熟悉他所用的技术,提前做点功课、想好应对的策略,可以大大增加面试通过的胜算。