61阅读

android学习方法-Android的SQLite学习及使用方法

发布时间:2018-04-17 所属栏目:Android

一 : Android的SQLite学习及使用方法

我们曾经介绍过《Android SQLite数据库应用技巧分享》,今天我们将介绍一下Android的SQLite学习及使用方法。

SQLite介绍

SQLite是轻量级的、嵌入式的、关系型数据库,目前已经在iPhone、Android等手机系统中使用,SQLite可移植性好,很容易使用,很小,高效而且可靠。SQLite嵌入到使用它的应用程序中,它们共用相同的进程空间,而不是单独的一个进程。从外部看,它并不像一个RDBMS,但在进程内部,它却是完整的,自包含的数据库引擎。

在android中当需要操作SQLite数据库的时候需要得到一个SQLiteOpenHelper对象,而SQLiteOpenHelper是一个抽象类,用户需要继承这个类,并实现该类中的一些方法。

1、继承SQLiteOpenHelper之后就拥有了以下两个方法:

◆getReadableDatabase() 创建或者打开一个查询数据库

◆getWritableDatabase()创建或者打开一个可写数据库

◆他们都会返回SQLiteDatabase对象,用户通过得到的SQLiteDatabase对象进行后续操作

2、同时用户还可以覆盖以下回调函数,再对数据库进行操作的时候回调以下方法:

◆onCreate(SQLiteDatabase):在数据库第一次创建的时候会调用这个方法,一般我们在这个方法里边创建数据库表。

◆onUpgrade(SQLiteDatabase,int,int):当数据库需要修改的时候,Android系统会主动的调用这个方法。一般我们在这个方法里边删除数据库表,并建立新的数据库表,当然是否还需要做其他的操作,完全取决于应用程序的需求。

◆onOpen(SQLiteDatabase):这是当打开数据库时的回调函数,一般也不会用到。

需要注意

1、在SQLiteOepnHelper的子类当中,必须有以下该构造函数

  1. publicDatabaseHelper(Contextcontext,Stringname,CursorFactoryfactory,
  2. intversion){
  3. //必须通过super调用父类当中的构造函数
  4. super(context,name,factory,version);
  5. }

为了方便,也可以创建其它的构造函数,含二个参数或者三个参数的。

2、函数public void onCreate(SQLiteDatabase db)是在调用getReadableDatabase()或者是getWritableDatabase()第一次创建数据库的时候执行,实际上是在第一次得到SQLiteDatabse对象的时候,才会调用这个方法.

  1. publicvoidonCreate(SQLiteDatabasedb){
  2. System.out.println("createaDatabase");
  3. //execSQL函数用于执行SQL语句
  4. db.execSQL("createtableuser(idint,namevarchar(20))");
  5. }

在向数据库的表中插入记录时,需要先将数据包含在一个ContentValues中,向该对象当中插入键值对,其中键是列名,值是希望插入到这一列的值,值必须和数据库当中的数据类型一致。接着调用Databasehelper的getWritableDatabase方法来获得可以写入的Databasehelper对象,再向其中insert记录。注意调用DatabaseHelper对象的insert,update或者query方法的参数的传递。

另外执行query方法后,返回的是一个Cursor游标,游标最开始指向的是记录集合中第一行的上一行,因此首先需要先调用cursor.next()将游标移动到记录集合的第一行,接着再获取数据即可。

Java代码

  1. publicclassSQLiteActivityextendsActivity{
  2. /**Calledwhentheactivityisfirstcreated.*/
  3. privateButtoncreateButton;
  4. privateButtoninsertButton;
  5. privateButtonupdateButton;
  6. privateButtonupdateRecordButton;
  7. privateButtonqueryButton;
  8. @Override
  9. publicvoidonCreate(BundlesavedInstanceState){
  10. super.onCreate(savedInstanceState);
  11. setContentView(R.layout.main);
  12. createButton=(Button)findViewById(R.id.createDatabase);
  13. updateButton=(Button)findViewById(R.id.updateDatabase);
  14. insertButton=(Button)findViewById(R.id.insert);
  15. updateRecordButton=(Button)findViewById(R.id.update);
  16. queryButton=(Button)findViewById(R.id.query);
  17. createButton.setOnClickListener(newCreateListener());
  18. updateButton.setOnClickListener(newUpdateListener());
  19. insertButton.setOnClickListener(newInsertListener());
  20. updateRecordButton.setOnClickListener(newUpdateRecordListener());
  21. queryButton.setOnClickListener(newQueryListener());
  22. }
  23. classCreateListenerimplementsOnClickListener{
  24. @Override
  25. publicvoidonClick(Viewv){
  26. //创建一个DatabaseHelper对象
  27. DatabaseHelperdbHelper=newDatabaseHelper(SQLiteActivity.this,"test_mars_db");
  28. //只有调用了DatabaseHelper对象的getReadableDatabase()方法,或者是getWritableDatabase()方法之后,才会创建,或打开一个数据库
  29. SQLiteDatabasedb=dbHelper.getReadableDatabase();
  30. }
  31. }
  32. classUpdateListenerimplementsOnClickListener{
  33. @Override
  34. publicvoidonClick(Viewv){
  35. DatabaseHelperdbHelper=newDatabaseHelper(SQLiteActivity.this,"test_mars_db",2);
  36. SQLiteDatabasedb=dbHelper.getReadableDatabase();
  37. }
  38. }
  39. classInsertListenerimplementsOnClickListener{
  40. @Override
  41. publicvoidonClick(Viewv){
  42. //生成ContentValues对象
  43. ContentValuesvalues=newContentValues();
  44. //想该对象当中插入键值对,其中键是列名,值是希望插入到这一列的值,值必须和数据库当中的数据类型一致
  45. values.put("id",1);
  46. values.put("name","zhangsan");
  47. DatabaseHelperdbHelper=newDatabaseHelper(SQLiteActivity.this,"test_mars_db",2);
  48. SQLiteDatabasedb=dbHelper.getWritableDatabase();
  49. //调用insert方法,就可以将数据插入到数据库当中
  50. db.insert("user",null,values);
  51. }
  52. }
  53. //更新操作就相当于执行SQL语句当中的update语句
  54. //UPDATEtable_nameSETXXCOL=XXXWHEREXXXXCOL=XX...
  55. classUpdateRecordListenerimplementsOnClickListener{
  56. @Override
  57. publicvoidonClick(Viewarg0){
  58. //TODOAuto-generatedmethodstub
  59. //得到一个可写的SQLiteDatabase对象
  60. DatabaseHelperdbHelper=newDatabaseHelper(SQLiteActivity.this,"test_mars_db");
  61. SQLiteDatabasedb=dbHelper.getWritableDatabase();
  62. ContentValuesvalues=newContentValues();
  63. values.put("name","zhangsanfeng");
  64. //第一个参数是要更新的表名
  65. //第二个参数是一个ContentValeus对象
  66. //第三个参数是where子句
  67. db.update("user",values,"id=?",newString[]{"1"});
  68. }
  69. }
  70. classQueryListenerimplementsOnClickListener{
  71. @Override
  72. publicvoidonClick(Viewv){
  73. System.out.println("aaa------------------");
  74. Log.d("myDebug","myFirstDebugMsg");
  75. DatabaseHelperdbHelper=newDatabaseHelper(SQLiteActivity.this,"test_mars_db");
  76. SQLiteDatabasedb=dbHelper.getReadableDatabase();
  77. Cursorcursor=db.query("user",newString[]{"id","name"},"id=?",newString[]{"1"},null,null,null);
  78. while(cursor.moveToNext()){
  79. Stringname=cursor.getString(cursor.getColumnIndex("name"));
  80. System.out.println("query--->"+name);
  81. }
  82. }
  83. }
  84. }

二 : Android之Service学习篇一:Service启动方式之startService

Service概念及用途:

A service is an application component that can perform long-running operations in the background and does not provide a user interface。

通常service用来执行一些耗时操作,或者后台执行不提供用户交互界面的操作,例如:下载、播放音乐。

Service生命周期 :

Android Service的生命周期并不像Activity那么复杂,它只继承了onCreate(),onStart(),onDestroy()三个方法,当我们第一次启动Service时,先后调用了onCreate(),onStart()这两个方法,当停止Service时,则执行onDestroy()方法,这里需要注意的是,如果Service已经启动了,当我们再次启动Service时,不会在执行onCreate()方法,而是直接执行onStart()方法,具体的可以看下面的实例。

Service与Activity通信:

Service后端的数据最终还是要呈现在前端Activity之上的,因为启动Service时,系统会重新开启一个新的进程,这就涉及到不同进程间通信的问题了(AIDL)这一节我不作过多描述,当我们想获取启动的Service实例时,我们可以用到bindService和onBindService方法,它们分别执行了Service中IBinder()和onUnbind()方法。

这里要提及一点:继承service的子类在重写service的方法中,除了一个onStart()方法之外,还有一个onStartCommand()方法,有关onStartCommand()方法稍微作点介绍:

Android开发的过程中,每次调用startService(Intent)的时候,都会调用该Service对象的onStartCommand(Intent,int,int)方法,然后在onStartCommand方法中做一些处理。然后我们注意到这个函数有一个int的返回值,这篇文章就是简单地讲讲int返回值的作用。

从Android官方文档中,我们知道onStartCommand有4种返回值:

START_STICKY:如果service进程被kill掉,保留service的状态为开始状态,但不保留递送的intent对象。随后系统会尝试重新创建service,由于服务状态为开始状态,所以创建服务后一定会调用onStartCommand(Intent,int,int)方法。如果在此期间没有任何启动命令被传递到service,那么参数Intent将为null。

START_NOT_STICKY:“非粘性的”。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统将会把它置为started状态,系统不会自动重启该服务,直到startService(Intent intent)方法再次被调用;。

START_REDELIVER_INTENT:重传Intent。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,并将Intent的值传入。

START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,但不保证服务被kill后一定能重启。

有了 Service 类我们如何启动他呢,有两种方法:

• Context.startService()

• Context.bindService()

1. 在同一个应用任何地方调用 startService() 方法就能启动 Service 了,然后系统会回调 Service 类的 onCreate() 以及 onStart() 方法。这样启动的 Service 会一直运行在后台,直到 Context.stopService() 或者 selfStop() 方法被调用。另外如果一个 Service 已经被启动,其他代码再试图调用 startService() 方法,是不会执行 onCreate() 的,但会重新执行一次 onStart() 。

2. 另外一种 bindService() 方法的意思是,把这个 Service 和调用 Service 的客户类绑起来,如果调用这个客户类被销毁,Service 也会被销毁。用这个方法的一个好处是,bindService() 方法执行后 Service 会回调上边提到的 onBind() 方发,你可以从这里返回一个实现了 IBind 接口的类,在客户端操作这个类就能和这个服务通信了,比如得到 Service 运行的状态或其他操作。如果 Service 还没有运行,使用这个方法启动 Service 就会 onCreate() 方法而不会调用 onStart()。

区别概况为:

startService() 的调用者与服务没有联系,即使调用者退出了,服务仍然运行,而bindService() 的调用者与服务绑在一起,调用者一旦退出了,服务也随即终止掉。www.2cto.com

这里用一个实例(使用startService()方法来启动)来讲解一下service的声明周期和使用方法:

首先编写一个类继承Service这个基类,重写里面的方法,然后在Activity中调用startService()和stopService()来启动和停止服务。

运行界面:

service Android之Service学习篇一:Service启动方式之startService

工程目录结构:

service Android之Service学习篇一:Service启动方式之startService

ExampleService.java

[html]

package com.service.activity;

import android.app.Service;

import android.content.Intent;

import android.os.IBinder;

import android.util.Log;

public class ExampleService extends Service{

private static final String TAG = "ExampleService";

@Override

public void onCreate() {

Log.i(TAG, "ExampleService-onCreate");

super.onCreate();

}

@Override

public void onStart(Intent intent, int startId) {

Log.i(TAG, "ExampleService-onStart");

super.onStart(intent, startId);

}

@Override

public int onStartCommand(Intent intent, int flags, int startId) {

//执行文件的下载或者播放等操作

Log.i(TAG, "ExampleService-onStartCommand");

/*

* 这里返回状态有三个值,分别是:

* 1、START_STICKY:当服务进程在运行时被杀死,系统将会把它置为started状态,但是不保存其传递的Intent对象,之后,系统会尝试重新创建服务;

* 2、START_NOT_STICKY:当服务进程在运行时被杀死,并且没有新的Intent对象传递过来的话,系统将会把它置为started状态,

* 但是系统不会重新创建服务,直到startService(Intent intent)方法再次被调用;

扩展:android startservice / android 启动service / android自启动service

* 3、START_REDELIVER_INTENT:当服务进程在运行时被杀死,它将会在隔一段时间后自动创建,并且最后一个传递的Intent对象将会再次传递过来。

*/

return super.onStartCommand(intent, flags, startId);

}

@Override

public IBinder onBind(Intent intent) {

Log.i(TAG, "ExampleService-onBind");

return null;

}

@Override

public void onDestroy() {

Log.i(TAG, "ExampleService-onDestroy");

super.onDestroy();

}

}

MainActivity.java

[html]

package com.service.activity;

import android.app.Activity;

import android.content.Intent;

import android.os.Bundle;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.Button;

public class MainActivity extends Activity implements OnClickListener{

private static final String TAG = "MainActivity"; //日志输出标志

private Button btnStartService;

private Button btnStopService;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

btnStartService = (Button)findViewById(R.id.btnStartService);

btnStopService = (Button)findViewById(R.id.btnStopService);

btnStartService.setOnClickListener(this);

btnStopService.setOnClickListener(this);

}

//点击事件处理监听器

@Override

public void onClick(View v) {

Intent intent = new Intent(MainActivity.this,ExampleService.class);

switch(v.getId()){

case R.id.btnStartService:

startService(intent);

break;

case R.id.btnStopService:

stopService(intent);

break;

default:

break;

}

}

}

最后在AndroidManifest.xml中对service进行声明,它跟Activity同一级,都写在Application标签里面:

<service android:name=".ExampleService"/>

创建完运行

在运行点击"启动service"之后(第一次启动service),我们可以查看LogCat控制台输出的日志如下:

service Android之Service学习篇一:Service启动方式之startService

这个时候我们点击"返回",Activity被干掉了,但是我们的服务仍然在运行,可以查看Setting-->Application-->Running Service,截图如下:

service Android之Service学习篇一:Service启动方式之startService

然后回到我们的Activity,再次点击启动Service,控制台输出日志如下:

service Android之Service学习篇一:Service启动方式之startService

onCreate()方法没有被调用,说明它并没有重新被创建。

然后我们点击停止Service,输出日志如下:

service Android之Service学习篇一:Service启动方式之startService

扩展:android startservice / android 启动service / android自启动service

三 : android学习十八(Service服务的基本用法)

定义一个服务

在项目中定义一个服务,新建一个ServiceTest项目,然后在这个项目中新增一个名为MyService的类,并让它继承自Service,完成后的代码如下所示:

package com.jack.servicetest;import android.app.Service;import android.content.Intent;import android.os.IBinder;public class MyService extends Service {@Overridepublic IBinder onBind(Intent arg0) {// TODO Auto-generated method stubreturn null;}}

目前MyService中只用一个onBind()方法,这个方法是Service中唯一的一个抽象方法,所以必须要在子类里实现。既然定义了一个服务,自然应该在服务中去处理一些事情,那处理事情的逻辑应该写在哪里?这时我们就可以重写Service中的另外一些方法了,如下所示:
package com.jack.servicetest;import android.app.Service;import android.content.Intent;import android.os.IBinder;public class MyService extends Service {@Overridepublic IBinder onBind(Intent arg0) {// TODO Auto-generated method stubreturn null;}@Overridepublic void onCreate() {// TODO Auto-generated method stubsuper.onCreate();}@Overridepublic void onDestroy() {// TODO Auto-generated method stubsuper.onDestroy();}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {// TODO Auto-generated method stubreturn super.onStartCommand(intent, flags, startId);}}

可以看到,这里我们又重写了onCreate(),onDestroy()和onStartCommand(Intent intent, int flags, int startId)这三个方法,它们是每个服务中最常用到的三个方法。其中onCreate方法会在服务创建的时候调用,onStartCommand方法会在每次服务启动的时候调用。onDestroy()方法会在服务销毁的时候调用。

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

另外需要注意的是,没一个服务都需要在AndroidManifest.xml文件中进行注册才能生效,android四大组件都需要进行注册。于是我们修改AndroidManifest.xml文件,代码如下所示:


这样的话,就已经将一个服务定义好了。

启动和停止服务

定义好了服务后,接下来就应该考虑如何启动以及停止这个服务。启动服务和停止服务主要借助Intent来实现,下面我们在ServiceTest项目中尝试去启动已经停止MyService这个服务。

首先修改activity_main.xml中的代码,如下所示:


上面的布局主要加入了2个按钮,用来启动和停止服务。

然后修改MainActivity中的代码,如下所示:

package com.jack.servicetest;import android.app.Activity;import android.content.Intent;import android.os.Bundle;import android.view.Menu;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;public class MainActivity extends Activity implements OnClickListener{private Button startService;private Button stopService;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);startService=(Button) findViewById(R.id.start_service);stopService=(Button) findViewById(R.id.stop_service);startService.setOnClickListener(this);stopService.setOnClickListener(this);}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// Inflate the menu; this adds items to the action bar if it is present.getMenuInflater().inflate(R.menu.main, menu);return true;}@Overridepublic void onClick(View v) {// TODO Auto-generated method stubswitch(v.getId()){case R.id.start_service:Intent startIntent =new Intent(this,MyService.class);startService(startIntent);//启动服务break;case R.id.stop_service:Intent stopIntent =new Intent(this,MyService.class);stopService(stopIntent);//停止服务break;default:break;}}}

上面我们在onCreate方法中分别获取到start service按钮和stop service按钮的实例,并给它们注册了点击
事件。然后在start service按钮的点击事件里面,我们构建出了一个Intent对象,并调用startService()
方法来启动MyService这个服务。在stop service按钮的点击事件里,我们同样构建出了一个Intent对象,并调用
stopService()方法来停止MyService这个服务。startService()和stopService()方法都是定义在Context
类中的,所以我们在活动里可以直接调用这两个方法。注意,这里完全是由活动来决定服务何时停止的,如果没有点击stop service
按钮,服务就会一直处于运行状态。那服务有什么办法让自己停下来了?只需要在MyService的任何一个位置调用shopSelf()
方法就能让服务停止下来了。

接下来我们要考虑,如何才能证明服务已经成功启动或者停止了?最简单的方法就是在MyService的几个方法中加入打印日志,如下所示:

package com.jack.servicetest;import android.app.Service;import android.content.Intent;import android.os.IBinder;import android.util.Log;public class MyService extends Service {@Overridepublic IBinder onBind(Intent arg0) {// TODO Auto-generated method stubreturn null;}@Overridepublic void onCreate() {// TODO Auto-generated method stubsuper.onCreate();Log.d(MyService, onCreate());}@Overridepublic void onDestroy() {// TODO Auto-generated method stubsuper.onDestroy();Log.d(MyService, onDestroy());}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {// TODO Auto-generated method stubLog.d(MyService, onStartCommand);return super.onStartCommand(intent, flags, startId);}}

由此证明MyService服务确实已经成功停止下来了。

onCreate方法和onStartCommand方法的区别:onCreate方法是在服务第一次创建的时候调用的,而onStartCommand方法则在每次启动服务的时候都会调用,由于刚才我们是第一次点击start service按钮,服务此时还未创建过,所以两个方法都会执行,之后如果你在连续多点击几次start service按钮,你就会发现只有onStartCommand方法可以得到执行了。

活动和服务进行通信

目前我们希望在MyService里提供一个下载的功能,然后再活动中可以决定何时开始下载,以及随时查看下载进。实现这个功能的思路是创建一个专门的Binder对象来对下载功能进行管理,修改MyService中的代码:如下所示:

package com.jack.servicetest;import android.app.Service;import android.content.Intent;import android.os.Binder;import android.os.IBinder;import android.util.Log;public class MyService extends Service {private DownloadBinder mBinder=new DownloadBinder();class DownloadBinder extends Binder{public void startDownload(){Log.d(MyService, startdownload executed);}public int getProgress(){Log.d(MyService, getProgress executed);return 0;}}@Overridepublic IBinder onBind(Intent intent) {// TODO Auto-generated method stubreturn mBinder;}@Overridepublic void onCreate() {// TODO Auto-generated method stubsuper.onCreate();Log.d(MyService, onCreate());}@Overridepublic void onDestroy() {// TODO Auto-generated method stubsuper.onDestroy();Log.d(MyService, onDestroy());}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {// TODO Auto-generated method stubLog.d(MyService, onStartCommand);return super.onStartCommand(intent, flags, startId);}}

这里我们新建了一个DownloadBinder类,并让它继承自Binder,然后再它的内部提供开始下载以及查看下载进度的方法。当然这只是两个模拟的方法,并没有实现真正的功能,我们在这两个方法中分别打印了一行日志。

接着,在MyService中创建了DownloadBinder的实例,然后再onBind()方法里返回了这个实例,这样MyService中的工作就全部完成了。

下面我们需要在活动中调用服务里的方法,首先需要在布局文件中新曾两个按钮,修改activity_main.xml中的代码,如下所示:


这两个活动用于在活动中进行绑定和取消绑定服务,当一个活动和服务绑定了之后,就可以调用该服务里的Binder提供的方法了,修改MainActivity中的代码,如下所示:
package com.jack.servicetest;import com.jack.servicetest.MyService.DownloadBinder;import android.app.Activity;import android.content.ComponentName;import android.content.Intent;import android.content.ServiceConnection;import android.os.Bundle;import android.os.IBinder;import android.view.Menu;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;public class MainActivity extends Activity implements OnClickListener{private Button startService;private Button stopService;private Button bindService;private Button unbindService;private MyService.DownloadBinder downloadBinder;private ServiceConnection connection=new ServiceConnection() {/* * 这里创建了一个ServiceConnection的匿名类,在这里重写了onServiceConnected方法和 * onServiceDisconnected方法,这两个方法分别会在活动与服务成功绑定以及解除绑定的时候调用。 * 在onServiceConnected方法中,我们又通过向下转型得到了DownloadBinder的实例,有了这个 * 实例,活动和服务之间的关系就变得非常紧密了,现在我们可以在活动中根据具体的场景来调用DownloadBinder * 中的任何public方法,及实现了指挥服务干什么,服务就干什么的功能,这里只做了简单的测试,在onServiceConnected * 中调用了DownloadBinder的startDownload(),getProgress()方法。 * */@Overridepublic void onServiceDisconnected(ComponentName name) {// TODO Auto-generated method stub}@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {// TODO Auto-generated method stubdownloadBinder=(MyService.DownloadBinder) service;downloadBinder.startDownload();downloadBinder.getProgress();}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);startService=(Button) findViewById(R.id.start_service);stopService=(Button) findViewById(R.id.stop_service);startService.setOnClickListener(this);stopService.setOnClickListener(this);bindService = (Button) findViewById(R.id.bind_service);unbindService = (Button) findViewById(R.id.unbind_service);bindService.setOnClickListener(this);unbindService.setOnClickListener(this);}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// Inflate the menu; this adds items to the action bar if it is present.getMenuInflater().inflate(R.menu.main, menu);return true;}@Overridepublic void onClick(View v) {// TODO Auto-generated method stubswitch(v.getId()){case R.id.start_service:Intent startIntent =new Intent(this,MyService.class);startService(startIntent);//启动服务break;case R.id.stop_service:Intent stopIntent =new Intent(this,MyService.class);stopService(stopIntent);//停止服务break;case R.id.bind_service:/* *现在我们需要进行活动和服务的绑定,构建一个Intent对象,然后调用bindService()方法将 *MainActivity()和MyService进行绑定。 bindService方法接收三个参数,第一个参数就是 *上面创建出的Intent对象,第二个参数就是前面创建出的ServiceConnection的实例,第三个 *参数则是一个标志位,这里传入BIND_AUTO_CREATE表示在活动和服务进行绑定后自动创建服务。 *这会使得MyService中的onCreate()方法得到执行,但onStartCommand()方法不会执行。 * */Intent bindIntent=new Intent(this,MyService.class); bindService(bindIntent, connection, BIND_AUTO_CREATE);//绑定服务break;case R.id.unbind_service:/* * 如果我们想解除活动和服务之间的绑定,调用一下unbindService()方法就可以了。 * */unbindService(connection);//解绑服务break;default:break;}}}

可以看到,首先是MyService的onCreate()方法得到了执行,然后startDownload和getProgeress方法得到了执行,说明我们确实已经在活动力成功的调用了服务里提供的方法了。另外需要注意的,任何一个服务在整个应用程序范围内都是通用的,即MyService不仅可以和MainActivity绑定,还可以和任何一个其他的活动进行绑定,而且在绑定完成之后他们都可以获取到相同的DownloadBinder实例。

服务的生命周期

服务也有自己的生命周期,前面我们使用到的onCreate(),onStartCommand(),onBind()和onDestroy()等方法
都是在服务的生命周期内可能回掉的方法。
一旦在项目的任何位置调用了Context的startService()方法,相应的服务就会启动起来,并回调onStartCommand()。如果 这个服务之前还没创建过,onCreate()方法会先于onStartCommand()方法执行。服务启动了之后一直保持运行状态,
直到stopService()或stopSelf()方法被调用。注意虽然每调用一次startService()方法,onStartCommand()就会
执行一次,但实际上每个服务都只会存在一个实例。所以不管你调用了多少次startService()方法,只需调用一次stopService()或stopSelf()方法,服务就会停止下来了。
另外,还可以调用Context的bindService()来获取一个服务的持久连接,这时就会回调服务中的onBind()方法。类似地,如果这个服务之前还没有创建过,onCreate()方法会先于onBind()方法执行。之后,调用方可以获取到onBind()方法里返回的IBinder对象的实例,这样就能自由地和服务进行通信了。只要调用方和服务之间的连接没有断开,服务就会一直保持运行状态。
当调用了startService()方法后,又去调用stopService()方法,这时服务中的onDestroy()方法就会执行,表示
服务已经销毁了。类似地,当调用了bindService()方法后,又去调用unbindService()方法,onDestroy()方法也会执行,这
两种情况都很好理解。但是需要注意,我们是完全有可能对一个服务既调用了startService()方法,又调用了bindService()方法的,这种情况下该如何才能让服务销毁掉?根据android系统的机制,一个服务只要被启动或者绑定了之后就会一直处于运行状态,必须要让以上两种条件同时不满足,服务才能被销毁。所以,这种情况下需要同时调用stopService()和unbindService()方法,onDestroy()方法才会执行。

使用前台服务

服务几乎都是在后台运行的,一直以来它都是默默的做着辛苦的工作。但是服务的系统优先级还是比较低的,当系统出现内存不足的情况时,就有可能会回收掉正在后台运行的服务。如果你希望服务可以一直 保持运行状态,而 不会由于系统内存不足的原因导致被回收,就可以考虑使用前台服务。前台服务和普通服务最大的区别就在于,它会一直有一个正在运行的系统状态栏显示,下拉状态栏后可以看到更加详细的信息,非常类似于通知的效果。

下面我们创建一个前台服务吧,修改MyService中的代码,如下所示:

package com.jack.servicetest;import android.app.Notification;import android.app.PendingIntent;import android.app.Service;import android.content.Intent;import android.os.Binder;import android.os.IBinder;import android.util.Log;public class MyService extends Service {private DownloadBinder mBinder=new DownloadBinder();class DownloadBinder extends Binder{public void startDownload(){Log.d(MyService, startdownload executed);}public int getProgress(){Log.d(MyService, getProgress executed);return 0;}}@Overridepublic IBinder onBind(Intent intent) {// TODO Auto-generated method stubreturn mBinder;}@Overridepublic void onCreate() {// TODO Auto-generated method stubsuper.onCreate();@SuppressWarnings(deprecation)Notification notification=new Notification(R.drawable.ic_launcher,Notification comes,System.currentTimeMillis());Intent notificationIntent=new Intent(this,MainActivity.class);PendingIntent pendingIntent=PendingIntent.getActivity(this, 0,notificationIntent,0);notification.setLatestEventInfo(this, this is title, this is content,pendingIntent);startForeground(1, notification);/* 可以看到,这里只是修改了onCreate()方法中的代码,相信这部分代码你会非常眼熟。这就是我们前面学习的 创建通知的方法。只不过这次在构建出Notification对象并没有使用NotificationManager来将通知显示 出来,而是调用了startForeground()方法。这个方法接收两个参数,第一个参数是通知的id,类似于notify()方法 的第一个参数,第二个参数则是构建出来的Notification对象。调用startForeground()方法后就会让MyService变成 一个前台服务,并在系统状态显示出来。 */Log.d(MyService, onCreate());}@Overridepublic void onDestroy() {// TODO Auto-generated method stubsuper.onDestroy();Log.d(MyService, onDestroy());}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {// TODO Auto-generated method stubLog.d(MyService, onStartCommand);return super.onStartCommand(intent, flags, startId);}}

现在重新运行下程序,并点击start service或bind service按钮,MyService就会以前台服务的模式开启了,并且在系统状态栏会显示一个通知图标,下拉状态栏后可以看到该通知的详细内容

使用IntentService

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

所以这个时候,就需要用到Android多线程编程的技术了,我们应该在服务的每个具体的方法里开启一个子线程,然后再这里去处理那些耗时的逻辑。因此,一个比较标准的服务就可以写成如下形式了:

package com.jack.servicetest;import android.app.Notification;import android.app.PendingIntent;import android.app.Service;import android.content.Intent;import android.os.Binder;import android.os.IBinder;import android.util.Log;public class MyService extends Service {/*private DownloadBinder mBinder=new DownloadBinder();class DownloadBinder extends Binder{public void startDownload(){Log.d(MyService, startdownload executed);}public int getProgress(){Log.d(MyService, getProgress executed);return 0;}}*/@Overridepublic IBinder onBind(Intent intent) {// TODO Auto-generated method stub//return mBinder;return null;}/*@Overridepublic void onCreate() {// TODO Auto-generated method stubsuper.onCreate();@SuppressWarnings(deprecation)Notification notification=new Notification(R.drawable.ic_launcher,Notification comes,System.currentTimeMillis());Intent notificationIntent=new Intent(this,MainActivity.class);PendingIntent pendingIntent=PendingIntent.getActivity(this, 0,notificationIntent,0);notification.setLatestEventInfo(this, this is title, this is content,pendingIntent);startForeground(1, notification); 可以看到,这里只是修改了onCreate()方法中的代码,相信这部分代码你会非常眼熟。这就是我们前面学习的 创建通知的方法。只不过这次在构建出Notification对象并没有使用NotificationManager来将通知显示 出来,而是调用了startForeground()方法。这个方法接收两个参数,第一个参数是通知的id,类似于notify()方法 的第一个参数,第二个参数则是构建出来的Notification对象。调用startForeground()方法后就会让MyService变成 一个前台服务,并在系统状态显示出来。 Log.d(MyService, onCreate());}*//*@Overridepublic void onDestroy() {// TODO Auto-generated method stubsuper.onDestroy();Log.d(MyService, onDestroy());}*/@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {// TODO Auto-generated method stubLog.d(MyService, onStartCommand);new Thread(new Runnable(){@Overridepublic void run() {// TODO Auto-generated method stub//处理具体的逻辑}}).start();return super.onStartCommand(intent, flags, startId);}}

但是,这种服务一旦启动之后,就会一直处于运行状态,必须调用stopService()或者stopSelf()方法才能让服务停止下来。所以,如果想要实现一个服务执行完毕后自动停止的功能,就可以这样写:

package com.jack.servicetest;import android.app.Notification;import android.app.PendingIntent;import android.app.Service;import android.content.Intent;import android.os.Binder;import android.os.IBinder;import android.util.Log;public class MyService extends Service {/*private DownloadBinder mBinder=new DownloadBinder();class DownloadBinder extends Binder{public void startDownload(){Log.d(MyService, startdownload executed);}public int getProgress(){Log.d(MyService, getProgress executed);return 0;}}*/@Overridepublic IBinder onBind(Intent intent) {// TODO Auto-generated method stub//return mBinder;return null;}/*@Overridepublic void onCreate() {// TODO Auto-generated method stubsuper.onCreate();@SuppressWarnings(deprecation)Notification notification=new Notification(R.drawable.ic_launcher,Notification comes,System.currentTimeMillis());Intent notificationIntent=new Intent(this,MainActivity.class);PendingIntent pendingIntent=PendingIntent.getActivity(this, 0,notificationIntent,0);notification.setLatestEventInfo(this, this is title, this is content,pendingIntent);startForeground(1, notification); 可以看到,这里只是修改了onCreate()方法中的代码,相信这部分代码你会非常眼熟。这就是我们前面学习的 创建通知的方法。只不过这次在构建出Notification对象并没有使用NotificationManager来将通知显示 出来,而是调用了startForeground()方法。这个方法接收两个参数,第一个参数是通知的id,类似于notify()方法 的第一个参数,第二个参数则是构建出来的Notification对象。调用startForeground()方法后就会让MyService变成 一个前台服务,并在系统状态显示出来。 Log.d(MyService, onCreate());}*//*@Overridepublic void onDestroy() {// TODO Auto-generated method stubsuper.onDestroy();Log.d(MyService, onDestroy());}*/@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {// TODO Auto-generated method stubLog.d(MyService, onStartCommand);new Thread(new Runnable(){@Overridepublic void run() {// TODO Auto-generated method stub//处理具体的逻辑stopSelf();}}).start();return super.onStartCommand(intent, flags, startId);}}

虽然这种写法并不复杂,但是总会有一些人忘记开启线程,或者忘记调用stopSelf()方法。为了可以简单地创建一个异步的,会自动停止的服务,android专门提供了一个IntentService类,这个类就很好的解决了前面所提到的两种尴尬,下面我们来看下它的用法。

新建一个MyIntentService类继承IntentService,代码如下所示:

package com.jack.servicetest;import android.app.IntentService;import android.content.Intent;import android.util.Log;public class MyIntentService extends IntentService {/* 这里首先是提供了一个无参的构造函数,并且必须在其内部调用父类的有参构造函数。然后要在子类中去实现 onHandleIntent()这个抽象方法,在这个方法中可以处理一些具体的逻辑,而且不用担心ANR的问题,因为 这个方法已经是在子线程中运行的了。这里为了证实一下,我们在onHandleIntent()方法中打印了当前线程的id。 另外根据IntentService的特性,这个服务在运行结束后应该是会自动停止的,所以我们又重写了onDestroy()方法,在 这里也打印l一行日志,以证实是不是停止掉了。 */public MyIntentService() {super(MyIntentService);//调用父类的有参构造函数// TODO Auto-generated constructor stub}@Overrideprotected void onHandleIntent(Intent arg0) {// TODO Auto-generated method stub//打印当前线程的idLog.d(MyIntentService, Thread id is +Thread.currentThread().getId());}@Overridepublic void onDestroy() {// TODO Auto-generated method stubsuper.onDestroy();Log.d(MyIntentService, onDestroy() executed);}}

接下来修改activity_main.xml中的代码,加入一个用于启动MyIntentService这个服务的按钮,如下所示:


然后修改MainActivity中的代码,如下所示:
package com.jack.servicetest;//import com.jack.servicetest.MyService.DownloadBinder;import android.app.Activity;import android.content.ComponentName;import android.content.Intent;import android.content.ServiceConnection;import android.os.Bundle;import android.os.IBinder;import android.util.Log;import android.view.Menu;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;public class MainActivity extends Activity implements OnClickListener{private Button startService;private Button stopService;private Button bindService;private Button unbindService;private Button startIntentService;//private MyService.DownloadBinder downloadBinder;private ServiceConnection connection=new ServiceConnection() {/* * 这里创建了一个ServiceConnection的匿名类,在这里重写了onServiceConnected方法和 * onServiceDisconnected方法,这两个方法分别会在活动与服务成功绑定以及解除绑定的时候调用。 * 在onServiceConnected方法中,我们又通过向下转型得到了DownloadBinder的实例,有了这个 * 实例,活动和服务之间的关系就变得非常紧密了,现在我们可以在活动中根据具体的场景来调用DownloadBinder * 中的任何public方法,及实现了指挥服务干什么,服务就干什么的功能,这里只做了简单的测试,在onServiceConnected * 中调用了DownloadBinder的startDownload(),getProgress()方法。 * */@Overridepublic void onServiceDisconnected(ComponentName name) {// TODO Auto-generated method stub}@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {// TODO Auto-generated method stub/*downloadBinder=(MyService.DownloadBinder) service;downloadBinder.startDownload();downloadBinder.getProgress();*/}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);startService=(Button) findViewById(R.id.start_service);stopService=(Button) findViewById(R.id.stop_service);startService.setOnClickListener(this);stopService.setOnClickListener(this);bindService = (Button) findViewById(R.id.bind_service);unbindService = (Button) findViewById(R.id.unbind_service);bindService.setOnClickListener(this);unbindService.setOnClickListener(this);startIntentService=(Button) findViewById(R.id.start_intent_service);startIntentService.setOnClickListener(this);}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// Inflate the menu; this adds items to the action bar if it is present.getMenuInflater().inflate(R.menu.main, menu);return true;}@Overridepublic void onClick(View v) {// TODO Auto-generated method stubswitch(v.getId()){case R.id.start_service:Intent startIntent =new Intent(this,MyService.class);startService(startIntent);//启动服务break;case R.id.stop_service:Intent stopIntent =new Intent(this,MyService.class);stopService(stopIntent);//停止服务break;case R.id.bind_service:/* *现在我们需要进行活动和服务的绑定,构建一个Intent对象,然后调用bindService()方法将 *MainActivity()和MyService进行绑定。 bindService方法接收三个参数,第一个参数就是 *上面创建出的Intent对象,第二个参数就是前面创建出的ServiceConnection的实例,第三个 *参数则是一个标志位,这里传入BIND_AUTO_CREATE表示在活动和服务进行绑定后自动创建服务。 *这会使得MyService中的onCreate()方法得到执行,但onStartCommand()方法不会执行。 * */Intent bindIntent=new Intent(this,MyService.class); bindService(bindIntent, connection, BIND_AUTO_CREATE);//绑定服务break;case R.id.unbind_service:/* * 如果我们想解除活动和服务之间的绑定,调用一下unbindService()方法就可以了。 * */unbindService(connection);//解绑服务break;case R.id.start_intent_service://打印主线程的idLog.d(MainActivity, Thread id is+Thread.currentThread().getId());Intent intentService=new Intent(this,MyIntentService.class);startService(intentService);break;default:break;}}}/* 服务也有自己的生命周期,前面我们使用到的onCreate(),onStartCommand(),onBind()和onDestroy()等方法 都是在服务的生命周期内可能回掉的方法。 一旦在项目的任何位置调用了Context的startService()方法,相应的服务就会启动起来,并回调onStartCommand()。如果 这个服务之前还没创建过,onCreate()方法会先于onStartCommand()方法执行。服务启动了之后一直保持运行状态, 直到stopService()或stopSelf()方法被调用。注意虽然每调用一次startService()方法,onStartCommand()就会 执行一次,但实际上每个服务都只会存在一个实例。所以不管你调用了多少次startService()方法,只需调用一次stopService() 或stopSelf()方法,服务就会停止下来了。 另外,还可以调用Context的bindService()来获取一个服务的持久连接,这时就会回调服务中的onBind()方法。类似地, 如果这个服务之前还没有创建过,onCreate()方法会先于onBind()方法执行。之后,调用方可以获取到onBind()方法里 返回的IBinder对象的实例,这样就能自由地和服务进行通信了。只要调用方和服务之间的连接没有断开,服务就会一直保持运行状态。 当调用了startService()方法后,又去调用stopService()方法,这时服务中的onDestroy()方法就会执行,表示 服务已经销毁了。类似地,当调用了bindService()方法后,又去调用unbindService()方法,onDestroy()方法也会执行,这 两种情况都很好理解。但是需要注意,我们是完全有可能对一个服务既调用了startService()方法,又调用了bindService()方法的, 这种情况下该如何才能让服务销毁掉?根据android系统的机制,一个服务只要被启动或者绑定了之后就会一直处于运行状态,必须要让以上两种条件同时 不满足,服务才能被销毁。所以,这种情况下需要同时调用stopService()和unbindService()方法,onDestroy()方法才会执行。 */

可以看到,我们在start intentservice按钮的点击事件里面去启动MyIntentService这个服务,并在这里打印了一下主线程的id,其实IntentService的用法和普通的服务没什么两样。

在AndroidManifest.xml里注册,如下所示:


重新运行程序,界面如下所示:

可以看到MyIntentService和MainActivity所在的线程id不一样,而且onDestory()方法也得到了执行,说明MyIntentService在运行完毕后确实自动停止了。集开启线程和自动停止于一身。

服务的最佳实践---------后台执行的定时任务

Android中实现定时任务一般有两种方式,一种是使用java api里提供的Timer类,一种是使用android的Alarm机制。
这两种方式在多数情况下都能实现类似的效果,但是Timer有一个明显的短板,它并不太适用于那些需要长期在后台运行的定时任务。我们都知道,为了能让电池更加耐用,每种手机都会有自己的休眠策略,andorid手机就会在长时间不操作的情况下自动让cpu进入的到睡眠状态,这就有可能导致Timer中的定时任务无法正常运行。而Alarm机制不存在这种情况,它具有唤醒cpu的功能,即可以保证每次需要执行定时任务的时候cpu都能正常工作。需要注意,这里的唤醒cpu和唤醒屏幕完全不是同一个概念,不要弄混淆了。
我们来看看Alarm机制的用法吧,主要需要借助AlarmManager类来实现。这个类和NotificationManager有点类似,都是通过调用Context的getSystemService()方法来获取实例的,只是这里需要传入的参数是Context.ALARM_SERVICE.
因此,获取一个AlarmManager的实例就可以写成:
AlarmManager manager=(AlarmManager)getSystemService(Context.ALARM_SERVICE);
接下来调用AarmManager的set()方法就可以设置一个定时任务了,比如说想要设定一个任务在10秒后执行,就可以写成:
long triggerAtTime=SystemClock.elapsedRealtime()+10*1000;
manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,triggerAtTime,pendingIntent);
第一个参数是一个整形参数,用于指定AlarmManager的工作类型,有四种值可选,分别是ELAPSED_REALTIME,ELAPSED_REALTIME_WAKEUP,
RTC 和 RTC_WAKEUP。其中ELAPSED_REALTIME表示让定时任务的触发从系统开机开始算起,但不会唤醒cpu。
ELAPSED_REALTIME_WAKEUP同样表示让定时任务的触发时间从系统开机开始算起,但会唤醒cpu。
RTC表示让定时任务的触发时间从1970年1月1日0点开始算起,但不会唤醒cpu。

RTC_WAKEUP同样表示让定时任务的触发时间从1970年1月1日0点开始算起,但会唤醒cpu。使用SystemClock.elapsedRealtime()方法
可以获取到系统开机至今所历经的毫秒数,使用System.currentTimeMillis()方法可以获取到1970年1月1日0点
至今所经历时间的毫秒数。
第二个参数就是定时任务触发的时间,以毫秒为单位。如果第一个参数使用的是ELAPSED_REALTIME或ELAPSED_REALTIME_WAKEUP则这里传入开机至今的时间在加上延迟执行的时间。如果第一个参数使用的是RTC或RTC_WAKEUP,则这里传入1970年1月1日0点至今的时间再加上延迟执行的时间。
第三个参数是一个PendingIntent,对于它应该不会陌生了 吧。这里我们一般会调用getBroadcast()方法来
获取一个能够执行广播的PendingIntent。这样当定时任务被触发的时候,广播接收器的onReceive()方法就可以得到执行。
了解了 set()方法的每个参数之后,你应该能想到,设定一个任务在10秒后执行还可以写成:
long triggerAtTime=System.curentTimeMillis()+10*1000;
manager.set(AlarmManager.RTC_WAKEUP,triggerAtTime,pendingIntent);
现在已经掌握了Alarm机制的基本用法,下面我们就来创建一个可以长期在后台执行定时任务的服务。创建一个ServiceBestPractice项目,
然后新增一个LongRunningService类,代码如下所示:

package com.jcak.servicebestpractice;import java.util.Date;import android.app.AlarmManager;import android.app.PendingIntent;import android.app.Service;import android.content.Intent;import android.os.IBinder;import android.os.SystemClock;import android.util.Log;public class LongRunningService extends Service {@Overridepublic IBinder onBind(Intent intent) {// TODO Auto-generated method stubreturn null;}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {// TODO Auto-generated method stubnew Thread(new Runnable(){@Overridepublic void run() {// TODO Auto-generated method stubLog.d(LongRunningService,executed at +new Date().toString());}}).start();AlarmManager manager=(AlarmManager) getSystemService(ALARM_SERVICE);int anHour=10*1000;long triggerAtTime=SystemClock.elapsedRealtime()+anHour;Intent i=new Intent(this,AlarmReceiver.class);PendingIntent pi=PendingIntent.getBroadcast(this, 0, i, 0);manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pi);return super.onStartCommand(intent, flags, startId);}}

在onStartCommand()方法中开启了一个子线程,然后在子线程里就可以执行具体的逻辑操作了,这里简单的,只是打印了当前的时间。

创建线程之后的代码就是上面讲解的Alarm机制的用法,先是获取到了AlarmManager的实例,然后定义任务的触发时间为10秒,在使用PendingIntent指定处理定时任务的广播接收器为AlarmReceiver,最后调用set()方法完成设定。显然,AlarmReceiver不存在,我们就创建一个,代码如下所示:

package com.jcak.servicebestpractice;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;public class AlarmReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {// TODO Auto-generated method stubIntent i=new Intent(context,LongRunningService.class);context.startService(i);}}

onReceiver()方法里的代码非常简单,就是构建出了一个Intent对象,然后去启动LongRunningService这个服务

。这就已经将一个长期服务在后台定时运行的服务完成了。因为一旦启动了LongRunningService,就会在onStartCommand()方法里设定一个定时任务,这样10秒后AlarmReceiver的onReceive()方法就将得到执行了,然后我们在这里再次启动LongRunningService,这样就形成了一个永久的循环,保证LongRunningService可以每隔10秒就会启动一次,这个长期在后台运行的服务就完成了。

接下来,我们需要在打开程序的时候启动一次LongRunningService,之后LongRunningService就可以一直运行了。修改MainActivity中的代码,如下所示:

package com.jcak.servicebestpractice;import android.os.Bundle;import android.app.Activity;import android.content.Intent;import android.view.Menu;public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Intent intent=new Intent(this,LongRunningService.class);startService(intent);}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// Inflate the menu; this adds items to the action bar if it is present.getMenuInflater().inflate(R.menu.main, menu);return true;}}

最后要注册服务和广播,代码如下所示:

现在运行一下程序,然后观察LogCat中打印的日志,

可以看到LongRunningService每隔10秒打印一条日志。

另外需要注意的是,从android4.4版开始,Alarm任务的触发时间将会变得不准确,有可能会延迟一段时间后任务才能得到执行。这并不是bug,而是系统在耗电方面进行的优化。系统会自动检测目前有多少Alarm任务存在,然后将触发时间将近的几个任务存放在一起执行,这就可以大幅度减少cpu被唤醒的次数,从而有效延长电池的使用时间。

当然,如果要求Alarm任务的执行时间必须准确无误,android仍然提供l解决方案。使用AlarmManager的setExact()方法来替代set()方法,就可以保证任务准时执行了。

四 : Android入门系列一(Android学习方法)

Android学习方法

一:了解什么是Android

二: 建立开发环境

三: 阅读SDK文档

四: 背景知识

1、Java

2、面向对象

3、设计模式

4、J2ME、Brew、Symbian

先说说什么是Android;

1、Android是一种基于Linux的自由及开放源代码的操作系统,主要使用于便携设备,如智能手机和平板电脑。它包括一个操作系统,中间件和一些重要的应用程序。Beta版的Android SDK提供了在Android平台上使用Java语言进行Android应用开发必须的工具和API接口。

2、特性

&bull; 应用程序框架支持组件的重用与替换

&bull; Dalvik D alvik 虚拟机专为移动设备优化

&bull; 集成的浏览器基于开源的WebKit引擎

&bull; 优化的图形库包括定制的2D图形库,3D图形库基于OpenGL ES 1.0(硬件加速可选)

&bull; SQLite SQLite SQLite用作结构化的数据存储

&bull; 多媒体支持包括常见的音频、视频和静态图像格式(如MPEG4, H.264, MP3, AAC,AMR, JPG, PNG,

GIF)

&bull; GSM电话技术(依赖于硬件)

&bull; 蓝牙Bluetooth, Bluetooth, Bluetooth, Bluetooth,EDGE, EDGE, EDGE, EDGE,3G, 3G,和WiFi WiFi WiFi W iFi (依赖于硬件)

&bull; 照相机,GPS,指南针,和加速度计(accelerometer accelerometer accelerometer)(依赖于硬件)

&bull; 丰富的开发环境包括设备模拟器,调试工具,内存及性能分析图表,和Eclipse集成开发环境插件。

3、应用程序

Android会同一系列核心应用程序包一起发布,该应用程序包包括email客户端,SMS短消息程序,日历,地图,浏览器,联系人管理程序等。所有的应用程序都是使用JAVA语言编写的。

4、应用程序框架

开发人员也可以完全访问核心应用程序所使用的API框架。该应用程序的架构设计简化了组件的重用;任何

一个应用程序都可以发布它的功能块并且任何其它的应用程序都可以使用其所发布的功能块(不过得遵循框

架的安全性限制)。同样,该应用程序重用机制也使用户可以方便的替换程序组件。

隐藏在每个应用后面的是一系列的服务和系统, 其中包括;

&bull; 丰富而又可扩展的视图(Views),可以用来构建应用程序,它包括列表(lists),网格(grids),文

本框(text boxes),按钮(buttons), 甚至可嵌入的web浏览器。

&bull; 内容提供器(Content Providers)使得应用程序可以访问另一个应用程序的数据(如联系人数据库),或

者共享它们自己的数据

&bull; 资源管理器(Resource Manager)提供非代码资源的访问,如本地字符串,图形,和布局文件(layout

files )。

&bull; 通知管理器 (Notification Manager)使得应用程序可以在状态栏中显示自定义的提示信息。

&bull; 活动管理器(Activity Manager)用来管理应用程序生命周期并提供常用的导航回退功能。

5、程序库

Android包含一些C/C++库,这些库能被Android系统中不同的组件使用。它们通过Android应用程序框架

为开发者提供服务。以下是一些核心库:

&bull; 系统C库- 一个从BSD继承来的标准C系统函数库(libc ), 它是专门为基于embedded linux

的设备定制的。

&bull; 媒体库- 基于PacketVideo OpenCORE;该库支持多种常用的音频、视频格式回放和录制,同时支持

静态图像文件。编码格式包括MPEG4, H.264,MP3,AAC,AMR, JPG, PNG。

&bull; Surface Surface SurfaceManager Manager M anager - 对显示子系统的管理,并且为多个应用程序提 供了2D和3D图层的无缝融合。

&bull; LibWebCore LibWebCore LibWebCore- 一个最新的web浏览器引擎用,支持Android浏览器和一个可嵌入的web视图。

&bull; SGL- 底层的2D图形引擎

&bull; 3Dlibraries libraries libraries- 基于OpenGL ES 1.0APIs实现;该库可以使用硬件3D加速(如果可用)或者使用高

度优化的3D软加速。

&bull; FreeType FreeType FreeType-位图(bitmap)和矢量(vector)字体显示。

&bull; SQLite SQLite SQLite- 一个对于所有应用程序可用,功能强劲的轻型关系型数据库引擎。

6、Android 运行库

Android包括了一个核心库,该核心库提供了JAVA编程语言核心库的大多数功能。

每一个Android应用程序都在它自己的进程中运行,都拥有一个独立的Dalvik虚拟 机实例。Dalvik被设计

成一个设备可以同时高效地运行多个虚拟系统。Dalvik虚拟机执行(.dex)的Dalvik可执行文件,该格式文

件针对小内存使用做了 优化。同时虚拟机是基于寄存器的,所有的类都经由JAVA编译器编译,然后通过SDK

中 的"dx" 工具转化成.dex 格式由虚拟机执行。

Dalvik虚拟机依赖于linux内核的一些功能,比如线程机制和底层内存管理机制。

7、Linux Linux Linux内核

Android的核心系统服务依赖于Linux 2.6内核,如安全性,内存管理,进程管理,网络协议栈和驱动模型。

Linux内核也同时作为硬件和软件栈之间的抽象层。

8、Android的系统架构

android学习方法 Android入门系列一(Android学习方法)

8.1、Android内核

Linux内核版本2.6

位于硬件和软件堆之间的抽象层

核心服务:安全机制、内存管理、进程管理、网络、硬件驱动。

android学习方法 Android入门系列一(Android学习方法)

Android依赖Linux内核2.6提供核心服务,比如安全、内存管理、进程管理、网络、硬件驱动。在这里,Linux

内核扮演的是硬件层和系统其它层次之间的一个抽象层的概念。这个操作系统并非类GNU/Linux的,因为其

系统库,系统初始化和编程接口都和标准的Linux系统是有所不同的。

从Google目前release的Linux系统来看,其没有虚拟内存文件系统,系统所用的是yaffs2文件系统,具体

扩展:android入门学习 / 英语入门学习方法 / 学习速写快速入门方法

的映像也都位于SDK安装目录下。通过emulator -console命令,我们可以在host中断下得到一个简单的可以

控制Android的shell,这个系统包含了一个Toolbox,提供一些基本的命令工具,集中在

/sbin,/system/sbin,/system/bin 中,但是很简陋,命令种类也很少。

目前Android的程序安装模式是靠Eclipse自动进行的,通过对底层的分析可知,大致步骤就是在/data/app和

data/data下存放android底层和普通内核没有什么大的区别,我们可以将其作为一个Linux来进行开发和

hacking。

8.2、Lib和运行环境

android学习方法 Android入门系列一(Android学习方法)

lib

C/C++库:被各种Android组件使用

通过应用程序框架开发者可以使用其功能

包括:

媒体库:MPEG4 H.264 MP3 JPG PNG .....

WebKit/LibWebCore:Web浏览引擎

SQLite关系数据库引擎

2D,3D图形库、引擎

丰富的类库支持:2D和3D图像库OpenGL ES、数据库SQLite、对象数据库db4o类库、媒体库、基于Linux

底层系统C库等等,让应用开发更简单多样。Google使用Apache的Harmony类库,Harmony某些方面速度

快于Sun的VM。Runtime在Dalvik Java VM上,Dalvik采用简练、高效的byte code格式运行,它能够在低

资耗和没有应用相互干扰的情况下并行执行多个应用。

运行时环境

核心库提供的Java功能

Dalvik虚拟机依赖于Linux内核,例如线程或底层内存管理

设备可以运行多个Dalvik虚拟机,每一个Android应用程序在它自己的Dalvik VM实例中运行

VM执行优化的Dalvik可执行文件(.dex)

Dx-工具把编译过的Java文件转换为dex文件

8.3应用和框架

核心应用,例如联系人,电子邮件,电话,浏览器,日历,地图,...

充分访问所有核心应用框架API

简化组件的重用

用Java编写应用程序

android学习方法 Android入门系列一(Android学习方法)

扩展:android入门学习 / 英语入门学习方法 / 学习速写快速入门方法

五 : Android UI学习对View的onMeasure方法理解

Android UI学习|对View的onMeasure方法理解

-泡在网上的日子|一个程序员的网站|为了所有的程序员 - http://www.61k.com

参考一

我们知道View在屏幕上显示出来要先经过measure和layout. 在调用onMeasure(int widthSpec, int heightSpec)方法时,要涉及到MeasureSpec的使用,MeasureSpec有3种模式分别是UNSPECIFIED, EXACTLY和AT_MOST, 那么这些模式和我们平时设置的layout参数fill_parent, wrap_content有什么关系呢。经过代码测试就知道,当我们设置width或height为fill_parent时,容器在布局时调用子 view的measure方法传入的模式是EXACTLY,因为子view会占据剩余容器的空间,所以它大小是确定的。而当设置为 wrap_content时,容器传进去的是AT_MOST, 表示子view的大小最多是多少,这样子view会根据这个上限来设置自己的尺寸。当子view的大小设置为精确值时,容器传入的是EXACTLY, 而MeasureSpec的UNSPECIFIED模式目前还没有发现在什么情况下使用。

View的onMeasure方法默认行为是当模式为UNSPECIFIED时,设置尺寸为

mMinWidth(通常为0)或者背景drawable的最小尺寸,当模式为EXACTLY或者AT_MOST时,尺寸设置为传入的MeasureSpec的大小。

有个观念需要纠正的是,fill_parent应该是子view会占据剩下容器的空间,而不会覆盖前面已布局好的其他view空间,当然后面布局子 view就没有空间给分配了,所以fill_parent属性对布局顺序很重要。以前所想的是把所有容器的空间都占满了,难怪google在2.2版本里 把fill_parent的名字改为match_parent.

参考二

onMeasure方法在父元素正要放置该控件时调用.它会问一个问题,“你想要用多大地方啊?”,然后传入两个参数——widthMeasureSpec和heightMeasureSpec.

它们指明控件可获得的空间以及关于这个空间描述的元数据.

比返回一个结果要好的方法是你传递View的高度和宽度到setMeasuredDimension方法里.

接下来的代码片段给出了如何重写onMeasure.注意,调用的本地空方法是来计算高度和宽度的.它们会译解widthHeightSpec和heightMeasureSpec值,并计算出合适的高度和宽度值.

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

int measuredHeight = measureHeight(heightMeasureSpec);

int measuredWidth = measureWidth(widthMeasureSpec);

setMeasuredDimension(measuredHeight, measuredWidth);

}

private int measureHeight(int measureSpec) {

// Return measured widget height.

}

private int measureWidth(int measureSpec) {

// Return measured widget width.

}

边界参数——widthMeasureSpec和heightMeasureSpec ,效率的原因以整数的方式传入。在它们使用之前,首先要做的是使用MeasureSpec类的静态方法getMode和

getSize来译解,如下面的片段所示:

int specMode = MeasureSpec.getMode(measureSpec);

int specSize = MeasureSpec.getSize(measureSpec);

依据specMode的值,如果是AT_MOST,specSize 代表的是最大可获得的空间;如果是EXACTLY,specSize 代表的是精确的尺寸;如果是UNSPECIFIED,对于控件尺寸来说,没有任何参考意义。

当以EXACT方式标记测量尺寸,父元素会坚持在一个指定的精确尺寸区域放置View。在父元素问子元素要多大空间时,AT_MOST指示者会说给我最大的范围。在很多情况下,你得到的值都是相同的。

在两种情况下,你必须绝对的处理这些限制。在一些情况下,它可能会返回超出这些限制的尺寸,在这种情况下,你可以让父元素选择如何对待超出的View,使用裁剪还是滚动等技术。

接下来的框架代码给出了处理View测量的典型实现:

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

int measuredHeight = measureHeight(heightMeasureSpec);

int measuredWidth = measureWidth(widthMeasureSpec);

setMeasuredDimension(measuredHeight, measuredWidth);

}

private int measureHeight(int measureSpec) {

int specMode = MeasureSpec.getMode(measureSpec);

int specSize = MeasureSpec.getSize(measureSpec);

// Default size if no limits are specified.

int result = 500;

if (specMode == MeasureSpec.AT_MOST){

// Calculate the ideal size of your

// control within this maximum size.

// If your control fills the available

// space return the outer bound.

result = specSize;

}

else if (specMode == MeasureSpec.EXACTLY){

// If your control can fit within these bounds return that value.

result = specSize;

}

return result;

}

private int measureWidth(int measureSpec) {

int specMode = MeasureSpec.getMode(measureSpec);

int specSize = MeasureSpec.getSize(measureSpec);

// Default size if no limits are specified.

int result = 500;

if (specMode == MeasureSpec.AT_MOST){

// Calculate the ideal size of your control

// within this maximum size.

// If your control fills the available space

// return the outer bound.

result = specSize;

}

else if (specMode == MeasureSpec.EXACTLY){

// If your control can fit within these bounds return that value.

result = specSize;

}

return result;

}

本文标题:android学习方法-Android的SQLite学习及使用方法
本文地址: http://www.61k.com/1144694.html

61阅读| 精彩专题| 最新文章| 热门文章| 苏ICP备13036349号-1