用途: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
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的显示:
一个Service是一个长期可以在后台执行(当然不需要提供UI)的应用组件。其它组件可以启动service,即使切换到另一个应用,该service仍然可以在后台执行。另外,其它组件可以绑定一个service进行交互,甚至可以进行进程间通信(interprocess communication,IPC)。例如,服务可以处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序交互,而所有这一切均可在后台进行。
一个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的描述
服务的整个生命周期从调用 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
在Android系统中,广播体现在方方面面,例如当开机完成后系统会产生一条广播,接收到这条广播就能实现开机启动服务的功能;当网络状态改变时系统会产生一条广播,接收到这条广播就能及时地做出提示和保存数据等操作;当电池电量改变时,系统会产生一条广播,接收到这条广播就能在电量低时告知用户及时保存进度,等等。
1.同一app内部的同一组件内的消息通信(单个或多个线程之间);
2.同一app内部的不同组件之间的消息通信(单个进程);
3.同一app具有多个进程的不同组件之间的消息通信;
4.不同app之间的组件之间消息通信;
5.Android系统在特定情况下与App之间的消息通信。
其作用:监听 / 接收 应用 App 发出的广播消息,并 做出响应
Android广播的两个角色: 广播发送者 广播接收者
具体实现流程要点粗略概括如下:
广播的两种注册方式
静态注册:
"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" >
//用于指定此广播接收器将接收的广播类型
//本示例中给出的是用于接收网络状态改变时发出的广播
filter>
"android.net.conn.CONNECTIVITY_CHANGE" />
filter>
<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用于接收广播,获取广播具体信息。
无论哪种情形,这些安全隐患都确实是存在的。由此,最常见的增加安全性的方案是:
App应用内广播可以理解成一种局部广播的形式,广播的发送者和接收者都同属于一个App。实际的业务需求中,App应用内广播确实可能需要用到。同时,之所以使用应用内广播时,而不是使用全局广播的形式,更多的考虑到的是Android广播机制中的安全性问题。
相比于全局广播,App应用内广播优势体现在:
1.安全性更高;
2.更加高效。
BroadcastReceiver参考:
https://www.cnblogs.com/lwbqqyumidi/p/4168017.html
https://www.jianshu.com/p/ca3d87a4cdf3
作用
进程间 进行数据交互 & 共享,即跨进程通信
统一资源标识符(URI)
定义:Uniform Resource Identifier,即统一资源标识符 作用:唯一标识 ContentProvider &其中的数据外界进程通过 URI 找到对应的ContentProvider & 其中的数据,再进行数据操作。
URI分为 系统预置 & 自定义,分别对应系统内置的数据(如通讯录、日程表等等)和自定义数据库
MIME数据类型
作用:
指定某个扩展名的文件用某种应用程序来打开。如指定.html文件采用text应用程序打开、指定.pdf文件采用flash应用程序打开
每种MIME类型 由2部分组成 = 类型 + 子类型(MIME类型是 一个 包含2部分的字符串)
MIME类型有2种形式:
// 形式1:单条记录
vnd.android.cursor.item/自定义
// 形式2:多条记录(集合)
vnd.android.cursor.dir/自定义
// 注:
// 1. vnd:表示父类型和子类型具有非标准的、特定的形式。
// 2. 父类型已固定好(即不能更改),只能区别是单条还是多条记录
// 3. 子类型可自定义
主要方法
<-- 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
打完收工!!