當你收到產品一個需求是模仿某個競品且時間很短沒有過多時間給你調研技術方案的時候,如何儘快肯定這個功能的技術方案呢? 這裏我給出我本身的一個小竅門,能夠避免走彎路,好比先肯定的方案到最後發現各類各樣的緣由方案不行,致使最後臨時變動 方案,需求延期發佈的悲劇。。。、react
比方說抖音的主界面這種滑動整屏的東西,相信以前不少人都沒作過,不少人可能覺得recyclerview能夠拿來作這種場景, 實際上recyclerview確實能夠作,可是抖音這裏能看出來倒是使用了viewpager來作。做爲一個模仿者,爲了少走彎路, 那麼顯然 咱們也是優先要選擇viewpager的。android
其次用這個軟件,還能夠看出來界面上的某個控件所屬的id。這個id很重要,後面咱們反編譯的時候能夠經過全局 搜索這個id 找到大概的源碼位置,雖然都是混淆過的代碼,可是大體仍是能分析出一些技術細節的。 這裏注意一點:不少阿里系的產品用這個方法你分析界面的時候你會發現根本沒有id能夠捕捉到,放心這並非什麼 阿里的黑科技反 反編譯技術,而是阿里使用了諸如weex react native等相似的方案,當使用這種方案的時候, 咱們是找不到id的噢,若是你大體瞭解weex react-native的實現方案的話你應該明白我在說些什麼git
反編譯就用jadx就能夠,足夠使用。在用jadx反編譯以後搜索你以前的id,就能夠大概找到對應的代碼位置,我就是經過 這個手段,肯定了抖音使用的垂直的viewpager究竟是出自於github上哪一個開源控件。。。爲了不法律糾紛我這裏就 不明說是用的哪一個開源的垂直滑動的viewpager,有興趣的同窗能夠本身玩一玩。github
adb shell dumpsys activity | grep mFocusedActivity 這個命令是神器,能夠把你當前正在顯示的activity的名字 打出來,方便你更加迅速的定位競品的代碼。。shell
不要小看這條命令哦,不少功能當你不知道競品是彈了一個popwindow仍是一個dialoagfragment仍是啓動了一個透明的activity的時候,這個方法就頗有用了,能夠避免走不少彎路。
複製代碼
抖音總體技術方案是 垂直的viewpager+下拉刷新上拉加載控件+fragment的組合方案。不論是前者仍是後者咱們都有成熟的開源方案能夠選擇,咱們只是把他合併起來而已。這第一步其實並不難。react-native
儘可能使用FragmentStatePagerAdapter。FragmentPagerAdapter不推薦使用,可能平時咱們用FragmentPagerAdapter更多, 可是要知道FragmentPagerAdapter是不會釋放內存的,你不可見的fragment也是常駐內存的,像抖音這種幾乎無限滑動的 短視頻方案,使用FragmentPagerAdapter確定是爆內存oom的。而用 FragmentStatePagerAdapter的話,對於不可見的 fragment,系統是自動釋放內存的,內存裏只會保留你能看到的fragment 和這個看到的fragment先後2個fragmnt。固然 這個值能夠動態設置,可是至少都會保留3個fragment。數組
fragmentStatePagerAdapter.notifyDataSetChanged()失效? 有時候,notifyDataSetChanged方法調用了,界面卻沒有變化?實際上對於fragmentStatePagerAdapter來講,界面刷新不刷新 重繪不重繪 主要取決於getItemPosition方法的返回值,默認是返回 POSITION_UNCHANGED 也就是不重繪。只有 返回POSITION_NONE的時候纔會重繪界面,從新繪製一遍fragemnt。 不少人不太理解什麼意思,我簡單描述一下場景:bash
好比說,咱們剛進抖音的主界面,假設返回了10條視頻數據,咱們默認播第一條。展現的是第一個fragment,對吧。 而後這個時候咱們下拉刷新又來了10條數據,咱們應該播放的是新來的這10條數據的第一條。因而你等待接口返回 之後把新來的10條數據插在了咱們的數據源這個數組的 頭部。 而後調用了notifyDataSetChanged這個方法。 若是你沒有重寫getItemPosition的方法的話,這個方法默認返回POSITION_UNCHANGED,這是fragmentStatePagerAdapter就認爲 我這個界面不須要重繪。因此你仍是在不停的播放老的視頻。 有人可能會問,咱們一直加載更多的話爲何不會出現這種狀況?weex
比方說如今的加載更多其實都是預加載,好比咱們一頁是返回10條數據,當咱們滑到第5條數據的時候 咱們可能就會自動請求 下一頁數據了。因此咱們不停的滑動viewpager 日後面滑,由於position在不停的變化,因此是不斷的有新的fragment進來的。 因此無論getItemPosition 的值如何變化,針對此種狀況咱們都會刷新界面的。性能
可是對於下拉刷新這種狀況就不行,由於咱們默認加載的是第一條數據,咱們內存裏面已經有了這一條數據了,對於position 位置爲0的fragment來講,他已經在內存裏了,等咱們下拉刷新來了新數據之後,雖然咱們調用了notifyDataSetChanged方法, 可是咱們發現這個位置爲0的fragment 內存裏已經有了啊,getItemPosition又返回了POSITION_UNCHANGED,那我就不重繪了, 這就是一個容易出bug的地方。
既然如此咱們getItemPosition固定返回POSITION_NONE行不行? 答案是不行的,固定返回POSITION_NONE這個值,雖然能夠解決下拉刷新 界面不刷新的問題,可是會引起新的問題。 主要有2: 第一,固定返回POSITION_NONE 意味着每次notifyDataSetChanged 被調用的時候,咱們內存裏存在的三個fragment 都要從新繪製 這樣的成本太大,低端手機明顯會卡,android大部分視頻播放都是軟解碼方案,這樣的性能不行。
第二:仍是上面的預加載,比方說咱們第一頁返回了10條數據,當咱們滑到第五條數據的時候,咱們預加載預先請求了第二頁的時候 而後第二頁的數據回來之後 咱們調用了notifyDataSetChanged,注意這個時候 咱們可能第五條數據對應的視頻咱們還沒看完呢,好比這條短視頻咱們纔看到第六秒,結果整個界面忽然重繪了,直接又從新 從第一秒開始播放。。這個體驗明顯不可接受。
因此咱們要作的就是 在須要的時候返回POSITION_NONE 不須要的時候返回 POSITION_UNCHANGED ,具體的邏輯能夠根據大家本身的業務進行相應的調整。
比方說咱們能夠判斷一下,若是源數據也就是mData裏面的id 和正在播放的fragmetn裏面的id 相等話,咱們就斷定不須要刷新界面 不然不相等,就刷一下,恰好對應加載更多和下拉刷新的2個場景。
如何定位內存泄漏的問題? 對於播放器來講,初次接觸的團隊若是沒有經驗,即便有b站開源播放器的幫助也會發生內存泄漏的問題,比方說咱們 繪製一個播放界面,總免不了要畫進度條,要展現倒計時,textview默認的跑馬燈效果那麼差,說不定還要本身寫個自定義view 來完成跑馬燈的特效,這些都免不了使用線程,handler,timer等等容易發生內存泄漏的東西。因此當不停的滑動的時候, 若是被滑走的fragment沒有及時被釋放掉,那上線就確定會發生oom的問題。對於android studio 3.0或者以上的版原本說:
嗯,既然mat都能讀了,剩下的就不囉嗦了,網上一搜一大堆。
這裏給出反射的實現:
public static Fragment getIndexFragment(FragmentStatePagerAdapter fragmentStatePagerAdapter, int index) {
try {
Field privateArrayList = FragmentStatePagerAdapter.class.getDeclaredField("mFragments");
privateArrayList.setAccessible(true);
ArrayList<Fragment> mFragments = (ArrayList<Fragment>) privateArrayList.get(fragmentStatePagerAdapter);
return mFragments.size() > 0 ? mFragments.get(index) : null;
} catch (NoSuchFieldException e) {
} catch (IllegalAccessException e) {
}
return null;
}
複製代碼