Android基础知识整理(一) 四大组件

文章目录

一、四大组件

Activity

生命周期:

Android基础知识整理(一) 四大组件

Intent与序列化:

IntentAndroid Toast与Intent使用_z18223345669的博客-CSDN博客

序列化:

目的:

(1)永久的保存对象数据(将对象数据保存在文件当中,或者是磁盘中

(2)通过序列化操作将对象数据在网络上进行传输(由于网络传输是以字节流的方式对数据进行传输的.因此序列化的目的是将对象数据转换成字节流的形式)

(3)将对象数据在进程之间进行传递(Activity之间传递对象数据时,需要在当前的Activity中对对象数据进行序列化操作.在另一个Activity中需要进行反序列化操作讲数据取出)

两种实现:

1.Implements Serializable 接口 (声明一下即可)

2.Implements Parcelable 接口(不仅仅需要声明,还需要实现内部的相应方法)

启动模式和FLAG:

启动模式一共有 4 种,分别是 standardsingleTopsingleTasksingleInstance,可以在 AndroidManifest.xml 中通过给标签指定 android:launchMode属性来选择启动模式。

standard 是活动默认的启动模式,在不进行显式指定的情况下,所有活动都会自动使用这种启动模式。每当启动一个新的活动,它就会在返回栈中入栈,并处于栈顶的位置。对于使用 standard 模式的活动,系统不会在乎这个活动是否已经在返回栈中存在,每次启动都会创建该活动的一个新的实例。

singleTop在启动活动时如果发现返回栈的栈顶已经是该活动,则认为可以直接使用它,不会再创建新的活动实例。

singleTask每次启动该活动时系统首先会在返回栈中检查是否存在该活动的实例,如果发现已经存在则直接使用该实例,并把在这个活动之上的所有活动统统出栈,如果没有发现就会创建一个新的活动实例。

singleInstance 模式的活动会启用一个新的返回栈来管理这个活动。程序中有一个活动是允许其他程序调用的,实现其他程序和我们的程序可以共享这个活动的实例。

Intent的常用Flag参数:

FLAG_ACTIVITY_CLEAR_TOP:例如现在的栈情况为:A B C D 。D此时通过intent跳转到B,如果这个intent添加FLAG_ACTIVITY_CLEAR_TOP 标记,则栈情况变为:A B。如果没有添加这个标记,则栈情况将会变成:A B C D B。也就是说,如果添加了FLAG_ACTIVITY_CLEAR_TOP标记,并且目标Activity在栈中已经存在,则将会把位于该目标activity之上的activity从栈中弹出销毁。这跟上面把B的Launch mode设置成singleTask类似。

FLAG_ACTIVITY_NEW_TASK:例如现在栈1的情况是:A B C。C通过intent跳转到D,并且这个intent添加了FLAG_ACTIVITY_NEW_TASK 标记,如果D这个Activity在Manifest.xml中的声明中添加了Task affinity,并且和栈1的affinity不同,系统首先会查找有没有和D的Task affinity相同的task栈存在,如果有存在,将D压入那个栈,如果不存在则会新建一个D的affinity的栈将其压入。如果D的Task affinity默认没有设置,或者和栈1的affinity相同,则会把其压入栈1,变成:A B C D,这样就和不加FLAG_ACTIVITY_NEW_TASK 标记效果是一样的了。 注意如果试图从非activity的非正常途径启动一个activity,比如从一个service中启动一个activity,则intent比如要添加FLAG_ACTIVITY_NEW_TASK 标记。

FLAG_ACTIVITY_NO_HISTORY:例如现在栈情况为:A B C。C通过intent跳转到D,这个intent添加FLAG_ACTIVITY_NO_HISTORY标志,则此时界面显示D的内容,但是它并不会压入栈中。如果按返回键,返回到C,栈的情况还是:A B C。如果此时D中又跳转到E,栈的情况变为:A B C E,此时按返回键会回到C,因为D根本就没有被压入栈中。

FLAG_ACTIVITY_SINGLE_TOP:和上面Activity的 Launch mode的singleTop类似。如果某个intent添加了这个标志,并且这个intent的目标activity就是栈顶的activity,那么将不会新建一个实例压入栈中。

问题:

1.什么是 Activity?

四大组件之一 , 一般的 , 一个用户交互界面对应一个 activity ,setContentView() 显示布局

activity 是 Context 的子类 , 同时实现了 window.callback 和 keyevent.callback, 可以处理与窗体用户交互的事件 。

2.请描述一下 Activity 生命周期。

生命周期描述的是一个类 从创建 (new 出来 ) 到死亡 ( 垃圾回收 ) 的过程中会执行的方法

在这个过程中 会针对不同的生命阶段会调用不同的方法Activity 从创建到销毁有多种状态,从一种状态到另一种状态时会激发相应的回调方法,这些回调方法包括: oncreate() ondestroy() onstop() onstart() onresume() onpause() 其实这些方法都是两两对应的,

onCreate 创建与 onDestroy 销毁;onStart 可见与 onStop 不可见; onResume可编辑(即焦点)与 onPause;

onRestart 方法在 Activity 被 onStop 后,但是没有被 onDestroy ,在再次启动此 Activity 时就调用 onRestart (而不再调用 onCreate )方法;如果被 onDestroy 了,则是调用 onCreate 方法。

3.两个 Activity 之间跳转时必然会执行的是哪几个方法。

一般情况比如说有两个 activity, 分别叫 A,B , 当在 A 里面激活 B 组件的时候 , A 会调用onPause() 方法 , 然后 B 调用 onCreate() ,onStart(), OnResume() , 这个时候 B覆盖了窗体 , A会调用 onStop() 方法 .

如果 B呢 是个透明的 , 或者是对话框的样式 , 就不会调用 onStop() 方法 . 对话框的话也不会调用 Onpause() 方法 . 因为对话框也是Activity 的一部分 . 不等于失去焦点 .

4.横竖屏切换时候 Activity 的生命周期。

不设置 Activity 的 android:configChanges 时,切屏会重新调用各个生命周期默认首先销毁当前 activity, 然后重新加载

onPause onStop onDestory onCreate onStart onResume

5.你后台的 Activity 被系统回收怎么办?如果后台的 Activity 由于某原因被系统回收了,如何在被系统回收之前保存当前状态?

除了在栈顶的 activity, 其他的 activity 都有可能在内存不足的时候被系统回收 , 一个activity 越处于栈底 , 被回收的可能性越大

// 当系统回收时会调用下面的方法 可以将应用的状态保存在这个方法中

protected void onSaveInstanceState(Bundle outState);

// 恢复现场

public void onCreate(Bundle savedInstanceState) ;

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

退出 activity 直接调用 finish () 方法 .

用户点击 back 键 就是退出一个 activity 退出 activity 会执行 onDestroy() 方法 .

1、抛异常强制退出

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

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

4、递归退出

7.两个 Activity 之间怎么传递数据?

1)八种基本数据类型可以通过 Intent 传递数据

2)Application 全局里面存放 对象 , 自己去实现自己的 application 的这个类 , 继承系统的application , 每个 activity 都可以取到

3)让对象实现 implements Serializable 接口把对象存放到文件上 让类实现 Serializable 接口 , 然后可以通过 ObjectOutputStream 对象输出流获取

Service

服务(Service)是 Android 中实现程序后台运行的解决方案,它非常适合去执行那些不需要和用户交互而且还要求长期运行的任务。

服务的运行不依赖于任何用户界面,即使程序被切换到后台,或者用户打开了另外一个应用程序,服务仍然能够保持正常运行。

服务并不是运行在一个独立的进程当中的,而是依赖于创建服务时所在的应用程序进程。当某个应用程序进程被杀掉时,所有依赖于该进程的服务也会停止运行。

服务并不会自动开启线程,所有的代码都是默认运行在主线程当中的。也就是说,我们需要在服务的内部手动创建子线程,并在这里执行具体的任务,否则就有可能出现主线程被阻塞住的情况。

start/stop

其中 onCreate()方法会在服务创建的时候调用,onStartCommand()方法会在每次服务启动的时候调用,onDestroy()方法会在服务销毁的时候调用。

通常情况下,如果我们希望服务一旦启动就立刻去执行某个动作,就可以将逻辑写在onStartCommand()方法里。而当服务销毁时,我们又应该在 onDestroy()方法中去回收那些不再使用的资源。

另外需要注意,每一个服务都需要在 AndroidManifest.xml 文件中进行注册才能生效,这是 Android 四大组件共有的特点
Android基础知识整理(一) 四大组件

bind/unbind

首先创建了一个 ServiceConnection 的匿名类,在里面重写了 onServiceConnected()方法和 onServiceDisconnected()方法,这两个方法分别会在活动与服务成功绑定以及活动与服务的连接断开的时候调用。

建出了一个 Intent 对象,然后调用 bindService()方法将 MainActivity 和 MyService 进行绑定。bindService()方法接收 3 个参数,第一个参数就是刚刚构建出的 Intent 对象,第二个参数是前面创建出的 ServiceConnection 的实例,第三个参数则是一个标志位.

switch (v.getId()) { 
 ... 
 case R.id.bind_service: 
	Intent bindIntent = new Intent(this, MyService.class); 
	bindService(bindIntent, connection, BIND_AUTO_CREATE); // 绑定服务
 	break; 
 case R.id.unbind_service: 
	unbindService(connection); // 解绑服务
 	break; 
 default: 
 	break; 
 }
跨进程,binder/aidl

IPC:Inter-Process Communication,进程间的通信或跨进程通信。简单点理解,一个应用可以存在多个进程,但需要数据交换就必须用IPC;或者是二个应用之间的数据交换。

Binder:Binder是Android的一个类,它实现了IBinder接口。从IPC角度来说,Binder是Android中的一种跨进程通信方式。通过这个Binder对象,客户端就可以获取服务端提供的服务或数据,这里的服务包括普通服务和基于AIDL的服务。

AIDL:Android Interface Definition Language 用于生成可以在Android设备上两个进程之间进行进程间通信(interprocess communication,IPC)的代码。

IntentService

服务中的代码都是默认运行在主线程当中的,如果直接在服务里去处理一些耗时的逻辑,就很容易出现 ANR(Application Not Responding)的情况。

IntentService简单地创建一个异步的、会自动停止的服务.

public class MyIntentService extends IntentService { 
     public MyIntentService() { 
     super("MyIntentService"); // 调用父类的有参构造函数
 } 
 @Override 
 protected void onHandleIntent(Intent intent) { 
     // 打印当前线程的 id 
     Log.d("MyIntentService", "Thread id is " + Thread.currentThread(). getId()); 
 } 
 @Override 
     public void onDestroy() { 
     super.onDestroy(); 
     Log.d("MyIntentService", "onDestroy executed"); 
 } 
}

首先要提供一个无参的构造函数,并且必须在其内部调用父类的有参构造函数。然后要在子类中去实现 onHandleIntent()这个抽象方法,在这个方法中可以去处理一些具体的逻辑,而且不用担心 ANR 的问题,因为这个方法已经是在子线程中运行的了。

问题:

1.怎么让在启动一个 Activity 就启动一个 service ?

在 activity 的 onCreate() 方法里面 startService()

2.Activity 怎么和 service 绑定,怎么在 activity 中启动自己对应的 service ?

bindService () 把 service 与调用者绑定 , 如果调用者被销毁 , service 会销毁

我们可以使用 service 里面的方法bindService(). 让 activity 能够访问到 service 里面的方法

Activity 通过 bindService 来启动一个 Service. 在 Service 中会调用 OnBind 方法返回一个带有 Service 方法引用的 IBinder 对象 . 在 Activity 中用这个对象就可以调用 Service 中的方法了

3.什么是 Service 以及描述下它的生命周期。 Service 有哪些启动方法,有什么区别,怎样停用 Service ?

在 Service 的生命周期中,只有 onCreate, onStart, onDestroy, onBind 和 onUnbind 。

通常有两种方式启动一个 Service, 他们对 Service 生命周期的影响是不一样的。

1)通过 startService

Service 会经历 onCreate 到 onStart ,然后处于运行状态, stopService 的时候调用onDestroy 方法。如果是调用者自己直接退出而没有调用 stopService 的话, Service 会一直在后台运行。

2)通过 bindService

Service 会运行 onCreate ,然后是调用 onBind , 这个时候调用者和 Service 绑定在一起。调用者退出了, Srevice 就会调用 onUnbind->onDestroyed 方法

4.什么是 IntentService ?有何优点?

普通的 service , 默认运行在 ui main 主线程

IntentService是sdk 给我们提供的方便的 , 带有异步处理的 service 类,

异步处理的方法 OnHandleIntent() 处理耗时的操作

5.什么时候使用 Service ?

1)拥有 service 的进程具有较高的优先级,那么用于当前 service 的进程相当于前台进程以避免被 killed

2)Service 的特点可以让他在后台一直运行 , 可以在 service 里面创建线程去完成耗时的操作

3)Broadcast receiver 捕获到一个事件之后 , 可以起一个 service 来完成一个耗时的操作.

6.请描述一下 Intent 和 Intent Filter

Android 中通过 Intent 对象来表示一条消息, 一个 Intent 对象不仅包含有这个消息的目的地, 还可以包含消息的内容

Intent filter: 用来注册 Activity 、 Service 和 Broadcast Receiver 具有能在某种数据上执行一个动作的能力。使用 Intent Filter ,应用程序组件告诉 Android ,它们能为其它程序的组件的动作请求提供服务,包括同一个程序的组件、本地的或第三方的应用程序。

这个分拣系统通过 3 个参数来识别

Action: 动作 Intent.ation_view

Data: 数据 uri uri mime

Category : 附加信息

7.Intent 传递数据时,可以传递哪些类型数据?

1)一般的基本数据类型 Intent .putextra() intent.getStringextra();

2)数据的 URI intent.setData() intent.getData();

8.说说 Activity ,Intent ,Service 是什么关系

intent 去激活组件 , 传递数据.

说自己项目中有这样一个网络更新的功能 , 显示界面就用的 activity, 后台有个service 每隔半小时都去访问下服务器获取更新的数据…开启服务用的是 intent 来开启

BroadcastReceiver

Android 中的每个应用程序都可以对自己感兴趣的广播进行注册,这样该程序就只会接收到自己所关心的广播内容,这些广播可能是来自于系统的,也可能是来自于其他应用程序的。Android 提供了一套完整的 API,允许应用程序*地发送和接收广播。

普通和有序广播

标准广播(Normal broadcasts)是一种完全异步执行的广播,在广播发出之后,所有的广播接收器几乎都会在同一时刻接收到这条广播消息,因此它们之间没有任何先后顺序可言。这种广播的效率会比较高,但同时也意味着它是无法被截断的。
Android基础知识整理(一) 四大组件

有序广播(Ordered broadcasts)则是一种同步执行的广播,在广播发出之后,同一时刻只会有一个广播接收器能够收到这条广播息,当这个广播接收器中的逻辑执行完毕后,广播才会继续传递。所以此时的广播接收器是有先后顺序的,优先级高的广播接收器就可以先收到广播消息,并且前面的广播接收器还可以截断正在传递的广播,这样后面的广播接收器就无法收到广播消息了。

Android基础知识整理(一) 四大组件

系统广播

Android 内置了很多系统级别的广播,我们可以在应用程序中通过监听这些广播来得到各种系统的状态信息。比如手机开机完成后会发出一条广播,电池的电量发生变化会发出一条广播,时间或时区发生改变也会发出一条广播,等等。

1.动态注册

只需要新建一个类,让它继承自 BroadcastReceiver,并重写父类的 onReceive()方法就行了。这样当有广播到来时,onReceive()方法就会得到执行,具体的逻辑就可以在这个方法中处理。

2.静态注册

编写类继承 BroadcastReceiver,重写onReceive()方法,AndroidManifest.xml文件中添加标签。

LocalBroadcastManager

发出的广播只能够在应用程序的内部进行传递,并且广播接收器也只能接收来自本应用程序发出的广播

localBroadcastManager = LocalBroadcastManager.getInstance(this); // 获取实例

Intent intent = new Intent("com.example.broadcasttest.LOCAL_BROADCAST"); 
localBroadcastManager.sendBroadcast(intent); // 发送本地广播

intentFilter.addAction("com.example.broadcasttest.LOCAL_BROADCAST"); 
localReceiver = new LocalReceiver(); 
localBroadcastManager.registerReceiver(localReceiver, intentFilter); // 注册本地广播监听器

基本上就和动态注册广播接收器以及发送广播的代码是一样的。只不过现在首先是通过 LocalBroadcastManager的 getInstance()方法得到了它的一个实例,然后在注册广播接收器的时候调用的是 LocalBroadcastManager 的registerReceiver()方法,在发送广播的时候调用的是LocalBroadcastManager的sendBroadcast()方法。

问题:

ContentProvide

内容提供器(Content Provider)主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另一个程序中的数据,同时还能保证被访数据的安全性。

内容提供器的用法一般有两种,一种是使用现有的内容提供器来读取和操作相应程序中的数据,另一种是创建自己的内容提供器给我们程序的数据提供外部访问接口。

增删改查

对于每一个应用程序来说,如果想要访问内容提供器*享的数据,就一定要借助 ContentResolver 类,可以通过 Context 中的 getContentResolver()方法获取到该类的实例。ContentResolver 中提供了一系列的方法用于对数据进行 CRUD 操作,其中 insert()方法用于添加数据,update()方法用于更新数据,delete()方法用于删除数据,query()方法用于查询数据。

不同于 SQLiteDatabase,ContentResolver 中的增删改查方法都是不接收表名参数的,而是使用一个 Uri 参数代替,这个参数被称为内容 URI。内容 URI 给内容提供器中的数据建立了唯一标识符,它主要由两部分组成:authoritypathauthority 是用于对不同的应用程序做区分的,一般为了避免冲突,都会采用程序包名的方式来进行命名。比如某个程序的包名是 com.example. app,那么该程序对应的 authority 就可以命名为 com.example.app. provider。path 则是用于对同一应用程序中不同的表做区分的,通常都会添加到 authority 的后面。比如某个程序的数据库里存在两张表:table1 和 table2,这时就可以将 path 分别命名为/table1 和/table2,然后把 authority 和 path进行组合,内容 URI 就变成了 com.example.app.provider/table1 和 com.example.app.provider/table2。不过,目前还很难辨认出这两个字符串就是两个内容 URI,我们还需要在字符串的头部加上协议声明。因此,内容 URI 最标准的格式写法如下:

content://com.example.app.provider/table1

content://com.example.app.provider/table2

在得到了内容 URI 字符串之后,我们还需要将它解析成 Uri 对象才可以作为参数传入

Uri uri = Uri.parse("content://com.example.app.provider/table1") 

只需要调用 Uri.parse()方法,就可以将内容 URI 字符串解析成 Uri 对象了。

现在我们就可以使用这个 Uri 对象来查询 table1 表中的数据,代码如下所示:

Cursor cursor = getContentResolver().query( uri, projection, selection, selectionArgs, sortOrder); 

查询完成后返回的仍然是一个 Cursor 对象,这时我们就可以将数据从 Cursor 对象中逐个读取出来了。读取的思路仍然是通过移动游标的位置来遍历 Cursor 的所有行,然后再取出每一行中相应列的数据

if (cursor != null) { 
	 while (cursor.moveToNext()) { 
		 String column1 = cursor.getString(cursor.getColumnIndex("column1")); 
		 int column2 = cursor.getInt(cursor.getColumnIndex("column2")); 
	 } 
	 cursor.close(); 
}
getContentResolver().insert(uri, values);
getContentResolver().update(uri, values, "column1 = ? and column2 = ?", new String[] {"text", "1"});
getContentResolver().delete(uri, "column2 = ?", new String[] { "1" });
ContenetObserver

ContentObserver——内容观察者,目的是观察(捕捉)特定Uri引起的数据库的变化,继而做一些相应的处理,它类似于数据库技术中的触发器(Trigger),当ContentObserver所观察的Uri发生变化时,便会触发它。触发器分为表触发器、行触发器,相应地ContentObserver也分为“表ContentObserver、“行ContentObserver,当然这是与它所监听的Uri MIME Type有关的。

自定义ContentProvider

新建一个类去继承 ContentProvider 的方式来创建一个自己的内容提供器。ContentProvider 类中有 6 个抽象方法,我们在使用子类继承它的时候,需要将这 6 个方法全部重写。

public class MyProvider extends ContentProvider { 
     @Override 
     public boolean onCreate() { 
         return false; 
     } 
     @Override 
     public Cursor query(Uri uri, String[] projection, String selection, String[] 
         selectionArgs, String sortOrder) { 
         return null; 
     } 
     @Override 
     public Uri insert(Uri uri, ContentValues values) { 
     	return null; 
     } 
     @Override 
     public int update(Uri uri, ContentValues values, String selection, String[] 
     selectionArgs) { 
     	return 0; 
     } 
     @Override 
     public int delete(Uri uri, String selection, String[] selectionArgs) { 
     	return 0; 
     } 
     @Override 
     public String getType(Uri uri) { 
     	return null; 
     } 
}
问题:

1.为什么要用 ContentProvider ?它和 sql 的实现上有什么差别?

ContentProvider 屏蔽了数据存储的细节 , 内部实现对用户完全透明 , 用户只需要关心操作数据的uri 就可以了, ContentProvider 可以实现不同 app之间 共享。
Sql 也有增删改查的方法, 但是 sql 只能查询本应用下的数据库。
而 ContentProvider 还可以去增删改查本地文件. xml 文件的读取等。

2.ContentProvider 是如何实现数据共享的:

把自己的数据通过 uri 的形式共享出去 android 系统下 不同程序 数据默认是不能共享访问 需要去实现一个类去继承 ContentProvider

3.多个进程同时调用一个 ContentProvider 的 query 获取数据,ContentPrvoider 是如何反应的呢?

一个 ContentProvider 可以接受来自另外一个进程的数据请求。
尽管 ContentResolver 与 ContentProvider 类隐藏了实现细节,但是 ContentProvider 所提供的 query(),insert(),delete(),update() 都是在 ContentProvider 进程的线程池中被调用执行的,而不是进程的主线程中。
这个线程池是有 Binder 创建和维护的,其实使用的就是每个应用进程中的 Binder 线程池。

上一篇:一个简单智能停车APP——主功能界面


下一篇:Android——Intent