zoukankan      html  css  js  c++  java
  • 【Android

    1、Content Provider简介

      Content Provider是Android中提供的一种专门用于不同应用之间进行数据共享的方式,从这一点来看,它天生就适合IPC(Inter-Process Communication,进程间通信)。Content Provider的底层实现是Binder,但它比AIDL要简单很多,因此系统已经给我们做了封装。

      Android系统为我们预置了许多Content Provider,比如通讯录信息、日程表信息等,要想访问这些应用的信息,只需要调用ContentResolver的insert、update、delete、query四个方法即可。但是,我们往往不止需要这些应用的数据,因此,在很多情况下,我们需要创建自定义的Content Provider。

      Content Provider主要是以表格的形式来组织数据的,并且可以包含多张表。除了表格的形式,Content Provider还支持文件格式,比如图片、视频等。

    2、Content Provider实例

      在这个例子中,我们要在客户端操作服务端的SQLite数据库,包括查询、删除、添加和修改的操作。

    2.1、服务端

      在服务端中,我们先创建一个SQLite数据库,名字叫做server.db,数据库中有一张数据表,名字叫做book表,book表中有两个字段:bookId表示书籍的编号,bookName表示书籍的名称。

      我们创建一个MyDBOpenHelper类继承自SQLiteOpenHelper,来初始化数据库中的信息,代码如下:

    package my.itgungnir.server;
    
    import android.content.Context;
    import android.database.sqlite.SQLiteDatabase;
    import android.database.sqlite.SQLiteOpenHelper;
    
    /**
     * 操作数据库的SQLiteOpenHelper工具类
     * Created by ITGungnir on 2017/4/6.
     */
    public class MyDBOpenHelper extends SQLiteOpenHelper {
        private static final String DB_NAME = "server.db";
        public static final String TABLE_NAME = "book";
    
        private static final int DB_VERSION = 1;
    
        public MyDBOpenHelper(Context context) {
            super(context, DB_NAME, null, DB_VERSION);
        }
    
        @Override
        public void onCreate(SQLiteDatabase db) {
            db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + "(bookId INTEGER PRIMARY KEY, bookName TEXT);");
        }
    
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        }
    }

      有了数据库之后,我们就可以创建一个Content Provider,用来提供操作数据库的接口。

      我们创建一个Content Provider类BookProvider,继承自ContentProvider类,并实现onCreate()、insert()、update()、delete()、query()和getType()这六个方法。其中,onCreate()方法是ContentProvider创建的时候调用的方法,可以在这个方法中做一些初始化的操作;getType()方法用来返回一个 Uri 请求所对应的MIME类型(媒体类型,比如图片、视频等),如果不关注这个选项,可以直接返回null或“*/*”;剩下的四个方法用于CRUD操作,即实现对数据表的增删改查功能。注意:这六个方法均运行在ContentProvider的进程中,除了onCreate()方法运行在主线程之外,其他五个方法都运行在Binder线程池中。

      下面是我们创建的BookProvider类中的代码:

    package my.itgungnir.server;
    
    import android.content.ContentProvider;
    import android.content.ContentValues;
    import android.content.Context;
    import android.content.UriMatcher;
    import android.database.Cursor;
    import android.database.sqlite.SQLiteDatabase;
    import android.net.Uri;
    import android.support.annotation.NonNull;
    import android.support.annotation.Nullable;
    
    /**
     * 服务端的ContentProvider
     * Created by ITGungnir on 2017/4/6.
     */
    public class BookProvider extends ContentProvider {
        private static final String AUTHORITY = "my.itgungnir.server.book_provider";
        private static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/book");
        private static final int URI_CODE = 0x001;
        private static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
    
        static {
            URI_MATCHER.addURI(AUTHORITY, "book", URI_CODE);
        }
    
        private Context context;
        private SQLiteDatabase database;
    
        private String getTableName(Uri uri) {
            if (URI_MATCHER.match(uri) == URI_CODE) {
                return MyDBOpenHelper.TABLE_NAME;
            }
            return null;
        }
    
        private void initData() {
            database = new MyDBOpenHelper(context).getWritableDatabase();
            database.execSQL("DELETE FROM " + MyDBOpenHelper.TABLE_NAME + ";");
            database.execSQL("INSERT INTO " + MyDBOpenHelper.TABLE_NAME + " VALUES(1, 'Android');");
            database.execSQL("INSERT INTO " + MyDBOpenHelper.TABLE_NAME + " VALUES(2, 'iOS');");
            database.execSQL("INSERT INTO " + MyDBOpenHelper.TABLE_NAME + " VALUES(3, 'HTML5');");
        }
    
        @Override
        public boolean onCreate() {
            context = getContext();
            initData();
            return true;
        }
    
        @Nullable
        @Override
        public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
            String tableName = getTableName(uri);
            if (tableName == null) {
                throw new IllegalArgumentException("Unsupported URI: " + uri);
            }
            return database.query(tableName, projection, selection, selectionArgs, null, null, sortOrder, null);
        }
    
        @Nullable
        @Override
        public String getType(@NonNull Uri uri) {
            String tableName = getTableName(uri);
            if (tableName == null) {
                throw new IllegalArgumentException("Unsupported URI: " + uri);
            }
            return null;
        }
    
        @Nullable
        @Override
        public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
            String tableName = getTableName(uri);
            if (tableName == null) {
                throw new IllegalArgumentException("Unsupported URI: " + uri);
            }
            database.insert(tableName, null, values);
            context.getContentResolver().notifyChange(uri, null);
            return uri;
        }
    
        @Override
        public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
            String tableName = getTableName(uri);
            if (tableName == null) {
                throw new IllegalArgumentException("Unsupported URI: " + uri);
            }
            int count = database.delete(tableName, selection, selectionArgs);
            if (count > 0) {
                context.getContentResolver().notifyChange(uri, null);
            }
            return count;
        }
    
        @Override
        public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
            String tableName = getTableName(uri);
            if (tableName == null) {
                throw new IllegalArgumentException("Unsupported URI: " + uri);
            }
            int count = database.update(tableName, values, selection, selectionArgs);
            if (count > 0) {
                context.getContentResolver().notifyChange(uri, null);
            }
            return count;
        }
    }

      从上面的代码中可以看到,Content Provider是通过Uri来区分外界想要访问的数据集合的。为了知道外界要访问的是哪张数据表,我们需要为每张表都定义单独的Uri和Uri Code,并将Uri和Uri Code相关联,这一步可以通过UriMatcher的AddUri()方法来实现。这样一来,我们就可以根据外界请求的Uri来得到Uri Code,进而知道外界想要访问哪张表,从而进行相应的数据操作。

      需要注意的是,query()、update()、insert()和delete()四个方法是存在多线程的并发访问的,因此方法内部最好做好线程同步,但是,本例中由于只有一个SQLiteDatabase,因此可以自动处理线程同步,原因是:SQLiteDatabase内部对数据库的操作时有同步处理的。但是,如果有多个SQLiteDatabase对象来操作数据库,就无法保证线程的同步了。

      由于Content Provider属于Android四大组件,因此,需要在项目的Menifest文件中进行注册。在注册声明Content Provider的时候,需要提供一个name属性和一个authorities属性,name属性是要注册的Content Provider的包路径,而authorities属性是Content Provider的唯一标识。另外,如果服务端和客户端在不同的进程中,我们还需要指定Content Provider的exported属性为true,否则会报错:Permission Denied。下面是我们项目服务端Conttent Provider的注册代码:

    <provider
        android:name=".BookProvider"
        android:authorities="my.itgungnir.server.book_provider"
        android:exported="true">
    </provider>

      以上就是服务端的代码,可以直接运行。

    2.2、客户端

      在客户端,我们提供增、删、改、查四个按钮,点击按钮进行对应的操作。界面如下图所示:

      从图中可以看到,点击“Query”按钮的时候,从服务端远程查询所有的书籍信息,显示到最上面的TextView中;点击“Delete”按钮的时候,从上面的EditText中获取要删除的书籍的ID,然后删除书籍;点击“Insert”按钮的时候,从上面的两个EditText中分别去除要添加的书籍的ID和名称,添加到服务端数据库中;点击“Update”按钮的时候,从上面的两个EditText中取出要更新的书籍的ID和名称,然后进行修改。下面是这个界面的布局代码:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="15.0dip">
    
        <TextView
            android:id="@+id/client_tv_result"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="@android:color/black"
            android:textSize="16.0sp" />
    
        <Button
            android:id="@+id/client_btn_query"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="15.0dip"
            android:onClick="function_query"
            android:text="Query" />
    
        <EditText
            android:id="@+id/client_input_deleteid"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="Book ID" />
    
        <Button
            android:id="@+id/client_btn_delete"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="15.0dip"
            android:onClick="function_delete"
            android:text="Delete" />
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="15.0dip"
            android:orientation="horizontal">
    
            <EditText
                android:id="@+id/client_input_insertid"
                android:layout_width="0.0dip"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:hint="Book ID" />
    
            <EditText
                android:id="@+id/client_input_insertname"
                android:layout_width="0.0dip"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:hint="Book Name" />
        </LinearLayout>
    
        <Button
            android:id="@+id/client_btn_insert"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="15.0dip"
            android:onClick="function_insert"
            android:text="Insert" />
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="15.0dip"
            android:orientation="horizontal">
    
            <EditText
                android:id="@+id/client_input_updateid"
                android:layout_width="0.0dip"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:hint="Book ID" />
    
            <EditText
                android:id="@+id/client_input_updatename"
                android:layout_width="0.0dip"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:hint="Book Name" />
        </LinearLayout>
    
        <Button
            android:id="@+id/client_btn_update"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="15.0dip"
            android:onClick="function_update"
            android:text="Update" />
    
    </LinearLayout>

      在MainActiviy中,获取到界面中的控件,指定好Content Provider的Uri,然后就可以写四个Button的点击事件了。MainActivity中的代码如下:

    package my.itgungnir.client;
    
    import android.content.ContentResolver;
    import android.content.ContentValues;
    import android.database.Cursor;
    import android.net.Uri;
    import android.os.Bundle;
    import android.support.v7.app.AppCompatActivity;
    import android.view.View;
    import android.widget.EditText;
    import android.widget.TextView;
    import android.widget.Toast;
    
    public class MainActivity extends AppCompatActivity {
        private static final String TAG = "Client";
    
        private TextView tv_result;
        private EditText input_deleteId, input_updateId, input_updateName, input_insertId, input_insertName;
    
        private Uri uri;
        private ContentResolver resolver;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            initComponents();
        }
    
        private void initComponents() {
            tv_result = (TextView) findViewById(R.id.client_tv_result);
            input_deleteId = (EditText) findViewById(R.id.client_input_deleteid);
            input_updateId = (EditText) findViewById(R.id.client_input_updateid);
            input_updateName = (EditText) findViewById(R.id.client_input_updatename);
            input_insertId = (EditText) findViewById(R.id.client_input_insertid);
            input_insertName = (EditText) findViewById(R.id.client_input_insertname);
            uri = Uri.parse("content://my.itgungnir.server.book_provider/book");
            resolver = getContentResolver();
        }
    
        public void function_query(View v) {
            String resultStr = "Book List:
    ";
            Cursor cursor = resolver.query(uri, null, null, null, null);
            while (cursor.moveToNext()) {
                Book book = new Book(cursor.getInt(cursor.getColumnIndex("bookId")), cursor.getString(cursor.getColumnIndex("bookName")));
                resultStr += book.toString();
            }
            cursor.close();
            tv_result.setText(resultStr);
        }
    
        public void function_delete(View v) {
            String bookIdStr = input_deleteId.getEditableText().toString().trim();
            if ("".equals(bookIdStr)) {
                toast("Book ID cannot be empty!");
            } else {
                int count = resolver.delete(uri, "bookId=?", new String[]{bookIdStr});
                if (count > 0) {
                    toast("Delete Successfully.");
                    input_deleteId.setText("");
                } else {
                    toast("Delete Failed...");
                }
            }
        }
    
        public void function_insert(View v) {
            String bookId = input_insertId.getEditableText().toString().trim();
            String bookName = input_insertName.getEditableText().toString().trim();
            if ("".equals(bookId) || "".equals(bookName)) {
                toast("Both input cannot be empty!");
            } else {
                ContentValues values = new ContentValues();
                values.put("bookId", Integer.parseInt(bookId));
                values.put("bookName", bookName);
                resolver.insert(uri, values);
                toast("Insert successfully.");
                input_insertId.setText("");
                input_insertName.setText("");
            }
        }
    
        public void function_update(View v) {
            String bookId = input_updateId.getEditableText().toString().trim();
            String bookName = input_updateName.getEditableText().toString().trim();
            if ("".equals(bookId) || "".equals(bookName)) {
                toast("Both input cannot be empty!");
            } else {
                ContentValues values = new ContentValues();
                values.put("bookName", bookName);
                int count = resolver.update(uri, values, "bookId=?", new String[]{bookId});
                if (count > 0) {
                    toast("Update successfully.");
                    input_updateId.setText("");
                    input_updateName.setText("");
                }
            }
        }
    
        private void toast(String text) {
            Toast.makeText(MainActivity.this, text, Toast.LENGTH_SHORT).show();
        }
    }

      以上就是使用Android四大组件中的Content Provider来进行进程间通信的服务端和客户端的代码。

  • 相关阅读:
    P3478 [POI2008]STA-Station
    P2015 二叉苹果树
    P2014 选课 (树型背包模版)
    求树的每个子树的重心
    求树的直径
    Javascript--防抖与节流
    JavaScript中call和apply的区别
    解决谷歌浏览器“此Flash Player与您的地区不相容,请重新安装Flash”问题(最新版)
    matlab实验代码(总)
    表达式树
  • 原文地址:https://www.cnblogs.com/itgungnir/p/6674546.html
Copyright © 2011-2022 走看看