生命週期:對象何時生,何時死,怎麼寫代碼,代碼往那裏寫。android
注意:面試
Main1Activity: onPause Main2Activity: onCreate Main2Activity: onStart Main2Activity: onResume MainA1ctivity: onStop
異常狀態下的生命週期:算法
資源相關的系統配置發生改變或者資源不足:例如屏幕旋轉,當前Activity會銷燬,而且在onStop以前回調onSaveInstanceState保存數據,在從新建立Activity的時候在onStart以後回調onRestoreInstanceState。其中Bundle數據會傳到onCreate(不必定有數據)和onRestoreInstanceState(必定有數據)。數據庫
防止屏幕旋轉的時候重建,在清單文件中添加配置: android:configChanges="orientation"
Activity: onCreate Fragment: onAttach Fragment: onCreate Fragment: onCreateView Fragment: onActivityCreated Activity: onStart Activity: onResume
Activity: onPause Activity: onStop Fragment: onDestroyView Fragment: onDestroy Fragment: onDetach Activity: onDestroy
//Activity中對fragment設置一些參數 fragment.setArguments(bundle); //fragment中經過getArguments得到Activity中的方法 Bundle arguments = getArguments();
//Activity中的代碼 EventBus.getDefault().post("消息"); //Fragment中的代碼 EventBus.getDefault().register(this); @Subscribe public void test(String text) { tv_test.setText(text); }
Service分爲兩種:canvas
對應的生命週期以下:緩存
context.startService() ->onCreate()- >onStartCommand()->Service running--調用context.stopService() ->onDestroy() context.bindService()->onCreate()->onBind()->Service running--調用>onUnbind() -> onDestroy()
Service默認是運行在main線程的,所以Service中若是須要執行耗時操做(大文件的操做,數據庫的拷貝,網絡請求,文件下載等)的話應該在子線程中完成。安全
特殊狀況是!:Service在清單文件中指定了在其餘進程中運行。性能優化
由於屏幕的刷新頻率是60Hz,大概16毫秒會刷新一次,因此爲了保證UI的流暢性,耗時操做須要在子線程中處理,子線程不能直接對UI進行更新操做。所以須要Handler在子線程發消息給主線程來更新UI。服務器
這裏再深刻一點,Android中的UI控件不是線程安全的,所以在多線程併發訪問UI的時候會致使UI控件處於不可預期的狀態。Google不經過鎖的機制來處理這個問題是由於:網絡
所以,Google的工程師最後是經過單線程的模型來操做UI,開發者只須要經過Handler在不一樣線程之間切花就能夠了。
Android中的消息機制主要是指Handler的運行機制。Handler是進行線程切換的關鍵,在主線程和子線程之間切換隻是一種比較特殊的使用情景而已。其中消息傳遞機制須要瞭解的東西有Message、Handler、Looper、Looper裏面的MessageQueue對象。
如上圖所示,咱們能夠把整個消息機制看做是一條流水線。其中:
Handler的工做是依賴於Looper的,而Looper(與消息隊列)又是屬於某一個線程(ThreadLocal是線程內部的數據存儲類,經過它能夠在指定線程中存儲數據,其餘線程則沒法獲取到),其餘線程不能訪問。所以Handler就是間接跟線程是綁定在一塊兒了。所以要使用Handler必需要保證Handler所建立的線程中有Looper對象而且啓動循環。由於子線程中默認是沒有Looper的,因此會報錯。
正確的使用方法是:
handler = null; new Thread(new Runnable() { private Looper mLooper; @Override public void run() { //必須調用Looper的prepare方法爲當前線程建立一個Looper對象,而後啓動循環 //prepare方法中實質是給ThreadLocal對象建立了一個Looper對象 //若是當前線程已經建立過Looper對象了,那麼會報錯 Looper.prepare(); handler = new Handler(); //獲取Looper對象 mLooper = Looper.myLooper(); //啓動消息循環 Looper.loop(); //在適當的時候退出Looper的消息循環,防止內存泄漏 mLooper.quit(); } }).start();
主線程中默認是建立了Looper而且啓動了消息的循環的,所以不會報錯:
應用程序的入口是ActivityThread的main方法,在這個方法裏面會建立Looper,而且執行Looper的loop方法來啓動消息的循環,使得應用程序一直運行。
能夠。有時候出於業務須要,主線程能夠向子線程發送消息。子線程的Handler必須按照上述方法建立,而且關聯Looper。
Android中View的機制主要是Activity的顯示,每一個Activity都有一個Window(具體在手機中的實現類是PhoneWindow),Window如下有DecorView,DecorView下面有TitleVie以及ContentView,而ContentView就是咱們在Activity中經過setContentView指定的。
ViewGroup有如下三個與事件分發的方法,而View只有dispatchTouchEvent和onTouchEvent。
@Override public boolean dispatchTouchEvent(MotionEvent ev) { return super.dispatchTouchEvent(ev); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { return super.onTouchEvent(event); }
事件老是從上往下進行分發,即先到達Activity,再到達ViewGroup,再到達子View,若是沒有任何視圖消耗事件的話,事件會順着路徑往回傳遞。其中:
View的測量最終是在onMeasure方法中經過setMeasuredDimension把表明寬高兩個MeasureSpec設置給View,所以須要掌握MeasureSpec。MeasureSpec包括大小信息以及模式信息。
MeasureSpec的三種模式:
下面給出模板代碼:
public class MeasureUtils { /** * 用於View的測量 * * @param measureSpec * @param defaultSize * @return */ public static int measureView(int measureSpec, int defaultSize) { int measureSize; //獲取用戶指定的大小以及模式 int mode = View.MeasureSpec.getMode(measureSpec); int size = View.MeasureSpec.getSize(measureSpec); //根據模式去返回大小 if (mode == View.MeasureSpec.EXACTLY) { //精確模式(指定大小以及match_parent)直接返回指定的大小 measureSize = size; } else { //UNSPECIFIED模式、AT_MOST模式(wrap_content)的話須要提供默認的大小 measureSize = defaultSize; if (mode == View.MeasureSpec.AT_MOST) { //AT_MOST(wrap_content)模式下,須要取測量值與默認值的最小值 measureSize = Math.min(measureSize, defaultSize); } } return measureSize; } }
最後,複寫onMeasure方法,把super方法去掉:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(MeasureUtils.measureView(widthMeasureSpec, 200), MeasureUtils.measureView(heightMeasureSpec, 200) ); }
View繪製,須要掌握Android中View的座標體系:
View的座標體系是以左上角爲座標原點,向右爲X軸正方向,向下爲Y軸正方向。
View繪製,主要是經過Android的2D繪圖機制來完成,時機是onDraw方法中,其中包括畫布Canvas,畫筆Paint。下面給出示例代碼。相關API不是介紹的重點,重點是Canvas的save和restore方法,經過save之後能夠對畫布進行一些放大縮小旋轉傾斜等操做,這兩個方法通常配套使用,其中save的調用次數能夠多於restore。
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Bitmap bitmap = ImageUtils.drawable2Bitmap(mDrawable); canvas.drawBitmap(bitmap, getLeft(), getTop(), mPaint); canvas.save(); //注意,這裏的旋轉是指畫布的旋轉 canvas.rotate(90); mPaint.setColor(Color.parseColor("#FF4081")); mPaint.setTextSize(30); canvas.drawText("測試", 100, -100, mPaint); canvas.restore(); }
與佈局位置相關的是onLayout方法的複寫,通常咱們自定義View的時候,只須要完成測量,繪製便可。若是是自定義ViewGroup的話,須要作的就是在onLayout中測量自身以及控制子控件的佈局位置,onLayout是自定義ViewGroup必須實現的方法。
<include android:id="@+id/v_test" layout="@layout/include_view" />
<ViewStub android:id="@+id/v_stub" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout="@layout/view_stub" /> //須要手動調用inflate方法,佈局纔會顯示出來。 stub.inflate(); //其中setVisibility在底層也是會調用inflate方法 //stub.setVisibility(View.VISIBLE); //以後,若是要使用ViewStub標籤裏面的View,只須要按照日常來便可。 TextView tv_1 = (TextView) findViewById(R.id.tv_1);
APP設計以及代碼編寫階段都應該考慮內存優化:
IntentService在內部實際上是經過線程以及Handler實現的,當有新的Intent到來的時候,會建立線程而且處理這個Intent,處理完畢之後就自動銷燬自身。所以使用IntentService可以節省系統資源。
@Override public void onLowMemory() { super.onLowMemory(); } @Override public void onTrimMemory(int level) { super.onTrimMemory(level); switch (level) { case TRIM_MEMORY_COMPLETE: //... break; case 其餘: } }
android:largeHeap="true"
分析方法 1\. 使用Android Studio提供的Android Monitors中Memory工具查看內存的使用以及沒使用的狀況。 2\. 使用DDMS提供的Heap工具查看內存使用狀況,也能夠手動觸發GC。 3\. 使用性能分析的依賴庫,例如Square的LeakCanary,這個庫會在內存泄漏的先後經過Notification通知你。
解決方案 1\. 應該儘可能避免static 成員變量引用資源耗費過多的實例,好比Context。 2\. Context 儘可能使用ApplicationContext,由於Application 的Context 的生命週期比較長,引用它不會出現內存泄露的問題。 3\. 使用WeakReference 代替強引用。好比可使用WeakReference<Context> mContextRef
解決方案 1\. 將線程的內部類,改成靜態內部類(由於非靜態內部類擁有外部類對象的強引用,而靜態類則不擁有)。 2\. 在線程內部採用弱引用保存Context 引用。
//開啓數據採集 Debug.startMethodTracing("test.trace"); //關閉 Debug.stopMethodTracing();
避免OOM的一些常見方法:
BitmapFactory.Options options = new BitmapFactory.Option(); options.inSampleSize = 2; //Options 只保存圖片尺寸大小,不保存圖片到內存 BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inSampleSize = 2; Bitmap bmp = null; bmp = BitmapFactory.decodeResource(getResources(), mImageIds[position],opts); //回收 bmp.recycle();
不一樣的組件發生ANR 的時間不同,主線程(Activity、Service)是5 秒,BroadCastReceiver 是10 秒。
ANR通常有三種類型:
主要類型按鍵或觸摸事件在特定時間內無響應
BroadcastReceiver在特定時間內沒法處理完成
小几率類型Service在特定的時間內沒法處理完成
解決方案: 1\. UI線程只進行UI相關的操做。全部耗時操做,好比訪問網絡,Socket 通訊,查詢大量SQL 語句,複雜邏輯計算等都放在子線程中去,而後經過handler.sendMessage、runonUITread、AsyncTask 等方式更新UI。 2\. 不管如何都要確保用戶界面操做的流暢度。若是耗時操做須要讓用戶等待,那麼能夠在界面上顯示進度條。 3\. BroadCastReceiver要進行復雜操做的的時候,能夠在onReceive()方法中啓動一個Service來處理。
點九圖,是Android開發中用到的一種特殊格式的圖片,文件名以」.9.png「結尾。這種圖片能告訴程序,圖像哪一部分能夠被拉昇,哪一部分不能被拉昇須要保持原有比列。運用點九圖能夠保證圖片在不模糊變形的前提下作到自適應。點九圖經常使用於對話框背景圖片中。
無論發生什麼,垃圾回收器都不會自動回收這些 Bitmap。當 Android 繪製系統在渲染這些圖片,Android 的系統庫就會把這些 Bitmap 從 Ashmem 堆中抽取出來,而當渲染結束後,這些 Bitmap 又會被放回到原來的位置。若是一個被抽取的圖片須要再繪製一次,系統僅僅須要把它再解碼一次,這個操做很是迅速。
傳統點的方法就是往同步代碼塊裏些數據,而後使用回調讓另一條線程去讀。在Android裏我通常會建立Looper線程,而後Hanlder傳遞消息。
這塊瞭解的很少。我給你說說個人思路吧,利用哈希算法,好比MD5,服務器給咱們的數據能夠經過時間戳和其餘參數作個加密,獲得一個key,在客戶端取出數據後根據數據和時間戳再去生成key與服務端給的作個對比。
RXJava:一個異步請求庫,核心就是異步。利用的是一種擴展的觀察模式,被觀察者發生某種變化的時候,能夠經過事件(onNext、onError、onComplete)等方式經過觀察者。RXJava同時支持線程的調度和切換,用戶能夠指定訂閱發生的線程以及觀察者觸發的線程。
Retrofit:經過註解的方式來指定URL、請求方法,實質上底層是經過OKHttp來實現的。
好了,今天的分享就到這裏,若是你對在面試中遇到的問題,或者剛畢業及工做幾年迷茫不知道該如何準備面試並突破現狀提高本身,對於本身的將來還不夠了解不知道給如何規劃,能夠加一下合做的技術羣:1018342383。來看看同行們都是如何突破現狀,怎麼學習的,來吸取他們的面試以及工做經驗完善本身的以後的面試計劃及職業規劃。
這裏放上一部分我工做以來以及參與過的大大小小的面試收集總結出來的一套 進階學習的視頻及面試專題資料包,在這裏[免費分享]給你們,主要仍是但願你們在現在大環境很差的狀況下面試可以順利一點,但願能夠幫助到你們~
領取方式:獲取免費架構視頻資料請加羣:1018342383
轉發+點贊+關注,第一時間獲取最新知識點
Android架構師之路很漫長,一塊兒共勉吧!