https://juejin.im/entry/5aa69dc851882555602093b2
- 什么是自動(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)拆箱膝擂; -
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
靜態(tài)注冊(cè):在AndroidManifest文件中注冊(cè)BroadcastReciever。不需要應(yīng)用啟動(dòng)姓赤,就可以在廣播傳來時(shí)自動(dòng)啟動(dòng)應(yīng)用赡译,接收、處理廣播不铆。
動(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ū)別是什么舀武?
- ContentProvider一般為存儲(chǔ)和獲取數(shù)據(jù)提供統(tǒng)一的接口,可以在不同的應(yīng)用程序之間共享數(shù)據(jù)离斩。
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ū)別双炕。
- public SharedPreferences getPreferences (int mode)
通過Activity對(duì)象獲取,獲取的是本Activity私有的Preference撮抓,保存在系統(tǒng)中的xml形式的文件的名稱為這個(gè)Activity的名字妇斤,因此一個(gè)Activity只能有一個(gè),屬于這個(gè)Activity丹拯。
- public SharedPreferences getSharedPreferences (String name, int mode)
因?yàn)锳ctivity繼承了ContextWrapper站超,因此也是通過Activity對(duì)象獲取,但是屬于整個(gè)應(yīng)用程序乖酬,可以有多個(gè)死相,以第一參數(shù)的name為文件名保存在系統(tǒng)中。
- 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))
- 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)部類中使用弱引用來引用外部類的成員變量
- 屬性動(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為空的情況。再看下主流程:
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
}
...
}
- 重寫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螟炫。
- 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