Android系统架构
Android 系统架构大致分为四层:Linux内核层,系统运行库层,应用框架层,应用层。
- Linux内核层 :为Android设备的各种硬件提供底层驱动。如显示驱动,音频驱动,照相机驱动,蓝牙驱动,WI-FI驱动,电源管理等。
- 系统运行库层:这一层通过C/C++库来为Android系统提供了主要的特性支持。如SQlite库提供了数据库支持,OpenGL|ES提供3D绘图支持,Webkit提供浏览器内核支持等。同样在这一层还有Android运行时库,它主要提供了一些核心库,能允许开发者使用java语言来编写Android程序。另外运行时库中还包含了Dalvik虚拟机(5.0以后改为ART运行环境),它使得每一个Android应用都能运行在独立的进程中,并且拥有一个自己的Dalvik虚拟机实例。对于java虚拟机,Dalvik是专门为移动设备定制的,针对手机的内存、CPU性能有限的情况做了优化。
- 应用框架层:这一层主要提供了构建应用是可能用到的各种API,Android自带的一些核心应用就是使用这些API实现的,开发者也可以使用这些API来构建自己的应用。
- 应用层:所有安装在手机上的应用程序都属于这一层。
在Dalvik下,应用每次运行的时候,字节码都需要通过即时编译器转换为机器码,这会拖慢应用的运行效率,而在ART 环境中,应用在第一次安装的时候,字节码就会预先编译成机器码,使其成为真正的本地应用。
Activity
Activity是一种可以包含用户界面的组件,主要用于和用户交互。
隐式Intent ,它并不明确指出我们想要启动那个活动,而是制定了一系列更为抽象的action和category等信息,然后交由系统分析这个Intent,并帮我们找出合适的活动来启动。
Activity的状态
- 运行状态,当一个个活动位于返回栈栈顶的时候,活动处于运行状态,系统最不愿意回收处于运行状态的活动。
- 暂停状态,当一个活动不在位于栈顶位置,但是依然可见,活动就进入了暂停状态。处于暂停状态的活动依然是完全存活的,系统也不愿回收这种活动。
- 停止状态,当一个活动不再位于栈顶位置,并且完全不可见的时候,就进入了停止状态。系统仍然会为这种活动保存相应的状态和成员变量,但这并不是完全可靠,当其他地方需要内存的时候,处于停止状态的活动可能被系统回收。
- 销毁状态,当一个活动从返回栈中移除后,就变成了销毁状态,系统会最倾向于回收处于这种状态的活动,从而保证手机内存充足。
Activity的生命周期
- onCreate() 在活动第一次被创建的时候调用,应在这个方法中完成初始化操作,比如加载布局,绑定事件。
- onStart() 在活动由不可见变为可见的时候调用。
- onResume() 这个方法在活动准备好和用户交互的时候调用。此时活动一定位于返回栈的栈顶,并处于运行状态。
- onPause() 这个方法在系统准备去启动或者恢复另一个活动的时候调用。我们通常在这个方法中将一些消耗CPU的资源释放掉,以及保存一些关键数据,但这个方法的执行速度一定要快,不然会影响到新的栈顶活动的使用。
- onStop() 这个方法在活动完全不可见的时候调用。和onPause()的主要区别就在于,如果启动的新活动是一个对话框式的活动,那么onPause()方法会得到执行,而onStop()方法不会执行。
- onDestroy()这个方法在活动被销毁之前调用,之后活动的状态变为销毁状态。
- onRestart()这个方法在活动由停止状态变为运行状态之前调用,也就是活动被重新启动了。
Activity的启动模式
standard : 默认的启动模式
singleTop 当Activity的启动模式指定为singleTop,在启动活动时,如果发现返回栈的栈顶已经是该活动,则可以直接使用它,不再创建新的活动实例。
singleTask:当Activity的启动模式指定为singleTask,每次启动该活动的时候系统会在返回栈中检查是否存在该活动的实例,如果发现已经存在,则直接使用该活动的实例,并把这个活动之上的所有活动统统出栈,如果没有发现就会创建一个新的活动实例。这种启动模式会让整个应用中只存在一个活动的实例。
singleInstance:当Activity的启动模式指定为singleInstance,那么系统会启用一个新的返回栈来管理这个活动。用于程序间共享活动实例。 FirstActivity启动SecondActivity(SecondActivity使用singleInstance模式),SecondActivity启动ThirdActivity.然后点击Back键,活动结束顺序是ThirdActivity->FirstActivity->SecondActivity.
Service
Service是Android中实现程序后台运行的解决方案,它非常适合去执行那些不需要和用户交互而且还要求长期运行的任务。服务的运行不依赖于任何用户界面,即使程序被切换到后台,或者用户打开了另外一个应用程序,服务仍然能够保持正常运行。
需要注意的是,服务并不是运行在独立的进程中,而是依赖于创建服务的应用程序进程。当某个应用程序被杀掉的时候,所有依赖于该进程的服务也会停止运行。
另外,服务并不会自动开启线程,所有的代码都是默认运行在主线程当中的。也就是说,我们需要在服务内部手动创建子线程,并在这里执行具体的任务,否则就可能出现主线程被阻塞的情况。
定义一个Service
public class MyService extends Service {
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
- onCreate()方法会在创建服务的时候调用。
- onStartCommand()方法会在服务启动的时候调用。如果我们希望服务一旦启动就立即执行某些操作,可以将逻辑写这个方法里。多次启动服务的时候,这个方法会被调用多次。
- onDestroy()在服务销毁的时候调用,在这里回收那些不再使用的资源。
- onBind()方法,用于绑定服务。
private MyService.DownloadBinder binder;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "onServiceConnected: ");
binder = (MyService.DownloadBinder) service;
binder.startDownload();
binder.onDownloadProgress();
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "onServiceDisconnected: ");
}
};
public void bindService(View view) {
Intent intent = new Intent(this, MyService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
public void unbindService(View view) {
Intent intent = new Intent(this, MyService.class);
unbindService(connection);
}
绑定服务并不会调用Service的onStartCommand()方法。
Intent intent = new Intent(this, MyService.class);
startService(intent);
启动服务,多次启动服务,会导致Service的onStartCommand()方法执行多次。停止服务,只需要调用一次停止服务的方法就可以了。
Intent intent = new Intent(this, MyService.class);
stopService(intent);
如果启动服务startService(intent);以后又绑定了服务bindService(intent, connection, Context.BIND_AUTO_CREATE);,那么得调用stopService(intent);和unbindService(connection);服务的onDestroy方法才会执行。
创建一个前台服务
public class MyService extends Service {
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
Log.e(TAG, "onCreate: ");
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("title")
.setContentText("content text")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
.setContentIntent(pi)
.build();
//让服务变成一个前台服务,并在系统状态栏展示出来
startForeground(1, notification);
}
}
使用IntentService,创建异步的,自动停止的服务。
public class MyIntentService extends IntentService {
private static final String TAG = "MyIntentService";
public MyIntentService() {
super("MyIntentService");
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
//处理具体逻辑
Log.d(TAG, "onHandleIntent: thread id="+Thread.currentThread().getId());
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy: MyIntentService");
}
}
Content Provider
内容提供器主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另外一个程序中的数据,同时还能保证被访问数据的安全性。目前,使用内容提供器是Android实现跨进程共享数据的标准方式。
访问内容提供器中共享的数据
首先,通过Context中的getContentResolver()方法获取 ContentResolver实例。ContentResolver的方法insert,update,query,delete对数据进行增删改查操作。
提供一个内容Uri。内容Uri给内容提供器中的数据提供了唯一标识符,它由两部分组成:authority和path。autuority用于区分不同的应用程序,path则是用于对应用程序中的表作区分的。内容Uri的两种格式
Uri uri=Uri.parse("content://com.hm.app.provider/table1")
表示调用方希望访问com.hm.app这个应用的table1表中的数据。
Uri uri=Uri.parse("content://com.hm.app.provider/table1/1")
表示调用方希望访问com.hm.app这个应用的table1表中id为1的的数据。
内容Uri的书写方式支持通配符
(* :表示匹配任意长度的任意字符。)
一个匹配任意表名的内容Uri可以这样写
content://com.hm.app.provider/*
(# :表示匹配任意长度的任意数字。)
一个能匹配table1表中任意一行数据的内容Uri可以这样写
content://com.hm.app.provider/table1/#
通过 Context的getContentResolver
方法获取ContentResolver实例,并不需要Intent
的介入。
public abstract ContentResolver getContentResolver();
查询操作
Cursor cursor=getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder);
if (cursor!=null){
while (cursor.moveToNext()){
String column1=cursor.getString(cursor.getColumnIndex("column1"));
int column2=cursor.getString(cursor.getColumnIndex("column2"));
}
cursor.close();
}
插入操作
ContentValues values=new ContentValues();
values.put("column1","text");
values.put("column2",1);
getContentResolver().insert(uri,values);
更新操作
ContentValues values=new ContentValues();
values.put("column1","1");
getContentResolver().update(uri,values,"column1 = ? and column2 = ? ",new String[]{"text","1"});
删除操作
getContentResolver().delete(uri, "column2 = ? ", new String[]{"1"});
创建自己的内容提供器
新建一个类MyProvider继承自ContentProvider
public class MyProvider extends ContentProvider {
public MyProvider() {
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return 0;
}
@Override
public String getType(Uri uri) {
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
return null;
}
@Override
public boolean onCreate() {
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
return null;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return 0;
}
}
- onCreate()
初始化内容提供器的时候调用。通常会在这里完成对数据库的创建和升级操作。返回true表示内容提供器初始化成功,false表示失败。注意,只有当ContentProvider尝试访问程序中的数据的时候,内容提供器才会被初始化。
2.query()
从内容提供器中查询数据,uri用于确定查询那张表。projection用于确定查询那些列。selection和selectionArgs参数用于约束查询那些行,sortOrder用于对查询结果排序。查询的结果放在Cursor中返回。
3 insert()
向内容提供器中插入一条数据。uri确定插入的表,插入的参数保存在value参数中。添加完成后返回一个用于表示这条新纪录的URI.
4 update()
更新内容提供器中的数据,uri表示表明,新数据保存在value中,selection和selectionArgsyongyu约束更新那些行,返回受影响的行数。
5 delete()
删除内容提供器中的数据,uri确定表明,selection和selectionArgs参数用于约束删除那些行,返回被删除的行数。
6 getType()
根据传入的uri返回相应的MIME类型。所有的内容提供器都必须提供这个方法,用于获取Uri对象所对应的MIME类型。
借助UriMatcher这个类实现匹配内容Uri的功能
public class MyProvider extends ContentProvider {
public static final int TABLE1_DIR = 0;
public static final int TABLE1_ITEM = 1;
private static UriMatcher uriMatcher;
static {
uriMatcher.addURI("com.brotherd.contentproviderdemo.provider", "table1", TABLE1_DIR);
uriMatcher.addURI("com.brotherd.contentproviderdemo.provider", "table1/#", TABLE1_ITEM);
}
public MyProvider() {
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
switch (uriMatcher.match(uri)) {
case TABLE1_DIR:
//查询table1的数据
break;
case TABLE1_ITEM:
//查询table1的单条数据
break;
}
return null;
...
}
MyProvider 新增了两个静态常量,TABLE1_DIR 表示访问table1中的数据,TABLE1_ITEM表示访问table1中的单条数据。然后在静态代码块里面创建了UriMatcher实例,并调用addURI方法,将期望匹配的Uri格式传进去。当query方法调用的时候,就会通过UriMatcher的match方法匹配传入的Uri对象,我们可以根据匹配结果了解调用方想访问什么数据。
广播接收器 BroadcastReceiver
标准广播:异步执行的广播,在广播发出之后,所有的广播接收器几乎会在同一时刻收到这条广播消息。这种广播效率比较高,但同时意味着它是无法被截断的。
有序广播:同步执行的广播,在广播发出之后,同一时刻只有一个广播接收器能够收到这条广播消息,当这个广播接收器中的逻辑执行完毕后,广播才会继续传递。优先级高的广播接收器先收到广播消息,并且前面的广播接收器可截断正在传递的广播,这样后面的广播接收器就无法收到广播消息了。
注册广播接收器的两种方式:
静态注册(在AndroidManifest.xml中注册)在程序未启动的情况下就可以接收到广播;每次广播事件到来的时候,系统会创建新的BroadcastReceiver实例,并且自动触发他的 onReceive() 方法,onReceive() 方法执行完后,BroadcastReceiver 的实例就会被销毁。
动态注册 (在代码中注册),只会创建一个BroadcastReceiver实例,必须要在程序启动之后才能接收到广播,要记得取消注册。
创建BroadcastReceiver
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//处理具体的逻辑
Log.d(TAG, "onReceive: ");
Toast.makeText(context.getApplicationContext(), "MyReceiver onReceive", Toast.LENGTH_SHORT).show();
}
}
注意: 不要在onReceive方法中添加过多的逻辑或者进行耗时操作,因为在广播接收器onReceive
方法是运行在主线程的,当onReceive方法运行了较长时间而没有结束时,程序就会ANR。因此广播接受器更多的是扮演一种打开程序其他组件的角色,比如创建一条通知,开启一个服务等。
静态注册
<receiver
android:name=".MyReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.brotherd.broadcastdemo.BROADCAST"/>
</intent-filter>
</receiver>
enabled 表示是否启用广播接收器,exported表示是否接受本程序以外的广播。action表示接收的广播。
动态注册
public class MainActivity extends AppCompatActivity {
private IntentFilter intentFilter;
private MyReceiver receiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intentFilter = new IntentFilter("com.brotherd.broadcastdemo.BROADCAST");
receiver = new MyReceiver();
//动态注册广播接收器
registerReceiver(receiver, intentFilter);
}
/**
* 发送广播
*/
public void sendBroadcast(View view) {
Intent intent = new Intent();
intent.setAction("com.brotherd.broadcastdemo.BROADCAST");
sendBroadcast(intent);
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(receiver);
}
}
发送有序广播
Intent intent = new Intent();
intent.setAction("com.brotherd.broadcastdemo.BROADCAST");
sendOrderedBroadcast(intent,null);
如果一个广播接收器接收到有序广播后,可以终止这个广播的传递。
@Override
public void onReceive(Context context, Intent intent) {
//处理具体的逻辑
Log.d(TAG, "onReceive: ");
Toast.makeText(context.getApplicationContext(), "MyReceiver onReceive", Toast.LENGTH_SHORT).show();
//中断广播的传递
abortBroadcast();
}
使用本地广播:本地广播只能在应用程序内部进行传递。本地广播通过一个LocalBroadcastManager来对广播进行管理,并提供了发送广播和注册广播接收器的方法。LocalBroadcastManager发送的广播只有通过LocalBroadcastManager进行注册广播接收器才能接收到广播。
public class MainActivity extends AppCompatActivity {
private IntentFilter intentFilter;
private MyReceiver receiver;
private LocalBroadcastManager manager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intentFilter = new IntentFilter("com.brotherd.broadcastdemo.BROADCAST");
receiver = new MyReceiver();
manager = LocalBroadcastManager.getInstance(this);
manager.registerReceiver(receiver, intentFilter);
}
public void sendBroadcast(View view) {
Intent intent = new Intent();
intent.setAction("com.brotherd.broadcastdemo.BROADCAST");
//使用LocalBroadcastManager 发送广播
manager.sendBroadcast(intent);
}
@Override
protected void onDestroy() {
super.onDestroy();
manager.unregisterReceiver(receiver);
}
}
本地广播的优点
- 广播在应用内传递,不会泄露机密数据
- 其他程序发送的广播无法到达应用内部,不会有安全漏洞
- 发送本地广播比发送全局广播更加高效。
参考链接: