欧美性xxxx极品高清,九九99久久精品综合,国产乱人伦精品一区二区,gogo欢欢销魄人体

首頁 我們 服務(wù) 網(wǎng)站建設(shè) 移動應(yīng)用 案例 資訊 聯(lián)系
業(yè)務(wù)專線:15989169178

期待聆聽您的聲音

15989169178

不忽悠,不作惡,不欺詐;敬天理,存良知,思利他。
QQ咨詢 QQ咨詢 QQ咨詢
服務(wù)網(wǎng)點:廣州 深圳 佛山 粵西

與我們一起分享美好

程序員開發(fā)避坑指南(移動端篇)

發(fā)布時間:2022-07-13 發(fā)布作者:睿思設(shè)計 查閱次數(shù):1379次 標(biāo)簽:

01 NSTimer造成的內(nèi)存泄漏問題?


1.1 什么是內(nèi)存泄漏?


一個對象在引用計數(shù)變?yōu)?時,系統(tǒng)會回收內(nèi)存。如果一個本應(yīng)該被回收的內(nèi)存,沒有被回收(引用計數(shù)>0),那么就會造成內(nèi)存泄漏。


以下代碼將造成內(nèi)存泄漏:


@interface ViewController ()
@property (nonatomic, strong) NSTimer *timer;
@end

@implementation ViewController

- (void)viewDidLoad {
   [super viewDidLoad];
   self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerTest) userInfo:nil repeats:YES];
}

- (void)timerTest
{
   NSLog(@"%s", __func__);
}

// 該ViewController將不會釋放
- (void)dealloc
{
   NSLog(@"%s", __func__);
   [self.timer invalidate];
}

1.2 分析如下:


NSTimer的scheduledTimerWith


TimeInterval方法會傳進(jìn)去一個target,NSTimer內(nèi)部實現(xiàn)會有一個對象強引用傳入的對象例如(偽代碼如下,示意圖如下):

// 偽代碼@interface NSTimer ()@property (strong, nonatomic) id target;@end // 強引用該對象self.target = target


ViewController和NSTimer互相引用,此刻ViewController的引用計數(shù)為2

當(dāng)一個對象的引用計數(shù)變?yōu)?時,系統(tǒng)將回收這塊內(nèi)存。


假設(shè)對象A在某一時刻需要從內(nèi)存中釋放,那么理應(yīng)他引用的ViewController也應(yīng)該釋放,但是由于ViewController內(nèi)部的NSTimer對其有個強引用,最終導(dǎo)致ViewController不能釋放,從而導(dǎo)致內(nèi)存泄漏。如圖所示:

對象A釋放,ViewController的引用計數(shù)變?yōu)?,原本應(yīng)該引用計數(shù)變?yōu)?,從而ViewController內(nèi)存泄漏


1.3 如何解決?


按照分析,那應(yīng)該打破ViewController和NSTimer雙方的強引用。使用弱引用(弱引用不增加對象的引用計數(shù))。


方案1


使用系統(tǒng)代碼Block塊的方法破除循環(huán)引用

- (void)viewDidLoad {    [super viewDidLoad];        __weak typedef(self) weakSelf = self;    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {        [weakSelf timerTest];    }];}

NSTimer弱引用ViewController,在ViewController釋放時,NSTimer也獲得釋放,循環(huán)鏈條斷開


方案2


使用中間代理層來解決循環(huán)引用

// 代理類@interface Proxy : NSObject+ (instancetype)proxyWithTarget:(id)target;// 弱引用target@property (weak, nonatomic) id target;@end@implementation Proxy+ (instancetype)proxyWithTarget:(id)target {    Proxy *proxy = [[MJProxy1 alloc] init];    proxy.target = target;    return proxy;}- (id)forwardingTargetForSelector:(SEL)aSelector {    return self.target;}@end@interface ViewController ()@property (nonatomic, strong) NSTimer *timer;@end@implementation ViewController- (void)viewDidLoad {    [super viewDidLoad];    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:[Proxy proxyWithTarget:self] selector:@selector(timerTest) userInfo:nil repeats:YES];}- (void)timerTest{    NSLog(@"%s", __func__);}// 該ViewController將不會釋放- (void)dealloc{    NSLog(@"%s", __func__);    [self.timer invalidate];}

如下圖所示,ViewController需要強引用NSTimer,NSTimer內(nèi)部需要強引用一個target對象,所以可以創(chuàng)建一個代理類來處理這個問題,所以proxy內(nèi)部有一個弱引用的target對象,ViewController調(diào)用proxyWithTarget把self傳入時不會強持有self。


三方之間沒有循環(huán)引用,最終可以釋放對象


02 淺析Android的焦點機制


焦點是一個很寬泛的概念,中文釋義是比喻問題的關(guān)鍵所在或爭論的集中點,在物理學(xué)、數(shù)學(xué)、生活中都有廣泛的使用。那么Android中的焦點是什么呢?


2.1 Android焦點概念


焦點在Android中也就是Focus,稱為Focus機制。focus在英文中的釋義是:

"the main or central point of something, especially of attention or interest",和中文語義相同。


回到我們Android開發(fā)中,我們手機屏幕可以同時顯示多種多樣的內(nèi)容,那么你的焦點或者說你的注意力在哪個內(nèi)容上?系統(tǒng)又該如何判斷呢?舉個例子,當(dāng)屏幕界面中同時存在多個EditText(輸入框)時,你的鍵盤輸入會顯示在哪個輸入框內(nèi)呢?亦或是同時顯示在所有輸入框中?這顯然是不合理的,而這時焦點機制就體現(xiàn)了它的意義。對于EditText控件來說,獲取到焦點,則意味著激活了和用戶的交互,鍵盤輸入的內(nèi)容會輸入到這個EditText上面。


2.2 焦點處理


焦點的處理包含獲取焦點、分發(fā)焦點、清除焦點等。


2.2.1 獲取焦點


讓一個View獲取焦點直接調(diào)用View#requestFocus方法,最終會調(diào)用到View#requestFocusNoSearch方法,其通過多個條件判斷該View是否允許獲取焦點,包括是否可見、是否可獲取焦點、是否可用,以及在觸屏設(shè)備中是否允許獲取焦點等。

private boolean requestFocusNoSearch(int direction, Rect previouslyFocusedRect) {
       // need to be focusable
       if (!canTakeFocus()) {
           return false;
       }
       // need to be focusable in touch mode if in touch mode
       if (isInTouchMode() &&
           (FOCUSABLE_IN_TOUCH_MODE != (mViewFlags & FOCUSABLE_IN_TOUCH_MODE))) {
              return false;
       }
       // need to not have any parents blocking us
       if (hasAncestorThatBlocksDescendantFocus()) {
           return false;
       }
       if (!isLayoutValid()) {
           mPrivateFlags |= PFLAG_WANTS_FOCUS;
       } else {
           clearParentsWantFocus();
       }
       handleFocusGainInternal(direction, previouslyFocusedRect);
       return true;
   }


2.2.2 獲取焦點的模式


獲取焦點有兩種模式,分別是:

普通模式(focusable):允許有普通獲取焦點的能力(比如物理鍵、電視、手表等非觸摸的輸入方式)


觸摸模式(focusableInTouchMode):允許有觸摸獲取焦點的能力。


需要注意的是,在設(shè)置允許觸摸模式時會默認(rèn)開啟普通模式,注意同時設(shè)置這兩個屬性時不要沖突。


并且由此我們可以得到一條關(guān)于焦點的特性:


  • 并不是所有View都可以獲取焦點。獲取焦點的前提是視圖必需要有獲取焦點的資格。


2.2.3 分發(fā)焦點


上述View在獲取焦點時,需要逐級通知它的父View進(jìn)行焦點處理,清除舊焦點信息并保存新焦點信息,參見ViewGroup#requestChildFocus。


通過ViewGroup中mFocused(View類型)這個成員來保存具有焦點的子View,并且一直遞歸下去,為父View判斷是否包含焦點(hasFocus)和查找焦點(findFocus)提供了便利。


舉例:某個根View A包含B、C兩個子View,C下又包含C1、C2兩個子View,且C2具有焦點,則C中mFocused保存的是C2,根View A中mFocused保存的則是C。


另外ViewGroup也可以獲取焦點,參見ViewGroup#requestFocus,與View獲取焦點邏輯不同,ViewGroup獲取焦點受策略控制,如下:

FOCUS_BLOCK_DESCENDANTS:This view will block any of its descendants from getting focus, even if they are focusable.

FOCUS_BEFORE_DESCENDANTS:This view will get focus before any of its descendants.

FOCUS_AFTER_DESCENDANTS:This view will get focus only if none of its descendants want it.


public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
       // ...省略
       int descendantFocusability = getDescendantFocusability();
       boolean result;
       switch (descendantFocusability) {
           case FOCUS_BLOCK_DESCENDANTS:
               result = super.requestFocus(direction, previouslyFocusedRect);
               break;
           case FOCUS_BEFORE_DESCENDANTS: {
               final boolean took = super.requestFocus(direction, previouslyFocusedRect);
               result = took ? took : onRequestFocusInDescendants(direction,
                       previouslyFocusedRect);
               break;
           }
           case FOCUS_AFTER_DESCENDANTS: {
               final boolean took = onRequestFocusInDescendants(direction, previouslyFocusedRect);
               result = took ? took : super.requestFocus(direction, previouslyFocusedRect);
               break;
           }
           default:
              // ...省略
       }
       if (result && !isLayoutValid() && ((mPrivateFlags & PFLAG_WANTS_FOCUS) == 0)) {
           mPrivateFlags |= PFLAG_WANTS_FOCUS;
       }
       return result;
   }


由此我們也能得到另一些關(guān)于焦點的特性:


  • 一個窗口內(nèi)最多只有一個View具有焦點,或者無焦點。上述在遞歸分發(fā)焦點時,當(dāng)有View獲取焦點后則會退出遞歸。

  • 根View沒有焦點不能說明子View一定沒有焦點。子View具有焦點,根View能夠感知。


2.2.4 清除焦點


需要我們主動清除焦點的場景其實較少,我們可以通過clearFocus來清除焦點,View和ViewGroup的清除邏輯有細(xì)微差異,ViewGroup會同時清除上訴分發(fā)焦點過程中所記錄的狀態(tài)(需區(qū)分當(dāng)前焦點是自己還是子View),最終都會調(diào)用View#clearFocusInternal進(jìn)行真正的清除操作,后面會繼續(xù)提到焦點清除的問題。


/**
    * Clears focus from the view, optionally propagating the change up through
    * the parent hierarchy and requesting that the root view place new focus.
    *
    * @param propagate whether to propagate the change up through the parent
    *            hierarchy
    * @param refocus when propagate is true, specifies whether to request the
    *            root view place new focus
    */
   void clearFocusInternal(View focused, boolean propagate, boolean refocus) {
       if ((mPrivateFlags & PFLAG_FOCUSED) != 0) {
           mPrivateFlags &= ~PFLAG_FOCUSED;
           clearParentsWantFocus();
           if (propagate && mParent != null) {
               mParent.clearChildFocus(this);
           }
           onFocusChanged(false, 0, null);
           refreshDrawableState();
           if (propagate && (!refocus || !rootViewRequestFocus())) {
               notifyGlobalFocusCleared(this);
           }
       }
   }


問題1:錯誤啟用獲取焦點能力導(dǎo)致點擊失效


以EditText為例,我們在點擊時即會獲取焦點,輸入框中會顯示光標(biāo),彈出輸入法等。但像Button、TextView等控件,默認(rèn)觸摸不會獲取焦點,如果對此類控件設(shè)置了focusableInTouchMode=true,就會發(fā)現(xiàn)第一次觸摸無法響應(yīng)點擊事件,第二次點擊才會響應(yīng),這是為什么呢?從事件分發(fā)機制中尋找線索,看View#onTouchEvent中對MotionEvent.ACTION_UP的處理,可以清晰看到UP事件的處理會優(yōu)先處理焦點獲取,只有在無焦點變化時才會如我們所想的開始分發(fā)點擊事件。所以我們在第一次點擊時收到的是onFocusChange事件,第二次點擊收到的才是onClick事件。


public boolean onTouchEvent(MotionEvent event) {
       // ...省略
       switch (action) {
           case MotionEvent.ACTION_UP:
               // ...省略
               // take focus if we don't have it already and we should in
               // touch mode.
               boolean focusTaken = false;
               if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
                   focusTaken = requestFocus();
               }
               // ...省略
               if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
                   // Only perform take click actions if we were in the pressed state
                   if (!focusTaken) {
                       // Use a Runnable and post this rather than calling
                       // performClick directly. This lets other visual state
                       // of the view update before click actions start.
                       if (mPerformClick == null) {
                           mPerformClick = new PerformClick();
                       }
                       if (!post(mPerformClick)) {
                           performClickInternal();
                       }
                   }
               }
       }
   }


問題2:clearFocus“無效”?


在之前我們了解了清除焦點的機制,但為什么有時候會碰到調(diào)用clearFocus時"無效"呢?我們對比一下我們可以主動調(diào)用的clearFocus方法和系統(tǒng)內(nèi)部調(diào)用的unFocus方法。


void unFocus(View focused) {
       clearFocusInternal(focused, false, false);
   }


發(fā)現(xiàn)一處可疑點,propagate和refocus的值決定了rootViewRequestFocus是否被調(diào)用,由于&&和||的短路作用,當(dāng)propagate和refocus均為true時,才會執(zhí)行rootViewRequestFocus,而在rootViewRequestFocus中會觸發(fā)root的獲取焦點邏輯。


boolean rootViewRequestFocus() {
       final View root = getRootView();
       return root != null && root.requestFocus();
   }


因此clearFocus看似“無效”,其實是焦點被清除后又立馬被設(shè)置上了。那該如何解決呢?回顧之前提到的焦點分發(fā)邏輯,當(dāng)父View搶先獲取了焦點就能夠解決,因此,讓父view自動獲取焦點是很好的解決方法。這里我們可以回憶上面分發(fā)焦點中所提及的三種焦點分發(fā)策略,我們希望父View先于子View獲取焦點,很明顯這符合FOCUS_BEFORE_DESCENDANTS策略,但我們好像并沒有手動配置過這個策略,那FOCUS_BEFORE_DESCENDANTS策略是否是ViewGroup的默認(rèn)策略呢?我們查看ViewGroup源碼發(fā)現(xiàn)在initViewGroup中確實有默認(rèn)的設(shè)置,如下:


private void initViewGroup() {
       // ...省略
       setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS);
       // ...省略
   }


舉一反三,如果我們想讓子View先于父View獲取焦點或者禁止子View獲取焦點,即可通過setDescendantFocusability方法來設(shè)置。


另外感興趣的同學(xué)可以繼續(xù)探究refocus的取值邏輯。


問題3:焦點搶占


在問題2中,我們通過焦點搶占解決了一些問題,但有時候View錯誤的獲取焦點會帶來一些意料外的問題。比如EditText自動獲取了焦點導(dǎo)致自動彈起輸入法。又比如RecyclerView在嵌套時子View搶占了焦點導(dǎo)致列表發(fā)生預(yù)期外的移動等等,這是個有趣的問題,感興趣的同學(xué)可以查看RecyclerView#requestChildFocus方法,其中執(zhí)行的requestChildRectangleOnScreen方法會為你解決這個疑惑。碰到這些問題時,我們可以考慮禁止不需要獲取焦點的View的焦點獲取能力,或者讓其父View先獲取焦點來解決問題。


2.3 總結(jié)


Android中的焦點機制是一個很有趣的內(nèi)容,很多疑難問題的答案都藏在源碼中,理解了焦點的機制后,相關(guān)問題都將變得有跡可循。


03 Android中Cookie


3.1 首先什么是Cookie:


Cookie是服務(wù)器保存在瀏覽器的一小段文本信息,每個 Cookie 的大小一般不能超過4KB。瀏覽器每次向服務(wù)器發(fā)出請求,就會自動附上這段信息。


3.2 Webview的Cookie存儲:


WebView是基于 webkit 內(nèi)核的UI控件,相當(dāng)于一個瀏覽器客戶端。


它會在本地維護(hù)每次會話的cookie( 保存在 data/data/package_name/app_WebView/Cookies.db )



導(dǎo)出后可見:



3.3 Cookie屬性:


Set-Cookie:name=value [ ;expires=date][ ;max-age=time][ ;domain=domain][ ;path=path][ ;secure][ ;httponly]



例:

Set-Cookie: TEST=1234567890; Expires=Wed, 21 Oct 2022 07:28:00 GMT; Domain=baidu.com; Path=/test;Secure; HttpOnly


2.4 Cookie的設(shè)置


Android中的WebKit為我們提供了CookieManager,它是一個單例,我們可以利用它進(jìn)行Cookie的讀取和存儲,例如

CookieManager.getInstance().setCookie(url, cookie); CookieManager.getInstance().getCookie(url);


2.5 Cookie在請求中攜帶:



2.5.1 Request的Header:


WebView中H5的請求:

在WebView的H5中發(fā)送請求時,同瀏覽器一樣,每次向服務(wù)器發(fā)出請求(domain&path與cookie中設(shè)置一致),就會自動附上這段信息。


客戶端Native發(fā)請求:


由客戶端發(fā)送,包含在HTTP請求的頭部中。注意,Native發(fā)送請求時,需要網(wǎng)絡(luò)庫主動addHeader,所以建議封裝網(wǎng)絡(luò)庫時,Native仿照瀏覽器自動攜帶Cookie的機制。如:

// 簡單寫了個意思,具體實現(xiàn)需要遍歷拼接等判斷,大家明白就好CookieManager cookieManager = CookieManager.getInstance();String webviewCookies = cookieManager.getCookie(url);httpURLConnection.setRequestProperty("Cookie", webviewCookies);


2.5.2 Response的Set-Header:


WebView中H5的請求響應(yīng):

在WebView的H5中接收到服務(wù)端響應(yīng)時,同瀏覽器一樣,會響應(yīng)response的set-header自動為內(nèi)核種上cookie。


客戶端Native請求響應(yīng):


由客戶端接收到response后,需要注意的是系統(tǒng)并不會自動為內(nèi)核種上cookie,建議封裝網(wǎng)絡(luò)庫時,Native仿照瀏覽器響應(yīng)response的set-header自動為內(nèi)核種上Cookie。如:

// 簡單寫了個意思,具體實現(xiàn)需要添加安全性的判斷,大家明白就好
Map> responseHeaderMap = httpURLConnection.getHeaderFields();
List cookieList = responseHeaderMap.get("Set-Cookie");
CookieSyncManager.createInstance(context);
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.setAcceptCookie(true);
for (String cookie : cookieList) {
   List httpCookieList = HttpCookie.parse(cookie);
   HttpCookie httpCookie = httpCookieList.get(0);
   String relCookie = buildCookie(httpCookie.getDomain(), httpCookie.getName(),
           httpCookie.getValue(), System.currentTimeMillis() + httpCookie.getMaxAge() * 1000,
           httpCookie.getSecure());
   cookieManager.setCookie(domain, relCookie);
}


其他額外知識:

Cookie多進(jìn)程使用及同步:https://iluhcm.com/2018/04/27/android-cookie-sync-between-multiprocess/


本文轉(zhuǎn)自 百度Geek說

哪些后端框架正在影響Web應(yīng)用程序開發(fā)

小程序開源業(yè)務(wù)架構(gòu)是怎樣做的

我們的位置

廣州 廣州市黃埔區(qū)科學(xué)城科學(xué)大道18號芯大廈 159 8916 9178

深圳 深圳市南山區(qū)大沖國際中心九樓 159 1543 2684

粵西 茂名市茂南區(qū)油城三路粵西創(chuàng)業(yè)創(chuàng)新孵化基地B110 157 6767 8148

我們的服務(wù)

網(wǎng)站及移動應(yīng)用 高端品牌網(wǎng)站 APP開發(fā) 小程序開發(fā) 微信運營

系統(tǒng)應(yīng)用開發(fā) OA/ERP/CRM/HR系統(tǒng)開發(fā) 教學(xué)管理系統(tǒng) 電商系統(tǒng) 應(yīng)用型軟件系統(tǒng)定制開發(fā)

了解我們

公司簡介 聯(lián)系我們 我們的案例 新聞資訊

使用條款 隱私聲明 Cookies

© 2009-2025 廣州睿網(wǎng)信息科技有限公司 版權(quán)所有 粵ICP備16051058號