編輯:關於android開發
1、概念
Application:由多個相關的松散的與用戶進行交互Activity組成,通常被打包成apk後綴文件中;
Activity:就是被用來進行與用戶交互和用來與android內部特性交互的組件,是應用程序的核心;
ActivityStack:將應用程序中打開的Activity保存在歷史棧中;Start Activity時入棧,返回時出棧;
AcitivityManager(Service):Activity管理的核心,是一個獨立的進程,負責管理Activity的生命周期;
Task:任務棧,將一系列相關的Activity組合(可以包含多個APP),完成某個應用程序完整操作;
ActivityThread:每一個應用程序所在進程的主線程,循環的消息處理;
ApplicationThread: Binder對象,完成ActivityThread與AcitivityManager的通信,屬於進程間通信;
2、 Activity啟動過程
下面簡要介紹一下Activity啟動的過程:(對照以上時序圖)
在APK文件安裝的時候,PackageManager會解析APK中重要的AndroidManifest.xml文件,你在其中注冊過的所有Activity和Service等四大組件的信息,也就會在此刻被PM獲取到並存儲起來。
Step1:無論是通過Launcher來啟動Activity,還是通過Activity內部調用startActivity接口來啟動新的Activity,都通過Binder進程間通信進入到ActivityManager進程中,並且調用ActivityManager.startActivity接口;
Step2:ActivityManager調用ActivityStack.startActivityMayWait來做准備要啟動的Activity的相關信息;
Step3:ActivityStack通知ApplicationThread要進行Activity啟動調度了,這裡的ApplicationThread代表的是調用ActivityManager.startActivity接口的進程,對於通過點擊應用程序圖標的情景來說,這個進程就是Launcher了,而對於通過在Activity內部調用startActivity的情景來說,這個進程就是這個Activity所在的進程了;
Step4:ApplicationThread不執行真正的啟動操作,它通過調用ActivityManager.activityPaused接口進入到ActivityManager進程中,看看是否需要創建新的進程來啟動Activity;
Step5:對於通過點擊應用程序圖標來啟動Activity的情景來說,ActivityManager在這一步中,會調用startProcessLocked來創建一個新的進程,而對於通過在Activity內部調用startActivity來啟動新的Activity來說,這一步是不需要執行的,因為新的Activity就在原來的Activity所在的進程中進行啟動;
Step6:調用ApplicationThread.scheduleLaunchActivity通知相應的進程ActivityThread執行啟動Activity的操作;
Step7:ApplicationThread把這個啟動Activity的操作轉發給ActivityThread,ActivityThread通過ClassLoader導入相應的Activity類,然後把它啟動起來。
3、 Activity和View的關系
主要涉及的對象:
Window 類是一個抽象類,提供了繪制窗口的一組通用API。
PhoneWindow類繼承於Window類,是Window類的具體實現,即我們可以通過該類具體去繪制窗口。該類內部包含了一個DecorView對象,並對其進行一定的包裝,將它作為根View,並提供一組通用的窗口操作接口。
DecorView類是PhoneWindow類的內部類。該類是一個FrameLayout的子類(聯系<Merge>標簽),並且是PhoneWindow的子類,該類就是對普通的FrameLayout進行功能的擴展,更確切點可以說是修飾(Decor的英文全稱是Decoration),比如說添加TitleBar(標題欄),以及TitleBar上的滾動條等 。它是所有應用窗口的根View!
WindowManager是Android中一個重要的服務(Service)。WindowManager Service 是全局的,是唯一的。它將用戶的操作,翻譯成為指令,發送給呈現在界面上的各個Window。
ViewRoot是GUI管理系統與GUI呈現系統之間的橋梁,根據ViewRoot的定義,我們發現它並不是一個View類型,而是一個Handler,連接的是PhoneWindow跟WindowManagerService。
它的主要作用如下:(WindowManager讀取android系統裡所有事件通過這個ViewRoot分發到各個activity)
A. 向DecorView分發收到的用戶發起的event事件,如按鍵,觸屏,軌跡球等事件;
B. 與WindowManager Service交互,完成整個Activity的GUI的繪制。
Activity創建View的流程如下:
Activity創建後,系統會調用其attach方法,將其添加到ActivityThread當中,在attach方法中創建了一個PhoneWindow對象實例,要注意PhoneWindow對象創建時並沒有創建Decor對象。當我們調用Acitivity的setContentView()時,實際上是調用的PhoneWindow對象的setContentView方法,這時會檢查 DecorView是否存在,如果不存在則(才)創建DecorView對象,將其設置為所有窗口的根View,然後把自己的View添加到DecorView中。
Android中的視圖都是通過Window來呈現的,然後通過WindowManager來管理View。
在建立PhoneWindow的過程中,會得到WindowManager實例,系統會調用WindowManger的addView(decor,l),把DecorView加入到WindowManagerProxy的mViews(保存View的數組)中,即Activity會將DecorView注冊到WindowManager中。
WindowManger在保存好這個DecorView對象的同時,也會新創建一個ViewRoot對象用來溝通WindowManager。這樣,當用戶觸碰屏幕或鍵盤的時候,WindowManager就會通知到;而當控件有一些請求產生,也會經由ViewParent送回到Window Manager中,從而完成整個通信流程。Activity是Android應用程序的載體,允許用戶在其上創建一個用戶界面,並提供用戶處理事件的API,如 onKeyEvent,onTouchEvent等,並維護應用程序的生命周期。Activity也可以理解成Android應用程序的入口,系統服務ActivityManager負責維護Activity的實例對象,並根據運行狀態維護其狀態信息。
4、 Activity編程
Activity之間通過Intent進行通信,android應用中每一個Activity都必須要在AndroidManifest.xml配置文件中聲明,否則系統將不識別也不執行該Activity。Activity的加載模式(launchMode)受啟動Activity的Intent對象中設置的Flag和manifest文件中Activity的元素的特性值交互控制。
l launchMode:
大致生命周期:onCreate >>onStart>> onResume>>onPause>> onStop>> onDestroy
SingleTask時有 onStop>> (onNewIntent>>) onRestart>>onStart>>onResume
橫豎屏切換:onSaveInstance->onPause->onStop->onDestory->onCreate->onStart->onRestoreInstance->onResume
launchMode為singleTask的時候,通過Intent啟到一個Activity,如果系統已經存在一個實例,系統就會將請求發送到這個實例上,但這個時候,系統就不會再調用通常情況下我們處理請求數據的onCreate方法,而是調用onNewIntent方法,類似onCreate用法,需要更新intent:調用setIntent(intent)
l 打開Activity:
1、startActivity( ) :僅僅是跳轉到目標頁面,若是想跳回當前頁面,則必須再使用一次startActivity( )。
2、startActivityForResult( ) :可以一次性完成這項任務,當程序執行到這段代碼的時候,假若從A跳轉到下一個B,而當這個B調用了finish()方法以後,程序會自動跳轉回A,並調用A中的onActivityResult( )方法。
A: 調用 startActivityForResult(intent, RequestCode); //此處的RequestCode的值要大於0;
並重寫 onActivityResult(int requestCode, int resultCode, Intent data)
B: setResult(RESULT_OK, intent); // RESULT_OK為resultCode
finish();
退出單一Activity:finish()
退出多Activity:
1) 記錄打開的Activity:自定義一個單例模式的Activity棧來管理所有Activity,在需要退出時,逐個關閉即可。
2) 發送特定廣播:在需要結束應用時,發送一個特定的廣播,每個Activity收到廣播後,關閉即可。
3) 遞歸退出:都使用startActivityForResult,然後自己加標志,在onActivityResult中處理,遞歸關閉。
4) 拋異常強制退出:通過拋異常,使程序Force Close。問題是如何使程序結束而不彈出Force Close窗口
5) 通用方法:Dalvik VM的本地方法
android.os.Process.killProcess(android.os.Process.myPid()) //獲取PID
System.exit(0); //常規Java、c#的標准退出法,返回值為0代表正常退出
另:在Intent中加入標志Intent.FLAG_ACTIVITY_CLEAR_TOP,這打開時將會清除該進程空間的所有Activity。
1) 不設置Activity的configChanges 屬性時,切屏會重新調用各個生命周期(見上),切橫屏會執行一次,豎屏兩次。
2) 設置Activity的android:configChanges=”orientation”時,橫豎屏切換都只執行一次
3) 設置Activity的android:configChanges=”orientation|keyboardHidden”時,屏幕切換不會重新調用個各生命周期,只會執行:onConfigurationChange()。
在實際應用中,當一個Activity結束前,如果需要保存狀態,就在onsaveInstanceState()中,將狀態數據以key-value的形式放入到savedInstanceState()中。這樣,當一個Activity被創建時,就能從onCreate的參數savedInsanceState中獲得狀態數據。
onsaveInstanceState ()觸發條件(五個):按下HOME鍵時;長按HOME鍵選擇運行其他程序時;關閉屏幕顯示時;啟動一個新的Activity時;屏幕方向切換時。(當系統“未經你許可”時銷毀了你的activity時)
與之配套的有onRestoreInstanceState()方法,但不一定成對出現。這個方法被調用的前提是activity“確實”被系統銷毀了。它的bundle參數也會傳遞到onCreate方法中,你也可以選擇在onCreate方法中做數據還原。
5、Activity 和Fragment
1) Fragment中通過getActivity()獲得Activity對象,調用Activity中的公有方法((RootActivity)getActivity()).fun();
2) Activity實現一個接口,Fragment在onAttach()方法中,將該Activity轉化為該接口,在需要調用的時候回調。
3) Activity在切換Fragment的時候,通過setArguments向Fragment傳遞參數,Fragment通過getArguments()取值。
1) FragmentTransaction的add()方法,添加一個Fragment;
2) FragmentTransaction的popBackStack()彈出
public class MainActivity extends ActionBarActivity implements FragmentCallBack{
private Button btn;
private MyFragment1 fragment1;
private MyFragment2 fragment2;
private FragmentManager fragmentManager;
private Fragment currentFragment;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out);
fragment1 = new MyFragment1();
Bundle data = new Bundle();
data.putString("TEXT", "這是Activiy通過Bundle傳遞過來的值");
fragment1.setArguments(data);//通過Bundle向Fragment中傳遞值,在onCreateView中getArguments()
fragmentTransaction.add(R.id.rl_container, fragment1);//將fragment1設置到Activity的布局rl_container上
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commitAllowingStateLoss();
currentFragment = fragment1;
btn = (Button)findViewById(R.id.btn);
btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if(currentFragment instanceof MyFragment1){
if(null == fragment2) //可以避免切換的時候重復創建
fragment2 = new MyFragment2();
……//設值,同fragment1
fragmentTransaction.replace(R.id.fl, fragment2); // 替代fl的控件
fragmentTransaction.commit();
currentFragment = fragment2;
}else{ //當前是fragment2,因此,只需要將fragment2出棧即可變成fragment1
fragmentManager.popBackStack();
currentFragment = fragment1;
}
}
});
}
@Override
public void callbackFun1(Bundle arg) {…… }
}
和Activity類似,可以用Bundle對象保存Fragment的狀態,當重建activity時,可以用於恢復Fragment的狀態。存儲是利用onSaveInstanceState()回調函數,恢復時是在onCreate,onCreateView或onActivityCreated裡。
Activity停止時,時存在一個由系統維護的backsatck中;但是當Fragment停止(被remove)時,需要程序員顯示調用addToBackStack(),並且Fragment是保存在一個由宿主activity掌管的backstack中。
擁有Fragment的Activity的生命周期直接影響了其中的Fragment的生命周期,這樣,針對Activity的每一個生命周期的回調都會有一個類似的針對Fragment的回調。例如,當Activity收到onPause()回調時,在Activity中每個Fragment都會收到onPause()回調。但是,Fragment有幾個額外的生命周期回調方法,用來處理跟Activity的交互,以便執行諸如創建和銷毀Fragment的UI的動作。
這些額外的回調方法如下:
如下圖中說明的那樣,Fragment的生命周期流收到持有這些Fragment的Activity的影響,在這個圖中,你能看到每個連續的Activity狀態決定了Fragment的那個回調方法可以被調用。
例如,當Activity已經收到了onCreate()的回調之後,在Activity中的Fragment就不會再接收onActivityCreated()以上的回調了。一旦Activity到達了被恢復的狀態,你就可以自由的給這個Activity添加和刪除Fragment了,只有Activity在恢復態時,Fragment的生命周期才能獨立的改變。但是,當Activity離開恢復態時,Fragment會再次被推進Activity的生命周期中。
6、ViewPager+Fragment實現滑動標簽頁
1)主布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<include layout="@layout/activity_main_top_tab" />
<android.support.v4.view.ViewPager
android:id="@+id/id_page_vp"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" >
</android.support.v4.view.ViewPager>
</LinearLayout>
注意:需要引入android-support-v4.jar(正常情況系統會自動引入)
2)加載代碼
public class MainActivity extends FragmentActivity {
private ViewPager mPager;
private ArrayList<Fragment> fragmentList;
private ImageView image;
private TextView view1, view2, view3;
private int currIndex; // 當前頁卡編號
private int bmpW; // 橫線圖片寬度
private int offset; // 圖片移動的偏移量
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
InitTextView();
InitImage();
InitViewPager();
}
public void InitTextView(){
view1 = (TextView)findViewById(R.id.tv_guid1);
view2 = (TextView)findViewById(R.id.tv_guid2);
view3 = (TextView)findViewById(R.id.tv_guid3);
view1.setOnClickListener(new txListener(0));
view2.setOnClickListener(new txListener(1));
view3.setOnClickListener(new txListener(2));
}
public class txListener implements View.OnClickListener{
private int index=0;
public txListener(int i) { index =i; }
@Override
public void onClick(View v) { mPager.setCurrentItem(index); }
}
public void InitImage(){
image = (ImageView)findViewById(R.id.cursor);
bmpW = BitmapFactory.decodeResource(getResources(), R.drawable.cursor).getWidth();
DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
int screenW = dm.widthPixels;
offset = (screenW/3 - bmpW)/2;
//imgageview設置平移,使下劃線平移到初始位置(平移一個offset)
Matrix matrix = new Matrix();
matrix.postTranslate(offset, 0);
image.setImageMatrix(matrix);
}
public void InitViewPager(){
mPager = (ViewPager)findViewById(R.id. id_page_vp);
fragmentList = new ArrayList<Fragment>();
Fragment btFragment= new ButtonFragment();
Fragment secondFragment = TestFragment.newInstance("this is second fragment");
Fragment thirdFragment = TestFragment.newInstance("this is third fragment");
fragmentList.add(btFragment);
fragmentList.add(secondFragment);
fragmentList.add(thirdFragment);
//給ViewPager設置適配器
mPager.setAdapter(new MyFragmentPagerAdapter(getSupportFragmentManager(), fragmentList));
mPager.setCurrentItem(0);//設置當前顯示標簽頁為第一頁
mPager.setOnPageChangeListener(new MyOnPageChangeListener());//頁面變化時的監聽器
}
public class MyOnPageChangeListener implements OnPageChangeListener{
private int one = offset *2 +bmpW;//兩個相鄰頁面的偏移量
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) { }
@Override
public void onPageScrollStateChanged(int arg0) { }
@Override
public void onPageSelected(int arg0) {
Animation animation = new TranslateAnimation(currIndex*one,arg0*one,0,0);//平移動畫
currIndex = arg0;
animation.setFillAfter(true); // 動畫終止時停留在最後一幀,不然會回到沒有執行前的狀態
animation.setDuration(200); // 動畫持續時間0.2秒
image.startAnimation(animation);// 是用ImageView來顯示動畫的
int i = currIndex + 1;
Toast.makeText(MainActivity.this, "您選擇了第"+i+"個頁卡", Toast.LENGTH_SHORT).show();
}
}
}
3)Adapter
public class MyFragmentPagerAdapter extends FragmentPagerAdapter{
ArrayList<Fragment> list;
public MyFragmentPagerAdapter(FragmentManager fm,ArrayList<Fragment> list) {
super(fm);
this.list = list;
}
@Override
public int getCount() {
return list.size();
}
@Override
public Fragment getItem(int arg0) {
return list.get(arg0);
}
}
ViewPager默認會緩存三頁數據,即:Viewpager每加載一個Fragment,都會預先加載此Fragment左側或右側的Fragment。而如果每個fragment都需要去加載數據,或從本地加載,或從網絡加載,那麼在這個activity剛創建的時候就變成需要初始化大量資源,浪費用戶流量不止,還造成卡頓。那麼,能不能做到當切換到這個fragment的時候,它才去初始化呢?答案就在Fragment裡的setUserVisibleHint這個方法裡。
該方法用於告訴系統,這個Fragment的UI是否是可見的。所以我們只需要繼承Fragment並重寫該方法,即可實現在fragment可見時才進行數據加載操作,即Fragment的懶加載。
/** 基類Fragment */
public abstract class BaseFragment extends Fragment {
protected View mRootView;
public Context mContext;
protected boolean isVisible;
private boolean isPrepared;
private boolean isFirst = true;
public BaseFragment() { /* Required empty public constructor*/ }
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (getUserVisibleHint()) {
isVisible = true;
lazyLoad();
} else {
isVisible = false;
onInvisible();
}
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = getActivity();
setHasOptionsMenu(true);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (mRootView == null) {
mRootView = initView();
}
return mRootView;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
isPrepared = true;
lazyLoad();
}
protected void lazyLoad() {
① isPrepared參數在系統調用onActivityCreated時設置為true,這時onCreateView方法已調用完畢(一般我們在這方法裡執行findviewbyid等方法),確保 initData()方法不會報空指針異常。
② isVisible參數在fragment可見時通過系統回調setUserVisibileHint方法設置為true,不可見時為false,這是fragment實現懶加載的關鍵。
③ isFirst確保ViewPager來回切換時BaseFragment的initData方法不會被重復調用,initData在該Fragment的整個生命周期只調用一次,第一次調用initData()方法後馬上執行 isFirst = false。
if (!isPrepared || !isVisible || !isFirst)
return;
initData();
isFirst = false;
}
protected void onInvisible() { /* do sth*/ }
public abstract View initView();
public abstract void initData();
}
為了可復用,這裡我新建了個BaseFragment,在basefragment,我增加了三個方法:一個是onVisiable,即fragment被設置為可見時調用,一個是onInvisible,即fragment被設置為不可見時調用。另外再寫了一個lazyLoad的抽象方法,該方法在onVisible裡面調用。
【附】
1. Activity怎麼獲取Service中的信息?
1) 使用Intent的putExtras傳參數過去;
2) 使用SharePreference;
3) 使用全局變量Application;
4) AIDL;
5) 如果是在同一個進程,可以通過Handler發送message;
6) 廣播發送,效率低點;
7) bind service,獲得service對象,訪問其公共方法。
2. Activity和Service如何通過Handler通信?
1) 在Activity中將Handler聲明為Static,直接在Service中通過類方法調用:XActivity.myStaticHandler.sendMessage(…);
2) 在service中定義一個Activity類本身的靜態對象,在Activity中定義一個Public方法返回handler對象。
3) Service通過onBind方法返回一個Messager對象給Activity
3. ClassLoader:
將制定的class類對象加載到內存中;一個運行的APP至少有兩個Classloader。
系統啟動時創建的BootClassLoader和應用啟動時創建的PathClassLoader
4. Android string.xml 通配符 %$用法
<string name="welcome_messages">Hello, %1$s! You have %2$d new messages.</string>
在這個例子中,這個格式化的字符串有2個參數
屬性值舉例說明
%n$ms:輸出字符串,n代表是第幾個參數,設置m的值可以在輸出之前放置空格
%n$md:輸出整數,設置m的值可以在輸出之前放置空格,也可以設為0m,在輸出之前放置m個0
%n$mf:輸出浮點數,n代表是第幾個參數,設置m的值可以控制小數位數,如m=2.2時,輸出為00.00
在程序中按照下面的方法來根據參數來格式化字符串:
Resources res = getResources();
String text = String.format(res.getString(R.string.welcome_messages), username, mailCount);
Android百度地圖API集成三《搜索》,android《搜索》 書接上回↑ 一、基礎地圖界面地址:http://www.cnblogs.com/dhr125/p
Android 自定義控件的使用,android自定義控件首先自定義一個attrs.xml的資源文件,聲明自定義屬性 <?xml version=1.0 enco
Android開發學習之路--RxAndroid之lambda RxJava的簡單使用基本上也了解了,其實還有一個比較好玩的就是java8才有的lambda了。 l
Android中AsyncTask分析--你所不注意的坑,androidasynctaskAsyncTask,是android提供的輕量級的異步類,可以直接繼承Async