分类目录归档:Android

android的四种启动模式,与五大布局,五种存储模式,四大组件::::::演道网Andriod专栏提供Andriod基础概念,具体代码演示,方便大家深入理解和运用Android。大部分代码可以直接使用。

Android中全屏或者取消标题栏的三种方法

先介绍去掉标题栏的方法:

第一种:也一般入门的时候经常使用的一种方法

requestWindowFeature(Window.FEATURE_NO_TITLE);//去掉标题栏

注意这句一定要写在setContentView()方法的前面,不然会报错的

第二种:在AndroidManifest.xml文件中定义

<application android:icon=@drawable/icon
android:label
=@string/app_name
android:theme
=”@android:style/Theme.NoTitleBar”>

可以看出,这样写的话,整个应用都会去掉标题栏,如果只想去掉某一个Activity的标题栏的话,可以把这个属性加到activity标签里面

第三种:这种在一般的应用中不常用,就是在res/values目录下面新建一个style.xml的文件

例如:

<?xml version=”1.0″ encoding=”UTF-8″ ?>
<resources>
<style name=”notitle>
<item name=”android:windowNoTitle”>true</item>
</style> 
</resources>

这样,我们就自定义了一个style,就相当于一个主题,然后在AndroidManifest.xml文件中定义

<application android:icon=”@drawable/icon”
android:label
=”@string/app_name”
android:theme
=”@style/notitle>

这样也可以达到去掉标题栏的效果

三种去掉标题栏方法的总结

第一种,有的时候我们会看到,会先出现标题栏,然后再消失,因为我们只是在activity的oncreate方法中定义的,第二种相对第一种比较好一些,不会出现这种情况,第三种我个人感觉最好,这样把功能分开,便于维护和扩展

再介绍全屏的方法:

第一种

getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

第二种

android:theme=”@android:style/Theme.NoTitleBar.Fullscreen”

第三种

application android:icon=”@drawable/icon”
android:label
=”@string/app_name”
android:theme
=”@style/fullscreem”

大家自己总结一下吧,个人感觉第三种比较好,看自己的喜欢用吧

Android Fragments的使用

Fragment 表现 Activity 中用UI的一个行为或者一部分.可以组合多个fragment放在一个单独的activity中来创建一个多界面区域的UI,并可以在多个activity里重用某一个fragment.把fragment想象成一个activity的模块化区域, 有它自己的生命周期, 接收属于它的输入事件,并且可以在activity运行期间添加和删除.

Fragment 必须总是被嵌入到一个activity中, 它们的生命周期直接被其所属的宿主activity的生命周期影响.例如, 当activity被暂停,那么在其中的所有fragment也被暂停; 当activity被销毁,所有隶属于它的fragment也被销毁. 然而,当一个activity正在运行时(处于resumed状态),我们可以独立地操作每一个fragment, 比如添加或删除它们. 当处理这样一个fragment事务时,也可以将它添加到activity所管理的back stack — 每一个activity中的backstack实体都是一个发生过的fragment事务的记录. back stack允许用户通过按下 BACK按键从一个fragment事务后退(往后导航).

将一个fragment作为activity布局的一部分添加进来时, 它处在activity的viewhierarchy中的ViewGroup中,并且定义有它自己的view布局.通过在activity的布局文件中声明fragment来插入一个fragment到你的activity布局中,或者可以写代码将它添加到一个已存在的ViewGroup.然而, fragment并不一定必须是activity布局的一部分;也可以将一个fragment作为activity的隐藏的后台工作者.

本文档描述了如何使用fragment创建你的应用程序, 包括:当被添加到activity的back stack后,fragment如何维护他们的状态.在activity中,与activity和其他fragment共享事件.构建到activity的actionbar.以及更多内容.

设计哲学


Android在3.0中引入了fragments的概念,主要目的是用在大屏幕设备上–例如平板电脑上,支持更加动态和灵活的UI设计.平板电脑的屏幕要比手机的大得多,有更多的空间来放更多的UI组件,并且这些组件之间会产生更多的交互.Fragment允许这样的一种设计,而不需要你亲自来管理viewhierarchy的复杂变化. 通过将activity的布局分散到fragment中, 你可以在运行时修改activity的外观,并在由activity管理的back stack中保存那些变化.

例如, 一个新闻应用可以在屏幕左侧使用一个fragment来展示一个文章的列表,然后在屏幕右侧使用另一个fragment来展示一篇文章 – 2个fragment并排显示在相同的一个activity中,并且每一个fragment拥有它自己的一套生命周期回调方法,并且处理它们自己的用户输入事件. 因此, 取代使用一个activity来选择一篇文章,而另一个activity来阅读文章 的方式,用户可以在相同的activity中选择一篇文章并且阅读, 如图所示:

fragment在你的应用中应当是一个模块化和可重用的组件.即,因为fragment定义了它自己的布局, 以及通过使用它自己的生命周期回调方法定义了它自己的行为,你可以将fragment包含到多个activity中. 这点特别重要, 因为这允许你将你的用户体验适配到不同的屏幕尺寸.举个例子,你可能会仅当在屏幕尺寸足够大时,在一个activity中包含多个fragment,并且,当不属于这种情况时,会启动另一个单独的,使用不同fragment的activity.

继续之前那个新闻的例子 — 当运行在一个特别大的屏幕时(例如平板电脑),app可以在Activity A中嵌入2个fragment.然而,在一个正常尺寸的屏幕(例如手机)上,没有足够的空间同时供2个fragment用, 因此, Activity A会仅包含文章列表的fragment, 而当用户选择一篇文章时, 它会启动Activity B,它包含阅读文章的fragment. 因此, 应用可以同时支持图1中的2种设计模式.

创建Fragment


    要创建一个fragment, 必须创建一个 Fragment 的子类 (或者继承自一个已存在的它的子类). Fragment类的代码看起来很像 Activity .它包含了和activity类似的回调方法, 例如 onCreate(), onStart(),onPause, 以及 onStop(). 事实上, 如果你准备将一个现成的Android应用转换到使用fragment,你可能只需简单的将代码从你的activity的回调函数分别移动到你的fragment的回调方法.

    通常, 应当至少实现如下的生命周期方法:

 

 

  • onCreate()
    当创建fragment时, 系统调用此方法.
    在实现代码中,应当初始化想要在fragment中保持的必要组件, 当fragment被暂停或者停止后可以恢复.
  • onCreateView()
    fragment第一次绘制它的用户界面的时候, 系统会调用此方法. 为了绘制fragment的UI,此方法必须返回一个View, 这个view是你的fragment布局的根view. 如果fragment不提供UI, 可以返回null.
  • onPause()
    用户将要离开fragment时,系统调用这个方法作为第一个指示(然而它不总是意味着fragment将被销毁.) 在当前用户会话结束之前,通常应当在这里提交任何应该持久化的变化(因为用户有可能不会返回).

    大多数应用应当为每一个fragment实现至少这3个方法,但是还有一些其他回调方法你也应当用来去处理fragment生命周期的各种阶段.全部的生命周期回调方法将会在后面章节 Handlingthe Fragment Lifecycle 中讨论.

    除了继承基类 Fragment , 还有一些子类你可能会继承:

 

  • DialogFragment
    显示一个浮动的对话框.
    用这个类来创建一个对话框,是使用在Activity类的对话框工具方法之外的一个好的选择,
    因为你可以将一个fragment对话框合并到activity管理的fragment back stack中,允许用户返回到一个之前曾被摒弃的fragment.
  • ListFragment
    显示一个由一个adapter(例如 SimpleCursorAdapter)管理的项目的列表, 类似于ListActivity.
    它提供一些方法来管理一个list view, 例如 onListItemClick()回调来处理点击事件.
  • PreferenceFragment
    显示一个 Preference对象的层次结构的列表, 类似于PreferenceActivity.
    这在为你的应用创建一个”设置”activity时有用处.

添加一个用户界面


fragment通常用来作为一个activity的用户界面的一部分,并将它的layout提供给activity.为了给一个fragment提供一个layout,你必须实现 onCreateView()回调方法, 当到了fragment绘制它自己的layout的时候,Android系统调用它.你的此方法的实现代码必须返回一个你的fragment的layout的根view.

       注意: 如果你的fragment是ListFragment的子类,它的默认实现是返回从onCreateView()返回一个ListView,所以一般情况下不必实现它.

从onCreateView()返回的View, 也可以从一个xmllayout资源文件中读取并生成. 为了帮助你这么做, onCreateView() 提供了一个LayoutInflater 对象.

举个例子, 这里有一个Fragment的子类, 从文件 example_fragment.xml 加载了一个layout:

public static class ExampleFragment extends Fragment { 
         @Override 
         public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
             // Inflate the layout for this fragment 
             return inflater.inflate(R.layout.example_fragment, container, false); 
         } 
     }

 

传入 onCreateView() 的 container 参数是你的fragmentlayout将被插入的父ViewGroup(来自activity的layout).savedInstanceState 参数是一个Bundle, 如果fragment是被恢复的,它提供关于fragment的之前的实例的数据,

inflate() 方法有3个参数:

 

  • 想要加载的layout的resource ID.
  • 加载的layout的父ViewGroup.
    传入container是很重要的, 目的是为了让系统接受所要加载的layout的根view的layout参数,
    由它将挂靠的父view指定.
  • 布尔值指示在加载期间, 展开的layout是否应当附着到ViewGroup (第二个参数).
    (在这个例子中, 指定了false, 因为系统已经把展开的layout插入到container –传入true会在最后的layout中创建一个多余的view group.)

将fragment添加到activity
通常地, fragment为宿主activity提供UI的一部分, 被作为activity的整个viewhierarchy的一部分被嵌入. 有2种方法你可以添加一个fragment到activitylayout:

在activity的layout文件中声明fragment
你可以像为View一样, 为fragment指定layout属性.
例子是一个有2个fragment的activity:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:orientation="horizontal"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
     <fragment android:name="com.example.news.ArticleListFragment"
             android:id="@+id/list"
             android:layout_weight="1"
             android:layout_width="0dp"
             android:layout_height="match_parent" />
     <fragment android:name="com.example.news.ArticleReaderFragment"
             android:id="@+id/viewer"
             android:layout_weight="2"
             android:layout_width="0dp"
             android:layout_height="match_parent" />
</LinearLayout>

<fragment> 中的 android:name属性指定了在layout中实例化的Fragment类.

当系统创建这个activity layout时,它实例化每一个在layout中指定的fragment,并调用每一个上的onCreateView()方法,来获取每一个fragment的layout.系统将从fragment返回的 View直接插入到<fragment>元素所在的地方.

        注意: 每一个fragment都需要一个唯一的标识,如果activity重启,系统可以用来恢复fragment(并且你也可以用来捕获fragment来处理事务,例如移除它.)

有3种方法来为一个fragment提供一个标识:

 

  • 为 android:id 属性提供一个唯一ID.
  • 为 android:tag 属性提供一个唯一字符串.
  • 如果以上2个你都没有提供, 系统使用容器view的ID.

撰写代码将fragment添加到一个已存在的ViewGroup.
当activity运行的任何时候, 都可以将fragment添加到activitylayout.只需简单的指定一个需要放置fragment的ViewGroup.为了在你的activity中操作fragment事务(例如添加,移除,或代替一个fragment),必须使用来自FragmentTransaction 的API.

可以按如下方法,从你的Activity取得一个 FragmentTransaction 的实例:

FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

然后你可以使用 add() 方法添加一个fragment, 指定要添加的fragment, 和要插入的view.

ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();

 

add()的第一个参数是fragment要放入的ViewGroup, 由resource ID指定,第二个参数是需要添加的fragment.一旦用FragmentTransaction做了改变,为了使改变生效,必须调用commit().

添加一个无UI的fragment

之前的例子展示了对UI的支持, 如何将一个fragment添加到activity. 然而,也可以使用fragment来为activity提供后台行为而不用展现额外的UI.

要添加一个无UI的fragment, 需要从activity使用 add(Fragment, String) 来添加fragment (为fragment提供一个唯一的字符串”tag”, 而不是一个view ID).这么做添加了fragment,但因为它没有关联到一个activity layout中的一个view, 所以不会接收到onCreateView()调用.因此不必实现此方法.

为fragment提供一个字符串tag并不是专门针对无UI的fragment的 –也可以提供字符串tag给有UI的fragment – 但是如果fragment没有UI,那么这个tag是仅有的标识它的途径.如果随后你想从activity获取这个fragment, 需要使用 findFragmentByTag().

管理Fragment


要在activity中管理fragment,需要使用FragmentManager. 通过调用activity的getFragmentManager()取得它的实例.

可以通过FragmentManager做一些事情, 包括:

 

 

  • 使用findFragmentById() (用于在activitylayout中提供一个UI的fragment)或findFragmentByTag()(适用于有或没有UI的fragment)获取activity中存在的fragment
  • 将fragment从后台堆栈中弹出, 使用 popBackStack() (模拟用户按下BACK 命令).
  • 使用addOnBackStackChangeListener()注册一个监听后台堆栈变化的listener.

处理Fragment事务


关于在activity中使用fragment的很强的一个特性是:根据用户的交互情况,对fragment进行添加,移除,替换,以及执行其他动作.提交给activity的每一套变化被称为一个事务,可以使用在 FragmentTransaction 中的 API 处理.我们也可以保存每一个事务到一个activity管理的backstack,允许用户经由fragment的变化往回导航(类似于通过activity往后导航).

从 FragmentManager 获得一个FragmentTransaction的实例 :

FragmentManager fragmentManager = getFragmentManager(); 
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

每一个事务都是同时要执行的一套变化.可以在一个给定的事务中设置你想执行的所有变化,使用诸如 add(), remove(),和 replace().然后, 要给activity应用事务, 必须调用 commit().
在调用commit()之前, 你可能想调用 addToBackStack(),将事务添加到一个fragment事务的backstack. 这个back stack由activity管理, 并允许用户通过按下 BACK按键返回到前一个fragment状态.

举个例子, 这里是如何将一个fragment替换为另一个, 并在后台堆栈中保留之前的状态:

// Create new fragment and transaction
Fragment newFragment = new ExampleFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();

在这个例子中, newFragment替换了当前layout容器中的由R.id.fragment_container标识的fragment.通过调用addToBackStack(), replace事务被保存到back stack,因此用户可以回退事务,并通过按下BACK按键带回前一个fragment.

如果添加多个变化到事务(例如add()或remove())并调用addToBackStack(),然后在你调用commit()之前的所有应用的变化会被作为一个单个事务添加到后台堆栈, BACK按键会将它们一起回退.

添加变化到 FragmentTransaction的顺序不重要, 除以下例外:

 

 

  • 必须最后调用 commit().
  • 如果添加多个fragment到同一个容器, 那么添加的顺序决定了它们在view hierarchy中显示的顺序.

 

当执行一个移除fragment的事务时, 如果没有调用 addToBackStack(), 那么当事务提交后,那个fragment会被销毁,并且用户不能导航回到它. 有鉴于此, 当移除一个fragment时,如果调用了addToBackStack(), 那么fragment会被停止, 如果用户导航回来,它将会被恢复.

提示: 对于每一个fragment事务, 你可以应用一个事务动画,通过在提交事务之前调用setTransition()实现.

调用 commit() 并不立即执行事务.恰恰相反, 它将事务安排排期, 一旦准备好,就在activity的UI线程上运行(主线程).如果有必要, 无论如何, 你可以从你的UI线程调用executePendingTransactions()来立即执行由commit()提交的事务. 但这么做通常不必要,除非事务是其他线程中的job的一个从属.

       警告:你只能在activity保存它的状态(当用户离开activity)之前使用commit()提交事务.

如果你试图在那个点之后提交, 会抛出一个异常.这是因为如果activity需要被恢复,提交之后的状态可能会丢失.对于你觉得可以丢失提交的状况, 使用 commitAllowingStateLoss().

与Activity通信


尽管Fragment被实现为一个独立于Activity的对象,并且可以在多个activity中使用,但一个给定的fragment实例是直接绑定到包含它的activity的. 特别的,fragment可以使用 getActivity() 访问Activity实例, 并且容易地执行比如在activitylayout中查找一个view的任务.

View listView = getActivity().findViewById(R.id.list);

同样地,activity可以通过从FragmentManager获得一个到Fragment的引用来调用fragment中的方法, 使用findFragmentById() 或 findFragmentByTag().

ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);

android BottomTabBar 代码示例


 1 在Android app开发过程中,会发现很多App的底部(顶部一样) 会仿效IPHONE的设计。。做一个导航。  如下图黑色部分:


(这个是实现效果)                                                                 这个是设计原型

 

1.1 要是中间部分没有那个拱起来的部分,那么可能设计还简单一些。。 仔细分析下设计原型,,,中间那个 摇摇 的图标要大一些,而且和其他图标没在一个水平线上。

 

1.2  总的思想就是 先封装一个 没有 凸起 的通用,自动适配的组。。。然后。。。在上面 通过 FrameLayout 放置一个 突起的组建。。。

 

1.3 设计细节

1.3.1  自己写一个  BottomTabBar   (extends) LinearLayout .  通过 Inflater 加载一个 自定义的 View (通过xml配置), 在View 的xml 中,注意书写好适配就OK了。

注意 layout_weight 的使用。(参见上一篇 listview)

 

 

自定义LinearLayout:

 

package com.dp.android.widget;
import com.dp.android.elong.shake.R;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
public class BottomTabBar extends LinearLayout {
    //--implements two kinds of constructions--
    public  BottomTabBar(Context context) {
        super(context);
        init(context);
    }
    public BottomTabBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }
    private void init(Context context) {
        LayoutParams lp = new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.WRAP_CONTENT);
        setLayoutParams(lp);
        LayoutInflater mInflater = LayoutInflater.from(context);
        View v = mInflater.inflate(R.layout.bottom_tabs, null);
        addView(v,lp);
    }
}

 

自定义的View(xml 形式)

<?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
       xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        >
        <!-- 搜索 -->
        <LinearLayout
            android:id="@+id/ll_bottom_tabls_search"
            style = "@style/bottom_tabs_ll"
            >
            <ImageView
                android:id="@+id/im_search"
                style = "@style/bottom_tabs_image"
                android:src="@drawable/bottom_tab_search" />
            <TextView
                style = "@style/bottom_tabs_tv"
                android:text="@string/bottom_tab_search"
                />
         </LinearLayout>
        <!-- 摇过 -->
        <LinearLayout
            android:id="@+id/ll_bottom_tabls_history"
            style = "@style/bottom_tabs_ll"
            >
            <ImageView
                android:id="@+id/im_history"
                style = "@style/bottom_tabs_image"
                android:src="@drawable/bottom_tab_history" />
            <TextView
                style = "@style/bottom_tabs_tv"
                android:text="@string/bootom_tab_shake"
                />
         </LinearLayout>
        <!-- 换一个 -->
        <LinearLayout
            android:id="@+id/ll_bottom_tabls_change"
            style = "@style/bottom_tabs_ll"
            >
            <ImageView
                android:id="@+id/im_change"
                style = "@style/bottom_tabs_image"
                android:src="@drawable/bottom_tab_blank"
                />
            <TextView
                style = "@style/bottom_tabs_tv"
                android:text="@string/bottom_tab_change"
                />
         </LinearLayout>
        <!-- 收藏 -->
        <LinearLayout
            android:id="@+id/ll_bottom_tabls_collection"
            style = "@style/bottom_tabs_ll"
            >
            <ImageView
                android:id="@+id/im_collection"
                style = "@style/bottom_tabs_image"
                android:src="@drawable/bottom_tab_collection"
                />
            <TextView
                style = "@style/bottom_tabs_tv"
                android:text="@string/bootom_tab_collection"
                />
         </LinearLayout>
        <!-- 更多 -->
        <LinearLayout
            android:id="@+id/ll_bottom_tabls_more"
            style = "@style/bottom_tabs_ll"
            >
            <ImageView
                android:id="@+id/im_more"
                style = "@style/bottom_tabs_image"
                android:src="@drawable/bottom_tab_more"
                />
            <TextView
                style = "@style/bottom_tabs_tv"
                android:text="@string/bootom_tab_more"
                />
         </LinearLayout>
    </LinearLayout>

 

view 中用到的 三个 style

<style name="bottom_tabs_ll" >
    <item name="android:layout_width">wrap_content</item>
    <item name="android:layout_height">wrap_content</item>
    <item name="android:background">@color/black</item>
    <item name="android:orientation">vertical</item>
    <item name="android:gravity">center</item>
    <item name="android:layout_gravity">bottom</item>
    <item name="android:layout_weight">1</item>
</style>
<style name="bottom_tabs_image" >
    <item name="android:layout_width">fill_parent</item>
    <item name="android:layout_height">wrap_content</item>
    <item name="android:paddingTop">5dip</item>
</style>
<style name="bottom_tabs_tv" >
    <item name="android:layout_width">fill_parent</item>
    <item name="android:layout_height">wrap_content</item>
    <item name="android:textSize">12sp</item>
    <item name="android:textColor">@color/common_white</item>
    <item name="android:paddingTop">3dip</item>
    <item name="android:gravity">center</item>
</style>

//—————————————————————下面是如何使用自己定义的 组建了————————————-

1: 注意 BottomTabBar ,,, 这里面还用到了自定义的 “凸起” 组件。 MyBottmArc

 

<FrameLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@color/shake_yellow_bg"
android:orientation="vertical"
>
<com.dp.android.widget.BottomTabBar
android:id="@+id/bottom_tab_bar1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingTop="30dip"
/>

<com.dp.android.widget.MyBottmArc
android:id="@+id/im_change"
android:layout_width="80dip"
android:layout_height="60dip"
android:padding="15dip"
android:src="@drawable/bottom_tab_search"
android:layout_gravity="top|center_horizontal"
/>

</FrameLayout>

 

//————————————————————————————–下面看看  凸起 是怎么弄的———————————————————————————

凸起 也是自己定义了一个 ImageView,  这个ImageView 的背景  就是绘制半个 圆弧。。。“拱形”

 

package com.dp.android.widget;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.widget.ImageView;
public class MyBottmArc extends ImageView {
    private static Paint paint = new Paint();
    public MyBottmArc(Context context) {
        super(context);
    }
    public MyBottmArc(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public MyBottmArc(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }
    protected void onDraw(Canvas canvas) {
        Rect m_rect = canvas.getClipBounds();
        canvas.save();
        paint.setColor(0xff000000);
        float m_left = m_rect.left;
        float m_top = m_rect.top;
        float m_right = m_rect.right;
        float m_bottom = (1.2f)*(m_rect.bottom);
        RectF m_rectf = new RectF(m_left,m_top,m_right,m_bottom);
        canvas.drawOval(m_rectf, paint);
        canvas.restore();
        super.onDraw(canvas);
    }
}

 

 

Android 面试题(答案最全)

  1. Android dvm的进程和Linux的进程, 应用程序的进程是否为同一个概念?

    DVM指dalivk的虚拟机。每一个Android应用程序都在它自己的进程中运行,都拥有一个独立的Dalvik虚拟机实例。而每一个DVM都是在Linux 中的一个进程,所以说可以认为是同一个概念。

  2. sim卡的EF 文件有何作用?

    sim卡的文件系统有自己规范,主要是为了和手机通讯,sim本 身可以有自己的操作系统,EF就是作存储并和手机通讯用的

  3. 嵌入式操作系统内存管理有哪几种, 各有何特性?

    页式,段式,段页,用到了MMU,虚拟空间等技术

  4. 什么是嵌入式实时操作系统, Android 操作系统属于实时操作系统吗?

    嵌入式实时操作系统是指当外界事件或数据产生时,能够接受并以足够快的速度予以处理,其处理的结果又能在规定的时间之内来控制生产过程或对处理系统作出快速响应,并控制所有实时任务协调一致运行的嵌入式操作系统。主要用于工业控制、 军事设备、 航空航天等领域对系统的响应时间有苛刻的要求,这就需要使用实时系统。又可分为软实时和硬实时两种,而android是基于linux内核的,因此属于软实时。

  5. 一条最长的短信息约占多少byte?

    中文70(包括标点),英文160,160个字节。

  6. android中的动画有哪几类,它们的特点和区别是什么?

    两种,一种是Tween动画、还有一种是Frame动画。Tween动画,这种实现方式可以使视图组件移动、放大、缩小以及产生透明度的变化;另一种Frame动画,传统的动画方法,通过顺序的播放排列好的图片来实现,类似电影。

  7. handler机制的原理

    andriod提供了 Handler 和 Looper 来满足线程间的通信。Handler 先进先出原则。Looper类用来管理特定线程内对象之间的消息交换(Message Exchange)。
    1)Looper: 一个线程可以产生一个Looper对象,由它来管理此线程里的Message Queue(消息队列)。
    2)Handler: 你可以构造Handler对象来与Looper沟通,以便push新消息到Message Queue里;或者接收Looper从Message Queue取出)所送来的消息。
    3) Message Queue(消息队列):用来存放线程放入的消息。
    4)线程:UI thread 通常就是main thread,而Android启动程序时会替它建立一个Message Queue。

  8. 说说mvc模式的原理,它在android中的运用

android的官方建议应用程序的开发采用mvc模式。何谓mvc?
mvc是model,view,controller的缩写,mvc包含三个部分:
* 模型(model)对象:是应用程序的主体部分,所有的业务逻辑都应该写在该层。
* 视图(view)对象:是应用程序中负责生成用户界面的部分。也是在整个mvc架构>中用户唯一可以看到的一层,接收用户的输入,显示处理结果。
* 控制器(control)对象:是根据用户的输入,控制用户界面数据显示及更新>model对象状态的部分,控制器更重要的一种导航功能,想用用户出发的相关事件,交给m哦得了处理。

android鼓励弱耦合和组件的重用,在android中mvc的具体体现如下:

  • 视图层(view):一般采用xml文件进行界面的描述,使用的时候可以非常方便的引入,当然,如何你对android了解的比较的多了话,就一定 可以想到在android中也可以使用javascript+html等的方式作为view层,当然这里需要进行java和javascript之间的通 信,幸运的是,android提供了它们之间非常方便的通信实现。
  • 控制层(controller):android的控制层的重 任通常落在了众多的acitvity的肩上,这句话也就暗含了不要在acitivity中写代码,要通过activity交割model业务逻辑层处理, 这样做的另外一个原因是android中的acitivity的响应时间是5s,如果耗时的操作放在这里,程序就很容易被回收掉。
  • 模型层(model):对数据库的操作、对网络等的操作都应该在model里面处理,当然对业务计算等操作也是必须放在的该层的。

9.Activity的生命周期

和其他手机 平台 的应用 程序 一样,Android的应用程序 的生命周期是被统一掌控 的,也就是说我们写的应用程序命运掌握在别人(系统)的手里,我们不能改变它,只能学习 并适应它。
简单地说一下为什么是这样:我们手机在运行 一个应用程序的时候,有可能打进来电话发进来短信 ,或者没有电了,这时候程序都会被中断,优先去服务电话的基本功能 ,另外系统也不允许你占用太多资源 ,至少要保证电话功能吧,所以资源不足的时候也就有可能被干掉。
言归正传,Activity的基本生命周期如下代码 所示:

public class MyActivity extends Activity {
  protected void onCreate(Bundle savedInstanceState);
  protected void onStart();
  protected void onResume();
  protected void onPause();
  protected void onStop();
  protected void onDestroy();
}

你自己写的Activity会按需要 重载这些方法,onCreate是免不了的,在一个Activity正常启动的过程中,他们被调用的顺序是
onCreate -> onStart -> onResume,
在Activity被干掉的时候顺序是onPause -> onStop -> onDestroy ,这样就是一个完整的生命周期。
但是有人问了,程序正运行着呢来电话了,这个程序咋办?中止了呗,如果中止的时候新出的一个Activity是全屏的,那么:onPause->onStop ,恢复的时候onStart->onResume ,如果打断 这个应用程序的是一个Theme为Translucent 或者Dialog 的Activity那么只是onPause ,恢复 的时候onResume 。
详细介绍一下这几个方法中系统在做什么以及我们应该做什么:

onCreate: 在这里创建界面 ,做一些数据 的初始化工作
onStart: 到这一步变成用户可见不可交互 的
onResume: 变成和用户可交互 的,(在activity 栈系统通过栈的方式管理这些个
Activity的最上面,运行完弹出栈,则回到上一个Activity)
onPause: 到这一步是可见但不可交互 的,系统会停止动画 等消耗CPU 的事情
从上文的描述已经知道,应该在这里保存你的一些数据,因为这个时候
你的程序的优先级降低,有可能被系统收回。在这里保存的数据,应该在
onResume里读出来,注意:这个方法里做的事情时间要短,因为下一
个activity不会等到这个方法完成才启动
onstop: 变得不可见 ,被下一个activity覆盖了
onDestroy: 这是activity被干掉前最后一个被调用方法了,可能是外面类调用finish方法或者是系统为了节省空间将它暂时性的干掉,可以用isFinishing()来判断它,如果你有一个Progress Dialog在线程中转动,请在onDestroy里
把他cancel掉,不然等线程结束的时候,调用Dialog的cancel方法会抛
异常的。

onPause,onstop, onDestroy,三种状态 下 activity都有可能被系统干掉
为了保证程序的正确性,你要在onPause()里写上持久层操作的代码,将用户编辑的内容都保存到存储介质上(一般都是数据库 )。实际工作中因为生命周期的变化而带来的问题也很多,比如你的应用程序起了新的线程在跑,这时候中断了,你还要去维护那个线程,是暂停还是杀掉还是数据 回滚,是吧?因为Activity可能被杀掉,所以线程中使用的变量和一些界面元素就千万要注意了,一般都是采用Android的消息机制 [Handler,Message]来处理多线程和界面交互的问题。

10.让Activity变成一个窗口:Activity属性设定

讲点轻松的吧,可能有人希望做出来的应用程序是一个漂浮在手机主界面的东西,那么很简单你只需要设置 一下Activity的主题就可以了在AndroidManifest.xml 中定义 Activity的地方一句话:

android :theme=”@android:style/Theme.Dialog”
android:theme=”@android:style/Theme.Dialog”

这就使你的应用程序变成对话框的形式弹出来了,或者

android:theme=”@android:style/Theme.Translucent”
android:theme=”@android:style/Theme.Translucent”

就变成半透明的,[友情提示-.-]类似的这种activity的属性可以在android.R.styleable 类的AndroidManifestActivity 方法中看到,AndroidManifest.xml中所有元素的属性的介绍都可以参考这个类android.R.styleable上面说的是属性名称,具体有什么值是在android.R.style中 可以看到,比如这个”@android:style/Theme.Dialog” 就对应于android.R.style.Theme_Dialog ,(‘_’换成’.’ < –注意:这个是文章内容不是笑脸)就可以用在描述文件 中了,找找类定义和描述文件中的对应关系就都明白了。

11.你后台的Activity被系统回收怎么办?

onSaveInstanceState
当你的程序中某一个Activity A 在运行时中,主动或被动地运行另一个新的Activity B. 这个时候A会执行

public void onSaveInstanceState(Bundle outState) {
  super.onSaveInstanceState(outState);
  outState.putLong("id", 1234567890);
}

B 完成以后又会来找A, 这个时候就有两种情况,一种是A被回收,一种是没有被回收,被回收的A就要重新调用onCreate()方法,不同于直接启动的是这回onCreate()里是带上参数savedInstanceState,没被收回的就还是onResume就好了。
savedInstanceState是一个Bundle对象,你基本上可以把他理解为系统帮你维护的一个Map对象。在onCreate()里你可能会用到它,如果正常启动onCreate就不会有它,所以用的时候要判断一下是否为空。

if(savedInstanceState != null){
  long id = savedInstanceState.getLong("id");
}

就像官方的Notepad教程 里的情况,你正在编辑某一个note,突然被中断,那么就把这个note的id记住,再起来的时候就可以根据这个id去把那个note取出来,程序就完整一些。这也是看你的应用需不需要保存什么,比如你的界面就是读取一个列表,那就不需要特殊记住什么,哦, 没准你需要记住滚动条的位置…

12.调用与被调用:我们的通信使者Intent

要说Intent了,Intent就是这个这个意图 ,应用程序间Intent进行交流,打个电话啦,来个电话啦都会发Intent, 这个是Android架构的松耦合的精髓部分,大大提高了组件的复用性,比如你要在你的应用程序中点击按钮,给某人打电话,很简单啊,看下代码先:

Intent intent = new Intent();
intent.setAction(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:" + number));
startActivity(intent);

扔出这样一个意图,系统看到了你的意图就唤醒了电话拨号程序,打出来电话。什么读联系人,发短信啊,邮件啊,统统只需要扔出intent就好了,这个部分设计 地确实很好啊。
那Intent通过什么来告诉系统需要谁来接受他呢?
通常使用Intent有两种方法,第一种是直接说明需要哪一个类来接收代码如下:

Intent intent = new Intent(this, MyActivity.class);
intent.getExtras().putString("id", "1");
tartActivity(intent);

第一种方式很明显,直接指定了MyActivity为接受者,并且传了一些数据给MyActivity,在MyActivity里可以用getIntent()来的到这个intent和数据。
第二种就需要先看一下AndroidMenifest中的intentfilter的配置了


< action android:name="android.intent.action.VIEW"/>
< action android:value="android.intent.action.EDIT"/>
< action android:value="android.intent.action.PICK"/>
< category android:name="android.intent.category.DEFAULT"/>
< data android:mimeType="vnd.android.cursor.dir/vnd.google.note"/>

这里面配置用到了action, data, category这些东西,那么聪明的你一定想到intent里也会有这些东西,然后一匹配不就找到接收者了吗?
action其实就是一个意图的字符串名称。
上面这段intent-filter的配置文件说明了这个Activity可以接受不同的Action,当然相应的程序逻辑也不一样咯,提一下那个 mimeType,他是在ContentProvider里定义的,你要是自己实现一个ContentProvider就知道了,必须指定 mimeType才能让数据被别人使用。
不知道原理说明白没,总结一句,就是你调用别的界面不是直接new那个界面,而是通过扔出一个intent,让系统帮你去调用那个界面,这样就多么松藕合啊,而且符合了生命周期被系统管理的原则。
想知道category都有啥,Android为你预先定制好的action都有啥等等,请亲自访问官方链接Intent
ps:想知道怎么调用系统应用程序的同学,可以仔细看一下你的logcat,每次运行一个程序的时候是不是有一些信息比如:

Starting activity: Intent { action=android.intent.action.MAINcategories={android.intent.category.LAUNCHER} flags=x10200000comp={com.android.camera/com.android.camera.GalleryPicker} }

再对照一下Intent的一些set方法,就知道怎么调用咯,希望你喜欢:)

13.如何退出Activity?如何安全退出已调用多个Activity的Application?

对于单一Activity的应用来说,退出很简单,直接finish()即可。
当然,也可以用killProcess()和System.exit()这样的方法。

但是,对于多Activity的应用来说,在打开多个Activity后,如果想在最后打开的Activity直接退出,上边的方法都是没有用的,因为上边的方法都是结束一个Activity而已。
当然,网上也有人说可以。
就好像有人问,在应用里如何捕获Home键,有人就会说用keyCode比较KEYCODE_HOME即可,而事实上如果不修改framework,根本不可能做到这一点一样。
所以,最好还是自己亲自试一下。

那么,有没有办法直接退出整个应用呢?
在2.1之前,可以使用ActivityManager的restartPackage方法。
它可以直接结束整个应用。在使用时需要权限android.permission.RESTART_PACKAGES。
注意不要被它的名字迷惑。

可是,在2.2,这个方法失效了。
在2.2添加了一个新的方法,killBackgroundProcesses(),需要权限 android.permission.KILL_BACKGROUND_PROCESSES。
可惜的是,它和2.2的restartPackage一样,根本起不到应有的效果。

另外还有一个方法,就是系统自带的应用程序管理里,强制结束程序的方法,forceStopPackage()。
它需要权限android.permission.FORCE_STOP_PACKAGES。
并且需要添加android:sharedUserId=”android.uid.system”属性
同样可惜的是,该方法是非公开的,他只能运行在系统进程,第三方程序无法调用。
因为需要在Android.mk中添加LOCAL_CERTIFICATE := platform。
而Android.mk是用于在Android源码下编译程序用的。

从以上可以看出,在2.2,没有办法直接结束一个应用,而只能用自己的办法间接办到。

现提供几个方法,供参考:

1、抛异常强制退出:
该方法通过抛异常,使程序Force Close。
验证可以,但是,需要解决的问题是,如何使程序结束掉,而不弹出Force Close的窗口。

2、记录打开的Activity:
每打开一个Activity,就记录下来。在需要退出时,关闭每一个Activity即可。

3、发送特定广播:
在需要结束应用时,发送一个特定的广播,每个Activity收到广播后,关闭即可。

4、递归退出
在打开新的Activity时使用startActivityForResult,然后自己加标志,在onActivityResult中处理,递归关闭。

除了第一个,都是想办法把每一个Activity都结束掉,间接达到目的。
但是这样做同样不完美。
你会发现,如果自己的应用程序对每一个Activity都设置了nosensor,在两个Activity结束的间隙,sensor可能有效了。
但至少,我们的目的达到了,而且没有影响用户使用。

为了编程方便,最好定义一个Activity基类,处理这些共通问题。
摘自:http://blog.csdn.net/debug2/archive/2011/02/18/6193644.aspx

14.请介绍下Android中常用的五种布局。

  1. LinearLayout – 线性布局。
    orientation – 容器内元素的排列方式。vertical: 子元素们垂直排列;horizontal: 子元素们水平排列
    gravity – 内容的排列形式。
    常用的有 top, bottom, left, right, center 等
  2. AbsoluteLayout – 绝对布局。
    layout_x – x 坐标。以左上角为顶点
    layout_y – y 坐标。以左上角为顶点
  3. TableLayout – 表格式布局
    表格布局主要以行列的形式来管理子控件,其中每一行即一个TableRow对象,每个TableRow对象可以添加子控件,并且每加入一个空间即相当于添加了一列
  4. RelativeLayout – 相对布局。
    layout_centerInParent – 将当前元素放置到其容器内的水平方向和垂直方向的中央位置(类似的属性有 :layout_centerHorizontal, layout_alignParentLeft 等)
    layout_marginLeft – 设置当前元素相对于其容器的左侧边缘的距离
    layout_below – 放置当前元素到指定的元素的下面
    layout_alignRight – 当前元素与指定的元素右对齐
  5. FrameLayout – 层叠布局。以左上角为起点,将 FrameLayout 内的元素一层覆盖一层地显示,在帧布局中,先添加的图片会被后添加的图片覆盖。
    摘自:http://javalover00000.javaeye.com/blog/851266

15.请介绍下Android的数据存储方式。

Android提供了5种方式存储数据:
* 使用SharedPreferences存储数据;
* 文件存储数据;
* SQLite数据库存储数据;
* 使用ContentProvider存储数据;
* 网络存储数据;
Android 中的数据存储都是私有的,其他应用程序都是无法访问的,除非通过ContentResolver获取其他程序共享的数据。
摘自:http://www.moandroid.com/?p=319

16.请介绍下ContentProvider是如何实现数据共享的。

一个程序可以通过实现一个Content provider的抽象接口将自己的数据完全暴露出去,而且Content providers是以类似数据库中表的方式将数据暴露。Content providers存储和检索数据,通过它可以让所有的应用程序访问到,这也是应用程序之间唯一共享数据的方法。要想使应用程序的数据公开化,可通过2种方法:创建一个属于你自己的Content provider或者将你的数据添加到一个已经存在的Content provider中,前提是有相同数据类型并且有写入Content provider的权限。

如何通过一套标准及统一的接口获取其他应用程序暴露的数据?Android提供了ContentResolver,外界的程序可以通过ContentResolver接口访问ContentProvider提供的数据。
参考:http://www.moandroid.com/?p=319

17.如何启用Service,如何停用Service。

1.第一种是通过调用Context.startService()启动,调用Context.stopService()结束,startService()可以传递参数给Service

2.第二种方式是通过调用Context.bindService()启动,调用Context.unbindservice()结束,还可以通过ServiceConnection访问Service。
在Service每一次的开启关闭过程中,只有onStart可被多次调用(通过多次startService调用),其他onCreate,onBind,onUnbind,onDestory在一个生命周期中只能被调用一次。
参考:http://www.cnblogs.com/feisky/archive/2010/06/14/1758336.html

18.注册广播有几种方式,这些方式有何优缺点?请谈谈Android引入广播机制的用意。

android中,不同进程之间传递信息要用到广播,可以有两种方式来实现。
第一种方式:在Manifest.xml中注册广播,是一种比较推荐的方法,因为它不需要手动注销广播(如果广播未注销,程序退出时可能会出错)。
具体实现在Manifest的application中添加:

上面两个android:name分别是广播名和广播的动作(这里的动作是表示系统启动完成),如果要自己发送一个广播,在代码中为:

Intent i = new Intent(“android.intent.action.BOOT_COMPLETED”);
sendBroadcast(i);

这样,广播就发出去了,然后是接收。
接收可以新建一个类,继承至BroadcastReceiver,也可以建一个BroadcastReceiver的实例,然后得写onReceive方法,实现如下:

protected BroadcastReceiver mEvtReceiver = new BroadcastReceiver() {
  @Override
  public void onReceive(Context context, Intent intent) {
      String action = intent.getAction();
      if (action.equals(“android.intent.action.BOOT_COMPLETED”))      {
      //Do something
      }
  }
};

第二种方式,直接在代码中实现,但需要手动注册注销,实现如下:

IntentFilter filter = new IntentFilter();
filter.addAction(“android.intent.action.BOOT_COMPLETED”);
registerReceiver(mEvtReceiver, filter); 

这时注册了一个recevier ,名为mEvtReceiver,然后同样用上面的方法以重写onReceiver,
最后在程序的onDestroy中要注销广播,实现如下:

@Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(mPlayerEvtReceiver);
}

Android系统中的广播是广泛用于应用程序之间通信的一种手段,它类似于事件处理机制,不同的地方就是广播的处理是系统级别的事件处理过程(一般事件处理是控件级别的)。在此过程中仍然是离不开Intent对象,理解广播事件的处理过程,灵活运用广播处理机制,在关键之处往往能实现特别的效果,
在Android 中如果要发送一个广播必须使用sendBroadCast 向系统发送对其感兴趣的广播接收器中。
使用广播必须要有一个intent 对象必设置其action动作对象
使用广播必须在配置文件中显式的指明该广播对象
每次接收广播都会重新生成一个接收广播的对象
在BroadCast 中尽量不要处理太多逻辑问题,建议复杂的逻辑交给Activity 或者 Service 去处理
转自:http://www.dengdeng.name/u/deng/archives/2010/92.html

http://www.cnblogs.com/TerryBlog/archive/2010/08/16/1801016.html

19.请解释下在单线程模型中Message、Handler、Message Queue、Looper之间的关系。

简单的说,Handler获取当前线程中的looper对象,looper用来从存放Message的MessageQueue中取出Message,再有Handler进行Message的分发和处理
参见:http://hi.baidu.com/%C1%F7%C0˲%BB%D0%DD/blog/item/4e576b7c58d742360cd7dac9.html

http://blog.csdn.net/xuxinyl/archive/2010/12/25/6097560.aspx

20.AIDL的全称是什么?如何工作?能处理哪些类型的数据?

AIDL全称Android Interface Definition Language(AndRoid接口描述语言) 是一种接口描述语言; 编译器可以通过aidl文件生成一段代码,通过预先定义的接口达到两个进程内部通信进程跨界对象访问的目的.AIDL的IPC的机制和COM或CORBA类似, 是基于接口的,但它是轻量级的。它使用代理类在客户端和实现层间传递值. 如果要使用AIDL, 需要完成2件事情:
1. 引入AIDL的相关类.;
2. 调用aidl产生的class.
理论上, 参数可以传递基本数据类型和String, 还有就是Bundle的派生类, 不过在Eclipse中,目前的ADT不支持Bundle做为参数,
具体实现步骤如下:

1、创建AIDL文件, 在这个文件里面定义接口, 该接口定义了可供客户端访问的方法和属性。

2、编译AIDL文件, 用Ant的话, 可能需要手动, 使用Eclipse plugin的话,可以根据adil文件自动生产java文件并编译, 不需要人为介入.

3、在Java文件中, 实现AIDL中定义的接口. 编译器会根据AIDL接口, 产生一个JAVA接口。这个接口有一个名为Stub的内部抽象类,它继承扩展了接口并实现了远程调用需要的几个方法。接下来就需要自己去实现自定义的几个接口了.
4、向客户端提供接口ITaskBinder, 如果写的是service,扩展该Service并重载onBind ()方法来返回一个实现上述接口的类的实例。
5、在服务器端回调客户端的函数. 前提是当客户端获取的IBinder接口的时候,要去注册回调函数, 只有这样, 服务器端才知道该调用那些函数

AIDL语法很简单,可以用来声明一个带一个或多个方法的接口,也可以传递参数和返回值。 由于远程调用的需要, 这些参数和返回值并不是任何类型.下面是些AIDL支持的数据类型:

  1. 不需要import声明的简单Java编程语言类型(int,boolean等)
  2. String, CharSequence不需要特殊声明
  3. List, Map和Parcelables类型, 这些类型内所包含的数据成员也只能是简单数据类型, String等其他比支持的类型.

(另外: 我没尝试Parcelables, 在Eclipse+ADT下编译不过, 或许以后会有所支持).

实现接口时有几个原则:

.抛出的异常不要返回给调用者. 跨进程抛异常处理是不可取的.

.IPC调用是同步的。如果你知道一个IPC服务需要超过几毫秒的时间才能完成地话,你应该避免在Activity的主线程中调用。 也就是IPC调用会挂起应用程序导致界面失去响应. 这种情况应该考虑单起一个线程来处理.

.不能在AIDL接口中声明静态属性。

IPC的调用步骤:

  1. 声明一个接口类型的变量,该接口类型在.aidl文件中定义。
  2. 实现ServiceConnection。
  3. 调用ApplicationContext.bindService(),并在ServiceConnection实现中进行传递.
  4. 在ServiceConnection.onServiceConnected()实现中,你会接收一个IBinder实例(被调用的Service). 调用

YourInterfaceName.Stub.asInterface((IBinder)service)将参数转换为YourInterface类型。

  1. 调用接口中定义的方法。 你总要检测到DeadObjectException异常,该异常在连接断开时被抛出。它只会被远程方法抛出。
  2. 断开连接,调用接口实例中的ApplicationContext.unbindService()
    参考:http://buaadallas.blog.51cto.com/399160/372090

21.请解释下Android程序运行时权限与文件系统权限的区别。

apk程序是运行在虚拟机上的,对应的是Android独特的权限机制,只有体现到文件系统上时才使用linux的权限设置。
android系统有的权限是基于签名的。
具体参见:http://blog.csdn.net/Zengyangtech/archive/2010/07/20/5749999.aspx

22.系统上安装了多种浏览器,能否指定某浏览器访问指定页面?请说明原由。

通过直接发送Uri把参数带过去,或者通过manifest里的intentfilter里的data属性

23.有一个一维整型数组int[]data保存的是一张宽为width,高为height的图片像素值信息。请写一个算法,将该图片所有的白色不透明(0xffffffff)像素点的透明度调整为50%。

24.什么是ANR 如何避免它?

ANR:Application Not Responding,五秒在Android中,活动管理器和窗口管理器这两个系统服务负责监视应用程序的响应。当出现下列情况时,Android就会显示ANR对话框了:
对输入事件(如按键、触摸屏事件)的响应超过5秒
意向接受器(intentReceiver)超过10秒钟仍未执行完毕
Android应用程序完全运行在一个独立的线程中(例如main)。这就意味着,任何在主线程中运行的,需要消耗大量时间的操作都会引发ANR。因为此时,你的应用程序已经没有机会去响应输入事件和意向广播(Intent broadcast)。
因此,任何运行在主线程中的方法,都要尽可能的只做少量的工作。特别是活动生命周期中的重要方法如onCreate()和 onResume()等更应如此。潜在的比较耗时的操作,如访问网络和数据库;或者是开销很大的计算,比如改变位图的大小,需要在一个单独的子线程中完成 (或者是使用异步请求,如数据库操作)。但这并不意味着你的主线程需要进入阻塞状态已等待子线程结束 — 也不需要调用Therad.wait()或者Thread.sleep()方法。取而代之的是,主线程为子线程提供一个句柄(Handler),让子线程 在即将结束的时候调用它(xing:可以参看Snake的例子,这种方法与以前我们所接触的有所不同)。使用这种方法涉及你的应用程序,能够保证你的程序 对输入保持良好的响应,从而避免因为输入事件超过5秒钟不被处理而产生的ANR。这种实践需要应用到所有显示用户界面的线程,因为他们都面临着同样的超时 问题。

25.什么情况会导致Force Close ?如何避免?能否捕获导致其的异常?

一般像空指针啊,可以看起logcat,然后对应到程序中 来解决错误

26.Android本身的api并未声明会抛出异常,则其在运行时有无可能抛出runtime异常,你遇到过吗?诺有的话会导致什么问题?如何解决?
27.简要解释一下activity、 intent 、intent filter、service、Broadcase、BroadcaseReceiver

一个activity呈现了一个用户可以操作的可视化用户界面
一个service不包含可见的用户界面,而是在后台无限地运行
可以连接到一个正在运行的服务中,连接后,可以通过服务中暴露出来的借口与其进行通信
一个broadcast receiver是一个接收广播消息并作出回应的component,broadcast receiver没有界面
intent:content provider在接收到ContentResolver的请求时被激活。
activity, service和broadcast receiver是被称为intents的异步消息激活的。
一个intent是一个Intent对象,它保存了消息的内容。对于activity和service来说,它指定了请求的操作名称和待操作数据的URI
Intent对象可以显式的指定一个目标component。如果这样的话,android会找到这个component(基于 manifest文件中的声明)并激活它。但如果一个目标不是显式指定的,android必须找到响应intent的最佳component。
它是通过将Intent对象和目标的intent filter相比较来完成这一工作的。一个component的intent filter告诉android该component能处理的intent。intent filter也是在manifest文件中声明的。

28.IntentService有何优点?

IntentService 的好处
* Acitivity的进程,当处理Intent的时候,会产生一个对应的Service
* Android的进程处理器现在会尽可能的不kill掉你
* 非常容易使用

29.横竖屏切换时候activity的生命周期?

  1. 不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次
  2. 设置Activity的android:configChanges=”orientation”时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次
  3. 设置Activity的android:configChanges=”orientation|keyboardHidden”时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法

30.如何将SQLite数据库(dictionary.db文件)与apk文件一起发布?

可以将dictionary.db文件复制到Eclipse Android工程中的res aw目录中。所有在res aw目录中的文件不会被压缩,这样可以直接提取该目录中的文件。可以将dictionary.db文件复制到res aw目录中

31.如何将打开res aw目录中的数据库文件?

在Android中不能直接打开res aw目录中的数据库文件,而需要在程序第一次启动时将该文件复制到手机内存或SD卡的某个目录中,然后再打开该数据库文件。复制的基本方法是使用getResources().openRawResource方法获得res aw目录中资源的 InputStream对象,然后将该InputStream对象中的数据写入其他的目录中相应文件中。在Android SDK中可以使用SQLiteDatabase.openOrCreateDatabase方法来打开任意目录中的SQLite数据库文件。

32.Android引入广播机制的用意?

  1. 从MVC的角度考虑(应用程序内) 其实回答这个问题的时候还可以这样问,android为什么要有那4大组件,现在的移动开发模型基本上也是照搬的web那一套MVC架构,只不过 是改了点嫁妆而已。android的四大组件本质上就是为了实现移动或者说嵌入式设备上的MVC架构,它们之间有时候是一种相互依存的关系,有时候又是一 种补充关系,引入广播机制可以方便几大组件的信息和数据交互。
  2. 程序间互通消息(例如在自己的应用程序内监听系统来电)
  3. 效率上(参考UDP的广播协议在局域网的方便性)
  4. 设计模式上(反转控制的一种应用,类似监听者模式) 转自:http://www.cnmsdn.com/html/201101/1295431222ID9251.html

33.android 的优势与不足

Android平台手机 5大优势:
1. 开放性
在优势方面,Android平台首先就是其开发性,开发的平台允许任何移动终端厂商加入到Android联盟中来。显著的开放性可以使其拥有更多的开发者,随着用户和应用的日益丰富,一个崭新的平台也将很快走向成熟
开发性对于Android的发展而言,有利于积累人气,这里的人气包括消费者和厂商,而对于消费者来讲,随大的受益正是丰富的软件资源。开放的平台也会带来更大竞争,如此一来,消费者将可以用更低的价位购得心仪的手机。
2. 挣脱运营商的束缚
在 过去很长的一段时间,特别是在欧美地区,手机应用往往受到运营商制约,使用什么功能接入什么网络,几乎都受到运营商的控制。从去年iPhone 上市 ,用户可以更加方便地连接网络,运营商的制约减少。随着EDGE、HSDPA这些2G至3G移动网络的逐步过渡和提升,手机随意接入网络已不是运营商口中 的笑谈,当你可以通过手机IM软件方便地进行即时聊天时,再回想不久前天价的彩信和图铃下载业务,是不是像噩梦一样?
互联网巨头Google推动的Android终端天生就有网络特色,将让用户离互联网更近。
3. 丰富的硬件选择
这 一点还是与Android平台的开放性相关,由于Android的开放性,众多的厂商会推出千奇百怪,功能特色各具的多种产品。功能上的差异和特色,却不 会影响到数据同步、甚至软件的兼容,好比你从诺基亚 Symbian风格手机 一下改用苹果 iPhone ,同时还可将Symbian中优秀的软件带到iPhone上使用、联系人等资料更是可以方便地转移,是不是非常方便呢?
4. 不受任何限制的开发商
Android平台提供给第三方开发商一个十分宽泛、自由的环境,不会受到各种条条框框的阻扰,可想而知,会有多少新颖别致的软件会诞生。但也有其两面性,血腥、暴力、情色方面的程序和游戏如可控制正是留给Android难题之一。
5. 无缝结合的Google应用
如今叱诧互联网的Google已经走过10年度历史,从搜索巨人到全面的互联网渗透,Google服务如地图、邮件、搜索等已经成为连接用户和互联网的重要纽带,而Android平台手机将无缝结合这些优秀的Google服务。
再说Android的5大不足:
1. 安全和隐私
由于手机 与互联网的紧密联系,个人隐私很难得到保守。除了上网过程中经意或不经意留下的个人足迹,Google这个巨人也时时站在你的身后,洞穿一切,因此,互联网的深入将会带来新一轮的隐私危机。
2. 首先开卖Android手机的不是最大运营商
众 所周知,T-Mobile在23日,于美国纽约发布 了Android首款手机G1。但是在北美市场,最大的两家运营商乃AT&T和Verizon,而目前所知取得Android手机销售权的仅有 T-Mobile和Sprint,其中T-Mobile的3G网络相对于其他三家也要逊色不少,因此,用户可以买账购买G1,能否体验到最佳的3G网络服 务则要另当别论了!
3. 运营商仍然能够影响到Android手机
在国内市场,不少用户对购得移动定制机不满,感觉所购的手机被人涂画了广告一般。这样的情况在国外市场同样出现。Android手机的另一发售运营商Sprint就将在其机型中内置其手机商店程序。
4. 同类机型用户减少
在 不少手机论坛都会有针对某一型号的子论坛,对一款手机的使用心得交流,并分享软件资源。而对于Android平台手机,由于厂商丰富,产品类型多样,这样 使用同一款机型的用户越来越少,缺少统一机型的程序强化。举个稍显不当的例子,现在山寨机泛滥,品种各异,就很少有专门针对某个型号山寨机的讨论和群组, 除了哪些功能异常抢眼、颇受追捧的机型以外。
5. 过分依赖开发商缺少标准配置
在 使用PC端的Windows Xp系统的时候,都会内置微软Windows Media Player这样一个浏览器程序,用户可以选择更多样的播放器,如Realplay或暴风影音等。但入手开始使用默认的程序同样可以应付多样的需要。在 Android平台中,由于其开放性,软件更多依赖第三方厂商,比如Android系统的SDK中就没有内置音乐 播放器,全部依赖第三方开发,缺少了产品的统一性。

34.android 中有哪几种解析xml的类?官方推荐哪种?以及它们的原理和区别。

XML解析主要有三种方式,SAX、DOM、PULL。常规在PC上开发我们使用Dom相对轻松些,但一些性能敏感的数据库或手机上还是主要采用SAX方 式,SAX读取是单向的,优点:不占内存空间、解析属性方便,但缺点就是对于套嵌多个分支来说处理不是很方便。而DOM方式会把整个XML文件加载到内存 中去,这里Android开发网提醒大家该方法在查找方面可以和XPath很好的结合如果数据量不是很大推荐使用,而PULL常常用在J2ME对于节点处 理比较好,类似SAX方式,同样很节省内存,在J2ME中我们经常使用的KXML库来解析。
详细情况请参考 http://blog.csdn.net/Android_Tutor/archive/2010/09/17/5890835.aspx

http://www.linuxidc.com/Linux/2010-11/29768.htm

http://littlefermat.blog.163.com/blog/static/59771167200981853037951/

35.DDMS和TraceView的区别?

DDMS是一个程序执行查看器,在里面可以看见线程和堆栈等信息,TraceView是程序性能分析器

36.Activity被回收了怎么办?

只有另启用了

37.java中如何引用本地语言

可以用JNI接口

38.谈谈Android的IPC机制

IPC是内部进程通信的简称, 是共享”命名管道”的资源。Android中的IPC机制是为了让Activity和Service之间可以随时的进行交互,故在Android中该机制,只适用于Activity和Service之间的通信,类似于远程方法调用,类似于C/S模式的访问。通过定义AIDL接口文件来定义IPC接口。Servier端实现IPC接口,Client端调用IPC接口本地代理。

39.NDK是什么

NDK是一些列工具的集合,
NDK提供了一系列的工具,帮助开发者迅速的开发C/C++的动态库,并能自动将so和java 应用打成apk包。
NDK集成了交叉编译器,并提供了相应的mk文件和隔离cpu、平台等的差异,开发人员只需简单的修改mk文件就可以创建出so

40.描述一下android的系统架构

android系统架构分从下往上为linux 内核层、运行库、应用程序框架层、和应用程序层
linuxkernel:负责硬件的驱动程序、网络、电源、系统安全以及内存管理等功能。

libraries和 androidruntime:libraries:即c/c++函数库部分,大多数都是开放源代码的函数库,例如webkit,该函数库负责 android网页浏览器的运行,例如标准的c函数库libc、openssl、sqlite等,当然也包括支持游戏开发2dsgl和 3dopengles,在多媒体方面有mediaframework框架来支持各种影音和图形文件的播放与显示,例如mpeg4、h.264、mp3、 aac、amr、jpg和png等众多的多媒体文件格式。android的runtime负责解释和执行生成的dalvik格式的字节码。

applicationframework(应用软件架构),java应用程序开发人员主要是使用该层封装好的api进行快速开发。

applications:该层是java的应用程序层,android内置的googlemaps、e-mail、即时通信工具、浏览器、mp3播放 器等处于该层,java开发人员开发的程序也处于该层,而且和内置的应用程序具有平等的位置,可以调用内置的应用程序,也可以替换内置的应用程序。

上面的四个层次,下层为上层服务,上层需要下层的支持,调用下层的服务,这种严格分层的方式带来的极大的稳定性、灵活性和可扩展性,使得不同层的开发人员可以按照规范专心特定层的开发。

android应用程序使用框架的api并在框架下运行,这就带来了程序开发的高度一致性,另一方面也告诉我们,要想写出优质高效的程序就必须对整个 applicationframework进行非常深入的理解。精通applicationframework,你就可以真正的理解android的设计 和运行机制,也就更能够驾驭整个应用层的开发。

android-Service和Thread的区别

1.服务不是单一的进程。服务没有自己的进程,应用程序可以不同,服务运行在相同的进程中。

2.服务不是线程。可以在线程中工作。

一.在应用中,如果是长时间的在后台运行,而且不需要交互的情况下,使用服务。

同样是在后台运行,不需要交互的情况下,如果只是完成某个任务,之后就不需要运行,而且可能是多个任务,需要长时间运行的情况下使用线程。

二.如果任务占用CPU时间多,资源大的情况下,要使用线程。

 

servie是系统的组件,它由系统进程托管(servicemanager);它们之间的通信类似于client和server,是一种轻量级的ipc通信,这种通信的载体是binder,它是在linux层交换信息的一种ipc。而thread是由本应用程序托管。

 

1). Thread:Thread 是程序执行的最小单元,它是分配CPU的基本单位。可以用Thread 来执行一些异步的操作。

 

2). Service:Service 是android的一种机制,当它运行的时候如果是Local Service,那么对应的Service 是运行在主进程的main 线程上的。如:onCreate,onStart 这些函数在被系统调用的时候都是在主进程的main 线程上运行的。如果是Remote Service,那么对应的Service 则是运行在独立进程的main 线程上。

 

既然这样,那么我们为什么要用Service 呢?其实这跟android 的系统机制有关,我们先拿Thread 来说。Thread 的运行是独立于Activity 的,也就是说当一个Activity 被finish 之后,如果你没有主动停止Thread 或者Thread 里的run 方法没有执行完毕的话,Thread 也会一直执行。因此这里会出现一个问题:当Activity 被finish 之后,你不再持有该Thread 的引用。另一方面,你没有办法在不同的Activity 中对同一Thread 进行控制。

 

举个例子:如果你的Thread 需要不停地隔一段时间就要连接服务器做某种同步的话,该Thread 需要在Activity 没有start的时候也在运行。这个时候当你start 一个Activity 就没有办法在该Activity 里面控制之前创建的Thread。因此你便需要创建并启动一个Service ,在Service 里面创建、运行并控制该Thread,这样便解决了该问题(因为任何Activity 都可以控制同一Service,而系统也只会创建一个对应Service 的实例)。

 

因此你可以把Service 想象成一种消息服务,而你可以在任何有Context 的地方调用Context.startService、Context.stopService、Context.bindService,Context.unbindService,来控制它,你也可以在Service 里注册BroadcastReceiver,在其他地方通过发送broadcast 来控制它,当然这些都是Thread 做不到的。

———————————————————————————

 

广播接收者(BroadcastReceiver)用于接收广播Intent,广播Intent的发送是通过调用Context.sendBroadcast()、Context.sendOrderedBroadcast()来实现的。通常一个广播Intent可以被订阅了此Intent的多个广播接收者所接收,这个特性跟JMS中的Topic消息接收者类似。要实现一个广播接收者方法如下:
第一步:继承BroadcastReceiver,并重写onReceive()方法。
public class IncomingSMSReceiver extends BroadcastReceiver {
@Override public void onReceive(Context context, Intent intent) {
}
}
第二步:订阅感兴趣的广播Intent,订阅方法有两种:
第一种:使用代码进行订阅
IntentFilter filter = new IntentFilter(“android.provider.Telephony.SMS_RECEIVED”);
IncomingSMSReceiver receiver = new IncomingSMSReceiver();
registerReceiver(receiver, filter);
第二种:在AndroidManifest.xml文件中的<application>节点里进行订阅:
<receiver android:name=”.IncomingSMSReceiver”>
<intent-filter>
<action android:name=”android.provider.Telephony.SMS_RECEIVED”/>
</intent-filter>
</receiver>

如果你想别人接收到的短信,达到你不可告人的目的,那么使用BroadcastReceiver
当系统收到短信时,会发出一个广播Intent,Intent的action名称为、

如果要短信终止广播就要配置上你的广播接收者的级别

<intent-filter android:priority=”100″ >
<action android:name=”android.provider.Telephony.SMS_RECEIVED” />
</intent-filter>

android.provider.Telephony.SMS_RECEIVED,该Intent存放了系统接收到的短信内容,我们使用名称“pdus”即可从Intent中获取到短信内容。

在AndroidManifest.xml文件中的<application>节点里对接收到短信的广播Intent进行订阅:
<receiver android:name=”.你的receiver名称”>
<intent-filter><action android:name=”android.provider.Telephony.SMS_RECEIVED”/></intent-filter></receiver>
在AndroidManifest.xml文件中添加以下权限:
<uses-permission android:name=”android.permission.RECEIVE_SMS”/><!– 接收短信权限 –>
<uses-permission android:name=”android.permission.SEND_SMS”/><!– 发送短信权限 –>

广播接收者的响应性

在Android中,每次广播消息到来时都会创建BroadcastReceiver实例并执行onReceive() 方法, onReceive() 方法执行完后,BroadcastReceiver 的实例就会被销毁。当onReceive() 方法在10秒内没有执行完毕,Android会认为该程序无响应。所以在BroadcastReceiver里不能做一些比较耗时的操作,否侧会弹出ANR(Application No Response)错误对话框。如果需要完成一项比较耗时的工作,应该通过发送Intent给Service,由Service来完成。这里不能使用子线程来解决,因为BroadcastReceiver的生命周期很短,子线程可能还没有结束BroadcastReceiver就先结束了。BroadcastReceiver一旦结束,此时BroadcastReceiver所在的进程很容易在系统需要内存时被优先杀死,因为它属于空进程(没有任何活动组件的进程)。如果它的所在进程被杀死,那么正在工作的子线程也会被杀死。所以采用子线程来解决是不可靠的。

public class IncomingSMSReceiver extends BroadcastReceiver {
@Override public void onReceive(Context context, Intent intent) {
//发送Intent启动服务,由服务来完成比较耗时的操作
Intent service = new Intent(context, XxxService.class);
context.startService(service);
}

除了短信到来广播Intent,Android还有很多广播Intent,如:开机启动、电池电量变化、时间已经改变等广播Intent。
接收电池电量变化广播Intent ,在AndroidManifest.xml文件中的<application>节点里订阅此Intent:
<receiver android:name=”.IncomingSMSReceiver”>
<intent-filter>
<action android:name=”android.intent.action.BATTERY_CHANGED”/>
</intent-filter>
</receiver>

接收开机启动广播Intent,在AndroidManifest.xml文件中的<application>节点里订阅此Intent:
<receiver android:name=”.IncomingSMSReceiver”>
<intent-filter>
<action android:name=”android.intent.action.BOOT_COMPLETED”/>
</intent-filter>
</receiver>
并且要进行权限声明:
<uses-permission android:name=”android.permission.RECEIVE_BOOT_COMPLETED”/>

广播接收者补充

广播分两种
有序广播 按照广播的优先级 发给相对应的广播接收者-1000-1000 激活广播通过onrecve方法处理
无序广播

有序广播有一个特例
sendOrderedBroadcast(intent, receiverPermission, resultReceiver, scheduler, initialCode, initialData, initialExtras);
resultReceiver 广播接受者 如果我们显示的指定了广播接收者
无论如何 都会接受广播 无法通过abortBroadcast();的方法终止广播
比如拨打电话有个out_goingcall 的广播是指定广播接收者的无法通过abortBroadcast()方法终止的,但是是可以将拨打的电话号码数据清空置为null,setResultData(null)就无法拨打电话

 

另外一种特殊的广播sendStickyBroadcast(intent) // 阴魂不散的广播
一般广播事件发送完毕被广播接受者接收到onReceive执行完毕后广播接收者的生命周期就结束了 这个会保持长时间的停留直到广播事件结束完毕
例如系统的Wifi,网卡状态的改变要一定的时间,保证网络状态更新完毕后才结束

代码中注册,如果代码没有执行,就接受不到广播事件

什么时候使用广播,例如sdcard新增图片的时候是无法显示到图库的当sdcard被挂载状态发生改变才会重新加载sdcard的数据

这时可以发送一个sd挂载的通知,通知系统的gallery去获取到新的图片.

Intent intent = newIntent(Intent.ACTION_MEDIA_MOUNTED,Uri.parse(“file://”+Environment.getExternalStorageDirectory()));

sendBroadcast(intent);

Anroid https代码示例

https与http的通信,在我看来主要的区别在于https多了一个安全验证机制,而Android采用的是X509验证,首先我们需要这重写X509类,建立我们的验证规则、、不过对于特定的项目,我们一般都是无条件信任服务端的,因此我们可以对任何证书都无条件信任(其实本质上我们只是信任了特定url的证书,为了偷懒,才那么选择的)/**

 * 信任所有主机-对于任何证书都不做检查
 */
class MytmArray implements X509TrustManager {
    public X509Certificate[] getAcceptedIssuers() {
        // return null;
        return new X509Certificate[] {};
    }
    @Override
    public void checkClientTrusted(X509Certificate[] chain, String authType)
            throws CertificateException {
        // TODO Auto-generated method stub
    }
    @Override
    public void checkServerTrusted(X509Certificate[] chain, String authType)
            throws CertificateException {
        // TODO Auto-generated method stub
        // System.out.println("cert: " + chain[0].toString() + ", authType: "
        // + authType);
    }
};

好了,我们写好了信任规则,接下载就要创建一个主机的信任列表

static TrustManager[] xtmArray = new MytmArray[] { new MytmArray() };
    /**
     * 信任所有主机-对于任何证书都不做检查
     */
    private static void trustAllHosts() {
        // Create a trust manager that does not validate certificate chains
        // Android 采用X509的证书信息机制
        // Install the all-trusting trust manager
        try {
            SSLContext sc = SSLContext.getInstance("TLS");
            sc.init(null, xtmArray, new java.security.SecureRandom());
            HttpsURLConnection
                    .setDefaultSSLSocketFactory(sc.getSocketFactory());
            // HttpsURLConnection.setDefaultHostnameVerifier(DO_NOT_VERIFY);//
            // 不进行主机名确认
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    static HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {
        @Override
        public boolean verify(String hostname, SSLSession session) {
            // TODO Auto-generated method stub
            // System.out.println("Warning: URL Host: " + hostname + " vs. "
            // + session.getPeerHost());
            return true;
        }
    };

上面的都是https通信需要做的几个基本要求,接下载我们要做的就是https的使用啦下面就以get和post为例进行说明,中间还涉及到cookie的使用

String httpUrl="XXXXX"
String result = "";
        HttpURLConnection http = null;
        URL url;
        try {
            url = new URL(httpUrl);
            // 判断是http请求还是https请求
            if (url.getProtocol().toLowerCase().equals("https")) {
                trustAllHosts();
                http = (HttpsURLConnection) url.openConnection();
                ((HttpsURLConnection) http).setHostnameVerifier(DO_NOT_VERIFY);// 不进行主机名确认
            } else {
                http = (HttpURLConnection) url.openConnection();
            }
            http.setConnectTimeout(10000);// 设置超时时间
            http.setReadTimeout(50000);
            http.setRequestMethod("GET");// 设置请求类型为
            http.setDoInput(true);
            http.setRequestProperty("Content-Type", "text/xml");
//http.getResponseCode());http或https返回状态200还是403
BufferedReader in = null;
            if (obj.getHttpStatus() == 200) {
                getCookie(http);
                in = new BufferedReader(new InputStreamReader(
                        http.getInputStream()));
            } else
                in = new BufferedReader(new InputStreamReader(
                        http.getErrorStream()));
            result = in.readLine();
            Log.i("result", result);
            in.close();
            http.disconnect();
 https或http的get请求写好了,哦中间涉及到了一个getCookie的方法,如下:
Java代码  收藏代码
/** 得到cookie */
    private static void getCookie(HttpURLConnection http) {
        String cookieVal = null;
        String key = null;
        DataDefine.mCookieStore = "";
        for (int i = 1; (key = http.getHeaderFieldKey(i)) != null; i++) {
            if (key.equalsIgnoreCase("set-cookie")) {
                cookieVal = http.getHeaderField(i);
                cookieVal = cookieVal.substring(0, cookieVal.indexOf(";"));
                DataDefine.mCookieStore = DataDefine.mCookieStore + cookieVal
                        + ";";
            }
        }
    }
public static Query HttpQueryReturnClass(String httpUrl, String base64) {

Java代码  收藏代码
        String result = "";
        Log.i("控制", httpUrl);
        Query obj = new Query();
        HttpURLConnection http = null;
        URL url;
        try {
            url = new URL(httpUrl);
            // 判断是http请求还是https请求
            if (url.getProtocol().toLowerCase().equals("https")) {
                trustAllHosts();
                http = (HttpsURLConnection) url.openConnection();
                ((HttpsURLConnection) http).setHostnameVerifier(DO_NOT_VERIFY);// 不进行主机名确认
            } else {
                http = (HttpURLConnection) url.openConnection();
            }
            http.setConnectTimeout(10000);// 设置超时时间
            http.setReadTimeout(50000);
            http.setRequestMethod("POST");// 设置请求类型为post
            http.setDoInput(true);
            http.setDoOutput(true);
            http.setRequestProperty("Content-Type", "text/xml");
            http.setRequestProperty("Cookie", DataDefine.mCookieStore);
            DataOutputStream out = new DataOutputStream(http.getOutputStream());
            out.writeBytes(base64);
            out.flush();
            out.close();
            obj.setHttpStatus(http.getResponseCode());// 设置http返回状态200还是403
            BufferedReader in = null;
            if (obj.getHttpStatus() == 200) {
                getCookie(http);
                in = new BufferedReader(new InputStreamReader(
                        http.getInputStream()));
            } else
                in = new BufferedReader(new InputStreamReader(
                        http.getErrorStream()));
            result = in.readLine();// 得到返回结果
            in.close();
            http.disconnect();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
}

这里面的base64是我经过base64加密过以后的数据

Android获取网络状态及类型

代码示例


package com.example.vdisktest;  
  
import android.app.Activity;  
import android.content.Context;  
import android.database.Cursor;  
import android.net.ConnectivityManager;  
import android.net.NetworkInfo;  
import android.net.Uri;  
import android.os.Bundle;  
import android.telephony.TelephonyManager;  
import android.text.TextUtils;  
import android.util.Log;  
  
public class NetTypeActivity extends Activity {  
  
    /** Called when the activity is first created. */  
    public static final String CTWAP = "ctwap";  
    public static final String CTNET = "ctnet";  
    public static final String CMWAP = "cmwap";  
    public static final String CMNET = "cmnet";  
    public static final String NET_3G = "3gnet";  
    public static final String WAP_3G = "3gwap";  
    public static final String UNIWAP = "uniwap";  
    public static final String UNINET = "uninet";  
  
    public static final int TYPE_CT_WAP = 5;  
    public static final int TYPE_CT_NET = 6;  
    public static final int TYPE_CT_WAP_2G = 7;  
    public static final int TYPE_CT_NET_2G = 8;  
  
    public static final int TYPE_CM_WAP = 9;  
    public static final int TYPE_CM_NET = 10;  
    public static final int TYPE_CM_WAP_2G = 11;  
    public static final int TYPE_CM_NET_2G = 12;  
  
    public static final int TYPE_CU_WAP = 13;  
    public static final int TYPE_CU_NET = 14;  
    public static final int TYPE_CU_WAP_2G = 15;  
    public static final int TYPE_CU_NET_2G = 16;  
  
    public static final int TYPE_OTHER = 17;  
  
    public static Uri PREFERRED_APN_URI = Uri  
            .parse("content://telephony/carriers/preferapn");  
  
    /** 没有网络 */  
    public static final int TYPE_NET_WORK_DISABLED = 0;  
  
    /** wifi网络 */  
    public static final int TYPE_WIFI = 4;  
  
    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        long start = System.currentTimeMillis();  
        int checkNetworkType = checkNetworkType(this);  
        Log.i("NetType""===========elpase:"  
                + (System.currentTimeMillis() - start));  
  
        switch (checkNetworkType) {  
        case TYPE_WIFI:  
            Log.i("NetType""================wifi");  
            break;  
        case TYPE_NET_WORK_DISABLED:  
            Log.i("NetType""================no network");  
            break;  
        case TYPE_CT_WAP:  
            Log.i("NetType""================ctwap");  
            break;  
        case TYPE_CT_WAP_2G:  
            Log.i("NetType""================ctwap_2g");  
            break;  
        case TYPE_CT_NET:  
            Log.i("NetType""================ctnet");  
            break;  
        case TYPE_CT_NET_2G:  
            Log.i("NetType""================ctnet_2g");  
            break;  
        case TYPE_CM_WAP:  
            Log.i("NetType""================cmwap");  
            break;  
        case TYPE_CM_WAP_2G:  
            Log.i("NetType""================cmwap_2g");  
            break;  
        case TYPE_CM_NET:  
            Log.i("NetType""================cmnet");  
            break;  
        case TYPE_CM_NET_2G:  
            Log.i("NetType""================cmnet_2g");  
            break;  
        case TYPE_CU_NET:  
            Log.i("NetType""================cunet");  
            break;  
        case TYPE_CU_NET_2G:  
            Log.i("NetType""================cunet_2g");  
            break;  
        case TYPE_CU_WAP:  
            Log.i("NetType""================cuwap");  
            break;  
        case TYPE_CU_WAP_2G:  
            Log.i("NetType""================cuwap_2g");  
            break;  
        case TYPE_OTHER:  
            Log.i("NetType""================other");  
            break;  
        default:  
            break;  
        }  
    }  
  
    /*** 
     * 判断Network具体类型(联通移动wap,电信wap,其他net) 
     *  
     * */  
    public static int checkNetworkType(Context mContext) {  
        try {  
            final ConnectivityManager connectivityManager = (ConnectivityManager) mContext  
                    .getSystemService(Context.CONNECTIVITY_SERVICE);  
            final NetworkInfo mobNetInfoActivity = connectivityManager  
                    .getActiveNetworkInfo();  
            if (mobNetInfoActivity == null || !mobNetInfoActivity.isAvailable()) {  
                // 注意一:  
                // NetworkInfo 为空或者不可以用的时候正常情况应该是当前没有可用网络,  
                // 但是有些电信机器,仍可以正常联网,  
                // 所以当成net网络处理依然尝试连接网络。  
                // (然后在socket中捕捉异常,进行二次判断与用户提示)。  
                return TYPE_NET_WORK_DISABLED;  
            } else {  
                // NetworkInfo不为null开始判断是网络类型  
                int netType = mobNetInfoActivity.getType();  
                if (netType == ConnectivityManager.TYPE_WIFI) {  
                    // wifi net处理  
                    return TYPE_WIFI;  
                } else if (netType == ConnectivityManager.TYPE_MOBILE) {  
                    // 注意二:  
                    // 判断是否电信wap:  
                    // 不要通过getExtraInfo获取接入点名称来判断类型,  
                    // 因为通过目前电信多种机型测试发现接入点名称大都为#777或者null,  
                    // 电信机器wap接入点中要比移动联通wap接入点多设置一个用户名和密码,  
                    // 所以可以通过这个进行判断!  
  
                    boolean is3G = isFastMobileNetwork(mContext);  
  
                    final Cursor c = mContext.getContentResolver().query(  
                            PREFERRED_APN_URI, nullnullnullnull);  
                    if (c != null) {  
                        c.moveToFirst();  
                        final String user = c.getString(c  
                                .getColumnIndex("user"));  
                        if (!TextUtils.isEmpty(user)) {  
                            if (user.startsWith(CTWAP)) {  
                                return is3G ? TYPE_CT_WAP : TYPE_CT_WAP_2G;  
                            } else if (user.startsWith(CTNET)) {  
                                return is3G ? TYPE_CT_NET : TYPE_CT_NET_2G;  
                            }  
                        }  
                    }  
                    c.close();  
  
                    // 注意三:  
                    // 判断是移动联通wap:  
                    // 其实还有一种方法通过getString(c.getColumnIndex("proxy")获取代理ip  
                    // 来判断接入点,10.0.0.172就是移动联通wap,10.0.0.200就是电信wap,但在  
                    // 实际开发中并不是所有机器都能获取到接入点代理信息,例如魅族M9 (2.2)等...  
                    // 所以采用getExtraInfo获取接入点名字进行判断  
  
                    String netMode = mobNetInfoActivity.getExtraInfo();  
                    Log.i("""==================netmode:" + netMode);  
                    if (netMode != null) {  
                        // 通过apn名称判断是否是联通和移动wap  
                        netMode = netMode.toLowerCase();  
  
                        if (netMode.equals(CMWAP)) {  
                            return is3G ? TYPE_CM_WAP : TYPE_CM_WAP_2G;  
                        } else if (netMode.equals(CMNET)) {  
                            return is3G ? TYPE_CM_NET : TYPE_CM_NET_2G;  
                        } else if (netMode.equals(NET_3G)  
                                || netMode.equals(UNINET)) {  
                            return is3G ? TYPE_CU_NET : TYPE_CU_NET_2G;  
                        } else if (netMode.equals(WAP_3G)  
                                || netMode.equals(UNIWAP)) {  
                            return is3G ? TYPE_CU_WAP : TYPE_CU_WAP_2G;  
                        }  
                    }  
                }  
            }  
  
        } catch (Exception ex) {  
            ex.printStackTrace();  
            return TYPE_OTHER;  
        }  
  
        return TYPE_OTHER;  
  
    }  
  
    private static boolean isFastMobileNetwork(Context context) {  
        TelephonyManager telephonyManager = (TelephonyManager) context  
                .getSystemService(Context.TELEPHONY_SERVICE);  
  
        switch (telephonyManager.getNetworkType()) {  
        case TelephonyManager.NETWORK_TYPE_1xRTT:  
            return false// ~ 50-100 kbps  
        case TelephonyManager.NETWORK_TYPE_CDMA:  
            return false// ~ 14-64 kbps  
        case TelephonyManager.NETWORK_TYPE_EDGE:  
            return false// ~ 50-100 kbps  
        case TelephonyManager.NETWORK_TYPE_EVDO_0:  
            return true// ~ 400-1000 kbps  
        case TelephonyManager.NETWORK_TYPE_EVDO_A:  
            return true// ~ 600-1400 kbps  
        case TelephonyManager.NETWORK_TYPE_GPRS:  
            return false// ~ 100 kbps  
        case TelephonyManager.NETWORK_TYPE_HSDPA:  
            return true// ~ 2-14 Mbps  
        case TelephonyManager.NETWORK_TYPE_HSPA:  
            return true// ~ 700-1700 kbps  
        case TelephonyManager.NETWORK_TYPE_HSUPA:  
            return true// ~ 1-23 Mbps  
        case TelephonyManager.NETWORK_TYPE_UMTS:  
            return true// ~ 400-7000 kbps  
        case TelephonyManager.NETWORK_TYPE_EHRPD:  
            return true// ~ 1-2 Mbps  
        case TelephonyManager.NETWORK_TYPE_EVDO_B:  
            return true// ~ 5 Mbps  
        case TelephonyManager.NETWORK_TYPE_HSPAP:  
            return true// ~ 10-20 Mbps  
        case TelephonyManager.NETWORK_TYPE_IDEN:  
            return false// ~25 kbps  
        case TelephonyManager.NETWORK_TYPE_LTE:  
            return true// ~ 10+ Mbps  
        case TelephonyManager.NETWORK_TYPE_UNKNOWN:  
            return false;  
        default:  
            return false;  
  
        }  
    }  
  
}  

 

android如何让service不被杀死-提高进程优先级

1.在service中重写下面的方法,这个方法有三个返回值, START_STICKY是service被kill掉后自动重写创建

[代码]java代码:

@Override

public int onStartCommand(Intent intent, int flags, int startId) {

return START_STICKY;

}—————-

@Override

public int onStartCommand(Intent intent, int flags, int startId) {

// TODO Auto-generated method stub

Log.v(“TrafficService”,”startCommand”);

flags = START_STICKY;

return super.onStartCommand(intent, flags, startId);

// return START_REDELIVER_INTENT;

}

2.在Service的onDestroy()中重启Service.

public void onDestroy() {

Intent localIntent = new Intent();

localIntent.setClass(this, MyService.class); //销毁时重新启动Service

this.startService(localIntent);

}

用qq管家杀掉进程的时候,调用的是系统自带的强制kill功能(即settings里的),在kill时,会将应用的整个进程停掉,当然包括service在内,如果在running里将service强制kill掉,显示进程还在。不管是kill整个进程还是只kill掉进应用的 service,都不会重新启动service。不知道你是怎么实现重启的,实在是不解。 在eclipse中,用stop按钮kill掉进程的时候,倒是会重启service

KILL问题:

1. settings 中stop service

onDestroy方法中,调用startService进行Service的重启。

2.settings中force stop 应用

捕捉系统进行广播(action为android.intent.action.PACKAGE_RESTARTED)

3. 借助第三方应用kill掉running task

提升service的优先级

service开机启动

今天我们主要来探讨android怎么让一个service开机自动启动功能的实现。Android手机在启动的过程中会触发一个Standard Broadcast Action,名字叫android.intent.action.BOOT_COMPLETED(记得只会触发一次呀),在这里我们可以通过构建一个广播接收者来接收这个这个action.下面我就来简单写以下实现的步骤:

第一步:首先创建一个广播接收者,重构其抽象方法 onReceive(Context context, Intent intent),在其中启动你想要启动的Service或app。

[代码]java代码:

import android.content.BroadcastReceiver;

import android.content.Context;

import android.content.Intent;

import android.util.Log;

public class BootBroadcastReceiver extends BroadcastReceiver {

//重写onReceive方法

@Override

public void onReceive(Context context, Intent intent) {

//后边的XXX.class就是要启动的服务

Intent service = new Intent(context,XXXclass);

context.startService(service);

Log.v(“TAG”, “开机自动服务自动启动…..”);

//启动应用,参数为需要自动启动的应用的包名

Intent intent = getPackageManager().getLaunchIntentForPackage(packageName);

context.startActivity(intent );

}

}

第二步:配置xml文件,在re

ceiver接收这种添加intent-filter配置

[代码]java代码:

<receiver android:name=”BootBroadcastReceiver”>

<intent-filter>

<action android:name=”android.intent.action.BOOT_COMPLETED”></action>

<category android:name=”android.intent.category.LAUNCHER” />

</intent-filter>

</receiver>

第三步:添加权限 <uses-permission android:name=”android.permission.RECEIVE_BOOT_COMPLETED” />

如何实现一个不会被杀死的进程

看Android的文档知道,当进程长期不活动,或系统需要资源时,会自动清理门户,杀死一些Service,和不可见的Activity等所在的进程。

但是如果某个进程不想被杀死(如数据缓存进程,或状态监控进程,或远程服务进程),应该怎么做,才能使进程不被杀死。

add
android:persistent=”true” into the <application> section in your AndroidManifest.xml

切记,这个
不可滥用,系统中用这个的service,app一多,整个系统就完蛋了。

目前系统中有phone等非常有限的,必须一直活着的应用在试用。

提升service优先级的方法

Android 系统对于内存管理有自己的一套方法,为了保障系统有序稳定的运信,系统内部会自动分配,控制程序的内存使用。当系统觉得当前的资源非常有限的时候,为了保 证一些优先级高的程序能运行,就会杀掉一些他认为不重要的程序或者服务来释放内存。这样就能保证真正对用户有用的程序仍然再运行。如果你的 Service 碰上了这种情况,多半会先被杀掉。但如果你增加 Service 的优先级就能让他多留一会,我们可以用 setForeground(true) 来设置 Service 的优先级。

为什么是 foreground ? 默认启动的 Service 是被标记为 background,当前运行的 Activity 一般被标记为 foreground,也就是说你给 Service 设置了 foreground 那么他就和正在运行的 Activity 类似优先级得到了一定的提高。当让这并不能保证你得 Service 永远不被杀掉,只是提高了他的优先级。

从Android 1.5开始,一个已启动的service可以调用startForeground(int, Notification)将service置为foreground状态,调用stopForeground(boolean)将service置为 background状态。

我们会在调用startForeground(int, Notification)传入参数notification,它会在状态栏里显示正在进行的foreground service。background service不会在状态栏里显示。

在Android 1.0中,将一个service置为foreground状态:

setForeground(true);

mNM.notify(id, notification);

将一个service置为background状态:

mNM.cancel(id);

setForeground(false);

对比看出,在1.0 API中调用setForeground(boolean)只是简单的改变service的状态,用户不会有任何觉察。新API中强制将 notification和改变service状态的动作绑定起来,foreground service会在状态栏显示,而background service不会。

Remote service controller & binding

跨进程调用Service。暂时不研究。

如何防止Android应用中的Service被系统回收? 很多朋友都在问,如何防止Android应用中的Service被系统回收?下面简单解答一下。

对于Service被系统回收,一般做法是通过提高优先级可以解决,在AndroidManifest.xml文件中对于intent-filter可以通过
android:priority = “1000”这个属性设置最高优先级,1000是最高值,如果数字越小则优先级越低,同时实用于广播,推荐大家如果你的应用很重要,可以考虑通过系统常用intent action来触发。

 

 

 

下面这是从另外的网页上看到的,一并转过来了:http://goo.gl/eEfBup

为了提高 我们的Activity中的线程的
线程优先级(Thread-Priority),我们需要在AndroidManifest.xml 中使用 ‘uses-permission’ 这样做:
XML: 
          <uses-permission id=”android.permission.RAISED_THREAD_PRIORITY”/>

  现在你可以在你的Activity中使用以下代码改变或提高任何线程的优先级:
Java: 
          import android.os.Process;
// …

// ———————————–
// Set the priority of the calling thread, based on Linux priorities:
// ———————————–

// Changes the Priority of the calling Thread!
Process.setThreadPriority(12);
// Changes the Priority of passed Thread (first param)
Process.setThreadPriority(Process.myTid(), 12);

  这里 range 的范围是 -20 (高) 到 +19 (低). 不要选得 太高  

  最好使用预先定义在 android.os.Process 的constants :
Java: 
          // Lower is ‘more impotant’
Process.THREAD_PRIORITY_LOWEST = 19
Process.THREAD_PRIORITY_BACKGROUND = 5
Process.THREAD_PRIORITY_DEFAULT = 0
Process.THREAD_PRIORITY_FOREGROUND = -5
Process.THREAD_PRIORITY_DISPLAY = -10
Process.THREAD_PRIORITY_URGENT_DISPLAY = -15

Android四大基本组件介绍与生命周期

Android四大基本组件分别是Activity,Service服务,Content Provider内容提供者,BroadcastReceiver广播接收器。

一:了解四大基本组件

Activity :

应用程序中,一个Activity通常就是一个单独的屏幕,它上面可以显示一些控件也可以监听并处理用户的事件做出响应。

Activity之间通过Intent进行通信。在Intent 的描述结构中,有两个最重要的部分:动作和动作对应的数据。

典型的动作类型有:M AIN(activity的门户)、VIEW、PICK、EDIT 等。而动作对应的数据则以URI 的形式进行表示。例如:要查看一个人的联系方式,你需要创建一个动作类型为VIEW 的intent,以及一个表示这个人的URI。

与之有关系的一个类叫IntentFilter。相对于intent 是一个有效的做某事的请求,一个intentfilter 则用于描述一个activity(或者IntentReceiver)能够操作哪些intent。一个activity 如果要显示一个人的联系方式时,需要声明一个IntentFilter,这个IntentFilter 要知道怎么去处理VIEW 动作和表示一个人的URI。IntentFilter 需要在AndroidManifest.xml 中定义。通过解析各种intent,从一个屏幕导航到另一个屏幕是很简单的。当向前导航时,activity 将会调用startActivity(Intent myIntent)方法。然后,系统会在所有安装的应用程序中定义的IntentFilter 中查找,找到最匹配myIntent 的Intent 对应的activity。新的activity 接收到myIntent 的通知后,开始运行。当startActivity 方法被调用将触发解析myIntent 的动作,这个机制提供了两个关键好处:

A、Activities 能够重复利用从其它组件中以Intent 的形式产生的一个请求;

B、Activities 可以在任何时候被一个具有相同IntentFilter 的新的Activity 取代。

AndroidManifest文件中含有如下过滤器的Activity组件为默认启动类当程序启动时系统自动调用它

<intent-filter>
       <action android:name="android.intent.action.MAIN" />
       <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

BroadcastReceive广播接收器:

你的应用可以使用它对外部事件进行过滤只对感兴趣的外部事件(如当电话呼入时,或者数据网络可用时)进行接收并做出响应。广播接收器没有用户界面。然而,它们可以启动一个activity或serice 来响应它们收到的信息,或者用NotificationManager 来通知用户。通知可以用很多种方式来吸引用户的注意力──闪动背灯、震动、播放声音等。一般来说是在状态栏上放一个持久的图标,用户可以打开它并获取消息。

广播类型:

普通广播通过Context.sendBroadcast(Intent myIntent)发送的

有序广播通过Context.sendOrderedBroadcast(intent, receiverPermission)发送的,该方法第2个参数决定该广播的级别,级别数值是在 -1000 到 1000 之间 , 值越大 , 发送的优先级越高;广播接收者接收广播时的级别级别(可通过intentfilter中的priority进行设置设为2147483647时优先级最高),同级别接收的先后是随机的, 再到级别低的收到广播,高级别的或同级别先接收到广播的可以通过abortBroadcast()方法截断广播使其他的接收者无法收到该广播,还有其他构造函数

异步广播通过Context.sendStickyBroadcast(Intent myIntent)发送的,还有sendStickyOrderedBroadcast(intent, resultReceiver, scheduler,  initialCode, initialData, initialExtras)方法,该方法具有有序广播的特性也有异步广播的特性;发送异步广播要: <uses-permission android:name=“android.permission.BROADCAST_STICKY” />权限,接收并处理完Intent后,广播依然存在,直到你调用removeStickyBroadcast(intent)主动把它去掉

注意:发送广播时的intent参数与Contex.startActivity()启动起来的Intent不同,前者可以被多个订阅它的广播接收器调用,后者只能被一个(Activity或service)调用

监听广播Intent步骤:

1>             写一个继承BroadCastReceiver的类,重写onReceive()方法,广播接收器仅在它执行这个方法时处于活跃状态。当onReceive()返回后,它即为失活状态,注意:为了保证用户交互过程的流畅,一些费时的操作要放到线程里,如类名SMSBroadcastReceiver

2>            注册该广播接收者,注册有两种方法程序动态注册和AndroidManifest文件中进行静态注册(可理解为系统中注册)如下:

        静态注册,注册的广播,下面的priority表示接收广播的级别”2147483647″为最高优先级

<receiver android:name=".SMSBroadcastReceiver" >
  <intent-filter android:priority = "2147483647" >
    <action android:name="android.provider.Telephony.SMS_RECEIVED" />
  </intent-filter>
</receiver >

动态注册,一般在Activity可交互时onResume()内注册BroadcastReceiver

IntentFilter intentFilter=new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
registerReceiver(mBatteryInfoReceiver ,intentFilter);

//反注册
unregisterReceiver(receiver);

注意:

1.生命周期只有十秒左右,如果在 onReceive() 内做超过十秒内的事情,就会报ANR(Application No Response) 程序无响应的错误信息,如果需要完成一项比较耗时的工作 , 应该通过发送 Intent 给 Service, 由Service 来完成 . 这里不能使用子线程来解决 , 因为 BroadcastReceiver 的生命周期很短 , 子线程可能还没有结束BroadcastReceiver 就先结束了 .BroadcastReceiver 一旦结束 , 此时 BroadcastReceiver 的所在进程很容易在系统需要内存时被优先杀死 , 因为它属于空进程 ( 没有任何活动组件的进程 ). 如果它的宿主进程被杀死 , 那么正在工作的子线程也会被杀死 . 所以采用子线程来解决是不可靠的

2. 动态注册广播接收器还有一个特点,就是当用来注册的Activity关掉后,广播也就失效了。静态注册无需担忧广播接收器是否被关闭,只要设备是开启状态,广播接收器也是打开着的。也就是说哪怕app本身未启动,该app订阅的广播在触发时也会对它起作用

系统常见广播Intent,如开机启动、电池电量变化、时间改变等广播

Service 服务:

一个Service 是一段长生命周期的,没有用户界面的程序,可以用来开发如监控类程序。

比较好的一个例子就是一个正在从播放列表中播放歌曲的媒体播放器。在一个媒体播放器的应用中,应该会有多个activity,让使用者可以选择歌曲并播放歌曲。然而,音乐重放这个功能并没有对应的activity,因为使用者当然会认为在导航到其它屏幕时音乐应该还在播放的。在这个例子中,媒体播放器这个activity 会使用Context.startService()来启动一个service,从而可以在后台保持音乐的播放。同时,系统也将保持这个service 一直执行,直到这个service 运行结束。另外,我们还可以通过使用Context.bindService()方法,连接到一个service 上(如果这个service 还没有运行将启动它)。当连接到一个service 之后,我们还可以service 提供的接口与它进行通讯。拿媒体播放器这个例子来说,我们还可以进行暂停、重播等操作。

Service使用步骤如下

       1>继承service类

       2>AndroidManifast.xml配置清单文件中<application>节点里对服务进行配置

              <service name=”.SMSService”/>

服务不能自己运行,需要通过Contex.startService()或Contex.bindService()启动服务

通过startService()方法启动的服务于调用者没有关系,即使调用者关闭了,服务仍然运行想停止服务要调用Context.stopService(),此时系统会调用onDestory(),使用此方法启动时,服务首次启动系统先调用服务的onCreate()–>onStart(),如果服务已经启动再次调用只会触发onStart()方法

使用bindService()启动的服务与调用者绑定,只要调用者关闭服务就终止,使用此方法启动时,服务首次启动系统先调用服务的onCreate()–>onBind(),如果服务已经启动再次调用不会再触发这2个方法,调用者退出时系统会调用服务的onUnbind()–>onDestory(),想主动解除绑定可使用Contex.unbindService(),系统依次调用onUnbind()–>onDestory();

Content Provider内容提供者 :

android平台提供了Content Provider使一个应用程序的指定数据集提供给其他应用程序。这些数据可以存储在文件系统中、在一个SQLite数据库、或以任何其他合理的方式,

其他应用可以通过ContentResolver类(见ContentProviderAccessApp例子)从该内容提供者中获取或存入数据.(相当于在应用外包了一层壳),

只有需要在多个应用程序间共享数据是才需要内容提供者。例如,通讯录数据被多个应用程序使用,且必须存储在一个内容提供者中

它的好处:统一数据访问方式。

android系统自带的内容提供者(顶级的表示数据库名,非顶级的都是表名)这些内容提供者在SDK文档的android.provider Java包中都有介绍。见:http://developer.android.com/reference/android/provider/package-summary.html

├────Browser

├────CallLog

├────Contacts

│                ├────Groups

│                ├────People

│                ├────Phones

│                └────Photos

├────Images

│                └────Thumbnails

├────MediaStore

│                ├────Albums

│                ├────Artists

│                ├────Audio

│                ├────Genres

│                └────Playlists

├────Settings

└────Video

 CallLog:地址和接收到的电话信息

 Contact.People.Phones:存储电话号码

 Setting.System:系统设置和偏好设置

使用Content Provider对外共享数据的步骤

1>继承ContentProvider类并根据需求重写以下方法:

复制代码
    public boolean onCreate();//处理初始化操作

       /**
        * 插入数据到内容提供者(允许其他应用向你的应用中插入数据时重写)
        * @param uri
        * @param initialValues 插入的数据
        * @return
        */
       public Uri insert(Uri uri, ContentValues initialValues);

       /**
        * 从内容提供者中删除数据(允许其他应用删除你应用的数据时重写)
        * @param uri
        * @param selection 条件语句
        * @param selectionArgs 参数
        * @return
        */
       public int delete(Uri uri, String selection, String[] selectionArgs);

       /**
        * 更新内容提供者已存在的数据(允许其他应用更新你应用的数据时重写)
        * @param uri
        * @param values 更新的数据
        * @param selection 条件语句
        * @param selectionArgs 参数
        * @return
        */
       public int update(Uri uri, ContentValues values, String selection,
                     String[] selectionArgs);

       /**
        * 返回数据给调用者(允许其他应用从你的应用中获取数据时重写)
        * @param uri
        * @param projection 列名
        * @param selection 条件语句
        * @param selectionArgs 参数
        * @param sortOrder 排序
        * @return
        */
       public Cursor query(Uri uri, String[] projection, String selection,
                     String[] selectionArgs, String sortOrder) ;         

       /**
        * 用于返回当前Uri所代表数据的MIME类型
        * 如果操作的数据为集合类型(多条数据),那么返回的类型字符串应该为vnd.android.cursor.dir/开头
        * 例如要得到所有person记录的Uri为content://com.bravestarr.provider.personprovider/person,
     *   那么返回的MIME类型字符串应该为"vnd.android.cursor.dir/person"
        * 如果操作的数据为单一数据,那么返回的类型字符串应该为vnd.android.cursor.item/开头
        * 例如要得到id为10的person记录的Uri为content://com.bravestarr.provider.personprovider/person/10,
     *   那么返回的MIME类型字符串应该为"vnd.android.cursor.item/person"
        * @param uri
        */
       public String getType(Uri uri)
复制代码

这些方法中的Uri参数,得到后需要进行解析然后做对应处理,Uri表示要操作的数据,包含两部分信息:

       1.需要操作的contentprovider

       2.对contentprovider中的什么数据进行操作,一个Uri格式:结构头://authorities(域名)/路径(要操作的数据,根据业务而定)

              content://com.bravestarr.provider.personprovider/person/10

说明:contentprovider的结构头已经由android规定为content://

authorities用于唯一标识这个contentprovider程序,外部调用者可以根据这个找到他

路径表示我们要操作的数据,路径的构建根据业务而定.路径格式如下:

       要操作person表行号为10的记录,可以这样构建/person/10

       要操作person表的所有记录,可以这样构建/person

2>在AndroidManifest.xml中使用<provider>对ContentProvider进行配置注册(内容提供者注册它自己就像网站注册域名),ContentProvider采用authoritie(原意授权,可理解为域名)作为唯一标识,方便其他应用能找到

复制代码
<application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <!-- authorities属性命名建议:公司名.provider.SomeProvider-->
        <provider android:name=".PersonProvider" android:authorities="com.bravestarr.provider.personprovider"/>
         ...
</application>
复制代码

关于四大基本组件的一个总结:

1>    4大组件的注册

4大基本组件都需要注册才能使用,每个Activity、service、Content Provider内容提供者都需要在AndroidManifest文件中进行配置AndroidManifest文件中未进行声明的activity、服务以及内容提供者将不为系统所见,从而也就不可用,而BroadcastReceive广播接收者的注册分静态注册(在AndroidManifest文件中进行配置)和通过代码动态创建并以调用Context.registerReceiver()的方式注册至系统。需要注意的是在AndroidManifest文件中进行配置的广播接收者会随系统的启动而一直处于活跃状态,只要接收到感兴趣的广播就会触发(即使程序未运行)

AndroidManifest文件中进行注册格式如下:

<activity>元素的name 属性指定了实现了这个activity 的Activity 的子类。icon 和label 属性指向了包含展示给用户的此activity 的图标和标签的资源文件。

<service> 元素用于声明服务

<receiver> 元素用于声明广播接收器

<provider> 元素用于声明内容提供者

2>   4大组件的激活

• 容提供者的激活:当接收到ContentResolver 发出的请求后,内容提供者被激活。而其它三种组件──activity、服务和广播接收器被一种叫做intent 的异步消息所激活

• Activity的激活通过传递一个Intent 对象至Context.startActivity()或Activity.startActivityForResult()以载入(或指定新工作给)一个activity。相应的activity 可以通过调用getIntent() 方法来查看激活它的intent。如果它期望它所启动的那个activity 返回一个结果,它会以调用startActivityForResult()来取代startActivity()。比如说,如果它启动了另外一个Activity 以使用户挑选一张照片,它也许想知道哪张照片被选中了。结果将会被封装在一个Intent 对象中,并传递给发出调用的activity 的onActivityResult() 方法。

• 服务的激活可以通过传递一个Intent 对象至Context.startService()或Context.bindService()前者Android 调用服务的onStart()方法并将Intent 对象传递给它,后者Android 调用服务的onBind()方法将这个Intent 对象传递给它

• 发送广播可以通过传递一个Intent 对象至给Context.sendBroadcast() 、

Context.sendOrderedBroadcast()或Context.sendStickyBroadcast()Android 会调用所有对此广播有兴趣的广播接收器的onReceive()方法,将intent 传递给它们

3>   四大组件的关闭

内容提供者仅在响应ContentResolver 提出请求的时候激活。而一个广播接收器仅在响应广播信息的时候激活。所以,没有必要去显式的关闭这些组件。

Activity关闭:可以通过调用它的finish()方法来关闭一个activity

服务关闭:对于通过startService()方法启动的服务要调用Context.stopService()方法关闭服务,使用bindService()方法启动的服务要调用Contex.unbindService ()方法关闭服务

二:四大组件的生命周期

     介绍生命周期之前,先提一下任务的概念

任务其实就是activity 的栈它由一个或多个Activity组成的共同完成一个完整的用户体验, 换句话说任务就是” 应用程序” (可以是一个也可以是多个,比如假设你想让用户看到某个地方的街道地图。而已经存在一个具有此功能的activity 了,那么你的activity 所需要做的工作就是把请求信息放到一个Intent 对象里面,并把它传递给startActivity()。于是地图浏览器就会显示那个地图。而当用户按下BACK 键的时候,你的activity 又会再一次的显示在屏幕上,此时任务是由2个应用程序中的相关activity组成的)栈底的是启动整个任务的Activity,栈顶的是当前运行的用户可以交互的Activity,当一个activity 启动另外一个的时候,新的activity 就被压入栈,并成为当前运行的activity。而前一个activity 仍保持在栈之中。当用户按下BACK 键的时候,当前activity 出栈,而前一个恢复为当前运行的activity。栈中保存的其实是对象,栈中的Activity 永远不会重排,只会压入或弹出,所以如果发生了诸如需要多个地图浏览器的情况,就会使得一个任务中出现多个同一Activity 子类的实例同时存在。

任务中的所有activity 是作为一个整体进行移动的。整个的任务(即activity 栈)可以移到前台,或退至后台。举个例子说,比如当前任务在栈中存有四个activity──三个在当前activity 之下。当用户按下HOME 键的时候,回到了应用程序加载器,然后选择了一个新的应用程序(也就是一个新任务)。则当前任务遁入后台,而新任务的根activity 显示出来。然后,过了一小会儿,用户再次回到了应用程序加载器而又选择了前一个应用程序(上一个任务)。于是那个任务,带着它栈中所有的四个activity,再一次的到了前台。当用户按下BACK 键的时候,屏幕不会显示出用户刚才离开的activity(上一个任务的根

activity)。取而代之,当前任务的栈中最上面的activity 被弹出,而同一任务中的上一个activity 显示了出来。

Activity栈:先进后出规则

                                                   

Android系统是一个多任务(Multi-Task)的操作系统,可以在用手机听音乐的同时,也执行其他多个程序。每多执行一个应用程序,就会多耗费一些系统内存,当同时执行的程序过多,或是关闭的程序没有正确释放掉内存,系统就会觉得越来越慢,甚至不稳定。

为了解决这个问题, Android 引入了一个新的机制– 生命周期(Life Cycle)。

Android 应用程序的生命周期是由Android 框架进行管理,而不是由应用程序直接控

制。通常,每一个应用程序(入口一般会是一个Activity 的onCreate 方法),都会产生

一个进程(Process)。当系统内存即将不足的时候,会依照优先级自动进行进程(process)的回收。不管是使用者或开发者, 都无法确定的应用程序何时会被回收。所以为了很好的防止数据丢失和其他问题,了解生命周期很重要。

Activity生命周期

                                                          

图3.1activity生命周期图

Activity整个生命周期的4种状态、7个重要方法和3个嵌套循环

1>   四种状态

  1.       活动(Active/Running)状态

当Activity运行在屏幕前台(处于当前任务活动栈的最上面),此时它获取了焦点能响应用户的操作,属于运行状态,同一个时刻只会有一个Activity 处于活动(Active)或运行

(Running)状态

  1.     暂停(Paused)状态

当Activity失去焦点但仍对用户可见(如在它之上有另一个透明的Activity或Toast、AlertDialog等弹出窗口时)它处于暂停状态。暂停的Activity仍然是存活状态(它保留着所有的状态和成员信息并保持和窗口管理器的连接),但是当系统内存极小时可以被系统杀掉

3.      停止(Stopped)状态

完全被另一个Activity遮挡时处于停止状态,它仍然保留着所有的状态和成员信息。只是对用户不可见,当其他地方需要内存时它往往被系统杀掉

4.      非活动(Dead)状态

Activity 尚未被启动、已经被手动终止,或已经被系统回收时处于非活动的状态,要手动终止Activity,可以在程序中调用”finish”方法。

如果是(按根据内存不足时的回收规则)被系统回收,可能是因为内存不足了

内存不足时,Dalvak 虚拟机会根据其内存回收规则来回收内存:

      1. 先回收与其他Activity 或Service/Intent Receiver 无关的进程(即优先回收独

立的Activity)因此建议,我们的一些(耗时)后台操作,最好是作成Service的形式

      2.不可见(处于Stopped状态的)Activity

      3.Service进程(除非真的没有内存可用时会被销毁)

      4.非活动的可见的(Paused状态的)Activity

      5.当前正在运行(Active/Running状态的)Activity

 

2>  7个重要方法,当Activity从一种状态进入另一状态时系统会自动调用下面相应的方

法来通知用户这种变化

当Activity第一次被实例化的时候系统会调用,

整个生命周期只调用1次这个方法

通常用于初始化设置: 1、为Activity设置所要使用的布局文件2、为按钮绑定监听器等静态的设置操作

onCreate(Bundle savedInstanceState);

 

当Activity可见未获得用户焦点不能交互时系统会调用

onStart();

 

当Activity已经停止然后重新被启动时系统会调用

onRestart();

当Activity可见且获得用户焦点能交互时系统会调用

      onResume();

当系统启动另外一个新的Activity时,在新Activity启动之前被系统调用保存现有的Activity中的持久数据、停止动画等,这个实现方法必须非常快。当系统而不是用户自己出于回收内存时,关闭了activity 之后。用户会期望当他再次回到这个activity 的时候,它仍保持着上次离开时的样子。此时用到了onSaveInstanceState(),方法onSaveInstanceState()用来保存Activity被杀之前的状态,在onPause()之前被触发,当系统为了节省内存销毁了Activity(用户本不想销毁)时就需要重写这个方法了,当此Activity再次被实例化时会通过onCreate(Bundle savedInstanceState)将已经保存的临时状态数据传入因为onSaveInstanceState()方法不总是被调用,触发条件为(按下HOME键,按下电源按键关闭屏幕,横竖屏切换情况下),你应该仅重写onSaveInstanceState()来记录activity的临时状态,而不是持久的数据。应该使用onPause()来存储持久数据。

      onPause();

当Activity被新的Activity完全覆盖不可见时被系统调用

      onStop();

当Activity(用户调用finish()或系统由于内存不足)被系统销毁杀掉时系统调用,(整个生命周期只调用1次)用来释放onCreate ()方法中创建的资源,如结束线程等

      onDestroy();

3>  3个嵌套循环

             1.Activity完整的生命周期:从第一次调用onCreate()开始直到调用onDestroy()结束

             2.Activity的可视生命周期:从调用onStart()到相应的调用onStop()

                    在这两个方法之间,可以保持显示Activity所需要的资源。如在onStart()中注册一个广播接收者监听影响你的UI的改变,在onStop() 中注销。

             3.Activity的前台生命周期:从调用onResume()到相应的调用onPause()。

      举例说明:

例1:有3个Acitivity,分别用One,Two(透明的),Three表示,One是应用启动时的主Activity

      启动第一个界面Activity One时,它的次序是

             onCreate (ONE) – onStart (ONE) – onResume(ONE)

      点”打开透明Activity”按钮时,这时走的次序是

             onPause(ONE) – onCreate(TWO) – onStart(TWO) – onResume(TWO)

      再点back回到第一个界面,Two会被杀这时走的次序是

             onPause(TWO) – onActivityResult(ONE) – onResume(ONE) – onStop(TWO) – onDestroy(TWO)

      点”打开全屏Activity”按钮时,这时走的次序是

             onPause(ONE) – onCreate(Three) – onStart(Three) – onResume(Three) – onStop(ONE)

      再点back回到第一个界面,Three会被杀这时走的次序是

             onPause(Three) – onActivityResult(ONE) – onRestart(ONE) – onStart(ONE)- onResume(ONE) – onStop(Three) – onDestroy(Three)

      再点back退出应用时,它的次序是

             onPause(ONE) – onStop(ONE) – onDestroy(ONE)

例2:横竖屏切换时候Activity的生命周期

他切换时具体的生命周期是怎么样的:

1、新建一个Activity,并把各个生命周期打印出来

2、运行Activity,得到如下信息

onCreate–>
onStart–>
onResume–>

3、按crtl+f12切换成横屏时

onSaveInstanceState–>
onPause–>
onStop–>
onDestroy–>
onCreate–>
onStart–>
onRestoreInstanceState–>
onResume–>

4、再按crtl+f12切换成竖屏时,发现打印了两次相同的log

onSaveInstanceState–>
onPause–>
onStop–>
onDestroy–>
onCreate–>
onStart–>
onRestoreInstanceState–>
onResume–>
onSaveInstanceState–>
onPause–>
onStop–>
onDestroy–>
onCreate–>
onStart–>
onRestoreInstanceState–>
onResume–>

5、修改AndroidManifest.xml,把该Activity添加android:configChanges=”orientation”,执行步骤3

onSaveInstanceState–>
onPause–>
onStop–>
onDestroy–>
onCreate–>
onStart–>
onRestoreInstanceState–>
onResume–>

6、再执行步骤4,发现不会再打印相同信息,但多打印了一行onConfigChanged

onSaveInstanceState–>
onPause–>
onStop–>
onDestroy–>
onCreate–>
onStart–>
onRestoreInstanceState–>
onResume–>
onConfigurationChanged–>

7、把步骤5的android:configChanges=”orientation” 改成 android:configChanges=”orientation|keyboardHidden”,执行步骤3,就只打印onConfigChanged

onConfigurationChanged–>

8、执行步骤4

onConfigurationChanged–>
onConfigurationChanged–>

总结:

1、不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次

2、设置Activity的android:configChanges=”orientation”时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次

3、设置Activity的android:configChanges=”orientation|keyboardHidden”时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法
总结一下整个Activity的生命周期

补充一点,当前Activity产生事件弹出Toast和AlertDialog的时候Activity的生命周期不会有改变

Activity运行时按下HOME键(跟被完全覆盖是一样的):onSaveInstanceState –> onPause –> onStop,再次进入激活状态时: onRestart –>onStart—>onResume

BroadcastReceive广播接收器生命周期

生命周期只有十秒左右,如果在 onReceive() 内做超过十秒内的事情,就会报ANR(Application No Response) 程序无响应的错误信息

它的生命周期为从回调onReceive()方法开始到该方法返回结果后结束

Service服务生命周期

                                                            

图3.2service生命周期图

Service完整的生命周期:从调用onCreate()开始直到调用onDestroy()结束

Service有两种使用方法:

1>以调用Context.startService()启动,而以调用Context.stopService()结束

2>以调用Context.bindService()方法建立,以调用Context.unbindService()关闭

service重要的生命周期方法

当用户调用startService ()或bindService()时,Service第一次被实例化的时候系统会调用,整个生命周期只调用1次这个方法,通常用于初始化设置。注意:多次调用startService()或bindService()方法不会多次触发onCreate()方法

void onCreate()

当用户调用stopService()或unbindService()来停止服务时被系统调用,(整个生命周期只调用1次)用来释放onCreate()方法中创建的资源

void onDestroy()

通过startService()方法启动的服务

      初始化结束后系统会调用该方法,用于处理传递给startService()的Intent对象。如音乐服务会打开Intent 来探明将要播放哪首音乐,并开始播放。注意:多次调用startService()方法会多次触发onStart()方法

void onStart(Intent intent)

通过bindService ()方法启动的服务

      初始化结束后系统会调用该方法,用来绑定传递给bindService 的Intent 的对象。注意:多次调用bindService()时,如果该服务已启动则不会再触发此方法

IBinder onBind(Intent intent)

用户调用unbindService()时系统调用此方法,Intent 对象同样传递给该方法

boolean onUnbind(Intent intent)

如果有新的客户端连接至该服务,只有当旧的调用onUnbind()后,新的才会调用该方法

void onRebind(Intent intent)

补充:onCreate(Bundle savedInstanceState)与onSaveInstanceState(Bundle savedInstanceState)配合使用,见如下代码,达到显示activity被系统杀死前的状态

复制代码
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (null != savedInstanceState) {
            String _userid = savedInstanceState.getString("StrUserId");
            String _uid = savedInstanceState.getString("StrUid");
            String _serverid = savedInstanceState.getString("StrServerId");
            String _servername = savedInstanceState.getString("StrServerName");
            int _rate = savedInstanceState.getInt("StrRate");
            //updateUserId(_userid);
            //updateUId(_uid);
            //updateServerId(_serverid);
            //updateUserServer(_servername);
            //updateRate(_rate);
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle savedInstanceState) {
        super.onSaveInstanceState(savedInstanceState);
        savedInstanceState.putString("StrUserId", getUserId());
        savedInstanceState.putString("StrUid", getUId());
        savedInstanceState.putString("StrServerId", getServerId());
        savedInstanceState.putString("StrServerName", getServerName());
        savedInstanceState.putInt("StrRate", getRate());
    }
复制代码

 

引发activity摧毁和重建的其他情形

除了系统处于内存不足的原因会摧毁activity之外, 某些系统设置的改变也会导致activity的摧毁和重建. 例如改变屏幕方向(见上例), 改变设备语言设定, 键盘弹出等.

 

Android应用程序窗口设计框架介绍

 

Android系统中,一个Activity对应一个应用程序窗口,任何一个Activity的启动都是由AMS服务和应用程序进程相互配合来完成的。AMS服务统一调度系统中所有进程的Activity启动,而每个Activity的启动过程则由其所属进程来完成。AMS服务通过realStartActivityLocked函数来通知应用程序进程启动某个Activity:

frameworksbaseservicesjavacomandroidserveram ActivityStack.java

  1. final boolean realStartActivityLocked(ActivityRecord r,
  2.         ProcessRecord app, boolean andResume, boolean checkConfig)
  3.         throws RemoteException {
  4.     …
  5.     //系统参数发送变化,通知Activity
  6.     if (checkConfig) {
  7.         ①Configuration config = mService.mWindowManager.updateOrientationFromAppTokens(mService.mConfiguration,
  8.                 r.mayFreezeScreenLocked(app) ? r.appToken : null);
  9.         mService.updateConfigurationLocked(config, r, falsefalse);
  10.     }
  11.     //将进程描述符设置到启动的Activity描述符中
  12.     r.app = app;
  13.     app.waitingToKill = null;
  14.     //将启动的Activity添加到进程启动的Activity列表中
  15.     int idx = app.activities.indexOf(r);
  16.     if (idx < 0) {
  17.         app.activities.add(r);
  18.     }
  19.     mService.updateLruProcessLocked(app, truetrue);
  20.     try {
  21.         …
  22.         //通知应用程序进程加载Activity
  23.         ②app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
  24.                 System.identityHashCode(r), r.info,
  25.                 new Configuration(mService.mConfiguration),
  26.                 r.compat, r.icicle, results, newIntents, !andResume,
  27.                 mService.isNextTransitionForward(), profileFile, profileFd,
  28.                 profileAutoStop);
  29.         …
  30.     } catch (RemoteException e) {
  31.         …
  32.     }
  33.     if (mMainStack) {
  34.         mService.startSetupActivityLocked();
  35.     }
  36.     return true;
  37. }

AMS通过realStartActivityLocked函数来调度应用程序进程启动一个Activity,参数r为即将启动的Activity在AMS服务中的描述符,参数app为Activity运行所在的应用程序进程在AMS服务中的描述符。函数通过IApplicationThread代理对象ApplicationThreadProxy通知应用程序进程启动r对应的Activity,应用程序进程完成Activity的加载等准备工作后,AMS最后启动该Activity。启动Activity的创建等工作是在应用程序进程中完成的,AMS是通过IApplicationThread接口和应用程序进程通信的。r.appToken
在AMS服务端的类型为Token,是IApplicationToken的Binder本地对象。

frameworksbasecorejavaandroidapp ActivityThread.java

  1. public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
  2.         ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
  3.         Bundle state, List<ResultInfo> pendingResults,
  4.         List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
  5.         String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler) {
  6.     //将AMS服务传过来的参数封装为ActivityClientRecord对象
  7.     ActivityClientRecord r = new ActivityClientRecord();
  8.     r.token = token;
  9.     r.ident = ident;
  10.     r.intent = intent;
  11.     r.activityInfo = info;
  12.     r.compatInfo = compatInfo;
  13.     r.state = state;
  14.     r.pendingResults = pendingResults;
  15.     r.pendingIntents = pendingNewIntents;
  16.     r.startsNotResumed = notResumed;
  17.     r.isForward = isForward;
  18.     r.profileFile = profileName;
  19.     r.profileFd = profileFd;
  20.     r.autoStopProfiler = autoStopProfiler;
  21.     updatePendingConfiguration(curConfig);
  22.     //使用异步消息方式实现Activity的启动
  23.     queueOrSendMessage(H.LAUNCH_ACTIVITY, r);
  24. }

参数token从AMS服务端经过Binder传输到应用程序进程后,变为IApplicationToken的Binder代理对象,类型为IApplicationToken.Proxy,这是因为AMS和应用程序运行在不同的进程中。

通过queueOrSendMessage函数将Binder跨进程调用转换为应用程序进程中的异步消息处理

frameworksbasecorejavaandroidapp ActivityThread.java

  1. private class H extends Handler {
  2.  public void handleMessage(Message msg) {
  3.     switch (msg.what) {
  4.             case LAUNCH_ACTIVITY: {
  5.                 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, “activityStart”);
  6.                 ActivityClientRecord r = (ActivityClientRecord)msg.obj;
  7.                 r.packageInfo = getPackageInfoNoCheck(r.activityInfo.applicationInfo, r.compatInfo);
  8.                 handleLaunchActivity(r, null);
  9.                 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
  10.             } break;
  11.         }
  12.     }
  13. }

LAUNCH_ACTIVITY消息在应用程序主线程消息循环中得到处理,应用程序通过handleLaunchActivity函数来启动Activity。到此AMS服务就完成了Activity的调度任务,将Activity的启动过程完全交给了应用程序进程来完成。

frameworksbasecorejavaandroidapp ActivityThread.java

  1. private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
  2.     //主线程空闲时会定时执行垃圾回收,主线程当前要完成启动Activity的任务,因此这里先暂停GC
  3.     unscheduleGcIdler();
  4.     if (r.profileFd != null) {
  5.         mProfiler.setProfiler(r.profileFile, r.profileFd);
  6.         mProfiler.startProfiling();
  7.         mProfiler.autoStopProfiler = r.autoStopProfiler;
  8.     }
  9.     // Make sure we are running with the most recent config.
  10.     ①handleConfigurationChanged(nullnull);
  11.     //创建Activity
  12.     ②Activity a = performLaunchActivity(r, customIntent);
  13.     if (a != null) {
  14.         r.createdConfig = new Configuration(mConfiguration);
  15.         Bundle oldState = r.state;
  16.         //启动Activity
  17.         ③handleResumeActivity(r.token, false, r.isForward);
  18.         …
  19.     }else{
  20.         …
  21.     }
  22. }

performLaunchActivity

应用程序进程通过performLaunchActivity函数将即将要启动的Activity加载到当前进程空间来,同时为启动Activity做准备。

frameworksbasecorejavaandroidapp ActivityThread.java

  1. private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
  2.     ActivityInfo aInfo = r.activityInfo;
  3.     if (r.packageInfo == null) {
  4.         //通过Activity所在的应用程序信息及该Activity对应的CompatibilityInfo信息从PMS服务中查询当前Activity的包信息
  5.         r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,Context.CONTEXT_INCLUDE_CODE);
  6.     }
  7.     //获取当前Activity的组件信息
  8.     ComponentName component = r.intent.getComponent();
  9.     if (component == null) {
  10.         component = r.intent.resolveActivity(mInitialApplication.getPackageManager());
  11.         r.intent.setComponent(component);
  12.     }
  13.     if (r.activityInfo.targetActivity != null) {
  14.         //packageName为启动Activity的包名,targetActivity为Activity的类名
  15.         component = new ComponentName(r.activityInfo.packageName,
  16.                 r.activityInfo.targetActivity);
  17.     }
  18.     //通过类反射方式加载即将启动的Activity
  19.     Activity activity = null;
  20.     try {
  21.         java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
  22.         ①activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
  23.         StrictMode.incrementExpectedActivityCount(activity.getClass());
  24.         r.intent.setExtrasClassLoader(cl);
  25.         if (r.state != null) {
  26.             r.state.setClassLoader(cl);
  27.         }
  28.     } catch (Exception e) {
  29.         …
  30.     }
  31.     try {
  32.         //通过单例模式为应用程序进程创建Application对象
  33.         ②Application app = r.packageInfo.makeApplication(false, mInstrumentation);
  34.         if (activity != null) {
  35.             //为当前Activity创建上下文对象ContextImpl
  36.             ContextImpl appContext = new ContextImpl();
  37.             //上下文初始化
  38.             ③appContext.init(r.packageInfo, r.token, this);
  39.             appContext.setOuterContext(activity);
  40.             CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
  41.             …
  42.             Configuration config = new Configuration(mCompatConfiguration);
  43.             //将当前启动的Activity和上下文ContextImpl、Application绑定
  44.             ④activity.attach(appContext, this, getInstrumentation(), r.token,
  45.                     r.ident, app, r.intent, r.activityInfo, title, r.parent,
  46.                     r.embeddedID, r.lastNonConfigurationInstances, config);
  47.             …
  48.             //调用Activity的OnCreate函数
  49.             ⑤mInstrumentation.callActivityOnCreate(activity, r.state);
  50.             …
  51.             //将Activity保存到ActivityClientRecord中,ActivityClientRecord为Activity在应用程序进程中的描述符
  52.             r.activity = activity;
  53.             …
  54.         }
  55.         r.paused = true;
  56.         //ActivityThread的成员变量mActivities保存了当前应用程序进程中的所有Activity的描述符
  57.         mActivities.put(r.token, r);
  58.     } catch (SuperNotCalledException e) {
  59.         …
  60.     }
  61.     return activity;
  62. }

在该函数中,首先通过PMS服务查找到即将启动的Activity的包名信息,然后通过类反射方式创建一个该Activity实例,同时为应用程序启动的每一个Activity创建一个LoadedApk实例对象,应用程序进程中创建的所有LoadedApk对象保存在ActivityThread的成员变量mPackages中。接着通过LoadedApk对象的makeApplication函数,使用单例模式创建Application对象,因此在android应用程序进程中有且只有一个Application实例。然后为当前启动的Activity创建一个ContextImpl上下文对象,并初始化该上下文,到此我们可以知道,启动一个Activity需要以下对象:

1)      XXActivity对象,需要启动的Activity;

2)      LoadedApk对象,每个启动的Activity都拥有属于自身的LoadedApk对象;

3)      ContextImpl对象,每个启动的Activity都拥有属于自身的ContextImpl对象;

4)      Application对象,应用程序进程中有且只有一个实例,和Activity是一对多的关系;

加载Activity类

  1. public Activity newActivity(ClassLoader cl, String className,
  2.         Intent intent)
  3.         throws InstantiationException, IllegalAccessException,
  4.         ClassNotFoundException {
  5.     return (Activity)cl.loadClass(className).newInstance();
  6. }

这里通过类反射的方式来加载要启动的Activity实例对象。

LoadedApk构造过程

首先介绍一下LoadedApk对象的构造过程:

frameworksbasecorejavaandroidapp ActivityThread.java

  1. public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo,
  2.         int flags) {
  3.     synchronized (mPackages) {
  4.         //通过Activity的包名从对应的成员变量中查找LoadedApk对象
  5.         WeakReference<LoadedApk> ref;
  6.         if ((flags&Context.CONTEXT_INCLUDE_CODE) != 0) {
  7.             ref = mPackages.get(packageName);
  8.         } else {
  9.             ref = mResourcePackages.get(packageName);
  10.         }
  11.         LoadedApk packageInfo = ref != null ? ref.get() : null;
  12.         if (packageInfo != null && (packageInfo.mResources == null
  13.                 || packageInfo.mResources.getAssets().isUpToDate())) {
  14.             …
  15.             return packageInfo;
  16.         }
  17.     }
  18.     //如果没有,则为当前Activity创建对应的LoadedApk对象
  19.     ApplicationInfo ai = null;
  20.     try {
  21.         //通过包名在PMS服务中查找应用程序信息
  22.         ai = getPackageManager().getApplicationInfo(packageName,
  23.                 PackageManager.GET_SHARED_LIBRARY_FILES, UserId.myUserId());
  24.     } catch (RemoteException e) {
  25.         // Ignore
  26.     }
  27.     //使用另一个重载函数创建LoadedApk对象
  28.     if (ai != null) {
  29.         return getPackageInfo(ai, compatInfo, flags);
  30.     }
  31.     return null;
  32. }

 

  1. public final LoadedApk getPackageInfo(ApplicationInfo ai, CompatibilityInfo compatInfo,
  2.         int flags) {
  3.     boolean includeCode = (flags&Context.CONTEXT_INCLUDE_CODE) != 0;
  4.     boolean securityViolation = includeCode && ai.uid != 0
  5.             && ai.uid != Process.SYSTEM_UID && (mBoundApplication != null
  6.                     ? !UserId.isSameApp(ai.uid, mBoundApplication.appInfo.uid)
  7.                     : true);
  8.     if ((flags&(Context.CONTEXT_INCLUDE_CODE|Context.CONTEXT_IGNORE_SECURITY))
  9.             == Context.CONTEXT_INCLUDE_CODE) {
  10.         …
  11.     }
  12.     return getPackageInfo(ai, compatInfo, null, securityViolation, includeCode);
  13. }

 

  1. private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
  2.         ClassLoader baseLoader, boolean securityViolation, boolean includeCode) {
  3.     //再次从对应的成员变量中查找LoadedApk实例
  4.     synchronized (mPackages) {
  5.         WeakReference<LoadedApk> ref;
  6.         if (includeCode) {
  7.             ref = mPackages.get(aInfo.packageName);
  8.         } else {
  9.             ref = mResourcePackages.get(aInfo.packageName);
  10.         }
  11.         LoadedApk packageInfo = ref != null ? ref.get() : null;
  12.         if (packageInfo == null || (packageInfo.mResources != null
  13.                 && !packageInfo.mResources.getAssets().isUpToDate())) {
  14.             …
  15.             //构造一个LoadedApk对象
  16.             packageInfo =new LoadedApk(this, aInfo, compatInfo, this, baseLoader,
  17.                         securityViolation, includeCode &&
  18.                         (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0);
  19.             //保存LoadedApk实例到ActivityThread的相应成员变量中
  20.             if (includeCode) {
  21.                 mPackages.put(aInfo.packageName,
  22.                         new WeakReference<LoadedApk>(packageInfo));
  23.             } else {
  24.                 mResourcePackages.put(aInfo.packageName,
  25.                         new WeakReference<LoadedApk>(packageInfo));
  26.             }
  27.         }
  28.         return packageInfo;
  29.     }
  30. }

 

frameworksbasecorejavaandroidappLoadedApk.java

  1. public LoadedApk(ActivityThread activityThread, ApplicationInfo aInfo,
  2.         CompatibilityInfo compatInfo,
  3.         ActivityThread mainThread, ClassLoader baseLoader,
  4.         boolean securityViolation, boolean includeCode) {
  5.     mActivityThread = activityThread;
  6.     mApplicationInfo = aInfo;
  7.     mPackageName = aInfo.packageName;
  8.     mAppDir = aInfo.sourceDir;
  9.     final int myUid = Process.myUid();
  10.     mResDir = aInfo.uid == myUid ? aInfo.sourceDir
  11.             : aInfo.publicSourceDir;
  12.     if (!UserId.isSameUser(aInfo.uid, myUid) && !Process.isIsolated()) {
  13.         aInfo.dataDir = PackageManager.getDataDirForUser(UserId.getUserId(myUid),
  14.                 mPackageName);
  15.     }
  16.     mSharedLibraries = aInfo.sharedLibraryFiles;
  17.     mDataDir = aInfo.dataDir;
  18.     mDataDirFile = mDataDir != null ? new File(mDataDir) : null;
  19.     mLibDir = aInfo.nativeLibraryDir;
  20.     mBaseClassLoader = baseLoader;
  21.     mSecurityViolation = securityViolation;
  22.     mIncludeCode = includeCode;
  23.     mCompatibilityInfo.set(compatInfo);
  24.     if (mAppDir == null) {
  25.         //为应用程序进程创建一个ContextImpl上下文
  26.         if (ActivityThread.mSystemContext == null) {
  27.             ActivityThread.mSystemContext =
  28.                 ContextImpl.createSystemContext(mainThread);
  29.             ActivityThread.mSystemContext.getResources().updateConfiguration(
  30.                      mainThread.getConfiguration(),
  31.                      mainThread.getDisplayMetricsLocked(compatInfo, false),
  32.                      compatInfo);
  33.         }
  34.         mClassLoader = ActivityThread.mSystemContext.getClassLoader();
  35.         mResources = ActivityThread.mSystemContext.getResources();
  36.     }
  37. }

从以上LoadedApk的构造函数可以看出,LoadedApk类记录了Activity运行所在的ActivityThread、Activity所在的应用程序信息、Activity的包名、Activity的资源路径、Activity的库路径、Activity的数据存储路径、类加载器和应用程序所使用的资源等信息。

Application构造过程

当Activity为应用程序进程启动的第一个Activity,因此需要构造一个Application对象

frameworksbasecorejavaandroidappLoadedApk.java

  1. public Application makeApplication(boolean forceDefaultAppClass,
  2.         Instrumentation instrumentation) {
  3.     //在应用程序进程空间以单例模式创建Application对象
  4.     if (mApplication != null) {
  5.         return mApplication;
  6.     }
  7.     Application app = null;
  8.     //得到应用程序的Application类名
  9.     String appClass = mApplicationInfo.className;
  10.     //如果应用程序没用重写Application,则使用Android默认的Application类
  11.     if (forceDefaultAppClass || (appClass == null)) {
  12.         appClass = “android.app.Application”;
  13.     }
  14.     try {
  15.         java.lang.ClassLoader cl = getClassLoader();
  16.         //为Application实例创建一个上下文对象ContextImpl
  17.         ①ContextImpl appContext = new ContextImpl();
  18.         //初始化上下文
  19.         ②appContext.init(thisnull, mActivityThread);
  20.         //创建Application实例对象
  21.         ③app = mActivityThread.mInstrumentation.newApplication(
  22.                 cl, appClass, appContext);
  23.         appContext.setOuterContext(app);
  24.     } catch (Exception e) {
  25.         …
  26.     }
  27.     mActivityThread.mAllApplications.add(app);
  28.     mApplication = app;
  29.     if (instrumentation != null) {
  30.         try {
  31.             //调用Application的OnCreate函数
  32.             ④instrumentation.callApplicationOnCreate(app);
  33.         } catch (Exception e) {
  34.             …
  35.         }
  36.     }
  37.     return app;
  38. }

在应用程序开发过程中,当我们重写了Application类后,应用程序加载运行的是我们定义的Application类,否则就加载运行默认的Application类。从Application对象的构造过程就可以解释为什么应用程序启动后首先执行的是Application的OnCreate函数。在实例化Application对象时,同样创建并初始化了一个ContextImpl上下文对象。

ContextImpl构造过程

前面我们介绍了,每一个Activity拥有一个上下文对象ContextImpl,每一个Application对象也拥有一个ContextImpl上下文对象,那么ContextImpl对象又是如何构造的呢?

frameworksbasecorejavaandroidapp ContextImpl.java

  1. ContextImpl() {
  2.     mOuterContext = this;
  3. }

ContextImpl的构造过程什么也没干,通过调用ContextImpl的init函数进行初始化

  1. final void init(LoadedApk packageInfo,IBinder activityToken, ActivityThread mainThread) {
  2.     init(packageInfo, activityToken, mainThread, nullnull);
  3. }

 

  1. final void init(LoadedApk packageInfo,IBinder activityToken, ActivityThread mainThread,
  2.             Resources container, String basePackageName) {
  3.     mPackageInfo = packageInfo;
  4.     mBasePackageName = basePackageName != null ? basePackageName : packageInfo.mPackageName;
  5.     mResources = mPackageInfo.getResources(mainThread);
  6.     if (mResources != null && container != null
  7.             && container.getCompatibilityInfo().applicationScale !=
  8.                     mResources.getCompatibilityInfo().applicationScale) {
  9.         mResources = mainThread.getTopLevelResources(
  10.                 mPackageInfo.getResDir(), container.getCompatibilityInfo());
  11.     }
  12.     mMainThread = mainThread;
  13.     mContentResolver = new ApplicationContentResolver(this, mainThread);
  14.     setActivityToken(activityToken);
  15. }

从ContextImpl的初始化函数中可以知道,ContextImpl记录了应用程序的包名信息、应用程序的资源信息、应用程序的主线程、ContentResolver及Activity对应的IApplicationToken.Proxy,当然对应Application对象所拥有的ContextImpl上下文就没有对应的Token了。通过前面的分析我们可以知道各个对象之间的关系:

对象Attach过程

Activity所需要的对象都创建好了,就需要将Activity和Application对象、ContextImpl对象绑定在一起。

frameworksbasecorejavaandroidapp Activity.java

  1. final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token,
  2.         Application application, Intent intent, ActivityInfo info, CharSequence title,
  3.         Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances,
  4.         Configuration config) {
  5.     attach(context, aThread, instr, token, 0, application, intent, info, title, parent, id,
  6.         lastNonConfigurationInstances, config);
  7. }

context:Activity的上下文对象,就是前面创建的ContextImpl对象;

aThread:Activity运行所在的主线程描述符ActivityThread;

instr:用于监控Activity运行状态的Instrumentation对象;

token:用于和AMS服务通信的IApplicationToken.Proxy代理对象;

application:Activity运行所在进程的Application对象;

parent:启动当前Activity的Activity;

  1. final void attach(Context context, ActivityThread aThread,
  2.         Instrumentation instr, IBinder token, int ident,
  3.         Application application, Intent intent, ActivityInfo info,
  4.         CharSequence title, Activity parent, String id,
  5.         NonConfigurationInstances lastNonConfigurationInstances,
  6.         Configuration config) {
  7.     //将上下文对象ContextImpl保存到Activity的成员变量中
  8.     attachBaseContext(context);
  9.     //每个Activity都拥有一个FragmentManager,这里就是将当前Activity设置到FragmentManager中管理
  10.     mFragments.attachActivity(this);
  11.     //创建窗口对象
  12.     ①mWindow = PolicyManager.makeNewWindow(this);
  13.     mWindow.setCallback(this);
  14.     mWindow.getLayoutInflater().setPrivateFactory(this);
  15.     if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
  16.         mWindow.setSoftInputMode(info.softInputMode);
  17.     }
  18.     if (info.uiOptions != 0) {
  19.         mWindow.setUiOptions(info.uiOptions);
  20.     }
  21.     //记录应用程序的UI线程
  22.     mUiThread = Thread.currentThread();
  23.     //记录应用程序的ActivityThread对象
  24.     mMainThread = aThread;
  25.     mInstrumentation = instr;
  26.     mToken = token;
  27.     mIdent = ident;
  28.     mApplication = application;
  29.     mIntent = intent;
  30.     mComponent = intent.getComponent();
  31.     mActivityInfo = info;
  32.     mTitle = title;
  33.     mParent = parent;
  34.     mEmbeddedID = id;
  35.     mLastNonConfigurationInstances = lastNonConfigurationInstances;
  36.     //为Activity所在的窗口创建窗口管理器
  37.     ②mWindow.setWindowManager(null, mToken, mComponent.flattenToString(),
  38.             (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
  39.     if (mParent != null) {
  40.         mWindow.setContainer(mParent.getWindow());
  41.     }
  42.     mWindowManager = mWindow.getWindowManager();
  43.     mCurrentConfig = config;
  44. }

在该attach函数中主要做了以下几件事:

1)        将Activity设置到FragmentManager中;

2)        根据参数初始化Activity的成员变量;

3)        为Activity创建窗口Window对象;

4)        为Window创建窗口管理器;

到此为止应用程序进程为启动的Activity对象创建了以下不同的实例对象,它们之间的关系如下:

应用程序窗口创建过程

frameworksbasecorejavacomandroidinternalpolicy PolicyManager.java

  1. public static Window makeNewWindow(Context context) {
  2.     return sPolicy.makeNewWindow(context);
  3. }

通过Policy类的makeNewWindow函数来创建一个应用程序窗口

  1. private static final String POLICY_IMPL_CLASS_NAME =
  2.         “com.android.internal.policy.impl.Policy”;
  3. private static final IPolicy sPolicy;
  4. static {
  5.     try {
  6.         Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
  7.         sPolicy = (IPolicy)policyClass.newInstance();
  8.     } catch (ClassNotFoundException ex) {
  9.         …
  10.     }
  11. }

frameworksbasepolicysrccomandroidinternalpolicyimpl Policy.java

  1. public Window makeNewWindow(Context context) {
  2.     return new PhoneWindow(context);
  3. }

应用程序窗口的创建过程其实就是构造一个PhoneWindow对象。PhoneWindow类是通过静态方式加载到应用程序进程空间的。

  1. private static final String[] preload_classes = {
  2.     “com.android.internal.policy.impl.PhoneLayoutInflater”,
  3.     “com.android.internal.policy.impl.PhoneWindow”,
  4.     “com.android.internal.policy.impl.PhoneWindow$1”,
  5.     “com.android.internal.policy.impl.PhoneWindow$ContextMenuCallback”,
  6.     “com.android.internal.policy.impl.PhoneWindow$DecorView”,
  7.     “com.android.internal.policy.impl.PhoneWindow$PanelFeatureState”,
  8.     “com.android.internal.policy.impl.PhoneWindow$PanelFeatureState$SavedState”,
  9. };
  1. static {
  2.     for (String s : preload_classes) {
  3.         try {
  4.             Class.forName(s);
  5.         } catch (ClassNotFoundException ex) {
  6.             Log.e(TAG, “Could not preload class for phone policy: “ + s);
  7.         }
  8.     }
  9. }

PhoneWindow的构造过程

  1. public PhoneWindow(Context context) {
  2.     super(context);
  3.     mAlternativePanelStyle=getContext().getResources().getBoolean(com.android.internal.R.bool.config_alternativePanelStyle);
  4.     mLayoutInflater = LayoutInflater.from(context);
  5. }

构造过程比较简单,只是得到布局加载服务对象。

窗口管理器创建过程

通过前面的分析我们可以知道,在Activity启动过程中,会为Activity创建一个窗口对象PhoneWindow,应用程序有了窗口那就需要有一个窗口管理器来管理这些窗口,因此在Activity启动过程中还会创建一个WindowManager对象。

frameworksbasecorejavaandroidview Window.java

  1. public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
  2.         boolean hardwareAccelerated) {
  3.     mAppToken = appToken;// IApplicationToken.Proxy代理对象
  4.     mAppName = appName;
  5.     //得到WindowManagerImpl实例,
  6.     if (wm == null) {
  7.         wm = WindowManagerImpl.getDefault();
  8.     }
  9.     //为每个启动的Activity创建一个轻量级的窗口管理器LocalWindowManager
  10.     mWindowManager = new LocalWindowManager(wm, hardwareAccelerated);
  11. }

WindowManagerImpl为重量级的窗口管理器,应用程序进程中有且只有一个WindowManagerImpl实例,它管理了应用程序进程中创建的所有PhoneWindow窗口。Activity并没有直接引用WindowManagerImpl实例,Android系统为每一个启动的Activity创建了一个轻量级的窗口管理器LocalWindowManager,每个Activity通过LocalWindowManager来访问WindowManagerImpl,它们三者之间的关系如下图所示:

WindowManagerImpl以单例模式创建,应用程序进程中有且只有一个WindowManagerImpl实例

frameworksbasecorejavaandroidview WindowManagerImpl.java

  1. private final static WindowManagerImpl sWindowManager = new WindowManagerImpl();
  2. public static WindowManagerImpl getDefault() {
  3.     return sWindowManager;
  4. }

应用程序进程会为每一个Activity创建一个LocalWindowManager实例对象

frameworksbasecorejavaandroidview Window.java

  1. LocalWindowManager(WindowManager wm, boolean hardwareAccelerated) {
  2.     super(wm, getCompatInfo(mContext));
  3.     mHardwareAccelerated = hardwareAccelerated ||
  4.             SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
  5. }

frameworksbasecorejavaandroidview WindowManagerImpl.java

  1. CompatModeWrapper(WindowManager wm, CompatibilityInfoHolder ci) {
  2.     mWindowManager = wm instanceof CompatModeWrapper
  3.             ? ((CompatModeWrapper)wm).mWindowManager : (WindowManagerImpl)wm;
  4.     if (ci == null) {
  5.         mDefaultDisplay = mWindowManager.getDefaultDisplay();
  6.     } else {
  7.         mDefaultDisplay = Display.createCompatibleDisplay(
  8.                 mWindowManager.getDefaultDisplay().getDisplayId(), ci);
  9.     }
  10.     mCompatibilityInfo = ci;
  11. }

  1. public Display getDefaultDisplay() {
  2.     return new Display(Display.DEFAULT_DISPLAY, null);
  3. }

frameworksbasecorejavaandroidviewDisplay.java

  1. Display(int display, CompatibilityInfoHolder compatInfo) {
  2.     synchronized (sStaticInit) {
  3.         if (!sInitialized) {
  4.             nativeClassInit();
  5.             sInitialized = true;
  6.         }
  7.     }
  8.     mCompatibilityInfo = compatInfo != null ? compatInfo : new CompatibilityInfoHolder();
  9.     mDisplay = display;
  10.     init(display);
  11. }

构造Display对象时需要初始化该对象。

frameworksbasecorejniandroid_view_Display.cpp

  1. static void android_view_Display_init(
  2.         JNIEnv* env, jobject clazz, jint dpy)
  3. {
  4.     DisplayInfo info;
  5.     if (headless) {
  6.         // initialize dummy display with reasonable values
  7.         info.pixelFormatInfo.format = 1// RGB_8888
  8.         info.fps = 60;
  9.         info.density = 160;
  10.         info.xdpi = 160;
  11.         info.ydpi = 160;
  12.     } else {
  13.         status_t err = SurfaceComposerClient::getDisplayInfo(DisplayID(dpy), &info);
  14.         if (err < 0) {
  15.             jniThrowException(env, “java/lang/IllegalArgumentException”, NULL);
  16.             return;
  17.         }
  18.     }
  19.     env->SetIntField(clazz, offsets.pixelFormat,info.pixelFormatInfo.format);
  20.     env->SetFloatField(clazz, offsets.fps,      info.fps);
  21.     env->SetFloatField(clazz, offsets.density,  info.density);
  22.     env->SetFloatField(clazz, offsets.xdpi,     info.xdpi);
  23.     env->SetFloatField(clazz, offsets.ydpi,     info.ydpi);
  24. }

Display的初始化过程很简单,就是通过SurfaceComposerClient请求SurfaceFlinger得到显示屏的基本信息。

frameworksnativelibsgui SurfaceComposerClient.cpp

  1. status_t SurfaceComposerClient::getDisplayInfo(
  2.         DisplayID dpy, DisplayInfo* info)
  3. {
  4.     if (uint32_t(dpy)>=NUM_DISPLAY_MAX)
  5.         return BAD_VALUE;
  6.     volatile surface_flinger_cblk_t const * cblk = get_cblk();
  7.     volatile display_cblk_t const * dcblk = cblk->displays + dpy;
  8.     info->w              = dcblk->w;
  9.     info->h              = dcblk->h;
  10.     info->orientation      = dcblk->orientation;
  11.     info->xdpi           = dcblk->xdpi;
  12.     info->ydpi           = dcblk->ydpi;
  13.     info->fps            = dcblk->fps;
  14.     info->density        = dcblk->density;
  15.     return getPixelFormatInfo(dcblk->format, &(info->pixelFormatInfo));
  16. }

我们知道在SurfaceFlinger启动过程中,创建了一块匿名共享内存来保存显示屏的基本信息,这里就是通过访问这块匿名共享内存来读取显示屏信息。到此一个Activity所需要的窗口对象就创建完成了,在应用程序窗口的创建过程中一共创建了以下几个对象:

Activity视图对象的创建过程

在Activity的attach函数中完成应用程序窗口的创建后,通过Instrumentation回调Activity的OnCreate函数来为当前Activity加载布局文件,进一步创建视图对象。

frameworksbasecorejavaandroidappInstrumentation.java

  1. public void callActivityOnCreate(Activity activity, Bundle icicle) {
  2.     …
  3.     activity.performCreate(icicle);
  4.     …
  5. }

frameworksbasecorejavaandroidappActivity.java

  1. final void performCreate(Bundle icicle) {
  2.     onCreate(icicle);
  3.     mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(
  4.             com.android.internal.R.styleable.Window_windowNoDisplay, false);
  5.     mFragments.dispatchActivityCreated();
  6. }

我们知道在应用程序开发中,需要重写Activity的OnCreate函数:

Packagesappsxxxsrccomxxx xxxActivity.java

  1. public void onCreate(Bundle savedInstanceState) {
  2.     super.onCreate(savedInstanceState);
  3.     setContentView(R.layout.main_activity);
  4.     …
  5. }

在OnCreate函数中通过setContentView来设置Activity的布局文件,就是生成该Activity的所有视图对象。

frameworksbasecorejavaandroidappActivity.java

  1. public void setContentView(View view, ViewGroup.LayoutParams params) {
  2.     getWindow().setContentView(view, params);
  3.     //初始化动作条
  4.     initActionBar();
  5. }

getWindow()函数得到前面创建的窗口对象PhoneWindow,通过PhoneWindow来设置Activity的视图。

frameworksbasepolicysrccomandroidinternalpolicyimplPhoneWindow.java

  1. public void setContentView(int layoutResID) {
  2.     //如果窗口顶级视图对象为空,则创建窗口视图对象
  3.     if (mContentParent == null) {
  4.         installDecor();
  5.     } else {//否则只是移除该视图对象中的其他视图
  6.         mContentParent.removeAllViews();
  7.     }
  8.     //加载布局文件,并将布局文件中的所有视图对象添加到mContentParent容器中
  9.     mLayoutInflater.inflate(layoutResID, mContentParent);
  10.     final Callback cb = getCallback();
  11.     if (cb != null && !isDestroyed()) {
  12.         cb.onContentChanged();
  13.     }
  14. }

PhoneWindow的成员变量mContentParent的类型为ViewGroup,是窗口内容存放的地方

frameworksbasepolicysrccomandroidinternalpolicyimplPhoneWindow.java

  1. private void installDecor() {
  2.     if (mDecor == null) {
  3.         ①mDecor = generateDecor();
  4.         mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
  5.         mDecor.setIsRootNamespace(true);
  6.         if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
  7.             mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
  8.         }
  9.     }
  10.     if (mContentParent == null) {
  11.         ②mContentParent = generateLayout(mDecor);
  12.         mDecor.makeOptionalFitsSystemWindows();
  13.         //应用程序窗口标题栏
  14.         mTitleView = (TextView)findViewById(com.android.internal.R.id.title);
  15.         if (mTitleView != null) {
  16.             …
  17.         } else {
  18.             //应用程序窗口动作条
  19.             mActionBar = (ActionBarView) findViewById(com.android.internal.R.id.action_bar);
  20.             if (mActionBar != null) {
  21.                 …
  22.             }
  23.         }
  24.     }
  25. }

通过函数generateDecor()来创建一个DecorView对象

  1. protected DecorView generateDecor() {
  2.     return new DecorView(getContext(), –1);
  3. }

接着通过generateLayout(mDecor)来创建视图对象容器mContentParent

  1. protected ViewGroup generateLayout(DecorView decor) {
  2.     //通过读取属性配置文件设置窗口风格
  3.     if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBarOverlay, false)) {
  4.         requestFeature(FEATURE_ACTION_BAR_OVERLAY);
  5.     }
  6.     …
  7.     //通过读取属性配置文件设置窗口标志
  8.     if (a.getBoolean(com.android.internal.R.styleable.Window_windowFullscreen, false)) {
  9.     setFlags(FLAG_FULLSCREEN,FLAG_FULLSCREEN&(~getForcedWindowFlags()));
  10.     }
  11.     …
  12.     WindowManager.LayoutParams params = getAttributes();
  13.     …
  14.     mDecor.startChanging();
  15.     //根据窗口主题风格选择不同的布局文件layoutResource
  16.     …
  17.     //加载布局文件
  18.     ①View in = mLayoutInflater.inflate(layoutResource, null);
  19.     //添加到DecorView中
  20.     ②decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
  21.     //从窗口视图中找出窗口内容视图对象
  22.     ③ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
  23.     …
  24.     mDecor.finishChanging();
  25.     return contentParent;
  26. }

到此Activity的所有视图对象都已经创建完毕,DecorView是Activity的顶级视图,由窗口PhoneWindow对象持有,在DecorView视图对象中添加了一个ViewGroup容器组件contentParent,所有用户定义视图组件将被添加到该容器中。

handleResumeActivity

performLaunchActivity函数完成了两件事:

1)        Activity窗口对象的创建,通过attach函数来完成;

2)        Activity视图对象的创建,通过setContentView函数来完成;

这些准备工作完成后,就可以显示该Activity了,应用程序进程通过调用handleResumeActivity函数来启动Activity的显示过程。

frameworksbasecorejavaandroidapp ActivityThread.java

  1. final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {
  2.     unscheduleGcIdler();
  3.     ActivityClientRecord r;
  4.     try {
  5.         ①r = performResumeActivity(token, clearHide);
  6.     } catch (Exception e) {
  7.         …
  8.     }
  9.     if (r != null) {
  10.         final Activity a = r.activity;
  11.         …
  12.         if (r.window == null && !a.mFinished && willBeVisible) {
  13.             //获得为当前Activity创建的窗口PhoneWindow对象
  14.             r.window = r.activity.getWindow();
  15.             //获取为窗口创建的视图DecorView对象
  16.             View decor = r.window.getDecorView();
  17.             decor.setVisibility(View.INVISIBLE);
  18.             //在attach函数中就为当前Activity创建了WindowManager对象
  19.             ViewManager wm = a.getWindowManager();
  20.             //得到该视图对象的布局参数
  21.             ②WindowManager.LayoutParams l = r.window.getAttributes();
  22.             //将视图对象保存到Activity的成员变量mDecor中
  23.             a.mDecor = decor;
  24.             l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
  25.             if (r.intent.hasCategory(Intent.CATEGORY_HOME)) {
  26.                 l.idleScreenAvailable = true;
  27.             } else {
  28.                 l.idleScreenAvailable = false;
  29.             }
  30.             l.softInputMode |= forwardBit;
  31.             if (a.mVisibleFromClient) {
  32.                 a.mWindowAdded = true;
  33.                 //将创建的视图对象DecorView添加到Activity的窗口管理器中
  34.                 ③wm.addView(decor, l);
  35.             }
  36.         } else if (!willBeVisible) {
  37.             …
  38.         }
  39.         …
  40.         if (!r.onlyLocalRequest) {
  41.             r.nextIdle = mNewActivities;
  42.             mNewActivities = r;
  43.             Looper.myQueue().addIdleHandler(new Idler());
  44.         }
  45.         …
  46.     } else {
  47.         …
  48.     }
  49. }

我们知道,在前面的performLaunchActivity函数中完成Activity的创建后,会将当前当前创建的Activity在应用程序进程端的描述符ActivityClientRecord以键值对的形式保存到ActivityThread的成员变量mActivities中:mActivities.put(r.token, r),r.token就是Activity的身份证,即是IApplicationToken.Proxy代理对象,也用于与AMS通信。上面的函数首先通过performResumeActivity从mActivities变量中取出Activity的应用程序端描述符ActivityClientRecord,然后取出前面为Activity创建的视图对象DecorView和窗口管理器WindowManager,最后将视图对象添加到窗口管理器中。

我们知道Activity引用的其实是轻量级的窗口管理器LocalWindowManager

frameworksbasecorejavaandroidview Window.java

  1. public final void addView(View view, ViewGroup.LayoutParams params) {
  2.     WindowManager.LayoutParams wp = (WindowManager.LayoutParams)params;
  3.     CharSequence curTitle = wp.getTitle();
  4.     //应用程序窗口
  5.     if (wp.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
  6.         wp.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
  7.         if (wp.token == null) {
  8.             View decor = peekDecorView();
  9.             if (decor != null) {
  10.                 // LayoutParams 的token设置为W本地Binder对象
  11.                 wp.token = decor.getWindowToken();
  12.             }
  13.         }
  14.         if (curTitle == null || curTitle.length() == 0) {
  15.             //根据窗口类型设置不同的标题
  16.             …
  17.             if (mAppName != null) {
  18.                 title += “:” + mAppName;
  19.             }
  20.             wp.setTitle(title);
  21.         }
  22.     } else {//系统窗口
  23.         if (wp.token == null) {
  24.             wp.token = mContainer == null ? mAppToken : mContainer.mAppToken;
  25.         }
  26.         if ((curTitle == null || curTitle.length() == 0)
  27.                 && mAppName != null) {
  28.             wp.setTitle(mAppName);
  29.         }
  30.     }
  31.     if (wp.packageName == null) {
  32.         wp.packageName = mContext.getPackageName();
  33.     }
  34.     if (mHardwareAccelerated) {
  35.         wp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
  36.     }
  37.     super.addView(view, params);
  38. }

LocalWindowManager的addView函数对不同类型窗口的布局参数进行相应的设置,比如布局参数中的token设置,如果是应用程序窗口,则设置token为W本地Binder对象。如果不是应用程序窗口,同时当前窗口没有父窗口,则设置token为当前窗口的IApplicationToken.Proxy代理对象,否则设置为父窗口的IApplicationToken.Proxy代理对象。最后视图组件的添加工作交给其父类来完成。LocalWindowManager继承于CompatModeWrapper,是WindowManagerImpl的内部类。

frameworksbasecorejavaandroidviewWindowManagerImpl.java

  1. public void addView(View view, android.view.ViewGroup.LayoutParams params) {
  2.     mWindowManager.addView(view, params, mCompatibilityInfo);
  3. }

前面我们介绍了,每一个Activity拥有一个轻量级窗口管理器,通过轻量级窗口管理器LocalWindowManager来访问重量级窗口管理器WindowManagerImpl,因此视图组件的添加过程又转交给了WindowManagerImpl来实现。

  1. public void addView(View view, ViewGroup.LayoutParams params, CompatibilityInfoHolder cih) {
  2.     addView(view, params, cih, false);
  3. }

该函数又调用WindowManagerImpl的另一个重载函数来添加视图组件

  1. private void addView(View view, ViewGroup.LayoutParams params,
  2.         CompatibilityInfoHolder cih, boolean nest) {
  3.     …
  4.     final WindowManager.LayoutParams wparams= (WindowManager.LayoutParams)params;
  5.     ViewRootImpl root;
  6.     View panelParentView = null;
  7.     synchronized (this) {
  8.         …
  9.         //从mViews中查找当前添加的View
  10.         int index = findViewLocked(view, false);
  11.         //如果已经存在,直接返回
  12.         if (index >= 0) {
  13.             …
  14.             return;
  15.         }
  16.         //尚未添加当前View
  17.         if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
  18.                 wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
  19.             final int count = mViews != null ? mViews.length : 0;
  20.             for (int i=0; i<count; i++) {
  21.                 if (mRoots[i].mWindow.asBinder() == wparams.token) {
  22.                     panelParentView = mViews[i];
  23.                 }
  24.             }
  25.         }
  26.         //为Activity创建一个ViewRootImpl对象
  27.         ①root = new ViewRootImpl(view.getContext());
  28.         …
  29.         //设置视图组件的布局参数
  30.         view.setLayoutParams(wparams);
  31.         if (mViews == null) {
  32.             index = 1;
  33.             mViews = new View[1];
  34.             mRoots = new ViewRootImpl[1];
  35.             mParams = new WindowManager.LayoutParams[1];
  36.         } else {
  37.             //动态增加mViews数组长度
  38.             index = mViews.length + 1;
  39.             Object[] old = mViews;
  40.             mViews = new View[index];
  41.             System.arraycopy(old, 0, mViews, 0, index-1);
  42.             //动态增加mRoots数组长度
  43.             old = mRoots;
  44.             mRoots = new ViewRootImpl[index];
  45.             System.arraycopy(old, 0, mRoots, 0, index-1);
  46.             //动态增加mParams数组长度
  47.             old = mParams;
  48.             mParams = new WindowManager.LayoutParams[index];
  49.             System.arraycopy(old, 0, mParams, 0, index-1);
  50.         }
  51.         index–;
  52.         ②mViews[index] = view;
  53.         mRoots[index] = root;
  54.         mParams[index] = wparams;
  55.     }
  56.     try {
  57.         ③root.setView(view, wparams, panelParentView);
  58.     } catch (RuntimeException e) {
  59.         …
  60.     }
  61. }

到此我们知道,当应用程序向窗口管理器中添加一个视图对象时,首先会为该视图对象创建一个ViewRootImpl对象,并且将视图对象、ViewRootImpl对象、视图布局参数分别保存到窗口管理器WindowManagerImpl得mViews、mRoots、mParams数组中,如下图所示:

最后通过ViewRootImpl对象来完成视图的显示过程。

ViewRootImpl构造过程

frameworksbasecorejavaandroidviewViewRootImpl.java

  1. public ViewRootImpl(Context context) {
  2.     …
  3.     ①getWindowSession(context.getMainLooper());
  4.     mThread = Thread.currentThread();
  5.     mLocation = new WindowLeaked(null);
  6.     mLocation.fillInStackTrace();
  7.     mWidth = –1;
  8.     mHeight = –1;
  9.     mDirty = new Rect();
  10.     mTempRect = new Rect();
  11.     mVisRect = new Rect();
  12.     mWinFrame = new Rect();
  13.     ②mWindow = new W(this);
  14.     mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
  15.     mInputMethodCallback = new InputMethodCallback(this);
  16.     mViewVisibility = View.GONE;
  17.     mTransparentRegion = new Region();
  18.     mPreviousTransparentRegion = new Region();
  19.     mFirst = true// true for the first time the view is added
  20.     mAdded = false;
  21.     mAccessibilityManager = AccessibilityManager.getInstance(context);
  22.     mAccessibilityInteractionConnectionManager =
  23.         new AccessibilityInteractionConnectionManager();
  24.     mAccessibilityManager.addAccessibilityStateChangeListener(
  25.             mAccessibilityInteractionConnectionManager);
  26.     ③mAttachInfo = new View.AttachInfo(sWindowSession, mWindow, this, mHandler, this);
  27.     mViewConfiguration = ViewConfiguration.get(context);
  28.     mDensity = context.getResources().getDisplayMetrics().densityDpi;
  29.     mFallbackEventHandler = PolicyManager.makeNewFallbackEventHandler(context);
  30.     mProfileRendering = Boolean.parseBoolean(
  31.             SystemProperties.get(PROPERTY_PROFILE_RENDERING, “false”));
  32.     ④mChoreographer = Choreographer.getInstance();
  33.     PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
  34.     mAttachInfo.mScreenOn = powerManager.isScreenOn();
  35.     loadSystemProperties();
  36. }

在ViewRootImpl的构造函数中初始化了一些成员变量,ViewRootImpl创建了以下几个主要对象:

1)        通过getWindowSession(context.getMainLooper())得到IWindowSession的代理对象,该对象用于和WMS通信。

2)        创建了一个W本地Binder对象,用于WMS通知应用程序进程。

3)        采用单例模式创建了一个Choreographer对象,用于统一调度窗口绘图。

4)        创建ViewRootHandler对象,用于处理当前视图消息。

5)        构造一个AttachInfo对象;

6)        创建Surface对象,用于绘制当前视图,当然该Surface对象的真正创建是由WMS来完成的,只不过是WMS传递给应用程序进程的。

  1. private final Surface mSurface = new Surface();
  2. final ViewRootHandler mHandler = new ViewRootHandler();

IWindowSession代理获取过程

frameworksbasecorejavaandroidviewViewRootImpl.java

  1. public static IWindowSession getWindowSession(Looper mainLooper) {
  2.     synchronized (mStaticInit) {
  3.         if (!mInitialized) {
  4.             try {
  5.                 //获取输入法管理器
  6.                 InputMethodManager imm = InputMethodManager.getInstance(mainLooper);
  7.                 //获取窗口管理器
  8.                 IWindowManager windowManager = Display.getWindowManager();
  9.                 //得到IWindowSession代理对象
  10.                 sWindowSession = windowManager.openSession(imm.getClient(), imm.getInputContext());
  11.                 float animatorScale = windowManager.getAnimationScale(2);
  12.                 ValueAnimator.setDurationScale(animatorScale);
  13.                 mInitialized = true;
  14.             } catch (RemoteException e) {
  15.             }
  16.         }
  17.         return sWindowSession;
  18.     }
  19. }

以上函数通过WMS的openSession函数创建应用程序与WMS之间的连接通道,即获取IWindowSession代理对象,并将该代理对象保存到ViewRootImpl的静态成员变量sWindowSession中

  1. static IWindowSession sWindowSession;

因此在应用程序进程中有且只有一个IWindowSession代理对象。

frameworksbaseservicesjavacomandroidserverwmWindowManagerService.java

  1. public IWindowSession openSession(IInputMethodClient client,
  2.         IInputContext inputContext) {
  3.     if (client == nullthrow new IllegalArgumentException(“null client”);
  4.     if (inputContext == nullthrow new IllegalArgumentException(“null inputContext”);
  5.     Session session = new Session(this, client, inputContext);
  6.     return session;
  7. }

在WMS服务端构造了一个Session实例对象。

AttachInfo构造过程

frameworksbasecorejavaandroidview View.java

  1. AttachInfo(IWindowSession session, IWindow window,
  2.         ViewRootImpl viewRootImpl, Handler handler, Callbacks effectPlayer) {
  3.     mSession = session;//IWindowSession代理对象,用于与WMS通信
  4.     mWindow = window;//W对象
  5.     mWindowToken = window.asBinder();//W本地Binder对象
  6.     mViewRootImpl = viewRootImpl;//ViewRootImpl实例
  7.     mHandler = handler;//ViewRootHandler对象
  8.     mRootCallbacks = effectPlayer;//ViewRootImpl实例
  9. }

 

创建Choreographer对象

Android Project Butter分析中介绍了Android4.1引入VSYNC、Triple Buffer和Choreographer来改善Android先天存在的UI流畅性差问题,有关Choreographer的实现过程请参看Android系统Choreographer机制实现过程

视图View添加过程

窗口管理器WindowManagerImpl为当前添加的窗口创建好各种对象后,调用ViewRootImpl的setView函数向WMS服务添加一个窗口对象。

  1. public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
  2.     synchronized (this) {
  3.         if (mView == null) {
  4.             //将DecorView保存到ViewRootImpl的成员变量mView中
  5.             mView = view;
  6.             mFallbackEventHandler.setView(view);
  7.             mWindowAttributes.copyFrom(attrs);
  8.             attrs = mWindowAttributes;
  9.             mClientWindowLayoutFlags = attrs.flags;
  10.             setAccessibilityFocus(nullnull);
  11.             //DecorView实现了RootViewSurfaceTaker接口
  12.             if (view instanceof RootViewSurfaceTaker) {
  13.                 mSurfaceHolderCallback =
  14.                         ((RootViewSurfaceTaker)view).willYouTakeTheSurface();
  15.                 if (mSurfaceHolderCallback != null) {
  16.                     mSurfaceHolder = new TakenSurfaceHolder();
  17.                     mSurfaceHolder.setFormat(PixelFormat.UNKNOWN);
  18.                 }
  19.             }
  20.             …
  21.             //同时将DecorView保存到mAttachInfo中
  22.             mAttachInfo.mRootView = view;
  23.             mAttachInfo.mScalingRequired = mTranslator != null;
  24.             mAttachInfo.mApplicationScale = mTranslator == null ? 1.0f : mTranslator.applicationScale;
  25.             if (panelParentView != null) {
  26.                 mAttachInfo.mPanelParentWindowToken
  27.                         = panelParentView.getApplicationWindowToken();
  28.             }
  29.             …
  30.             //在添加窗口前进行UI布局
  31.             ①requestLayout();
  32.             if ((mWindowAttributes.inputFeatures& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
  33.                 mInputChannel = new InputChannel();
  34.             }
  35.             try {
  36.                 mOrigWindowType = mWindowAttributes.type;
  37.                 mAttachInfo.mRecomputeGlobalAttributes = true;
  38.                 collectViewAttributes();
  39.                 //将窗口添加到WMS服务中,mWindow为W本地Binder对象,通过Binder传输到WMS服务端后,变为IWindow代理对象
  40.                 ②res = sWindowSession.add(mWindow, mSeq, mWindowAttributes,
  41.                         getHostVisibility(), mAttachInfo.mContentInsets,
  42.                         mInputChannel);
  43.             } catch (RemoteException e) {
  44.                 …
  45.             }
  46.             …
  47.             //建立窗口消息通道
  48.             if (view instanceof RootViewSurfaceTaker) {
  49.                 mInputQueueCallback =
  50.                     ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
  51.             }
  52.             if (mInputChannel != null) {
  53.                 if (mInputQueueCallback != null) {
  54.                     mInputQueue = new InputQueue(mInputChannel);
  55.                     mInputQueueCallback.onInputQueueCreated(mInputQueue);
  56.                 } else {
  57.                     mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,Looper.myLooper());
  58.                 }
  59.             }
  60.             …
  61.         }
  62.     }
  63. }

通过前面的分析可以知道,用户自定义的UI作为一个子View被添加到DecorView中,然后将顶级视图DecorView添加到应用程序进程的窗口管理器中,窗口管理器首先为当前添加的View创建一个ViewRootImpl对象、一个布局参数对象ViewGroup.LayoutParams,然后将这三个对象分别保存到当前应用程序进程的窗口管理器WindowManagerImpl中,最后通过ViewRootImpl对象将当前视图对象注册到WMS服务中。

ViewRootImpl的setView函数向WMS服务添加一个窗口对象过程:

1)         requestLayout()在应用程序进程中进行窗口UI布局;

2)         WindowSession.add()向WMS服务注册一个窗口对象;

3)         注册应用程序进程端的消息接收通道;

窗口UI布局过程

frameworksbasecorejavaandroidviewViewRootImpl.java

  1. public void requestLayout() {
  2.     //检查当前线程是否是UI线程
  3.     checkThread();
  4.     //标识当前正在请求UI布局
  5.     mLayoutRequested = true;
  6.     scheduleTraversals();
  7. }

窗口布局过程必须在UI线程中进行,因此该函数首先检查调用requestLayout()函数的线程是否为创建ViewRootImpl对象的线程。然后调用scheduleTraversals()函数启动Choreographer的Callback遍历过程。

  1. void scheduleTraversals() {
  2.     if (!mTraversalScheduled) {
  3.         mTraversalScheduled = true;
  4.         //暂停UI线程消息队列对同步消息的处理
  5.         mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
  6.         //向Choreographer注册一个类型为CALLBACK_TRAVERSAL的回调,用于处理UI绘制
  7.         mChoreographer.postCallback(
  8.                 Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
  9.         //向Choreographer注册一个类型为CALLBACK_INPUT的回调,用于处理输入事件
  10.         scheduleConsumeBatchedInput();
  11.     }
  12. }

关于Choreographer的postCallback()用法在前面进行了详细的介绍,当Vsync事件到来时,mTraversalRunnable对象的run()函数将被调用。

frameworksbasecorejavaandroidviewViewRootImpl.java

  1. final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
  2. final class TraversalRunnable implements Runnable {
  3.         @Override
  4.         public void run() {
  5.             doTraversal();
  6.         }
  7. }

mTraversalRunnable对象的类型为TraversalRunnable,该类实现了Runnable接口,在其run()函数中调用了doTraversal()函数来完成窗口布局。

  1. void doTraversal() {
  2.     if (mTraversalScheduled) {
  3.         mTraversalScheduled = false;
  4.         mHandler.getLooper().removeSyncBarrier(mTraversalBarrier);
  5.         if (mProfile) {
  6.             Debug.startMethodTracing(“ViewAncestor”);
  7.         }
  8.         Trace.traceBegin(Trace.TRACE_TAG_VIEW, “performTraversals”);
  9.         try {
  10.             performTraversals();
  11.         } finally {
  12.             Trace.traceEnd(Trace.TRACE_TAG_VIEW);
  13.         }
  14.         if (mProfile) {
  15.             Debug.stopMethodTracing();
  16.             mProfile = false;
  17.         }
  18.     }
  19. }

performTraversals函数相当复杂,其主要实现以下几个重要步骤:

1.执行窗口测量;

2.执行窗口注册;

3.执行窗口布局;

4.执行窗口绘图;

  1. private void performTraversals() {
  2.     // cache mView since it is used so much below…
  3.     final View host = mView;
  4.     if (host == null || !mAdded)
  5.         return;
  6.     mWillDrawSoon = true;
  7.     boolean windowSizeMayChange = false;
  8.     boolean newSurface = false;
  9.     boolean surfaceChanged = false;
  10.     WindowManager.LayoutParams lp = mWindowAttributes;
  11.     int desiredWindowWidth;
  12.     int desiredWindowHeight;
  13.     final View.AttachInfo attachInfo = mAttachInfo;
  14.     final int viewVisibility = getHostVisibility();
  15.     boolean viewVisibilityChanged = mViewVisibility != viewVisibility
  16.             || mNewSurfaceNeeded;
  17.     WindowManager.LayoutParams params = null;
  18.     if (mWindowAttributesChanged) {
  19.         mWindowAttributesChanged = false;
  20.         surfaceChanged = true;
  21.         params = lp;
  22.     }
  23.     …
  24.     /****************执行窗口测量******************/
  25.     boolean layoutRequested = mLayoutRequested && !mStopped;
  26.     if (layoutRequested) {
  27.         …
  28.         // Ask host how big it wants to be
  29.         windowSizeMayChange |= measureHierarchy(host, lp, res,
  30.                 desiredWindowWidth, desiredWindowHeight);
  31.     }
  32.     …
  33.     /****************向WMS服务添加窗口******************/
  34.     if (mFirst || windowShouldResize || insetsChanged ||
  35.             viewVisibilityChanged || params != null) {
  36.         …
  37.         try {
  38.             final int surfaceGenerationId = mSurface.getGenerationId();
  39.             relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
  40.             …
  41.         } catch (RemoteException e) {
  42.         }
  43.         …
  44.         if (!mStopped) {
  45.             boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
  46.                     (relayoutResult&WindowManagerImpl.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
  47.             if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
  48.                     || mHeight != host.getMeasuredHeight() || contentInsetsChanged) {
  49.                 …
  50.                  // Ask host how big it wants to be
  51.                 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
  52.                 …
  53.             }
  54.         }
  55.     }
  56.     /****************执行窗口布局******************/
  57.     final boolean didLayout = layoutRequested && !mStopped;
  58.     boolean triggerGlobalLayoutListener = didLayout
  59.             || attachInfo.mRecomputeGlobalAttributes;
  60.     if (didLayout) {
  61.         performLayout();
  62.         …
  63.     }
  64.     …
  65.     /****************查找窗口焦点******************/
  66.     boolean skipDraw = false;
  67.     if (mFirst) {
  68.         // handle first focus request
  69.         if (DEBUG_INPUT_RESIZE) Log.v(TAG, “First: mView.hasFocus()=”
  70.                 + mView.hasFocus());
  71.         if (mView != null) {
  72.             if (!mView.hasFocus()) {
  73.                 mView.requestFocus(View.FOCUS_FORWARD);
  74.                 mFocusedView = mRealFocusedView = mView.findFocus();
  75.                 if (DEBUG_INPUT_RESIZE) Log.v(TAG, “First: requested focused view=”
  76.                         + mFocusedView);
  77.             } else {
  78.                 mRealFocusedView = mView.findFocus();
  79.                 if (DEBUG_INPUT_RESIZE) Log.v(TAG, “First: existing focused view=”
  80.                         + mRealFocusedView);
  81.             }
  82.         }
  83.         if ((relayoutResult&WindowManagerImpl.RELAYOUT_RES_ANIMATING) != 0) {
  84.             // The first time we relayout the window, if the system is
  85.             // doing window animations, we want to hold of on any future
  86.             // draws until the animation is done.
  87.             mWindowsAnimating = true;
  88.         }
  89.     } else if (mWindowsAnimating) {
  90.         skipDraw = true;
  91.     }
  92.     /****************执行窗口绘制******************/
  93.     mFirst = false;
  94.     mWillDrawSoon = false;
  95.     mNewSurfaceNeeded = false;
  96.     mViewVisibility = viewVisibility;
  97.     …
  98.     boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw() ||
  99.             viewVisibility != View.VISIBLE;
  100.     if (!cancelDraw && !newSurface) {
  101.         if (!skipDraw || mReportNextDraw) {
  102.             if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
  103.                 for (int i = 0; i < mPendingTransitions.size(); ++i) {
  104.                     mPendingTransitions.get(i).startChangingAnimations();
  105.                 }
  106.                 mPendingTransitions.clear();
  107.             }
  108.             performDraw();
  109.         }
  110.     } else {
  111.         if (viewVisibility == View.VISIBLE) {
  112.             // Try again
  113.             scheduleTraversals();
  114.         } else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
  115.             for (int i = 0; i < mPendingTransitions.size(); ++i) {
  116.                 mPendingTransitions.get(i).endChangingAnimations();
  117.             }
  118.             mPendingTransitions.clear();
  119.         }
  120.     }
  121. }
performMeasure

frameworksbasecorejavaandroidviewViewRootImpl.java

  1. private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
  2.  Trace.traceBegin(Trace.TRACE_TAG_VIEW, “measure”);
  3.  try {
  4.   mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
  5.  } finally {
  6.   Trace.traceEnd(Trace.TRACE_TAG_VIEW);
  7.  }
  8. }
relayoutWindow

frameworksbasecorejavaandroidviewViewRootImpl.java

  1. private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
  2.         boolean insetsPending) throws RemoteException {
  3.     …
  4.     int relayoutResult = sWindowSession.relayout(
  5.             mWindow, mSeq, params,
  6.             (int) (mView.getMeasuredWidth() * appScale + 0.5f),
  7.             (int) (mView.getMeasuredHeight() * appScale + 0.5f),
  8.             viewVisibility, insetsPending ? WindowManagerImpl.RELAYOUT_INSETS_PENDING : 0,
  9.             mWinFrame, mPendingContentInsets, mPendingVisibleInsets,
  10.             mPendingConfiguration, mSurface);
  11.     …
  12.     return relayoutResult;
  13. }

这里通过前面获取的IWindowSession代理对象请求WMS服务执行窗口布局,mSurface是ViewRootImpl的成员变量

  1. private final Surface mSurface = new Surface();

frameworksbasecorejavaandroidview Surface.java

  1. public Surface() {
  2.     checkHeadless();
  3.     if (DEBUG_RELEASE) {
  4.         mCreationStack = new Exception();
  5.     }
  6.     mCanvas = new CompatibleCanvas();
  7. }

该Surface构造函数仅仅创建了一个CompatibleCanvas对象,并没有对该Surface进程native层的初始化,到此我们知道应用程序进程为每个窗口对象都创建了一个Surface对象。并且将该Surface通过跨进程方式传输给WMS服务进程,我们知道,在Android系统中,如果一个对象需要在不同进程间传输,必须实现Parcelable接口,Surface类正好实现了Parcelable接口。ViewRootImpl通过IWindowSession接口请求WMS的完整过程如下:

frameworksbasecorejavaandroidviewIWindowSession.java$ Proxy

  1. public int relayout(android.view.IWindow window, int seq,
  2.         android.view.WindowManager.LayoutParams attrs, int requestedWidth,
  3.         int requestedHeight, int viewVisibility, int flags,
  4.         android.graphics.Rect outFrame,
  5.         android.graphics.Rect outOverscanInsets,
  6.         android.graphics.Rect outContentInsets,
  7.         android.graphics.Rect outVisibleInsets,
  8.         android.content.res.Configuration outConfig,
  9.         android.view.Surface outSurface) throws android.os.RemoteException {
  10.     android.os.Parcel _data = android.os.Parcel.obtain();
  11.     android.os.Parcel _reply = android.os.Parcel.obtain();
  12.     int _result;
  13.     try {
  14.         _data.writeInterfaceToken(DESCRIPTOR);
  15.         _data.writeStrongBinder((((window != null)) ? (window.asBinder()): (null)));
  16.         _data.writeInt(seq);
  17.         if ((attrs != null)) {
  18.             _data.writeInt(1);
  19.             attrs.writeToParcel(_data, 0);
  20.         } else {
  21.             _data.writeInt(0);
  22.         }
  23.         _data.writeInt(requestedWidth);
  24.         _data.writeInt(requestedHeight);
  25.         _data.writeInt(viewVisibility);
  26.         _data.writeInt(flags);
  27.         mRemote.transact(Stub.TRANSACTION_relayout, _data, _reply, 0);
  28.         _reply.readException();
  29.         _result = _reply.readInt();
  30.         if ((0 != _reply.readInt())) {
  31.             outFrame.readFromParcel(_reply);
  32.         }
  33.         if ((0 != _reply.readInt())) {
  34.             outOverscanInsets.readFromParcel(_reply);
  35.         }
  36.         if ((0 != _reply.readInt())) {
  37.             outContentInsets.readFromParcel(_reply);
  38.         }
  39.         if ((0 != _reply.readInt())) {
  40.             outVisibleInsets.readFromParcel(_reply);
  41.         }
  42.         if ((0 != _reply.readInt())) {
  43.             outConfig.readFromParcel(_reply);
  44.         }
  45.         if ((0 != _reply.readInt())) {
  46.             outSurface.readFromParcel(_reply);
  47.         }
  48.     } finally {
  49.         _reply.recycle();
  50.         _data.recycle();
  51.     }
  52.     return _result;
  53. }

从该函数的实现可以看出,应用程序进程中创建的Surface对象并没有传递到WMS服务进程,只是读取WMS服务进程返回来的Surface。那么WMS服务进程是如何响应应用程序进程布局请求的呢?
frameworksbasecorejavaandroidviewIWindowSession.java$ Stub

  1. public boolean onTransact(int code, android.os.Parcel data,
  2.         android.os.Parcel reply, int flags)throws android.os.RemoteException {
  3.     switch (code) {
  4.     case TRANSACTION_relayout: {
  5.         data.enforceInterface(DESCRIPTOR);
  6.         android.view.IWindow _arg0;
  7.         _arg0 = android.view.IWindow.Stub.asInterface(data.readStrongBinder());
  8.         int _arg1;
  9.         _arg1 = data.readInt();
  10.         android.view.WindowManager.LayoutParams _arg2;
  11.         if ((0 != data.readInt())) {
  12.             _arg2 = android.view.WindowManager.LayoutParams.CREATOR
  13.                     .createFromParcel(data);
  14.         } else {
  15.             _arg2 = null;
  16.         }
  17.         int _arg3;
  18.         _arg3 = data.readInt();
  19.         int _arg4;
  20.         _arg4 = data.readInt();
  21.         int _arg5;
  22.         _arg5 = data.readInt();
  23.         int _arg6;
  24.         _arg6 = data.readInt();
  25.         android.graphics.Rect _arg7;
  26.         _arg7 = new android.graphics.Rect();
  27.         android.graphics.Rect _arg8;
  28.         _arg8 = new android.graphics.Rect();
  29.         android.graphics.Rect _arg9;
  30.         _arg9 = new android.graphics.Rect();
  31.         android.graphics.Rect _arg10;
  32.         _arg10 = new android.graphics.Rect();
  33.         android.content.res.Configuration _arg11;
  34.         _arg11 = new android.content.res.Configuration();
  35.         android.view.Surface _arg12;
  36.         _arg12 = new android.view.Surface();
  37.         int _result = this.relayout(_arg0, _arg1, _arg2, _arg3, _arg4,
  38.                 _arg5, _arg6, _arg7, _arg8, _arg9, _arg10, _arg11, _arg12);
  39.         reply.writeNoException();
  40.         reply.writeInt(_result);
  41.         if ((_arg7 != null)) {
  42.             reply.writeInt(1);
  43.             _arg7.writeToParcel(reply,
  44.                     android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
  45.         } else {
  46.             reply.writeInt(0);
  47.         }
  48.         if ((_arg8 != null)) {
  49.             reply.writeInt(1);
  50.             _arg8.writeToParcel(reply,
  51.                     android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
  52.         } else {
  53.             reply.writeInt(0);
  54.         }
  55.         if ((_arg9 != null)) {
  56.             reply.writeInt(1);
  57.             _arg9.writeToParcel(reply,
  58.                     android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
  59.         } else {
  60.             reply.writeInt(0);
  61.         }
  62.         if ((_arg10 != null)) {
  63.             reply.writeInt(1);
  64.             _arg10.writeToParcel(reply,
  65.                     android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
  66.         } else {
  67.             reply.writeInt(0);
  68.         }
  69.         if ((_arg11 != null)) {
  70.             reply.writeInt(1);
  71.             _arg11.writeToParcel(reply,
  72.                     android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
  73.         } else {
  74.             reply.writeInt(0);
  75.         }
  76.         if ((_arg12 != null)) {
  77.             reply.writeInt(1);
  78.             _arg12.writeToParcel(reply,
  79.                     android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
  80.         } else {
  81.             reply.writeInt(0);
  82.         }
  83.         return true;
  84.     }
  85.     }
  86. }

该函数可以看出,WMS服务在响应应用程序进程请求添加窗口时,首先在当前进程空间创建一个Surface对象,然后调用Session的relayout()函数进一步完成窗口添加过程,最后将WMS服务中创建的Surface返回给应用程序进程。

到目前为止,在应用程序进程和WMS服务进程分别创建了一个Surface对象,但是他们调用的都是Surface的无参构造函数,在该构造函数中并未真正初始化native层的Surface,那native层的Surface是在那里创建的呢?
frameworksbaseservicesjavacomandroidserverwm Session.java

  1. public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
  2.         int requestedWidth, int requestedHeight, int viewFlags,
  3.         int flags, Rect outFrame, Rect outContentInsets,
  4.         Rect outVisibleInsets, Configuration outConfig, Surface outSurface) {
  5.     int res = mService.relayoutWindow(this, window, seq, attrs,
  6.             requestedWidth, requestedHeight, viewFlags, flags,
  7.             outFrame, outContentInsets, outVisibleInsets,
  8.             outConfig, outSurface);
  9.     return res;
  10. }

frameworksbaseservicesjavacomandroidserverwm WindowManagerService.java

  1. public int relayoutWindow(Session session, IWindow client, int seq,
  2.         WindowManager.LayoutParams attrs, int requestedWidth,
  3.         int requestedHeight, int viewVisibility, int flags,
  4.         Rect outFrame, Rect outContentInsets,
  5.         Rect outVisibleInsets, Configuration outConfig, Surface outSurface) {
  6.     …
  7.     synchronized(mWindowMap) {
  8.         // TODO(cmautner): synchronize on mAnimator or win.mWinAnimator.
  9.         WindowState win = windowForClientLocked(session, client, false);
  10.         if (win == null) {
  11.             return 0;
  12.         }
  13.         …
  14.         if (viewVisibility == View.VISIBLE &&
  15.                 (win.mAppToken == null || !win.mAppToken.clientHidden)) {
  16.             …
  17.             try {
  18.                 if (!win.mHasSurface) {
  19.                     surfaceChanged = true;
  20.                 }
  21.                 //创建Surface
  22.                 Surface surface = winAnimator.createSurfaceLocked();
  23.                 if (surface != null) {
  24.                     outSurface.copyFrom(surface);
  25.                 } else {
  26.                     outSurface.release();
  27.                 }
  28.             } catch (Exception e) {
  29.                 …
  30.             }
  31.             …
  32.         }
  33.         …
  34.     }
  35.     …
  36. }

frameworksbaseservicesjavacomandroidserverwmWindowStateAnimator.java

  1. Surface createSurfaceLocked() {
  2.     if (mSurface == null) {
  3.         …
  4.         try {
  5.             …
  6.             if (DEBUG_SURFACE_TRACE) {
  7.                 mSurface = new SurfaceTrace(
  8.                         mSession.mSurfaceSession, mSession.mPid,
  9.                         attrs.getTitle().toString(),
  10.                         0, w, h, format, flags);
  11.             } else {
  12.                 mSurface = new Surface(
  13.                     mSession.mSurfaceSession, mSession.mPid,
  14.                     attrs.getTitle().toString(),
  15.                     0, w, h, format, flags);
  16.             }
  17.             mWin.mHasSurface = true;
  18.         } catch (Surface.OutOfResourcesException e) {
  19.             …
  20.         }
  21.         Surface.openTransaction();
  22.         …
  23.     }
  24.     return mSurface;
  25. }

Surface创建过程
frameworksbasecorejavaandroidviewSurface.java

  1. public Surface(SurfaceSession s,int pid, String name, int display, int w, int h, int format, int flags)
  2.     throws OutOfResourcesException {
  3.     checkHeadless();
  4.     if (DEBUG_RELEASE) {
  5.         mCreationStack = new Exception();
  6.     }
  7.     mCanvas = new CompatibleCanvas();
  8.     init(s,pid,name,display,w,h,format,flags);
  9.     mName = name;
  10. }

frameworksbasecorejni android_view_Surface.cpp

  1. static void Surface_init(
  2.         JNIEnv* env, jobject clazz,
  3.         jobject session,
  4.         jint, jstring jname, jint dpy, jint w, jint h, jint format, jint flags)
  5. {
  6.     if (session == NULL) {
  7.         doThrowNPE(env);
  8.         return;
  9.     }
  10.     SurfaceComposerClient* client =
  11.             (SurfaceComposerClient*)env->GetIntField(session, sso.client);
  12.     sp<SurfaceControl> surface;
  13.     if (jname == NULL) {
  14.         surface = client->createSurface(dpy, w, h, format, flags);
  15.     } else {
  16.         const jchar* str = env->GetStringCritical(jname, 0);
  17.         const String8 name(str, env->GetStringLength(jname));
  18.         env->ReleaseStringCritical(jname, str);
  19.         surface = client->createSurface(name, dpy, w, h, format, flags);
  20.     }
  21.     if (surface == 0) {
  22.         jniThrowException(env, OutOfResourcesException, NULL);
  23.         return;
  24.     }
  25.     setSurfaceControl(env, clazz, surface);
  26. }

到此才算真正创建了一个可用于绘图的Surface,从上面的分析我们可以看出,在WMS服务进程端,其实创建了两个Java层的Surface对象,第一个Surface使用了无参构造函数,仅仅构造一个Surface对象而已,而第二个Surface却使用了有参构造函数,参数指定了图象宽高等信息,这个Java层Surface对象还会在native层请求SurfaceFlinger创建一个真正能用于绘制图象的native层Surface。最后通过浅拷贝的方式将第二个Surface复制到第一个Surface中,最后通过writeToParcel方式写回到应用程序进程。

到目前为止,应用程序和WMS一共创建了3个Java层Surface对象,如上图所示,而真正能用于绘图的Surface只有3号,那么3号Surface与2号Surface之间是什么关系呢?outSurface.copyFrom(surface)
frameworksbasecorejni android_view_Surface.cpp

  1. static void Surface_copyFrom(JNIEnv* env, jobject clazz, jobject other)
  2. {
  3.     if (clazz == other)
  4.         return;
  5.     if (other == NULL) {
  6.         doThrowNPE(env);
  7.         return;
  8.     }
  9.     //得到当前Surface所引用的SurfaceControl对象
  10.     const sp<SurfaceControl>& surface = getSurfaceControl(env, clazz);
  11.     //得到源Surface所引用的SurfaceControl对象
  12.     const sp<SurfaceControl>& rhs = getSurfaceControl(env, other);
  13.     //如果它们引用的不是同一个SurfaceControl对象
  14.     if (!SurfaceControl::isSameSurface(surface, rhs)) {
  15.         setSurfaceControl(env, clazz, rhs);
  16.     }
  17. }

2号Surface引用到了3号Surface的SurfaceControl对象后,通过writeToParcel()函数写会到应用程序进程。
frameworksbasecorejni android_view_Surface.cpp

  1. static void Surface_writeToParcel(
  2.         JNIEnv* env, jobject clazz, jobject argParcel, jint flags)
  3. {
  4.     Parcel* parcel = (Parcel*)env->GetIntField(
  5.             argParcel, no.native_parcel);
  6.     if (parcel == NULL) {
  7.         doThrowNPE(env);
  8.         return;
  9.     }
  10.     const sp<SurfaceControl>& control(getSurfaceControl(env, clazz));
  11.     if (control != NULL) {
  12.         SurfaceControl::writeSurfaceToParcel(control, parcel);
  13.     } else {
  14.         sp<Surface> surface(Surface_getSurface(env, clazz));
  15.         if (surface != NULL) {
  16.             Surface::writeToParcel(surface, parcel);
  17.         } else {
  18.             SurfaceControl::writeSurfaceToParcel(NULL, parcel);
  19.         }
  20.     }
  21.     if (flags & PARCELABLE_WRITE_RETURN_VALUE) {
  22.         setSurfaceControl(env, clazz, NULL);
  23.         setSurface(env, clazz, NULL);
  24.     }
  25. }

由于2号Surface引用的SurfaceControl对象不为空,因此这里就将SurfaceControl对象写会给应用程序进程
frameworksnativelibsgui Surface.cpp

  1. status_t SurfaceControl::writeSurfaceToParcel(
  2.         const sp<SurfaceControl>& control, Parcel* parcel)
  3. {
  4.     sp<ISurface> sur;
  5.     uint32_t identity = 0;
  6.     if (SurfaceControl::isValid(control)) {
  7.         sur = control->mSurface;
  8.         identity = control->mIdentity;
  9.     }
  10.     parcel->writeStrongBinder(sur!=0 ? sur->asBinder() : NULL);
  11.     parcel->writeStrongBinder(NULL);  // NULL ISurfaceTexture in this case.
  12.     parcel->writeInt32(identity);
  13.     return NO_ERROR;
  14. }

写入Parcel包裹的对象顺序如下:

应用程序进程中的1号Surface通过readFromParcel()函数读取从WMS服务进程写回的Binder对象。
frameworksbasecorejni android_view_Surface.cpp

  1. static void Surface_readFromParcel(
  2.         JNIEnv* env, jobject clazz, jobject argParcel)
  3. {
  4.     Parcel* parcel = (Parcel*)env->GetIntField( argParcel, no.native_parcel);
  5.     if (parcel == NULL) {
  6.         doThrowNPE(env);
  7.         return;
  8.     }
  9.     sp<Surface> sur(Surface::readFromParcel(*parcel));
  10.     setSurface(env, clazz, sur);
  11. }

frameworksnativelibsgui Surface.cpp

  1. sp<Surface> Surface::readFromParcel(const Parcel& data) {
  2.     Mutex::Autolock _l(sCachedSurfacesLock);
  3.     sp<IBinder> binder(data.readStrongBinder());
  4.     sp<Surface> surface = sCachedSurfaces.valueFor(binder).promote();
  5.     if (surface == 0) {
  6.        surface = new Surface(data, binder);
  7.        sCachedSurfaces.add(binder, surface);
  8.     } else {
  9.         // The Surface was found in the cache, but we still should clear any
  10.         // remaining data from the parcel.
  11.         data.readStrongBinder();  // ISurfaceTexture
  12.         data.readInt32();         // identity
  13.     }
  14.     if (surface->mSurface == NULL && surface->getISurfaceTexture() == NULL) {
  15.         surface = 0;
  16.     }
  17.     cleanCachedSurfacesLocked();
  18.     return surface;
  19. }

应用程序进程中的1号Surface按相反顺序读取WMS服务端返回过来的Binder对象等数据,并构造一个native层的Surface对象。

  1. Surface::Surface(const Parcel& parcel, const sp<IBinder>& ref)
  2.     : SurfaceTextureClient()
  3. {
  4.     mSurface = interface_cast<ISurface>(ref);
  5.     sp<IBinder> st_binder(parcel.readStrongBinder());
  6.     sp<ISurfaceTexture> st;
  7.     if (st_binder != NULL) {
  8.         st = interface_cast<ISurfaceTexture>(st_binder);
  9.     } else if (mSurface != NULL) {
  10.         st = mSurface->getSurfaceTexture();
  11.     }
  12.     mIdentity   = parcel.readInt32();
  13.     init(st);
  14. }

每个Activity可以有一个或多个Surface,默认情况下一个Activity只有一个Surface,当Activity中使用SurfaceView时,就存在多个Surface。Activity默认surface是在relayoutWindow过程中由WMS服务创建的,然后回传给应用程序进程,我们知道一个Surface其实就是应用程序端的本地窗口,关于Surface的初始化过程这里就不在介绍。

performLayout

frameworksbasecorejavaandroidviewViewRootImpl.java

  1. private void performLayout() {
  2.     mLayoutRequested = false;
  3.     mScrollMayChange = true;
  4.     final View host = mView;
  5.     if (DEBUG_ORIENTATION || DEBUG_LAYOUT) {
  6.         Log.v(TAG, “Laying out “ + host + ” to (“ +
  7.                 host.getMeasuredWidth() + “, “ + host.getMeasuredHeight() + “)”);
  8.     }
  9.     Trace.traceBegin(Trace.TRACE_TAG_VIEW, “layout”);
  10.     try {
  11.         host.layout(00, host.getMeasuredWidth(), host.getMeasuredHeight());
  12.     } finally {
  13.         Trace.traceEnd(Trace.TRACE_TAG_VIEW);
  14.     }
  15. }

performDraw

frameworksbasecorejavaandroidview ViewRootImpl.java

  1. private void performDraw() {
  2.     if (!mAttachInfo.mScreenOn && !mReportNextDraw) {
  3.         return;
  4.     }
  5.     final boolean fullRedrawNeeded = mFullRedrawNeeded;
  6.     mFullRedrawNeeded = false;
  7.     mIsDrawing = true;
  8.     Trace.traceBegin(Trace.TRACE_TAG_VIEW, “draw”);
  9.     try {
  10.         draw(fullRedrawNeeded);
  11.     } finally {
  12.         mIsDrawing = false;
  13.         Trace.traceEnd(Trace.TRACE_TAG_VIEW);
  14.     }
  15.     …
  16. }

 

  1. private void draw(boolean fullRedrawNeeded) {
  2.     Surface surface = mSurface;
  3.     if (surface == null || !surface.isValid()) {
  4.         return;
  5.     }
  6.     …
  7.     if (!dirty.isEmpty() || mIsAnimating) {
  8.         //使用硬件渲染
  9.         if (attachInfo.mHardwareRenderer != null && attachInfo.mHardwareRenderer.isEnabled()) {
  10.             // Draw with hardware renderer.
  11.             mIsAnimating = false;
  12.             mHardwareYOffset = yoff;
  13.             mResizeAlpha = resizeAlpha;
  14.             mCurrentDirty.set(dirty);
  15.             mCurrentDirty.union(mPreviousDirty);
  16.             mPreviousDirty.set(dirty);
  17.             dirty.setEmpty();
  18.             if (attachInfo.mHardwareRenderer.draw(mView, attachInfo, this,
  19.                     animating ? null : mCurrentDirty)) {
  20.                 mPreviousDirty.set(00, mWidth, mHeight);
  21.             }
  22.         //使用软件渲染
  23.         } else if (!drawSoftware(surface, attachInfo, yoff, scalingRequired, dirty)) {
  24.             return;
  25.         }
  26.     }
  27.     …
  28. }

窗口添加过程

frameworksbaseservicesjavacomandroidserverwmSession.java

  1. public int add(IWindow window, int seq, WindowManager.LayoutParams attrs,
  2.         int viewVisibility, Rect outContentInsets, InputChannel outInputChannel) {
  3.     return mService.addWindow(this, window, seq, attrs, viewVisibility, outContentInsets,
  4.             outInputChannel);
  5. }

frameworksbaseservicesjavacomandroidserverwmWindowManagerService.java

  1. public int addWindow(Session session, IWindow client, int seq,
  2.         WindowManager.LayoutParams attrs, int viewVisibility,
  3.         Rect outContentInsets, InputChannel outInputChannel) {
  4.     //client为IWindow的代理对象,是Activity在WMS服务中的唯一标示
  5.     int res = mPolicy.checkAddPermission(attrs);
  6.     if (res != WindowManagerImpl.ADD_OKAY) {
  7.         return res;
  8.     }
  9.     boolean reportNewConfig = false;
  10.     WindowState attachedWindow = null;
  11.     WindowState win = null;
  12.     long origId;
  13.     synchronized(mWindowMap) {
  14.         if (mDisplay == null) {
  15.             throw new IllegalStateException(“Display has not been initialialized”);
  16.         }
  17.         //判断窗口是否已经存在
  18.         if (mWindowMap.containsKey(client.asBinder())) {
  19.             Slog.w(TAG, “Window “ + client + ” is already added”);
  20.             return WindowManagerImpl.ADD_DUPLICATE_ADD;
  21.         }
  22.         //如果添加的是应用程序窗口
  23.         if (attrs.type >= FIRST_SUB_WINDOW && attrs.type <= LAST_SUB_WINDOW) {
  24.             //根据attrs.token从mWindowMap中取出应用程序窗口在WMS服务中的描述符WindowState
  25.             attachedWindow = windowForClientLocked(null, attrs.token, false);
  26.             if (attachedWindow == null) {
  27.                 Slog.w(TAG, “Attempted to add window with token that is not a window: “
  28.                       + attrs.token + “.  Aborting.”);
  29.                 return WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN;
  30.             }
  31.             if (attachedWindow.mAttrs.type >= FIRST_SUB_WINDOW
  32.                     && attachedWindow.mAttrs.type <= LAST_SUB_WINDOW) {
  33.                 Slog.w(TAG, “Attempted to add window with token that is a sub-window: “
  34.                         + attrs.token + “.  Aborting.”);
  35.                 return WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN;
  36.             }
  37.         }
  38.         boolean addToken = false;
  39.         //根据attrs.token从mWindowMap中取出应用程序窗口在WMS服务中的描述符WindowState
  40.         WindowToken token = mTokenMap.get(attrs.token);
  41.         if (token == null) {
  42.             …
  43.             ①token = new WindowToken(this, attrs.token, –1false);
  44.             addToken = true;
  45.         }
  46.         //应用程序窗口
  47.         else if (attrs.type >= FIRST_APPLICATION_WINDOW
  48.                 && attrs.type <= LAST_APPLICATION_WINDOW) {
  49.             AppWindowToken atoken = token.appWindowToken;
  50.             …
  51.         }
  52.         //输入法窗口
  53.         else if (attrs.type == TYPE_INPUT_METHOD) {
  54.             …
  55.         }
  56.         //墙纸窗口
  57.         else if (attrs.type == TYPE_WALLPAPER) {
  58.             …
  59.         }
  60.         //Dream窗口
  61.         else if (attrs.type == TYPE_DREAM) {
  62.             …
  63.         }
  64.         //为Activity窗口创建WindowState对象
  65.         ②win = new WindowState(this, session, client, token,
  66.                 attachedWindow, seq, attrs, viewVisibility);
  67.         …
  68.         if (outInputChannel != null && (attrs.inputFeatures& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
  69.             String name = win.makeInputChannelName();
  70.             InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
  71.             win.setInputChannel(inputChannels[0]);
  72.             inputChannels[1].transferTo(outInputChannel);
  73.             mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
  74.         }
  75.         …
  76.         //以键值对<IWindow.Proxy/Token,WindowToken>形式保存到mTokenMap表中
  77.         if (addToken) {
  78.             ③mTokenMap.put(attrs.token, token);
  79.         }
  80.         ④win.attach();
  81.         //以键值对<IWindow的代理对象,WindowState>形式保存到mWindowMap表中
  82.         ⑤mWindowMap.put(client.asBinder(), win);
  83.         …
  84.     }
  85.     …
  86.     return res;
  87. }

我们知道当应用程序进程添加一个DecorView到窗口管理器时,会为当前添加的窗口创建ViewRootImpl对象,同时构造了一个W本地Binder对象,无论是窗口视图对象DecorView还是ViewRootImpl对象,都只是存在于应用程序进程中,在添加窗口过程中仅仅将该窗口的W对象传递给WMS服务,经过Binder传输后,到达WMS服务端进程后变为IWindow.Proxy代理对象,因此该函数的参数client的类型为IWindow.Proxy。参数attrs的类型为WindowManager.LayoutParams,在应用程序进程启动Activity时,handleResumeActivity()函数通过WindowManager.LayoutParams
l = r.window.getAttributes();来得到应用程序窗口布局参数,由于WindowManager.LayoutParams实现了Parcelable接口,因此WindowManager.LayoutParams对象可以跨进程传输,WMS服务的addWindow函数中的attrs参数就是应用程序进程发送过来的窗口布局参数。在LocalWindowManager的addView函数中为窗口布局参数设置了相应的token,如果是应用程序窗口,则布局参数的token设为W本地Binder对象。如果不是应用程序窗口,同时当前窗口没有父窗口,则设置token为当前窗口的IApplicationToken.Proxy代理对象,否则设置为父窗口的IApplicationToken.Proxy代理对象,由于应用程序和WMS分属于两个不同的进程空间,因此经过Binder传输后,布局参数的令牌attrs.token就转变为IWindow.Proxy或者Token。以上函数首先根据布局参数的token等信息构造一个WindowToken对象,然后在构造一个WindowState对象,并将添加的窗口信息记录到mTokenMap和mWindowMap哈希表中。


在WMS服务端创建了所需对象后,接着调用了WindowState的attach()来进一步完成窗口添加。
frameworksbaseservicesjavacomandroidserverwmWindowState.java

  1. void attach() {
  2.     if (WindowManagerService.localLOGV) Slog.v(
  3.         TAG, “Attaching “ + this + ” token=” + mToken
  4.         + “, list=” + mToken.windows);
  5.     mSession.windowAddedLocked();
  6. }

frameworksbaseservicesjavacomandroidserverwmSession.java

  1. void windowAddedLocked() {
  2.     if (mSurfaceSession == null) {
  3.         mSurfaceSession = new SurfaceSession();
  4.         if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(
  5.                 WindowManagerService.TAG, ”  NEW SURFACE SESSION “ + mSurfaceSession);
  6.         mService.mSessions.add(this);
  7.     }
  8.     mNumWindow++;//记录对应的某个应用程序添加的窗口数量
  9. }

到此一个新的应用程序窗口就添加完成了。总结一下:

应用程序通过IWindowSession接口请求WMS服务添加一个应用程序窗口,WMS服务首先在自己服务进程为应用程序创建创建一个对应的WindowState描述符,然后保存到成员变量mWindowMap中。如果还没有为应用程序进程创建连接SurfaceFlinger的会话,就接着创建该会话通道SurfaceSession,我们知道,Activity中的视图所使用的画布Surface是在WMS服务进程中创建的,但是该画布所使用的图形buffer确实在SurfaceFlinger进程中分配管理的,而图形的绘制确是在应用程序进程中完成的,所以Activity的显示过程需要三个进程的配合才能完成。应用程序进程只与WMS服务进程交互,并不直接和SurfaceFlinger进程交互,而是由WMS服务进程同SurfaceFlinger进程配合。前面我们介绍了应用程序进程是通过IWindowSession接口与WMS服务进程通信的,那WMS服务是如何与SurfaceFlinger进程通信的呢,这就是windowAddedLocked函数要完成的工作。

在windowAddedLocked函数中使用单例模式创建一个SurfaceSession对象,在构造该对象时,通过JNI在native层创建一个与SurfaceFlinger进程的连接。

frameworksbasecorejavaandroidviewSurfaceSession.java

  1. public SurfaceSession() {
  2.     init();
  3. }

该init()函数是一个native函数,其JNI实现如下:

frameworksbasecorejni android_view_Surface.cpp

  1. static void SurfaceSession_init(JNIEnv* env, jobject clazz)
  2. {
  3.     sp<SurfaceComposerClient> client = new SurfaceComposerClient;
  4.     client->incStrong(clazz);
  5.     env->SetIntField(clazz, sso.client, (int)client.get());
  6. }

该函数构造了一个SurfaceComposerClient对象,在第一次强引用该对象时,会请求SurfaceFlinger创建一个专门处理当前应用程序进程请求的Client会话。

每个应用程序进程都持有一个与WMS服务会话通道IWindowSession,而服务端的Session有且只有一个SurfaceSession对象。系统中创建的所有IWindowSession都被记录到WMS服务的mSessions成员变量中,这样WMS就可以知道自己正在处理那些应用程序的请求。到此我们来梳理一下在WMS服务端都创建了那些对象:

1)        WindowState对象,是应用程序窗口在WMS服务端的描述符;

2)        Session对象,应用程序进程与WMS服务会话通道;

3)        SurfaceSession对象,应用程序进程与SurfaceFlinger的会话通道;