android原生的数据库实现[ContentProvider+SQLiteOpenHelpe...

人盡茶涼 提交于 2020-04-10 08:55:57

   先吐槽下:最近几周压力老大了,前面我们三个ios开发人员花了一个多月开发的应用要移植到android上,由于应用相对比较复杂,有拖拽排序、离线下载、二维码扫描,而这次,另一个ios开发人员离职,剩下的另一个还没做个android,得由我带,于是,我估计了下开发时间,大约34天,我将计划发给领导,领导却说最多给我15天,我半天没说一个字:*&%¥#%**&⋯⋯&&

   哎,出来混的,不管怎样,尽量吧。

   这个开发,我们四张表要操作,相对于一般的移动应用,还是比较大的,故数据库实现肯定是整个开发的第一个和至关重要的一步,于是,我整理了下面这个demo。

   其实网上有很多比较不错的orm移动开发框架,相对还是比较优秀,可以节省大把的开发时间,但我个人认为,现在这个还不是很稳定,而且,在效率上,我持保守态度,移动开发上,应相对的少用反射和抽象接口,而网络上的一些第三方的框架,却抓住这个大用特用,个人觉得,不大好,另外,采用android原生的数据库实现方案,还有个极大的好处是:很容易实现产品生态系统,这个是其他的orm所不易达到的。也即,可以轻而易举地实现我们的几个应用间的数据共享和交互,URI访问即可,从而形成一个生态圈的目的,具体的,就不多说了。

   这个demo是一个完整的db操作,但只给出了一个实体的db实现,不过,足以:

程序结构图:

后面的所有db操作,都会被封装到DBManager中,也即,以后应用的db操作,只需通过DBManager进行交互接口,具体的看TestDBAct中实现:

1、抽象实体基类 BasicEntity.java:

package blockcheng.android.model;

import java.io.Serializable;

import org.json.JSONObject;

import android.content.ContentValues;
import android.database.Cursor;
import android.os.Parcelable;
import android.provider.BaseColumns;

public abstract class BasicEntity implements BaseColumns,Serializable, Parcelable{
	private static final long serialVersionUID = 7169909425485915669L;
	
	protected String entityName;
	protected String entityId;
	protected long lastUpdateTime;
	
	public static final String sDEFAULTSORT = "  lastUpdateTime desc";
	public static final String sKEY_LASTUPDATETIMEADID = "lastUpdateTime";
	/**
	 * demo methods
	 * @param obj
	 * @return
	 */
	public static BasicEntity translateJson2Object(JSONObject obj){
		//TODO:this method must be override by subclass.
		return null;
	}

	public String getEntityName() {
		return entityName;
	}

	public void setEntityName(String entityName) {
		this.entityName = entityName;
	}

	public String getEntityId() {
		return entityId;
	}

	public void setEntityId(String entityId) {
		this.entityId = entityId;
	}

	public long getLastUpdateTime() {
		return lastUpdateTime;
	}

	public void setLastUpdateTime(long lastUpdateTime) {
		this.lastUpdateTime = lastUpdateTime;
	}
	
	
	public abstract ContentValues getContentValues();
	
	/**
	 * demo method :should be copy by subclass
	 * @param c
	 * @return
	 */
	public static BasicEntity fromCursor(Cursor c) {
		return null;
	}

	@Override
	public String toString() {
		return "id=" + entityId  + ", name="
				+ entityName + ", lastUpdateTime=" + lastUpdateTime + "";
	}
}

2、广告实体类AdvertisementEntity.java:

package blockcheng.android.model;

import java.io.Serializable;

import org.json.JSONObject;

import android.content.ContentValues;
import android.database.Cursor;
import android.os.Parcel;

public class AdvertisementEntity extends BasicEntity implements Serializable{
	private static final long serialVersionUID = 3045767248752772598L;
	
	/**
	 * 广告图片路径
	 **/
	private String adPic;
	/**
	 * 广告类型  1主题 2 单个商品(后可扩充,客户端可根据广告类型设定广告的点击状态)
	 **/
	private int adType;
	/**
	 * 广告对象地址(主题ID、单个商品ID或者其他)
	 **/
	private String adTarget;

	//'广告位置:1.banner(后扩充位置)
	private int adPosition;
	private String data;

	/**
	 * demo methods
	 * @param obj
	 * @return
	 */
	public static AdvertisementEntity translateJson2Object(JSONObject obj){
		//TODO:implement it later.
		AdvertisementEntity aEntity = new AdvertisementEntity();
		return aEntity;
	}
	public String getAdPic() {
		return adPic;
	}

	public void setAdPic(String adPic) {
		this.adPic = adPic;
	}

	public int getAdType() {
		return adType;
	}

	public void setAdType(int adType) {
		this.adType = adType;
	}

	public String getAdTarget() {
		return adTarget;
	}

	public void setAdTarget(String adTarget) {
		this.adTarget = adTarget;
	}

	public int getAdPosition() {
		return adPosition;
	}

	public void setAdPosition(int adPosition) {
		this.adPosition = adPosition;
	}

	public String getData() {
		return data;
	}

	public void setData(String data) {
		this.data = data;
	}

	@Override
	public int describeContents() {
		// TODO Auto-generated method stub
		return 0;
	}

	/******************************************************************/
	/*********************Database config information********************/
	/******************************************************************/
	
	public static final String sTABLE_NAME = "AdvertisementEntity";
	public static final String sKEY_ADID = "adId";
	public static final String sKEY_ADNAME = "adName";
	
	public static final String sKEY_ADTYPE = "adType";
	public static final String sKEY_ADPIC = "adPic";
	public static final String sKEY_ADTARGET = "adTarget";
	
	/*
	 * 要查询的全部字段
	 */
	public  static String[] ADS_PROJECTION_ALL = new String[] {
		"adId","adName","lastUpdateTime",
		"adType","adPic","adTarget",
	};

	@Override
	public void writeToParcel(Parcel dest, int flags) {
		// TODO Auto-generated method stub
	}

	@Override
	public ContentValues getContentValues() {
		// TODO Auto-generated method stub
		final ContentValues values = new ContentValues();
		values.put(sKEY_ADID, getEntityId());
		values.put(sKEY_ADNAME, getEntityName());
		values.put(sKEY_ADTYPE, adType);
		
		values.put(sKEY_ADPIC, adPic);
		values.put(sKEY_ADTARGET, adTarget);
		return values;
	}

	public static AdvertisementEntity fromCursor(Cursor c) {
		AdvertisementEntity aEntity = new AdvertisementEntity();
		
		aEntity.entityId = c.getString(c.getColumnIndexOrThrow(sKEY_ADID));
		aEntity.entityName = c.getString(c.getColumnIndexOrThrow(sKEY_ADNAME));
		aEntity.adPic = c.getString(c.getColumnIndexOrThrow(sKEY_ADTARGET));
		aEntity.adType = c.getInt(c.getColumnIndexOrThrow(sKEY_ADTYPE));
		aEntity.lastUpdateTime = c.getLong(c.getColumnIndexOrThrow(BasicEntity.sKEY_LASTUPDATETIMEADID));
		return aEntity;
	}

	@Override
	public String toString() {
		return "AdvertisementEntity [" + super.toString()+ "adPic= "+ adPic + ", adType=" + adType
				+ ", adTarget=" + adTarget + ", adPosition=" + adPosition
				+ ", data=" + data + "]" + super.toString();
	}
}
3、DB的一些常量配置 DBConfig.java:

package blockcheng.android.service.database;

import android.net.Uri;

public interface DBConfig {
	public static final String DATABASE_NAME = "blockchengDB";
	public static final int DATABASE_VERSION = 1;
	public static final String DB_TAG = "blockcheng";
	
	public static final String AUTHORITY = "blockcheng.android";
	// content url
	public static final Uri CONTENT_URI_AdvertisementEntity = Uri
			.parse("content://" +AUTHORITY+
					"/AdvertisementEntity");
	//search value
	public static final int sSEARCH = 10;
	public static final int sADVERTISEMENT  = 100;
	public static final int sADVERTISEMENT_ID  = 101;

}
4、建表类DBHelper.java:

package blockcheng.android.service.database;

import blockcheng.android.model.AdvertisementEntity;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

public class DBHelper extends SQLiteOpenHelper implements DBConfig{
	public DBHelper(Context context) {
		super(context, DBConfig.DATABASE_NAME, null, DBConfig.DATABASE_VERSION);
	}

	@Override
	public void onCreate(SQLiteDatabase db) {
		// TODO Auto-generated method stub
		StringBuffer createAdSql = new StringBuffer("CREATE TABLE'");
		createAdSql.append(AdvertisementEntity.sTABLE_NAME);
		createAdSql.append( "'('adId'  TEXT NOT NULL," );
		createAdSql.append( "'adName'  TEXT,");
		createAdSql.append("'lastUpdateTime'  real DEFAULT 0,");
		createAdSql.append("'adType'  INTEGER,");
		
		createAdSql.append("'adPic'  TEXT,");
		createAdSql.append( "'adTarget'  TEXT,");
		createAdSql.append( "PRIMARY KEY ('adId')" );
		createAdSql.append( ");");;
		
		Log.i(DBConfig.DB_TAG, "createAdSql::"+createAdSql.toString());
		db.execSQL(createAdSql.toString());
		
		String createIndexSql_0 = "CREATE INDEX 'id' ON '" +
				AdvertisementEntity.sTABLE_NAME +
				"' ('adId' ASC);";
		db.execSQL(createIndexSql_0);

	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		// TODO Auto-generated method stub
		Log.w(DBConfig.DB_TAG, "Upgrading database from version " + oldVersion
				+ " to " + newVersion + ", which will destroy all old data");

		db.execSQL("DROP TABLE IF EXISTS " +
				AdvertisementEntity.sTABLE_NAME);
		onCreate(db);
	}

}
5、contentProvider实现类DemoContentProvider.java:

package blockcheng.android.service.database;

import java.util.HashMap;

import blockcheng.android.model.AdvertisementEntity;
import blockcheng.android.model.BasicEntity;

import android.app.SearchManager;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
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;
import android.util.Log;

public class DemoContentProvider extends ContentProvider {
	private SQLiteOpenHelper dbHelper;
	private static final UriMatcher URI_MATCHER;
	static {//TODO 需要整合下面的地址:
		URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
		URI_MATCHER.addURI(DBConfig.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY,
				DBConfig.sSEARCH);
		URI_MATCHER.addURI(DBConfig.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY
				+ "/*", DBConfig.sSEARCH);
		
		//advertisement uri config
		URI_MATCHER.addURI(DBConfig.AUTHORITY, AdvertisementEntity.sTABLE_NAME, DBConfig.sADVERTISEMENT);
		URI_MATCHER.addURI(DBConfig.AUTHORITY, AdvertisementEntity.sTABLE_NAME+"/*", DBConfig.sADVERTISEMENT_ID);
		
	}

	// TODO: need more investigate.
	private static final HashMap<String, String> SUGGESTION_PROJECTION_MAP;
	static {
		SUGGESTION_PROJECTION_MAP = new HashMap<String, String>();
		SUGGESTION_PROJECTION_MAP.put(SearchManager.SUGGEST_COLUMN_TEXT_1,
				"topicName " + " AS " + SearchManager.SUGGEST_COLUMN_TEXT_1);
		SUGGESTION_PROJECTION_MAP.put(SearchManager.SUGGEST_COLUMN_TEXT_2,
				"goodName " + " AS " + SearchManager.SUGGEST_COLUMN_TEXT_2);
		SUGGESTION_PROJECTION_MAP.put(
				SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID, AdvertisementEntity._ID + " AS "
						+ SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID);
		SUGGESTION_PROJECTION_MAP.put(AdvertisementEntity._ID, AdvertisementEntity._ID);
	}

	@Override
	public boolean onCreate() {
		// TODO Auto-generated method stub
		dbHelper = new DBHelper(getContext());
		return true;
	}

	// TODO: update the manifest accordingly.
	public String getType(Uri uri) {
		switch (URI_MATCHER.match(uri)) {
		case DBConfig.sADVERTISEMENT:
		case DBConfig.sADVERTISEMENT_ID:
			return "vnd.android.cursor.dir/" + DBConfig.AUTHORITY + "."
					+ AdvertisementEntity.sTABLE_NAME;
		
		default:
			throw new IllegalArgumentException("Unknown URI " + uri);
		}
	}

	@Override
	public Uri insert(Uri uri, ContentValues initialValues) {
		// TODO Auto-generated method stub
		Log.i(DBConfig.DB_TAG, "insert::"+uri.toString());
		ContentValues values;

		SQLiteDatabase db = dbHelper.getWritableDatabase();
		switch(URI_MATCHER.match(uri)){
		case DBConfig.sADVERTISEMENT:
			if (initialValues != null) {
				values = new ContentValues(initialValues);
				values.put(BasicEntity.sKEY_LASTUPDATETIMEADID, System.currentTimeMillis());
			} else {
				values = new ContentValues();
			}
			final long rowId = db.insert(AdvertisementEntity.sTABLE_NAME, AdvertisementEntity.sKEY_ADPIC, values);
			if (rowId > 0) {
				Uri insertUri = ContentUris.withAppendedId(DBConfig.CONTENT_URI_AdvertisementEntity, rowId);
				getContext().getContentResolver().notifyChange(uri, null);
				return insertUri;
			}
			throw new SQLException("Failed to insert row into " + uri);
		default:
			throw new IllegalArgumentException("Unknown URI " + uri);
		}
			
	}

	@Override
	public int delete(Uri uri, String selection, String[] selectionArgs) {
		// TODO Auto-generated method stub
		Log.i(DBConfig.DB_TAG, "delete::"+uri.toString());
		SQLiteDatabase db = dbHelper.getWritableDatabase();
		int count;
		switch (URI_MATCHER.match(uri)) {
		case DBConfig.sADVERTISEMENT:
			count = db.delete(AdvertisementEntity.sTABLE_NAME, selection, selectionArgs);
			break;
		case DBConfig.sADVERTISEMENT_ID:
			String adId = uri.getPathSegments().get(1);
			count = db.delete(AdvertisementEntity.sTABLE_NAME,
					AdvertisementEntity.sKEY_ADID
							+ "="
							+ adId
							+ (!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 where,
			String[] whereArgs) {
		Log.i(DBConfig.DB_TAG, "update::"+uri.toString() +": value="+values.toString());
		SQLiteDatabase db = dbHelper.getWritableDatabase();
		int count = -1;
		switch (URI_MATCHER.match(uri)) {
		case DBConfig.sADVERTISEMENT_ID:
			Log.i(DBConfig.DB_TAG, "update::sADVERTISEMENT_ID");
			String adverstisementId = uri.getPathSegments().get(1);
			count = db.update(
					AdvertisementEntity.sTABLE_NAME,
					values,
					AdvertisementEntity.sKEY_ADID
							+ "='"
							+ adverstisementId
							+ "'"
							+ (!TextUtils.isEmpty(where) ? " AND (" + where
									+ ')' : ""), whereArgs);
			break;
		case DBConfig.sADVERTISEMENT:
			Log.i(DBConfig.DB_TAG, "update::sADVERTISEMENT");
			String aidString = values.getAsString(AdvertisementEntity.sKEY_ADID);
			values.remove(AdvertisementEntity.sKEY_ADID);
			count = db.update(
					AdvertisementEntity.sTABLE_NAME,
					values,
					AdvertisementEntity.sKEY_ADID
					+ "='"
					+ aidString
					+ "'"
					+ (!TextUtils.isEmpty(where) ? " AND (" + where
							+ ')' : ""), whereArgs);
			break;
		
		default:
			throw new IllegalArgumentException("Unknown URI " + uri);
		}
		return count;
	}

	@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder) {
		Log.i(DBConfig.DB_TAG, "query::"+uri.toString());
		SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
		String orderBy;
		switch (URI_MATCHER.match(uri)) {
		case DBConfig.sADVERTISEMENT:
			qb.setTables(AdvertisementEntity.sTABLE_NAME);
			break;
		case DBConfig.sADVERTISEMENT_ID:
			qb.setTables(AdvertisementEntity.sTABLE_NAME);
			qb.appendWhere(AdvertisementEntity.sKEY_ADID + "='"
					+ uri.getPathSegments().get(1) +"'");
			break;
		default:
			throw new IllegalArgumentException("Unknown URI " + uri);
		}

		// If no sort order is specified use the default
		if (TextUtils.isEmpty(sortOrder)) {
			orderBy = BasicEntity.sDEFAULTSORT;
		} else {
			orderBy = sortOrder;
		}
		
		SQLiteDatabase db = dbHelper.getReadableDatabase();
		Cursor c = qb.query(db, projection, selection, selectionArgs, null,
				null, orderBy);
		c.setNotificationUri(getContext().getContentResolver(), uri);

		return c;
	}

	

}
测试界面布局:

<?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="15dip" >

    <EditText
        android:id="@+id/et_condition"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10"
        android:text="1"
         >

        <requestFocus />
    </EditText>

    <Button
        android:id="@+id/db_button1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:clickable="true"
        android:onClick="handleEvent"
        android:text="add " />

    <Button
        android:id="@+id/db_button2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:clickable="true"
        android:onClick="handleEvent"
        android:text="delete" />

    <Button
        android:id="@+id/db_button3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:clickable="true"
        android:onClick="handleEvent"
        android:text="update" />

    <Button
        android:id="@+id/db_button4"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:clickable="true"
        android:onClick="handleEvent"
        android:text="query" />

</LinearLayout>
测试类TestDBAct.java:

package cn.helloclq.android.activity;

import blockcheng.android.R;
import blockcheng.android.model.AdvertisementEntity;
import blockcheng.android.service.database.DBManager;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;

public class TestDBAct extends Activity {

	EditText etCondition;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.dbtest_activity);
		etCondition = (EditText)findViewById(R.id.et_condition);
		
	}
	
	
	public void handleEvent(View view){

		String adIdString = etCondition.getText().toString();
		AdvertisementEntity aEntity = new AdvertisementEntity();
		aEntity.setEntityId(adIdString);
		aEntity.setEntityName("ad"+adIdString);
		aEntity.setAdType(Integer.parseInt(adIdString));
		aEntity.setAdTarget(adIdString);
		
		
		//public WebTaskTest(Context context, boolean showDialog,
//		boolean cancelAble, String dialogLoadingStr,
//		WebRequestCallbackInfc cb)
		switch(view.getId()) {
		case R.id.db_button1://add 
			DBManager.getInstance().addAdvertiseMent(getContentResolver(), aEntity);
			break;
		case R.id.db_button2://delete
			DBManager.getInstance().deleteAdvertiseMentById(getContentResolver(), adIdString);
			
			break;
		case R.id.db_button3://update
			aEntity.setEntityName("ad update"+adIdString);
			DBManager.getInstance().updateAdervertiseEntity(getContentResolver(), aEntity);
			break;
		case R.id.db_button4://query
			DBManager.getInstance().querAllAdvertisementEntity(getContentResolver());
			break;
		
		}
		
		
		
	}
	
	
}
运行效果图:


运行日志:

08-07 10:52:50.305: I/blockcheng(458): query::content://blockcheng.android/AdvertisementEntity/1
08-07 10:52:50.415: I/blockcheng(458): createAdSql::CREATE TABLE'AdvertisementEntity'('adId'  TEXT NOT NULL,'adName'  TEXT,'lastUpdateTime'  real DEFAULT 0,'adType'  INTEGER,'adPic'  TEXT,'adTarget'  TEXT,PRIMARY KEY ('adId'));
08-07 10:52:50.434: I/blockcheng(458): insert::content://blockcheng.android/AdvertisementEntity
08-07 10:52:50.455: I/blockcheng(458): addAdvertiseMent::AdvertisementEntity [id=1, name=ad1, lastUpdateTime=0adPic= null, adType=1, adTarget=1, adPosition=0, data=null]id=1, name=ad1, lastUpdateTime=0
08-07 10:53:08.567: I/blockcheng(458): query::content://blockcheng.android/AdvertisementEntity
08-07 10:53:08.575: I/blockcheng(458): ad:AdvertisementEntity [id=1, name=ad1, lastUpdateTime=1375843970443adPic= 1, adType=1, adTarget=null, adPosition=0, data=null]id=1, name=ad1, lastUpdateTime=1375843970443
08-07 10:53:15.585: I/blockcheng(458): update::content://blockcheng.android/AdvertisementEntity: value=adTarget=1 adType=1 adPic=null adId=1 lastUpdateTime=1375843995585 adName=ad update1
08-07 10:53:15.585: I/blockcheng(458): update::sADVERTISEMENT
08-07 10:53:17.355: I/blockcheng(458): delete::content://blockcheng.android/AdvertisementEntity/1
08-07 10:53:18.835: I/blockcheng(458): query::content://blockcheng.android/AdvertisementEntity

麻雀虽小,五脏俱全,加多张表的话,也是如此的,就这样吧,都写了好一会儿了。

补充manifest文件:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="blockcheng.android"
    android:versionCode="1"
    android:versionName="1.0" >
    <uses-sdk
        android:minSdkVersion="4"
        android:targetSdkVersion="17" />

    <uses-permission android:name="android.permission.RECEIVE_USER_PRESENT" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>  
    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
	<uses-permission android:name="android.permission.READ_LOGS"></uses-permission>
    <application
        android:allowBackup="true"
        android:label="@string/app_name"
         >
         <activity
            android:name="cn.helloclq.android.activity.TestAct"
            android:label="@string/app_name"
            android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|screenLayout|fontScale|uiMode|orientation"
            android:screenOrientation="portrait" >
        </activity>
         <activity
            android:name="cn.helloclq.android.activity.TestDBAct"
            android:label="@string/app_name"
            android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|screenLayout|fontScale|uiMode|orientation"
            android:screenOrientation="portrait" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <provider
			android:exported="false"
            android:authorities="blockcheng.android"
            android:name="blockcheng.android.service.database.DemoContentProvider" />
        
    </application>

</manifest>

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!