知識(shí)點(diǎn)積累

https://juejin.im/entry/5aa69dc851882555602093b2

  1. 什么是自動(dòng)拆裝包授霸?
    http://blog.csdn.net/u012542422/article/details/50721701
    Java有8種基本類型,每種基本類型又有對(duì)應(yīng)的包裝類型际插。
    在Java中碘耳,一切都以對(duì)象作為基礎(chǔ),但是基本類型并不是對(duì)象框弛,
    如果想以對(duì)象的方式使用這8中基本類型辛辨,可以將它們轉(zhuǎn)換為對(duì)應(yīng)的包裝類型
    轉(zhuǎn)換工作由編譯器來完成
    (1)當(dāng)需要一個(gè)對(duì)象的時(shí)候會(huì)自動(dòng)裝箱,比如Integer a = 10;equals(Object o)方法的參數(shù)是Object對(duì)象瑟枫,所以需要裝箱斗搞。
    (2)當(dāng)需要一個(gè)基本類型時(shí)會(huì)自動(dòng)拆箱,比如int a = new Integer(10);算術(shù)運(yùn)算是在基本類型間進(jìn)行的慷妙,所以當(dāng)遇到算術(shù)運(yùn)算時(shí)會(huì)自動(dòng)拆箱僻焚,比如代碼中的 c == (a + b);
    (3) 包裝類型 == 基本類型時(shí),包裝類型自動(dòng)拆箱膝擂;
  2. Java的作用域修飾詞有哪些虑啤?他們的作用是什么?(不要忘記default架馋,default可以理解成包級(jí)別的作用域修飾符)


    這里寫圖片描述

    Android基礎(chǔ)知識(shí)

Activity的生命周期狞山。啟動(dòng)模式。

https://www.cnblogs.com/lwbqqyumidi/p/3769113.html
https://mp.weixin.qq.com/s?__biz=MzIwMzYwMTk1NA%3D%3D&mid=2247488588&idx=1&sn=3f7c59654835ec8d560610ba97d10fc0
在實(shí)際應(yīng)用場(chǎng)景中叉寂,假設(shè)A Activity位于棧頂萍启,此時(shí)用戶操作,從A Activity跳轉(zhuǎn)到B Activity屏鳍。那么對(duì)AB來說勘纯,具體會(huì)回調(diào)哪些生命周期中的方法呢?回調(diào)方法的具體回調(diào)順序又是怎么樣的呢钓瞭?

開始時(shí)驳遵,A被實(shí)例化是复,執(zhí)行的回調(diào)有A:onCreate -> A:onStart -> A:onResume产捞。

當(dāng)用戶點(diǎn)擊A中按鈕來到B時(shí),假設(shè)B全部遮擋住了A洼畅,將依次執(zhí)行A:onPause -> B:onCreate -> B:onStart -> B:onResume -> A:onStop佳鳖。

此時(shí)如果點(diǎn)擊Back鍵霍殴,將依次執(zhí)行B:onPause -> A:onRestart -> A:onStart -> A:onResume -> B:onStop -> B:onDestroy。

至此系吩,Activity棧中只有A来庭。在Android中,有兩個(gè)按鍵在影響Activity生命周期這塊需要格外區(qū)分下穿挨,即Back鍵和Home鍵月弛。我們先直接看下實(shí)驗(yàn)結(jié)果:

此時(shí)如果按下Back鍵肴盏,系統(tǒng)返回到桌面,并依次執(zhí)行A:onPause -> A:onStop -> A:onDestroy帽衙。

此時(shí)如果按下Home鍵(非長按)菜皂,系統(tǒng)返回到桌面,并依次執(zhí)行A:onPause -> A:onStop厉萝。由此可見恍飘,Back鍵和Home鍵主要區(qū)別在于是否會(huì)執(zhí)行onDestroy。
橫豎屏切換(Nexus x5 API 26 Android 7.0模擬器)
1谴垫、不設(shè)置Activity的Android:configChanges時(shí)章母,切屏?xí)匦抡{(diào)用各個(gè)生命周期,不會(huì)調(diào)用onConfigurationChanged()
,但是會(huì)調(diào)用onSaveInstanceState(Bundle outState)
onPause
onSaveInstanceState
onStop
onDestroy
onCreate
onStart
onRestoreInstanceState
onResume
其中onSaveInstanceState與onCreate與onRestoreInstanceState的Bundle參數(shù)為同一對(duì)象.重新切換,Bundle對(duì)象會(huì)重新創(chuàng)建
2、設(shè)置Activity的android:configChanges=”orientation|keyboardHidden|screenSize”時(shí)翩剪,切屏不會(huì)重新調(diào)用各個(gè)生命周期乳怎,只會(huì)執(zhí)行onConfigurationChanged方法,不會(huì)調(diào)用onSaveInstanceState

singleTop
棧頂復(fù)用模式,如果要開啟的activity在任務(wù)棧的頂部已經(jīng)存在前弯,就不會(huì)創(chuàng)建新的實(shí)例蚪缀,而是調(diào)用 onNewIntent() 方法。避免棧頂?shù)腶ctivity被重復(fù)的創(chuàng)建博杖。應(yīng)用場(chǎng)景:在通知欄點(diǎn)擊收到的通知椿胯,然后需要啟動(dòng)一個(gè)Activity,這個(gè)Activity就可以用singleTop剃根,否則每次點(diǎn)擊都會(huì)新建一個(gè)Activity。
singleTask
棧內(nèi)復(fù)用模式前方, activity只會(huì)在任務(wù)棧里面存在一個(gè)實(shí)例狈醉。應(yīng)用場(chǎng)景:大多數(shù)App的主頁。
singleInstance
單一實(shí)例模式惠险,整個(gè)手機(jī)操作系統(tǒng)里面只有一個(gè)實(shí)例存在苗傅。不同的應(yīng)用去打開這個(gè)activity 共享公用的同一個(gè)activity。他會(huì)運(yùn)行在自己?jiǎn)为?dú)班巩,獨(dú)立的任務(wù)棧里面渣慕,并且任務(wù)棧里面只有他一個(gè)實(shí)例存在。應(yīng)用場(chǎng)景:呼叫來電界面抱慌。
FLAG_ACTIVITY_NEW_TASK
使用一個(gè)新的Task來啟動(dòng)一個(gè)Activity逊桦,但啟動(dòng)的每個(gè)Activity都講在一個(gè)新的Task中。該Flag通常使用在從Service中啟動(dòng)Activity的場(chǎng)景抑进,由于Service中并不存在Activity棧强经,所以使用該Flag來創(chuàng)建一個(gè)新的Activity棧,并創(chuàng)建新的Activity實(shí)例寺渗。

FLAG_ACTIVITY_SINGLE_TOP
使用singletop模式啟動(dòng)一個(gè)Activity匿情,與指定android:launchMode=“singleTop”效果相同兰迫。

FLAG_ACTIVITY_CLEAR_TOP
使用SingleTask模式來啟動(dòng)一個(gè)Activity,與指定android:launchMode=“singleTask”效果相同炬称。

FLAG_ACTIVITY_NO_HISTORY
Activity使用這種模式啟動(dòng)Activity汁果,當(dāng)該Activity啟動(dòng)其他Activity后,該Activity就消失了玲躯,不會(huì)保留在Activity棧中据德。

Service的生命周期,和兩種啟動(dòng)方式府蔗。

bindService(intent,ServiceConnection ,BIND_AUTO_CREATE);
onCreate() ——> onBind() ——> Service running ——> onUnbind() ——> onDestroy() ——> Service stop
startService
onCreate()——> onStartCommand()——> Service running ——> onDestroy() ——> Service stop

Fragemnt的生命周期和使用場(chǎng)景晋控。

http://www.jb51.net/article/80040.htm
onAttach->onCreate->onCreateView->onActivityCreate->onStart->onResume->
onPause->onStop->onDestroyView_Fragment->onDestroy_Fragment->onDetach_Fragment

  • FragmentPagerAdapter保留所有fragment
  • 在destroyItem的時(shí)候
    FragmentStatePagerAdapter會(huì)調(diào)用remove
    onPause->onStop->onDestroyView->onDestroy->onDetach
    恢復(fù)onAttach->onCreate->onCreateView
    而FragmentPagerAdapter會(huì)調(diào)用detach
    onPause->onStop->onDestroyView
    恢復(fù)onCreateView

BroadCastReciever的兩種注冊(cè)方法

http://www.reibang.com/p/ca3d87a4cdf3

  1. 靜態(tài)注冊(cè):在AndroidManifest文件中注冊(cè)BroadcastReciever。不需要應(yīng)用啟動(dòng)姓赤,就可以在廣播傳來時(shí)自動(dòng)啟動(dòng)應(yīng)用赡译,接收、處理廣播不铆。

  2. 動(dòng)態(tài)注冊(cè):在項(xiàng)目中使用代碼注冊(cè)BroadcastReciever蝌焚。需要應(yīng)用啟動(dòng),運(yùn)行注冊(cè)代碼誓斥,才可以接收只洒、處理廣播;應(yīng)用退出時(shí)劳坑,要解除注冊(cè)毕谴,否則會(huì)報(bào)異常。
    對(duì)于LocalBroadcastManager方式發(fā)送的應(yīng)用內(nèi)廣播距芬,只能通過LocalBroadcastManager動(dòng)態(tài)注冊(cè)涝开,不能靜態(tài)注冊(cè)

ContentProvider的基本使用方法和作用。ContentValue的使用方法框仔,他和HashMap的區(qū)別是什么舀武?

  1. ContentProvider一般為存儲(chǔ)和獲取數(shù)據(jù)提供統(tǒng)一的接口,可以在不同的應(yīng)用程序之間共享數(shù)據(jù)离斩。
    URI

Authority:授權(quán)信息银舱,用以區(qū)別不同的ContentProvider;
Path:表名跛梗,用以區(qū)分ContentProvider中不同的數(shù)據(jù)表寻馏;
Id:Id號(hào),用以區(qū)別表中的不同數(shù)據(jù)茄袖;
getContentResolver()來獲得ContentResolver操软。

  • ContentValues 和HashTable類似都是一種存儲(chǔ)的機(jī)制 但是兩者最大的區(qū)別就在于,contenvalues Key只能是String類型宪祥,values只能存儲(chǔ)基本類型的數(shù)據(jù)聂薪,像string家乘,int之類的,不能存儲(chǔ)對(duì)象這種東西藏澳。ContentValues 常用在數(shù)據(jù)庫中的操作仁锯。
  • HashMap不是線程安全的,HashTable是線程安全的一個(gè)Collection翔悠。HashMap允許將null作為一個(gè)entry的key或者value业崖,而Hashtable不允許。

SharedPreference三種獲得方法和區(qū)別蓄愁,commit和apply的區(qū)別双炕。

  1. public SharedPreferences getPreferences (int mode)

通過Activity對(duì)象獲取,獲取的是本Activity私有的Preference撮抓,保存在系統(tǒng)中的xml形式的文件的名稱為這個(gè)Activity的名字妇斤,因此一個(gè)Activity只能有一個(gè),屬于這個(gè)Activity丹拯。

  1. public SharedPreferences getSharedPreferences (String name, int mode)

因?yàn)锳ctivity繼承了ContextWrapper站超,因此也是通過Activity對(duì)象獲取,但是屬于整個(gè)應(yīng)用程序乖酬,可以有多個(gè)死相,以第一參數(shù)的name為文件名保存在系統(tǒng)中。

  1. public static SharedPreferences getDefaultSharedPreferences (Context context)
    PreferenceManager的靜態(tài)函數(shù)咬像,保存PreferenceActivity中的設(shè)置算撮,屬于整個(gè)應(yīng)用程序,但是只有一個(gè)县昂,Android會(huì)根據(jù)包名和PreferenceActivity的布局文件來起一個(gè)名字保存钮惠。
  • commit和apply雖然都是原子性操作,但是原子的操作不同七芭,commit是原子提交到數(shù)據(jù)庫,所以從提交數(shù)據(jù)到存在Disk中都是同步過程蔑赘,中間不可打斷狸驳。

  • 而apply方法的原子操作是原子提交的內(nèi)存中,而非數(shù)據(jù)庫缩赛,所以在提交到內(nèi)存中時(shí)不可打斷耙箍,之后再異步提交數(shù)據(jù)到數(shù)據(jù)庫中,因此也不會(huì)有相應(yīng)的返回值酥馍。

  • 所有commit提交是同步過程辩昆,效率會(huì)比apply異步提交的速度慢,但是apply沒有返回值旨袒,永遠(yuǎn)無法知道存儲(chǔ)是否失敗汁针。

  • 在不關(guān)心提交結(jié)果是否成功的情況下术辐,優(yōu)先考慮apply方法。

Android執(zhí)行異步有哪些方法施无?線程間通訊的方式辉词?

http://blog.csdn.net/u011803341/article/details/52774867
thread
AsyncTask(源碼研究)
handler
rxjava

View的繪制流程?

measure: 判斷是否需要重新計(jì)算View的大小猾骡,需要的話則計(jì)算瑞躺;
layout: 判斷是否需要重新計(jì)算View的位置,需要的話則計(jì)算兴想;
draw: 判斷是否需要重新繪制View幢哨,需要的話則重繪制。
http://www.cnblogs.com/jycboy/p/6066654.html
mLayoutInflater.inflate->ViewRootImpl.requestLayout->performTraversals()

private void performTraversals() {
        // ... ...
        // Ask host how big it wants to be
        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
        // ... ...
        performLayout(lp, mWidth, mHeight);
        // ... ...
        performDraw();
    }
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
        try {
            mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
         // 調(diào)用onMeasure()
         onMeasure(widthMeasureSpec, heightMeasureSpec); 
    }

頂層父View向子View的遞歸調(diào)用view.onMeasure

private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
            int desiredWindowHeight) {
        final View host = mView;
        // 調(diào)用layout()方法
        host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
    }

    public void layout(int l, int t, int r, int b) {
        onLayout(changed, l, t, r, b);
    }

從頂層父View向子View的遞歸調(diào)用view.layout方法的過程

private void performDraw() {
        try {
            draw(fullRedrawNeeded);
        } finally {
            mIsDrawing = false;
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }

    private void draw(boolean fullRedrawNeeded) {
        Surface surface = mSurface;
        if (!surface.isValid()) {
            return;
        }
        
        if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
            return;
        }
    }

    private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
            boolean scalingRequired, Rect dirty) {
        // Draw with software renderer.
        final Canvas canvas;
        final int left = dirty.left;
        final int top = dirty.top;
        final int right = dirty.right;
        final int bottom = dirty.bottom;
        canvas = mSurface.lockCanvas(dirty);
        // ... ...
        mView.draw(canvas);
    }

View嫂便,SurfaceView捞镰,GLSurfaceView有什么區(qū)別?

這三個(gè)都是畫布顽悼,展示UI

   View:顯示視圖曼振,內(nèi)置畫布,提供圖形繪制函數(shù)蔚龙、觸屏事件冰评、按鍵事件函數(shù)等;必須在UI主線程內(nèi)更新畫面木羹,速度較慢甲雅。

SurfaceView:基于view視圖進(jìn)行拓展的視圖類,更適合2D游戲的開發(fā)坑填;是view的子類抛人,類似使用雙緩機(jī)制,在新的線程中更新畫面所以刷新界面速度比view快脐瑰。

GLSurfaceView:基于SurfaceView視圖再次進(jìn)行拓展的視圖類妖枚,專用于3D游戲開發(fā)的視圖;是SurfaceView的子類苍在,openGL專用绝页。

SurfaceView和View最本質(zhì)的區(qū)別在于,surfaceView是在一個(gè)新起的單獨(dú)線程中可以重新繪制畫面而View必須在UI的主線程中更新畫面

那么在UI的主線程中更新畫面 可能會(huì)引發(fā)問題寂恬,比如你更新畫面的時(shí)間過長续誉,那么你的主UI線程會(huì)被你正在畫的函數(shù)阻塞。那么將無法響應(yīng)按鍵初肉,觸屏等消息酷鸦。

當(dāng)使用surfaceView 由于是在新的線程中更新畫面所以不會(huì)阻塞你的UI主線程。但這也帶來了另外一個(gè)問題,就是事件同步臼隔。比如你觸屏了一下嘹裂,你需要surfaceView中 thread處理,一般就需要有一個(gè)eventqueue(時(shí)間隊(duì)列)的設(shè)計(jì)來保存touch event躬翁,這會(huì)稍稍復(fù)雜一點(diǎn)焦蘑,因?yàn)樯婕暗骄€程同步。

所以基于以上盒发,根據(jù)不同的時(shí)間需求例嘱,一般分成兩類。
  1 被動(dòng)更新畫面的宁舰。這種用view就好了拼卵。因?yàn)楫嬅娴母率且蕾囉?onTouch 來更新,可以直接使用 invalidate()蛮艰。 因?yàn)檫@種情況下腋腮,這一次Touch和下一次的Touch需要的時(shí)間比較長些,不會(huì)產(chǎn)生影響壤蚜。
  2 主動(dòng)更新即寡。比如一個(gè)人在一直跑動(dòng)。這就需要一個(gè)單獨(dú)的thread不停的重繪人的狀態(tài)袜刷,避免阻塞main UI thread聪富。所以顯然view不合適,需要surfaceView來控制著蟹。
  一般2D游戲開發(fā)使用SurfaceView足夠墩蔓,因?yàn)樗彩莋oogle專們擴(kuò)展用于2D游戲開發(fā)的畫布
  使用普通的游戲畫布(Android中2D專用游戲畫布)中進(jìn)行繪制圖片,然后在GLSurfaceView(Android中3D游戲?qū)S卯嫴迹┲袖秩緢D片的對(duì)比中發(fā)現(xiàn)GLSurfaceView的效率高于SurfaceView的30倍萧豆;GLSurfaceView的效率主要是因?yàn)闄C(jī)器硬件的GPU加速奸披,現(xiàn)在flash技術(shù)也有了GPU加速技術(shù);
  下面總結(jié)一下:
  一般2D游戲使用SurfaceView足夠涮雷,所以不要認(rèn)為什么都要使用GLSurfaceView(openGL)阵面,而且 GLSurfaceView的弊端在于適配能力差,因?yàn)楹芏鄼C(jī)型中是沒有GPU加速的洪鸭。

WebView的基本使用方法膜钓。WebViewClient和WebChromeClient。

http://blog.csdn.net/lanxingfeifei/article/details/52045082
WebViewClient類與WebChromClient兩個(gè)類在android開發(fā)的過程中卿嘲,主要是在使用WebView這個(gè)組件的時(shí)候,可能會(huì)使用到夫壁。那么這兩個(gè)類到底有什么不同之處呢拾枣?

WebViewClient 這個(gè)類主要幫助WebView處理各種通知、請(qǐng)求時(shí)間的,比如:

onLoadResource
onPageStart
onPageFinish
onReceiveError
onReceivedHttpAuthRequest

WebChromeClient主要輔助WebView處理JavaScript的對(duì)話框梅肤、網(wǎng)站圖片司蔬、網(wǎng)站title、加載進(jìn)度等比如

onCloseWindow(關(guān)閉WebView)
onCreateWindow()
onJsAlert(WebView上alert無效姨蝴,需要定制WebChromeClient處理彈出)
onJsConfirm
onProgressChanged
onReceivedIcon
onReceivedTitle

Android和H5通信俊啼。(基本上就是JS和Android原生互調(diào))

  • Android調(diào)用JS
ws.setJavaScriptEnable(True);
webView.loadUrl(url);
webView.loadUrl("javascript:changeTitle('Android調(diào)用js')");
  • JS調(diào)用Android
ws.setJavaScriptEnabled(true);
//給webView添加JS接口類, 該類封裝了原生的操作. 參數(shù)2是JS中的實(shí)體類名字,需要和js代碼中名字保持一致
webView.addJavascriptInterface(new JsInterface(),"js2android");
public class JsInterface{
  @JavascriptInterface  //這個(gè)注解一定要帶上
  public void selectPic(){
    Intent intent = new Intent(WebViewActivity.this,GetPicActivity.class);
    startActivityForResult(intent,100);
  }
}
function appSelectPic(){
  javascript:js2android.selectPic();
}

Android的屏幕適配方法有哪些?

  • Linearlayout 權(quán)重左医,Relativelayout
  • 設(shè)置不同分辨率的dimen,多套資源文件
  • AndroidAutoLayout 鴻陽大神的百分比布局
  • DataBindPercent 自定義@BindingAdapter授帕,設(shè)置各種padding,margin等值
  • Rudeness 從系統(tǒng)長度計(jì)算的入口,TypedValue里的applyDimension方法下手浮梢,簡(jiǎn)單好用跛十,性能優(yōu)秀,在activityonCreate時(shí)修改DisplayMetrics
//系統(tǒng)最終的長度換算
public static float applyDimension(int unit, float value, DisplayMetrics metrics){
    switch (unit) {
        case COMPLEX_UNIT_PX:
            return value;
        case COMPLEX_UNIT_DIP:
            return value * metrics.density;
        case COMPLEX_UNIT_SP:
            return value * metrics.scaledDensity;
        case COMPLEX_UNIT_PT:
            return value * metrics.xdpi * (1.0f/72);
        case COMPLEX_UNIT_IN:
            return value * metrics.xdpi;
        case COMPLEX_UNIT_MM:
            return value * metrics.xdpi * (1.0f/25.4f);
        }
        return 0;
    }
}

XML加載的幾種方式秕硝,各自的原理芥映。都有什么優(yōu)缺點(diǎn)?

現(xiàn)在解析XML的主流的方法有DOM远豺、SAX奈偏、JDOM和DOM4J
http://blog.csdn.net/bai435963/article/details/51246739

消息隊(duì)列

  • sendMessage() 和 sendMessageDelayed()
  • 發(fā)送的消息會(huì)存儲(chǔ)在MessageQueue中(單鏈表結(jié)構(gòu))


    image.png
  • Loop消息循環(huán)
    錯(cuò)誤寫法
new Thread(){
    @Override
    public void run() {
        Handler handler = new Handler();
    }
}.start();

正確寫法

new Thread(){
    @Override
    public void run() {
        Looper.prepare();
        Handler handler = new Handler();
        Looper.loop();
    }
}.start();
  • 主線程中不報(bào)錯(cuò)的原因是
public static void main(String[] args) {
    // ... 省略部分代碼
    
    Looper.prepareMainLooper();

    ActivityThread thread = new ActivityThread();
    thread.attach(false);

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }

    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}
  • Looper.prepareMainLooper() 創(chuàng)建了一個(gè) Looper 對(duì)象,而且保證一個(gè)線程只有一個(gè) Looper
  • Looper.loop() 里面是一個(gè)死循環(huán)躯护,不斷的從 消息隊(duì)列 MessageQueue 中取消息惊来,然后通過 Handler 執(zhí)行
Android中動(dòng)畫的分類,各自的優(yōu)缺點(diǎn)榛做。

有哪些容易造成內(nèi)存泄漏的原因唁盏?

1、單例造成的內(nèi)存泄漏
2检眯、非靜態(tài)內(nèi)部類創(chuàng)建靜態(tài)實(shí)例造成的內(nèi)存泄漏
**解決方法:將該內(nèi)部類設(shè)為靜態(tài)內(nèi)部類或?qū)⒃搩?nèi)部類抽取出來封裝成一個(gè)單例厘擂,如果需要使用Context,就使用Application的Context锰瘸。
3刽严、Handler造成的內(nèi)存泄漏
在Java中,非靜態(tài)內(nèi)部類和匿名類內(nèi)部類都會(huì)潛在持有它們所屬的外部類的引用避凝,但是靜態(tài)內(nèi)部類卻不會(huì)舞萄。
解決方法:將Handler類獨(dú)立出來或者使用靜態(tài)內(nèi)部類,這樣便可以避免內(nèi)存泄漏管削。

由于單例的靜態(tài)特性使得其生命周期和應(yīng)用的生命周期一樣長倒脓,如果一個(gè)對(duì)象已經(jīng)不再需要使用了,而單例對(duì)象還持有該對(duì)象的引用含思,就會(huì)使得該對(duì)象不能被正称槠回收甘晤,從而導(dǎo)致了內(nèi)存泄漏。
4饲做、線程造成的內(nèi)存泄漏
AsyncTask和Runnable都使用了匿名內(nèi)部類线婚,那么它們將持有其所在Activity的隱式引用。如果任務(wù)在Activity銷毀之前還未完成盆均,那么將導(dǎo)致Activity的內(nèi)存資源無法被回收塞弊,從而造成內(nèi)存泄漏。
解決方法:將AsyncTask和Runnable類獨(dú)立出來或者使用靜態(tài)內(nèi)部類泪姨,這樣便可以避免內(nèi)存泄漏游沿。
5、資源未關(guān)閉造成的內(nèi)存泄漏
對(duì)于使用了BraodcastReceiver驴娃,ContentObserver奏候,F(xiàn)ile,Cursor唇敞,Stream蔗草,Bitmap等資源,應(yīng)該在Activity銷毀時(shí)及時(shí)關(guān)閉或者注銷疆柔,否則這些資源將不會(huì)被回收咒精,從而造成內(nèi)存泄漏。
6旷档、WebView造成的泄露
當(dāng)我們不要使用WebView對(duì)象時(shí)模叙,應(yīng)該調(diào)用它的destory()函數(shù)來銷毀它,并釋放其占用的內(nèi)存鞋屈,否則其長期占用的內(nèi)存也不能被回收范咨,從而造成內(nèi)存泄露厂庇。
解決方法:為WebView另外開啟一個(gè)進(jìn)程,通過AIDL與主線程進(jìn)行通信译柏,WebView所在的進(jìn)程可以根據(jù)業(yè)務(wù)的需要選擇合適的時(shí)機(jī)進(jìn)行銷毀,從而達(dá)到內(nèi)存的完整釋放蹦肴。
1)將內(nèi)部類改為靜態(tài)內(nèi)部類
2)靜態(tài)內(nèi)部類中使用弱引用來引用外部類的成員變量

Android動(dòng)畫分類

  • 屬性動(dòng)畫 Property Animation :通過動(dòng)態(tài)改變對(duì)象的屬性達(dá)到動(dòng)畫效果,View的屬性是真正改變了
  • View Animation
    • Frame Animation 幀動(dòng)畫:圖片切換動(dòng)畫
    • Tween Animation 補(bǔ)間動(dòng)畫 :可以對(duì)view實(shí)現(xiàn)一系列的轉(zhuǎn)換,例如:移動(dòng)、漸變荔棉、伸縮、旋轉(zhuǎn)。只是在視圖層實(shí)現(xiàn)了動(dòng)畫效果逝变,并沒有真正改變View的屬性基茵。
HashMap
  • HashMap是由數(shù)組和鏈表組合構(gòu)成的數(shù)據(jù)結(jié)構(gòu),Java8中鏈表長度超過8時(shí)會(huì)把長度超過8的鏈表轉(zhuǎn)化成紅黑樹骨田;存取時(shí)都會(huì)根據(jù)鍵值計(jì)算出"類別"(hashCode)耿导,再根據(jù)"類別"定位到數(shù)組中的位置并執(zhí)行操作。

  • hashCode是一個(gè)對(duì)象的標(biāo)識(shí)态贤,Java中對(duì)象的hashCode是一個(gè)int類型值舱呻。通過hashCode來指定數(shù)組的索引可以快速定位到要找的對(duì)象在數(shù)組中的位置,之后再遍歷鏈表找到對(duì)應(yīng)值悠汽,理想情況下時(shí)間復(fù)雜度為O(1)箱吕,并且不同對(duì)象可以擁有相同的hashCode。

  • HashMap的時(shí)間復(fù)雜度取決于hash算法柿冲,優(yōu)秀的hash算法可以讓時(shí)間復(fù)雜度趨于常數(shù)O(1)茬高,糟糕的hash算法可以讓時(shí)間復(fù)雜度趨于O(N)。

  • 在數(shù)組大小不變的情況下假抄,存放鍵值對(duì)越多怎栽,查找的時(shí)間效率會(huì)降低,擴(kuò)容可以解決該問題宿饱,而負(fù)載因子決定了什么時(shí)候擴(kuò)容熏瞄,負(fù)載因子是已存鍵值對(duì)的數(shù)量和總的數(shù)組長度的比值。默認(rèn)情況下負(fù)載因子為0.75谬以,我們可在初始化HashMap的時(shí)候自己修改强饮。

  • HashMap中是允許key為空的情況。再看下主流程:


    image.png

    image.png
  • HashMap是線程不安全的數(shù)據(jù)結(jié)構(gòu)为黎,多線程情況下HashMap會(huì)引起死循環(huán)引用

對(duì)比

1.發(fā)生hash沖突時(shí)邮丰,Java7會(huì)在鏈表頭部插入行您,Java8會(huì)在鏈表尾部插入

2.擴(kuò)容后轉(zhuǎn)移數(shù)據(jù),Java7轉(zhuǎn)移前后鏈表順序會(huì)倒置剪廉,Java8還是保持原來的順序

3.關(guān)于性能對(duì)比可以參考美團(tuán)技術(shù)博客娃循,引入紅黑樹的Java8大程度得優(yōu)化了HashMap的性能

  • 通過源碼分析,Java7在多線程操作hashmap時(shí)可能引起死循環(huán)斗蒋,原因是擴(kuò)容轉(zhuǎn)移后前后鏈表順序倒置淮野,在轉(zhuǎn)移過程中修改了原來鏈表中節(jié)點(diǎn)的引用關(guān)系;Java8在同樣的前提下并不會(huì)引起死循環(huán)吹泡,原因是擴(kuò)容轉(zhuǎn)移后前后鏈表順序不變,保持之前節(jié)點(diǎn)的引用關(guān)系经瓷。那是不是意味著Java8就可以把HashMap用在多線程中呢爆哑?個(gè)人感覺即使不會(huì)出現(xiàn)死循環(huán),但是通過源碼看到put/get方法都沒有加同步鎖舆吮,多線程情況最容易出現(xiàn)的就是:無法保證上一秒put的值揭朝,下一秒get的時(shí)候還是原值,建議使用 ConcurrentHashMap色冀。
LruCache
  • LRU 是 Least Recently Used 最近最少使用算法潭袱。核心思想是當(dāng)緩存滿時(shí),會(huì)優(yōu)先淘汰那些近期最少使用的緩存對(duì)象锋恬。
  • 1.(必填)你需要提供一個(gè)緩存容量作為構(gòu)造參數(shù)屯换。
  • 2.(必填) 覆寫 sizeOf 方法 ,自定義設(shè)計(jì)一條數(shù)據(jù)放進(jìn)來的容量計(jì)算与学,如果不覆寫就無法預(yù)知數(shù)據(jù)的容量彤悔,不能保證緩存容量限定在最大容量以內(nèi)。
  • 3.(選填) 覆寫 entryRemoved 方法 索守,你可以知道最少使用的緩存被清除時(shí)的數(shù)據(jù)( evicted, key, oldValue, newVaule )晕窑。
  • 4.(記住)LruCache是線程安全的卵佛,在內(nèi)部的 get杨赤、put、remove 包括 trimToSize 都是安全的(因?yàn)槎忌湘i了)截汪。
    LruCache 就是 利用 LinkedHashMap 的一個(gè)特性( accessOrder=true 基于訪問順序 )再加上對(duì) LinkedHashMap 的數(shù)據(jù)操作上鎖實(shí)現(xiàn)的緩存策略疾牲。

LruCache 的數(shù)據(jù)緩存是內(nèi)存中的。

1.首先設(shè)置了內(nèi)部 LinkedHashMap 構(gòu)造參數(shù) accessOrder=true挫鸽, 實(shí)現(xiàn)了數(shù)據(jù)排序按照訪問順序说敏。

2.然后在每次 LruCache.get(K key) 方法里都會(huì)調(diào)用 LinkedHashMap.get(Object key)。

3.如上述設(shè)置了 accessOrder=true 后丢郊,每次 LinkedHashMap.get(Object key) 都會(huì)進(jìn)行 LinkedHashMap.makeTail(LinkedEntry<K, V> e)盔沫。

4.LinkedHashMap 是雙向循環(huán)鏈表医咨,然后每次 LruCache.get -> LinkedHashMap.get 的數(shù)據(jù)就被放到最末尾了。

5.在 put 和 trimToSize 的方法執(zhí)行下架诞,如果發(fā)生數(shù)據(jù)量移除拟淮,會(huì)優(yōu)先移除掉最前面的數(shù)據(jù)(因?yàn)樽钚略L問的數(shù)據(jù)在尾部)。
LruCache重要的幾點(diǎn):

1.LruCache 是通過 LinkedHashMap 構(gòu)造方法的第三個(gè)參數(shù)的 accessOrder=true 實(shí)現(xiàn)了 LinkedHashMap 的數(shù)據(jù)排序基于訪問順序 (最近訪問的數(shù)據(jù)會(huì)在鏈表尾部)谴忧,在容量溢出的時(shí)候很泊,將鏈表頭部的數(shù)據(jù)移除。從而沾谓,實(shí)現(xiàn)了 LRU 數(shù)據(jù)緩存機(jī)制委造。

2.LruCache 在內(nèi)部的get、put均驶、remove包括 trimToSize 都是安全的(因?yàn)槎忌湘i了)昏兆。

3.LruCache 自身并沒有釋放內(nèi)存,將 LinkedHashMap 的數(shù)據(jù)移除了妇穴,如果數(shù)據(jù)還在別的地方被引用了爬虱,還是有泄漏問題,還需要手動(dòng)釋放內(nèi)存腾它。

4.覆寫 entryRemoved 方法能知道 LruCache 數(shù)據(jù)移除是是否發(fā)生了沖突跑筝,也可以去手動(dòng)釋放資源。

5.maxSize 和 sizeOf(K key, V value) 方法的覆寫息息相關(guān)瞒滴,必須相同單位曲梗。( 比如 maxSize 是7MB,自定義的 sizeOf 計(jì)算每個(gè)數(shù)據(jù)大小的時(shí)候必須能算出與MB之間有聯(lián)系的單位 )

CircleImageView

一般實(shí)現(xiàn)自定義形狀的圖形有三種方式:PorterDuffXfermode 逛腿、BitmapShader稀并、ClipPath。

  • 繼承ImageView,重寫onDraw
  • 通過getDrawable獲取圖片bitmap
  • 創(chuàng)建BitmapShader,將bitmap作為底片,TileMode為 CLAMP :如果渲染器超出原始邊界范圍单默,會(huì)復(fù)制范圍內(nèi)邊緣染色碘举。
  • 使用Matrix,讓圖片縮放,平移,保持居中,BitmapShader設(shè)置該Matrix
  • 新建畫筆,設(shè)置Shader為該BitmapShader
  • canvas使用該畫筆畫圓
mBitmap = getBitmapFromDrawable(getDrawable());
        if (mBitmap != null) {
            BitmapShader bitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
            //設(shè)置縮放比
            Matrix mShaderMatrix = new Matrix();
            mShaderMatrix.setScale(1, 1);
            //平移操作,(dx + 0.5f)的處理搁廓,是四舍五入 
            mShaderMatrix.postTranslate(0, 0);
            bitmapShader.setLocalMatrix(mShaderMatrix);
            Paint paint = new Paint();
            paint.setShader(bitmapShader);
            canvas.drawCircle(getWidth() / 2, getHeight() / 2, getWidth() / 2, paint);
        }
藍(lán)牙4.0

BLE分為三部分:

Service

Characteristic

Descriptor

這三部分都用UUID作為唯一標(biāo)識(shí)符引颈。UUID為這種格式:0000ffe1-0000-1000-8000-00805f9b34fb。比如有3個(gè)Service境蜕,那么就有三個(gè)不同的UUID與Service對(duì)應(yīng)蝙场。這些UUID都寫在硬件里,我們通過BLE提供的API可以讀取到粱年。

一個(gè)BLE終端可以包含多個(gè)Service售滤, 一個(gè)Service可以包含多個(gè)Characteristic,一個(gè)Characteristic包含一個(gè)value和多個(gè)Descriptor,一個(gè)Descriptor包含一個(gè)Value完箩。Characteristic是比較重要的赐俗,是手機(jī)與BLE終端交換數(shù)據(jù)的關(guān)鍵,讀取設(shè)置數(shù)據(jù)等操作都是操作Characteristic的相關(guān)屬性弊知。
比如阻逮。有個(gè)藍(lán)牙ble的血壓計(jì)。他可能包括多個(gè)Servvice秩彤,每個(gè)Service有包括多個(gè)Characteristic

注意:藍(lán)牙ble只能支持Android 4.3以上的系統(tǒng) SDK>=18
2.以下是開發(fā)的步驟:

2.1首先獲取BluetoothManager

2.2獲取BluetoothAdapter

2.3創(chuàng)建BluetoothAdapter.LeScanCallback

2.4.開始搜索設(shè)備叔扼。

2.5.BluetoothDevice 描述了一個(gè)藍(lán)牙設(shè)備 提供了getAddress()設(shè)備Mac地址,getName()設(shè)備的名稱。

2.6開始連接設(shè)備

2.7連接到設(shè)備之后獲取設(shè)備的服務(wù)(Service)和服務(wù)對(duì)應(yīng)的Characteristic漫雷。

2.8獲取到特征之后瓜富,找到服務(wù)中可以向下位機(jī)寫指令的特征,向該特征寫入指令降盹。

2.9寫入成功之后食呻,開始讀取設(shè)備返回來的數(shù)據(jù)。

2.10澎现、斷開連接

2.11、數(shù)據(jù)的轉(zhuǎn)換方法
設(shè)備廠家都會(huì)給一份設(shè)備的通訊協(xié)議其中就有 哪一個(gè)UUID 代表什么每辟。

private void changeMonitorMod(BluetoothGatt gatt, byte[] buffer) {
   if (gatt != null && gatt != null) {
       BluetoothGattService writeService = gatt.getService(MYUUID);
       if (writeService == null) {
              return;
       }
   }
    BluetoothGattCharacteristic writeCharacteristic = writeService.getCharacteristic(MYWRITECHARACTERISTIC);
    if (writeCharacteristic == null) {
          return;
    }
    writeCharacteristic.setValue(buffer);
    //上面的buffer數(shù)組中裝的就是指令,多長? 每一位上面的數(shù)字代表什么意思在協(xié)議中查看速妖!
    gatt.writeCharacteristic(writeCharacteristic);//像設(shè)備寫入指令罗丰。    
}

65535原因及解決

  • 因?yàn)樵?a target="_blank" rel="nofollow">Dalvik指令集里,調(diào)用方法的invoke-kind指令中挠将,method reference index只給了16bits胳岂,最多能調(diào)用65535個(gè)方法,所以在生成dex文件的過程中舔稀,當(dāng)方法數(shù)超過65535就會(huì)報(bào)錯(cuò)乳丰。細(xì)看指令集,除了method内贮,field和class的index也是16bits产园,所以也存在65535的問題。
 android {
  ...
    defaultConfig {
      ...
        // 設(shè)置支持multidex
        multiDexEnabled true
    }
    ...
 }
  1. 重寫Application的attachBaseContext,使用MultiDex.install(this);
public class MyApplication extends Application {

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        MultiDex.install(this);
    }
}

或繼承

public class MyApplication extends MultiDexApplication {

     // 不需要重寫attachBaseContext()

     //..........
}
進(jìn)程币褂簦活

黑色笔惭啵活:不同的app進(jìn)程,用廣播相互喚醒(包括利用系統(tǒng)提供的廣播進(jìn)行喚醒)

白色本憾耍活:?jiǎn)?dòng)前臺(tái)Service,白色笔杭矗活手段非常簡(jiǎn)單,就是調(diào)用系統(tǒng)api啟動(dòng)一個(gè)前臺(tái)的Service進(jìn)程,這樣會(huì)在系統(tǒng)的通知欄生成一個(gè)Notification技俐,用來讓用戶知道有這樣一個(gè)app在運(yùn)行著乘陪,

灰色保活:利用系統(tǒng)的漏洞啟動(dòng)前臺(tái)Service,它是利用系統(tǒng)的漏洞來啟動(dòng)一個(gè)前臺(tái)的Service進(jìn)程虽另,與普通的啟動(dòng)方式區(qū)別在于暂刘,它不會(huì)在系統(tǒng)通知欄處出現(xiàn)一個(gè)Notification,看起來就如同運(yùn)行著一個(gè)后臺(tái)Service進(jìn)程一樣捂刺。這樣做帶來的好處就是谣拣,用戶無法察覺到你運(yùn)行著一個(gè)前臺(tái)進(jìn)程(因?yàn)榭床坏絅otification),但你的進(jìn)程優(yōu)先級(jí)又是高于普通后臺(tái)進(jìn)程的

public class GrayService extends Service {

    private final static int GRAY_SERVICE_ID = 1001;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (Build.VERSION.SDK_INT < 18) {
            startForeground(GRAY_SERVICE_ID, new Notification());//API < 18 ,此方法能有效隱藏Notification上的圖標(biāo)
        } else {
            Intent innerIntent = new Intent(this, GrayInnerService.class);
            startService(innerIntent);
            startForeground(GRAY_SERVICE_ID, new Notification());
        }

        return super.onStartCommand(intent, flags, startId);
    }

    ...
    ...

    /**
     * 給 API >= 18 的平臺(tái)上用的灰色弊逭梗活手段
     */
    public static class GrayInnerService extends Service {

        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            startForeground(GRAY_SERVICE_ID, new Notification());
            stopForeground(true);
            stopSelf();
            return super.onStartCommand(intent, flags, startId);
        }

    }
}

dumpsys activity services PackageName
打印出指定包名的所有進(jìn)程中的Service信息森缠,看下有沒有 isForeground=true 的關(guān)鍵信息。如果通知欄沒有看到屬于app的 Notification 且又看到 isForeground=true 則說明了仪缸,此app利用了這種灰色惫蠛活的手段。

DiskLruCache
  • 通常情況下多數(shù)應(yīng)用程序都會(huì)將緩存的位置選擇為 /sdcard/Android/data/<application package>/cache 這個(gè)路徑恰画。選擇在這個(gè)位置有兩點(diǎn)好處:第一宾茂,這是存儲(chǔ)在SD卡上的,因此即使緩存再多的數(shù)據(jù)也不會(huì)對(duì)手機(jī)的內(nèi)置存儲(chǔ)空間有任何影響拴还,只要SD卡空間足夠就行跨晴。第二,這個(gè)路徑被Android系統(tǒng)認(rèn)定為應(yīng)用程序的緩存路徑片林,當(dāng)程序被卸載的時(shí)候端盆,這里的數(shù)據(jù)也會(huì)一起被清除掉,這樣就不會(huì)出現(xiàn)刪除程序之后手機(jī)上還有很多殘留數(shù)據(jù)的問題费封。
  • 而journal文件是DiskLruCache的一個(gè)日志文件焕妙,程序?qū)γ繌垐D片的操作記錄都存放在這個(gè)文件中,基本上看到j(luò)ournal這個(gè)文件就標(biāo)志著該程序使用DiskLruCache技術(shù)了弓摘。
  • mDiskLruCache = DiskLruCache.open(cacheDir, getAppVersion(context), 1, 10 * 1024 * 1024);
  • 圖片放入緩存
new Thread(new Runnable() {  
    @Override  
    public void run() {  
        try {  
            String imageUrl = "http://img.my.csdn.net/uploads/201309/01/1378037235_7476.jpg";  
            String key = hashKeyForDisk(imageUrl);  
            DiskLruCache.Editor editor = mDiskLruCache.edit(key);  
            if (editor != null) {  
                OutputStream outputStream = editor.newOutputStream(0);  
                if (downloadUrlToStream(imageUrl, outputStream)) {  
                    editor.commit();  
                } else {  
                    editor.abort();  
                }  
            }  
            mDiskLruCache.flush();  
        } catch (IOException e) {  
            e.printStackTrace();  
        }  
    }  
}).start();  
  • 讀取圖片緩存
try {  
    String imageUrl = "http://img.my.csdn.net/uploads/201309/01/1378037235_7476.jpg";  
    String key = hashKeyForDisk(imageUrl);  
    DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);  
    if (snapShot != null) {  
        InputStream is = snapShot.getInputStream(0);  
        Bitmap bitmap = BitmapFactory.decodeStream(is);  
        mImage.setImageBitmap(bitmap);  
    }  
} catch (IOException e) {  
    e.printStackTrace();  
}  
  • flush()這個(gè)方法用于將內(nèi)存中的操作記錄同步到日志文件(也就是journal文件)當(dāng)中.比較標(biāo)準(zhǔn)的做法就是在Activity的onPause()方法中去調(diào)用一次flush()方法就可以了焚鹊。
  • close()
MVC與MVP

MVC的缺點(diǎn)

在Android開發(fā)中,Activity并不是一個(gè)標(biāo)準(zhǔn)的MVC模式中的Controller韧献,它的首要職責(zé)是加載應(yīng)用的布局和初始化用戶界面寺旺,接受并處理來自用戶的操作請(qǐng)求,進(jìn)而作出響應(yīng)势决。隨著界面及其邏輯的復(fù)雜度不斷提升阻塑,Activity類的職責(zé)不斷增加,以致變得龐大臃腫果复。
View:負(fù)責(zé)繪制UI元素陈莽、與用戶進(jìn)行交互(在Android中體現(xiàn)為Activity)
Model:負(fù)責(zé)存儲(chǔ)、檢索、操縱數(shù)據(jù)(有時(shí)也實(shí)現(xiàn)一個(gè)Model interface用來降低耦合)
Presenter:作為View與Model交互的中間紐帶走搁,處理與用戶交互的負(fù)責(zé)邏輯独柑。
兩種模式的主要區(qū)別:

(最主要區(qū)別)View與Model并不直接交互,而是通過與Presenter交互來與Model間接交互私植。而在MVC中View可以與Model直接交互

通常View與Presenter是一對(duì)一的忌栅,但復(fù)雜的View可能綁定多個(gè)Presenter來處理邏輯。而Controller是基于行為的曲稼,并且可以被多個(gè)View共享索绪,Controller可以負(fù)責(zé)決定顯示哪個(gè)View

Presenter與View的交互是通過接口來進(jìn)行的,更有利于添加單元測(cè)試贫悄。
因此我們可以發(fā)現(xiàn)MVP的優(yōu)點(diǎn)如下:

1瑞驱、模型與視圖完全分離,我們可以修改視圖而不影響模型窄坦;

2唤反、可以更高效地使用模型,因?yàn)樗械慕换ザ及l(fā)生在一個(gè)地方——Presenter內(nèi)部鸭津;

3彤侍、我們可以將一個(gè)Presenter用于多個(gè)視圖,而不需要改變Presenter的邏輯逆趋。這個(gè)特性非常的有用拥刻,因?yàn)橐晥D的變化總是比模型的變化頻繁;

4父泳、如果我們把邏輯放在Presenter中,那么我們就可以脫離用戶接口來測(cè)試這些邏輯(單元測(cè)試)吴汪。
舉個(gè)簡(jiǎn)單的例子惠窄,UI層通知邏輯層(Presenter)用戶點(diǎn)擊了一個(gè)Button,邏輯層(Presenter)自己決定應(yīng)該用什么行為進(jìn)行響應(yīng)漾橙,該找哪個(gè)模型(Model)去做這件事杆融,最后邏輯層(Presenter)將完成的結(jié)果更新到UI層。
通過對(duì)比同一實(shí)例的MVC與MVP的代碼霜运,可以證實(shí)MVP模式的一些優(yōu)點(diǎn):

在MVP中脾歇,Activity的代碼不臃腫;

在MVP中淘捡,Model(IUserModel的實(shí)現(xiàn)類)的改動(dòng)不會(huì)影響Activity(View)藕各,兩者也互不干涉,而在MVC中會(huì)焦除;

在MVP中激况,IUserView這個(gè)接口可以實(shí)現(xiàn)方便地對(duì)Presenter的測(cè)試;

在MVP中,UserPresenter可以用于多個(gè)視圖乌逐,但是在MVC中的Activity就不行竭讳。
心得:先實(shí)現(xiàn),再重構(gòu)吧浙踢。直接考慮代碼不臃腫得話绢慢,不知道什么時(shí)候才能寫好了
能用第三方庫就用第三方庫。別管是否穩(wěn)定洛波,是否被持續(xù)維護(hù)胰舆,因?yàn)椋魏蔚谌綆斓淖髡叻芩辏寄苣雺簞側(cè)腴T的菜鳥思瘟,你絕對(duì)寫不出比別人更好的代碼了。

最后附上知乎上面點(diǎn)贊次數(shù)很高的一段話:

如果“從零開始”闻伶,用什么設(shè)計(jì)架構(gòu)的問題屬于想得太多做得太少的問題滨攻。

從零開始意味著一個(gè)項(xiàng)目的主要技術(shù)難點(diǎn)是基本功能實(shí)現(xiàn)。當(dāng)每一個(gè)功能都需要考慮如何做到的時(shí)候蓝翰,我覺得一般人都沒辦法考慮如何做好光绕。

因?yàn)椋械膬?yōu)化都是站在最上層進(jìn)行統(tǒng)籌規(guī)劃畜份。在這之前诞帐,你必須對(duì)下層的每一個(gè)模塊都非常熟悉,進(jìn)而提煉可復(fù)用的代碼爆雹、規(guī)劃邏輯流程停蕉。

所以,如果真的是從零開始钙态,別想太多了

Android手勢(shì)識(shí)別

GestureDetector.OnGestureListener
OnDown(MotionEvent e):用戶按下屏幕就會(huì)觸發(fā)慧起;
onShowPress(MotionEvent e):如果是按下的時(shí)間超過瞬間,而且在按下的時(shí)候沒有松開或者是拖動(dòng)的册倒,那么onShowPress就會(huì)執(zhí)行蚓挤,具體這個(gè)瞬間是多久,我也不清楚呃……
onLongPress(MotionEvent e):長按觸摸屏驻子,超過一定時(shí)長灿意,就會(huì)觸發(fā)這個(gè)事件
觸發(fā)順序:
onDown->onShowPress->onLongPress
onSingleTapUp(MotionEvent e):從名子也可以看出,一次單獨(dú)的輕擊抬起操作,也就是輕擊一下屏幕,立刻抬起來崇呵,才會(huì)有這個(gè)觸發(fā)缤剧,當(dāng)然,如果除了Down以外還有其它操作,那就不再算是Single操作了,所以也就不會(huì)觸發(fā)這個(gè)事件
觸發(fā)順序:
點(diǎn)擊一下非常快的(不滑動(dòng))Touchup:
onDown->onSingleTapUp->onSingleTapConfirmed
點(diǎn)擊一下稍微慢點(diǎn)的(不滑動(dòng))Touchup:
onDown->onShowPress->onSingleTapUp->onSingleTapConfirmed
onFling(MotionEvent e1, MotionEvent e2, float velocityX,float velocityY) :滑屏域慷,用戶按下觸摸屏鞭执、快速移動(dòng)后松開司顿,由1個(gè)MotionEvent ACTION_DOWN, 多個(gè)ACTION_MOVE, 1個(gè)ACTION_UP觸發(fā)
參數(shù)解釋:
e1:第1個(gè)ACTION_DOWN MotionEvent
e2:最后一個(gè)ACTION_MOVE MotionEvent
velocityX:X軸上的移動(dòng)速度,像素/秒
velocityY:Y軸上的移動(dòng)速度兄纺,像素/秒
onScroll(MotionEvent e1, MotionEvent e2,float distanceX, float distanceY):在屏幕上拖動(dòng)事件大溜。無論是用手拖動(dòng)view,或者是以拋的動(dòng)作滾動(dòng)估脆,都會(huì)多次觸發(fā),這個(gè)方法 在ACTION_MOVE動(dòng)作發(fā)生時(shí)就會(huì)觸發(fā)
滑屏:手指觸動(dòng)屏幕后钦奋,稍微滑動(dòng)后立即松開
onDown-----》onScroll----》onScroll----》onScroll----》………----->onFling
拖動(dòng)
onDown------》onScroll----》onScroll------》onFiling

可見,無論是滑屏疙赠,還是拖動(dòng)付材,影響的只是中間OnScroll觸發(fā)的數(shù)量多少而已,最終都會(huì)觸發(fā)onFling事件圃阳!

要使用GestureDetector厌衔,有三步要走:
1、創(chuàng)建OnGestureListener監(jiān)聽函數(shù):
2捍岳、創(chuàng)建GestureDetector實(shí)例mGestureDetector:

GestureDetector gestureDetector=new GestureDetector(GestureDetector.OnGestureListener listener);  
GestureDetector gestureDetector=new GestureDetector(Context context,GestureDetector.OnGestureListener listener);  
GestureDetector gestureDetector=new GestureDetector(Context context,GestureDetector.SimpleOnGestureListener listener);  

3富寿、onTouch(View v, MotionEvent event)中攔截:

public boolean onTouch(View v, MotionEvent event) {  
    return mGestureDetector.onTouchEvent(event);     
}  

GestureDetector.OnDoubleTapListener
使用GestureDetector::setOnDoubleTapListener();函數(shù)設(shè)置監(jiān)聽:
onSingleTapConfirmed(MotionEvent e):?jiǎn)螕羰录S脕砼卸ㄔ摯吸c(diǎn)擊是SingleTap而不是DoubleTap锣夹,如果連續(xù)點(diǎn)擊兩次就是DoubleTap手勢(shì)页徐,如果只點(diǎn)擊一次,系統(tǒng)等待一段時(shí)間后沒有收到第二次點(diǎn)擊則判定該次點(diǎn)擊為SingleTap而不是DoubleTap银萍,然后觸發(fā)SingleTapConfirmed事件变勇。觸發(fā)順序是:OnDown->OnsingleTapUp->OnsingleTapConfirmed
關(guān)于onSingleTapConfirmed和onSingleTapUp的一點(diǎn)區(qū)別: OnGestureListener有這樣的一個(gè)方法onSingleTapUp,和onSingleTapConfirmed容易混淆贴唇。二者的區(qū)別是:onSingleTapUp搀绣,只要手抬起就會(huì)執(zhí)行,而對(duì)于onSingleTapConfirmed來說戳气,如果雙擊的話链患,則onSingleTapConfirmed不會(huì)執(zhí)行。
onDoubleTap(MotionEvent e):雙擊事件

onDoubleTapEvent(MotionEvent e):雙擊間隔中發(fā)生的動(dòng)作物咳。指觸發(fā)onDoubleTap以后,在雙擊之間發(fā)生的其它動(dòng)作蹄皱,包含down览闰、up和move事件;

Android圖像顯示的底層原理
注解
  • 4種元注解:
    • @Target 定義作用域
    • @Retention 運(yùn)行級(jí)別,作用時(shí)機(jī),常見有RetentionPolicy.CLASS 編譯期 RetentionPolicy.CLASS 運(yùn)行器,多用反射獲取信息
    • @Documented 表示將此注解包含在javadoc中
    • @Inherited 子類繼承父類注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.CLASS)
public @interface BindView {
    int value() ;
}
利用APT實(shí)現(xiàn)編譯期注解

四個(gè)模塊巷折,前三個(gè)為核心模塊:

  • proxytool-api:框架api模塊压鉴,供使用者調(diào)用,Android Library類型模塊
  • proxytool-annotations:自定義注解模塊锻拘,Java類型模塊
  • proxytool-compiler:注解處理器模塊油吭,用于處理注解并生成文件击蹲,Java類型模塊
  • proxytool-sample:示例Demo模塊,Android工程類型模塊
    其中這四個(gè)模塊的依賴關(guān)系如下:
    proxytool-api依賴proxytool-annotations模塊婉宰。
    proxytool-compiler依賴proxytool-annotations模塊歌豺。
    proxytool-sample模塊依賴proxytool-api模塊。
    繼承AbstractProcessor心包,最核心的方法就是process()类咧,在這里你可以掃描和處理注解,并生成java文件,使用了javapoet來幫助我們生成Java源碼
OKHttp源碼解析

從Android4.4開始HttpURLConnection底層實(shí)現(xiàn)采用的是okHttp
進(jìn)行通信的原理是:


這里寫圖片描述

上 面是OKHttp總體設(shè)計(jì)圖蟹腾,主要是通過Diapatcher不斷從RequestQueue中取出請(qǐng)求(Call)痕惋,根據(jù)是否已緩存調(diào)用Cache或 Network這兩類數(shù)據(jù)獲取接口之一,從內(nèi)存緩存或是服務(wù)器取得請(qǐng)求的數(shù)據(jù)娃殖。該引擎有同步和異步請(qǐng)求值戳,同步請(qǐng)求通過Call.execute()直接返 回當(dāng)前的Response,而異步請(qǐng)求會(huì)把當(dāng)前的請(qǐng)求Call.enqueue添加(AsyncCall)到請(qǐng)求隊(duì)列中炉爆,并通過回調(diào)(Callback) 的方式來獲取最后結(jié)果堕虹。

  • 支持 SPDY ,共享同一個(gè)Socket來處理同一個(gè)服務(wù)器的所有請(qǐng)求
  • 如果SPDY不可用叶洞,則通過連接池來減少請(qǐng)求延時(shí)
  • 無縫的支持GZIP來減少數(shù)據(jù)流量
  • 緩存響應(yīng)數(shù)據(jù)來減少重復(fù)的網(wǎng)絡(luò)請(qǐng)求
Retrofit分析 對(duì)網(wǎng)絡(luò)請(qǐng)求客戶端的完美封裝
  • retrofit的最大特點(diǎn)就是解耦鲫凶,要解耦就需要大量的設(shè)計(jì)模式,假如一點(diǎn)設(shè)計(jì)模式都不懂的人衩辟,可能很難看懂retrofit螟炫。


    image.png
  • Retrofit通俗的說就是一個(gè)HTTP請(qǐng)求的一個(gè)框架,通過一個(gè)Interface來定義api的實(shí)現(xiàn)艺晴,通過注釋的方式進(jìn)行一些請(qǐng)求的設(shè)置昼钻。運(yùn)用大量的設(shè)計(jì)模式進(jìn)行網(wǎng)絡(luò)請(qǐng)求的解耦.

ANR

1.只有主線程才會(huì)產(chǎn)生ANR,主線程就是UI線程封寞;

2.必須發(fā)生某些輸入事件或特定操作然评,比如按鍵或觸屏等輸入事件,在BroadcastReceiver或Service的各個(gè)生命周期調(diào)用函數(shù)狈究;

3.上述事件響應(yīng)超時(shí)碗淌,不同的context規(guī)定的上限時(shí)間不同

a.主線程對(duì)輸入事件5秒內(nèi)沒有處理完畢

b.主線程在執(zhí)行BroadcastReceiver的onReceive()函數(shù)時(shí)10秒內(nèi)沒有處理完畢

c.主線程在Service的各個(gè)生命周期函數(shù)時(shí)20秒內(nèi)沒有處理完畢。

那么如何避免ANR的發(fā)生呢或者說ANR的解決辦法是什么呢抖锥?
1.避免在主線程執(zhí)行耗時(shí)操作亿眠,所有耗時(shí)操作應(yīng)新開一個(gè)子線程完成,然后再在主線程更新UI磅废。

2.BroadcastReceiver要執(zhí)行耗時(shí)操作時(shí)應(yīng)啟動(dòng)一個(gè)service纳像,將耗時(shí)操作交給service來完成。

Force close

  • 發(fā)生未捕獲異常時(shí)產(chǎn)生
  • 如何避免彈出Force Close窗口 拯勉,可以實(shí)現(xiàn)Thread.UncaughtExceptionHandler接口的uncaughtException方法
  • 想要哪個(gè)線程可以處理未捕獲異常竟趾,Thread.setDefaultUncaughtExceptionHandler( this); 這句代碼都要在那個(gè)線程中執(zhí)行一次

OOM

堆內(nèi)存溢出
Android為不同類型的進(jìn)程分配了不同的內(nèi)存使用上限憔购,如果程序在運(yùn)行過程中出現(xiàn)了內(nèi)存泄漏的而造成應(yīng)用進(jìn)程使用的內(nèi)存超過了這個(gè)上限,則會(huì)被系統(tǒng)視 為內(nèi)存泄漏岔帽,從而被kill掉玫鸟,這使得僅僅自己的進(jìn)程被kill掉,而不會(huì)影響其他進(jìn)程(如果是system_process等系統(tǒng)進(jìn)程出問題的話山卦,則會(huì) 引起系統(tǒng)重啟)鞋邑,這是,我們的應(yīng)用程序就會(huì)崩潰账蓉,我們就會(huì)看到OOM枚碗。

  • 一次性要求很大內(nèi)存
  • 內(nèi)存泄漏

app冷啟動(dòng)的流程如下:

-> Application 構(gòu)造函數(shù)
-> Application.attachBaseContext()
-> Application.onCreate()
-> Activity 構(gòu)造函數(shù)
-> Activity.setTheme()
-> Activity.onCreate()
-> Activity.onStart
-> Activity.onResume
-> Activity.onAttachedToWindow
-> Activity.onWindowFocusChanged

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市铸本,隨后出現(xiàn)的幾起案子肮雨,更是在濱河造成了極大的恐慌,老刑警劉巖箱玷,帶你破解...
    沈念sama閱讀 216,692評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件怨规,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡锡足,警方通過查閱死者的電腦和手機(jī)波丰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來舶得,“玉大人掰烟,你說我怎么就攤上這事°迮” “怎么了纫骑?”我有些...
    開封第一講書人閱讀 162,995評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長九孩。 經(jīng)常有香客問我先馆,道長,這世上最難降的妖魔是什么躺彬? 我笑而不...
    開封第一講書人閱讀 58,223評(píng)論 1 292
  • 正文 為了忘掉前任煤墙,我火速辦了婚禮,結(jié)果婚禮上宪拥,老公的妹妹穿的比我還像新娘仿野。我一直安慰自己,他們只是感情好江解,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,245評(píng)論 6 388
  • 文/花漫 我一把揭開白布设预。 她就那樣靜靜地躺著徙歼,像睡著了一般犁河。 火紅的嫁衣襯著肌膚如雪鳖枕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,208評(píng)論 1 299
  • 那天桨螺,我揣著相機(jī)與錄音宾符,去河邊找鬼。 笑死灭翔,一個(gè)胖子當(dāng)著我的面吹牛魏烫,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播肝箱,決...
    沈念sama閱讀 40,091評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼哄褒,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了煌张?” 一聲冷哼從身側(cè)響起呐赡,我...
    開封第一講書人閱讀 38,929評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎骏融,沒想到半個(gè)月后链嘀,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,346評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡档玻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,570評(píng)論 2 333
  • 正文 我和宋清朗相戀三年怀泊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片误趴。...
    茶點(diǎn)故事閱讀 39,739評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡霹琼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出冤留,到底是詐尸還是另有隱情碧囊,我是刑警寧澤,帶...
    沈念sama閱讀 35,437評(píng)論 5 344
  • 正文 年R本政府宣布纤怒,位于F島的核電站糯而,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏泊窘。R本人自食惡果不足惜熄驼,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,037評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望烘豹。 院中可真熱鬧瓜贾,春花似錦、人聲如沸携悯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽憔鬼。三九已至龟劲,卻和暖如春胃夏,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背昌跌。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評(píng)論 1 269
  • 我被黑心中介騙來泰國打工仰禀, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蚕愤。 一個(gè)月前我還...
    沈念sama閱讀 47,760評(píng)論 2 369
  • 正文 我出身青樓答恶,卻偏偏與公主長得像,于是被迫代替她去往敵國和親萍诱。 傳聞我的和親對(duì)象是個(gè)殘疾皇子悬嗓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,647評(píng)論 2 354