zoukankan      html  css  js  c++  java
  • 关于Android四大组件的学习总结

    Activity

    Android应用的用户界面是由Activity类管理的.和其他组件一样,Activity会用一系列生命周期回调函数通知当前的状态。

    生命周期

    Activity的四种状态

    1.运行状态
    2.暂停状态
    3.停止状态
    4.销毁状态

    Activity的三种生存期

    1.完整生存期
    2.可见生存期
    3.前台生存期

    生命周期示意图

    此处输入图片的描述

    个人理解:完整生存期 > 可见生存期 > 前台生存期
    完整生存期由onCreate()方法开始,到onDestory()方法结束,我们应该在onCreate()方法中完成各种初始化操作,比如加载布局、绑定事件等,在onDestory()方法中完成释放内存的操作,活动将变为销毁状态。
    可见生存期开始于onStart()方法,到onStop()结束。onStart()在活动由不可见变为可见时调用,onStop()与之相反。在该生存期内,活动总是可见的,但是不一定能够与用户进行交互,活动可能处于暂停状态,所以应该在这两个回调方法中对用户可见的资源进行加载和释放,从而保证处于停止状态的活动不会占用过多内存。
    活动在onResume()和onPause()之间所经历的就是前台生存期,该生存期内活动始终处于运行状态,这两个方法适合管理消耗cup的资源。

    Activity的启动模式

    启动模式一共有四种,分别是standard,singleTop,singleTask,singleInstance,可以在AndroidManifest.xml中通过给< activity >标签指定android:launchMode属性来选择启动模式。

    standard

    默认的启动模式,在该模式下,每当启动一个新的活动,它就会在返回栈(Android是使用任务(Task)来管理活动的,一个任务就是一组存放在栈里的活动的集合,这个栈也被称作返回栈(Back Stack))中入栈,并处于栈顶的位置。对于使用standard模式的活动,系统不会在乎这个是否已经在返回栈中存在,每次启动都会创建该活动的一个新的实例。

    singleTop

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

    singleTask

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

    singleInstance

    指定为该模式的活动会启用一个的返回栈来管理这个活动。使用该模式可以解决共享活动实例的问题,因为每个应用程序都有自己的返回栈,同一个活动在不同的返回栈中入栈必然是创建了新的实例,而在该模式下会有一个单独的返回栈来管理这个活动,不管是哪个应用程序来访问这个活动,都共用同一个返回栈。

    跳转Flag相关

    在 android.content.Intent 中一共定义了20种不同的 flag,其中和 Task 紧密关联的有四种:
    1.FLAG_ACTIVITY_NEW_TASK
    2.FLAG_ACTIVITY_CLEAR_TOP
    3.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
    4.FLAG_ACTIVITY_SINGLE_TOP

    在使用这四个 flag 时,一个 Intent 可以设置一个 flag,也可以选择若干个进行组合。Activity的加载模式受启动Activity的Intent对象中设置的Flag和manifest文件中Activity的元素的特性值交互控制。

    Intent intent = new Intent(this,xxx.class);
    //如果activity在task存在,拿到最顶端,不会启动新的Activity
    intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
    //如果activity在task存在,将Activity之上的所有Activity结束掉
    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    //默认的跳转类型,将Activity放到一个新的Task中
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    //如果activity已经是运行在 Task 的 top,则不会创建新的实例。
    intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
    

    参考链接:Activity跳转Flag详解


    Service

    基本上,任何不涉及用户界面的操作都应该使用Service。建议每个任务都有一个对应的Service。服务并不是运行在一个独立的进程当中,而是依赖于创建服务时所在的应用程序进程。当某个应用程序进程被杀掉时,所有依赖于该进程的服务也会停止运行。服务并不会自动开启线程,所有代码都是默认运行在主线程当中的,也就是说,我们需要在服务的内部手动创建子线程,并在这里执行具体的任务。

    生命周期

    一旦在项目的任何位置调用了Context的startService()方法,相应的服务就会启动起来,并调用onStartCommand()方法。如果这个服务之前没有创建过,onCreate()方法会先于onStartCommand()方法执行。服务启动之后会一直保持运行状态,直到stopService()或者stopSelf()方法被调用。注意虽然每调用一次startService()方法,onStartCommand()就会执行一次,但实际上每个服务都只会存在一个实例。所以不管你调用了多少次startService()方法,只需调用一次stopService()或stopSelf(),服务就会停止下来。

    另外,还可以调用Context的bindService()来获取一个服务的持久连接,这是就会回调服务中的onBind()方法。类似的,如果这个服务之前没有创建过,onCreate()方法会先于onBind()方法执行。之后,调用方可以获取到onBind()方法返回的IBinder对象的实例,这样就能自由地和服务通信了。只要调用放和服务之间的连接没有断开,服务就会一直保持运行状态。

    当调用了startService()方法后,又去调用stopService()方法,这是服务中的onDestory()方法就会执行,表示服务已经销毁了。类似的,当调用了bindService()方法后,又去调用unbindService()方法,onDestory()方法也会执行。

    Activity和Service进行通信

    被绑定的Service会一直运行,直到所有绑定的客户端都断开后才会停止。在同一个应用进程中绑定Service只需获取对象的引用,并调用对象的方法即可,这种方式称为本地binder

    public class MyLocalService extends Service {
    	private LocalBinder mLocalBinder = new LocalBinder();
    
    	@Override
    	public IBinder onBind(Intent intent) {
    		return mLocalBinder;
    	}
    	
    	public void doLongRunningOperation() {
    		
    	}
    	
    	public class LocalBinder extends Binder {
    		public MyLocalService getService() {
    			return MyLocalService.this;
    		}
    	}
    }
    
    public class MyActivity extends Activity implements ServiceConnection {
    
    	private MyLocalService mService;
    
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_main);
    	}
    
    	@Override
    	protected void onResume() {
    		super.onResume();
    		Intent bindIntent = new Intent(this, MyLocalService.class);
    		bindService(bindIntent, this, BIND_AUTO_CREATE);
    	}
    
    	@Override
    	protected void onPause() {
    		super.onPause();
    		if (mService != null) {
    			unbindService(this);
    		}
    	}
    
    	@Override
    	public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
    		mService = ((MyLocalService.LocalBinder) iBinder).getService();
    	}
    
    	@Override
    	public void onServiceDisconnected(ComponentName componentName) {
    		mService = null;
    	}
    
    }
    

    两种启动方式的对比

    • 通过StartService()启动Service,Service的onCreate()和onStartCommand()方法就会被执行,之后Service会一直处于运行状态,但具体运行的是什么,Activity就控制不了了。
      通过bindService()方法将Activity和Service进行绑定,在onServiceConnected()方法中,通过向下转型可以获得一个Binder子类的实例,通过调用该实例的public方法,实现Activity对Serivce的控制。
    • 采用Context.startService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用onStart()方法。采用startService()方法启动的服务,只能调用Context.stopService()方法结束服务,服务结束时会调用onDestroy()方法。
      采用Context.bindService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用onBind()方法。这个时候调用者和服务绑定在一起,调用者退出了,系统就会先调用服务的onUnbind()方法,接着调用onDestroy()方法。如果调用者希望与绑定的服务解除绑定,可以调用unbindService()方法,调用该方法导致系统调用服务的onUnbind()和onDestroy()方法.

    个人理解:两种启动方式适用场景不同,通过Context.startService()启动的Service适合执行由用户触发并且运行时间不确定的操作,可在操作结束后调用stopSelf()自行结束。虽然这种启动方式没有提供直接的通信机制,但是使用广播或者其他方式可以把结果返回给调用组件。然而,通过绑定的方式可以获取到binder对象,使用它可以直接调用Service对象,通过该对象的public方法,可以实现Activity操作Service。

    使用IntentService

    异步的,会自动停止的服务
    该工具类在Service中包装了一个处理后台线程的Hanlder。

    public class MyIntentService extends IntentService {
    
    	/**
    	 * 构造函数必须在其内部调用父类的有参构造函数
    	 */
    	public MyIntentService() {
    		super("MyIntentService");
    	}
    
    	/**
    	 * 实现该抽象方法,处理具体逻辑,不用担心ANR的问题,因为这个方法已经是在子线程中运行的了
    	 */
    	@Override
    	protected void onHandleIntent(Intent arg0) {
    		// TODO Auto-generated method stub
    	}
    
    	/**
    	 * 根据IntentService的特性,这个服务在运行结束后会自动停止
    	 */
    	@Override
    	public void onDestroy() {
    		super.onDestroy();
    	}
    
    }
    

    个人理解:由于IntentService已经帮我们实现了异步任务,可直接调用其onHandleIntent()在子线程中执行任务,如同使用AsyncTask(),所以误认为Service就是一个子线程,通过本次学习搞清楚了两者之间的关系:服务(后台运行,耗时) = Service(脱离用户界面) + 子线程 (防止主线程被阻塞)


    BraodcastReceiver

    BraodcastReceiver是没有状态的,这意味着BraodcastReceiver对象仅在onReceive()被调用时是有效的。基本上,在BraodcastReceiver的onReceive()方法中唯一应该做的是把调用委托给另一个组件,比如通过Context.startActivity()或者Context.startService()方法。

    Andorid中发送广播事件最常用的方式是通过Context.sendBroadcast()方法给BroadcastReceiver发送Intent对象。

    实现BroadcastReceiver的默认方法是在清单文件中声明它。因此,即便用户没有启动应用程序,也可以使用BroadcastReceiver来通知Service。也可在Activity和Service中以编程的方式注册BroadcastReceiver。有些广播Intent只能以编程的方式注册。

    普通广播和有序广播

    广播分为两种类型:普通广播和有序广播。
    普通广播会以异步方式发送给所有的接收者,并且没有指定的接收顺序,该方式更加高效,但是缺少有序广播的一些高级功能。
    有序广播按照特定的顺序分发,每次只发给一个注册的广播接收器。开发者可以在清单文件中控制接收顺序。有序广播还有另外一个特性:通过使用abortBroadcast(),setResultCode()和setResultData()方法,接收器可以把结果回传给广播,或者终止广播的分发。

    本地BroadcastReceiver

    如果只是在应用程序进程内发送和接收广播,使用LocalBroadcastManager更高效,因为不需要跨进程管理操作,也不需要广播通常涉及的安全问题。

    广播的变体

    粘性广播
    定向广播

    启用和禁止广播接收器

    如果只能在清单文件中定义才能监听某个广播,还有另外一种减少对系统负载影响的方法。通过使用PackageManager,可以启用和禁用应用程序中的组件,这在用户执行特定动作后才启动广播接收器是很有用。只需在清单文件中把组件的android:enabled属性默认设置为false,并稍后用下面的把他们改为true。

    public void enableBroadcastReceiver() {
    	PackageManager packageManager = getPackageManager();
    	packageManager.setComponentEnabledSetting(
    		new ComponentName(this,chargerConnecedListener.class),
    	    PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
    		PackageManager.DONT_KILL_APP);
    
    }
    
    public void disableBroadcastReceiver() {
    	PackageManager packageManager = getPackageManager();
    	packageManager.setComponentEnabledSetting(
    		new ComponentName(this,ChargerConnecedListener.class),
    		PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
    		PackageManager.DONT_KILL_APP);
    }
    

    个人理解:BroadcastReceiver不同于Activity和Service,没有生命周期的概念,该类的对象只有在onReceive()被调用时才有效,起到一个桥梁的作用,根据所接收到的数据决定是启动一个Activity,还是启动一个Service。动态注册的BroadcastReceiver适用于仅在应用启动情况下需要接收广播的情况,这样做有利于减少对系统负载的影响,对于仅能够静态注册的方法依然可以通过以上方法达到该目的。


    ContentProvider

    主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另一个程序中的数据,同时还能保证被访问数据的安全性。目前,使用内容提供器是Andorid实现跨程序共享数据的标准方式

    ContentResolver基本用法

    对于每一个应用程序来说,如果要访问内容提供器中共享的数据,就一定要借助ContentResolver类,可以通过Context中的getContentResolver()方法获取到该类的实例。ContentResolver中提供了一系列的方法用于对数据进行CRUD操作。
    不同于SQLiteDatabase,ContentResolver的增删查改方法都是不接受表名参数的,而是使用一个Uri参数代替,这个参数被称为内容URI。内容URI给内容提供器中的数据建立了唯一标识符,它主要由两部分组成,权限和路径。
    在得到内容URI字符串后调用Uri.parse()方法,将内容URI字符串解析成Uri对象,调用getContentResolver().query()等方法对数据进行操作。

    public class MainActivity extends Activity {
        @Override
    publicvoid onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            
            //获取上下文
            Context context = MainActivity.this;
            
            //获取ContentResolver对象
            ContentResolver resolver = context.getContentResolver();
            
            //获取Uri对象
            Uri uri = Uri.parse("content://com.test.MyProvider");
            
            //获取数据
            Cursor c = resolver.query(uri, null, null, null, null);
            
            c.moveToFirst();
            
            for(int i=0; i<c.getCount(); i++){
                int index = c.getColumnIndexOrThrow("name");
                String src = c.getString(index);
                Log.d("", src);
                c.moveToNext();
            }
        }
    }
    

    创建ContentProvider

    想要实现跨程序共享数据的功能,官方推荐的方式就是使用内容提供器,新建一个类去继承ContentProvider,将六个抽象方法全部重写。

    public class MyProvider extends ContentProvider {
    
    	// 在Create中初始化一个数据库
    	@Override
    public boolean onCreate() {
            SQLiteDatabase db =this.getContext().openOrCreateDatabase("test_db.db3", Context.MODE_PRIVATE, null);
            db.execSQL("create table tab(_id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL)");
            ContentValues values =new ContentValues();
            values.put("name", "test");
            db.insert("tab", "_id", values);
            db.close();
    returntrue;
        }
    
    	@Override
    	public Uri insert(Uri uri, ContentValues values) {
    		return null;
    	}
    	// 实现query方法
    	@Override
    	public Cursor query(Uri uri, String[] projection, String selection,
    			String[] selectionArgs, String sortOrder) {
    		SQLiteDatabase db = this.getContext().openOrCreateDatabase(
    				"test_db.db3", Context.MODE_PRIVATE, null);
    		Cursor c = db.query("tab", null, null, null, null, null, null);
    		return c;
    	}
    	
    	@Override
    	public int delete(Uri uri, String selection, String[] selectionArgs) {
    		// TODO Auto-generated method stub
    		return 0;
    	}
    
    	@Override
    	public int update(Uri uri, ContentValues values, String selection,
    			String[] selectionArgs) {
    		// TODO Auto-generated method stub
    		return 0;
    	}
    	
    	
    	@Override
    	public String getType(Uri uri) {
    		// TODO Auto-generated method stub
    		return null;
    	}
    }
    
    

    个人理解:在互联网上搜索“Android 数据存储”,大多数文章会给出四到五种方式,包括文件,SharedPreferences,数据库,网络和ContentProvicer,通过本次学习,我觉得这种说法并不合适,ContentProvicer与其他四种方式不同,与其说它是一种数据存储方式,不如说它是在其他几种存储方式的基础上进行封装,提供了一套标准的数据访问接口,正如它字面上的意思。

  • 相关阅读:
    部署 AppGlobalResources 到 SharePoint 2010
    还原一个已删除的网站集
    使用仪表板设计器配置级联筛选器 (SharePoint Server 2010 SP1)
    File or arguments not valid for site template
    Pex and Moles Documentation
    Content Query Webpart匿名访问
    Running Moles using NUnit Console from Visual Studio
    Calling a WCF Service using jQuery in SharePoint the correct way
    Updating Content Types and Site Columns That Were Deployed as a Feature
    asp.net中判断传过来的字符串不为空的代码
  • 原文地址:https://www.cnblogs.com/happyhacking/p/4676443.html
Copyright © 2011-2022 走看看