*详细的 Toolbar 开发实践总结

之前发了一篇介绍 Translucent System Bar 特性的文章 Translucent System Bar 的*佳实践,收到很多开发者的关注和反馈。今天开始写第二篇,全面的介绍一下 Toolbar 的使用。说起 Toolbar ,可能有很多开发的童鞋还比较陌生,没关系,请接着往下看。

初识 Toolbar

Toolbar 是在 Android 5.0 开始推出的一个 Material Design 风格的导航控件 ,Google 非常推荐大家使用 Toolbar 来作为Android客户端的导航栏,以此来取代之前的 Actionbar 。与 Actionbar 相比,Toolbar 明显要灵活的多。它不像 Actionbar 一样,一定要固定在Activity的顶部,而是可以放到界面的任意位置。除此之外,在设计 Toolbar 的时候,Google也留给了开发者很多可定制修改的余地,这些可定制修改的属性在API文档中都有详细介绍,如:

  • 设置导航栏图标;
  • 设置App的logo;
  • 支持设置标题和子标题;
  • 支持添加一个或多个的自定义控件;
  • 支持Action Menu;
%title插图%num

Toolbar支持的特性

总之,与 Actionbar 相比,Toolbar 让我感受到Google满满的诚意。怎样?是否已经对 Toolbar 有大概的了解,跃跃欲试的感觉出来了有木有?接下来,我们就一步一步的来看如何使用 Toolbar(其实是我使用 Toolbar 踩坑填坑的血泪史,你们接下去看,我先擦个眼泪…. )。

开始使用 Toolbar

前面提到 Toolbar 是在 Android 5.0 才开始加上的,Google 为了将这一设计向下兼容,自然也少不了要推出兼容版的 Toolbar 。为此,我们需要在工程中引入 appcompat-v7 的兼容包,使用 android.support.v7.widget.Toolbar 进行开发。下面看一下代码结构,同样把重点部分已经红圈圈出:

%title插图%num

关键部分代码
  • ToolbarActivity 包含了 Toolbar 的一些基本使用, ZhiHuActivity 是在熟悉了 Toolbar 后对知乎主页面的一个高仿实现。
  • layout和menu文件夹分别是上面提到的两个Activity的布局文件 和 actionmenu 菜单文件。
  • values、values-v19、values-v21 中包含了一些自定义的 theme,后面用到的时候会顺带讲解。

我们先来看一下 ToolbarActivity 的运行效果

%title插图%num

ToolbarActivity效果图

按照效果图,从左到右分别是我们前面提及到的 导航栏图标App的logo标题和子标题自定义控件、以及 ActionMenu 。接着,我们来看下布局文件和代码实现。

首先,在布局文件 activity_tool_bar.xml 中添加进我们需要的 Toolbar 控件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/color_0176da">

        <!--自定义控件-->
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Clock" />
    </android.support.v7.widget.Toolbar>
</LinearLayout>

接着在 base_toolbar_menu.xml 中添加 action menu 菜单项

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
        android:id="@id/action_search"
        android:icon="@mipmap/ic_search"
        android:title="@string/menu_search"
        app:showAsAction="ifRoom" />

    <item
        android:id="@id/action_notification"
        android:icon="@mipmap/ic_notifications"
        android:title="@string/menu_notifications"
        app:showAsAction="ifRoom" />

    <item
        android:id="@+id/action_item1"
        android:title="@string/item_01"
        app:showAsAction="never" />

    <item
        android:id="@+id/action_item2"
        android:title="@string/item_02"
        app:showAsAction="never" />
</menu>

*后到 ToolbarActivity 中调用代码拿到这 Toolbar 控件,并在代码中做各种setXXX操作。

/**
 * Toolbar的基本使用
 */
public class ToolBarActivity extends BaseActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tool_bar);

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);

        toolbar.setNavigationIcon(R.mipmap.ic_drawer_home);//设置导航栏图标
        toolbar.setLogo(R.mipmap.ic_launcher);//设置app logo
        toolbar.setTitle("Title");//设置主标题
        toolbar.setSubtitle("Subtitle");//设置子标题

        toolbar.inflateMenu(R.menu.base_toolbar_menu);//设置右上角的填充菜单
        toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
            @Override
            public boolean onMenuItemClick(MenuItem item) {
                int menuItemId = item.getItemId();
                if (menuItemId == R.id.action_search) {
                    Toast.makeText(ToolBarActivity.this , R.string.menu_search , Toast.LENGTH_SHORT).show();

                } else if (menuItemId == R.id.action_notification) {
                    Toast.makeText(ToolBarActivity.this , R.string.menu_notifications , Toast.LENGTH_SHORT).show();

                } else if (menuItemId == R.id.action_item1) {
                    Toast.makeText(ToolBarActivity.this , R.string.item_01 , Toast.LENGTH_SHORT).show();

                } else if (menuItemId == R.id.action_item2) {
                    Toast.makeText(ToolBarActivity.this , R.string.item_02 , Toast.LENGTH_SHORT).show();

                }
                return true;
            }
        });

    }

}

代码到此已经完成了 Toolbar 的基本使用,注意,是基本使用而已!!!!!下面有几个代码里面需要注意的地方:

  1. 我们在使用 Toolbar 时候需要先隐藏掉系统原先的导航栏,网上很多人都说给Activity设置一个NoActionBar的Theme。但个人觉得有点小题大做了,所以这里我直接在BaseActivity中调用 supportRequestWindowFeature(Window.FEATURE_NO_TITLE) 去掉了默认的导航栏(注意,我的BaseActivity是继承了AppCompatActivity的,如果是继承Activity就应该调用requestWindowFeature(Window.FEATURE_NO_TITLE));
  2. 如果你想修改标题和子标题的字体大小、颜色等,可以调用setTitleTextColorsetTitleTextAppearancesetSubtitleTextColorsetSubtitleTextAppearance 这些API;
  3. 自定义的View位于 titlesubtitle 和 actionmenu 之间,这意味着,如果 title 和 subtitle 都在,且 actionmenu选项 太多的时候,留给自定义View的空间就越小;
  4. 导航图标 和 app logo 的区别在哪?如果你只设置 导航图标( or app logo) 和 titlesubtitle,会发现 app logo 和 titlesubtitle 的间距比较小,看起来不如 导航图标 与 它们两搭配美观;
  5. Toolbar 和其他控件一样,很多属性设置方法既支持代码设置,也支持在xml中设置(这里也是*****坑爹的地方,如何坑爹法,请接着往下看);

Toolbar 踩坑填坑

  • 坑一:xml布局文件中,Toolbar属性设置无效

刚开始使用Toolbar的时候,我的布局文件中是这样写的

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/color_0176da"
        android:logo="@mipmap/ic_launcher"
        android:navigationIcon="@mipmap/ic_drawer_home"
        android:subtitle="456"
        android:title="123">

        <!--自定义控件-->
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Clock" />
    </android.support.v7.widget.Toolbar>
</LinearLayout>

在真机跑起来之后,看到的结果是下面这样的。

%title插图%num

Toolbar 属性设置无效

此时心中真是万千匹草泥马在奔腾,除了设置背景色和TextView有效外,说好的 logonavigationIconsubtitletitle 都跑哪去了?在编译器没报错又不见效果的情况下,参考了其他开发者的用法后找到了以下的解决方案,就是在根布局中加入自定义属性的命名空间

xmlns:toolbar="http://schemas.android.com/apk/res-auto"(这里的toolbar可以换成你想要其他命名,做过自定义控件的童鞋相比很熟悉此用法了)

然后把所有用 android:xxx 设置无效的,都用 toolbar:xxx 设置即可生效。*终的布局代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:toolbar="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/color_0176da"
        toolbar:navigationIcon="@mipmap/ic_drawer_home"
        toolbar:logo="@mipmap/ic_launcher"
        toolbar:subtitle="456"
        toolbar:title="123">

        <!--自定义控件-->
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Clock" />
    </android.support.v7.widget.Toolbar>
</LinearLayout>

到此即可解决 xml 中属性设置失效的问题,为什么会出现这种问题呢?我猜测是因为这个控件是兼容版的控件,用 android:xxx 设置无效是的这些属性是在兼容包中,不在默认的Android SDK中,所以我们需要额外的引入。至于为什么IDE不报错,估计就是bug了吧!

  • 坑二:Action Menu Item 的文字颜色设置无效

系统默设置了ActionMenu每个Item的文字颜色和大小,像ToolbarActivity在Google原生5.1系统下默认效果就是下面这样的

%title插图%num

Android 5.1 默认的ActionMenu Item的风格

此时,如果我有需求要改变一下item文字颜色,应该怎么破?我按照网上比较普遍的解决方案,做了如下两步的修改操作:

  • 在styles.xml中自定义一个Theme,并设置 actionMenuTextColor 属性(注意:不是 android:actionMenuTextColor )
<style name="Theme.ToolBar.Base" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="actionMenuTextColor">@color/color_red</item>
</style>
  • 在布局文件的Toolbar中设置popupTheme(注意:是toolbar:xxx,不是android:xxx)
    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/color_0176da"
        toolbar:popupTheme="@style/Theme.ToolBar.Base">

        <!--自定义控件-->
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Clock" />
    </android.support.v7.widget.Toolbar>

运行之后,文字的颜色的并没有发生任何改变。说好的改变颜色呢…..找来找去,*后在 StackOverflow 找到一个还不错的解决方案,就是把上面的的 actionMenuTextColor 属性换成 android:textColorPrimary 即可解决,*终得到下面的运行效果。

%title插图%num

成功修改 actionmenu item 文字的颜色

这种方法也有一个小缺点,如果我把自定义控件换成Button,你会发现Button默认的文字颜色也变成了红色。所以,此处如果有朋友有更好的解决方案,请留言赐教。

如果你想要修改 ActionMenu Item 的文字大小,也可以在theme中设置加上如下设置

<item name="android:textSize">20sp</item>

以上就是目前使用 Toolbar 一些比较折腾的坑,感觉 Google 对 Toolbar 这些坑,还可以进一步优化优化,不然就坑苦了开发者们了。

仿知乎主页面

为了加深一下 Toolbar 的开发体验,我们使用 Toolbar 来实现知乎主页的效果!先来看下知乎主页的效果

%title插图%num

Android 5.1上知乎主页效果图

如果前面的内容你看明白,想撸出这个界面无非是几分钟的事情,下面就直接上代码,不做赘述了。

ZhiHuActivity界面代码

public class ZhiHuActivity extends BaseActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_zhi_hu);

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        toolbar.inflateMenu(R.menu.zhihu_toolbar_menu);

        toolbar.setNavigationIcon(R.mipmap.ic_drawer_home);

        toolbar.setTitle(R.string.home_page);
        toolbar.setTitleTextColor(getResources().getColor(android.R.color.white));
    }
}

zhihu_toolbar_menu.xml 菜单

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
        android:id="@id/action_search"
        android:icon="@mipmap/ic_search"
        android:title="@string/menu_search"
        app:showAsAction="ifRoom" />

    <item
        android:id="@id/action_notification"
        android:icon="@mipmap/ic_notifications"
        android:title="@string/menu_notifications"
        app:showAsAction="ifRoom" />

    <item
        android:id="@id/action_settings"
        android:orderInCategory="100"
        android:title="@string/menu_settings"
        app:showAsAction="never" />

    <item
        android:id="@id/action_about"
        android:orderInCategory="101"
        android:title="@string/menu_about_us"
        app:showAsAction="never" />
</menu>

activity_zhi_hu.xml 布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/color_0176da"
        android:theme="@style/Theme.ToolBar.ZhiHu">

    </android.support.v7.widget.Toolbar>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/white">

        <ImageView
            android:layout_width="60dp"
            android:layout_height="60dp"
            android:layout_centerInParent="true"
            android:background="@mipmap/ic_zhihu_logo" />
    </RelativeLayout>

</LinearLayout>

styles.xml 中的 Theme.ToolBar.ZhiHu,给 Toolbar 设置android:theme用的

<resources>

    ...
    ...

    <style name="Theme.ToolBar.ZhiHu" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="actionOverflowButtonStyle">@style/ActionButton.Overflow.ZhiHu</item>
    </style>

    <style name="ActionButton.Overflow.ZhiHu" parent="android:style/Widget.Holo.Light.ActionButton.Overflow">
        <item name="android:src">@mipmap/ic_menu_more_overflow</item>
    </style>

</resources>

*终得到下面这样的效果

%title插图%num

Android5.1上仿知乎主页面效果

这里在 Toolbar 设置 android:theme=”@style/Theme.ToolBar.ZhiHu” 主要是为了替换系统右上角三个点的图标,如果不设置,则会成系统默认主题的样子。

%title插图%num

不设置Theme的效果

*后,再给知乎的主页面做个小小的优化,它在 Android 4.4 上运行还是能够看到一条黑乎乎的通知栏,为此我把 Toolbar 和 Translucent System Bar 的特性结合起来,*终改进成下面的效果(附上 Android4.4 和 5.1 上的运行效果)。

%title插图%num

Android4.4上改进版的知乎主页
%title插图%num

Android5.1上改进版的知乎主页

如果你还不知道 Translucent System Bar 的特性怎么使用,请查看:Translucent System Bar 的*佳实践

总结

关于 Toolbar 的使用就介绍到此,本来是怀着很简单就可以上手的心态来使用,结果发现还是有很多坑需要填。果然还是验证了一句老话

纸上得来终觉浅,*知此事要躬行

Android – toolbar navigation 样式

1.修改title 边距

修改边距使用系统的app属性来引入使用,即:

  xmlns:app="http://schemas.android.com/apk/res-auto"

 

比如:

<android.support.v7.widget.Toolbar
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginBottom="1dp"
    android:background="?attr/colorPrimary"
    android:minHeight="?attr/actionBarSize"
    android:orientation="vertical"
    app:contentInsetLeft="10dp"/>

 

当然也可以在style.xml中修改,自己研究吧;


2.修改navigation icon的padding值

修改padding值,就需要在style.xml中修改;在此我们修改的是navigation的pading值:

Widget.AppCompat.Toolbar.Button.Navigation

 

比如:

1.定义style

    <style name="myToolbarNavigationButtonStyle" parent="@style/Widget.AppCompat.Toolbar.Button.Navigation">
        <item name="android:minWidth">0dp</item>
        <item name="android:padding">@dimen/margin_horizontal_large</item>
        <item name="android:scaleType">centerInside</item>
    </style>
  • 1

2.app主题中应用

    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="toolbarNavigationButtonStyle">@style/myToolbarNavigationButtonStyle</item>
    </style>
  • 1

3.title居中

toolbar是可以自定义布局的,可以在toolbar中添加一个textview来实现,从而代替title;
比如:

1.布局

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar
    android:id="@+id/toolbar"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"android:layout_height="?actionBarSize"
    android:background="@null"
    app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
    app:theme="@style/AppTheme">

    <TextView
        android:id="@+id/toolbar_title"
        style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"/>

</android.support.v7.widget.Toolbar>

 

2.初始化

View v = findViewById(R.id.toolbar);
if (v != null) {
    toolbar = (Toolbar) v;
    setSupportActionBar(toolbar);
    toolbarTitle = (TextView) v.findViewById(R.id.toolbar_title);
    if (toolbarTitle != null) {
        getSupportActionBar().setDisplayShowTitleEnabled(false);
    }
}

 

3.使用
设置title两种方式:
(1)直接在setText();
(2)在AndroidManifest.xml中指定title;
(3)如果有baseActivity的话,解决如下:
Activity有一个onTitleChanged的接口,在Activity的onPostCreate与setTitle中都会调用这个方法;

protected void onPostCreate(@Nullable Bundle savedInstanceState) {
    if (!isChild()) {
        mTitleReady = true;
        onTitleChanged(getTitle(), getTitleColor());
    }
    mCalled = true;
}

 

所以只需要在BaseActivity中重载这个方法就行了,如下所示 :

@Override
protected void onTitleChanged(CharSequence title, int color) {
    super.onTitleChanged(title, color);
    if (toolbarTitle != null) {
        toolbarTitle.setText(title);
    }
}

 

 

4.修改menu的padding值

定义style值:

    <style name="mToolbarStyle" parent="@style/Widget.AppCompat.Toolbar">
        <item name="android:paddingRight">0dp</item>
        <item name="android:paddingLeft">13dp</item>
    </style>
  • 1

使用:

<android.support.v7.widget.Toolbar
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginBottom="1dp"
    android:background="?attr/colorPrimary"
    android:minHeight="?attr/actionBarSize"
    android:orientation="vertical"
    style="@style/mToolbarStyle"/>
  • 1

上面的方法,在有些机子上会失效,大家有上面办法,请告诉我,谢谢!

Android ToolBar 使用完全解析

ToolBar简介

ToolBar是Android 5.0推出的一个新的导航控件用于取代之前的ActionBar,由于其高度的可定制性、灵活性、具有Material Design风格等优点,越来越多的应用也用上了ToolBar,比如常用的知乎软件其顶部导航栏正是使用ToolBar。官方考虑到仍有一部分用户的手机版本号低于5.0,所以,ToolBar也放进了support v7包内,使得低版本的系统也能使用上ToolBar。本文将使用support v7支持包的ToolBar来进行讲解,包括其基本用法、样式定制等知识点。

ToolBar的基本使用

引入support v7支持包

在你项目的build.gradle内输入如下代码,即能引入支持包,该支持包内有能向下兼容的ToolBar:

dependencies {    
      compile fileTree(dir: 'libs', include: ['*.jar'])    
      compile 'com.android.support:appcompat-v7:23.1.1'
}

更改主题

为了能够正常使用ToolBar,我们需要隐藏原来的ActionBar,这个可以在主题中修改,在values/styles.xml中做出如下修改:

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">    
</style>

继承了Theme.Appcompat.Light.NoActionBar主题,这里提一下,这个Theme.AppCompat是支持包内的主题,对应着5.0版本的Theme.Material主题。然后在manifest文件中引用这个主题。

在布局文件中创建这个控件,activity_main.xml文件中,代码如下所示:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="标题"
            android:textSize="20sp"/>
    </android.support.v7.widget.Toolbar>
</FrameLayout>

在上面,创建了android.support.v7.widget.Toolbar,同时我们在内部放了一个TextView,这是与ActionBar*大的不同,因为ToolBar实际上是一个ViewGroup,支持在其内部放入子View。ok,我们运行程序,得到如下结果:

%title插图%num

结果1.png

可以看出ToolBar正常显示,当然了,这只是*简单的用法,接下来我们逐步添加内容、样式,使它看起开更加美,功能更加完善。

ToolBar的完善

一、首先我们考虑,改变ToolBar的颜色

要想改变toolbar的颜色很简单,直接在布局文件中添加一个backgroud属性指定颜色就可以了,但是为了全局考虑,我们可以这样:在values/styles.xml文件中做出如下修改:

<resources>
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">#2e8abb</item> <!--浅蓝色-->
        <item name="colorPrimaryDark">#3A5FCD</item> <!--深蓝色-->
    </style>
</resources>

然后在布局文件中,添加如下属性:

android:background="?attr/colorPrimary"

这样,就能方便对每一个toolbar引用同样的颜色了,我们先看看现在的效果是怎样的:

%title插图%num

结果2.png

可以看到,颜色已经改变,同时我们注意到,顶部状态栏的颜色也变成了深蓝色,这是因为添加了”colorPrimaryDark”的属性,使得顶部状态栏随之改变,利用这一特性,我们可以轻松实现“状态栏沉浸”的效果了。当然,这只适用于Android 5.0以上,如果在低版本则这个属性无效。这里再附上一张图(图片来自http://blog.csdn.net/bbld_/article/details/41439715):

%title插图%num

样式说明

根据图中的说明,我们可以轻松地在styles.xml文件中定制我们的样式,如果想要改变toolbar的title、subtitle以及menu中文字的颜色,可以利用“textColorPrimary”属性等。

二、添加title、subtitle、logo、导航栏图标

在MainActivity文件先获取控件的实例,接着通过一系列的set方法即可设置,代码如下:

public class MainActivity extends AppCompatActivity {

    private Toolbar toolbar;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        toolbar = (Toolbar) findViewById(R.id.toolbar);
        toolbar.setTitle("Title");
        toolbar.setSubtitle("SubTitle");
        toolbar.setLogo(R.mipmap.ic_launcher);

        //设置导航图标要在setSupportActionBar方法之后
        setSupportActionBar(toolbar);
        toolbar.setNavigationIcon(R.mipmap.ic_drawer_home);
    }

}

结果如下:

%title插图%num

结果3.png

如果你想修改标题和子标题的字体大小、颜色等,可以调用 setTitleTextColor 、 setTitleTextAppearance、 setSubtitleTextColor 、 setSubtitleTextAppearance 这些API。当然,这些设置都是支持在xml布局中直接添加的,但是用的不是android:命名空间,而是自定义命名空间,如下所示:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:toolbar="http://schemas.android.com/apk/res-auto"
    ...>
    <android.support.v7.widget.Toolbar
        ...
        toolbar:logo="@mipmap/ic_launcher"
        toolbar:title="Title"
        toolbar:subtitle="Sub Title"
        toolbar:titleTextColor="#ffffff">
    </android.support.v7.widget.Toolbar>
</FrameLayout>

三、添加菜单选项图标及点击事件

1.添加菜单选项图标

一般导航条,在其右侧都会有菜单选项,当然ToolBar也是支持自定义菜单的,首先我们在菜单文件中,修改如下:res/menu/menu_main.xml:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity">
    <item android:id="@+id/action_search"
        android:title="Search"
        android:icon="@mipmap/ic_search"
        app:showAsAction="ifRoom"/>
    <item android:id="@+id/action_notifications"
        android:title="notifications"
        android:icon="@mipmap/ic_notifications"
        app:showAsAction="ifRoom"/>
    <item android:id="@+id/action_settings"
        android:title="@string/action_settings"
        android:orderInCategory="100"
        android:icon="@mipmap/ic_launcher"
        app:showAsAction="never"/>
</menu>

这里用了这样一个熟悉:app:showAsAction=”ifRoom”/”never”,app是自定义的命名空间,因为我们的activity继承的是AppCompatActivity,是support v7包的,并不是原生sdk内部的,因此不能使用android:showAsAction,否则会报错。然后ifRoom表示有空间则显示,never表示从不显示,而是会通过overflowwindow显示。
接着我们在Activity中,要重写onCreateOptionsMenu()方法,把这个菜单加载进去:

@Overridepublic boolean onCreateOptionsMenu(Menu menu) { 
       getMenuInflater().inflate(R.menu.menu_main, menu);   
       return true;
}

2.添加点击事件

菜单有了,我们要为菜单添加点击事件,这样菜单才会有实际用途,添加点击事件也很方便,可以这样操作:

//设置导航图标、添加菜单点击事件要在setSupportActionBar方法之后
        setSupportActionBar(toolbar);
        toolbar.setNavigationIcon(R.mipmap.ic_drawer_home);

        toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
            @Override
            public boolean onMenuItemClick(MenuItem item) {
                switch (item.getItemId()) {
                    case R.id.action_search:
                        Toast.makeText(MainActivity.this, "Search !", Toast.LENGTH_SHORT).show();
                        break;
                    case R.id.action_notifications:
                        Toast.makeText(MainActivity.this, "Notificationa !", Toast.LENGTH_SHORT).show();
                        break;
                    case R.id.action_settings:
                        Toast.makeText(MainActivity.this, "Settings !", Toast.LENGTH_SHORT).show();
                        break;
                }
                return true;
            }
        });

结果如下:

%title插图%num

结果5.png

可以看到,菜单选项图标正常显示,以及点击事件都正常触发,但是还有一点瑕疵的地方,那就是右上角的三个圆点,是黑色的,与图标格格不入,有没有什么办法改变它呢?答案是有的,可以通过添加样式改变,如下所示:

<resources>
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        ...
        <item name="android:textColorSecondary">#ffffff</item>
    </style>
</resources>

“android:textColorSecondary”属性对应的就是右上角三个圆点的颜色了。改变之后将会变成你要的颜色。

四、其他样式修改

修改Toolbar popup menu样式

我们先点击右上角的三个点,会弹出一个popup menu,如下所示:

%title插图%num

结果6.png

可以看到右上角的popup menu是白底黑字,那么有没有什么办法改变它的背景颜色,使菜单显示为黑底白字呢呢?答案是有的,我们可以这样设置:
首先在styles.xml文件中,新建一个主题:

<!-- toolbar弹出菜单样式 -->
<style name="ToolbarPopupTheme" parent="@style/ThemeOverlay.AppCompat.Dark">   
     <item name="android:colorBackground">#000000</item>
</style>

可以看到这个主题的parent是直接继承自ThemeOverlay.AppCompat.Dark,是支持包的一个主题,并且我们在内部声明了“android:colorBackground”这个属性,我们只要更改这个属性就能变更菜单的背景颜色了。接下来我们在布局文件中引入这个主题,这也很简单,为toolbar添加额外的属性如下:

toolbar:popupTheme="@style/ToolbarPopupTheme"

这样,改几行代码即可修改popup menu的背景颜色了,如下面所示:

%title插图%num

结果7.png

修改Toolbar popup menu 弹出位置

我们可以看到,popup menu的位置是过于偏上的,我们还可以修改它的位置,使它处于Toolbar之下,这样看起来可能更美观:
修改styles.xml文件如下:

<style name="ToolbarPopupTheme" parent="@style/ThemeOverlay.AppCompat.Dark">    
    <item name="android:colorBackground">#000000</item>    
    <item name="actionOverflowMenuStyle">@style/OverflowMenuStyle</item> <!--新增一个item,用于控制menu-->
</style>
<style name="OverflowMenuStyle" parent="Widget.AppCompat.Light.PopupMenu.Overflow">   
     <item name="overlapAnchor">false</item>  <!--把该属性改为false即可使menu位置位于toolbar之下-->
</style>

效果如下图:

%title插图%num

结果8.png

那么到目前为止,对于toolbar的相关使用意见讲述完毕,可以看出toolbar的灵活性很高,能自定义很多样式,我们平常开发也会遇到各种不同的样式,因此toolbar用于取代actionbar是非常合适的。以后如果还有遇到别的样式,我也会分享出来,谢谢。

mac版safari开启调试模式

mac版safari开启调试模式
*近理解http/https协议需要

步骤如下

点击Safari启动浏览器
点击左上Safari标志,选择偏好设置
选择高级,勾选下方的在菜单栏显示开发菜单。
如此,Safari就出现了开发菜单,右键网页元素也会出现查看元素功能了。
如此,Safari就出现了开发菜单,右键网页元素也会出现查看元素功能了。

uni-app在iOS移动端页面上下滑动关闭(页面回弹问题,非刷新)

使用uni-app写出的App同时在安卓端和iOS运行对比时,发现在iOS会因为上下拉出现空白页,而安卓端并没有这种问题,通过查阅官网发现使用以下代码可以解决问题。
进入pages.josn文件找到想要禁止回弹的页面,在对应的style中添加以下代码

“app-plus”:{
// 将回弹属性关掉
“bounce”:”none”
}

例如:

“pages”: [ //pages数组中*项表示应用启动页
{
“path”: “pages/login/login”,
“style”: {
“navigationBarTitleText”: “uni-app”,
“app-plus”:{
// 将回弹属性关掉
“bounce”:”none”
}
}
},{
// 我的页
“path”: “pages/mine/mine”,
“style”: {
“navigationBarTitleText”: “”,
“navigationStyle”: “custom”, // 隐藏系统导航栏
“enablePullDownRefresh”: false, // 下拉刷新
“app-plus”: {
// 将回弹属性关掉
“bounce”: “none”
}
}
}

]

 

苹果ios模拟器,电脑运行苹果ios应用

一、配置要求

硬件 CPU 显卡 内存 硬盘
*低配置 Intel I3及以上 支持openGL 8G内存 固态硬盘

建议您下载硬件环境检测工具,自动检测您的设备是否符合要求。

二、下载3个文件:

1. 黑雷模拟器客户端,(MD5:323146F47DF2029ED9420F6395)为保证安装完整性,请使用MD5验证工具进行效验。 下载链接

https://www.cesafe.com/go/aHR0cHM6Ly9jb21tb24uaGVpbGVpLmNvbS9VcGxvYWQvQmlnZmlsZS9WZXJzaW9uLzIwMTgtMDctMjQvSExJbnN0YWxsZXJfMTcyNi5leGU=

2. 镜像文件OSX-10.12-System1.0vmx文件大小约1.62GB,感谢热心网友“nobody”提供。

(下载链接

https://www.cesafe.com/go/aHR0cHM6Ly9wYW4uYmFpZHUuY29tL3MvMXFCaDhDZE12VUprckc5TXhfR0NFdmc=)

3. VMware虚拟机,建议版本为14。下载链接

https://www.cesafe.com/go/aHR0cDovL2Rvd25sb2FkMy52bXdhcmUuY29tL3NvZnR3YXJlL3drc3QvZmlsZS9WTXdhcmUtd29ya3N0YXRpb24tZnVsbC0xNC4xLjItODQ5NzMyMC5leGU=

Zygote进程—SystemServer的诞生

在ZygoteInit的main()方法中做了几件大事,其中一件便是启动Systemserver进程,代码如下:

@/frameworks/base/core/Java/com/Android/internal/os/ZygoteInit.java

[java] view plain copy print?
  1. public static void main(String argv[]) {  
  2.     try {  
  3.         ……
  4.         if (argv[1].equals(“start-system-server”)) {  
  5.             startSystemServer();//启动system_server进程  
  6.         } else if (!argv[1].equals(“”)) {  
  7.             throw new RuntimeException(argv[0] + USAGE_STRING);  
  8.         }
  9.         ……
  10. }

startSystemServer方法的实现如下:

@/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

[java] view plain copy print?
  1. /** 
  2.  * Prepare the arguments and fork for the system server process. 
  3.  */  
  4. private static boolean startSystemServer()  
  5.         throws MethodAndArgsCaller, RuntimeException {  
  6.     long capabilities = posixCapabilitiesAsBits(  
  7.         OsConstants.CAP_KILL,
  8.         OsConstants.CAP_NET_ADMIN,
  9.         OsConstants.CAP_NET_BIND_SERVICE,
  10.         OsConstants.CAP_NET_BROADCAST,
  11.         OsConstants.CAP_NET_RAW,
  12.         OsConstants.CAP_SYS_MODULE,
  13.         OsConstants.CAP_SYS_NICE,
  14.         OsConstants.CAP_SYS_RESOURCE,
  15.         OsConstants.CAP_SYS_TIME,
  16.         OsConstants.CAP_SYS_TTY_CONFIG
  17.     );
  18.     /* Hardcoded command line to start the system server */  
  19.     String args[] = {
  20.         “–setuid=1000”,  
  21.         “–setgid=1000”,  
  22.         “–setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1032,3001,3002,3003,3006,3007”,  
  23.         “–capabilities=” + capabilities + “,” + capabilities,  
  24.         “–runtime-init”,  
  25.         “–nice-name=system_server”,  
  26.         “com.android.server.SystemServer”,  
  27.     };
  28.     ZygoteConnection.Arguments parsedArgs = null;  
  29.     int pid;  
  30.     try {  
  31.         parsedArgs = new ZygoteConnection.Arguments(args);  
  32.         ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
  33.         ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
  34.         /* Request to fork the system server process */  
  35.         pid = Zygote.forkSystemServer(//以fork的方式创建system_server进程  
  36.                 parsedArgs.uid, parsedArgs.gid,
  37.                 parsedArgs.gids,
  38.                 parsedArgs.debugFlags,
  39.                 null,  
  40.                 parsedArgs.permittedCapabilities,
  41.                 parsedArgs.effectiveCapabilities);
  42.     } catch (IllegalArgumentException ex) {  
  43.         throw new RuntimeException(ex);  
  44.     }
  45.     /* For child process */  
  46.     if (pid == 0) {//pid==0说明在子进程中,父进程为Zygote  
  47.         handleSystemServerProcess(parsedArgs);
  48.     }
  49.     return true;  
  50. }

在startSystemServer中先设置了fork SystemServer所需的参数,然后通过forkSystemServer方法fork出SystemServer进程,*后通过handleSystemServerProcess处理新进程中的善后事宜。

首先看一下参数:

1、setuid=1000,这里1000代表SYSTEM_UID,即系统进程,关于进程ID的说明可以参见:/frameworks/base/core/java/android/os/Process.java。

2、nice-name=system_server表示制定进程的名字为“system_server”

3、com.android.server.SystemServer表示SystemServer类的位置。

接下来看一下forkSystemServer的实现:

@/libcore/dalvik/src/main/java/dalvik/system/Zygote.java

[java] view plain copy print?
  1. /** 
  2.  * Special method to start the system server process. In addition to the 
  3.  * common actions performed in forkAndSpecialize, the pid of the child 
  4.  * process is recorded such that the death of the child process will cause 
  5.  * zygote to exit. 
  6.  * 
  7.  * @param uid the UNIX uid that the new process should setuid() to after 
  8.  * fork()ing and and before spawning any threads. 
  9.  * @param gid the UNIX gid that the new process should setgid() to after 
  10.  * fork()ing and and before spawning any threads. 
  11.  * @param gids null-ok; a list of UNIX gids that the new process should 
  12.  * setgroups() to after fork and before spawning any threads. 
  13.  * @param debugFlags bit flags that enable debugging features. 
  14.  * @param rlimits null-ok an array of rlimit tuples, with the second 
  15.  * dimension having a length of 3 and representing 
  16.  * (resource, rlim_cur, rlim_max). These are set via the posix 
  17.  * setrlimit(2) call. 
  18.  * @param permittedCapabilities argument for setcap() 
  19.  * @param effectiveCapabilities argument for setcap() 
  20.  * 
  21.  * @return 0 if this is the child, pid of the child 
  22.  * if this is the parent, or -1 on error. 
  23.  */  
  24. public static int forkSystemServer(int uid, int gid, int[] gids, int debugFlags,  
  25.         int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) {  
  26.     preFork();
  27.     int pid = nativeForkSystemServer(  
  28.             uid, gid, gids, debugFlags, rlimits, permittedCapabilities, effectiveCapabilities);
  29.     postFork();
  30.     return pid;  
  31. }
  32. native public static int nativeForkSystemServer(int uid, int gid, int[] gids, int debugFlags,  
  33.         int[][] rlimits, long permittedCapabilities, long effectiveCapabilities);  

nativeForkSystemServer*终通过JNI实现,代码为:

@/dalvik/vm/native/dalvik_system_Zygote.cpp

[java] view plain copy print?
  1. /* 
  2.  * native public static int nativeForkSystemServer(int uid, int gid, 
  3.  *     int[] gids, int debugFlags, int[][] rlimits, 
  4.  *     long permittedCapabilities, long effectiveCapabilities); 
  5.  */  
  6. static void Dalvik_dalvik_system_Zygote_forkSystemServer(  
  7.         const u4* args, JValue* pResult)  
  8. {
  9.     pid_t pid;
  10.     pid = forkAndSpecializeCommon(args, true);  
  11.     /* The zygote process checks whether the child process has died or not. */  
  12.     if (pid > 0) {//pid大于0,说明是在父进程中  
  13.         int status;  
  14.         ALOGI(“System server process %d has been created”, pid);  
  15.         gDvm.systemServerPid = pid;
  16.         /* There is a slight window that the system server process has crashed 
  17.          * but it went unnoticed because we haven’t published its pid yet. So 
  18.          * we recheck here just to make sure that all is well. 
  19.          */  
  20.         if (waitpid(pid, &status, WNOHANG) == pid) {//堵塞,等待system_server进程  
  21.             ALOGE(“System server process %d has died. Restarting Zygote!”, pid);  
  22.             kill(getpid(), SIGKILL);//一旦上面的等待返回,说明进程pid(system_server)已终止,此时Zygote杀死自己  
  23.         }
  24.     }
  25.     RETURN_INT(pid);
  26. }

可以看出Dalvik_dalvik_system_Zygote_forkSystemServer会调用forkAndSpecializeCommon来fork出system_server进程。这里要注意*后几句,在fork出system_server以后,Zygote会调用waitpid等待system_server的终止,一旦发现system_server终止,Zygote则马上自杀。

接下来看一下handleSystemServerProcess的实现:

@/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

[java] view plain copy print?
  1.      /** 
  2.      * Finish remaining work for the newly forked system server process. 
  3.      */  
  4.     private static void handleSystemServerProcess(  
  5.             ZygoteConnection.Arguments parsedArgs)
  6.             throws ZygoteInit.MethodAndArgsCaller {  
  7.         closeServerSocket();//关闭从Zygote复制过来的socket  
  8.         // set umask to 0077 so new files and directories will default to owner-only permissions.  
  9.         Libcore.os.umask(S_IRWXG | S_IRWXO);//设置文件的默认权限,去除所有者之外的权限  
  10.         if (parsedArgs.niceName != null) {  
  11.             Process.setArgV0(parsedArgs.niceName);//system_server  
  12.         }
  13.         if (parsedArgs.invokeWith != null) {  
  14.             WrapperInit.execApplication(parsedArgs.invokeWith,
  15.                     parsedArgs.niceName, parsedArgs.targetSdkVersion,
  16.                     null, parsedArgs.remainingArgs);  
  17.         } else {  
  18.             /* 
  19.              * Pass the remaining arguments to SystemServer. 
  20.              */  
  21.             RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs);
  22.         }
  23.         /* should never reach here */  
  24.     }

上面的代码中用到了Linux中的umask这个函数,不明白的读者可以参考:http://hi.baidu.com/fengyun409/item/82cd158ffe7f67c8b17154e7。

下面继续看RuntimeInit.zygoteInit方法的实现:

@/frameworks/base/core/java/com/android/internel/os/RuntimeInit.java

[java] view plain copy print?
  1. /** 
  2.  * The main function called when started through the zygote process. This 
  3.  * could be unified with main(), if the native code in nativeFinishInit() 
  4.  * were rationalized with Zygote startup.<p> 
  5.  * 
  6.  * Current recognized args: 
  7.  * <ul> 
  8.  *   <li> <code> [–] <start class name>  <args> 
  9.  * </ul> 
  10.  * 
  11.  * @param targetSdkVersion target SDK version 
  12.  * @param argv arg strings 
  13.  */  
  14. public static final void zygoteInit(int targetSdkVersion, String[] argv)  
  15.         throws ZygoteInit.MethodAndArgsCaller {  
  16.     if (DEBUG) Slog.d(TAG, “RuntimeInit: Starting application from zygote”);  
  17.     redirectLogStreams();//将System.out 和 System.err 输出重定向到Android 的Log系统  
  18.     /* 
  19.      * 初始化了一些系统属性,其中*重要的一点就是设置了一个未捕捉异常的handler, 
  20.      * 当代码有任何未知异常,就会执行它, 
  21.      * 调试过Android代码的同学经常看到的”*** FATAL EXCEPTION IN SYSTEM PROCESS” 打印就出自这里 
  22.      */  
  23.     commonInit();
  24.     /* 
  25.      * *终会调用app_main的onZygoteInit函数 
  26.      * 这里的作用是在新进程中引入Binder,也就说通过nativeZygoteInit以后,新的进程就可以使用Binder进程通信了 
  27.      */  
  28.     nativeZygoteInit();
  29.     applicationInit(targetSdkVersion, argv);//应用初始化  
  30. }

这个函数是不是有些面熟?没错在《Zygote进程【2】——Zygote的分裂》一文中我们见过,Zygote进程在接收到ActivityManagerService请求创建进程的请求时就调用的该方法来处理创建子进程的后续工作。

[java] view plain copy print?
  1. private static void applicationInit(int targetSdkVersion, String[] argv)  
  2.         throws ZygoteInit.MethodAndArgsCaller {  
  3.     // If the application calls System.exit(), terminate the process  
  4.     // immediately without running any shutdown hooks.  It is not possible to  
  5.     // shutdown an Android application gracefully.  Among other things, the  
  6.     // Android runtime shutdown hooks close the Binder driver, which can cause  
  7.     // leftover running threads to crash before the process actually exits.  
  8.     nativeSetExitWithoutCleanup(true);  
  9.     // We want to be fairly aggressive about heap utilization, to avoid  
  10.     // holding on to a lot of memory that isn’t needed.  
  11.     VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);  
  12.     VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
  13.     final Arguments args;  
  14.     try {  
  15.         args = new Arguments(argv);  
  16.     } catch (IllegalArgumentException ex) {  
  17.         Slog.e(TAG, ex.getMessage());
  18.         // let the process exit  
  19.         return;  
  20.     }
  21.     // Remaining arguments are passed to the start class’s static main  
  22.     invokeStaticMain(args.startClass, args.startArgs);
  23. }

所以,这里与Zygote分裂时不同的是:这里的args.startClass的值为com.android.server.SystemServer。接下来大家都知道了,SystemServer类的main函数将会被调用。

@/frameworks/base/services/java/com/android/server/SystemServer.java

[java] view plain copy print?
  1. public static void main(String[] args) {  
  2.     if (System.currentTimeMillis() < EARLIEST_SUPPORTED_TIME) {  
  3.         // If a device’s clock is before 1970 (before 0), a lot of  
  4.         // APIs crash dealing with negative numbers, notably  
  5.         // java.io.File#setLastModified, so instead we fake it and  
  6.         // hope that time from cell towers or NTP fixes it  
  7.         // shortly.  
  8.         Slog.w(TAG, “System clock is before 1970; setting to 1970.”);  
  9.         SystemClock.setCurrentTimeMillis(EARLIEST_SUPPORTED_TIME);//初始化系统时间  
  10.     }
  11.     if (SamplingProfilerIntegration.isEnabled()) {  
  12.         SamplingProfilerIntegration.start();
  13.         timer = new Timer();  
  14.         timer.schedule(new TimerTask() {  
  15.             @Override  
  16.             public void run() {  
  17.                 SamplingProfilerIntegration.writeSnapshot(“system_server”, null);  
  18.             }
  19.         }, SNAPSHOT_INTERVAL, SNAPSHOT_INTERVAL);
  20.     }
  21.     // Mmmmmm… more memory!  
  22.     dalvik.system.VMRuntime.getRuntime().clearGrowthLimit();
  23.     // The system server has to run all of the time, so it needs to be  
  24.     // as efficient as possible with its memory usage.  
  25.     VMRuntime.getRuntime().setTargetHeapUtilization(0.8f);  
  26.     Environment.setUserRequired(true);  
  27.     System.loadLibrary(“android_servers”);//加载android_servers库  
  28.     Slog.i(TAG, “Entered the Android system server!”);  
  29.     // Initialize native services.  
  30.     nativeInit();//初始化native service  
  31.     // This used to be its own separate thread, but now it is  
  32.     // just the loop we run on the main thread.  
  33.     ServerThread thr = new ServerThread();  
  34.     thr.initAndLoop();
  35. }

在main中会加载libandroid_servers.so库,然后调用nativeInit初始化native层的Service。

[java] view plain copy print?
  1. /** 
  2.  * Called to initialize native system services. 
  3.  */  
  4. private static native void nativeInit();  

@/frameworks/base/services/jni/com_android_server_SystemServer.cpp

[java] view plain copy print?
  1. static void android_server_SystemServer_nativeInit(JNIEnv* env, jobject clazz) {  
  2.     char propBuf[PROPERTY_VALUE_MAX];  
  3.     property_get(“system_init.startsensorservice”, propBuf, “1”);  
  4.     if (strcmp(propBuf, “1”) == 0) {  
  5.         // Start the sensor service  
  6.         SensorService::instantiate();
  7.     }
  8. }

可以看出这里只初始化了传感器service,这与之前的代码有所不同,在比较早的Android版本中,服务的初始化分为init1和init2两个过程,其中init主要负责native层service的初始化(SurfaceFlinger、AudioFlinger等),init2负责java层service的初始化。

在main方法*后会调用ServerThread类的initAndLoop来初始化系统服务,这个函数比较长,这里就不复制代码了。

好了,到这里SystemServer的诞生过程就完了

Zygote是什么?

Zygote是什么

    操作系统中,进程实际上是文件到地址空间的映射像。进程将要运行时,由操作系统将其映射到地址空间,完成这项工作的事物本质也应是一个进程,我们称这个进程为孵化进程,那么这个进程怎么收到消息创建新的进程呢?可以推测,在操作系统中应当存在一个全局监听创建新进程消息的监听进程,当它收到消息时,通知孵化进程进行孵化。在安卓中,负责孵化新进程的这个进程叫做Zygote,安卓上其他的APK进程都是由它孵化的。

世界的神

    众所周知,安卓是Linux内核,安卓系统上运行的一切程序都是放在Dalvik虚拟机上的,Zygote也不例外,事实上,它是安卓运行的*个Dalvik虚拟机进程。既然Zygote负责孵化其他的安卓进程,那么它自己是由谁孵化的呢?这个问题就像上帝创造了世界,那上帝是谁创造的呢?上帝一定是由我们这个世界之外,他自己所在的那个“新世界”的神所创造的。如果把安卓系统看做我们所在的这个世界,那么Zygote所在的那个“新世界”就是Linux内核,Zygote就是由Linux内核的“神”创造的,这个“神”就是Linux内核启动的用户级进程init,关于init这里不再讨论。

亚当和夏娃

    上面的比喻将Zygote看做安卓世界的神,事实上,由于操作系统还需要一个监听创建新进程请求的进程,在安卓中,这个进程就是SystemServer,Zygote与负责监听新进程的SystemServer协同合作才能孵化APK进程,所以Zygote更恰当的比喻是亚当。

    就像夏娃是由亚当身体里的一根肋骨创造的一样,SystemServer也是由Zygote孵化出来的*个Dalvik虚拟机进程。SystemServer与Zygote都是一个程序在内存中映射的进程,这个程序就是app_process。

造人的过程

    由于SystemServer与Zygote分属安卓操作系统中不同Davik虚拟机上运行的不同的进程,在安卓上不同进程之间通信是通过Binder机制来实现的(关于Binder的运行机制浅析在这里)。那么SystemServer与Zygote是不是分别有一个在需要孵化新进程时用于通信的Binder呢?就像亚当与夏娃分别有一个***用于造人。很遗憾的是,SystemServer与Zygote并不是通过Binder来进行通信的,而是利用更为原始的Socket传递消息,在SystemServer中有一个Socket客户端,Zygote中的Socket服务端负责接收孵化请求。

    就像亚当和夏娃生出了许多孩子后,可能需要一个保姆来帮他们照顾小孩一样。安卓系统中有那么多运行中的进程,肯定需要有一个管家来帮助他们管理孵化出来的进程,这个管家就是AmS,事实上AmS的主要任务是负责管理Activity,兼职管理安卓系统的内存和运行中的进程。

    下面将Zygote与SystemServer联合孵化新进程的大概流程梳理出来:SystemServer中负责管理进程的AmS利用Socket客户端向Zygote的Socket服务端发送创建新进程命令,Zygote收到命令后为APK创建进程ActivityThread,每个APK进程不仅有自己的数据资源和需要执行的代码,还与其他进程分享Android框架中通用的资源和代码,由于这些资源和代码是大部分APK都需要调用到的,并且比较重量级,所以安卓把它们都放在Zygote进程的空间中,当APK需要的时候就直接到里面去找就行了。

流程图如下:

 %title插图%num

——参考至《Android内核剖析》

递归输出

递归输出
题目 利用递归函数调用方式,将所输入的5个字符,以相反顺序打印出来。
1
程序分析 递归真是蠢方法。
1
def rec(string):
    if len(string)!=1:
        rec(string[1:])
    print(string[0],end=”)
rec(input(‘string here:’))