When I first learned the ContentProvider of the system, I didn't encounter any problems, but when I learned the implementation of custom ContentProvider, alas, I encountered several problems and spent a lot of time.
My problems:
1. java.lang.IllegalArgumentException: column '_id' does not exist
Solution: insert a data table named_ The type of id column is self increment, because this column is required when using the Cursor related Adapter
2.Failed to find provider <authority> for user 0 ; expected to find a valid ContentProvider for
Solution: add the following code in the AndroidManifest.xml of the first application
<provider android:authorities="cn.sch.myprovider" android:name=".MyProvide" android:exported="true" android:multiprocess="false" android:grantUriPermissions="true" android:writePermission="cn.sch.myprovider.Write" android:readPermission="cn.sch.myprovider.Read" > </provider>
However, when I use API 30, I still report errors and null pointer exceptions. It hasn't been solved yet. It's really a dish, so in order to make the program run, I choose API 29 to run normally
3. Note: when writing sql statements in the database class, it is most likely to make mistakes. For example, if there are no spaces, it is the wrong sql statement. The English comma is written as a Chinese comma. When adding, deleting and changing the query, the table name and column name should be written correctly
4. The authorities in uri are also very easy to make mistakes. If you make a mistake, you will report that uri cannot find an exception
5. There are many small problems that will not be described one by one. Be careful and serious. I believe you can solve them yourself
In order for you (novices, including myself) to run my program correctly, I post all classes, layout files and configuration information. If you look at the implementation of user-defined ContentProvider written by others, you will find these problems. You dare not say all of them. Most of them are like this. The implementation of user-defined ContentProvider is originally oriented to novices, so you can write all the information to learn, imitate and innovate.
1. The lack of layout files is really outrageous for a white man like me. I don't know which is which
2. Some classes are missing, not as good as database classes.
3. It's really hard for Xiaobai to post only some important code fragments
4. There is a lack of configuration information, which can only be seen. It is difficult to imitate. There are a lot of bugs waiting for you
5. The complete source code should be VIP or charged
I won't explain the detailed code in the project. Most of them have explanations. If you really don't understand it, you can baidu by yourself. It must be clearer, more detailed and easier to understand than what I said here. My article mainly allows you to implement a custom ContentProvider and run
Let's see my effect first
20211021
First take a look at my project structure, and you will know which class and layout files to write
The module gamedemo is the first application
The MainActivity code is as follows
import android.app.Activity; import android.content.ContentResolver; import android.content.ContentValues; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.net.Uri; import android.os.Bundle; import android.text.TextUtils; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; public class MainActivity extends Activity implements View.OnClickListener { private static final String TAG = "lzs"; private DbHelp dbHelp; private SQLiteDatabase db; private TextView userTv; private Button btn_query; private Button btn_insert; private Button btn_delete; private Button btn_update; private int m=2; private int n=3; private EditText xuhao; private EditText xingming; private EditText zhiye; private int id; private String name; private String job; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); this.dbHelp = new DbHelp(this); //Create database this.db = this.dbHelp.getWritableDatabase(); init(); //Initialize control } private void init() { btn_query = findViewById(R.id.btn_query); btn_insert = findViewById(R.id.btn_insert); btn_delete = findViewById(R.id.btn_delete); btn_update = findViewById(R.id.btn_update); xuhao = findViewById(R.id.xuhao); xingming = findViewById(R.id.xingming); zhiye = findViewById(R.id.zhiye); btn_query.setOnClickListener(this); btn_insert.setOnClickListener(this); btn_delete.setOnClickListener(this); btn_update.setOnClickListener(this); } @Override protected void onStart() { super.onStart(); } @Override protected void onDestroy() { super.onDestroy(); db.close(); dbHelp.close(); } @Override public void onClick(View view) { switch (view.getId()){ case R.id.btn_query: btn_query(); break; case R.id.btn_insert: btn_insert(); break; case R.id.btn_delete: btn_delete(); break; case R.id.btn_update: btn_update(); break; } } //Query function private void btn_query(){ Cursor user = this.db.query("user", null, null, null, null, null, null); this.userTv = findViewById(R.id.id_user_db); //Clear the text to prevent repeated query and multiple display if(!TextUtils.isEmpty(this.userTv.getText())) { this.userTv.setText(""); } while (user.moveToNext()){ String id = user.getString(0); String name = user.getString(1); String jobs = user.getString(2); this.userTv.append("Serial number:"+id+" full name:"+name+" work:"+jobs+"\n"); } } //Insert function (no SQL statement, Android method) private void btn_insert(){ if(!TextUtils.isEmpty(xuhao.getText())&&!TextUtils.isEmpty(xingming.getText())&&!TextUtils.isEmpty(zhiye.getText())){ id = Integer.parseInt(xuhao.getText().toString()); name = xingming.getText().toString(); job = zhiye.getText().toString(); Cursor result=db.rawQuery("select * from user where _id = "+id,null); if(!result.isAfterLast()){ Toast.makeText(MainActivity.this,"Failed to insert. The modified record already exists or the sequence number is duplicate!",Toast.LENGTH_SHORT).show(); }else { Uri uri_user = Uri.parse("content://cn.sch.myprovider/user"); // Insert data into user table ContentValues values3 = new ContentValues(); values3.put("_id", id); values3.put("name", name); values3.put("jobs", job); // Get ContentResolver ContentResolver resolver3 = getContentResolver(); // Insert data into ContentProvider according to URI through ContentResolver resolver3.insert(uri_user, values3); Toast.makeText(MainActivity.this,"Insert successful",Toast.LENGTH_SHORT).show(); } }else { Toast.makeText(MainActivity.this,"Please fill in completely and cannot be blank!",Toast.LENGTH_SHORT).show(); } } //Delete function (use SQL statement to delete, you can also delete by Android). Because the serial number is unique, you can delete it directly according to the serial number provided private void btn_delete(){ if(!TextUtils.isEmpty(xuhao.getText())){ id = Integer.parseInt(xuhao.getText().toString()); Cursor result=db.rawQuery("select _id from user where _id="+id,null); if(!result.isAfterLast()){ db.execSQL("delete from user where _id="+id); Toast.makeText(MainActivity.this,"Delete succeeded",Toast.LENGTH_SHORT).show(); }else { Toast.makeText(MainActivity.this,"Deletion failed, there is no record!",Toast.LENGTH_SHORT).show(); } }else { Toast.makeText(MainActivity.this,"Please fill in completely and cannot be blank!",Toast.LENGTH_SHORT).show(); } } //Update function (update with SQL statement, you can also update with Android). Because the serial number is unique, it can be changed directly according to the entered serial number private void btn_update(){ if(!TextUtils.isEmpty(xuhao.getText())&&!TextUtils.isEmpty(xingming.getText())&&!TextUtils.isEmpty(zhiye.getText())){ id = Integer.parseInt(xuhao.getText().toString()); name = xingming.getText().toString(); job = zhiye.getText().toString(); Cursor result=db.rawQuery("select * from user where _id="+id,null); if(!result.isAfterLast()){ db.execSQL("update user set _id="+id +" where _id="+id); db.execSQL("update user set name='"+name +"' where _id="+id);//Note that if set='name 'is not clear, you can look at the database knowledge again db.execSQL("update user set jobs='"+job +"' where _id="+id); Toast.makeText(MainActivity.this,"Update succeeded",Toast.LENGTH_SHORT).show(); }else { Toast.makeText(MainActivity.this,"Failed to update. The modified record does not exist!",Toast.LENGTH_SHORT).show(); } }else { Toast.makeText(MainActivity.this,"Please fill in completely and cannot be blank!",Toast.LENGTH_SHORT).show(); } } }
Layout file for MainActivity_ The main.xml code is as follows
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:gravity="center"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center" android:orientation="vertical"> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="50dp" android:text="First application" android:textSize="30dp"/> <TextView android:id="@+id/id_user_db" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="250dp" android:text="" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="2" android:gravity="center" android:orientation="vertical"> <EditText android:id="@+id/xuhao" android:layout_width="wrap_content" android:layout_height="wrap_content" android:ems="10" android:hint="Please enter serial number" android:inputType="number" /> <EditText android:id="@+id/xingming" android:layout_width="wrap_content" android:layout_height="wrap_content" android:ems="10" android:hint="Please enter your name" android:inputType="textPersonName" /> <EditText android:id="@+id/zhiye" android:layout_width="wrap_content" android:layout_height="wrap_content" android:ems="10" android:hint="Please enter occupation" android:inputType="textPersonName" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="2" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center" android:orientation="horizontal"> <Button android:id="@+id/btn_query" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:text="Query data" /> <Button android:id="@+id/btn_insert" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:layout_marginTop="20dp" android:text="insert data" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center_horizontal" android:orientation="horizontal"> <Button android:id="@+id/btn_delete" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Delete data" /> <Button android:id="@+id/btn_update" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:text="Update data" /> </LinearLayout> </LinearLayout> </LinearLayout>
The MyProvide code is as follows
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; public class MyProvide extends ContentProvider { private Context mContext; private DbHelp dbHelp = null; private SQLiteDatabase db = null; public static final String AUTHORITY = "cn.sch.myprovider"; public static final int User_Code = 1; private UriMatcher mMatcher; @Override public boolean onCreate() { mContext = getContext(); mMatcher = new UriMatcher(UriMatcher.NO_MATCH); //initialization // If URI resource path= content://cn.sch.myprovider/user User, the registration code is returned_ Code mMatcher.addURI(AUTHORITY, "user", User_Code); dbHelp = new DbHelp(mContext); this.db = dbHelp.getWritableDatabase(); // Initialize the data of the table db.execSQL("delete from user"); db.execSQL("insert into user values(1,'xiaoming','Android Development')"); db.execSQL("insert into user values(2,'xiaohua','Java Development')"); return true; } @Override public Cursor query( Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { String tableName = getTableName(uri); return db.query(tableName,projection,selection,selectionArgs,null,null,sortOrder,null); } @Override public String getType( Uri uri) { return null; } @Override public Uri insert( Uri uri, ContentValues values) { String tableName = getTableName(uri); //Insert data into a table db.insert(tableName,null,values); mContext.getContentResolver().notifyChange(uri,null); return uri; } @Override public int delete( Uri uri, String selection, String[] selectionArgs) { String tableName = getTableName(uri); int count=db.delete(tableName, null, null); if(count>0){ getContext().getContentResolver().notifyChange(uri, null); return count; } return 0; } @Override public int update( Uri uri, ContentValues values, String selection, String[] selectionArgs) { String tableName = getTableName(uri); int count = db.update(tableName, values, selection,selectionArgs); if(count>0){ getContext().getContentResolver().notifyChange(uri, null); return count; } return 0; } private String getTableName(Uri uri) { String tableName = null; switch (mMatcher.match(uri)){ case User_Code: tableName = DbHelp.USER_TABLE_NAME; break; } return tableName; } }
DbHelp code is as follows
import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; public class DbHelp extends SQLiteOpenHelper { // Database name private static final String DATABASE_NAME = "finch.db"; // Table name public static final String USER_TABLE_NAME = "user"; //Database version number private static final int DATABASE_VERSION = 1; public DbHelp(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { //Create database tables //be careful; If you change the data statement the second time after the first run, and only change the attribute without the table name, you will find an error when you run the App again, and the newly changed attribute does not exist //Solution: manually delete the created table, or delete the App and reinstall it, so that your changed properties can take effect db.execSQL("CREATE TABLE IF NOT EXISTS "+USER_TABLE_NAME+"(_id INTEGER PRIMARY KEY AUTOINCREMENT,"+" name TEXT,"+"jobs TEXT)"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } }
AndroidManifest.xml for the first application
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.example.gamedemo"> <uses-permission android:name="android.permission.READ_PERSON_DB" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" tools:ignore="ProtectedPermissions" /> <uses-permission android:name="cn.sch.myprovider.Read"/> <uses-permission android:name="cn.sch.myprovider.Write"/> <permission android:name="cn.sch.myprovider.Read" android:label="Permission for read content provider" android:protectionLevel="normal" ></permission> <permission android:name="cn.sch.myprovider.Write" android:label="Permission for write content provider" android:protectionLevel="normal" ></permission> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.MyApplication1"> <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <provider android:authorities="cn.sch.myprovider" android:name=".MyProvide" android:exported="true" android:multiprocess="false" android:grantUriPermissions="true" android:writePermission="cn.sch.myprovider.Write" android:readPermission="cn.sch.myprovider.Read" > </provider> </application> </manifest>
build.gradle for the first application
plugins { id 'com.android.application' } android { compileSdk 29 defaultConfig { applicationId "com.example.gamedemo" minSdk 21 targetSdk 29 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } } dependencies { implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'com.google.android.material:material:1.3.0' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' testImplementation 'junit:junit:4.+' androidTestImplementation 'androidx.test.ext:junit:1.1.2' androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' }
The module contentprovidertest is the second application
The MainActivity code is as follows
package com.example.contentprovidertest; import android.Manifest; import android.content.ContentResolver; import android.content.ContentValues; import android.content.pm.PackageManager; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.text.TextUtils; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; public class MainActivity extends AppCompatActivity implements View.OnClickListener { private static final String TAG = "lzs"; private TextView userTv; private Button btn_query2; private Button btn_insert2; private Button btn_delete2; private Button btn_update2; private EditText xuhao2; private EditText xingming2; private EditText zhiye2; private int id; private String name; private String job; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); this.userTv = findViewById(R.id.id_tv); //Get permissions dynamically if(ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)==PackageManager.PERMISSION_GRANTED ||ActivityCompat.checkSelfPermission (this, Manifest.permission.WRITE_EXTERNAL_STORAGE)==PackageManager.PERMISSION_GRANTED) { init(); //Initialize control }else { ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE},0x10); } } private void init() { xuhao2 = findViewById(R.id.xuhao2); xingming2 = findViewById(R.id.xingming2); zhiye2 = findViewById(R.id.zhiye2); btn_query2 = findViewById(R.id.btn_query2); btn_insert2 = findViewById(R.id.btn_insert2); btn_delete2 = findViewById(R.id.btn_delete2); btn_update2 = findViewById(R.id.btn_update2); btn_query2.setOnClickListener(this); btn_insert2.setOnClickListener(this); btn_delete2.setOnClickListener(this); btn_update2.setOnClickListener(this); } @Override public void onClick(View view) { switch (view.getId()){ case R.id.btn_query2: btn_query2(); break; case R.id.btn_insert2: btn_insert2(); break; case R.id.btn_delete2: btn_delete2(); break; case R.id.btn_update2: btn_update2(); break; } } private void btn_update2() { id = Integer.parseInt(xuhao2.getText().toString()); name = xingming2.getText().toString(); job = zhiye2.getText().toString(); if(!TextUtils.isEmpty(xuhao2.getText())&&!TextUtils.isEmpty(xingming2.getText())&&!TextUtils.isEmpty(zhiye2.getText())){ Uri uri = Uri.parse("content://cn.sch.myprovider/user"); ContentResolver resolver2 = getContentResolver(); ContentValues values2 = new ContentValues(); values2.put("_id", id); values2.put("name", name); values2.put("jobs", job); resolver2.update(uri,values2,"_id="+id,null); Toast.makeText(MainActivity.this, "Update succeeded", Toast.LENGTH_SHORT).show(); }else { Toast.makeText(MainActivity.this,"Please fill in completely and cannot be blank!",Toast.LENGTH_SHORT).show(); } } private void btn_delete2() { id = Integer.parseInt(xuhao2.getText().toString()); if(!TextUtils.isEmpty(xuhao2.getText())){ Uri uri = Uri.parse("content://cn.sch.myprovider/user"); ContentResolver resolver2 = getContentResolver(); resolver2.delete(uri,"_id ="+id,null); Toast.makeText(MainActivity.this, "Delete succeeded", Toast.LENGTH_SHORT).show(); }else { Toast.makeText(MainActivity.this,"Please fill in completely and cannot be blank!",Toast.LENGTH_SHORT).show(); } } private void btn_insert2() { this.userTv.setText(""); id = Integer.parseInt(xuhao2.getText().toString()); name = xingming2.getText().toString(); job = zhiye2.getText().toString(); if(!TextUtils.isEmpty(xuhao2.getText())&&!TextUtils.isEmpty(xingming2.getText())&&!TextUtils.isEmpty(zhiye2.getText())){ Uri uri = Uri.parse("content://cn.sch.myprovider/user"); // Insert data in table ContentValues values2 = new ContentValues(); values2.put("_id", id); values2.put("name", name); values2.put("jobs", job); // Get ContentResolver ContentResolver resolver2 = getContentResolver(); // Insert data into ContentProvider according to URI through ContentResolver resolver2.insert(uri, values2); // Query data from ContentProvider through ContentResolver Cursor cursor2 = resolver2.query(uri, new String[]{"_id", "name","jobs"}, null, null, null); while (cursor2.moveToNext()) { // Output all data in the table this.userTv.append(" Serial number:" + cursor2.getInt(0) + " full name:" + cursor2.getString(1) +" work:"+cursor2.getString(2)+ "\n"); } Toast.makeText(MainActivity.this,"Insert successful",Toast.LENGTH_SHORT).show(); // Close cursor cursor2.close(); }else { Toast.makeText(MainActivity.this,"Please fill in completely and cannot be blank!",Toast.LENGTH_SHORT).show(); } } private void btn_query2() { Uri uri = Uri.parse("content://cn.sch.myprovider/user"); Cursor query = getContentResolver().query(uri, null, null, null, null); //Clear the text to prevent repeated query and multiple display if(!TextUtils.isEmpty(this.userTv.getText())) { this.userTv.setText(""); } while (query.moveToNext()) { int id = query.getInt(0); String name = query.getString(1); String job = query.getString(2); this.userTv.append(" Serial number:" + id + " full name:" + name +" work:"+job+ "\n"); } query.close(); } }
Activity of MainActivity_ The main.xml code is as follows
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:gravity="center"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="50dp" android:text="Second application" android:textSize="30dp" /> <TextView android:id="@+id/id_tv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="250dp" android:text="" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="2" android:gravity="center" android:orientation="vertical"> <EditText android:id="@+id/xuhao2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:ems="10" android:hint="Please enter serial number" android:inputType="number" /> <EditText android:id="@+id/xingming2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:ems="10" android:hint="Please enter your name" android:inputType="textPersonName" /> <EditText android:id="@+id/zhiye2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:ems="10" android:hint="Please enter occupation" android:inputType="textPersonName" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="2" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center" android:orientation="horizontal"> <Button android:id="@+id/btn_query2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:text="Query data" /> <Button android:id="@+id/btn_insert2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:layout_marginTop="20dp" android:text="insert data" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center_horizontal" android:orientation="horizontal"> <Button android:id="@+id/btn_delete2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Delete data" /> <Button android:id="@+id/btn_update2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:text="Update data" /> </LinearLayout> </LinearLayout> </LinearLayout>
AndroidManifest.xml for the second application
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.example.contentprovidertest"> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" tools:ignore="ProtectedPermissions" /> <uses-permission android:name="android.permissions.READ_DATABASE"/> <uses-permission android:name="android.permissioms.WRITE_DATABASE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="cn.sch.myprovider.Read"/> <uses-permission android:name="cn.sch.myprovider.Write"/> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.MyApplication1"> <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
build.gradle for the second application
plugins { id 'com.android.application' } android { compileSdk 29 defaultConfig { applicationId "com.example.contentprovidertest" minSdk 21 targetSdk 29 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } } dependencies { implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'com.google.android.material:material:1.3.0' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' testImplementation 'junit:junit:4.+' androidTestImplementation 'androidx.test.ext:junit:1.1.2' androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' }
If you encounter any problems, you can leave a message and solve them together.
So far, all the codes have been displayed. I hope you like it. If you think it's good, you can give me a compliment, hehe.