Service的声明
Service是Android中的四大组件,使用它必定要在AndroidManifest.xml中声明,在AndroidManifest.xml中声明是为了让PackageManagerService能解析出该Service, 并创建对应的数据结构。以下图所示,
如图中所示,Service也能够定义IntentFilter.
Service分为以下三类java
foreground service
fg Service执行一些对于用户来讲是可感知的操做,如audio应用使用fg service来播放歌曲。
background service
bg service执行的操做对用户而言是不可感知的。
bound service
bound service主要是提供c/s接口,容许组件与service进行通讯,或者是跨进程的通讯。android
其实说到底,因为启动方式的不一样致使了三种service,
startService -> background service.
startForegroundService -> foreground service
bindService -> bound servicesql
2、foreground和background service
对于fg和bg service,它们的启动方式不一样,分别是startForegroundService和startService数据库
@Override public ComponentName startService(Intent service) { return startServiceCommon(service, false, mUser); } @Override public ComponentName startForegroundService(Intent service) { return startServiceCommon(service, true, mUser); } private ComponentName startServiceCommon(Intent service, boolean requireForeground, UserHandle user) { }
从启动方式能够看出,它们的仅仅在于 requireForeground,即一个boolean形的标志位决定是bg仍是fg service.网络
2.1 bg/fg启动流程
数据结构
其中 retriveServiceLocked 函数,主要去创建以下的关系图
并发
从图中能够看出,能够经过IntentFilter与ComponentName 两种方式去指定一个service.app
第一次启动 service的生命周期 onCreate(scheduleCreateService) -> onStartCommand(AMS 调用scheduleServiceArgs)
多个地方(如Activity)能够屡次调用startService, 若是以前已经打开,直接进入onStartCommand就好了
注意: 须要手动调用 stopService去中止Service框架
而对于IntentService.
IntentService继承于Service, 它的实现至关于在Service的基础上增长了一个HandlerThread, 以及自定义的Handler, IntentService将全部的业务 route 到HandlerThread线程中去处理(onHandleIntent), 当onHandleIntent处理完后,就会调用stopSelf来中止到这个Service,
因此每次启动一个IntentService, 都是通过这样的生命周期
onCreate -> onStartCommand -> onStart -> onHandleIntent -> onDestroy,
其中onStart/onStartCommand都将Intent route到了onHandleIntent中去处理ide
ndroid 广播接收器(Broadcast Receivers)
广播接收器用于响应来自其余应用程序或者系统的广播消息。这些消息有时被称为事件或者意图。例如,应用程序能够初始化广播来让其余的应用程序知道一些数据已经被下载到设备,并能够为他们所用。这样广播接收器能够定义适当的动做来拦截这些通讯。
有如下两个重要的步骤来使系统的广播意图配合广播接收器工做。
建立广播接收器
注册广播接收器
还有一个附加的步骤,要实现自定义的意图,你必须建立并广播这些意图。
建立广播接收器
广播接收器须要实现为BroadcastReceiver类的子类,并重写onReceive()方法来接收以Intent对象为参数的消息。
public class MyReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context, "Intent Detected.", Toast.LENGTH_LONG).show(); } }
注册广播接收器
应用程序经过在AndroidManifest.xml中注册广播接收器来监听制定的广播意图。假设咱们将要注册MyReceiver来监听系统产生的ACTION_BOOT_COMPLETED事件。该事件由Android系统的启动进程完成时发出。
<application android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <receiver android:name="MyReceiver"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED"> </action> </intent-filter> </receiver> </application>
如今,不管何时Android设备被启动,都将被广播接收器MyReceiver所拦截,而且在onReceive()中实现的逻辑将被执行。
有许多系统产生的事件被定义为类Intent中的静态常量值。下面的表格列举了重要的系统事件
广播自定义意图
若是你想要应用程序中生成并发送自定义意图,你须要在活动类中经过sendBroadcast()来建立并发送这些意图。若是你使用sendStickyBroadcast(Intent)方法,则意图是持久的(sticky),这意味者你发出的意图在广播完成后一直保持着。
public void broadcastIntent(View view) { Intent intent = new Intent(); intent.setAction("cn.uprogrammer.CUSTOM_INTENT"); sendBroadcast(intent); } cn.uprogrammer.CUSTOM_INTENT的意图能够像以前咱们注册系统产生的意图同样被注册。 <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <receiver android:name="MyReceiver"> <intent-filter> <action android:name="cn.uprogrammer.CUSTOM_INTENT"> </action> </intent-filter> </receiver> </application>
如今点击"广播意图"按钮来广播咱们的自定义意图。这将广播咱们的自定义意图"cn.programmer.CUSTOM_INTENT",在咱们注册的广播接收器MyReceiver中拦截并执行咱们实现的逻辑。模拟器的底部将出现toast。以下:
你能够尝试实现其余的广播接收器来拦截系统产生的意图,如系统启动,日期改变和低电量等。
本节带来的Android中的AlarmManager(闹钟服务),听名字咱们知道能够经过它开发手机闹钟类的APP, 而在文档中的解释是:在特定的时刻为咱们广播一个指定的Intent,简单说就是咱们本身定一个时间, 而后当到时间时,AlarmManager会为咱们广播一个咱们设定好的Intent,好比时间到了,能够指向某个 Activity或者Service!另外官方文档中有一些要注意的地方:
另外要注意一点的是,AlarmManager主要是用来在某个时刻运行你的代码的,即时你的APP在那个特定 时间并无运行!还有,从API 19开始,Alarm的机制都是非准确传递的,操做系统将会转换闹钟 ,来最小化唤醒和电池的使用!某些新的API会支持严格准确的传递,见 setWindow(int, long, long, PendingIntent)和setExact(int, long, PendingIntent)。 targetSdkVersion在API 19以前应用仍将继续使用之前的行为,全部的闹钟在要求准确传递的状况 下都会准确传递。更多详情可见官方API文档:AlarmManager
1.Timer类与AlarmManager类区别:
若是你学过J2SE的话,那么你对Timer确定不会陌生,定时器嘛,通常写定时任务的时候 确定离不开他,可是在Android里,他却有个短板,不太适合那些须要长时间在后台运行的 定时任务,由于Android设备有本身的休眠策略,当长时间的无操做,设备会自动让CPU进入 休眠状态,这样就可能致使Timer中的定时任务没法正常运行!而AlarmManager则不存在 这种状况,由于他具备唤醒CPU的功能,能够保证每次须要执行特定任务时CPU都能正常工做, 或者说当CPU处于休眠时注册的闹钟会被保留(能够唤醒CPU),但若是设备被关闭,或者从新 启动的话,闹钟将被清除!(Android手机关机闹钟不响...)
2.得到AlarmManager实例对象:
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
3.相关方法讲解:
set(int type,long startTime,PendingIntent pi):一次性闹钟
setRepeating(int type,long startTime,long intervalTime,PendingIntent pi): 重复性闹钟,和3有区别,3闹钟间隔时间不固定
setInexactRepeating(int type,long startTime,long intervalTime,PendingIntent pi): 重复性闹钟,时间不固定
cancel(PendingIntent pi):取消AlarmManager的定时服务
getNextAlarmClock():获得下一个闹钟,返回值AlarmManager.AlarmClockInfo
setAndAllowWhileIdle(int type, long triggerAtMillis, PendingIntent operation) 和set方法相似,这个闹钟运行在系统处于低电模式时有效
setExact(int type, long triggerAtMillis, PendingIntent operation): 在规定的时间精确的执行闹钟,比set方法设置的精度更高
setTime(long millis):设置系统墙上的时间
setTimeZone(String timeZone):设置系统持续的默认时区
setWindow(int type, long windowStartMillis, long windowLengthMillis, PendingIntent operation): 设置一个闹钟在给定的时间窗触发。相似于set,该方法容许应用程序精确地控制操做系统调 整闹钟触发时间的程度。
关键参数讲解:
Type(闹钟类型): 有五个可选值: AlarmManager.ELAPSED_REALTIME: 闹钟在手机睡眠状态下不可用,该状态下闹钟使用相对时间(相对于系统启动开始),状态值为3; AlarmManager.ELAPSED_REALTIME_WAKEUP 闹钟在睡眠状态下会唤醒系统并执行提示功能,该状态下闹钟也使用相对时间,状态值为2; AlarmManager.RTC 闹钟在睡眠状态下不可用,该状态下闹钟使用绝对时间,即当前系统时间,状态值为1; AlarmManager.RTC_WAKEUP 表示闹钟在睡眠状态下会唤醒系统并执行提示功能,该状态下闹钟使用绝对时间,状态值为0; AlarmManager.POWER_OFF_WAKEUP 表示闹钟在手机关机状态下也能正常进行提示功能,因此是5个状态中用的最多的状态之一,该状态下闹钟也是用绝对时间,状态值为4;不过本状态好像受SDK版本影响,某些版本并不支持;
startTime:闹钟的第一次执行时间,以毫秒为单位,能够自定义时间,不过通常使用当前时间。 须要注意的是,本属性与第一个属性(type)密切相关,若是第一个参数对应的闹钟使用的是相对时间 (ELAPSED_REALTIME和ELAPSED_REALTIME_WAKEUP),那么本属性就得使用相对时间 (相对于系统启动时间来讲),好比当前时间就表示为:SystemClock.elapsedRealtime(); 若是第一个参数对应的闹钟使用的是绝对时间(RTC、RTC_WAKEUP、POWER_OFF_WAKEUP), 那么本属性就得使用绝对时间,好比当前时间就表示 为:System.currentTimeMillis()。
intervalTime:表示两次闹钟执行的间隔时间,也是以毫秒为单位.
PendingIntent:绑定了闹钟的执行动做,好比发送一个广播、给出提示等等。 PendingIntent是Intent的封装类。须要注意的是,若是是经过启动服务来实现闹钟提 示的话,PendingIntent对象的获取就应该采用Pending.getService (Context c,int i,Intent intent,int j)方法;若是是经过广播来实现闹钟 提示的话,PendingIntent对象的获取就应该采用 PendingIntent.getBroadcast (Context c,int i,Intent intent,int j)方法;若是是采用Activity的方式来实 现闹钟提示的话,PendingIntent对象的获取就应该采用 PendingIntent.getActivity(Context c,int i,Intent intent,int j)方法。 若是这三种方法错用了的话,虽然不会报错,可是看不到闹钟提示效果。
首先一个简单的布局文件:activity_main.xml,另外在res建立一个raw文件夹,把音频文件丢进去! 另外建立一个只有外层布局的activity_clock.xml做为闹钟响时Activity的布局!没东西,就不贴了
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/LinearLayout1" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <Button android:id="@+id/btn_set" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="设置闹钟" /> <Button android:id="@+id/btn_cancel" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="关闭闹钟" android:visibility="gone" /> </LinearLayout>
接着是MainActivity.java,也很简单:
public class MainActivity extends AppCompatActivity implements View.OnClickListener{ private Button btn_set; private Button btn_cancel; private AlarmManager alarmManager; private PendingIntent pi; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); bindViews(); } private void bindViews() { btn_set = (Button) findViewById(R.id.btn_set); btn_cancel = (Button) findViewById(R.id.btn_cancel); alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE); Intent intent = new Intent(MainActivity.this, ClockActivity.class); pi = PendingIntent.getActivity(MainActivity.this, 0, intent, 0); btn_set.setOnClickListener(this); btn_cancel.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()){ case R.id.btn_set: Calendar currentTime = Calendar.getInstance(); new TimePickerDialog(MainActivity.this, 0, new TimePickerDialog.OnTimeSetListener() { @Override public void onTimeSet(TimePicker view, int hourOfDay, int minute) { //设置当前时间 Calendar c = Calendar.getInstance(); c.setTimeInMillis(System.currentTimeMillis()); // 根据用户选择的时间来设置Calendar对象 c.set(Calendar.HOUR, hourOfDay); c.set(Calendar.MINUTE, minute); // ②设置AlarmManager在Calendar对应的时间启动Activity alarmManager.set(AlarmManager.RTC_WAKEUP, c.getTimeInMillis(), pi); Log.e("HEHE",c.getTimeInMillis()+""); //这里的时间是一个unix时间戳 // 提示闹钟设置完毕: Toast.makeText(MainActivity.this, "闹钟设置完毕~"+ c.getTimeInMillis(), Toast.LENGTH_SHORT).show(); } }, currentTime.get(Calendar.HOUR_OF_DAY), currentTime .get(Calendar.MINUTE), false).show(); btn_cancel.setVisibility(View.VISIBLE); break; case R.id.btn_cancel: alarmManager.cancel(pi); btn_cancel.setVisibility(View.GONE); Toast.makeText(MainActivity.this, "闹钟已取消", Toast.LENGTH_SHORT) .show(); break; } } }
而后是闹铃页面的ClockActivity.java:
/** * Created by Jay on 2015/10/25 0025. */ public class ClockActivity extends AppCompatActivity { private MediaPlayer mediaPlayer; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_clock); mediaPlayer = mediaPlayer.create(this,R.raw.pig); mediaPlayer.start(); //建立一个闹钟提醒的对话框,点击肯定关闭铃声与页面 new AlertDialog.Builder(ClockActivity.this).setTitle("闹钟").setMessage("小猪小猪快起床~") .setPositiveButton("关闭闹铃", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { mediaPlayer.stop(); ClockActivity.this.finish(); } }).show(); }
内容提供者组件经过请求从一个应用程序向其余的应用程序提供数据。这些请求由类 ContentResolver 的方法来处理。内容提供者可使用不一样的方式来存储数据。数据能够被存放在数据库,文件,甚至是网络。
有时候须要在应用程序之间共享数据。这时内容提供者变得很是有用。
内容提供者可让内容集中,必要时能够有多个不一样的应用程序来访问。内容提供者的行为和数据库很像。你能够查询,编辑它的内容,使用 insert(), update(), delete() 和 query() 来添加或者删除内容。多数状况下数据被存储在 SQLite 数据库。
内容提供者被实现为类 ContentProvider 类的子类。须要实现一系列标准的 API,以便其余的应用程序来执行事务。
建立内容提供者这里描述建立本身的内容提供者的简单步骤。
首先,你须要继承类 ContentProviderbase 来建立一个内容提供者类。
其次,你须要定义用于访问内容的你的内容提供者URI地址。
接下来,你须要建立数据库来保存内容。一般,Android 使用 SQLite 数据库,并在框架中重写 onCreate() 方法来使用 SQLiteOpenHelper 的方法建立或者打开提供者的数据库。当你的应用程序被启动,它的每一个内容提供者的 onCreate() 方法将在应用程序主线程中被调用。
最后,使用<provider.../>标签在 AndroidManifest.xml 中注册内容提供者。
如下是让你的内容提供者正常工做,你须要在类 ContentProvider 中重写的一些方法:
onCreate():当提供者被启动时调用。
query():该方法从客户端接受请求。结果是返回指针(Cursor)对象。
insert():该方法向内容提供者插入新的记录。
delete():该方法从内容提供者中删除已存在的记录。
update():该方法更新内容提供者中已存在的记录。
getType():该方法为给定的URI返回元数据类型。
下面是修改的主要活动文件 src/cn.uprogrammer.contentprovider/MainActivity.java 的内容。该文件包含每一个基础的生命周期方法。咱们添加了两个新的方法,onClickAddName() 和 onClickRetrieveStudents() 来让应用程序处理用户交互。
package cn.uprogrammer.contentprovider; import android.net.Uri; import android.os.Bundle; import android.app.Activity; import android.content.ContentValues; import android.content.CursorLoader; import android.database.Cursor; import android.view.Menu; import android.view.View; import android.widget.EditText; import android.widget.Toast; import cn.uprogrammer.contentprovider.R; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_main, menu); return true; } public void onClickAddName(View view) { // Add a new student record ContentValues values = new ContentValues(); values.put(StudentsProvider.NAME, ((EditText)findViewById(R.id.editText2)).getText().toString()); values.put(StudentsProvider.GRADE, ((EditText)findViewById(R.id.editText3)).getText().toString()); Uri uri = getContentResolver().insert( StudentsProvider.CONTENT_URI, values); Toast.makeText(getBaseContext(), uri.toString(), Toast.LENGTH_LONG).show(); } public void onClickRetrieveStudents(View view) { // Retrieve student records String URL = "content://com.example.provider.College/students"; Uri students = Uri.parse(URL); Cursor c = managedQuery(students, null, null, null, "name"); if (c.moveToFirst()) { do{ Toast.makeText(this, c.getString(c.getColumnIndex(StudentsProvider._ID)) + ", " + c.getString(c.getColumnIndex( StudentsProvider.NAME)) + ", " + c.getString(c.getColumnIndex( StudentsProvider.GRADE)), Toast.LENGTH_SHORT).show(); } while (c.moveToNext()); } }
在包cn.uprogrammer.contentprovider下建立新的文件StudentsProvider.java。如下是src/cn.uprogrammer.contentprovider/StudentsProvider.java的内容。
package cn.uprogrammer.contentprovider; import java.util.HashMap; import android.content.ContentProvider; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.content.UriMatcher; import android.database.Cursor; import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; import android.text.TextUtils; public class StudentsProvider extends ContentProvider { static final String PROVIDER_NAME = "com.example.provider.College"; static final String URL = "content://" + PROVIDER_NAME + "/students"; static final Uri CONTENT_URI = Uri.parse(URL); static final String _ID = "_id"; static final String NAME = "name"; static final String GRADE = "grade"; private static HashMap<String, String> STUDENTS_PROJECTION_MAP; static final int STUDENTS = 1; static final int STUDENT_ID = 2; static final UriMatcher uriMatcher; static{ uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); uriMatcher.addURI(PROVIDER_NAME, "students", STUDENTS); uriMatcher.addURI(PROVIDER_NAME, "students/#", STUDENT_ID); } /** * 数据库特定常量声明 */ private SQLiteDatabase db; static final String DATABASE_NAME = "College"; static final String STUDENTS_TABLE_NAME = "students"; static final int DATABASE_VERSION = 1; static final String CREATE_DB_TABLE = " CREATE TABLE " + STUDENTS_TABLE_NAME + " (_id INTEGER PRIMARY KEY AUTOINCREMENT, " + " name TEXT NOT NULL, " + " grade TEXT NOT NULL);"; /** * 建立和管理提供者内部数据源的帮助类. */ private static class DatabaseHelper extends SQLiteOpenHelper { DatabaseHelper(Context context){ super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE_DB_TABLE); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("DROP TABLE IF EXISTS " + STUDENTS_TABLE_NAME); onCreate(db); } } @Override public boolean onCreate() { Context context = getContext(); DatabaseHelper dbHelper = new DatabaseHelper(context); /** * 若是不存在,则建立一个可写的数据库。 */ db = dbHelper.getWritableDatabase(); return (db == null)? false:true; } @Override public Uri insert(Uri uri, ContentValues values) { /** * 添加新学生记录 */ long rowID = db.insert( STUDENTS_TABLE_NAME, "", values); /** * 若是记录添加成功 */ if (rowID > 0) { Uri _uri = ContentUris.withAppendedId(CONTENT_URI, rowID); getContext().getContentResolver().notifyChange(_uri, null); return _uri; } throw new SQLException("Failed to add a record into " + uri); } @Override public Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) { SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); qb.setTables(STUDENTS_TABLE_NAME); switch (uriMatcher.match(uri)) { case STUDENTS: qb.setProjectionMap(STUDENTS_PROJECTION_MAP); break; case STUDENT_ID: qb.appendWhere( _ID + "=" + uri.getPathSegments().get(1)); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } if (sortOrder == null || sortOrder == ""){ /** * 默认按照学生姓名排序 */ sortOrder = NAME; } Cursor c = qb.query(db, projection, selection, selectionArgs,null, null, sortOrder); /** * 注册内容URI变化的监听器 */ c.setNotificationUri(getContext().getContentResolver(), uri); return c; } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { int count = 0; switch (uriMatcher.match(uri)){ case STUDENTS: count = db.delete(STUDENTS_TABLE_NAME, selection, selectionArgs); break; case STUDENT_ID: String id = uri.getPathSegments().get(1); count = db.delete( STUDENTS_TABLE_NAME, _ID + " = " + id + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""), selectionArgs); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } getContext().getContentResolver().notifyChange(uri, null); return count; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { int count = 0; switch (uriMatcher.match(uri)){ case STUDENTS: count = db.update(STUDENTS_TABLE_NAME, values, selection, selectionArgs); break; case STUDENT_ID: count = db.update(STUDENTS_TABLE_NAME, values, _ID + " = " + uri.getPathSegments().get(1) + (!TextUtils.isEmpty(selection) ? " AND (" +selection + ')' : ""), selectionArgs); break; default: throw new IllegalArgumentException("Unknown URI " + uri ); } getContext().getContentResolver().notifyChange(uri, null); return count; } @Override public String getType(Uri uri) { switch (uriMatcher.match(uri)){ /** * 获取全部学生记录 */ case STUDENTS: return "vnd.android.cursor.dir/vnd.example.students"; /** * 获取一个特定的学生 */ case STUDENT_ID: return "vnd.android.cursor.item/vnd.example.students"; default: throw new IllegalArgumentException("Unsupported URI: " + uri); }
下面是res/layout/activity_main.xml文件的内容:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="内容提供者实例" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" android:textSize="30dp" /> <TextView android:id="@+id/textView2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="www.uprogrammer.cn" android:textColor="#ff87ff09" android:textSize="30dp" android:layout_below="@+id/textView1" android:layout_centerHorizontal="true" /> <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/imageButton" android:src="@drawable/ic_launcher" android:layout_below="@+id/textView2" android:layout_centerHorizontal="true" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/button2" android:text="添加" android:layout_below="@+id/editText3" android:layout_alignRight="@+id/textView2" android:layout_alignEnd="@+id/textView2" android:layout_alignLeft="@+id/textView2" android:layout_alignStart="@+id/textView2" android:onClick="onClickAddName"/> <EditText android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/editText" android:layout_below="@+id/imageButton" android:layout_alignRight="@+id/imageButton" android:layout_alignEnd="@+id/imageButton" /> <EditText android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/editText2" android:layout_alignTop="@+id/editText" android:layout_alignLeft="@+id/textView1" android:layout_alignStart="@+id/textView1" android:layout_alignRight="@+id/textView1" android:layout_alignEnd="@+id/textView1" android:hint="姓名" android:textColorHint="@android:color/holo_blue_light" /> <EditText android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/editText3" android:layout_below="@+id/editText" android:layout_alignLeft="@+id/editText2" android:layout_alignStart="@+id/editText2" android:layout_alignRight="@+id/editText2" android:layout_alignEnd="@+id/editText2" android:hint="年级" android:textColorHint="@android:color/holo_blue_bright" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="查询" android:id="@+id/button" android:layout_below="@+id/button2" android:layout_alignRight="@+id/editText3" android:layout_alignEnd="@+id/editText3" android:layout_alignLeft="@+id/button2" android:layout_alignStart="@+id/button2" android:onClick="onClickRetrieveStudents"/> </RelativeLayout>
输入姓名和年级,并点击"添加"按钮,这将在数据中添加一条学生记录,并在底部删除一条信息。信息内容显示包含添加进数据库的记录数的内容提供者URI。这个操做使用了insert()方法。重复这个过程在咱们的内容提供者的数据库中添加更多的学生。
一旦你完成数据库记录的添加,是时候向内容提供者要求给回这些记录。点击"查询"按钮,这将经过实现的 query() 方法来获取并显示全部的数据记录。
你能够在 MainActivity.java 中提供回调方法,来编写更新和删除的操做,并修改用户界面来添加更新和删除操做。
你能够经过这种方式使用已有的内容提供者,如通信录。你也能够经过这种方式来开发一个优秀的面向数据库的应用,你能够像上面介绍的实例那样来执行素有的数据库操做,如读、写、更新和删除。