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获取到并存储起来。

  1. Step1:无论是通过Launcher来启动Activity,还是通过Activity内部调用startActivity接口来启动新的Activity,都通过Binder进程间通信进入到ActivityManager进程中,并且调用ActivityManager.startActivity接口;
  2. Step2:ActivityManager调用ActivityStack.startActivityMayWait来做准备要启动的Activity的相关信息;
  3. Step3:ActivityStack通知ApplicationThread要进行Activity启动调度了,这里的ApplicationThread代表的是调用ActivityManager.startActivity接口的进程,对于通过点击应用程序图标的情景来说,这个进程就是Launcher了,而对于通过在Activity内部调用startActivity的情景来说,这个进程就是这个Activity所在的进程了;
  4. Step4:ApplicationThread不执行真正的启动操作,它通过调用ActivityManager.activityPaused接口进入到ActivityManager进程中,看看是否需要创建新的进程来启动Activity;
  5. Step5:对于通过点击应用程序图标来启动Activity的情景来说,ActivityManager在这一步中,会调用startProcessLocked来创建一个新的进程,而对于通过在Activity内部调用startActivity来启动新的Activity来说,这一步是不需要执行的,因为新的Activity就在原来的Activity所在的进程中进行启动;
  6. Step6:调用ApplicationThread.scheduleLaunchActivity通知相应的进程ActivityThread执行启动Activity的操作;
  7. 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
  1. 退出单一Activity:finish()
  2. 退出多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>

2)加载代码注意:需要引入android-support-v4.jar(正常情况系统会自动引入)

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 = falseif (!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);