Android 綜合技術面試題

1.1 請談談你對 MVC、MVP、MVVM、MVI的理解?

MVC

Model:主要用于網絡請求、數據庫、業務邏輯處理等操作。
View:用于展示UI,一般采用XML文件進行界面的描述。
Controller:控制層的重任落在了眾多Activity上,Activity需要交割業務邏輯至Model層處理。
事件從View層流向Controller層,經過Model層處理,然后通知View層更新。

缺點:

  1. Controller層,也就是activity承擔了部分View層的職責,導致該層過于臃腫。
  2. 在MVC中,Model層處理完數據后,直接通知View層更新,因此MV耦合性強。

MVP

Model:數據處理層。
View:視圖顯示層。
Presenter:業務邏輯處理層。
通過接口,V和P間接相互持有引用,P和M間接相互持有引用,但是V和M完全解耦,而且把業務邏輯從activity中移到 P中,減輕了activity的壓力。

缺點:
當View層足夠復雜的時候,View接口會變得很龐大。

MVVM

Model:數據處理層。
View:視圖顯示層。
ViewModel:視圖模型,通過框架實現View層和Model層的雙向綁定。

優點:

  1. View和Model雙向綁定,一方的改變都會影響另一方,開發者不用再去手動修改UI的數據。
  2. 不需要findViewById也不需要butterknife,不需要拿到具體的View去設置數據綁定監聽器等等,這些都可以用DataBinding完成。
  3. View和Model的雙向綁定是支持生命周期檢測的,不會擔心頁面銷毀了還有回調發生,這個由Lifecycle完成。
  4. 不會像MVC一樣導致Activity中代碼量巨大,也不會像MVP一樣出現大量的View和Presenter接口,項目結構更加低耦合。

缺點:
由于數據和視圖的雙向綁定,導致出現問題時不好定位來源,有可能數據問題導致,也有可能業務邏輯中對視圖屬性的修改導致。

VMI

View: Activity 和 Layout XML 文件,與 MVVM 中 View 的概念相同;
Intent: 定義數據操作,是將數據傳到 Model 的唯一來源,相比 MVVM 是新的概念;
ViewModel: 存儲視圖狀態,負責處理表現邏輯,并將 ViewState 設置給可觀察數據容器;
ViewState: 一個數據類,包含頁面狀態和對應的數據。

唯一可信源: 數據只有一個來源(ViewModel),與 MVVM 的思想相同;
單數據流: View 和 ViewModel 之間只有一個數據流,只有一個地方可以修改數據,確保數據是安全穩定的。并且 View 只需要訂閱一個 ViewState 就可以獲取所有狀態和數據,相比 MVVM 是新的特性;
響應式: ViewState 包含頁面當前的狀態和數據,View 通過訂閱 ViewState 就可以完成頁面刷新,相比于 MVVM 是新的特性。

優點:
Mvi模式采用單向流動的設計思路,用戶意圖Intent通過View層傳輸到Model,數據處理完畢之后,再返回狀態State到View層顯示,由此一個循環結束.一個界面的所有東西,都是不斷循環在一個閉環內,所以數據的流向都能方便地監測到,哪一環出現問題就能快速地定位到問題所在.

缺點:
State 膨脹: 所有視圖變化都轉換為 ViewState,還需要管理不同狀態下對應的數據。實踐中應該根據狀態之間的關聯程度來決定使用單流還是多流;
內存開銷: ViewState 是不可變類,狀態變更時需要創建新的對象,存在一定內存開銷;
局部刷新: View 根據 ViewState 響應,不易實現局部 Diff 刷新,可以使用 Flow#distinctUntilChanged() 來刷新來減少不必要的刷新。

選擇:
1、如果項目簡單,沒什么復雜性,未來改動也不大的話,那就不要用設計模式或者架構方法,只需要將每個模塊封裝好,方便調用即可,不要為了使用設計模式或架構方法而使用。
2、對于偏向展示型的app,絕大多數業務邏輯都在后端,app主要功能就是展示數據,交互等,建議使用mvvm。
3、對于工具類或者需要寫很多業務邏輯app,使用mvp或者mvvm都可。
4、MVI 與前者的主要區別不在于強調嚴格的單向數據流,而在于從命令式的開發模式,轉變為響應式的開發模式。

我們并不是說越新潮,越復雜的架構就是最好的,只有合適的架構才是最好的。

1.2 分別介紹下你所知道Android的幾種存儲方式?

網絡存儲 :一般就是http get或http post 從服務器獲取數據,業務數據獲取的常用辦法。
SqlLite:將數據緩存到本地數據庫,可用于存儲大量不經常改變的數據,可配合ContentProvider使用。
文件存儲:將一些不太敏感的數據保存到本地,SharePreference:用XML格式文件存儲數據,在data/data/<pa'ka'geName>/shared_prefs下,不支持數據頻繁讀寫,頻繁讀寫會造成數據錯亂。
ContentProvider:四大組件之一,一般配合SqlLite、SharePreference、文件存儲使用,支持數據的并發讀取。

1.3 簡述下熱修復的原理?

熱修復分為三個部分,分別是Java代碼部分熱修復,Native代碼部分熱修復,還有資源熱修復。

資源部分熱更新直接反射更改所有保存的AssetManager和Resources對象就行(可能需要重啟應用)

Native代碼部分也很簡單,系統找到一個so文件的路徑是根據ClassLoader找的,修改ClassLoader里保存的路徑就行(可能需要重啟應用)

Java部分的話目前主流有兩種方式,一種是Java派,一種是Native派。

  • java派:通過修改ClassLoader來讓系統優先加載補丁包里的類
    代表作有騰訊的tinker,谷歌官方的Instant Run,包括multidex也是采用的這種方案優點是穩定性較好,缺點是可能需要重啟應用
  • native派:通過內存操作實現,比如方法替換等
    代表作是阿里的SopHix,如果算上hook框架的話,還有dexposed,epic等等
    優點是即時生效無需重啟,缺點是穩定性不好:如果采用方法替換方式實現,假如這個方法被內聯/Sharpening優化了,那么就失效了;inline hook則無法修改超短方法。
    熱修復后使用反射調用對應方法時可能發生IllegalArgumentException。

1.4 談談如何適配更多機型的?

  1. 一般來說主要針對屏幕適配,最小寬度適配和今日頭條density適配
  2. 權限適配,安卓6.0的運行時權限,這里有坑,6.0以前,Vivo有i管家進行權限管理,魅族自帶有權限管理,還有其他第三方軟件進行權限限制,導致權限不可用
  3. 異形屏幕適配,一般來說都是劉海,水滴,挖孔部分不進行使用或者就直接不管不顯示缺失部分,可以滿足大部分需求,小部分需求需要使用異形部分的需要按手機型號進行特定適配,官網都有適配方法
  4. 安卓系統適配,及時關注新系統新特性,使情況修改 targetSdk
  5. 語言,Left Right和Start End,這些適配基本不需要太大關注
  6. “和ios一樣”,口才或者腦細胞適配,能說服就下班,不能就加班

1.5 請談談你是如何進行多渠道打包的?

配置gradle實現多渠道打包

每當應用發布一個新的版本的時候,我們會分發到每一個應用市場中去,比如,360手機助手,小米應用市場,華為應用市場等。為了能夠統計每個應用市場的下載量,活躍量我們必須用一個標記來區分這些不同市場分發下去的應用,渠道號也就應運而生。隨著渠道的不斷增加,需要生成的渠道包也就越來越多。 在打包的過程中,我們一般都是使用gradle來進行的。gradle為我們的打包提高了很多的便利,多渠道打包也可以輕松實現。

  1. 首先在AndroidManifest.xml文件中定義一個metadata
<meta-data android:name="CHANNEL" android:value="${CHANNEL_VALUE}" />

2.然后在gradle文件中設置一下productFlavors

    android {
        productFlavors {
            xiaomi {
                manifestPlaceholders =[CHANNEL_VALUE: "xiaomi"]
            }
            _360 {
                manifestPlaceholders =[CHANNEL_VALUE: "_360"]
            }
            baidu {
                manifestPlaceholders =[CHANNEL_VALUE: "baidu"]
            }
            wandoujia {
                manifestPlaceholders =[CHANNEL_VALUE: "wandoujia"]
            }
        }
    }

productFlavors是為了在同一個項目中創建應用的不同版本。具體的配置信息可以看官方說明。

  1. 執行gradle AS就可以將所有的渠道包輸出了。

gradle實現多渠道打包的缺點

雖然gradle配置多渠道打包很簡單,也很方便,但是這種方式存在一個致命的缺陷,那就是費時間。因為AndroidManifest.xml文件被修改過了,所以所有的包都必須重新編譯簽名。一般來說100個渠道包就要至少一個小時的時間,這一個小時5杯咖啡都不夠等的。更要命的是萬一哪里需要微調一下代碼或者文案,那么不好意思,一切又得重頭來。這就很麻煩了,所以有沒有什么方法可以快速完成打包呢?我們繼續往下看。

1.6多渠道快速打包

快速打包方案Version_1.0

如上所說,我們去到信息只是修改了一下manifest文件里面的一個meta-data的值而已,有沒有什么辦法可以不需要重新構建代碼呢?答案是肯定的。我們可以使用apktool,反編譯我們的APK文件。

apktool d yourApkName build

經過解碼后,我們會得到如下文件:



我們發現我們需要修改的manifest文件就在里面,所以通過命令可以修改下他的內容,然后重新打包,就可以生成一個全新的渠道包了,省去了重新編譯構建代碼的過程。使用一下Python腳本,將manifest文件里面channel信息進行替換。

    import re
    def replace_channel(channel, manifest):
    pattern = r'(<metadata\s+android:name="channel"\s+android:value=")(\S+)("\s+/>)'
    replacement = r"\g<1>{channel}\g<3>".format(channel=channel)
    return re.sub(pattern, replacement, manifest)

然后通過apktool重新將文件夾打包生成APK。

apktool b build your_unsigned_apk

最后,使用jarsigner重新簽名apk:

jarsigner -sigalg MD5withRSA -digestalg SHA1 -keystore your_keystore_path -storepass your_storepass -signedjar your_signed_apk, your_unsigned_apk, your_alias

通過這上面的一系列過程,我們可以實現不重新編譯構建項目就生成不同的渠道包。這會節省很多的時間。但是隨著渠道包增加,重新簽名也會占用很大一部分時間,那能不能不重新簽名呢?

分析簽名的算法后發現,在打包過程后的META-INF文件夾下面添加空白文件是不會對簽名的結果產生影響的。

所以我們只要像META_INF文件夾里面寫入空白的文件來標識渠道號就可以了。
通過Python腳本像APK文件中寫入渠道:

import zipfile
zipped = zipfile.ZipFile(your_apk, 'a', zipfile.ZIP_DEFLATED)
empty_channel_file = "METAINF/mtchannel_{channel}".format(channel=your_channel)
zipped.write(your_empty_file,empty_channel_file)

執行后會在META-INF文件夾下面生成一個空白文件:
然后我們在項目中去讀取這個空白文件:

    public static String getChannel(Context context) {
        ApplicationInfo appinfo = context.getApplicationInfo();
        String sourceDir = appinfo.sourceDir;
        String ret = "";
        ZipFile zipfile = null;
        try {
            zipfile = new ZipFile(sourceDir);
            Enumeration<?> entries = zipfile.entries();
            while (entries.hasMoreElements()) {
                ZipEntry entry = ((ZipEntry) entries.nextElement());
                String entryName = entry.getName();
                if (entryName.startsWith("mtchannel")) {
                    ret = entryName;
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (zipfile != null) {
                try {
                    zipfile.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        String[] split = ret.split("_");
        if (split != null && split.length >= 2) {
            return
                    ret.substring(split[0].length() + 1);
        } else {
            return "";
        }
    }

這樣每生成一個渠道包只需要復制一下APK,然后添加一個空態的文件到META-INF下面就可以了,這樣100個渠道包一分鐘之內就可以搞定了。

快速打包方案Version_2.0

上面的方案基本上已經比較完美的解決我們打包的問題了,然而好景不長,Google在Android 7.0中更新了應用的簽名算法-APK Signature Scheme v2,它是一個對全文件進行簽名的方案,能提供更快的應用安裝時間、對未授權APK文件的更改提供更多保護,在默認情況下,Android Gradle 2.2.0插件會使用APK Signature Scheme v2和傳統簽名方案來簽署你的應用。因為是對全文件進行簽名的,所以之前的添加空白文件的方案就沒有作用了。

不過目前這個方案還不是強制性的,我們可以選擇在gradle配置文件中將其關閉:

    android {
        defaultConfig { ...}
        signingConfigs {
            release {
                storeFile file ("myreleasekey.keystore")
                storePassword "password"
                keyAlias "MyReleaseKey"
                keyPassword "password"
                v2SigningEnabled false
            }
        }
    }

那么新的簽名方案對已有的渠道生成方案有什么影響呢?
下圖是新的應用簽名方案和舊的簽名方案的一個對比:

新的簽名方案會在ZIP文件格式的 Central Directory 區塊所在文件位置的前面添加一個APK Signing Block區塊,下面按照ZIP文件的格式來分析新應用簽名方案簽名后的APK包。 整個APK(ZIP文件格式)會被分為以下四個區塊:

  1. Contents of ZIP entries(from offset 0 until the start of APK Signing Block)
  2. APK Signing Block
  3. ZIP Central Directory
  4. ZIP End of Central Directory

新應用簽名方案的簽名信息會被保存在區塊2(APK Signing Block)中, 而區塊1(Contents of ZIP entries)、區塊3(ZIP Central Directory)、區塊4(ZIP End of Central Directory)是受保護的,在簽名后任何對區塊1、3、4的修改都逃不過新的應用簽名方案的檢查。

之前的渠道包生成方案是通過在META-INF目錄下添加空文件,用空文件的名稱來作為渠道的唯一標識,之前在META-INF下添加文件是不需要重新簽名應用的,這樣會節省不少打包的時間,從而提高打渠道包的速度。但在新的應用簽名方案下META-INF已經被列入了保護區了,向META-INF添加空文件的方案會對區塊1、3、4都會有影響,新應用簽名方案簽署的應用經過我們舊的生成渠道包方案處理后,在安裝時會報以下錯誤:

Failure
[INSTALL_PARSE_FAILED_NO_CERTIFICATES: Failed to collect certificates from base.apk:
META-INF/CERT.SF indicates base.apk is signed using APK Signature Scheme v2,
but no such signature was found. Signature stripped?]

區塊1,3,4是受保護的,任何的修改都會引起簽名的不一致,但是區塊2是不受保護的,所以能不能在區塊2上面找到解決辦法呢?首先看一下區塊2的文件結構:

區塊2中APK Signing Block是由這幾部分組成:2個用來標示這個區塊長度的8字節 + 這個區塊的魔數(APK Sig Block 42)+ 這個區塊所承載的數據(ID-value)。

我們重點來看一下這個ID-value,它由一個8字節的長度標示+4字節的ID+它的負載組成。V2的簽名信息是以ID(0x7109871a)的ID-value來保存在這個區塊中,不知大家有沒有注意這是一組ID-value,也就是說它是可以有若干個這樣的ID-value來組成,那我們是不是可以在這里做一些文章呢?

對于簽名的認證過程是這樣的:

  1. 尋找APK Signing Block,如果能夠找到,則進行驗證,驗證成功則繼續進行安裝,如果失敗了則終止安裝
  2. 如果未找到APK Signing Block,則執行原來的簽名驗證機制,也是驗證成功則繼續進行安裝,如果失敗了則終止安裝

在校驗的時候,檢驗代碼如下:

    public static ByteBuffer findApkSignatureSchemeV2Block(ByteBuffer apkSigningBlock, Result result)
            throws SignatureNotFoundException {
        checkByteOrderLittleEndian(apkSigningBlock);
        // FORMAT:
        // OFFSET DATA TYPE DESCRIPTION
        // * @+0 bytes uint64: size in bytes (excluding this field)
        // * @+8 bytes pairs
        // * @-24 bytes uint64: size in bytes (same as the one above)
        // * @-16 bytes uint128: magic
        ByteBuffer pairs = sliceFromTo(apkSigningBlock, 8, apkSigningBlock.capacity() - 24);
        int entryCount = 0;
        while (pairs.hasRemaining()) {
            entryCount++;
            if (pairs.remaining() < 8) {
                throw new SignatureNotFoundException(
                        "Insufficient data to read size of APK Signing Block entry #" + entryCount);
            }
            long lenLong = pairs.getLong();
            if ((lenLong < 4) || (lenLong > Integer.MAX_VALUE)) {
                throw new SignatureNotFoundException("APK Signing Block entry #" + entryCount +
                        " size out of range: " + lenLong);
            }
            int len = (int) lenLong;
            int nextEntryPos = pairs.position() + len;
            if (len > pairs.remaining()) {
                throw new SignatureNotFoundException("APK Signing Block entry #" + entryCount +
                        " size out of range: " + len + ", available: " + pairs.remaining());
            }
            int id = pairs.getInt();
            if (id == APK_SIGNATURE_SCHEME_V2_BLOCK_ID) {
                return getByteBuffer(pairs, len - 4);
            }
            result.addWarning(Issue.APK_SIG_BLOCK_UNKNOWN_ENTRY_ID, id);
            pairs.position(nextEntryPos);
        }
        throw new SignatureNotFoundException(
                "No APK Signature Scheme v2 block in APK Signing Block");
    }

我們可以發現,述代碼中關鍵的一個位置是 if (id == APK_SIGNATURE_SCHEME_V2_BLOCK_ID) {return getByteBuffer(pairs, len - 4);},通過源代碼可以看出Android是通過查找ID為APK_SIGNATURE_SCHEME_V2_BLOCK_ID = 0x7109871a的ID-value,來獲取APK Signature Scheme v2 Block,對這個區塊中其他的ID-value選擇了忽略。也就是說,在APKSignature Scheme v2中沒有看到對無法識別的ID,有相關處理的介紹。所以我們可以通過寫入自定義的ID-Value來自定義渠道。

1.6 MVP中你是如何處理Presenter層以防止內存泄漏的?

首先 MVP 會出現內存泄漏是因為 Presenter 層持有 View對象,一般我們會把 Activity 做為 View 傳遞到Presenter,Presenter 持有 View對象,Activity 退出了但是沒有回收出現內存泄漏。
解決辦法:

  1. Activity onDestroy() 方法中調用 Presenter 中的方法,把 View 置為 null
  2. 使用 Lifecycle
  3. 使用 MVVM

1.7 如何計算一張圖片所占的內存空間大小?

通常情況下圖片占用內存的大小:圖片分辨率X像素點大小。

api獲取方法為bitmap.getByteCount()。

如果放入res/drawable下,通過BitmapFactory.decodeResource()方法加載不同dpi文件下的同一張圖片到內存的大小是不一樣(存在分辨率的轉換),同時也與設備的dpi大小有關。而放在sd卡、網絡或者assert里則是一樣的(即使是不通dpi的設備)。圖片占用內存大小跟控件大小無關(glide是先拿到控件大小,再去對圖片加載做的處理)。

1.8 有沒有遇到64k問題,應該如何解決?

單個dex文件方法超過64k,基本上都是引用過多的依賴才導致的。
解決方案:

  1. 導入依賴
'com.android.support:multidex:1.0.1'
  1. defaultConfig增加這個設置
multiDexEnabled true
  1. android下面增加這個設置
dexOptions {
    incremental true
    javaMaxHeapSize "4g"
}

以上都是在app的buildl.gradle中設置的,編譯。

  1. 打開自定義的Application,繼承MultiDexApplication,并重寫attachBaseContext方法

1.9 如何優化 Gradle 的構建速度?

  1. 物理設備:16g+內存、硬盤ssd、高配CPU,最好是超頻CPU。5.0GHz那種
  2. 配置:使用高版本AS
    android.injected.testOnly=false
    android.buildCacheDir=buildCacheDir
    org.gradle.caching=true
    # 開啟緩存
    android.enableBuildCache=true
    android.enableAapt2=true
    # 配置大內存
    org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
    # 并行編譯
    org.gradle.parallel=true
    # 開啟孵化模式
    org.gradle.configureondemand=true
    # 守護進程
    org.gradle.daemon=true
    android.enableSeparateAnnotationProcessing=true
    buildTypes{
        debug{
            crunchPngs false
            aaptOptions.cruncherEnabled = false
        }
    }
    dexOptions {
        preDexLibraries true
    }

如果使用了multiDexEnabled ,一定要依賴最新版本的1.0.3版本
依賴的庫不帶+號,每次都會自動檢查最新版

  1. AS設置,如果已下載好依賴,可以設置離線模式

以上配置可以加快構建和編譯速度。

1.10 如何獲取Android設備唯一ID?

常用方法:

  • IMEI
    權限(讀手機權限)
    可以手動更改(模擬器上) /關閉權限報錯
  • Mac地址
    訪問WIFI權限
    有些設備沒有wifi(沒有上網功能)/6.0以上版本返回的都是固定的02:00:00 之類地址
  • ANDROID_ID
    設備首次運行,系統會隨機生成一個64位的數字,轉換成16進制保存
    手機恢復出廠設置后值有變化/ 有些國產設備不會返回值
  • Serial Number (設備序列號)
    有些國產手機(紅米)會出現垃圾數據

實現思路:

  1. 獲取設備的IMEI -->設備的Mac地址-->隨機生成的UUID
  2. 拼接在一起,然后用MD5加密
  3. 存儲到本地的文件中,隱藏并且存儲在App中的SP中
  4. 使用的時候先從SP中讀取,沒有的再生成,然后把生成的唯一id保存到SP中

1.11 談一談Android P禁用http對我們開發有什么影響?

  • APP改用https請求
  • targetSdkVersion 降到27以下
  • res --> add : network_security_config.xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true" />
</network-security-config>

AndroidManifest.xml--> application :
android:networkSecurityConfig="@xml/network_security_config"

1.12 什么是AOP?在Android中它有哪些應用場景?

aop的實現有靜態代理和動態代理,靜態代理有靜態代理模式,基于Ajc編譯器的AspectJ,動態代理有JDK動態代理,在Android的實現是InvicationHandler,需要實現接口,還有CGlib實現方式創建子類來繼承源類。

應用場景有各種狀態監測比如登錄、網絡、權限等。日志埋點,性能分析。

1.13 什么是MVVM?你是如何將其應用于具體項目中的?

MVVM架構其實由來已久,在Android中MVVM指代的是Model-View-ViewModel。Model就是數據模型,View一般認為是Activity/Fragment。那么這個ViewModel其實是Google為Android應用程序搭建MVVM架構而設計的一個組件(類)。
顧名思義,ViewModel可以認為是View與Model之間的一個容器,在沒有MVVM之前,我們通常會將View和Model之間的業務處理直接放在Activity/Fragment中進行處理,現在有了ViewModel,可以將相關邏輯放在其中處理,View(Activity/Fragment)只進行數據的展示,而不進行數據的加工處理。
那么數據在處理或者變化后,如何通知到View呢?這就需要通過Jetpack的另外一個組件LiveData。LiveData可以使得ViewModel中的數據變化后,通知到View,它起到一個橋梁的作用。本質上這是一個觀察者模式。

在Android項目中應用MVVM架構,Google官方建議的做法是使用Jetpack相關組件,這些組件設計的目的就是為了方便開發者搭建MVVM規范的應用程序。

1.14 請談談你是如何實現數據埋點的?

一、埋點技術

代碼埋點:
所謂的代碼埋點就是在你需要統計數據的地方植入N行代碼,統計用戶的關鍵行為。比如你想統計首頁某個banner的點擊量,上報的數據可以采用KEY-VALUE形式,我們定義 KEY為「CLICK_ADD_BTN」,VALUE的值為點擊的次數。當用戶點擊banner時,banner詳情的代碼會通過按鈕的「回調」來觸發執行,程序猿在業務代碼執行完后,又加上了統計代碼,把「CLICK_ADD_BTN」對應的VALUE加1,banner被統計到了一次使用。

代碼埋點的優點:
使用者控制精準,可以非常精確地選擇什么時候發送數據使用者可以比較方便地設置自定義屬性、自定義事件,傳遞比較豐富的數據到服務端。

代碼埋點的缺點:
埋點代價比較大,每一個控件的埋點都需要添加相應的代碼,不僅工作量大,而且限定了必須是技術人員才能完成;
更新代價比較大,每一次更新,都需要更新埋點方案,然后通過各個應用市場進行分發,而且有的用戶還不一定更新,這樣你就獲取不到這批用戶數據。

可視化埋點:
既然代碼埋點代價比較大,每一個埋點都需要寫代碼,那就使用可視化交互手段代替寫代碼;既然每次代碼埋點都需要更新,那就參照現在的很多手游做法,把核心代碼和配置、資源分開,每次用戶啟動app的時候通過網絡更新配置和資源。

可視化埋點優勢:
可視化買點解決了代碼埋點埋點代價大和更新代價大兩個問題。

可視化埋點劣勢:
可視化埋點能夠覆蓋的功能有限,目前并不是所有的控件操作都可以通過這種方案進行定制;

無埋點:
可視化埋點先通過界面配置哪些控件的操作數據需要收集;“無埋點”則是先盡可能收集所有控件的操作數據,然后再通過界面配置哪些數據需要在系統里面進行分析,“無埋點”也就是“全埋點”的意思。

無埋點的優點:
可視化埋點只能收集到你埋點以后的數據,如果你想對某個按鈕進行點擊分析,則只能分析增加可視化埋點以后的數據,之前的數據你收集不到,而無埋點在你部署SDK的時候數據就一直在收集。
因為無埋點對頁面所有元素進行埋點,那么這個頁面每個元素被點擊的概率你也就知道,對點擊概率比較大的元素可以進行深入分析。

無埋點的缺點:
由于無埋點方案所有的元素數據都收集,會給數據傳輸和服務器帶來較大的壓力。

二、數據埋點方式

1、公司研發在自己的產品當中注入統計代碼,搭建相應的后臺查詢,這種代價比較大。
2、集成第三方統計的SDK,比如友盟、百度移動統計、Sensors Data、GrowingIO、Talking Data等。

三、如何進行數據埋點

1、明確目標
經常有人問我說我要獲取那些數據來進行數據分析,其實這個問題不應該問別人,應該問問你自己,你是想用這個數據干什么,如果你想繪制基礎的人群畫像你就需要獲取用戶機型、網絡類型、操作系統,IP地域等數據;如果你想分析每一個注冊轉化率,你就需要獲取每一個步驟的點擊次數,然后制作成漏斗,看那一步轉化率出現了問題;
目的不一樣,獲取的數據也不一樣,使用的埋點技術也不一樣,我們無論做什么事情都不能忘了我們的目的!
2、獲取相應數據
業務不同,目的不同獲取的數據也不同,這里我只說一些比較共性的數據。
2.1、產品各個渠道下載量
這個可以用第三方數據統計工具來進行,這樣我們可以知道我們產品著重在那個渠道進行推廣。
2.2、產品活躍狀態分析
產品活躍狀態監控,留存分析、流失分析、新增變化等,次日留存率、七日留存率、月留存率,尤其對于處于成長期的產品而已,這個指標很重要,如果留存率比較低,說明你的產品有問題,這個時候你就需要進行用戶調研,找到流失的問題,否則大面積拉新,只能拉多少死多少,至于留存率、新增的變化這些數據,我們也可以借助第三方統計工具來進行。
2.3、事件分析
比如你想統計某個頁面的UV、PV、元素的點擊量、用戶停留時長、頁面跳出率等數據指標,可以選擇代碼埋點和可視化埋點等前端埋點解決方案。當某個頁面的UV很高,但是跳出率也很高,說明頁面有問題,你就要好好想想頁面的問題出在什么地方。
2.4、基本信息獲取
基本信息獲取,例如機型、網絡類型、操作系統,IP地域等,繪制基礎用戶人群畫像,這種分析出來的用戶畫像顆粒度比較大,如果想更精準的進行用戶畫像可以結合推薦系統,來獲取用戶的興趣指標,以及用戶操作行為等數據來進行更精準的用戶畫像,從而為產品運營和產品設計提供參考,可以借助第三方統計工具和自定義埋點的方式進行數據的收集。
2.5、漏斗模型
對于產品的關鍵路徑一定要進行漏斗模型分析,比如注冊路徑,從用戶輸入注冊手機號到注冊成功,中間可能會有幾個步驟,如果100個人注冊,最后只有一個人注冊成功,那么求運營同學心里的陰影面積。還有電商的購買下單路徑,從瀏覽商品到最后下單購買成功,每一個步驟的轉化率是多少,對于漏的比較多的那個步驟我們肯定要著重關注,分析原因。這個可以技術研發進行埋點,獲取更精確的數據,比如下圖的埋點表。

如何給app客戶端進行埋點?

領導說,APP需要加一下統計,你負責搞定
研發說,APP需要統計哪些地方,你列一下埋點需求
研發說,APP的數據統計SDK用哪家的?你選好了注冊一下、運營說,咱們的APP都能看哪些數據?平臺在哪?怎么查首頁的UV?
數據分析是一個很復雜的工作,很多人在談如何挖掘數據,做用戶畫像,設計數據漏斗,如何負責用戶生命周期管理,但發現很多人卻卡在了數據分析的第一步,那就是如何做數據埋點。

首頁陳峰老師先明確下完成一個APP數據埋點的幾個步驟:

  • 注冊一家統計網站
  • 新建應用
  • 獲取KEY和SDK代碼包
  • 將埋點需求和SDK包發給研發
  • 自定義埋點需求完善
  • 研發開發并完成APP上線
  • 在后臺查看數據
    1、注冊賬號
    建議用公司郵箱或者公用郵箱注冊,別用自己的私人郵箱和手機號碼,后續一旦有交接和工作變動時會比較麻煩。
    2、新建應用
    登錄后一般都有“新建應用”,可以選標準統計,大部分APP都選這個。游戲的app另說。
    名稱寫自己app的名稱,分類自己選1個。選錯了也不影響。
    平臺根據情況自己選。后期我們看數據和埋點都是ios和安卓分開的,所以你如果2個端都做,就一起都選上。
    描述可選,不用填。
    3、獲取KEY和SDK代碼包
    完成后可以得到2個APPKEY。分別是ios和安卓的。
    這里的appkey很重要,你可以下載了給研發,也可以稍后讓研發自己登錄進來自己下載。
    ios和安卓是分開2個獨立的,后續埋點和看數據都是分開的。這個切記。
    這時候,重點來了。
    此時,如果我們只想看 APP的活躍用戶,留存用戶,下載量。用戶地域分布,渠道分布,那么其實就夠了。
    4、將埋點需求和SDK包發給研發
    你這時候,就把剛才獲得的appkey和sdk包的下載地址,發給研發。或者直接把賬號和密碼發給研發。然后告訴研發,集成下百度移動統計的SDK包。這樣發版后,就可以看到大部分數據了。
    如下的數據都可以看到:

5、自定義事件完善
比如我們想看頁面里面 注冊 搜索按鈕,頂部banner,底部 首頁和 我的 2個導航條的點擊量。
一個埋點事件對應1個按鈕或者一個頁面或者一個彈層。你來定義。
如果埋點比較多,你也可以批量添加。批量添加的時候,您需要下載excel模板,按照要求填寫好,上傳進來即可。具體一看便知。
添加完成后就可以把這個列表導出或者人肉復制出來一個表格。發給研發。并附上你的原型圖。做好對應關系標注。
6、研發開發并完成APP上線
完成上面幾步后,研發哥哥就可以看懂進入第7步研發階段了。
7、在后臺查看數據
上線后就可以看到數據了。大部分數據一般隔天更新。

三、埋點后能看到什么數據

上面提到,按照步驟完成數據分析sdk集成和自定義事件后,就可以看到數據了。
不添加自定義事件,可以看到基礎數據,添加后,可以看到更細節的按鈕,頁面等點擊數據。
查看自定義事件埋點數據,還是進入剛才的“事件分析”頁面,點擊對應埋點即可看到數據。
可以篩選時間段。
除了這些外,如果你還想看 幾個頁面之間的轉化路徑和數據漏斗。那還需要添加“轉化分析”。
添加轉化分析后,可以看到例如: 進入首頁-點擊注冊按鈕進入注冊成功頁 這幾步的轉化率和流失率。會自動生成一個轉化分析圖。當然你也可以分別看這幾個頁面的數據,自己去分析匯總。
進階的方法還有把事件埋點配合轉化分析、訪問路徑、轉化漏斗等工具使用,從點到面地了解用戶的使用行為、APP存在的問題。

產品核心指標一般包含:

  1. 產品規模
    1.1 用戶數據。如新增用戶、用戶類型分布、活躍用戶、沉默用戶、啟動次數、版本分析等。
    1.2 業務數據。這個與具體業務有關,如問答社區的問題數,回答數,全網熱度,瀏覽量;如對含交易平臺的交易量,交易額,客單價,轉化率,利潤等。
  2. 產品運營
    2.1 流量數據。pv,uv,dau,mau,留存分析(次日留存,7日留存, 用戶新鮮度), 流失分析(日周月、自然流失、回歸流失),
    2.2 渠道數據。渠道流量,渠道轉換率,渠道評價,渠道時段詳情,渠道質量(渠道活躍用戶/渠道流量)等。

1.15 假如讓你實現斷點上傳功能,你認為應該怎樣去做?

分2種情況:
分塊上傳:多線程讀取本地文件指定區域流上傳,header帶有上傳位置標記,服務器接收到多個上傳請求會生成多個上傳臨時文件接收,最后合并成完整文件。(續傳如下)

正常續傳:本地請求上傳,服務器響應是否未完成的臨時文件和文件byte,本地收到就接著指定位置讀流上傳。

其中會涉及塊的數據校驗等

1.16 webp和svg格式的圖片各自有什么特點?應該如何在Android中使用?

一、
svg格式
svg是矢量圖,這意味著svg圖片由直線和曲線以及繪制它們的方法組成。當你放大一個svg圖片的時候,你看到的還是線和曲線,而不會出現像素點。svg圖片在放大時,不會失真,所以它非常適合用來繪制企業Logo、Icon

SVG格式特點:
1、SVG 指可伸縮矢量圖形 (Scalable Vector Graphics)
2、SVG 用來定義用于網絡的基于矢量的圖形
3、SVG 使用 XML 格式定義圖形
4、SVG 圖像在放大或改變尺寸的情況下其圖形質量不會有所損失
5、SVG 是萬維網聯盟的標準
6、SVG 與諸如 DOM和 XSL 之類的W3C標準是一個整體

二、
webp格式
webp是谷歌開發的一種新圖片格式,是同時支持有損和無損壓縮的、使用直接色的、點陣圖。

1.17 說說你是如何進行單元測試的?以及如何應用在MVP和MVVM中?

單元測試庫 junit mockito Rebolectric
說下mvp工程中的測試方法
測試主要有 三大部分

  1. 普通工具類 使用junit 直接測試
  2. mvp的p使用@mock標注view的接口,初始化真正的p,直接調用p的方法看看verify view的某些方法是否按照預期被調用
  3. mvp的v用rebolectric去setup一個Activity,然后用這個庫找到界面上的按鈕,或者觸發生命周期(onStart),判斷一下當前界面的某些view是否被顯示或者TextView的值或者Dialog是否顯示,Toast是否彈出錯誤
  4. 還有網絡部分的測試,可以直接使用junit進行測試,判斷下返回值是否符合預期

1.18 對于GIF圖片加載有什么思路和建議?

gif圖實際上就是多幀合并的圖

參考Fresco內部實現:
1,View層使用一個Drawable,包含bitmap,并依據gif的信息不斷的更新并繪制bitmap
2,C層提供api功能,例如:輸入gif數據流,提供解析gif信息、更新bitmap等功能

1.19 為什么要將項目遷移到AndroidX?如何進行遷移?

現在Android官方支持的最低系統版本已經是4.0.1,對應的API版本號是15。support-v4、appcompat-v7庫也不再支持那么久遠的系統了,但是它們的名字卻一直保留了下來,雖然它們現在的實際作用已經對不上當初命名的原因了。

那么很明顯,Android團隊也意識到這種命名已經非常不合適了,于是對這些API的架構進行了一次重新的劃分,推出了AndroidX。因此,AndroidX本質上其實就是對Android Support Library進行的一次升級,升級內容主要在于以下兩個方面。

第一,包名。之前Android Support Library中的API,它們的包名都是在android.support.下面的,而AndroidX庫中所有API的包名都變成了在androidx.下面。這是一個很大的變化,意味著以后凡是android.包下面的API都是隨著Android操作系統發布的,而androidx.包下面的API都是隨著擴展庫發布的,這些API基本不會依賴于操作系統的具體版本。

第二,命名規則。吸取了之前命名規則的弊端,AndroidX所有庫的命名規則里都不會再包含具體操作系統API的版本號了。比如,像appcompat-v7庫,在AndroidX中就變成了appcompat庫。

一個AndroidX完整的依賴庫格式如下所示:

implementation 'androidx.appcompat:appcompat:1.0.2'

了解了AndroidX是什么之后,現在你應該放輕松了吧?它其實并不是什么全新的東西,而是對Android SupportLibrary的一次升級。因此,AndroidX上手起來也沒有任何困難的地方,比如之前你經常使用的RecyclerView、ViewPager等等庫,在AndroidX中都會有一個對應的版本,只要改一下包名就可以完全無縫使用,用法方面基本上都沒有任何的變化。

但是有一點需要注意,AndroidX和Android Support Library中的庫是非常不建議混合在一起使用的,因為它們可能會產生很多不兼容的問題。最好的做法是,要么全部使用AndroidX中的庫,要么全部使用Android Support Library中的庫。

而現在Android團隊官方的態度也很明確,未來都會為AndroidX為主,Android Support Library已經不再建議使用,并會慢慢停止維護。另外,從Android Studio 3.4.2開始,我發現新建的項目已經強制勾選使用AndroidX架構了。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容