廣播
註冊方式:
1、靜態註冊 ,在Manifest檔案的application
節點中配置廣播接收者
<receiver android:name=".MyBroadCastReceiver">
<!-- android:priority屬性是設定此接收者的優先順序(從-1000到1000) -->
<intent-filter android:priority="20">
<actionandroid:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
</receiver>
複製程式碼
2、動態註冊,通過Context
物件的registerReceiver
方法註冊廣播
//new出上邊定義好的BroadcastReceiver
MyBroadCastReceiver yBroadCastReceiver = new MyBroadCastReceiver();
//例項化過濾器並設定要過濾的廣播
IntentFilter intentFilter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
//註冊廣播
myContext.registerReceiver(smsBroadCastReceiver,intentFilter,
"android.permission.RECEIVE_SMS", null);
複製程式碼
區別:靜態註冊的為常駐型廣播,即使應用程式關閉了,如果又資訊廣播來,程式也會被系統呼叫執行。而動態註冊的廣播不是常駐型,廣播被取消註冊或者應用程式關閉後都不能接收
廣播的兩種型別:
1、有序廣播:按照優先順序,一級一級向下傳遞,接收者可以修改廣播資料,也可以終止廣播事件。
2、無序廣播:所有接收者都會接收事件,不能被攔截跟修改。
服務
啟動
1、使用Context
的startService
方法啟動
onCreate()
--->onStartCommand()
--->onDestroy()
2、使用Context
的bindService
方法啟動
onCreate()
--->onBind()
--->onUnBind()
--->onDestroy()
停止
1、在外部使用stopService
方法,如果使用bindService
的方式啟動,則使用unbindService
方法停止
2、在Service
內部(onStartCommand
方法內)使用stopSelf
onStartCommand
方法的返回值
1、START_NOT_STICKY
:“非粘性的”。使用這個返回值時,如果在執行完onStartCommand
方法後,服務被異常kill掉,系統不會自動重啟該服務
2、START_STICKY
:如果Service程式被kill掉,保留Service的狀態為開始狀態,但不保留遞送的intent物件。隨後系統會嘗試重新建立Service,由於服務狀態為開始狀態,所以建立服務後一定會呼叫onStartCommand(Intent,int,int)方法。如果在此期間沒有任何啟動命令被傳遞到Service,那麼引數Intent將為null。
3、START_REDELIVER_INTENT
:重傳Intent。使用這個返回值時,系統會自動重啟該服務,並將Intent的值傳入。
IntentService
繼承於Service
,啟動方式與Service
的傳統啟動方式一樣,不同點在於內部有一個執行緒來處理耗時操作,當任務執行完成時服務會自動停止。
Activity的啟動模式
-
standard
:標準模式,預設的啟動模式,不管是否已經存在例項都會生成新的例項 -
singleTop
:棧頂複用模式,如果發現有對應Activity的例項正位於棧頂,則直接開啟此頁面,不再生成新的例項,同時onNewIntent
方法會被執行,onCreate
跟onStart
方法都不會執行。否則跟standard
模式一樣繼續生成新的例項。 -
singleTask
:站內複用模式,如果棧記憶體在對應Activity的例項就會複用這個Activity,複用時會將它上面的Activity全部出棧,同時onNewIntent
方法也會被執行。 -
singleInstance
:單例模式,該模式具備singleTask模式的所有特性外,與它的區別就是,這種模式下的Activity會單獨佔用一個Task棧,具有全域性唯一性。以singleInstance模式啟動的Activity在整個系統中是單例的,如果在啟動這樣的Activiyt時,已經存在了一個例項,那麼會把它所在的任務排程到前臺,重用這個例項。
Activity的啟動過程
app啟動的過程有兩種情況,第一種是從桌面launcher上點選相應的應用圖示,第二種是在activity中通過呼叫startActivity來啟動一個新的activity。
Luncher.startActivitySafely()
public final class Launcher extends Activity implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher { ...... void startActivitySafely(Intent intent, Object tag) { intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); try { startActivity(intent); } catch (ActivityNotFoundException e) { ...... } catch (SecurityException e) { ...... } } ...... } 複製程式碼
Activity.startActivity
public class Activity extends ContextThemeWrapper implements LayoutInflater.Factory, Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks { ...... @Override public void startActivity(Intent intent) { startActivityForResult(intent, -1); } ...... } 複製程式碼
Activity.startActivityForResult
public class Activity extends ContextThemeWrapper implements LayoutInflater.Factory, Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks { ...... public void startActivityForResult(Intent intent, int requestCode) { if (mParent == null) { Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity( this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode); ...... } else { ...... } ...... } 複製程式碼
Instrumentation.execStartActivity
public class Instrumentation { ...... public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode) { IApplicationThread whoThread = (IApplicationThread) contextThread; if (mActivityMonitors != null) { ...... } try { int result = ActivityManagerNative.getDefault() .startActivity(whoThread, intent, intent.resolveTypeIfNeeded(who.getContentResolver()), null, 0, token, target != null ? target.mEmbeddedID : null, requestCode, false, false); ...... } catch (RemoteException e) { } return null; } ...... } 複製程式碼
這裡的
ActivityManagerNative.getDefault
返回ActivityManagerService
的遠端介面,即ActivityManagerProxy
介面
ActivityManagerProxy.startActivity
class ActivityManagerProxy implements IActivityManager { ...... public int startActivity(IApplicationThread caller, Intent intent, String resolvedType, Uri[] grantedUriPermissions, int grantedMode, IBinder resultTo, String resultWho, int requestCode, boolean onlyIfNeeded, boolean debug) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeStrongBinder(caller != null ? caller.asBinder() : null); intent.writeToParcel(data, 0); data.writeString(resolvedType); data.writeTypedArray(grantedUriPermissions, 0); data.writeInt(grantedMode); data.writeStrongBinder(resultTo); data.writeString(resultWho); data.writeInt(requestCode); data.writeInt(onlyIfNeeded ? 1 : 0); data.writeInt(debug ? 1 : 0); mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0); reply.readException(); int result = reply.readInt(); reply.recycle(); data.recycle(); return result; } ...... } 複製程式碼
ActivityManagerService.startActivity
Context
Context是一個抽象基類,翻譯為上下文,也可以理解為環境,提供一些程式執行基礎資訊。
Context有兩個子類,ContextWrapper
是上下文功能的封裝類,而 ContextImpl
則是上下文功能的實現類。而 ContextWrapper
又有三個直接的子類, ContextThemeWrapper
、Service
和Application
。其中,ContextThemeWrapper
是一個帶主題的封裝類,而它有一個直接子類就是Activity,所以Activity和Service以及Application的Context是不一樣的,只有Activity需要主題,Service不需要主題。Context一共有三種型別,分別是Application、Activity和Service。這三個類雖然分別各種承擔著不同的作用,但它們都屬於Context的一種,而它們具體Context的功能則是由ContextImpl
類去實現的,因此在絕大多數場景下,Activity、Service和Application這三種型別的Context都是可以通用的。不過有幾種場景比較特殊,比如啟動Activity,還有彈出Dialog。出於安全原因的考慮,Android是不允許Activity或Dialog憑空出現的,一個Activity的啟動必須要建立在另一個Activity的基礎之上,也就是以此形成的返回棧。而Dialog則必須在一個Activity上面彈出(除非是System Alert型別的Dialog),因此在這種場景下,我們只能使用Activity型別的Context,否則將會出錯。
Activity、Window、View三者之間的關係
Activity
構造的時候會初始化一個Window(PhoneWindw
)PhoneWindow
有一個RootView
,這個RootView
是一個ViewGroup,是最初始的根檢視RootView
通過addView
方法來一個個新增View
View的繪製流程
View的繪製流程:onMeasure
-> onLayout
-> onDraw
第一步:onMeasure
測量檢視大小,從頂層父View到子View遞迴呼叫 measure
方法,measure
方法又回撥 onMeasure
方法。
第二步:onLayout
確定View位置,進行頁面佈局。從頂層父View向子View遞迴呼叫 layout
方法的過程,即父View根據上一步 measure
得到的佈局大小和佈局引數,將子View放在合適的位置上。
第三步:onDraw
繪製檢視。主要步驟為①:繪製背景,②:繪製自己,③:繪製子View,④:繪製滾動條
View、ViewGroup事件分發
ViewGroup 包含 dispatchTouchEvent
、onInterceptTouchEvent
、onTouchEvent
三個相關方法,View包含 dispatchTouchEvent
、onTouchEvent
兩個相關方法。
- 當
Activity
接收到Touch事件時,將遍歷子View進行Down事件分發,分發的目的是為了找到真正處理本次完整觸控事件的View,這個View會在onTouchEvent
返回true。 - 當某個子View返回true時,就終止事件分發,並同時在ViewGroup中記錄該View,接下來的move事件跟up事件都由該子View直接進行處理。
- 當ViewGroup所有子View都不捕獲Down事件時,將觸發ViewGroup自身的
onTouchEvent
事件。觸發的方式是呼叫super.dispatchTouchEvent
函式,即呼叫父View的dispatchTouchEvent
方法。
Handler實現原理
Android的主執行緒不能進行耗時操作,子執行緒不能進行更新UI,所以就有了Handler,它的作用就是實現執行緒之間的通訊。
Handler整個流程中主要有四個物件:Handler
、Message
、MessageQueue
、Looper
。通過將要傳遞的訊息放在Message
中,Handler
通過 sendMessage
方法將訊息放入 MessageQueue
中,Looper
物件會不斷的呼叫loop()
方法不斷從 MessageQueue
中取出 Message
交給 Handler
進行處理。
Android記憶體洩露
-
記憶體洩漏跟記憶體溢位的區別:
- 記憶體洩漏:指程式在申請記憶體後,無法釋放已經申請的記憶體空間
- 記憶體溢位:指程式在申請記憶體時,沒有足夠的記憶體空間供其使用
-
記憶體洩漏的原因:
-
Handler引起的記憶體洩漏:
將Handler宣告為靜態內部類,就不會持有外部類的引用,其生命週期就跟外部類無關。如果Handler內部要使用Context,則可以使用弱引用的方式。
-
單例模式引起的記憶體洩漏:
Context是ApplicationCotnext,ApplicationCotnext的生命週期與app一致,不會導致記憶體洩漏.
-
非靜態內部類建立例項引起的:
建立為靜態例項
-
非靜態匿名內部類引起的:
將匿名內部類修改為靜態的
-
註冊/反註冊未成對使用引起的記憶體洩漏
註冊廣播接受器、EventBus等,記得解綁
-
資源物件沒有關閉引起的記憶體洩漏
在這些資源不使用的時候,記得呼叫相應的類似close()、destroy()、recycler()、release()等方法釋放
-
集合物件沒有及時清理引起的記憶體洩漏
通常會把一些物件裝入到集合中,當不使用的時候一定要記得及時清理集合,讓相關物件不再被引用
-
-
記憶體洩漏檢測:LeakCanary
ANR
ANR全名"Application not responding",即應用無響應。產生的原因:
- 5s內無法響應使用者輸入事件
- 廣播在10s內無法結束
- Service在20s內無法結束