Service 特点

  • 适用于耗时操作,并且不需要与用户交互的功能。
  • 其他应用程序组件能够启动服务并且即使用户切换到另一个应用,服务还可以在后台运行。
  • 组件能够绑定到服务并与之交互,甚至执行进程间通信。
  • Service 比处于非活动状态的Activity更高的优先级,因此当系统请求资源时,它们被终止的可能性很小。如果运行时过早得终止了一个已经启动的Service,只要有足够的资源可用,则运行时就会重新启动它。
  • 一个Service的优先级可以提升到和前台Activity的优先级一样高。
  • 开发人员可以将服务设为私有从而阻止其他应用程序访问。
  • 虽然Service在运行的时候没有专门的GUI,但是它们和Acitivity以及BroadCast Receiver一样还是运行在应用程序进程的主线程中,因此必须通过Thread和AsyncTask类把耗时的进程移到后台进程中。通过使用独立的线程,开发人员能减少应用不相应错误的风险,并且应用程序主线程仍然用于用户与Activity交互。

Service 的类型

  • Started Service:当应用程序组件通过调用startService()方法启动服务时候,服务处于started状态。一旦启动,服务能够在后台无期限运行,即使启动它的组件已经被销毁。通常,Started Service执行单个操作并且不会向调用者返回结果。如果操作完成,服务需要停止自身.
  • Bound Service :当应用程序通过bindService()方法绑定到服务的时候,服务处于bound状态,绑定服务提供客户端-服务器接口,以允许组件与服务交互,发送请求,获得结果,甚至使用进程间通信跨进程完成这些操作。仅当其他应用程序组件与之绑定的时候,绑定服务才能运行。多个组件可以绑定到一个服务上。只有在他们都解除绑定的时候服务被销毁。
    服务也可以同时属于上述两种类型既可以启动,无限期运行,可能绑定。这取决于是否实现一些回调方法:onStartCommand()方法允许组件启动,onBind方法允许组件绑定服务。

Service的生命周期

  1. OnStarted方式启动
  2. OnBound方式启动
  • onStartCommand()
    当其他组件,如Activity调用startService()方法请求服务启动的时候,系统调用该方法,一旦该方法执行,服务就启动,并在后台无限期运行。如果开发人员实现该方法,则需要在任务完成时候调用stopSelf()或者stopService()方法停止服务。

  • onBind()
    当其他组件调用bindService方法想与服务绑定的时候,系统调用该方法,在该方法的实现中,开发人员必须通过返回IBinder提供客户端用来与服务通信的接口。该方法必须实现,但是如果不想允许绑定,则应该返回null;

  • onCreate()
    当服务第一创建时,系统调用该方法执行一次性建立(在系统调用onStartCommand()或onBind()方法前)如果服务已经运行,该方法不被调用,从而保证在Android系统中一个Service只有一个实例。

  • onDestroy()
    当服务不再使用并即将销毁时,系统调用该方法。服务应该实现该方法来清除诸如线程,注册监听器,接收者等资源。这是服务受到的最后调用。

Service的声明

开发人员必须在应用程序配置文件中声明全部的Service,方法是在标签中添加子标签。

创建Service

一般方式创建

应用程序组件例如Activity能通过StartService()方法或者传递Intent对象来启动服务,在Intent对象中指定了服务并且包含服务所需要的全部数据,服务使用onStartCommand方法接收Intent。
实验及结果分析:代码见TestService

  • 按下Start Service 启动服务,这是输出结果如下:
  • 按下后退键和Home键,不会输出onDestroy,说明即使按下这两个键,服务仍然在后台运行,可以在Setting->Application 中查看到。
  • 再次点击App图标进入输出如下结果:说明一个Service在Android 系统中只存在一个实例,在已经存在的情况下不会再次调用onCreate创建新的Service。
  • 按下StopService,结果如下:
继承IntentService类

IntentService:这是Service的子类,它每次使用一个工作线程来处理全部启动请求,在不必同时处理多个请求时候,这是最佳选择。开发人员需要实现onHandleIntent方法,它接收每次启动请求的Intent以便完成后台的任务

  • 创建区别于主线程的默认工作线程来执行发送到onStartCommand方法的全部Intent
  • 创建工作队列每次传递一个Intent到onHandleIntent方法实现,这样就不必担心多线程。
  • 所有启动请求处理完毕后停止服务,这样就不要调用stopself方法。
    实现方法:
  • 提供OnBind方法实现,其返回值是null;
  • 提供onStartCommand方法的默认实现,它先发送Intent到默认队列,然后到onHandleIntent方法实现。
    我们要做的工作就是实现onHandleIntent方法,同时由于IntentService并没有提供空参数的构造方法因此必须提供一个构造方法。
    例子:
public class HelloIntentService extends IntentService {
public HelloIntentService() {
super("Hello Service");
}
protected void onHandleIntent(Intent intent) {

long endTime = System.currentTimeMillis()+5*1000;
while(System.currentTimeMillis()<endTime){
synchronized (this) {
try {
wait(endTime-System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
};
System.out.println("onHandleIntent");
}
}
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "Server Start: "+"flags:"+flags+"start ID:"+startId, Toast.LENGTH_LONG).show();
return super.onStartCommand(intent, flags, startId);
}
public IBinder onBind(Intent intent) {
return null;
}
}
private Button btn = null;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn = (Button) findViewById(R.id.btn);
btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
Intent intent= new Intent(MainActivity.this,HelloIntentService.class);
startService(intent);
}
});
}

实验结果分析:

按下Start IntentService 后输出如下结果:
从结果可以看出,IntentService 会在onHandleIntent方法中处理业务逻辑部分,同时在处理完成后会自动销毁服务,而不需调用stopself/stopService方法。(调用StopService方法不会调用onDestroy方法销毁服务)

继承Service类

使用IntentService类可以简化启动服务的实现,然而,如果需要让服务处理多线程,取代使用工作队列启动请求,也就是说为每次请求创建一个新线程并且立即运行它们,避免等待前一个请求的结束,则可以继承Service类来处理各个Intent
Service:是所有服务的基类,当继承该类的时候,创建新线程来执行服务的全部工作是非常重要的,因为默认服务默认是使用应用程序主线程,这可能会降低应用程序Activity的运行性能。
这种方法要自己使用stopself或者stopService方法结束服务,并且需要自己创建线程来处理服务的内部逻辑
使用这种方式的时候onStartCommand必须返回一个整数。用于表示系统停止服务后如何继续服务。

1. START_NOT_STICKY:如果系统在onStartCommand方法返回后停止服务,则系统不会重新创建服务,除非有PendingIntent要发送。在避免在不必要时运行服务和应用程序能简单地重启任何未完成工作的时候,这是最佳选择。
2. START_STICKY:如果系统在onStartCommand方法返回后停止服务,则系统会重新创建服务并调用onStartCommand方法。但是不重新发送最后的Intent,相反系统使用空Intent调用onStartCommand方法,除非有PeddingIntent来启动服务。此时,这些Intent会发送。这适合多媒体播放器,它们不执行命令但是无限期运行并等待工作。
3. START_REDELIVER_INTENT:如果系统在onStartCommand方法返回后停止服务,重新创建服务并使用发送给服务的最后Intent调用onStartCommand方法,全部PenddingIntent必须依次发送,这适合积极执行应该立即恢复工作的服务。如下载文件。

启动和停止服务

启动服务不能直接调用onStartCommand方法,而是使用Acitivity或者其他应用程序组件通过传递Intent对象到startService()方法启动服务。Android系统自动调用服务的onStartCommand方法并将Intent传递给它,如果服务还没有运行系统首先调用onCreate()方法,接着调用onStartCommand方法。
如果服务没有绑定,StartService方法发送的Intent是应用程序和服务之间唯一的通信方式,然而如果需要获得服务的放回结果,则可以通过启动该服务的客户端能为广播创建PeddingIntent并通过启动服务的Intent发送它,服务接下来能使用广播发送结果。

Started Service方式的服务,系统不会停止或者销毁它,它的生命周期必须自己管理。除非它必须回收系统内存并且在onStartCommand方法返回后服务继续运行,因此服务必须调用stopSelf方法停止自身或者其他组件调用stopService方法停止服务。
stopself(startid)方法可以确保停止服务的请求总是基于最近接收到的启动请求。

创建Bound Service

绑定服务是允许其他应用程序绑定并且与之交互的Service类实现类。为了提供绑定,开发人员必须实现onBind回调方法。该方法返回iBinder对象,它定义了客户端用来与服务交互的程序接口.
客户端能通过bindService方法绑定到服务,此时客户端必须提供ServiceConnection接口的实现类。它监视客户端与服务之间的联系,bindService立刻放回,但是当Android系统创建客户端与服务之间的连接时,它调用ServiceConnection 接口中的onServiceConnection方法来发送客户端用来发送与服务通信的IBinder对象。
多个客户端能够同时连接到服务,但是仅当第一个客户端绑定的时候,系统调用服务的onBind方法来获取Ibinder对象,系统接着发送同一个IBinder对象到其他的绑定的客户端但是不再调用onBind方法,当最后的客户端与服务解除绑定的时候,系统销毁服务。

绑定和解除绑定

应用程序能调用bindService方法绑定到服务,android系统接下来调用服务的onBind方法,它返回IBinder来与服务通信。绑定是异步的bindService方法立刻返回,为了接收IBinder。客户端必须创建ServiceConnection实例然后传递到bindService方法。
只有Activity,Service,Content Provider能够绑定到服务,BroadcastReceiver不能绑定到服务。
如果需要从客户端绑定服务需要完成如下工作:

  1. 实现ServiceConnection这需要重写onServiceConnected和onserviceDisconnected方法
  2. 调用bindService传递ServiceConnection实现。
  3. 当系统调用onServiceConnected方法的时候,就可以使用接口定义的方法。
  4. 调用unbindService解除绑定。

将Service移到前台

在确定哪个应用程序或者应用程序组件可以被终止的时候,Android给正在运行的Service赋予了第二高的优先级,仅仅次于处于激活状态,并且在前台与用户交互的活动Acitivity,在Service需要直接和用户进行交互的情况下,可以将Service的优先级提高到和前台Activity一样高,可以通过调用Service的startForeground方法来将一个服务移到前台,同样可以调用stopForeground来将一个服务移到后台。

int NOTIFICATION_ID = 1;
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pi = PendingIntent.getActivity(this, 1, intent, 0);
Notification notification = new Notification(R.drawable.ic_launcher, "前台服务", System.currentTimeMillis());
notification.setLatestEventInfo(this, "前台服务", "这个是被移到前台的服务", pi);
notification.flags =notification.flags|Notification.FLAG_ONGOING_EVENT;
startForeground(NOTIFICATION_ID,notification);

提升服务存活可能性的方法

http://blog.csdn.net/mad1989/article/details/22492519

  • onStartCommand方法,返回START_STICKY
  • 提升service优先级
    <service  
    android:name="xxxxxxx"
    android:enabled="true" >
    <intent-filter android:priority="1000" >
    ...............
    </intent-filter>
    </service>
  • 使用startForeground()提升service进程优先级
  • onDestroy方法里重启service
  • 监听系统广播判断Service状态
  • 将应用升级为系统应用
  • Application加上Persistent属性
<application  
android:name="xxxxxxxxxxx"
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:persistent="true"
android:theme="@style/AppTheme" >
</application>

Activity 是啥

  • Activity是用于负责提供用户交互的可视化界面,一个应用程序可以有一个或者多个Activity,一般情况下,每个窗口都是满屏的,但是它也可以是一个小的,位于其他窗口之上的浮动窗口。
  • Android允许同时运行多个应用程序,应用程序可以拥有前台进程和后台进程,然而在同一时刻只能由一个应用程序的Activity处于前台。

Activity 生命周期

完整生命周期:

从第一次调用onCreate开始到onDestroy为止,Activity在onCreate中设置所有全局状态以完成初始化,而在onDestroy中释放所有的系统资源:

  • onCreate:当一个Activity首次运行的时候被回调,负责初始化任务,onCreate方法仅具有一个传入参数,即一个Bundle对象,但对于首次启动的Activity而言,这个参数为空,如果这个Activity先前因为内存的原因而被终止,现在又需要重新启动,那么Bundle对象中将包含Activity先前的状态信息,这样它才能被重新初始化。
    主要任务:填充用户UI,将数据绑定到控件,得到Fragment引用,启动Service和定时器

  • onRestoreInstanceState :在onCreate方法完成后调用该函数用于恢复UI状态。
    onRestoreInstanceState 只有在当前视图被系统销毁了时候被调用,因此有时候onSaveInstanceState方法和onRestoreInstanceState方法不一定是成对出现的

  • onDestroy:销毁Activity的时候被调用。
    主要任务:确保清除了onCreate创建的所有资源,并保证所有的外部连接如网络或数据库已经被关闭。

可视生命周期:

从onStart到onStop。在此期间,用户是可见的但是可能还未获得焦点,尚且不能与用户交互。 在这两个方法之间,可以保留用来向用户显示这个Activity所需要的资源,例如,当用户看不到显示的内容的时候,可以在onStart中注册一个BroadCastReceiver广播接收器来监控可能影响的UI变化,而在onStop中来注销,onStart和onStop方法可以随着应用是否被用户可用而被多次调用。

  • onStart:当一个Activity即将显示时被回调,可见生命周期的开始。在这个阶段可以完成如注册专门用于更新用户界面的BroadCast Receiver等工作。
  • onStop:停止Activity的时候被调用。当前Activit处于不可视状态。
    主要任务:暂停或者停止动画,线程,传感器监听器,GPS查找,定时器,启动Service和定时器,或者其他用于更新用户界面的进程,当UI不可见的时候更新它是没有意义的,不但耗费了资源,却没起到实际的作用。当UI再次可用的时候,可使用onStart或者OnRestart方法来恢复或者重启这些线程。
前台生命周期:

从OnResume调用起,至相应的onPause调用为止,在此期间,Activity位于前台最上面并与用户开始进行交互.

  • onResume:这时候该Activity可以接受用户的输入事件,与用户进行交互,同时当Activity由于暂停恢复为活动状态的时候这个方法被调用。调用该方法后,该Activity位于Activity栈顶。在这个方法中不需要重新加载UI状态,因为当要求加载UI状态的时候,它会由onCreate和onRestoreInstanceState方法处理。

  • onPause:暂停的时候被回调,在该方法中通常用于存储持久的数据,在该方法中应该终止在OnrResume方法中播放的所有音频,视频和动画,同时在该方法中还必须解除某些资源,例如:在手动管理而非自动管理情况下的数据库Cursor对象。onPause方法是Activity在后台最后一次能够有机会进行清理工作,释放无需资源的地方,如果没有及时释放,这些资源将有可能不能被彻底释放掉。同时需要保存所有未提交的数据,以免由于程序终止后不再返回而丢失数据。
    应该尽量保证在onResume ,onPause方法中代码较少,以保证前台和后台之间进行切换的时候应用程序能够保持响应。在onPause方法中释放越多的资源,转入后台的Activity被终止的可能性就越小。

  • onSaveInstanceState:这个方法提供了把Activity的UI状态保存在Bundle中的机会,这个Bundle会被传递给onCreate和onRestoreInstanceState方法。一般在这个方法中保存,复选状态,用户焦点,已经输入但未被提交的用户输入,从而保证当Activity下次变成活动的时候,可以呈现出与之前相同的UI。
    这里需要注意的它是只有在非你本来意愿销毁了你的activity的时候被系统调用。但是在activity是被用户主动销毁的情况下(例如我们人为按下back键)不会被调用,下面是一些常见的情况:

  1. 当用户按下HOME键时
  2. 按下电源键灭屏的时候
  3. 从一个Activity A中启动一个新的Activity时。
  4. 在指定configchange属性的情况下切换屏幕方向
    布局中的每一个View默认实现了onSaveInstanceState()方法,这样的话,这个UI的任何改变都会自动的存储并在activity重新创建的时候自动的恢复。但是这种情况只有在你为这个UI提供了唯一的ID之后才起作用,如果没有提供ID,将不会存储它的状态。
    onSaveInstanceState()如果被调用,这个方法会在onStop()前被触发,但系统并不保证是否在onPause()之前或者之后触发
典型的Activity生命周期:
启动Activity: onCreate()—>onStart()—>onResume(),Activity进入运行状态。
Activity退居后台,且系统内存不足, 系统会杀死这个后台状态的Activity,若再次回到这个Activity,则会走onCreate()–>onStart()—>onResume()
Activity退居后台: 当前Activity转到新的Activity界面或按Home键回到主屏: onPause()—>onStop(),进入停滞状态。
Activity返回前台: onRestart()—>onStart()—>onResume(),再次回到运行状态。
锁定屏与解锁屏幕 只会调用onPause(),而不会调用onStop()方法,开屏后则调用onResume()

Activity任务堆栈

直接上图不解释:

我们可以通过android:launchMode = “standard|singleInstance|singleTask|singleTop”来控制Acivity任务栈。

standard : 标准模式,每次启动Activity都会创建一个新的Activity实例,并且将其压入任务栈栈顶,而不管这个Activity是否已经存在。
singleTop : 这种模式下,如果新Activity已经位于任务栈的栈顶,那么此Activity不会被重新创建,同时Activity的onNewIntent()方法会被回调.如果Activity已经存在但是不在栈顶,那么作用于standard模式一样.
singleTask: 创建这样的Activity的时候,系统会先确认它所需任务栈已经创建,否则先创建任务栈.然后放入Activity,如果栈中已经有一个Activity实例,那么这个Activity就会被调到栈顶,并调用onNewIntent(),并且singleTask会清理在当前Activity上面的所有Activity.
singleInstance : 这种模式的Activity只能单独位于一个任务栈内,由于栈内复用的特性,后续请求均不会创建新的Activity,除非这个独特的任务栈被系统销毁了

如果对上面的不大理解可以参考如下的文章,说的很详细,为了避免作者把文章取消所以把重点的引用如下:
http://www.cnblogs.com/xiaoQLu/archive/2011/09/29/2195742.html

standardActivity的默认加载方法,即使某个ActivityTask栈中已经存在,另一个activity通过Intent跳转到该activity,同样会新创建一个实例压入栈中。例如:现在栈的情况为:A B C D,在D这个Activity中通过Intent跳转到D,那么现在的栈情况为: A B C D D 。此时如果栈顶的D通过Intent跳转到B,则栈情况为:A B C D D B。此时如果依次按返回键,D  D C B A将会依次弹出栈而显示在界面上。
singleTop:如果某个ActivityLaunch mode设置成singleTop,那么当该Activity位于栈顶的时候,再通过Intent跳转到本身这个Activity,则将不会创建一个新的实例压入栈中。例如:现在栈的情况为:A B C DDLaunch mode设置成了singleTop,那么在D中启动Intent跳转到D,那么将不会新创建一个D的实例压入栈中,此时栈的情况依然为:A B C D。但是如果此时B的模式也是singleTopD跳转到B,那么则会新建一个B的实例压入栈中,因为此时B不是位于栈顶,此时栈的情况就变成了:A B C D B
singleTask:如果某个ActivitysingleTask模式,那么Task栈中将会只有一个该Activity的实例。例如:现在栈的情况为:A B C DBLaunch modesingleTask,此时D通过Intent跳转到B,则栈的情况变成了:A B。而CD被弹出销毁了,也就是说位于B之上的实例都被销毁了。
singleInstance:将Activity压入一个新建的任务栈中。例如:Task1的情况为:A B CC通过Intent跳转到D,而DLaunch modesingleInstance,则将会新建一个Task2。此时Task1的情况还是为:A B CTask2的情况为:D。此时屏幕界面显示D的内容,如果这时D又通过Intent跳转到D,则Task2中也不会新建一个D的实例,所以两个栈的情况也不会变化。而如果D跳转到C,则栈1的情况变成了:A B C C,因为CLaunch modestandard,此时如果再按返回键,则栈1变成:A B C。也就是说现在界面还显示C的内容,不是D

Activity 状态

上面介绍了Activity的堆栈接下来就来介绍下,Activity的状态,Activity状态是由其在Activity堆栈的位置决定的:

Android应用程序生命周期

Android应用不能控制它们自己的生命周期,它是由Android运行时控制的,默认情况下每个应用程序都是通过它们自己的进程运行的,每个进程都运行在独立的Dalvik实例中,每个应用程序的内存和进程管理都是由运行时专门进行处理的。
回收资源的时候,进程被终止的顺序是由它们的应用优先级所决定的,一个应用程序的优先级等同于它的优先级最高的组件的优先级。
当两个应用程序优先级相同的时候,在较低优先级状态运行时间最长的进程将会首先被终止。
进程的优先级也受进程间依赖性的影响,如果一个应用程序依赖于第二个应用程序所提供的Service或者Content Provider,那么第二个应用程序至少拥有与它所支持的这个应用程序相同的优先级。

进程状态

  1. 活动进程:指的是有组件正在和用户进行交互的应用程序进程,Android尝试回收其他应用状态资源来支持这些进程运行。

活动的进程包括:

  • 处于活动状态的Activity。
  • 正在运行,且已被标记为前台运行的Service
  • 正在执行onCreate ,onStart,onDestroy事件处理的Service。
  • 正在执行onReceiver 事件处理程序的BroadCast Receiver
  1. 可见进程:可见但是非活动的进程是指那些包含“可见”Acitivity的进程。

  2. 启动服务进程:已经启动的Service的进程,因为后台Service没有直接和用户交互,所以它的优先级要比可见进程或者前台Service低。

  3. 后台进程:不可见并且没有任何正在运行的Service的Activity进程,通常会有大量的后台进程。

  4. 空进程:为了提高系统整体性能,Android经常在应用程序的生存周期结束后仍然把它们保存在内存中,Android通过维护这个缓存来减少应用程序被再次启动时的启动时间。

Activity的创建

  1. 创建一个类继承自Activity,并重写相应的方法。
  2. 编写布局文件,设置布局。
  3. 在Android Manifest中配置Activity。

Activity属性

Android 开发是基于Java环境开发的,但是你如果写过测试用例的话你就会有个感觉就是,它和Java还是有很大区别的,比如你不能随便new一个类让它运行起来,我们在测试用例中有的使用要费劲心思来创建和待测试一样的运行环境,这里要介绍的Context就是一种上下文环境,下面内容是来自我在入职初期学习时候做的PPT来的,所以比较简洁不过大概涵盖了Context所涉及的知识点。
如上面提到的Android是基于组件,每个组件的运行都要有一个环境才能够正常工作,所以不是简单地New一个对象调用就能创建实例了,而是要有它们各自的上下文环境Context。在Android开发初期大家可能最难理解的就是这个Context了吧,至少我当时是萌逼状态的。

Context的作用

  • 可以通过 Context识别调用者的实例。
    (例如:TextView label = new TextView(this)
    意味着view拥有一个指向Activity的引用,进而引用Activity占有的资源).
  • 管理应用程序特定的配置细节
  • 管理应用程序范围的操作和数据
  • 访问当前包的资源(getResources、getAssets)
  • 启动其他组件(Activity、Service、Broadcast)
  • 得到各种系统服务(getSystemService)
  • 使用Application Context来访问设置和资源可以在多个Activity实例中共享

Application Context

Application-Context的生命周期是整个应用,Application Context可以通过Context.getApplicationContext或者Activity.getApplication方法获取。
在Activity或者Service中可以通过getApplication()函数获得,不管通过何种方法在哪里获得的,在一个进程内,总是获得到同一个实例。

Application通常有如下常用用途:

1.获得当前应用的主题,资源文件中的内容等
2.使用Application同步Activity之间的状态,例子见备注部分:

实现在任何位置获取Applcation Context 方法:

在Android中只有Activity, Provider等组件中才能使用API获取Application Context,但是在某些工具类中往往需要它来访问资源。这就需要我们自定义一个Application类来解决这个问题:

public class MyApplication extends Application {  
private static MyApplication instance;
public static MyApplication getInstance() {
return instance;
}
public void onCreate() {
super.onCreate();
instance = this;
}
}

为了能正确地调用MyApplication.getInstance(),需要在manifest中中加入name=”mypackage.MyApplication”。

获取Context的方式

  • 可以使用getApplicationContext()方法从当前进程中获取Context,这个方法在例如Activity或者Service中能够找到。通常我们使用Context对象时,要优先考虑这个全局的进程Context。

    Context context = getApplicationContext();
  • getContext()当前Activity对象的Context对象

  • Activity.this 返回当前的Activity实例,这个和上一个有点类似。

使用应用程序Context

获得了一个应用程序Context后,就可以用它来访问应用程序范围的功能和服务了,这些功能和服务包括:

  • 获取应用程序资源,例如字符串,图像和XML文件:
    String greeting = getResource().getString(R.string.hello);
  • 访问应用程序首选项使用getSharedPreferences来访问共享的应用程序首选项
  • 管理私有的应用程序文件以及目录
  • 获取未编译的应用程序组件使用getAssets方法来获取应用程序的资源
  • 获取系统服务
  • 管理私有的应用程序数据库
  • 使用应用程序权限

应用程序的Context数目

在Android中 Context的子类有:Application,Activity,Service,所以:

Context数量 = Activity数量 + Service数量 + 1 (Application Context)

错误使用带来的问题:

Context 错误使用会带来很严重的内存泄漏的问题,这个在内存优化部分有做过介绍,比如下面的例子,这是一个非常简单的懒汉式单例:

public class Singleton {

private Context mContext;
private static Singleton instance;
private Singleton(Context context) {
this.mContext = context;
}

public static Singleton getInstance(Context context) {
if (instance == null) {
instance = new Singleton(context);
}
return instance;
}
}

我们知道单例的生命周期要长于一般的组件,一般是常驻内存的,如果在创建这个单例的时候传入的是Activity的上下文那么除了应用退出,否则Activity的上下文会一直被单例持有,所以导致即使Activity被销毁掉,也不能被GC回收,从而导致了内存泄漏。

使用Context要注意的几点

一般Context造成的内存泄漏,几乎都是当Context销毁的时候,却因为被引用导致销毁失败,而Application的Context对象可以理解为随着进程存在的,所以我们总结出使用Context的正确姿势:

  • 在开发中尽量使用Application的Context,特别是生命周期长的情况下。不要让生命周期长于组件的对象持有组件的引用。
  • 非静态内部类会隐式持有外部类实例的引用,如果需要使用内部类那么需要将外部实例引用作为弱引用持有,从而避免内存泄漏。

  1. add-ons: 这里面保存着附加库,比如Google Maps。
  2. docs: 这里面是Android SDK API参考文档,所有的API都可以在这里查到。
  3. tools: 作为SDK根目录下的tools文件夹,这里包含了重要的工具,比如ddms用于启动Android调试工具,比如logcat、屏幕截图和文件管理器,而draw9patch则是绘制android平台的可缩放png图片的工具,sqlite3可以在PC上操作SQLite数据库, 而monkeyrunner则是一个不错的压力测试应用,模拟用户随机按键,mksdcard则是模拟器SD映像的创建工具,emulator是 Android SDK模拟器主程序,不过从android 1.5开始,需要输入合适的参数才能启动模拟器,traceview作为android平台上重要的调试工具。
  4. extas: 附件文档
  5. platforms: 是每个平台的SDK真正的文件,里面会根据APILevel划分的SDK版本,这里就以Android2.2来说,进入后有 一个android-8的文件夹,android-8进入后是Android2.2 SDK的主要文件,其中ant为ant编译脚本,data保存着一些系统资源,images是模拟器映像文件,skins则是Android模拟器的皮肤,templates是工程创建的默认模板,android.jar则 是该版本的主要framework文件,tools目录里面包含了重要的编译工具,比如aapt、aidl、逆向调试工具dexdump和编译脚本dx。
  6. platform-tools保存着一些通用工具,比如adb、和aapt、aidl、dx等文件,这里和platforms目录中tools文件夹有些重复,主要是从android2.3开始这些工具被划分为通用了。
  7. samples是Android SDK自带的默认示例工程
  8. system-images 系统镜像
  9. sources Android 资源文件夹
  10. temp 缓存目录

想必大家接触Android的时候最早接触的图就是下面这张吧,它是Android整个架构图,虽然Android 系统已经发展到Android M,并且Android N 也快发布,但是整个架构还是不变的。

  • Linux内核 Android核心系统服务依赖于Linux内核,如安全性、内存管理、进程管理、网络协议栈和驱动模型。Linux内核也是作为硬件与软件栈的抽象层。驱动:显示驱动、摄像头驱动、键盘驱动、WiFi驱动、Audio驱动、flash内存驱动、Binder(IPC)驱动、电源管理等。
  • 系统库和Android运行时

系统库包括九个子系统,分别是图层管理、媒体库、SQLite、OpenGL ES、FreeType、WebKit、SGL、SSL和libc。
Android运行时包括核心库和Dalvik虚拟机,前者既兼容了大多数Java语言所需要调用的功能函数,又包括了Android的核心库,比如android.os、android.net、android.media等等。
后者是一种基于寄存器的java虚拟机,Dalvik虚拟机主要是完成对生命周期的管理、堆栈的管理、线程的管理、安全和异常的管理以及垃圾回收等重要功能。

系统库:

libc            C语言标准库
Surface Menager 主要管理多个应用程序同时执行时,各个程序之间的显示与存储
Media Framework 系统多媒体库
SQLite 关系型数据库
OpenGL ES 3D效果支持
FreeType 位图及矢量库
WebKit Web 浏览器引擎
SGL 2D图形引擎
SSL 位于TCP/IP与各种应用层协议之间,为数据通信提供支持
  • 应用程序框架层

该层是Android应用开发的基础,开发人员大部分情况是在和它打交道。应用程序框架层包括活动管理器、窗口管理器、内容提供者、视图系统、包管理器、电话管理器、资源管理器、位置管理器、通知管理器和XMPP服务十个部分。在Android平台上,开发人员可以完全访问核心应用程序所使用的API框架。并且,任何一个应用程序都可以发布自身的功能模块,而其他应用程序则可以使用这些已发布的功能模块。基于这样的重用机制,用户就可以方便地替换平台本身的各种应用程序组件。

Activity Manager 活动管理器用于管理应用程序生命周期
View System 视图管理器,用来构建应用程序如列表,表格,文本框,按钮等
Window Manager窗口管理者
Content Provider 内容提供者
Notification Manager 通知管理者,用来设置在状态栏中显示的提示信息
Package Manager 包管理者,用来对Android系统内的应用程序进行管理
Telephony Manager 电话管理者,用来对联系人及通话记录等信息进行管理
Resource Manager 资源管理者,用来提供非代码资源的访问
Location Manager 位置管理者,用来提供使用者的当前位置等信息
XMPP Service Service服务
  • 应用程序层

该层提供一些核心应用程序包,例如电子邮件、短信、日历、地图、浏览器和联系人管理等。同时,开发者可以利用Java语言设计和编写属于自己的应用程序,而这些程序与那些核心应用程序彼此平等、友好共处。

对于Android系统架构推荐如下两篇博客:
http://developer.51cto.com/art/201001/180207.htm
http://mobile.51cto.com/android-235496.htm

Delvik虚拟机以及ART

这篇博客将会在以后的时间添加这一部分,先占个坑!

硬件设备:
RapsBerry 3
操作系统:
Rapsbian OS jessie

  1. Setup ROS Repositories
$ sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu jessie main" > /etc/apt/sources.list.d/ros-latest.list'
$ wget https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -O - | sudo apt-key add -

2.更新软件源

$ sudo apt-get update
$ sudo apt-get upgrade

3.Install Bootstrap Dependencies

$ sudo apt-get install python-pip python-setuptools python-yaml python-distribute python-docutils python-dateutil python-six
$ sudo pip install rosdep rosinstall_generator wstool rosinstall

4.Initializing rosdep

$ sudo rosdep init
$ rosdep update

Installation

Now, we will download and build ROS Indigo.

Create a catkin Workspace

In order to build the core packages, you will need a catkin workspace. Create one now:

$ mkdir ~/ros_catkin_ws
$ cd ~/ros_catkin_ws

Next we will want to fetch the core packages so we can build them. We will use wstool for this. Select the wstool command for the particular variant you want to install:

ROS-Comm: (recommended) ROS package, build, and communication libraries. No GUI tools.

$ rosinstall_generator ros_comm --rosdistro indigo --deps --wet-only --exclude roslisp --tar > indigo-ros_comm-wet.rosinstall
$ wstool init src indigo-ros_comm-wet.rosinstall


This will add all of the catkin or wet packages in the given variant and then fetch the sources into the ~/ros_catkin_ws/src directory. The command will take a few minutes to download all of the core ROS packages into the src folder. The -j8 option downloads 8 packages in parallel. 




Resolve Dependencies

Before you can build your catkin workspace, you need to make sure that you have all the required dependencies. We use the rosdep tool for this, however, a couple of dependencies are not available in the repositories. They must be manually built first.

Unavailable Dependencies

Following packages are not available for Raspbian:

Raspbian Wheezy: libconsole-bridge-dev, liburdfdom-headers-dev, liburdfdom-dev, liblz4-dev, collada-dom-dev

Raspbian Jessie: collada-dom-dev

The following packages are needed for each ROS variant:

Ros_Comm: libconsole-bridge-dev, liblz4-dev

Desktop: libconsole-bridge-dev, liblz4-dev, liburdfdom-headers-dev, liburdfdom-dev, collada-dom-dev

The required packages can be built from source in a new directory:

$ mkdir ~/ros_catkin_ws/external_src
$ sudo apt-get install checkinstall cmake
$ sudo sh -c 'echo "deb-src http://mirrordirector.raspbian.org/raspbian/ testing main contrib non-free rpi" >> /etc/apt/sources.list'
$ sudo apt-get update

libconsole-bridge-dev:

$ cd ~/ros_catkin_ws/external_src
$ sudo apt-get build-dep console-bridge
$ apt-get source -b console-bridge
$ sudo dpkg -i libconsole-bridge0.2*.deb libconsole-bridge-dev_*.deb



liblz4-dev:

$ cd ~/ros_catkin_ws/external_src
$ apt-get source -b lz4
$ sudo dpkg -i liblz4-*.deb



Resolving Dependencies with rosdep

The remaining dependencies should be resolved by running rosdep:

Raspbian Wheezy:

$ cd ~/ros_catkin_ws
$ rosdep install –from-paths src –ignore-src –rosdistro indigo -y -r –os=debian:wheezy

Raspbian Jessie:

$ cd ~/ros_catkin_ws
$ rosdep install –from-paths src –ignore-src –rosdistro indigo -y -r –os=debian:jessie

Building the catkin Workspace

Once you have completed downloading the packages and have resolved the dependencies, you are ready to build the catkin packages.

Invoke catkin_make_isolated:

$ sudo ./src/catkin/bin/catkin_make_isolated –install -DCMAKE_BUILD_TYPE=Release –install-space /opt/ros/indigo

Note: This will install ROS in the equivalent file location to Ubuntu in /opt/ros/indigo however you can modify this as you wish.

For rviz, you will also have to apply this patch.

Should the compilation fail with an “internal compiler error”, it may be because you’re out of memory. A quick fix for this is to add swap space to the Pi and recompile. If the error persists try building with the -j2 option instead of the default -j4 option:

$ sudo ./src/catkin/bin/catkin_make_isolated –install -DCMAKE_BUILD_TYPE=Release –install-space /opt/ros/indigo -j2

Now ROS should be installed! Remember to source the new installation:

$ source /opt/ros/indigo/setup.bash

Or optionally source the setup.bash in the ~/.bashrc, so that ROS environment variables are automatically added to your bash session every time a new shell is launched:

$ “source /opt/ros/indigo/setup.bash” >> ~/.bashrc

我们在介绍Hello World的时候介绍了roscore 命令,但是并没有介绍它具体做了什么操作:
其实在运行这个命令的时我们启动了如下的组件:

  • ROS Master
  • ROS 参数服务器
  • Rosout log节点
    前两者已经在上述的章节进行了介绍,至于roscore节点它是用于从其他ROS节点中收集Log信息,并将其存储在log文件中。

下面就先运行roscore命令看下效果,运行命令后会弹出如下的Log:

//产生的Log文件
... logging to /home/pi/.ros/log/674306a0-06fc-11e6-8a8b-b827eb2bb378/roslaunch-raspberrypi-2054.log
Checking log directory for disk usage. This may take awhile.
Press Ctrl-C to interrupt
Done checking log file disk usage. Usage is <1GB.

//这个命令将会启动roscore.xml ROS启动文件,这个文件会自动启动ros master和ros参数服务器,下面这个地方显示的就是ROS参数服务器的地址和端口
started roslaunch server http://raspberrypi:41429/
ros_comm version 1.11.18

//这个部分显示的是ros版本以及ros版本号信息的参数
SUMMARY
========

PARAMETERS
* /rosdistro: indigo
* /rosversion: 1.11.18

NODES

//显示ROS_MASTER_URI信息
auto-starting new master
process[master]: started with pid [2065]
ROS_MASTER_URI=http://raspberrypi:11311/

setting /run_id to 674306a0-06fc-11e6-8a8b-b827eb2bb378
process[rosout-1]: started with pid [2078]

//启动rosout服务
started core service [/rosout]

ROS 数据可以以Bag形式也可以存储在Parameter Service上

ROS Parameter:

我们在实现一个机器人的时候,我们一定会遇到需要存储很多参数的情况,当这些参数不断增加的时候我们可能会需要将这些参数存储在文件中,在某些时刻还需要在多个程序间共享这些参数,ROS 为了满足这种需求提供了一个参数服务器,它是一个共享的服务器,所有的ROS节点都可以共享这些参数,任何一个节点都可以对参数服务器上的参数进行增删查改操作。参数服务器可以存储各种类型的数据,并且还可以设置访问的范围是否只有当前的节点可以访问或者全部节点都可以访问。

  • 32-bit integers
  • Booleans
  • strings
  • doubles
  • iso8601 dates
  • lists
  • base64-encoded binary data

我们还可以存储字典参数:

/camera/name:’nikon’ #string type
/camera/fps : 30 #integer
/camera/exposure:1.2 #float
/camera/active : true #boolean

和参数服务器相关的命令:

$ rosparam set parameter value :这个命令用于将某个参数设置为某个值
$ rosparam get parameter_name : 获得某个参数的值
$ rosparam load [YAML file] :YAML文件中加载参数到参数服务器
$ rosparam dump [YAML file] : 这个命令将会将参数服务器中的数据存储到YAML文件
$ rosparam delete [parameter_name] : 将会从参数服务器中删除某个参数
$ rosparam list : 将会列出所有的参数名


ROS Bags:

ROS bag用于存储来自topic和service的消息数据,它是通过rosbag命令创建的,它会订阅一个或者更多的topic并且以它接收到的方式存储起来。
下面是rosbag的常用方法:

$rosbag	record  [topic_1] -o [bag_name]: 这个命令将会将指定的topic记录到一个bag文件中,我们还可以使用-a作为参数记录所有的topic

$rosbag play [bag_name] :这个会回放存在的bag文件.

下面推荐一个很好用的bag回放GUI工具:
http://wiki.ros.org/rqt

ROS Message:
之前介绍了 ROS 节点之间的通信方式,其中一种就是Message机制它是一种基于发布/订阅的轻量级通信方式,消息是一种包含位域类型的简单数据结构,ROS提供标准的原始数据类型和原始数据类型的数组。ROS使用MD5校验方式来确认发布者和订阅者是否正在交换同样类型的数据。
和之前介绍的ROS节点一样,ROS也提供了一个内置的称为rosmsg的命令来获取ROS消息的信息,下面是常见的命令:

rosmsg show [message]显示消息的描述
rosmsg list 显示所有的消息
rosmsg md5 [message]:显示某个消息的md5sum
rosmsg package [package_name]: 显示所有在这个包里面的消息
rosmsg packages :列出包含消息的包

ROS Topic:

ROS Topic 是一个用于ROS 节点交换消息的命名总线,Topic可以匿名地发送和订阅消息,这就意味着消息的产生和消费是解偶的,二者之间没有强行绑定的关系,ROS节点不在乎是谁在发布Topic,哪个节点在订阅Topic,它只关心topic的名字以及当前的消息类型时候和我要求的匹配。Topic和稍候介绍的Service的最大区别是它是单向的。

和之前介绍的ROS Message一样,ROS也提供了一个内置的称为rostopic的命令来获取ROS topic的信息,下面是常见的命令:

$rostopic bw  /topic :用于显示指定topic所占用的带宽
$rostopic echo /topic :用于显示指定topic的内容
$rostopic find /message_type:这个命令用于指定给定消息类型的topic
$rostopic hz /topic :这个命令用于显示给定topic的发布速度
$rostopic info /topic :这个会打印所有激活的topic消息
$rostopic list: 这个会显示ROS系统中所有激活的topic
$rostopic pub /topic message_type args :这个命令可以用于向一个topic上发布一个指定类型的消息
$rostopic type /topic:这个会显示指定topic的消息类型

ROS Service:

ROS Service 是基于请求/答复形式的交互形式,主要用于分布式系统中ROS服务是通过一对消息来定义的,其中一个用于定义请求数据类型,另一个用于定义答复数据类型,服务的定义一般位于一个包的srv目录的src文件内。在ROS 服务中一个节点作为ROS服务,ROS 客户端可以从这个服务中请求服务,等到服务完成后会发送结果到它的客户端上。

下面是用于ROS服务的命令:

$rosservice	call /service args: 这个命令将会使用特定的参数调用服务
$rosservice find service_type : 这个命令将会查找指定服务类型的服务
$rosservice info /services : 这个将会打印关于指定服务的消息
$rosservice list : 这个将会列出系统上的所有服务
$rosservice type /service : 这个命令将会打印某个服务的服务类型
$rosservice uri /service : 这个命令将会打印服务的ROSRPC URI