Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 關於android開發 >> 1.1 Activity,1.1activity

1.1 Activity,1.1activity

編輯:關於android開發

1.1 Activity,1.1activity


1概念

  Application:由多個相關的松散的與用戶進行交互Activity組成,通常被打包成apk後綴文件中;

  Activity:就是被用來進行與用戶交互和用來與android內部特性交互的組件,是應用程序的核心

  ActivityStack:將應用程序中打開的Activity保存在歷史棧中;Start Activity時入棧,返回時出棧;

  AcitivityManager(Service)Activity管理的核心,是一個獨立的進程,負責管理Activity的生命周期;

  Task任務棧,將一系列相關的Activity組合(可以包含多個APP),完成某個應用程序完整操作;

  ActivityThread每一個應用程序所在進程的主線程,循環的消息處理;

  ApplicationThread: Binder對象,完成ActivityThread與AcitivityManager的通信,屬於進程間通信;

2 Activity啟動過程

  • ActivityManager和ActivityStack位於同一個進程中,而ApplicationThread和ActivityThread位於另一個進程中。
  • ActivityManager借助ActivityStack是來把所有的Activity按照後進先出的順序放在一個堆棧中;
  • 每一個APP都有一個ActivityThread來表示主進程,而其中都包含一個ApplicationThread實例。

下面簡要介紹一下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 ActivityView的關系

主要涉及的對象:

  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的元素的特性值交互控制。

launchMode

  • Standard:標准模式,調用一次startActivity()就產生一個實例
  • singleTop:Task單例模式,即若已有一個實例在棧頂,則(才)不產生新的實例,轉而調用onNewIntent()。
  • singleTask:Task內單例模式,棧內可以有其他Activity實例
  • singleInstance:全局單單例模式,獨占Task

大致生命周期:onCreate >>onStart>> onResume>>onPause>> onStop>> onDestroy 

SingleTask時有 onStop>> (onNewIntent>>) onRestart>>onStart>>onResume

橫豎屏切換:onSaveInstance->onPause->onStop->onDestory->onCreate->onStart->onRestoreInstance->onResume

launchModesingleTask的時候,通過Intent啟到一個Activity,如果系統已經存在一個實例,系統就會將請求發送到這個實例上,但這個時候,系統就不會再調用通常情況下我們處理請求數據的onCreate方法,而是調用onNewIntent方法,類似onCreate用法,需要更新intent:調用setIntent(intent)

打開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設置成窗口模式:  將activity的屬性android:theme="@style/Theme.Dialog"
  • 退出Activity

    退出單一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。

  • 橫豎屏切換時Activity的生命周期:

    1) 不設置Activity的configChanges 屬性時,切屏會重新調用各個生命周期(見上),切橫屏會執行一次,豎屏兩次。

    2) 設置Activity的android:configChanges=”orientation”時,橫豎屏切換都只執行一次

    3) 設置Activity的android:configChanges=”orientation|keyboardHidden”時,屏幕切換不會重新調用個各生命周期,只會執行:onConfigurationChange()

  • onCreate方法中 Bundle savedInstanceSate這個參數作用:

  在實際應用中,當一個Activity結束前,如果需要保存狀態,就在onsaveInstanceState()中,將狀態數據以key-value的形式放入到savedInstanceState()中。這樣,當一個Activity被創建時,就能從onCreate的參數savedInsanceState中獲得狀態數據。

  onsaveInstanceState ()觸發條件(五個):按下HOME鍵時;長按HOME鍵選擇運行其他程序時;關閉屏幕顯示時;啟動一個新的Activity時;屏幕方向切換時。(當系統“未經你許可”時銷毀了你的activity時)

  與之配套的有onRestoreInstanceState()方法,但不一定成對出現。這個方法被調用的前提是activity“確實”被系統銷毀了。它的bundle參數也會傳遞到onCreate方法中,你也可以選擇在onCreate方法中做數據還原。

5Activity Fragment

  • 傳遞參數:

    1) Fragment中通過getActivity()獲得Activity對象,調用Activity中的公有方法((RootActivity)getActivity()).fun();

    2) Activity實現一個接口,Fragment在onAttach()方法中,將該Activity轉化為該接口,在需要調用的時候回調

    3) Activity在切換Fragment的時候,通過setArguments向Fragment傳遞參數,Fragment通過getArguments()取值。

  • 通過FragmentManager管理:我們創建Fragment和銷毀Fragment的時候,可以通過棧的方式:

    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裡。

  • Back Stack:

  Activity停止時,時存在一個由系統維護的backsatck中;但是當Fragment停止(被remove)時,需要程序員顯示調用addToBackStack(),並且Fragment是保存在一個由宿主activity掌管的backstack中。

  擁有Fragment的Activity的生命周期直接影響了其中的Fragment的生命周期,這樣,針對Activity的每一個生命周期的回調都會有一個類似的針對Fragment的回調。例如,當Activity收到onPause()回調時,在Activity中每個Fragment都會收到onPause()回調。但是,Fragment有幾個額外的生命周期回調方法,用來處理跟Activity的交互,以便執行諸如創建和銷毀Fragment的UI的動作。

這些額外的回調方法如下:

  • onAttach()       當Fragment已經跟Activity關聯上的時候調用。Activity會作為該方法的參數來傳遞。
  • onCreateView():   創建跟Fragment關聯的視圖層時調用
  • onActivityCreated(): 當onCreate()方法執行完之後調用
  • onDestroyView():     當關聯的視圖層正在被刪除時調用
  • onDetach():           當從Activity中解除Fragment的關聯時調用

  如下圖中說明的那樣,Fragment的生命周期流收到持有這些Fragment的Activity的影響,在這個圖中,你能看到每個連續的Activity狀態決定了Fragment的那個回調方法可以被調用。

  例如,當Activity已經收到了onCreate()的回調之後,在Activity中的Fragment就不會再接收onActivityCreated()以上的回調了。一旦Activity到達了被恢復的狀態,你就可以自由的給這個Activity添加和刪除Fragment了,只有Activity在恢復態時,Fragment的生命周期才能獨立的改變。但是,當Activity離開恢復態時,Fragment會再次被推進Activity的生命周期中。

 

6ViewPager+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();

              }

       }

}

  • ViewPager應該和Fragment一起使用時,此時ViewPager的適配器是FragmentPagerAdapter,當你實現一個FragmentPagerAdapter,你必須至少覆蓋以下方法:getCount()       getItem()
  • 如果ViewPager沒有和Fragment一起,ViewPager的適配器是PagerAdapter,它是基類提供適配器來填充頁面ViewPager內部,當你實現一個PagerAdapter,你必須至少覆蓋以下方法:instantiateItem(ViewGroup, int);destroyItem(ViewGroup, int, Object);       getCount();  isViewFromObject(View, Object)

3Adapter

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);

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved