客户端基础知识必备 | 青训营笔记
这是我参与「第四届青训营 」笔记创作活动的第2天
基础组件
Activity
Fragment
Service
Broadcast
ContentProvider
ContentResolver
通信组件
Handler
Binder
Activity
启停活动页面
Activity的启动和结束
从当前页面跳到新页面
startActivity(new Intent(源页面.this,目标页面.class));
从当前页面回到上一个页面,相当于关闭当前页面
finish();
Activity的生命周期
App引入活动的概念而非传统的页面概念,这是有原因的,单从字面意思理解,页面更像是静态的,而 活动更像是动态的。犹如花开花落那般,活动也有从含苞待放到盛开再到凋零的生命过程。每次创建新 的活动页面,自动生成的Java代码都给出了onCreate方法,该方法用于执行活动创建的相关操作,包括 加载XML布局、设置文本视图的初始文字、注册按钮控件的点击监听,等等。onCreate方法所代表的创 建动作,正是一个活动最开始的行为,除了onCreate,活动还有其他几种生命周期行为,它们对应的方 法说明如下:
-
onCreate:创建活动。此时会把页面布局加载进内存,进入了初始状态。 -
onStart:开启活动。此时会把活动页面显示在屏幕上,进入了就绪状态。 -
onResume:恢复活动。此时活动页面进入活跃状态,能够与用户正常交互,例如允许响应用户的 点击动作、允许用户输入文字等。 -
onPause:暂停活动。此时活动页面进入暂停状态(也就是退回就绪状态),无法与用户正常交 互。 -
onStop:停止活动。此时活动页面将不在屏幕上显示。 -
onDestroy:销毁活动。此时回收活动占用的系统资源,把页面从内存中清除掉。 -
onRestart:重启活动。处于停止状态的活动,若想重新开启的话,无须经历onCreate的重复创建 过程,而是走onRestart的重启过程。 -
onNewIntent:重用已有的活动实例。
上述的生命周期方法,涉及复杂的App运行状态,更直观的活动状态切换过程如图4-2所示。
如果一个Activity已经启动过,并且存在当前应用的Activity任务栈中,启动模式为singleTask, singleInstance
或singleTop(此时已在任务栈顶端),那么在此启动或回到这个Activity的时候,不会创建新的实例,也就是不会执
行onCreate方法,而是执行onNewIntent方法。
Activity的启动模式
上一小节提到,从第一个活动跳到第二个活动,接着结束第二个活动就能返回第一个活动,可是为什么 不直接返回桌面呢?这要从Android的内核设计说起了,系统给每个正在运行的App都分配了活动栈,栈 里面容纳着已经创建且尚未销毁的活动信息。鉴于栈是一种先进后出、后进先出的数据结构,故而后面 入栈的活动总是先出栈,假设3个活动的入栈顺序为:活动A→活动B→活动C,则它们的出栈顺序将变 为:活动C→活动B→活动A,可见活动C结束之后会返回活动B,而不是返回活动A或者别的地方。
不过前述的出入栈情况仅是默认的标准模式,实际上Android允许在创建活动时指定该活动的启动模 式,通过启动模式控制活动的出入栈行为。App提供了两种办法用于设置活动页面的启动模式,其一是 修改AndroidManifest.xml,在指定的activity节点添加属性android:launchMode,表示本活动以哪个 启动模式运行。其二是在代码中调用Intent对象的setFlags方法,表明后续打开的活动页面采用该启动标志。
- Standard 启动模式
- SingleTask 启动模式
- SingleTop 启动模式
- SingleInstance 启动模式
在活动之间传递信息
显式Intent和隐式Intent
ntent的中文名 是意图,意思是我想让你干什么,简单地说,就是传递消息。Intent是各个组件之间信息沟通的桥梁, 既能在Activity之间沟通,又能在Activity与Service之间沟通,也能在Activity与Broadcast之间沟通。总 而言之,Intent用于Android各组件之间的通信,它主要完成下列3部分工作:
(1)标明本次通信请求从哪里来、到哪里去、要怎么走。
(2)发起方携带本次通信需要的数据内容,接收方从收到的意图中解析数据。
(3)发起方若想判断接收方的处理结果,意图就要负责让接收方传回应答的数据内容。
- 显式Intent,直接指定来源活动与目标活动,属于精确匹配
-
隐式Intent,没有明确指定要跳转的目标活动,只给出一个动作字符串让系统自动匹配,属于模糊 匹配
通常App不希望向外部暴露活动名称,只给出一个事先定义好的标记串,这样大家约定俗成、按图索骥就好,隐 式Intent便起到了标记过滤作用。这个动作名称标记串,可以是自己定义的动作,也可以是已 有的系统动作。 常见系统动作的取值说明见表4-4。
向下一个Activity发送数据
Intent对象的setData方法只指定到达目标的路径,并非本次通信所携带的参数信息,真 正的参数信息存放在Extras中。Intent重载了很多种putExtra方法传递各种类型的参数,包括整型、双 精度型、字符串等基本数据类型,甚至Serializable这样的序列化结构。只是调用putExtra方法显然不好 管理,像送快递一样大小包裹随便扔,不但找起来不方便,丢了也难以知道。所以Android引入了 Bundle概念,可以把Bundle理解为超市的寄包柜或快递收件柜,大小包裹由Bundle统一存取,方便又 安全。
Bundle内部用于存放消息的数据结构是Map映射,既可添加或删除元素,还可判断元素是否存在。开发 者若要把Bundle数据全部打包好,只需调用一次意图对象的putExtras方法;若要把Bundle数据全部取 出来,也只需调用一次意图对象的getExtras方法。Bundle对象操作各类型数据的读写方法说明见表4- 5。
向上一个Activity返回数据
数据传递经常是相互的,上一个页面不但把请求数据发送到下一个页面,有时候还要处理下一个页面的 应答数据,所谓应答发生在下一个页面返回到上一个页面之际。如果只把请求数据发送到下一个页面, 上一个页面调用startActivity方法即可;如果还要处理下一个页面的应答数据,此时就得分多步处理
为活动补充附加消息
利用资源文件配置字符串
要改变参数值时要改代码,改代码两个弊端:
(1)代码文件那么多,每个文件又有许多行代码,一下子还真不容易找到修改的地方。
(2)每次改动代码都得重新编译,让Android Studio编译的功夫也稍微费点时间。
因此,对于可能要手工变动的参数值通常放入配置文件
<String name="weather_str">晴天</string>
setText(getString(R.string.weather_str))
利用元数据传递配置信息
给应用页面注册快捷方式
Fragment
Fragment基本概念
它是什么鬼,有什么用?
Fragment是Android3.0后引入的一个新的API,他出现的初衷是为了适应大屏幕的平板电脑, 当然现在他仍然是平板APP UI设计的宠儿,而且我们普通手机开发也会加入这个Fragment, 我们可以把他看成一个小型的Activity,又称Activity片段!想想,如果一个很大的界面,我们 就一个布局,写起界面来会有多麻烦,而且如果组件多的话是管理起来也很麻烦!而使用Fragment 我们可以把屏幕划分成几块,然后进行分组,进行一个模块化的管理!从而可以更加方便的在 运行过程中动态地更新Activity的用户界面!另外Fragment并不能单独使用,他需要嵌套在Activity 中使用,尽管他拥有自己的生命周期,但是还是会受到宿主Activity的生命周期的影响,比如Activity 被destory销毁了,他也会跟着销毁!
Fragment的生命周期图
创建一个Fragment
静态加载Fragment
示例代码:
Step 1: 定义Fragment的布局,就是fragment显示内容的
Step 2: 自定义一个Fragment类,需要继承Fragment或者他的子类,重写onCreateView()方法 在该方法中调用:inflater.inflate()方法加载Fragment的布局文件,接着返回加载的view对象
public class Fragmentone extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment1, container,false);
return view;
}
}
Step 3: 在需要加载Fragment的Activity对应的布局文件中添加fragment的标签, 记住,name属性是全限定类名哦,就是要包含Fragment的包名,如:
<fragment
android:id="@+id/fragment1"
android:name="com.jay.example.fragmentdemo.Fragmentone"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
Step 4: Activity在onCreate( )方法中调用setContentView()加载布局文件即可!
动态加载Fragment
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Display dis = getWindowManager().getDefaultDisplay();
if(dis.getWidth() > dis.getHeight())
{
Fragment1 f1 = new Fragment1();
getFragmentManager().beginTransaction().replace(R.id.LinearLayout1, f1).commit();
}
else
{
Fragment2 f2 = new Fragment2();
getFragmentManager().beginTransaction().replace(R.id.LinearLayout1, f2).commit();
}
}
}