20189200餘超 2018-2019-2 移動平臺應用開發實踐第十二週做業

20189200餘超 2018-2019-2 移動平臺應用開發實踐第十二週做業

服務

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 中提供回調方法,來編寫更新和刪除的操做,並修改用戶界面來添加更新和刪除操做。

你能夠經過這種方式使用已有的內容提供者,如通信錄。你也能夠經過這種方式來開發一個優秀的面向數據庫的應用,你能夠像上面介紹的實例那樣來執行素有的數據庫操做,如讀、寫、更新和刪除。

相關文章
相關標籤/搜索