1、四大組件是什麼?
Activity【活動】:用於表現功能。
Service【服務】:後臺運行服務,不提供界面呈現。
BroadcastReceiver【廣播接收器】:用來接收廣播。
Content Provider【內容提供商】:支持在多個應用中存儲和讀取數據,相當於數據庫。
2、四個組件的生命週期?
Activity生命週期圖及 Fragment生命週期圖
Service的生命週期:首先Service有兩種啓動方式,而在這兩種啓動方式下,它的生命週期不同。
通過startService()方法啓動的服務
初始化結束後系統會調用 void onStart(Intent intent) 方法,用於處理傳遞給startService()的Intent對象。如音樂服務會打開Intent 來探明將要播放哪首音樂,並開始播放。注意:多次調用startService()方法會多次觸發onStart()方法。
通過bindService ()方法啓動的服務
初始化結束後系統會調用 IBinder onBind(Intent intent) 方法,用來綁定傳遞給bindService 的Intent 的對象。注意:多次調用bindService()時,如果該服務已啓動則不會再觸發此方法。
3、Activity的四種啓動模式對比?
Standard:標準的啓動模式,如果需要啓動一個activity就會創建該activity的實例。也是activity的默認啓動模式。
SingeTop:如果啓動的activity已經位於棧頂,那麼就不會重新創建一個新的activity實例。而是複用位於棧頂的activity實例對象。如果不位於棧頂仍舊會重新創建activity的實例對象。
SingleTask:設置了singleTask啓動模式的activity在啓動時,如果位於activity棧中,就會複用該activity,這樣的話,在該實例之上的所有activity都依次進行出棧操作,即執行對應的onDestroy()方法,直到當前要啓動的activity位於棧頂。一般應用在網頁的圖集,一鍵退出當前的應用程序。
singleInstance:如果使用singleInstance啓動模式的activity在啓動的時候會複用已經存在的activity實例。不管這個activity的實例是位於哪一個應用當中,都會共享已經啓動的activity的實例對象。使用了singlestance的啓動模式的activity會單獨的開啓一個共享棧,這個棧中只存在當前的activity實例對象。
4、Activity在有Dialog時按Home鍵的生命週期?
當我們的Activity上彈出Dialog對話框時,程序的生命週期依然是onCreate() ---> onStart() ---> onResume(),在彈出Dialog的時候並沒有onPause()和onStop()方法。而在此時我們按下Home鍵,纔會繼續執行onPause()和onStop()方法。這說明對話框並沒有使Activity進入後臺,而是在點擊了Home鍵後Activity才進入後臺工作。
原因就是,其實Dialog是Activity的一個組件,此時Activity並不是不可見,而是被Dialog組件覆蓋了其他的組件,此時我們無法對其他組件進行操作而已。
5、兩個Activity 之間跳轉時必然會執行的是哪幾個方法?
首先定義兩個Activity,分別爲A和B。
當我們在A中激活B時,A調用onPause()方法,此時B出現在屏幕時,B調用onCreate()、onStart()、onResume()。
這個時候B【B不是一個透明的窗體或對話框的形式】已經覆蓋了A的窗體,A會調用onStop()方法。
6、 前臺切換到後臺,然後再回到前臺,Activity生命週期回調方法。彈出Dialog,生命值週期回調方法?
首先定義兩個Activity,分別爲A和B。
完整順序爲:A調用onCreate()方法 ---> onStart()方法 ---> onResume()方法。當A啓動B時,A調用onPause()方法,然後調用新的Activity B,此時調用onCreate()方法 ---> onStart()方法 ---> onResume()方法將新Activity激活。之後A再調用onStop()方法。當A再次回到前臺時,B調用onPause()方法,A調用onRestart()方法 ---> onStart()方法 ---> onResume()方法,最後調用B的onStop()方法 ---> onDestory()方法。
彈出Dialog時,調用onCreate()方法 ---> onStart()方法 ---> onResume()方法。
7、fragment各種情況下的生命週期?
由於Fragment的生命週期與Activity的生命週期有着牽扯,所以把兩者的圖放到一起作爲對比理解。
接下來就不同情況下的Fragment生命週期做一簡單介紹:
Fragment在Activity中replace
新替換的Activity:onAttach() ---> onCreate() ---> onCreatView() ---> onViewCreated ---> onActivityCreated() ---> onStart --->onResume()
被替換的Activity:onPause() ---> onStop() ---> onDestoryView() ---> onDestory() ---> onDetach()
Fragment在Activity中replace,並addToBackStack
新替換的Fragment(沒有在BackStack中):onAttach > onCreate > onCreateView > onViewCreated > onActivityCreated > onStart > onResume
新替換的Fragment(已經在BackStack中):onCreateView > onViewCreated > onActivityCreated > onStart > onResume
被替換的Fragment:onPause > onStop > onDestroyView
Fragment在ViewPager中切換
我們稱切換前的的Fragment稱爲PreviousFragment,簡稱PF;切換後的Fragment稱爲NextFragment,簡稱NF;其他Fragment稱爲OtherFragment,簡稱OF。
(在ViewPager中setUserVisibleHint能反映出Fragment是否被切換到後臺或前臺,所以在這裏也當作生命週期)
如果相關的Fragment沒有被加載過:
NF: setUserVisibleHint(false)【用戶不可見】 > onAttach > onCreate > setUserVisibleHint(true)【用戶可見】 > onCreateView > onViewCreated > onActivityCreated > onStart > onResume
OF跟NF相鄰: setUserVisibleHint(false) > onAttach > onCreate > onCreateView > onViewCreated > onActivityCreated > onStart > onResume
如果相關的Fragment已經被加載過:
NF跟PF相鄰 :setUserVisibleHint(true)
NF跟PF不相鄰:setUserVisibleHint(true) > onCreateView > onViewCreated > onActivityCreated > onStart > onResume
PF跟NF相鄰 :setUserVisibleHint(false)
PF跟NF不相鄰:setUserVisibleHint(false) > onPause > onStop > onDestroyView
OF跟PF相鄰:onPause > onStop > onDestroyView
OF跟NF相鄰:onCreateView > onViewCreated > onActivityCreated > onStart > onResume
OF夾在PF和NF中間:不調用任何生命週期方法
NF跟PF相鄰 :setUserVisibleHint(true)
NF跟PF不相鄰:setUserVisibleHint(true) > onCreateView > onViewCreated > onActivityCreated > onStart > onResume
PF跟NF相鄰 :setUserVisibleHint(false)
PF跟NF不相鄰:setUserVisibleHint(false) > onPause > onStop > onDestroyView
OF跟PF相鄰:onPause > onStop > onDestroyView
OF跟NF相鄰:onCreateView > onViewCreated > onActivityCreated > onStart > onResume
OF夾在PF和NF中間:不調用任何生命週期方法
如果重寫了FragmentPagerAdapter的DestroyItem方法,並且相關的Fragment已經加載過:
相互切換時只會調用setUserVisibleHint
Fragment進入了運行狀態:
Fragment在進入運行狀態時,以下四個生命週期會隨它所屬的Activity一起被調用:
onPause() ---> onStop() ---> onStart() ---> onResume()
關於Fragment的onActivityResult方法:
使用Fragment的startActivity方法時,FragmentActivity的onActivityResult方法會回調相應的Fragment的onActivityResult方法,所以在重寫FragmentActivity的onActivityResult方法時,注意調用super.onActivityResult。
8、 如何實現Fragment的滑動?
將Fragment與viewpager綁定,通過viewpager中的touch事件,會進行move事件的滑動處理。
Fragment佈局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_red_light">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Fragment One" />
</LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_red_light">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Fragment Two" />
</LinearLayout>
Fragment代碼:
public class FragmentOne extends Fragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_one, container, false);
}
}
public class FragmentTwo extends Fragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_Two, container, false);
}
}
viewpager佈局:
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.spreadtrumshitaoli.fragmentscroll.MainActivity">
<android.support.v4.view.ViewPager
android:id="@+id/view_pager"
android:layout_height="match_parent"
android:layout_width="match_parent"/>
</android.support.constraint.ConstraintLayout>
MainActivity代碼:
public class MainActivity extends AppCompatActivity {
private FragmentOne fragmentOne;
private FragmentTwo fragmentTwo;
private ViewPager viewPager;
private ArrayList<Fragment> mFragmentList = new ArrayList <Fragment>();
private FragmentPagerAdapter fragmentPagerAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
private void init() {
viewPager = (ViewPager) findViewById(R.id.view_pager);
fragmentOne = new FragmentOne();
fragmentTwo = new FragmentTwo();
mFragmentList.add(fragmentOne);
mFragmentList.add(fragmentTwo);
//將adapter和fragment綁定在一起。
fragmentPagerAdapter = new FragmentPagerAdapter(getSupportFragmentManager()) {
@Override
public Fragment getItem(int i) {
return mFragmentList != null ? mFragmentList.get(i) : null;
}
@Override
public int getCount() {
return mFragmentList != null ? mFragmentList.size() : 0;
}
};
viewPager.setAdapter(fragmentPagerAdapter);
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int i, float v, int i1) {
}
@Override
public void onPageSelected(int i) {
//TODO:
}
@Override
public void onPageScrollStateChanged(int i) {
}
});
}
}
在這段代碼中,我們
首先fragment以及viewpager都實例化;
再將fragment添加到泛型arraylist裏;
最後將帶有fragment的arraylist和adapter綁定。
9、fragment之間傳遞數據的方式?
方法一:
1、在MainFragment中設置一個setData()方法,在方法中設置更改按鈕名稱;
//MainFragment.java文件中
public void setData(String string) {
bt_main.setText(string);
}
2、在MenuFragment中的ListView條目點擊事件中通過標籤獲取到MainFragment,並調用對應的setData()方法,將數據設置進去,從而達到數據傳遞的目的。
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
MainFragment mainFragment =
(MainFragment) getActivity()
.getSupportFragmentManager()
.findFragmentByTag("mainFragment");
mainFragment.setData(mDatas.get(position));
}
});
只需上面區區兩步即可達到數據傳遞的目的。
方法二:
採取接口回調的方式進行數據傳遞。
step1: 在Menuragment中創建一個接口以及接口對應的set方法:
//MenuFragment.java文件中
public interface OnDataTransmissionListener {
public void dataTransmission(String data);
}
public void setOnDataTransmissionListener(OnDataTransmissionListener mListener) {
this.mListener = mListener;
}
step2: 在MenuFragment中的ListView條目點擊事件中進行接口進行接口回調
//MenuFragment.java文件中
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
/**
* 方法二:採取接口回調的方式進行數據傳遞
*/
if (mListener != null) {
mListener.dataTransmission(mDatas.get(position));
}
}
});
step3: 在MainActivity中根據menuFragment獲取到接口的set方法,在這個方法中進行進行數據傳遞,具體如下:
//在MainActivity.java中
menuFragment.setOnDataTransmissionListener(new MenuFragment.OnDataTransmissionListener() {
@Override
public void dataTransmission(String data) {
mainFragment.setData(data); //注:對應的mainFragment此時應該要用final進行修飾
}
});
通過上面的三步也可以輕鬆做到Fragment數據之間的傳遞。
方法三:
使用三方開源框架:EventBus
那麼問題來了:EventBus是個啥東西???
簡單來說,EventBus是一款針對Android優化的發佈/訂閱(publish/subscribe)事件總線。主要功能是替代Intent,Handler,BroadCast在Fragment,Activity,Service,線程之間傳遞消息。簡化了應用程序內各組件間、組件與後臺線程間的通信。優點是開銷小,代碼更優雅,以及將發送者和接收者解耦。比如請求網絡,等網絡返回時通過Handler或Broadcast通知UI,兩個Fragment之間需要通過Listener通信,這些需求都可以通過EventBus實現。
下面我們就用EventBus來實現以下Fragment之間的數據傳遞:
step1:引入EventBus
compile 'org.greenrobot:eventbus:3.0.0'
step2:註冊事件接收者
這裏MainFragment是要接收MenuFragment發送來的數據,所以我們在MainFragment中的onCreateView()方法中進行註冊:
EventBus.getDefault().register(this);
step3:發送事件
注:發送事件之前其實還有一步定義事件類型,這裏我們傳遞的數據只有一個類型,所以這一步取消了。
MenuFragment發送數據給MainFragment,所以我們在MenuFragment中將要傳遞的數據進行發送事件操作:
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
EventBus.getDefault().post(mDatas.get(position));
}
});
step4:接收消息並處理
在MainFragment中我們接收來自MenuFragment傳遞過來的數據,並進行對應的處理(注:EventBus 3.0版本這一步必須要寫註解@Subscribe (與2.4版本有所區別)):
@Subscribe
public void onEvent(String data) {
bt_main.setText(data);
}
通過上面這一步即可完成數據之間的傳遞,需要注意的是在銷燬的時候我們要註銷事件接收。
step5:註銷事件接收
//MainFragment.java中
@Override
public void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
以上五步完成了Fragment之間的數據傳遞,看似比上面兩個方法要複雜的多,但當我們涉及到複雜的Fragment之間數據傳遞(例如Fragment中嵌套多層Fragment)時,就會體會到EventBus的爽快之處~~~這裏不進行贅述了。
10、Activity 怎麼和Service 綁定?
這需要實現service中的onBind()函數以返回service實例給activity
1、創建service類和activity類。
2、在service類中定義一個內部類繼承自Binder()類:
public class MyBinder extends Binder{
public Service1 getService(){
return Service1.this;
}
}
實例化onBind()方法:
private final IBinder binder = new MyyBinder();
@Override
public IBinder onBind(Intent intent){
Log.i(LOG,"onBind......");
return binder;
}
3、在activity中完成綁定
Intent intent = new Intent(Activity1.this,Activity2.class);
bindService(intent,conn,Context.BIND_AUTO_CREATE);
bindService的第二個參數是一個ServiceConnection類型的參數。service和其他組件之間的連接都表示爲一個ServiceConnection,要想將service和其他組件進行綁定,就需要實現一個新的ServiceConnection。
public ServiceConnection conn= new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
//當連接意外斷開時調用
Log.i(LOG, "onServiceDisconnected>>>>>>>>");
myservice = null;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//當建立連接時調用
Log.i(LOG, "onServiceConnected>>>>>>>>");
myservice = ((Service1.MyBinder)service).getService();
}
};
bindService的第三個參數是一個flag。
可以使用的flag有:
BIND_AUTO_CREATE:綁定完成後就啓動目標service
BIND_DEBUG_UNBIND:這隻在debug時使用,跟unbind有關。
BIND_NOT_FOREGROUND:確保被綁定的service永遠不會有運行於前臺的優先級,因爲默認情況下,綁定一個service會提高它的優先級
BIND_ABOVE_CLIENT:確保客戶端處於前臺時,綁定的service也變爲前臺進程
BIND_ALLOW_OOM_MANAGEMENT:允許系統在低內存等狀態下刪除該service(這是自己對源碼中註釋的理解)
BIND_WAIVE_PRIORITY:綁定service時不改變其優先級
BIND_ADJUST_WITH_ACTIVITY:系統根據service所綁定的activity的重要程度來調整這個service的優先級。
11、service生命週期?
1. 被啓動的服務(startService())的生命週期。
如果一個Service被某個Activity 調用Context.startService() 方法啓動,那麼不管是否有Activity使用bindService()綁定或unbindService()解除綁定到該Service,該Service都在後臺運行。如果一個Service被多次執行startService(),它的onCreate()方法只會調用一次,也就是說該Service只會創建一個實例,而它的onStartCommand()將會被調用多次(對應調用startService()的次數)。該Service將會一直在後臺運行,直到被調用stopService(),或自身的stopSelf方法。當然如果系統資源不足,系統也可能結束服務。
2. 被綁定的服務(bindService())的生命週期。
如果一個Service被調用 Context.bindService ()方法綁定啓動,不管調用bindService()調用幾次,onCreate()方法都只會調用一次,而onStartCommand()方法始終不會被調用,這時會調用onBind()方法。當連接建立之後,Service將會一直運行,除非調用Context.unbindService() 斷開連接或者之前調用bindService() 的 Context 不存在了(如該Activity被finish),系統將會自動停止Service,對應onDestroy()將被調用。
3. 被啓動又被綁定的服務的生命週期。
如果一個Service又被啓動又被綁定,則該Service將會一直在後臺運行。調用unbindService()將不會停止Service,而必須調用stopService()或Service的stopSelf()方法來停止服務。
4. 當服務被停止時清除服務。
當一個Service被終止時,Service的onDestroy()方法將會被調用,在這裏應當做一些清除工作,如停止在Service中創建並運行的線程等。
12、 activity和service的綁定方式以及怎麼在Activity 中啓動自己對應的Service?
1、activity能進行綁定得益於Serviece的接口。爲了支持Service的綁定,實現onBind方法。
2、Service和Activity的連接可以用ServiceConnection來實現。需要實現一個新的ServiceConnection,重現onServiceConnected和OnServiceDisconnected方法,一旦連接建立,就能得到Service實例的引用。
3、執行綁定,調用bindService方法,傳入一個選擇了要綁定的Service的Intent(顯示或隱式)和一個你實現了的ServiceConnection的實例
13、Service的啓動方式?
採用Context.startService()方法啓動服務,在服務未被創建時,系統會先調用服務的onCreate()方法,接着調用onStart()方法。如果調用startService()方法前服務已經被創建,多次調用startService()方法並不會導致多次創建服務,但會導致多次調用onStart()方法。採用startService()方法啓動的服務,只能調用Context.stopService()方法結束服務,服務結束時會調用onDestroy()方法。
採用Context.bindService()方法啓動服務,在服務未被創建時,系統會先調用服務的 onCreate()方法,接着調用onBind()方法。這個時候調用者和服務綁定在一起,調用者退出了,系統就會先調用服務的onUnbind()方 法,接着調用onDestroy()方法。如果調用bindService()方法前服務已經被綁定,多次調用bindService()方法並不會導致 多次創建服務及綁定(也就是說onCreate()和onBind()方法並不會被多次調用)。如果調用者希望與正在綁定的服務解除綁定,可以調用 unbindService()方法,調用該方法也會導致系統調用服務的onUnbind()-->onDestroy()方法。
14、談談ContentProvider、ContentResolver、ContentObserver之間的關係?
ContentProvider:
* 四大組件的內容提供者,主要用於對外提供數據
* 實現各個應用程序之間的(跨應用)數據共享,比如聯繫人應用中就使用了ContentProvider,你在自己的應用中可以讀取和修改聯繫人的數據,不過需要獲得相應的權限。其實它也只是一箇中間人,真正的數據源是文件或者SQLite等
* 一個應用實現ContentProvider來提供內容給別的應用來操作,通過ContentResolver來操作別的應用數據,當然在自己的應用中也可以
ContentResolver:
* 內容解析者,用於獲取內容提供者提供的數據
* ContentResolver.notifyChange(uri)發出消息
ContentObserver:
* 內容監聽器,可以監聽數據的改變狀態
* 目的是觀察(捕捉)特定Uri引起的數據庫的變化,繼而做一些相應的處理,它類似於數據庫技術中的觸發器(Trigger),當ContentObserver所觀察的Uri發生變化時,便會觸發它。觸發器分爲表觸發器、行觸發器,相應地ContentObsever也分爲表ContentObserver、行ContentObserver,當然這是與它所監聽的Uri MIME Type有關的
* ContentResolver.registerContentObserver()監聽消息
15、廣播的分類?
分爲有序廣播和無序廣播兩類。
無序廣播發送代碼:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void click(View v){
//啓動界面 startActivity
//發送廣播 sendBroadcast
Intent intent = new Intent();
intent.setAction("com.itheima.cctv.action.NEWS");
intent.putExtra("data", "我是一個無須的廣播");
sendBroadcast(intent);
}
}
無序廣播的監聽代碼:
public class CctvReceiver extends BroadcastReceiver {
private static final String TAG = "CctvReceiver";
@Override
public void onReceive(Context context, Intent intent) {
String data = intent.getStringExtra("data");
Log.d(TAG, "data==="+data);
}
}
<receiver android:name="com.itheima.cctv.CctvReceiver">
<intent-filter >
<!--這裏監聽的廣播就是上面發送廣播設置的intent.setAction("com.itheima.cctv.action.NEWS");-->
<action android:name="com.itheima.cctv.action.NEWS"/>
</intent-filter>
</receiver>
有序廣播發送:
public class ShengReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.d("vivi", "我是省級部門,我收到的指令是:"+getResultData());
//getResultData()是用來獲取有序廣播裏面的數值.這裏的信息是:
//主席講話:每人獎勵10斤土豆
setResultData("主席講話:每人獎勵7斤土豆");//有序廣播的數值,可以被修改,後面的程序在接受到這個廣播,就會變成,現在我們改變的值了
//有序廣播傳輸是可以終止的.但是最終的接受者就算在終止之後,也是可以接受到數據的
//abortBroadcast();
}
}
public class ShiReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.d("vivi", "我是市級部門,我收到的指令是:"+getResultData());
//因爲上面修改了數據,所以這裏獲取到的數據是:主席講話:每人獎勵7斤土豆
}
}
<!--有序廣播的優先級別使用 android:priority=""來指定,最高是1000,最低是-1000-->
<receiver android:name="com.itheima.region.ShengReceiver">
<intent-filter android:priority="1000">
<action android:name="com.itheima.gov.action.POTATO"/>
</intent-filter>
</receiver>
<receiver android:name="com.itheima.region.ShiReceiver">
<intent-filter android:priority="500">
<action android:name="com.itheima.gov.action.POTATO"/>
</intent-filter>
</receiver>
有序接收:
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.d("vivi", "我是恩恩主席的內線,我收到的指令是:"+getResultData());
}
}
啓動模式:
1、standard:標準化啓動模式
每啓動一個Activity,都會重新創建Activity的新的實例,將其放在棧的頂部。不需要考慮這個實例是否已經存在。
每一次啓動,它的onCreate()、onStart()、onResume()方法都會被依次調用。
2、singleTop:棧頂複用模式
當前棧中已經有該Activity實例,並且該實例位於棧頂時,會去調用onNewIntent()方法。
當前棧中已有該Activity的實例但是該實例不在棧頂時,依然會去創建Activity。
當前棧中不存在該Activity實例時,會去新創建一個該Activity。
應用場景:IM對話框、新聞客戶端推送。
3、singleTask:棧內複用模式
它主要檢測【尋找,通過taskAffinity】整個棧中是否已經存在當前想要啓動的Activity,存在的話直接將該Activity置於棧頂,之前位於該Activity上面的Activity將被銷燬,同時調用onNewIntent()方法,而不存在的話進行創建。
應用場景:應用主界面。
4、singleInstance:
一個人獨享一個任務棧。當該Activity啓動時,系統會創建一個新的任務棧,同時將Activity放到這個新的任務棧當中,有別的應用來啓動該Activity時,由於棧內複用的特性,不會再去創建相應Activity任務棧,而是這兩個應用獨享一個Activity實例。
例如:應用A中現有兩個Activity E、Activity F,爲standard啓動模式,應用B中有一個Activity G,但其啓動模式是singleInstance。應用A想用應用B任務棧當中的Activity G,儘管在不同的應用下,但是應用A仍然會直接複用Activity G。
特性:
1、以SingleInstance模式啓動的Activity具有全局唯一性【全局唯一性即指在整個系統當中只會存在一個這樣的實例】;
2、如果在啓動這樣一個Activity時,【整個系統都是單例的】,已經存在了一個實例;
3、以SingleInstance模式啓動的Activity具有獨佔性。
應用場景:呼叫來電。
問題:onNewIntent()調用時機?
singleTop:如果新Activity已經位於任務棧的棧頂,就不會重新創建,並回調 onNewIntent(intent) 方法。
singleTask:只要該Activity在一個任務棧中存在,都不會重新創建,並回調 onNewIntent(intent) 方法。
網絡協議:
協議:【協議指計算機通信網絡中兩臺計算機之間進行通信所必須共同遵守的規定或規則】
HTTP協議
基本概念:【超文本傳輸協議】允許將HTML(超文本標記語言)文檔從Web服務器傳送到客戶端的瀏覽器。HTTP協議是 基於TCP/IP通信協議來傳輸數據的,可以從服務器端獲取圖片等數據資源。
URI:【uniform resource identifier】統一的資源標識符,用來唯一的標識一個資源。強調資源!!!
組成部分:
1)訪問資源的命名機制;file
2)存放資源的主機名;
3)資源自身的名稱,由路徑表示,着重於強調資源。
例:file://a:1234/b/c/d.txt 表示資源目標在a主機下的1234端口的b目錄下的c目錄下的d.txt文件。
URL:【uniform resource Location】統一資源定位器,是一種具體的URI。即URL可以用來標識一個資源,而且還指明瞭如 何定位這個資源。強調路徑!!!
組成部分:
1)協議;
2)存有該資源的主機IP地址;
3)主機資源的具體地址。
HTTP協議特點:
1)簡單快速;
2)無連接;【限制每次鏈接只處理一個請求,服務器處理完客戶的請求之後會收到客戶端的應答,再斷開鏈 接,節省了重複的時間】;
3)無狀態:【沒有記憶能力,】
HTTP協議的request/response請求頭原理剖析:
request有可能經過代理服務器到達web服務器
代理服務器最主要的作用:提高訪問速度【大部分代理服務器都具有緩存功能,當你再次訪問前一個網絡請求時,就可以直 接從代理服務器中獲取,而不需要請求我們的web服務器】。
HTTP協議容易混淆知識點:
(1)http1.0與http1.1的具體區別:
http處於計算機網絡的應用層。
1)緩存處理
2)帶寬優化及網絡連接的使用
3)Host頭使用:1.1上請求操作和響應操作都支持Host頭,而且在請求消息中如果沒有Host頭的話會報告一個400錯 誤。
4)長連接:在一個TCP連接上,可以傳送多個HTTP請求和響應,而不是發送一個HTTP請求就斷開一個連接,再發 送一個HTTP請求再建立一個連接。
存在的問題:
1)傳輸數據時,每次都需要重新創建連接,增加了大量的延遲時間;
2)傳輸數據時,所有傳輸的內容都是明文,客戶端和服務器端都無法驗證對方的身份;
3)使用時,header裏攜帶的內容過大,增加了傳輸成本。
(2)get / post方法的區別:
1)提交的數據:get提交的數據一般會放在我們的URL之後,用“ ?”來分割;而post提交數據都會放在我們entity- body【消息主體】當中。
2)提交的數據大小是否有限制:get提交的數據是有限制的,因爲url是有限制的,不能無限制的輸入一個url地址;而 post方法提交的是body,因此沒有限制。
3)取得變量的值:get方法通過Request.QueryString()來取得變量的值,而post方法通過Request.Form來取得變量的 值。
4)安全問題:get提交數據一定會帶來安全問題
(3)Cookie和Session的區別:
1)cookie【用戶身份的標識】:客戶端的解決方案。是由服務器發給客戶端的特殊信息,而這些信息以文本文件的方 式存放在客戶端,然後客戶端每次向服務器發送請求的時候都會帶上這些特殊的信息。存放在響應頭裏面。
客戶端 向服務端發送一個HTTP Request請求;
服務端給客戶端一個HTTP Response ,並且把它的cookies設置給我們的客戶端;
客戶端將HTTP Request和cookies打包給我們的服務端;
服務端會根據客戶端給我們的cookies來進行指定的判斷,返回HTTP Response給我們的客戶端。
此方法彌補了我們HTTP協議無狀態的不足。之前當上述請求響應操作完成後,服務端和客戶端就斷開連接,服務端 就無法從連接上跟蹤我們所想要的客戶端。
2)session:另一種記錄客戶狀態的限制,cookie保存在客戶端瀏覽器中,而session保存在服務器上。客戶端瀏覽器 訪問服務器時,服務器把客戶端信息以某種形式記錄在服務器上。
創建session;
在創建session同時,服務器會爲該session生成唯一的session id;
在session被創建之後,就可以調用session相關的方法往session中增加內容;
當客戶端再次發送請求的時候,會將這個session id帶上,服務器接收到請求之後就會依據session id找到相 應的session。
3)區別:
存放的位置不同;
存取的方式不同【cookie保存的是Ascll碼字符串,而session中能夠保存任何類型的數據】;
安全性上的不同【cookie存放在客戶端瀏覽器中,對我們客戶端可見,客戶端的一些程序就有可能去修改我們 cookie的內容,而session則不然,它存儲在服務端上,對客戶端是透明的,不存在敏感信息泄露的風險】;
有效期上的不同【一般我們會設置cookie過期時間, session依賴 id,若id設置爲-1,當關掉瀏覽器session就 會失效】;
對服務器的造成的壓力不同【cookie保存在客戶端不佔用客戶資源,session保存在服務端,每一個用戶都會 產生一個session。在併發很多用戶時cookie是一個很好的選擇】。
HTTPS協議:
基本概念:對工作在以加密連接(SSL / TLS)上的常規HTTP協議。通過在TCP和HTTP之間加入TLS【Transport Layer Security】來加密。
SSL / TLS協議:安全傳輸協議,TLS是SSL的升級版,也是現階段所使用的協議;
HTTPS傳輸速度:
1)通信慢;
2)SSL必須進行加密處理。
TLS / SSL握手:
1)密碼學原理
對稱加密:加密數據所用的密鑰和解密數據所用的密鑰相同。
非對稱加密:分私有密鑰和公有密鑰。
2)數字證書:互聯網通訊中標誌通訊各方身份信息的一串數字。
3)握手過程
Handler【Android SDK提供給開發者方便進行異步消息處理的類】:
AsyncTask、retrofit都對Handler進行了封裝。
(1)Handler四大組件
1)Message
Message是在線程之間傳遞的消息,它可以在內部攜帶少量的信息,用於在不同線程之間交換數據。
例:Message的what字段、arg1字段、arg2字段來攜帶整型數據,obj字段攜帶一個Object對象。
2)Handler
處理者,它主要用來發送和處理消息。發送消息一般是使用Handler的sendMessage()方法,消息經過處理後,最終 傳遞到Handler的handlerMessage()方法中。
3)MessageQueue
消息隊列,它主要用來存放所有通過Handler發送的消息,這部分消息會一直存在於消息隊列中,等待被處理。
注意:每個線程中只會有一個MessageQueue對象。
4)Looper
是每個線程中MessageQueue的管家,調用Looper的loop()方法後,就會進入到一個無限循環當中,每當發現 MessageQueue中存在一條消息,就會將其取出傳遞到Handler的handleMessage()方法當中。
注意:每個線程中只會有一個Looper對象。
異步消息處理流程:
1)在主線程當中創建一個Handler對象;
2)重寫handleMessage()方法;
3)當子線程需要進行UI操作時,創建一個Message對象,並通過Handler將消息發送出去;
4)消息添加到MessageQueue的隊列中等待被處理;
5)Looper在MessageQueue中取出待處理消息,發回Handler的handleMessage()方法中。
【由於Handler是在主線程中創建的,因此我們的handleMessage()方法中的代碼也會在主線程中執行,避免了異常的 產生】
Handler消息機制:
作用:跨線程通信。當子線程中進行耗時操作後需要更新UI時,通過Handler將有關UI的操作切換到主線程中執行。
四要素:
Message(消息):需要被傳遞的消息,其中包含了消息ID,消息處理對象以及處理的數據等,由MessageQueue統一列隊,最終由Handler處理。
MessageQueue(消息隊列):用來存放Handler發送過來的消息,內部通過單鏈表的數據結構來維護消息列表,等待Looper的抽取。
Handler(處理者):負責Message的發送及處理。通過 Handler.sendMessage() 向消息池發送各種消息事件;通過 Handler.handleMessage() 處理相應的消息事件。
Looper(消息泵):通過Looper.loop()不斷地從MessageQueue中抽取Message,按分發機制將消息分發給目標處理者。
具體流程如圖
Handler.sendMessage()發送消息時,會通過MessageQueue.enqueueMessage()向MessageQueue中添加一條消息;
通過Looper.loop()開啓循環後,不斷輪詢調用MessageQueue.next();
調用目標Handler.dispatchMessage()去傳遞消息,目標Handler收到消息後調用Handler.handlerMessage()處理消息。
原文:https://blog.csdn.net/m0_37868230/article/details/81457720