android 组件生命周期

一.Activity
http://xiechengfa.iteye.com/category/146425 博客
首先看一下Android api中所提供的Activity生命周期图(不明白的,可以看完整篇文章,在回头看一下这个图,你会明白的):

android 组件生命周期_第1张图片

Activity其实是继承了ApplicationContext这个类,我们可以重写以下方法,如下代码:
public class Activity extends ApplicationContext {  
       protected void onCreate(Bundle savedInstanceState);  
       protected void onStart();     
       protected void onRestart();  
       protected void onResume();  
       protected void onPause();   
       protected void onStop();  
       protected void onDestroy();  
   }  


为了便于大家更好的理解,我简单的写了一个Demo,不明白Activity周期的朋友们,可以亲手实践一下,大家按照我的步骤来。
  第一步:新建一个Android工程,我这里命名为ActivityDemo.
  第二步:修改ActivityDemo.java(我这里重新写了以上的七种方法,主要用Log打印),代码如下:

package com.tutor.activitydemo;  
import android.app.Activity;  
import android.os.Bundle;  
import android.util.Log;  
public class ActivityDemo extends Activity {  
    private static final String TAG = "ActivityDemo";  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  
        Log.e(TAG, "start onCreate~~~");  
    }  
    @Override  
    protected void onStart() {  
        super.onStart();  
        Log.e(TAG, "start onStart~~~");  
    }  
    @Override  
    protected void onRestart() {  
        super.onRestart();  
        Log.e(TAG, "start onRestart~~~");  
    }  
    @Override  
    protected void onResume() {  
        super.onResume();  
        Log.e(TAG, "start onResume~~~");  
    }  
    @Override  
    protected void onPause() {  
        super.onPause();  
        Log.e(TAG, "start onPause~~~");  
    }  
    @Override  
    protected void onStop() {  
        super.onStop();  
        Log.e(TAG, "start onStop~~~");  
    }  
    @Override  
    protected void onDestroy() {  
        super.onDestroy();  
        Log.e(TAG, "start onDestroy~~~");  
    }  
}  

第三步:运行上述工程,效果图如下(没什么特别的):

android 组件生命周期_第2张图片


我们打开应用时先后执行了onCreate()->onStart()->onResume三个方法,看一下LogCat视窗如下:

android 组件生命周期_第3张图片


  当我们打开应用程序时,比如浏览器,我正在浏览NBA新闻,看到一半时,我突然想听歌,这时候我们会选择按HOME键,然后去打开音乐应用程序,而当我们按HOME的时候,Activity先后执行了onPause()->onStop()这两个方法,这时候应用程序并没有销毁。如下图所示:

android 组件生命周期_第4张图片
而当我们再次启动ActivityDemo应用程序时,则先后分别执行了onRestart()->onStart()->onResume()三个方法,如下图所示:


android 组件生命周期_第5张图片


这里我们会引出一个问题,当我们按HOME键,然后再进入ActivityDemo应用时,我们的应用的状态应该是和按HOME键之前的状态是一样的,同样为了方便理解,在这里我将ActivityDemo的代码作一些修改,就是增加一个EditText。
  第四步:修改main.xml布局文件(增加了一个EditText),代码如下:
 <?xml version="1.0" encoding="utf-8"?>  
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
     android:orientation="vertical"  
     android:layout_width="fill_parent"  
     android:layout_height="fill_parent"  
     >  
 <TextView    
     android:layout_width="fill_parent"   
     android:layout_height="wrap_content"   
     android:text="@string/hello"  
     />  
 <EditText  
     android:id="@+id/editText"  
     android:layout_width="fill_parent"  
     android:layout_height="wrap_content"  
 />  
 </LinearLayout>  


第五步:然后其他不变,运行ActivityDemo程序,在EditText里输入如"Frankie"字符串(如下图:)

android 组件生命周期_第6张图片
这时候,大家可以按一下HOME键,然后再次启动ActivityDemo应用程序,这时候EditText里并没有我们输入的"Frankie"字样,如下图:

android 组件生命周期_第7张图片
这显然不能称得一个合格的应用程序,所以我们需要在Activity几个方法里自己实现,如下第六步所示:
第六步修改ActivityDemo.java代码如下:

package com.tutor.activitydemo;  
 import android.app.Activity;  
 import android.os.Bundle;  
 import android.util.Log;  
 import android.widget.EditText;  
 public class ActivityDemo extends Activity {  
     private static final String TAG = "ActivityDemo";  
     private EditText mEditText;  
     //定义一个String 类型用来存取我们EditText输入的值  
     private String mString;  
     public void onCreate(Bundle savedInstanceState) {  
         super.onCreate(savedInstanceState);  
         setContentView(R.layout.main);  
         mEditText = (EditText)findViewById(R.id.editText);  
         Log.e(TAG, "start onCreate~~~");  
     }  
     @Override  
     protected void onStart() {  
         super.onStart();  
         Log.e(TAG, "start onStart~~~");  
     }  
     //当按HOME键时,然后再次启动应用时,我们要恢复先前状态  
     @Override  
     protected void onRestart() {  
         super.onRestart();  
         mEditText.setText(mString);  
         Log.e(TAG, "start onRestart~~~");  
     }   
     @Override  
     protected void onResume() {  
         super.onResume();  
         Log.e(TAG, "start onResume~~~");  
     }  
     //当我们按HOME键时,我在onPause方法里,将输入的值赋给mString  
     @Override  
     protected void onPause() {  
         super.onPause();  
         mString = mEditText.getText().toString();  
         Log.e(TAG, "start onPause~~~");  
     }   
     @Override  
     protected void onStop() {  
         super.onStop();  
         Log.e(TAG, "start onStop~~~");  
     }  
       
     @Override  
     protected void onDestroy() {  
         super.onDestroy();  
         Log.e(TAG, "start onDestroy~~~");  
     }  
 }  

第七步:重新运行ActivityDemo程序,重复第五步操作,当我们按HOME键时,再次启动应用程序时,EditText里有上次输入的"Frankie"字样,如下图如示:

android 组件生命周期_第8张图片
OK,大功基本告成,这时候大家可以在回上面看一下Activity生命周期图,我想大家应该完全了解了Activity的生命周期了.

二.Service
一.基础知识
服务一般分为两种:

1:本地服务, Local Service 用于应用程序内部。在Service可以调用Context.startService()启动,调用Context.stopService()结束。在内部可以调用Service.stopSelf() 或 Service.stopSelfResult()来自己停止。无论调用了多少次startService(),都只需调用一次stopService()来停止。

2:远程服务, Remote Service 用于android系统内部的应用程序之间。可以定义接口并把接口暴露出来,以便其他应用进行操作。客户端建立到服务对象的连接,并通过那个连接来调用服务。调用Context.bindService()方法建立连接,并启动,以调用 Context.unbindService()关闭连接。多个客户端可以绑定至同一个服务。如果服务此时还没有加载,bindService()会先加载它。
提供给可被其他应用复用,比如定义一个天气预报服务,提供与其他应用调用即可。

那么先来看Service的生命周期吧:如图:


android 组件生命周期_第9张图片
context.startService() ->onCreate()- >onStart()->Service running--调用context.stopService() ->onDestroy()

context.bindService()->onCreate()->onBind()->Service running--调用>onUnbind() -> onDestroy()
从上诉可以知道分别对应本地的,以及远程的,也对应不同的方式启动这个服务。

二.实战
我们可以定义一个本地服务继承Service,然后在这个服务里播放媒体播放器或者记录地理位置变化。通常有时候我们的Service要与Activity交互,那么可以可以定义一个内部类,返回这个Service,当然我们要考虑到如果是以绑定方式启动服务,那么内部类可以定义为继承Binder,然后返回本地服务,具体代码如下。
package com.dongzi;

import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

public class LocalService extends Service {

    private static final String TAG = "LocalService"; 
    private IBinder binder=new LocalService.LocalBinder();
    
    @Override
    public IBinder onBind(Intent intent) {
         
        return binder;
    }
    MediaPlayer mediaPlayer=null;
    @Override 
    public void onCreate() { 
            Log.i(TAG, "onCreate"); 
            //这里可以启动媒体播放器
           // if(mediaPlayer==null)
           //     mediaPlayer=MediaPlayer.create(this, uri);
            super.onCreate(); 
    } 

    @Override 
    public void onStart(Intent intent, int startId) { 
            Log.i(TAG, "onStart"); 
            super.onStart(intent, startId); 
    } 

    @Override 
    public int onStartCommand(Intent intent, int flags, int startId) { 
          Log.i(TAG, "onStartCommand"); 
        return START_STICKY;
    }

    
    
    @Override 
    public void onDestroy() { 
            Log.i(TAG, "onDestroy"); 
            super.onDestroy(); 
    } 

    
    //定义内容类继承Binder
    public class LocalBinder extends Binder{
        //返回本地服务
        LocalService getService(){
            return LocalService.this;
        }
    }
    
    
}


我们可以从上面知道

//定义内容类继承Binder
    public class LocalBinder extends Binder{
        //返回本地服务
        LocalService getService(){
            return LocalService.this;
        }
    }

可以返回这个服务,然后activity可以通过服务调用服务的方法了。

那么如何启动服务呢?从上面基础知识中,我们知道有2种方法,如下:
//启动服务
  
 private void startCustomService(){
         Intent intent=new Intent(this,LocalService.class);
         startService(intent);
    }

第2种绑定方式:
LocalService localService=null;
    //用bindService方法启动服务
    private void BinderService(){
         Intent intent=new Intent(this,LocalService.class);
         bindService(intent, new ServiceConnection(){
            @Override
            public void onServiceConnected(ComponentName componentName, IBinder binder) {
                //调用bindService方法启动服务时候,如果服务需要与activity交互,
                //则通过onBind方法返回IBinder并返回当前本地服务
                localService=((LocalService.LocalBinder)binder).getService();
                //这里可以提示用户,或者调用服务的某些方法
            }

            @Override
            public void onServiceDisconnected(ComponentName componentName) {
                localService=null;
                //这里可以提示用户
            }     
         }, Context.BIND_AUTO_CREATE);
    }

在绑定服务的时候,需要一个服务连接对象,ServiceConnection,服务一旦连接,就会调用onServiceConnected方法,我们可以在这个方法里面返回我们的本地服务对象,具体看代码;而在服务断开时候会调用onServiceDisconnected方法,我们可以清理一些服务资源。

三、ContentProvider的概念
  ContentProvider:为存储和获取数据提供统一的接口。可以在不同的应用程序之间共享数据。Android已经为常见的一些数据提供了默认的ContentProvider
  1、ContentProvider使用表的形式来组织数据
   无论数据的来源是什么,ContentProvider都会认为是一种表,然后把数据组织成表格
  2、ContentProvider提供的方法
   query:查询
   insert:插入
   update:更新
   delete:删除
   getType:得到数据类型
   onCreate:创建数据时调用的回调函数
  3、每个ContentProvider都有一个公共的URI,这个URI用于表示这个ContentProvider所提供的数据。Android所提供的ContentProvider都存放在android.provider包当中
  二、ContentProvider的内部原理
  自定义一个ContentProvider,来实现内部原理
  步骤:
  1、定义一个CONTENT_URI常量(里面的字符串必须是唯一)
  Public static final Uri CONTENT_URI = Uri.parse("content://com.WangWeiDa.MyContentprovider");
  如果有子表,URI为:
  Public static final Uri CONTENT_URI = Uri.parse("content://com.WangWeiDa.MyContentProvider/users");
  2、定义一个类,继承ContentProvider
  Public class MyContentProvider extends ContentProvider
  3、实现ContentProvider的所有方法(query、insert、update、delete、getType、onCreate)
 
 
package com.WangWeiDa.cp;
  
  import java.util.HashMap;
  
  import com.WangWeiDa.cp.MyContentProviderMetaData.UserTableMetaData;
  import com.WangWeiDa.data.DatabaseHelp;
  
  import android.content.ContentProvider;
  import android.content.ContentUris;
  import android.content.ContentValues;
  import android.content.UriMatcher;
  import android.database.Cursor;
  import android.database.sqlite.SQLiteDatabase;
  import android.database.sqlite.SQLiteQueryBuilder;
  import android.net.Uri;
  import android.text.TextUtils;
  
  public class MyContentProvider extends ContentProvider {
   //访问表的所有列
   public static final int INCOMING_USER_COLLECTION = 1;
   //访问单独的列
   public static final int INCOMING_USER_SINGLE = 2;
   //操作URI的类
   public static final UriMatcher uriMatcher;
   //为UriMatcher添加自定义的URI
   static{
   uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
   uriMatcher.addURI(MyContentProviderMetaData.AUTHORITIES,"/user",
   INCOMING_USER_COLLECTION);
   uriMatcher.addURI(MyContentProviderMetaData.AUTHORITIES,"/user/#",
   INCOMING_USER_SINGLE);
  
   }
   private DatabaseHelp dh;
   //为数据库表字段起别名
   public static HashMap userProjectionMap;
   static
   {
   userProjectionMap = new HashMap();
   userProjectionMap.put(UserTableMetaData._ID,UserTableMetaData._ID);
   userProjectionMap.put(UserTableMetaData.USER_NAME, UserTableMetaData.USER_NAME);
   }
   /**
   * 删除表数据
   */
   @Override
   public int delete(Uri uri, String selection, String[] selectionArgs) {
   System.out.println("delete");
   //得到一个可写的数据库
   SQLiteDatabase db = dh.getWritableDatabase();
   //执行删除,得到删除的行数
   int count = db.delete(UserTableMetaData.TABLE_NAME, selection, selectionArgs);
   return count;
   }
   /**
   * 数据库访问类型
   */
   @Override
   public String getType(Uri uri) {
   System.out.println("getType");
   //根据用户请求,得到数据类型
   switch (uriMatcher.match(uri)) {
   case INCOMING_USER_COLLECTION:
   return MyContentProviderMetaData.UserTableMetaData.CONTENT_TYPE;
   case INCOMING_USER_SINGLE:
   return MyContentProviderMetaData.UserTableMetaData.CONTENT_TYPE_ITEM;
   default:
   throw new IllegalArgumentException("UnKnown URI"+uri);
   }
   }
   /**
   * 插入数据
   */
   @Override
   public Uri insert(Uri uri, ContentValues values) {
   //得到一个可写的数据库
   SQLiteDatabase db = dh.getWritableDatabase();
   //向指定的表插入数据,得到返回的Id
   long rowId = db.insert(UserTableMetaData.TABLE_NAME, null, values);
   if(rowId > 0){//判断插入是否执行成功
   //如果添加成功,利用新添加的Id和
   Uri insertedUserUri = ContentUris.withAppendedId(UserTableMetaData.CONTENT_URI, rowId);
   //通知监听器,数据已经改变
   getContext().getContentResolver().notifyChange(insertedUserUri, null);
   return insertedUserUri;
   }
   return uri;
   }
   /**
   * 创建ContentProvider时调用的回调函数
   */
   @Override
   public boolean onCreate() {
   System.out.println("onCreate");
   //得到数据库帮助类
   dh = new DatabaseHelp(getContext(),MyContentProviderMetaData.DATABASE_NAME);
   return false;
   }
   /**
   * 查询数据库
   */
   @Override
   public Cursor query(Uri uri, String[] projection, String selection,
   String[] selectionArgs, String sortOrder) {
   //创建一个执行查询的Sqlite
   SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
   //判断用户请求,查询所有还是单个
   switch(uriMatcher.match(uri)){
   case INCOMING_USER_COLLECTION:
   //设置要查询的表名
   qb.setTables(UserTableMetaData.TABLE_NAME);
   //设置表字段的别名
   qb.setProjectionMap(userProjectionMap);
   break;
   case INCOMING_USER_SINGLE:
   qb.setTables(UserTableMetaData.TABLE_NAME);
   qb.setProjectionMap(userProjectionMap);
   //追加条件,getPathSegments()得到用户请求的Uri地址截取的数组,get(1)得到去掉地址中/以后的第二个元素
   qb.appendWhere(UserTableMetaData._ID + "=" + uri.getPathSegments().get(1));
   break;
   }
   //设置排序
   String orderBy;
   if(TextUtils.isEmpty(sortOrder)){
   orderBy = UserTableMetaData.DEFAULT_SORT_ORDER;
   }
   else{
   orderBy = sortOrder;
   }
   //得到一个可读的数据库
   SQLiteDatabase db = dh.getReadableDatabase();
   //执行查询,把输入传入
   Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy);
   //设置监听
   c.setNotificationUri(getContext().getContentResolver(), uri);
   return c;
  
   }
   /**
   * 更新数据库
   */
   @Override
   public int update(Uri uri, ContentValues values, String selection,
   String[] selectionArgs) {
   System.out.println("update");
   //得到一个可写的数据库
   SQLiteDatabase db = dh.getWritableDatabase();
   //执行更新语句,得到更新的条数
   int count = db.update(UserTableMetaData.TABLE_NAME, values, selection, selectionArgs);
   return count;
   }
  
  }

  
  4、在AndroidMinifest.xml中进行声明
   android:name=".cp.MyContentProvider"
   android:authorities="com.WangWeiDa.cp.MyContentProvider"
   />

  **为ContentProvider提供一个常量类MyContentProviderMetaData.java
 
 package com.WangWeiDa.cp;
  
  import android.net.Uri;
  import android.provider.BaseColumns;
  
  public class MyContentProviderMetaData {
   //URI的指定,此处的字符串必须和声明的authorities一致
   public static final String AUTHORITIES = "com.wangweida.cp.MyContentProvider";
   //数据库名称
   public static final String DATABASE_NAME = "myContentProvider.db";
   //数据库的版本
   public static final int DATABASE_VERSION = 1;
   //表名 
   public static final String USERS_TABLE_NAME = "user";
   
   public static final class UserTableMetaData implements BaseColumns{
   //表名
   public static final String TABLE_NAME = "user";
   //访问该ContentProvider的URI
   public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITIES + "/user");
   //该ContentProvider所返回的数据类型的定义
   public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.myprovider.user";
   public static final String CONTENT_TYPE_ITEM = "vnd.android.cursor.item/vnd.myprovider.user";
   //列名
   public static final String USER_NAME = "name";
   //默认的排序方法
   public static final String DEFAULT_SORT_ORDER = "_id desc";
   }
  
  }




四.BroadcastReceiver
用接收短信举例:

第一种方式 :
实现
public class MyBroadcastReceiver extends BroadcastReceiver {

    // action 名称
    String SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED" ;

    public void onReceive(Context context, Intent intent) {

       if (intent.getAction().equals( SMS_RECEIVED )) {
           // 相关处理 : 地域变换、电量不足、来电来信;
       }
    }
}
系统注册:在 AndroidManifest.xml 中注册
< receiver android:name = ".MyBroadcastReceiver" >
           < intent-filter android:priority = "1000" >
            
< action android:name = " android.provider.Telephony.SMS_RECEIVED" />
           </ intent-filter >
       </ receiver > 当然了需要权限 :

< uses-permission android:name = "android.permission.RECEIVE_SMS" />
< uses-permission android:name = "android.permission.SEND_SMS" />


第二种方式:

// 广播接收者 - 广播的接收
private BroadcastReceiver myBroadcastReceiver = new BroadcastReceiver() {

       @Override
       public void onReceive(Context context, Intent intent) {
           // 相关处理,如收短信,监听电量变化信息
       }

    };

代码中注册:
IntentFilter intentFilter = new IntentFilter( "android.provider.Telephony.SMS_RECEIVED " );
registerReceiver( mBatteryInfoReceiver , intentFilter);

生命周期只有十秒左右,如果在 onReceive() 内做超过十秒内的事情,就会报错 。

每次广播到来时 , 会重新创建 BroadcastReceiver 对象 , 并且调用 onReceive() 方法 , 执行完以后 , 该对象即被销毁 . 当 onReceive() 方法在 10 秒内没有执行完毕, Android 会认为该程序无响应 . 所以在
BroadcastReceiver 里不能做一些比较耗时的操作 , 否侧会弹出 ANR(Application No
Response) 的对话框 . 。(如图):

怎么用好 BroadcastReceiver ?
如果需要完成一项比较耗时的工作 , 应该通过发送 Intent 给 Service, 由 Service 来完成 . 这里不能使用子线程来解决 , 因为 BroadcastReceiver 的生命周期很短 , 子线程可能还没有结束
BroadcastReceiver 就先结束了 .BroadcastReceiver 一旦结束 , 此时 BroadcastReceiver 的
所在进程很容易在系统需要内存时被优先杀死 , 因为它属于空进程 ( 没有任何活动组件的进程 ). 如果它的宿主进程被杀死 , 那么正在工作的子线程也会被杀死 . 所以采用子线程来解决是不可靠的 .


广播类型及广播的收发
广播类型
普通广播 (Normal broadcasts)
  发送一个广播,所以监听该广播的广播接收者都可以监听到改广播。
异步广播 , 当处理完之后的Intent ,依然存在,这时候registerReceiver(BroadcastReceiver, IntentFilter) 还能收到他的值,直到你把它去掉 , 不能将处理结果传给下一个接收者 , 无法终止广播 .

有序广播 (Ordered broadcasts)
按照接收者的优先级顺序接收广播 , 优先级别在 intent-filter 中的 priority 中声明 ,-1000 到
1000 之间 , 值越大 , 优先级越高 . 可以终止广播意图的继续传播 . 接收者可以篡改内容 .



广播的收发
该组件接收被广播的 intent,Context 可以通过 sendBroadcast() 和 sendOrderedBroadcast()
方法实现广播的发送 .
首先在需要发送信息的地方 ,把要发送的信息和用于过滤的信息 ( 如 Action 、 Category) 装入一个 Intent 对象 ,然后通过调用 Context.sendBroadcast() 、 sendOrderBroadcast() 或 sendStickyBroadcast() 方法,把 Intent 对象以广播方式发送出去。

使用 sendBroadcast() 或 sendStickyBroadcast() 方法发出去的 Intent ,所有满足条件的 BroadcastReceiver 都会随机地执行其 onReceive() 方法
普通广播的发送和接收:
sendBroadcast(intent);

Intent intent = new Intent( "cn.lenovo.yangguangf " );
        sendBroadcast(intent);
priority :这个是 AndroidManifest.xml 中 intent-filter 的参数。

< receiver android:name = ".MyBroadcastReceiver" >
           < intent-filter android:priority = "1000" >
            
< action android:name = "cn.lenovo.yangguangfu" />
</ intent-filter >
</ receiver >

sendOrderedBroadcast(intent, receiverPermission);

1 ,他决定该广播的级别,级别数值是在 -1000 到 1000 之间 , 值越大 , 优先级越高;

2 ,同级别接收是先后是随机的;级别低的收到广播;
3 ,在 android 系统中只要监听该广播的接收者,都能够收到 sendBroadcast(intent) 发出的广播 ;

3 ,不能截断广播的继续传播,

4 ,实验现象,在这个方法发来的广播中,代码注册方式中,收到的广播的先后和注明优先级最高的他们的先后是随机。如果都没有优先级,代码注册收到为最先。

你可能感兴趣的:(android)