当前位置:   article > 正文

【Android】21.0 权限处理(三)——访问其他程序数据:提供数据给别的程序使用...

mydatabasehelper dbhelper
1.0 事实上这章特别难写,知识点比较清楚,比较少。但是代码量特别多,主要还是因为重复性的代码比较多,而且在细节方面,会用了某个方法,不解释又完全不知道这个方法干什么了这么屌。但是要相信自己,只要沉下心去阅读,会很快掌握的。

特别吐槽一句,挺讨厌CSDN的,把界面搞得跟特么广告网站似的。看的不烦吗?

2.0 本篇需要结合我的上一篇的内容:

链接:【Android】20.0 权限处理(二)——访问其他程序数据:读取联系人信息

主要写的是应用程序如何从另外一个应用程序中读取数据。

而这章的内容是相反的:如何给别的应用程序提供权限,以访问自己的数据。

本章内容可参考下面两篇相关文章:
链接:Android:关于ContentProvider的知识都在这里了!
链接:Android跨进程通信:图文详解 Binder机制 原理

3.0 话不多说,知识点放最后,先把项目贴上来。

这里涉及两个应用程序:

  • 一个是之前已经写好的项目,DatabaseTest(所以该项目里面可能有与本篇内容无关的代码),主要作用是可以创建一个SQLite数据库,保存在该软件包名目录文件下。

  • 另一个是新开的项目,ProviderTest,主要是用于操作DatabaseTest应用程序中的数据,可以进行增删查改。
    两个项目的目录如下:


    16102290-cd691748a96fc6f3.png
    2019-02-20_210145.png
    16102290-32fcfdbf129741c4.png
    2019-02-20_210200.png
4.0 先看下DatabaseTest项目运行后的画面:
16102290-8207fa17b19a2506.png
2019-02-20_210309.png
5.0本篇内容只需要这个应用程序在模拟器上装载即可,不要去点击“创建数据库”按钮,测试运行的时候,可以把Android studio中DatabaseTest项目关闭(稍后还需要向DatabaseTest项目中添加代码)。
6.0 ContentProvider,内容提供器,Android 四大组件之一

(Activity、Service、Broadcast Receiver、Content Provider)

关于什么是Android四大组件:
链接:android四大组件(组成)是什么,功能分别是

想要实现跨程序共享数据功能,官方推荐的方式就是这个——使用内容提供器。

一个程序可以通过实现一个ContentProvider的抽象接口将自己的数据完全暴露出去,而且ContentProvider是以类似数据库中表的方式将数据暴露的。那么外界获取其提供的数据,也就应该与从数据库中获取数据的操作基本一样,只不过是采用URL来表示外界需要访问的“数据库”。

ContentProvider类有6个抽象方法,在使用子类继承它的时候,需要将这6个方法全部重写。比如新建一个MyProvider 继承自ContentProvider ,代码示范如下(本代码和上面所述的两个项目无关,但可以便于我们实际理解):

  1. package com.example.contactstest;
  2. import android.content.ContentProvider;
  3. import android.content.ContentValues;
  4. import android.database.Cursor;
  5. import android.net.Uri;
  6. public class MyProvider extends ContentProvider {
  7. @Override
  8. public boolean onCreate() {
  9. // TODO: Implement this to initialize your content provider on startup.
  10. //TODO: 实现此方法以在启动时初始化内容提供程序。
  11. return false;//返回true表示内容提供器创建成功
  12. }
  13. @Override
  14. public String getType(Uri uri) {
  15. // TODO: Implement this to handle requests for the MIME type of the data
  16. // TODO:实现此方法以处理对数据的mime类型的请求
  17. //根据传入的URI返回相应的MIME类型
  18. return null;
  19. }
  20. @Override
  21. public Uri insert(Uri uri, ContentValues values) {
  22. // TODO: Implement this to handle requests to insert a new row.
  23. // TODO: 实现此方法以处理插入新行的请求。
  24. //uri:用于确定更新哪张表
  25. // values:数据都保存在这个参数内
  26. return null;//添加完成后,返回一个用于表示这条新纪录的URI
  27. }
  28. @Override
  29. public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
  30. String sortOrder) {
  31. // TODO: Implement this to handle query requests from clients.
  32. //TODO:实现此方法以处理来自客户端的查询请求。
  33. //uri:用于确定查询哪张表
  34. //projection:用于确定查询哪列
  35. //selection和selectionArgs:用于约束确定查询哪些行
  36. //sortOrder:用于确定采用何种方式进行排序
  37. return null;
  38. }
  39. @Override
  40. public int delete(Uri uri, String selection, String[] selectionArgs) {
  41. //TODO: Implement this to handle requests to delete one or more rows.
  42. //TODO:实现此方法以处理删除一行或多行的请求。
  43. // uri:用于确定删除哪张表中的数据
  44. //selection和selectionArgs:用于约束确定删除哪些行
  45. return 0;//被删除的行数将作为返回值返回
  46. }
  47. @Override
  48. public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
  49. // TODO: Implement this to handle requests to update one or more rows.
  50. // TODO: 实现此方法以处理更新一行或多行的请求。
  51. // uri:用于确定更新哪张表中的数据
  52. // values:数据都保存在这个参数内
  53. // selection和selectionArgs:用于约束确定更新哪些行
  54. return 0;//受影响的行数将作为返回值返回
  55. }
  56. }



这里需要特别说明下的是getType( )方法。

一个标准的内容URI的写法是这样的:
content://com.example.app.provider/table1
知道什么是内容URI的程序员看一眼就知道,我们期望访问的是com.example.app这个应用的table1表中的数据,或者格式如下:
content://com.example.app.provider/table1/1 表示我们期望访问的是com.example.app这个应用的table1表中id为1的数据

划线的重点来了:
1.内容URI主要有以上两种格式:

  • 以路径结尾,表示期望访问该表中所有数据。
  • 以id结尾,表示期望访问该表中拥有相应id的数据。

2.可以转成使用通配符来表示,通配符规则如下:
* :表示匹配任意长度的任意字符。
# :表示匹配任意长度的数字。

因此,内容URI分别可以改写成:
content://com.example.app.provider/*
content://com.example.app.provider/table1/#

3.一个内容URI所对应的MIME字符串主要由3部分组成:

  • 必须以vnd开头
  • 如果内容URI以路径结尾,则后接android.cursor.dir/,如果内容URI以id结尾,则后接Android.cursor.item/
  • 最后接上vnd.<authority>.<path>

4.所以对于内容URIcontent://com.example.app.provider/table1所对应的MIME类型格式为:
vnd.android.cursor.dir/vnd.com.example.app.provider/table1
对于内容URIcontent://com.example.app.provider/table/1所对应的MIME类型格式为:
vnd.android.cursor.item/vnd.com.example.app.provider/table1/1

5.MIME数据类型
作用:指定某个扩展名的文件用某种应用程序来打开
如指定.html文件采用text应用程序打开、指定.pdf文件采用flash应用程序打开
(当然,这里其实用不到这个知识点,因为只有上面所述的两种写法)

OK,理论到位,接着往下走。

7.0 这时候我们可以摆弄DatabaseTest项目中的代码了。它需要做到公开自己的数据。

首先,activity_main.xml这个布局文件,不需要起作用(虽然里面有一个Button控件,所以这里就不贴代码了)
然后,MainActivity.java也不需要增加什么代码(虽然里面有个数据库创建的程序响应):

  1. package com.example.databasetest;
  2. import android.support.v7.app.AppCompatActivity;
  3. import android.os.Bundle;
  4. import android.view.View;
  5. import android.widget.Button;
  6. public class MainActivity extends AppCompatActivity {
  7. private MyDatabaseHelper dbHelper;
  8. @Override
  9. protected void onCreate(Bundle savedInstanceState) {
  10. super.onCreate(savedInstanceState);
  11. setContentView(R.layout.activity_main);
  12. dbHelper = new MyDatabaseHelper(this,"BookStore.db",null,1);
  13. Button createDatabase = (Button) findViewById(R.id.create_database);
  14. createDatabase.setOnClickListener(new View.OnClickListener() {
  15. @Override
  16. public void onClick(View v) {
  17. dbHelper.getWritableDatabase();
  18. }
  19. });
  20. }
  21. }
8.0 MyDatabaseHelper类主要是数据库的创建:
  1. package com.example.databasetest;
  2. import android.content.Context;
  3. import android.database.sqlite.SQLiteDatabase;
  4. import android.database.sqlite.SQLiteOpenHelper;
  5. import android.widget.Toast;
  6. public class MyDatabaseHelper extends SQLiteOpenHelper {
  7. public static final String CREATE_BOOK = "create table Book ("
  8. + "id integer primary key autoincrement,"
  9. + "author text,"
  10. + "price real,"
  11. + "pages integer,"
  12. + "name text)";
  13. private Context mContext;
  14. @Override
  15. public void onCreate(SQLiteDatabase db) {
  16. db.execSQL(CREATE_BOOK);
  17. }
  18. @Override
  19. public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  20. }
  21. public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory,
  22. int version) {
  23. super(context, name, factory, version);
  24. mContext=context;
  25. }
  26. }

MyDatabaseHelper类继承了SQLiteOpenHelper类,这是一个抽象类,这个抽象类需要重写onCreate()onUpgrade()方法,在这两个方法里面,我们可以实现创建、升级SQLite数据库。

9.0 在目录com.example.databasetest包名上右击,New→Other→Content Provider:
16102290-bfe2af2a79481806.png
2019-02-20_184028.png

会弹出如下窗口:

16102290-be235afee276c967.png
2019-02-20_184157.png

我们将内容提供器命名为: DatabaseProvider,authority指定为 com.example.databasetest.provider, Exported属性表示是否允许外部程序访问我们的内容提供器, Enable表示是否启用这个内容提供器,都勾上,点击finish创建:

接着修改DatabaseProvider中的代码:

  1. package com.example.databasetest;
  2. import android.content.ContentProvider;
  3. import android.content.ContentValues;
  4. import android.content.UriMatcher;
  5. import android.database.Cursor;
  6. import android.database.sqlite.SQLiteDatabase;
  7. import android.net.Uri;
  8. public class DatabaseProvider extends ContentProvider {
  9. public static final int BOOk_DIR = 0;
  10. public static final int BOOK_ITEM = 1;
  11. public static final int CATEGORY_DIR = 2;
  12. public static final int CATEGORY_ITEM = 3;
  13. public static final String AUTHORITY = "com.example.databasetest.provider";
  14. public static UriMatcher uriMatcher;
  15. private MyDatabaseHelper dbHelper;
  16. static {
  17. //uriMatcher中有个addURI()方法,这个方法接收三个参数
  18. // 分别把authority、path和一个自定义代码传进去。
  19. //这样可以调用uriMatcher的match()方法时可以将一个Uri对象传入
  20. //match()方法的返回值是某个能匹配这个Uri对象所对应的自定义代码
  21. //利用这个代码,我们就可以知道访问的是哪张表中的数据
  22. uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
  23. //BOOk_DIR:表示访问book表中的所有数据
  24. uriMatcher.addURI(AUTHORITY, "book", BOOk_DIR);
  25. //BOOK_ITEM:表示访问book表中的单条数据
  26. uriMatcher.addURI(AUTHORITY, "book/#", BOOK_ITEM);
  27. //CATEGORY_DIR:表示访问category表中的所有数据
  28. uriMatcher.addURI(AUTHORITY, "category", CATEGORY_DIR);
  29. //CATEGORY_ITEM:表示访问category表中的单条数据
  30. uriMatcher.addURI(AUTHORITY, "category/#", CATEGORY_ITEM);
  31. }
  32. public DatabaseProvider() {
  33. }
  34. @Override
  35. public int delete(Uri uri, String selection, String[] selectionArgs) {
  36. //TODO: Implement this to handle requests to delete one or more rows.
  37. //TODO:实现此方法以处理删除一行或多行的请求。
  38. SQLiteDatabase db = dbHelper.getReadableDatabase();
  39. int deletedRows = 0;
  40. switch (uriMatcher.match(uri)) {
  41. case BOOk_DIR:
  42. deletedRows = db.delete("Book", selection, selectionArgs);
  43. break;
  44. case BOOK_ITEM:
  45. String bookId = uri.getPathSegments().get(1);
  46. deletedRows = db.delete("Book", "id = ?", new String[]{bookId});
  47. break;
  48. case CATEGORY_DIR:
  49. deletedRows = db.delete("Category", selection, selectionArgs);
  50. break;
  51. case CATEGORY_ITEM:
  52. String categoryId = uri.getPathSegments().get(1);
  53. deletedRows = db.delete("Category", "id = ?", new String[]{categoryId});
  54. break;
  55. default:
  56. break;
  57. }
  58. return deletedRows;
  59. }
  60. @Override
  61. public String getType(Uri uri) {
  62. // TODO: Implement this to handle requests for the MIME type of the data
  63. // TODO:实现此方法以处理对数据的mime类型的请求
  64. // at the given URI.
  65. switch (uriMatcher.match(uri)) {
  66. //这里可以看到对内容URI转为MIME类型的实际处理
  67. case BOOk_DIR:
  68. return "vnd.android.cursor.dir/vnd.com.example.databasetest.provider.book";
  69. case BOOK_ITEM:
  70. return "vnd.android.cursor.item/vnd.com.example.databasetest.provider.book";
  71. case CATEGORY_DIR:
  72. return "vnd.android.cursor.dir/vnd.com.example.databasetest.provider.category";
  73. case CATEGORY_ITEM:
  74. return "vnd.android.cursor.item/vnd.com.example.databasetest.provider.category";
  75. }
  76. return null;
  77. }
  78. @Override
  79. public Uri insert(Uri uri, ContentValues values) {
  80. // TODO: Implement this to handle requests to insert a new row.
  81. // TODO: 实现此方法以处理插入新行的请求。
  82. SQLiteDatabase db = dbHelper.getReadableDatabase();
  83. Uri uriReturn = null;
  84. switch (uriMatcher.match(uri)) {
  85. case BOOk_DIR:
  86. case BOOK_ITEM:
  87. long newBookId = db.insert("Book", null, values);
  88. uriReturn = Uri.parse("content://" + AUTHORITY + "/book/" + newBookId);
  89. break;
  90. case CATEGORY_DIR:
  91. case CATEGORY_ITEM:
  92. long newCategoryId = db.insert("Category", null, values);
  93. uriReturn = Uri.parse("content://" + AUTHORITY + "/category/" + newCategoryId);
  94. break;
  95. default:
  96. break;
  97. }
  98. //因为insert()方法需要返回一个Uri对象,所以还需要把内容URI转成Uri对象
  99. return uriReturn;
  100. }
  101. @Override
  102. public boolean onCreate() {
  103. // TODO: Implement this to initialize your content provider on startup.
  104. //TODO: 实现此方法以在启动时初始化内容提供程序。
  105. dbHelper = new MyDatabaseHelper(getContext(), "BookStore.db", null, 2);
  106. return true;
  107. }
  108. @Override
  109. public Cursor query(Uri uri, String[] projection, String selection,
  110. String[] selectionArgs, String sortOrder) {
  111. // TODO: Implement this to handle query requests from clients.
  112. //TODO:实现此方法以处理来自客户端的查询请求。
  113. SQLiteDatabase db = dbHelper.getReadableDatabase();
  114. Cursor cursor = null;
  115. switch (uriMatcher.match(uri)) {
  116. case BOOk_DIR:
  117. cursor = db.query("Book", projection, selection, selectionArgs, null, null,
  118. sortOrder);
  119. break;
  120. case BOOK_ITEM:
  121. //访问单条数据时用到这个getPathSegments()方法
  122. // 它会将内容URI之后的部分以“/”符号进行分割
  123. // 返回的结果放到一个字符串列表中
  124. //列表的第0个位置放的是路径,第1个位置放的就是id了
  125. String bookId = uri.getPathSegments().get(1);
  126. cursor = db.query("Book", projection, "id = ?", new String[]{bookId}, null, null,
  127. sortOrder);
  128. break;
  129. case CATEGORY_DIR:
  130. cursor = db.query("Category", projection, selection, selectionArgs, null, null,
  131. sortOrder);
  132. break;
  133. case CATEGORY_ITEM:
  134. String categoryId = uri.getPathSegments().get(1);
  135. cursor = db.query("Category", projection, "id = ?", new String[]{categoryId},
  136. null, null, sortOrder);
  137. break;
  138. default:
  139. break;
  140. }
  141. return cursor;
  142. }
  143. @Override
  144. public int update(Uri uri, ContentValues values, String selection,
  145. String[] selectionArgs) {
  146. // TODO: Implement this to handle requests to update one or more rows.
  147. // TODO: 实现此方法以处理更新一行或多行的请求。
  148. SQLiteDatabase db = dbHelper.getReadableDatabase();
  149. int updateRows = 0;
  150. switch (uriMatcher.match(uri)) {
  151. case BOOk_DIR:
  152. updateRows = db.update("Book", values, selection, selectionArgs);
  153. break;
  154. case BOOK_ITEM:
  155. String bookId = uri.getPathSegments().get(1);
  156. updateRows = db.update("Book", values, "id = ?", new String[]{bookId});
  157. break;
  158. case CATEGORY_DIR:
  159. updateRows = db.update("Category", values, selection, selectionArgs);
  160. break;
  161. case CATEGORY_ITEM:
  162. String categoryId = uri.getPathSegments().get(1);
  163. updateRows = db.update("Category", values, "id = ?", new String[]{categoryId});
  164. break;
  165. default:
  166. break;
  167. }
  168. return updateRows;
  169. }
  170. }

代码说明都在代码块中。

10.0 最后,内容提供器一定要在AndroidManifest.xml中注册,但是由于我们是使用Android studio软件的快捷方式创建的,注册这一步已经自动完成了:
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="com.example.databasetest">
  4. <application
  5. android:allowBackup="true"
  6. android:icon="@mipmap/ic_launcher"
  7. android:label="@string/app_name"
  8. android:roundIcon="@mipmap/ic_launcher_round"
  9. android:supportsRtl="true"
  10. android:theme="@style/AppTheme">
  11. <provider
  12. android:name=".DatabaseProvider"
  13. android:authorities="com.example.databasetest.provider"
  14. android:enabled="true"
  15. android:exported="true"></provider>
  16. <activity android:name=".MainActivity">
  17. <intent-filter>
  18. <action android:name="android.intent.action.MAIN" />
  19. <category android:name="android.intent.category.LAUNCHER" />
  20. </intent-filter>
  21. </activity>
  22. </application>
  23. </manifest>

<provider>标签内就是,其中:

  • android:name属性指定了DatabaseProvider的类名
  • android:authorities属性指定了DatabaseProvider的authority
  • android:enabled属性和android:exported属性上面已经说了,根据我们创建时打钩的状态自动生成的,表示DatabaseProvider允许被其他应用程序进行访问。
11.0 DatabaseTest项目代码全部编写完毕。

一开始也说了,本篇内容只需要DatabaseTest这个应用程序在模拟器上装载即可,不要去点击“创建数据库”按钮,运行DatabaseTest项目后,可以把Android studio中DatabaseTest项目关闭了。

12.0 新建项目,ProviderTest。

先看布局文件activity_main.xml

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:app="http://schemas.android.com/apk/res-auto"
  4. xmlns:tools="http://schemas.android.com/tools"
  5. android:layout_width="match_parent"
  6. android:layout_height="match_parent"
  7. tools:context=".MainActivity">
  8. <Button
  9. android:id="@+id/add_data"
  10. android:layout_width="wrap_content"
  11. android:layout_height="wrap_content"
  12. android:text="添加数据到book"
  13. app:layout_constraintBottom_toBottomOf="parent"
  14. app:layout_constraintEnd_toEndOf="parent"
  15. app:layout_constraintHorizontal_bias="0.20"
  16. app:layout_constraintStart_toStartOf="parent"
  17. app:layout_constraintTop_toTopOf="parent"
  18. app:layout_constraintVertical_bias="0.06" />
  19. <Button
  20. android:id="@+id/query_data"
  21. android:layout_width="wrap_content"
  22. android:layout_height="wrap_content"
  23. android:text="从book中查询数据"
  24. app:layout_constraintBottom_toBottomOf="@+id/add_data"
  25. app:layout_constraintEnd_toEndOf="parent"
  26. app:layout_constraintStart_toEndOf="@+id/add_data"
  27. app:layout_constraintTop_toTopOf="@+id/add_data" />
  28. <Button
  29. android:id="@+id/update_data"
  30. android:layout_width="wrap_content"
  31. android:layout_height="wrap_content"
  32. android:layout_marginTop="8dp"
  33. android:layout_marginBottom="8dp"
  34. android:text="更新数据到book"
  35. app:layout_constraintBottom_toBottomOf="parent"
  36. app:layout_constraintEnd_toEndOf="@+id/add_data"
  37. app:layout_constraintStart_toStartOf="@+id/add_data"
  38. app:layout_constraintTop_toBottomOf="@+id/add_data"
  39. app:layout_constraintVertical_bias="0.06" />
  40. <Button
  41. android:id="@+id/delete_data"
  42. android:layout_width="wrap_content"
  43. android:layout_height="wrap_content"
  44. android:text="从book中删除数据"
  45. app:layout_constraintBottom_toBottomOf="@+id/update_data"
  46. app:layout_constraintEnd_toEndOf="@+id/query_data"
  47. app:layout_constraintStart_toStartOf="@+id/query_data"
  48. app:layout_constraintTop_toTopOf="@+id/update_data" />
  49. </android.support.constraint.ConstraintLayout>

效果很简单:


16102290-8746aa24094db270.png
2019-02-20_224956.png
MainActivity.java:
  1. package com.example.providertest;
  2. import android.content.ContentValues;
  3. import android.database.Cursor;
  4. import android.net.Uri;
  5. import android.support.v7.app.AppCompatActivity;
  6. import android.os.Bundle;
  7. import android.view.View;
  8. import android.widget.Button;
  9. import android.widget.Toast;
  10. public class MainActivity extends AppCompatActivity {
  11. private String newId;
  12. @Override
  13. protected void onCreate(Bundle savedInstanceState) {
  14. super.onCreate(savedInstanceState);
  15. setContentView(R.layout.activity_main);
  16. Button addData = (Button) findViewById(R.id.add_data);
  17. addData.setOnClickListener(new View.OnClickListener() {
  18. @Override
  19. public void onClick(View v) {
  20. //添加数据
  21. Uri uri = Uri.parse("content://com.example.databasetest.provider/book");
  22. ContentValues values = new ContentValues();
  23. values.put("name", "王之国度");
  24. values.put("author", "张三");
  25. values.put("pages", 1040);
  26. values.put("price", 45.96);
  27. Uri newUri = getContentResolver().insert(uri, values);
  28. newId = newUri.getPathSegments().get(1);
  29. }
  30. });
  31. Button queryData = (Button) findViewById(R.id.query_data);
  32. queryData.setOnClickListener(new View.OnClickListener() {
  33. @Override
  34. public void onClick(View v) {
  35. Uri uri = Uri.parse("content://com.example.databasetest.provider/book");
  36. Cursor cursor = getContentResolver().query(uri, null, null, null, null);
  37. if (cursor != null) {
  38. while (cursor.moveToNext()) {
  39. String name = cursor.getString(cursor.getColumnIndex("name"));
  40. String author = cursor.getString(cursor.getColumnIndex("author"));
  41. int pages = cursor.getInt(cursor.getColumnIndex("pages"));
  42. double price = cursor.getDouble(cursor.getColumnIndex("price"));
  43. Toast.makeText(MainActivity.this,
  44. "name is" + name + ",\n author is" + author +
  45. ",\n pages is" + pages + ",\n price is" + price,
  46. Toast.LENGTH_SHORT).show();
  47. }
  48. cursor.close();
  49. }
  50. }
  51. });
  52. Button updateData = (Button) findViewById(R.id.update_data);
  53. updateData.setOnClickListener(new View.OnClickListener() {
  54. @Override
  55. public void onClick(View v) {
  56. //更新数据
  57. Uri uri = Uri.parse("content://com.example.databasetest.provider/book/" + newId);
  58. ContentValues values = new ContentValues();
  59. values.put("name","创世之神");
  60. values.put("pages", 8250);
  61. values.put("price", 198.72);
  62. getContentResolver().update(uri, values, null, null);
  63. }
  64. });
  65. Button deleteData = (Button) findViewById(R.id.delete_data);
  66. deleteData.setOnClickListener(new View.OnClickListener() {
  67. @Override
  68. public void onClick(View v) {
  69. Uri uri = Uri.parse("content://com.example.databasetest.provider/book/" + newId);
  70. //删除数据
  71. getContentResolver().delete(uri, null, null);
  72. }
  73. });
  74. }
  75. }
13.0 运行:
16102290-f33655df3ec15cee.png
2019-02-20_203941.png

点击“添加数据到BOOK”按钮


16102290-3bd9efade3da0545.png
2019-02-20_20341.png

将数据库导出,在Navicat Premium中查看:


16102290-a08d2ab3183c836e.png
2019-02-20_203625.png

关于如何导出数据库文件并查看请参考:
链接:【Android】18.0 SQLite数据库——LitePal的使用及SQLite数据库怎么查看

点击“从BOOK中查询数据”按钮:


16102290-fdd646b817387a96.png
2019-02-20_203833.png

点击“更新数据到BOOK”按钮,然后再次导出数据库,查看数据库:


16102290-eeffed7ea8584413.png
2019-02-20_204801.png

点击“从BOOK中删除数据”按钮,再点击“从BOOK中查询数据”按钮,没有Toast提示响应。再次导出数据库,查看数据库:


16102290-56826f851bb28558.png
2019-02-20_203537.png

就这么些意思。

备注:更全面的理解可以参考本篇一开始提供的参考链接,本篇有助于那些需要实现类似功能的朋友,可以直接照猫画虎,先让自己的构思实现起来。

再备注:说实话,自己挺反感写太多理论东西,当它们扎堆的时候,完全不知道怎么用好伐。
把东西放在实际项目中,傻瓜式地先串起来run成功后,写两遍知识点就学得差不多了,再回头看看理论,简直不要太好懂……

再再备注:除了一开始写的两三篇文章版本控制比较乱之外,自己写的代码可以说:

  • 1.都是用的Android Studio3.3版本
  • 2.目标Android版本是7.0.0(API 24),但模拟器跑的Android版本是Android9.0.0(API 28)
  • 3.电脑系统Windows10专业版64bit

再再再备注:真的很讨厌CSDN……

END

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/245908
推荐阅读
相关标签
  

闽ICP备14008679号