了解Android四大组件

Activity

用途:Activity是一个应用程序组件,其所有操作都与用户密切相关,它提供一个屏幕,在此进行用户交互从而完成某项任务。(是用户操作的可视化界面;它为用户提供了一个完成操作指令的窗口)在一个android应用中,一个Activity通常就是一个单独的屏幕,它上面可以显示一些控件也可以监听并处理用户的事件做出响应。(可以通过setContentView(View)来显示指定控件。)Activity之间通过Intent进行通信。


四种基本状态:

一、Active/Running
一个新 Activity 启动入栈后,它显示在屏幕最前端,处理是处于栈的最顶端(Activity栈顶),此时它处于可见并可和用户交互的激活状态。
二、Paused
当 Activity失去焦点, 被一个新的非全屏的Activity 或者一个透明的Activity 被放置在栈顶,此时的状态叫做暂停状态(Paused)。此时它依然与窗口管理器保持连接,Activity依然保持活力(保持所有的状态,成员信息,和窗口管理器保持连接),但是在系统内存极端低下的时候将被强行终止掉。所以它仍然可见,但已经失去了焦点故不可与用户进行交互。
三、 Stopped
如果一个Activity被另外的Activity完全覆盖掉,叫做停止状态(Stopped)。它依然保持所有状态和成员信息,但是它不再可见,所以它的窗口被隐藏,当系统内存需要被用在其他地方的时候,Stopped的Activity将被强行终止掉。
四、Killed
如果一个Activity是Paused或者Stopped状态,系统可以将该Activity从内存中删除,Android系统采用两种方式进行删除,要么要求该Activity结束,要么直接终止它的进程。当该Activity再次显示给用户时,它必须重新开始和重置前面的状态。

状态的转换

这里写图片描述

此处引用:
https://baike.baidu.com/item/activity/7304419?fr=aladdin


Activity的生命周期和相关操作

了解Activity栈

Android 是通过一种 Activity 栈的方式来管理 Activity 的,一个 Activity 的实例的状态决定它在栈中的位置。处于前台的 Activity 总是在栈的顶端,当前台的 Activity 因为异常或其它原因被销毁时,处于栈第二层的 Activity 将被激活,上浮到栈顶。当新的 Activity 启动入栈时,原 Activity 会被压入到栈的第二层。一个 Activity 在栈中的位置变化反映了它在不同状态间的转换。Activity 的状态与它在栈中的位置关系如下图所示:
这里写图片描述

生命周期

通过图例初了解:
这里写图片描述


七大生命周期函数
onCreate : 该方法是在Activity被创建时回调,它是生命周期第一个调用的方法,我们在创建Activity时一般都需要重写该方法,然后在该方法中做一些初始化的操作,如通过setContentView设置界面布局的资源,初始化所需要的组件信息等。

onStart : 此方法被回调时表示Activity正在启动,此时Activity已处于可见状态,只是还没有在前台显示,因此无法与用户进行交互。可以简单理解为Activity已显示而我们无法看见摆了。

onResume : 当此方法回调时,则说明Activity已在前台可见,可与用户交互了(处于前面所说的Active/Running形态),onResume方法与onStart的相同点是两者都表示Activity可见,只不过onStart回调时Activity还是后台无法与用户交互,而onResume则已显示在前台,可与用户交互。当然从流程图,我们也可以看出当Activity停止后(onPause方法和onStop方法被调用),重新回到前台时也会调用onResume方法,因此我们也可以在onResume方法中初始化一些资源,比如重新初始化在onPause或者onStop方法中释放的资源。

onPause : 此方法被回调时则表示Activity正在停止(Paused形态),一般情况下onStop方法会紧接着被回调。但通过流程图我们还可以看到一种情况是onPause方法执行后直接执行了onResume方法,这属于比较极端的现象了,这可能是用户操作使当前Activity退居后台后又迅速地再回到到当前的Activity,此时onResume方法就会被回调。当然,在onPause方法中我们可以做一些数据存储或者动画停止或者资源回收的操作,但是不能太耗时,因为这可能会影响到新的Activity的显示——onPause方法执行完成后,新Activity的onResume方法才会被执行。

onStop : 一般在onPause方法执行完成直接执行,表示Activity即将停止或者完全被覆盖(Stopped形态),此时Activity不可见,仅在后台运行。同样地,在onStop方法可以做一些资源释放的操作(不能太耗时)。

onRestart :表示Activity正在重新启动,当Activity由不可见变为可见状态时,该方法被回调。这种情况一般是用户打开了一个新的Activity时,当前的Activity就会被暂停(onPause和onStop被执行了),接着又回到当前Activity页面时,onRestart方法就会被回调。

onDestroy :此时Activity正在被销毁,也是生命周期最后一个执行的方法,一般我们可以在此方法中做一些回收工作和最终的资源释放。

七大生命周期函数引用自
https://blog.csdn.net/javazejian/article/details/51932554


代码实例

package com.example.kaixuan.myapplication;

import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar; import android.view.View;
import android.view.Menu; import android.view.MenuItem;

import android.util.Log;// 关键是这个包要被引用,其余都是本机默认的。

public class MainActivity extends AppCompatActivity {

final String TAG = "--MainActivity--";
 // 定义Log.d(TAG, "-----onCreate-----");中的TAG;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Log.d(TAG, "-----onCreate-----");//在onCreate方法中插入此输出语句。
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
    fab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                    .setAction("Action", null).show();
        }
    });
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.menu_main, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();

    //noinspection SimplifiableIfStatement
    if (id == R.id.action_settings) {
        return true;
    }

    return super.onOptionsItemSelected(item);
}
// 上面为本机默认代码,无需修改,下面是测试生命周期时方法的重写。
@Override
public void onStart() {
    super.onStart();
    Log.d(TAG, "------onStart-------");//为了显示给我们看。
}
@Override
public void onRestart() {
    super.onRestart();
    Log.d(TAG, "------onRestart-------");
}
@Override
public void onResume() {
    super.onResume();
    Log.d(TAG, "------onResume-------");
}
@Override
public void onPause() {
    super.onPause();
    Log.d(TAG, "------onPause-------");
}
@Override
public void onStop() {
    super.onStop();
    Log.d(TAG, "------onStop-------");
}
@Override
public void onDestroy() {
    super.onDestroy();
    Log.d(TAG, "------onDestroy-------");
} }

在MainActivity.java中编写上述代码,按倒三角号在真机运行,真机成功运行此小程序。
这里写图片描述

此时logcat的显示:
这里写图片描述

然后我按下主屏幕键,返回主屏幕,注意:此时此程序还在我的收集后台运行,此时logcat的显示:
这里写图片描述

最后,我退出小程序,返回小程序占用的内存空间,此时logcat的显示:
这里写图片描述


Service

Service简介

一个Service是一个长期可以在后台执行(当然不需要提供UI)的应用组件。其它组件可以启动service,即使切换到另一个应用,该service仍然可以在后台执行。另外,其它组件可以绑定一个service进行交互,甚至可以进行进程间通信(interprocess communication,IPC)。例如,服务可以处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序交互,而所有这一切均可在后台进行。

  • 正如activity一样,service也必须在AndroidManifest.xml中进行注册。
  • service不是单独的进程,除非特别指定,否则它在应用程序的进程中。
  • service不是线程,但是它经常开启一个线程处理任务。

一个service本质上可以分为两种:
Started
即启动方式的service。当一个组件通过startService()方法启动service时,该service为”started”。一旦service启动,则无论启动它的组件是否被销毁,该service都能独立运行。通常,这种方式的service执行一些简单的操作,且不带有返回值。例如,它能够下载/上传文件,当操作完成时,该service需要停止。
Bound
即绑定方式的service。当组件通过bindService()方法绑定一个service时,该service为绑定service。绑定service允许组件和绑定service进行交互,例如发送请求,获得结果,甚至跨进程通信(IPC)。一个绑定service一旦被绑定,该service就会处于运行状态。多个组件可以绑定一个service,但只有所有的组件都解绑时,该service才销毁。

为了创建一个service,我们必须实现一个Service的子类(或Service的存在的子类)。通常,需要覆盖一些父类的回调方法,以此实现service生命周期的关键部分,同时如果需要的话,还要提供组件绑定它的机制(后面有详细介绍绑定的过程)。我们应该覆盖的主要方法如下:

onStartCommand() 
当一个组件通过startService()启动一个service时,系统会回调该方法。
一旦该方法执行,service就会在后台无限制地执行。
因此,你必须负责对该service的停止(通过调用stopSelf()stopService())。
如果只是想绑定service,则不需要实现该方法。 
每次startService()都会执行该方法。
onBind() 
当另一个组件想要通过bindService()绑定service时,系统会回调该方法。
该方法需要实现一个接口,通过返回IBinder对象实现用户通信。
若不需要绑定service则返回null即可,否则必须实现。
onCreate()service第一次被创建时,系统会调用该方法。
该方法在上述两个方法之前调用,且只会执行一次,用来一次性的操作。
若service处于运行状态,则系统不会回调该方法。
onDestroy()service不再被使用,准备销毁时,系统调用该方法销毁service。
这是service最后被调用的方法,通常用于释放资源。

当组件通过startService()方式启动service时,系统就会回调onStartCommand()方法,然后直到该service方法调用stopSelf()结束自己或另一个组件调用stopService()方法结束该service,它才会停止并被销毁。
当一个组件通过bindService()方式创建service时(系统不会调用onStartCommand()),那么只要该组件绑定它,它就一直处于运行状态。一旦service被所有的组件解绑,那么系统就会销毁该service。

有关系统杀死Service的一些事
系统会在内存不足时强制杀死service,以保证当前获得焦点的activity能够获得系统资源。若绑定service的activity处于运行状态,则不太可能销毁该service,同时若该service被声明为前台service(run in the foreground,startForegroud()设置为前台service),则它最不可能被杀死。否则,started service运行时间越长,被销毁的概率越大(运行时间越长所在后台任务中的位置越低,越容易被销毁)。因此,如果我们需要启动service的话,最好要设置好如何通过系统重新启动该service。如果系统销毁了service,系统通过onStartCommand()方法的返回值判断是否要重新启动该service。

Service的生命周期

这里写图片描述

下面为官方对于Service的描述

服务的整个生命周期从调用 onCreate() 开始起,到 onDestroy() 返回时结束。
与 Activity 类似,服务也在onCreate() 中完成初始设置,并在 onDestroy() 中释放所有剩余资源。例如,音乐播放服务可以在 onCreate()中创建用于播放音乐的线程,然后在 onDestroy() 中停止该线程
无论服务是通过 startService() 还是bindService() 创建,都会为所有服务调用 onCreate() 和 onDestroy() 方法。
服务的有效生命周期从调用 onStartCommand() 或 onBind() 方法开始。每种方法均有 Intent对象,该对象分别传递到 startService() 或 bindService()。
对于启动服务,有效生命周期与整个生命周期同时结束(即便是在 onStartCommand()返回之后,服务仍然处于活动状态)。
对于绑定服务,有效生命周期在 onUnbind() 返回时结束。

推荐一篇关于Service的全面且优秀解读

Service的参考网站:https://blog.csdn.net/wangyongge85/article/details/46873203#commentBox
https://blog.csdn.net/javazejian/article/details/52709857


BroadcastReceiver

在Android系统中,广播体现在方方面面,例如当开机完成后系统会产生一条广播,接收到这条广播就能实现开机启动服务的功能;当网络状态改变时系统会产生一条广播,接收到这条广播就能及时地做出提示和保存数据等操作;当电池电量改变时,系统会产生一条广播,接收到这条广播就能在电量低时告知用户及时保存进度,等等。

1.同一app内部的同一组件内的消息通信(单个或多个线程之间);
2.同一app内部的不同组件之间的消息通信(单个进程);
3.同一app具有多个进程的不同组件之间的消息通信;
4.不同app之间的组件之间消息通信;
5.Android系统在特定情况下与App之间的消息通信。

其作用:监听 / 接收 应用 App 发出的广播消息,并 做出响应

Android广播的两个角色: 广播发送者 广播接收者

具体实现流程要点粗略概括如下:

  1. 广播接收者BroadcastReceiver通过Binder机制向AMS(Activity Manager Service)进行注册;
  2. 广播发送者通过binder机制向AMS发送广播;
  3. AMS查找符合相应条件(IntentFilter/Permission等)的BroadcastReceiver,将广播发送到BroadcastReceiver(一般情况下是Activity)相应的消息循环队列中;
  4. 消息循环执行拿到此广播,回调BroadcastReceiver中的onReceive()方法。

广播的两种注册方式

静态注册:

<receiver android:enabled=["true" | "false"] 
//此broadcastReceiver能否接收其他App的发出的广播 
//默认值是由receiver中有无intent-filter决定的:
如果有intent-filter,默认值为true,否则为false 
android:exported=["true" | "false"]
android:icon="drawable resource" 
android:label="string resource" 
//继承BroadcastReceiver子类的类名 
android:name=".mBroadcastReceiver" 
//具有相应权限的广播发送者发送的广播才能被此BroadcastReceiver所接收; 
android:permission="string" 
//BroadcastReceiver运行所处的进程 
//默认为app的进程,可以指定独立的进程 
//注:Android四大基本组件都可以通过此属性指定自己的独立进程 
android:process="string" > 

//用于指定此广播接收器将接收的广播类型
 //本示例中给出的是用于接收网络状态改变时发出的广播 
 <intent-filter> 
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
  </intent-filter>
</receiver>

  • 注册示例
<receiver 
//此广播接收者类是mBroadcastReceiver 
android:name=".mBroadcastReceiver" > 
//用于接收网络状态改变时发出的广播 
<intent-filter>
 <action android:name="android.net.conn.CONNECTIVITY_CHANGE" /> 
 </intent-filter>
</receiver>

当此 App首次启动时,系统会自动实例化mBroadcastReceiver类,并注册到系统中。

动态注册

// 选择在Activity生命周期方法中的onResume()中注册 
@Override protected void onResume(){ 
    super.onResume();

 // 1. 实例化BroadcastReceiver子类 & IntentFilter 
mBroadcastReceiver mBroadcastReceiver = new mBroadcastReceiver();
IntentFilter intentFilter = new IntentFilter();

 // 2. 设置接收广播的类型 
IntentFilter.addAction(android.net.conn.CONNECTIVITY_CHANGE); 

// 3. 动态注册:调用Context的registerReceiver()方法 
registerReceiver(mBroadcastReceiver, intentFilter); 
}

 // 注册广播后,要在相应位置记得销毁广播 
 // 即在onPause() 中unregisterReceiver(mBroadcastReceiver)
// 当此Activity实例化时,会动态将MyBroadcastReceiver注册到系统中 
// 当此Activity销毁时,动态注册的MyBroadcastReceiver将不再接收到相应的广播。 

@Override protected void onPause() { 
super.onPause(); 
//销毁在onResume()方法中的广播
 unregisterReceiver(mBroadcastReceiver); 
    }
}

广播发送及广播类型

经常说”发送广播“和”接收“,表面上看广播作为Android广播机制中的实体,实际上这一实体本身是并不是以所谓的”广播“对象存在的,而是以”意图“(Intent)去表示。定义广播的定义过程,实际就是相应广播”意图“的定义过程,然后通过广播发送者将此”意图“发送出去。被相应的BroadcastReceiver接收后将会回调onReceive()函数。

下段代码片段显示的是一个普通广播的定义过程,并发送出去。其中setAction(..)对应于BroadcastReceiver中的intentFilter中的action。

1 Intent intent = new Intent();
2 intent.setAction(BROADCAST_ACTION);
3 intent.putExtra("name", "qqyumidi");
4 sendBroadcast(intent);

根据广播的发送方式,可以将其分为以下几种类型:

1.Normal Broadcast:普通广播
2.System Broadcast: 系统广播
3.Ordered broadcast:有序广播
4.Sticky Broadcast:粘性广播(在 android 5.0/api 21中deprecated,不再推荐使用,相应的还有粘性有序广播,同样已经deprecated)
5.Local Broadcast:App应用内广播

下面分别总结下各种类型的发送方式及其特点。

1).Normal Broadcast:普通广播
此处将普通广播界定为:开发者自己定义的intent,以context.sendBroadcast_”AsUser”(intent,…)形式。
具体可以使用的方法有:
sendBroadcast(intent)
sendBroadcast(intent,receiverPermission)
endBroadcastAsUser(intent,userHandler)
sendBroadcastAsUser(intent,userHandler,receiverPermission)
普通广播会被注册了的相应的感兴趣(intent-filter匹配)接收,且顺序是无序的。如果发送广播时有相应的权限要求,BroadCastReceiver如果想要接收此广播,也需要有相应的权限。

2).System Broadcast: 系统广播
Android系统中内置了多个系统广播,只要涉及到手机的基本操作,基本上都会发出相应的系统广播。如:开启启动,网络状态改变,拍照,屏幕关闭与开启,点亮不足等等。每个系统广播都具有特定的intent-filter,其中主要包括具体的action,系统广播发出后,将被相应的BroadcastReceiver接收。系统广播在系统内部当特定事件发生时,有系统自动发出。

3)Ordered broadcast:有序广播
有序广播的有序广播中的“有序”是针对广播接收者而言的,指的是发送出去的广播被BroadcastReceiver按照先后循序接收。有序广播的定义过程与普通广播无异,只是其的主要发送方式变为:sendOrderedBroadcast(intent,
receiverPermission, …)。 对于有序广播,其主要特点总结如下:
1>多个具当前已经注册且有效的BroadcastReceiver接收有序广播时,是按照先后顺序接收的,先后顺序判定标准遵循为:将当前系统中所有有效的动态注册和静态注册的BroadcastReceiver按照priority属性值从大到小排序,对于具有相同的priority的动态广播和静态广播,动态广播会排在前面。
2>先接收的BroadcastReceiver可以对此有序广播进行截断,使后面的BroadcastReceiver不再接收到此广播,也可以对广播进行修改,使后面的BroadcastReceiver接收到广播后解析得到错误的参数值。当然,一般情况下,不建议对有序广播进行此类操作,尤其是针对系统中的有序广播。

4)Sticky Broadcast:粘性广播(在 android 5.0/api
21中deprecated,不再推荐使用,相应的还有粘性有序广播,同样已经deprecated)。
既然已经deprecated,此处不再多做总结。

5)Local Broadcast:App应用内广播(此处的App应用以App应用进程为界)
由前文阐述可知,Android中的广播可以跨进程甚至跨App直接通信,且注册是exported对于有intent-filter的情况下默认值是true,由此将可能出现安全隐患如下:
1.其他App可能会针对性的发出与当前App intent-filter相匹配的广播,由此导致当前App不断接收到广播并处理;
2.其他App可以注册与当前App一致的intent-filter用于接收广播,获取广播具体信息。

无论哪种情形,这些安全隐患都确实是存在的。由此,最常见的增加安全性的方案是:

  1. 对于同一App内部发送和接收广播,将exported属性人为设置成false,使得非本App内部发出的此广播不被接收;
  2. 在广播发送和接收时,都增加上相应的permission,用于权限验证;
  3. 发送广播时,指定特定广播接收器所在的包名,具体是通过intent.setPackage(packageName)指定在,这样此广播将只会发送到此包中的App内与之相匹配的有效广播接收器中。

App应用内广播可以理解成一种局部广播的形式,广播的发送者和接收者都同属于一个App。实际的业务需求中,App应用内广播确实可能需要用到。同时,之所以使用应用内广播时,而不是使用全局广播的形式,更多的考虑到的是Android广播机制中的安全性问题。
相比于全局广播,App应用内广播优势体现在:
1.安全性更高;
2.更加高效。

BroadcastReceiver参考:
https://www.cnblogs.com/lwbqqyumidi/p/4168017.html
https://www.jianshu.com/p/ca3d87a4cdf3


ContentProvider

作用

进程间 进行数据交互 & 共享,即跨进程通信

具体使用

统一资源标识符(URI)

定义:Uniform Resource Identifier,即统一资源标识符 作用:唯一标识 ContentProvider &其中的数据外界进程通过 URI 找到对应的ContentProvider & 其中的数据,再进行数据操作。

URI分为 系统预置 & 自定义,分别对应系统内置的数据(如通讯录、日程表等等)和自定义数据库

这里写图片描述

MIME数据类型

作用:

指定某个扩展名的文件用某种应用程序来打开。如指定.html文件采用text应用程序打开、指定.pdf文件采用flash应用程序打开

  • ContentProvider根据 URI 返回MIME类型
  • 每种MIME类型 由2部分组成 = 类型 + 子类型(MIME类型是 一个 包含2部分的字符串)

  • MIME类型有2种形式:

// 形式1:单条记录  
vnd.android.cursor.item/自定义
// 形式2:多条记录(集合)
vnd.android.cursor.dir/自定义 

// 注:
  // 1. vnd:表示父类型和子类型具有非标准的、特定的形式。
  // 2. 父类型已固定好(即不能更改),只能区别是单条还是多条记录
  // 3. 子类型可自定义

ContentProvider类

  1. ContentProvider主要以 表格的形式 组织数据
  2. 同时也支持文件数据,只是表格形式用得比较多
  3. 每个表格中包含多张表,每张表包含行 & 列,分别对应记录 & 字段
  4. 同数据库

主要方法

<-- 4个核心方法 -->
  public Uri insert(Uri uri, ContentValues values) 
  // 外部进程向 ContentProvider 中添加数据

  public int delete(Uri uri, String selection, String[] selectionArgs) 
  // 外部进程 删除 ContentProvider 中的数据

  public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
  // 外部进程更新 ContentProvider 中的数据

  public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,  String sortOrder)  
  // 外部应用 获取 ContentProvider 中的数据

// 注:
  // 1. 上述4个方法由外部进程回调,并运行在ContentProvider进程的Binder线程池中(不是主线程)
 // 2. 存在多线程并发访问,需要实现线程同步
   // a. 若ContentProvider的数据存储方式是使用SQLite & 一个,则不需要,因为SQLite内部实现好了线程同步,若是多个SQLite则需要,因为SQL对象之间无法进行线程同步
  // b. 若ContentProvider的数据存储方式是内存,则需要自己实现线程同步

<-- 2个其他方法 -->
public boolean onCreate() 
// ContentProvider创建后 或 打开系统后其它进程第一次访问该ContentProvider时 由系统进行调用
// 注:运行在ContentProvider进程的主线程,故不能做耗时操作

public String getType(Uri uri)
// 得到数据类型,即返回当前 Url 所代表数据的MIME类型

参考:
https://www.2cto.com/kf/201707/661749.html


打完收工!!

猜你喜欢

转载自blog.csdn.net/STU17xkx/article/details/80933824