天天看點

Android Api Demos登頂之路(四十七)Loader-->Throttle

這個demo示範了如何利用類加載器對自定義的内容提供者共享的資料進行管理

MainActivity

public class MainActivity extends Activity {
    // 定義主機名,用以拼接Uri,Uri表明了内容提供的位址,外部應用通過Uri通路内容提供者,來實作對資料的增删改查
    private static final String AUTHORITY = "com.fishtosky.loaderthrottle";

    /*
     * 本例中我們将自定義的一個資料庫通過内容提供者共享給其它的外部應用,使外部應用可以對資料庫中的内容進行 增删改查的操作。
     * 定義一個類用于定義關于内容提供者和工作表的一些常量,該類實作了BaseColumns接口,表示将繼承該接口
     * _id和_icount兩列,我們無需再定義就會在表中建立出這兩個列
     */
    public static class MainTable implements BaseColumns {
        // 構造函數私有化,不允許建立此類的執行個體
        private MainTable() {
        };

        // 定義表的名字
        public static final String TABLE_NAME = "main";
        // 定義這張表的uri
        public static final Uri CONTENT_URI = Uri.parse("content://"
                + AUTHORITY + "/main");
        // 定義表中某個條目(某行資料)的Uri的前面公共的部分,使用時我們還需要在後面添加條目的Id
        public static final Uri CONTENT_ID_URI_BASE = Uri.parse("content://"
                + AUTHORITY + "/main");
        // 定義Uri的命名機制,/前面的部分是android系統定義的,不能改變,/後面的部分可以自定義任意字元串
        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/com.fishtosky.throttle";
        // 定義某個條目的Uri的命名機制
        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/com.fishtosky.throttle";
        // 定義預設的排序方式
        public static final String DEFAULT_SORT_ORDER = "data COLLATE LOCALIZED ASC";
        // 定義列的名字,本例中隻有一列
        public static final String COLUM_NAME_DATA = "data";
    }

    /*
     * 定義資料庫的幫助庫,用于建立資料庫和工作表
     */
    public static class DatabaseHelper extends SQLiteOpenHelper {
        // 定義資料庫的名字的版本
        private static final String DATABASE_NAME = "loader_throttle.db";
        private static final int DATABASE_VERSION = ;

        public DatabaseHelper(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }

        @Override
        public void onCreate(SQLiteDatabase db) {
            db.execSQL("CREATE TABLE " + MainTable.TABLE_NAME + " ("
                    + MainTable._ID + " INTEGER PRIMARY KEY,"
                    + MainTable.COLUM_NAME_DATA + " TEXT);");
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            Log.w("TAG", "Upgrading database from version " + oldVersion
                    + " to " + newVersion + ", which will destroy all old data");
            // 版本更新時調用,删除舊表,建立新表
            db.execSQL("DROP TABLE IF EXISTS notes");
            onCreate(db);
        }
    }

    /*
     * 自定義内容提供者,通過它實作把資料庫共享給外部應用
     */
    public static class SimpleProvider extends ContentProvider {
        // 定義一個集合,把從資料庫中選出的列映射到該集合中
        private Map<String, String> mNotesProjectionMap;
        // 定義Uri的比對器,用于解析傳入的Uri
        private UriMatcher mUriMatcher;
        // 定義當Uri比對時的傳回碼
        // 比對整個表時的傳回碼
        private static final int MAIN = ;
        // 比對某一行時的傳回碼
        private static final int MAIN_ID = ;
        private DatabaseHelper mOpenHelper;

        public SimpleProvider() {
            // 為内容提供者注冊Uri
            mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
            mUriMatcher.addURI(AUTHORITY, MainTable.TABLE_NAME, MAIN);
            // #表示通配符
            mUriMatcher.addURI(AUTHORITY, MainTable.TABLE_NAME + "/#", MAIN_ID);

            mNotesProjectionMap = new HashMap<String, String>();
            mNotesProjectionMap.put(MainTable._ID, MainTable._ID);
            mNotesProjectionMap.put(MainTable.COLUM_NAME_DATA,
                    MainTable.COLUM_NAME_DATA);
        }

        @Override
        public boolean onCreate() {
            // 建立資料庫和資料表
            mOpenHelper = new DatabaseHelper(getContext());
            return true;
        }

        /*
         * 用于供外部應用從内容提供者中擷取資料
         */
        @Override
        public Cursor query(Uri uri, String[] projection, String selection,
                String[] selectionArgs, String sortOrder) {
            // 使用Sql查詢語句建構的輔助類
            SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
            qb.setTables(MainTable.TABLE_NAME);
            // 根據比對Uri的傳回碼來判定是查詢整個資料表,還是查詢某條資料
            switch (mUriMatcher.match(uri)) {
            case MAIN:
                // 查詢整個表
                qb.setProjectionMap(mNotesProjectionMap);
                break;
            case MAIN_ID:
                qb.setProjectionMap(mNotesProjectionMap);
                // 追加篩選條件
                qb.appendWhere(MainTable._ID + "=?");
                // 擷取查詢參數即所要查詢條目的Id
                selectionArgs = DatabaseUtils.appendSelectionArgs(
                        selectionArgs,
                        new String[] { uri.getLastPathSegment() });
                break;

            default:
                throw new IllegalArgumentException("UnKnown Uri" + uri);
            }

            // 如果沒定義排序規則,則按照預設的規則進行排序
            if (TextUtils.isEmpty(sortOrder)) {
                sortOrder = MainTable.DEFAULT_SORT_ORDER;
            }

            // 擷取到可讀的資料庫
            SQLiteDatabase db = mOpenHelper.getReadableDatabase();
            Cursor c = qb.query(db, projection, selection, selectionArgs, null,
                    null, sortOrder);
            // 監聽uri的變化
            c.setNotificationUri(getContext().getContentResolver(), uri);
            return c;
        }

        /*
         * 傳回對應Uri MIME類型用以驗證資料的合法性
         */
        @Override
        public String getType(Uri uri) {
            switch (mUriMatcher.match(uri)) {
            case MAIN:
                return MainTable.CONTENT_TYPE;
            case MAIN_ID:
                return MainTable.CONTENT_ITEM_TYPE;
            default:
                throw new IllegalArgumentException("UnKnown Uri" + uri);
            }
        }

        /*
         * 用于外部應用向内容提供者中插入資料
         */
        @Override
        public Uri insert(Uri uri, ContentValues values) {
            // 隻能插入到資料表中
            if (mUriMatcher.match(uri) != MAIN) {
                throw new IllegalArgumentException("UnKnown Uri" + uri);
            }
            if (values != null) {
                SQLiteDatabase db = mOpenHelper.getWritableDatabase();
                long row_Id = db.insert(MainTable.TABLE_NAME, null, values);
                // 如果插入成功,則插入行的Id存在
                if (row_Id > ) {
                    // ContentUris用于操作Uri路徑後面的Id部分
                    Uri noteUri = ContentUris.withAppendedId(
                            MainTable.CONTENT_ID_URI_BASE, row_Id);
                    //必須設定對Uri的監聽,不然loader無法擷取到資料庫的更新,不能實作實時更新
                    getContext().getContentResolver().notifyChange(noteUri, null);
                    // 傳回所插入條目的Uri
                    return noteUri;
                }
            }
            throw new SQLException("Failed to insert row into " + uri);
        }

        /*
         * 用于外部應用删除内容提供者中的資料
         */
        @Override
        public int delete(Uri uri, String where, String[] whereArgs) {
            SQLiteDatabase db = mOpenHelper.getWritableDatabase();
            String findWhere;
            int count = -;

            switch (mUriMatcher.match(uri)) {
            case MAIN:
                count = db.delete(MainTable.TABLE_NAME, where, whereArgs);
                break;
            case MAIN_ID:
                // 組裝查詢條件
                findWhere = DatabaseUtils.concatenateWhere(MainTable._ID + "="
                        + ContentUris.parseId(uri), where);
                count = db.delete(MainTable.TABLE_NAME, findWhere, whereArgs);
                break;

            default:
                throw new IllegalArgumentException("Unknown URI " + uri);
            }

            getContext().getContentResolver().notifyChange(uri, null);
            return count;
        }

        @Override
        public int update(Uri uri, ContentValues values, String where,
                String[] whereArgs) {
            SQLiteDatabase db = mOpenHelper.getWritableDatabase();
            String findWhere;
            int count = -;

            switch (mUriMatcher.match(uri)) {
            case MAIN:
                count = db.update(MainTable.TABLE_NAME, values, where,
                        whereArgs);
                break;
            case MAIN_ID:
                // 組裝查詢條件
                findWhere = DatabaseUtils.concatenateWhere(MainTable._ID + "="
                        + ContentUris.parseId(uri), where);
                count = db.update(MainTable.TABLE_NAME, values, findWhere,
                        whereArgs);
                break;

            default:
                throw new IllegalArgumentException("Unknown URI " + uri);
            }

            getContext().getContentResolver().notifyChange(uri, null);
            return count;
        }

    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        FragmentManager fm = getFragmentManager();
        if (fm.findFragmentById(android.R.id.content) == null) {
            LoaderThrottleFragment frg = new LoaderThrottleFragment();
            fm.beginTransaction().add(android.R.id.content, frg).commit();
        }
    }

    public static class LoaderThrottleFragment extends ListFragment {
        private static final int POPULATE_ID = Menu.FIRST;
        private static final int CLEAR_ID = Menu.FIRST + ;
        private SimpleCursorAdapter mAdapter;
        private AsyncTask<Void, Void, Void> mPopulatingTask;
        final String[] projection = new String[] { MainTable._ID,
                MainTable.COLUM_NAME_DATA };

        private LoaderCallbacks<Cursor> myLoader = new LoaderCallbacks<Cursor>() {

            @Override
            public Loader<Cursor> onCreateLoader(int id, Bundle args) {
                CursorLoader c = new CursorLoader(getActivity(),
                        MainTable.CONTENT_URI, projection, null,
                        null, null);
                System.out.println("資料變化了嗎?");
                //最多每2秒更新一次
                c.setUpdateThrottle();
                return c;
            }

            @Override
            public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
                System.out.println("資料準備完畢了嗎?"+data.getCount());
                mAdapter.swapCursor(data);
                if(isResumed()){
                    setListShown(true);
                }else{
                    setListShownNoAnimation(true);
                }
            }

            @Override
            public void onLoaderReset(Loader<Cursor> loader) {
                mAdapter.swapCursor(null);
            }
        };

        @Override
        public void onActivityCreated(Bundle savedInstanceState) {
            super.onActivityCreated(savedInstanceState);
            setEmptyText("No data.  Select 'Populate' to fill with data from Z to A at a rate of 4 per second.");
            setHasOptionsMenu(true);
            mAdapter = new SimpleCursorAdapter(getActivity(),
                    android.R.layout.simple_list_item_1, null,
                    new String[] { MainTable.COLUM_NAME_DATA },
                    new int[] { android.R.id.text1 }, );
            setListAdapter(mAdapter);
            setListShown(false);

            getLoaderManager().initLoader(, null, myLoader);
        }

        @Override
        public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
            super.onCreateOptionsMenu(menu, inflater);
            menu.add(Menu.NONE, POPULATE_ID, , "populate").setShowAsAction(
                    MenuItem.SHOW_AS_ACTION_IF_ROOM);
            menu.add(Menu.NONE, CLEAR_ID, , "clear").setShowAsAction(
                    MenuItem.SHOW_AS_ACTION_IF_ROOM);
        }

        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            switch (item.getItemId()) {
            case POPULATE_ID:
                addToDatabase();
                //testadd();
                return true;
            case CLEAR_ID:
                deleteData();
                return true;
            }
            return super.onOptionsItemSelected(item);
        }

        /*private void testadd() {
            ContentValues values = new ContentValues();
            values.put(MainTable.COLUM_NAME_DATA, "I am test string");
            Uri rowUri=getActivity().getContentResolver().insert(MainTable.CONTENT_URI, values);
            if(rowUri!=null){
                getLoaderManager().restartLoader(111, null, myLoader);
            }
            System.out.println("rowUri:"+rowUri.toString());
        }*/

        private void deleteData() {
            if (mPopulatingTask != null) {
                mPopulatingTask.cancel(false);
                mPopulatingTask = null;
            }
            AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
                @Override
                protected Void doInBackground(Void... params) {
                    getActivity().getContentResolver().delete(MainTable.CONTENT_URI, null, null);
                    return null;
                }
            };
            task.execute((Void[]) null);
        }

        private void addToDatabase() {
            // 如果異步任務不為空,則取消任務
            if (mPopulatingTask != null) {
                mPopulatingTask.cancel(false);
                mPopulatingTask = null;
            }
            // 開啟線程向資料庫中添加内容
            mPopulatingTask = new AsyncTask<Void, Void, Void>() {
                @Override
                protected Void doInBackground(Void... params) {
                    // 向資料庫中添加内容
                    for (char c = 'z'; c >= 'a'; c--) {
                        if (isCancelled()) {
                            break;
                        }
                        StringBuilder sb = new StringBuilder("Data ");
                        sb.append(c);
                        ContentValues values = new ContentValues();
                        values.put(MainTable.COLUM_NAME_DATA, sb.toString());
                        Uri rowUri=getActivity().getContentResolver().insert(MainTable.CONTENT_URI, values);
                        //System.out.println("rowUri:"+rowUri.toString());
                        try {
                            Thread.sleep();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    return null;
                }

            };
            // 使用系統預設的線程池來管理線程
            //mPopulatingTask.execute((Void[]) null);
            mPopulatingTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
                    (Void[]) null);
            //getLoaderManager().restartLoader(111, null, myLoader);
        }

        @Override
        public void onListItemClick(ListView l, View v, int position, long id) {
            super.onListItemClick(l, v, position, id);
            Toast.makeText(getActivity(), "Item click:" + id, ).show();
        }
    }
}
           

在配置檔案中注冊内容提供者

<provider 
            android:name="com.fishtosky.loaderthrottle.MainActivity$SimpleProvider"
            android:authorities="com.fishtosky.loaderthrottle">
        </provider>
           

繼續閱讀