安卓进程间通信的四种方式(含案例)
由于应用程序之间不能共享内存。在不同应用程序之间交互数据(跨进程通讯),在android SDK中提供了4种用于跨进程通讯的方式。这4种方式正好对应于android系统中4种应用程序组件:Activity、Content Provider、Broadcast和Service。其中Activity可以跨进程调用其他应用程序的Activity;Content Provider可以跨进程访问其他应用程序中的数据(以Cursor对象形式返回),当然,也可以对其他应用程序的数据进行增、删、改操 作;Broadcast可以向android系统中所有应用程序发送广播,而需要跨进程通讯的应用程序可以监听这些广播;Service和Content Provider类似,也可以访问其他应用程序中的数据,但不同的是,Content Provider返回的是Cursor对象,而Service返回的是Java对象,这种可以跨进程通讯的服务叫AIDL服务。
一、Activity
Activity的跨进程访问与进程内访问略有不同。虽然它们都需要Intent对象,但跨进程访问并不需要指定Context对象和Activity的 Class对象,而需要指定的是要访问的Activity所对应的Action(一个字符串)。有些Activity还需要指定一个Uri(通过 Intent构造方法的第2个参数指定)。 在android系统中有很多应用程序提供了可以跨进程访问的Activity,例如,下面的代码可以直接调用拨打电话的Activity。如调用系统通话应用
Intent callIntent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:12345678" );
startActivity(callIntent);
二、 Brocast(广播)
广播是一种被动跨进程通讯的方式。当某个程序向系统发送广播时,其他的应用程序只能被动地接收广播数据。这就象电台进行广播一样,听众只能被动地收听,而不能主动与电台进行沟通。
在应用程序中发送广播比较简单。只需要调用sendBroadcast方法即可。该方法需要一个Intent对象。通过Intent对象可以发送需要广播的数据。如显示系统时间
三、 ContentProvider
ContentProvider向我们提供了我们在应用程序之前共享数据的一种机制,而我们知道每一个应用程序都是运行在不同的应用程序的,数据和文件在不同应用程序之间达到数据的共享不是没有可能,而是显得比较复杂,而正好Android中的ContentProvider则达到了这一需求,比如有时候我们需要操作手机里的联系人,手机里的多媒体等一些信息,我们都可以用到这个ContentProvider来达到我们所需。如访问系统相册
实例讲解:利用ContentProvider实现进程间的通信:
1、建立数据库,“game_provider.db”并创建一下表“game”:
public class DbOpenHelper extends SQLiteOpenHelper {
private static final String DB_NAME="game_provider.db";
static final String GAME_TABLE_NAME="game";
private static final int DB_VERSION=1;
private String CREATE_GAME_TABLE="create table if not exists " + GAME_TABLE_NAME +"(_id integer primary key," + "name TEXT, "+"describe TEXT)";
public DbOpenHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_GAME_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
2、使用ContentProvider对数据库进行操作
public class GameProvider extends ContentProvider {
public static final String AUTHORITY = "com.example.liuwangshu.mooncontentprovide.GameProvider";
public static final Uri GAME_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/game");
private static final UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
private SQLiteDatabase mDb;
private Context mContext;
private String table;
static {
mUriMatcher.addURI(AUTHORITY, "game", 0);
}
@Override
public boolean onCreate() {
table = DbOpenHelper.GAME_TABLE_NAME;
mContext = getContext();
initProvoder();
return false;
}
private void initProvoder() {
mDb = new DbOpenHelper(mContext).getWritableDatabase();
new Thread(new Runnable() {
@Override
public void run() {
mDb.execSQL("delete from " + DbOpenHelper.GAME_TABLE_NAME);
mDb.execSQL("insert into game values(1,'九阴真经ol','最好玩的武侠网游');");
}
}).start();
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
String table = DbOpenHelper.GAME_TABLE_NAME;
Cursor mCursor = mDb.query(table, projection, selection, selectionArgs, null, sortOrder, null);
return mCursor;
}
@Override
public String getType(Uri uri) {
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
mDb.insert(table, null, values);
mContext.getContentResolver().notifyChange(uri, null);
return null;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return 0;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return 0;
}
}
其中我们展开讲一下UriMatcher,UriMatcher本质上是一个文本过滤器,用在contentProvider中帮助我们过滤,分辨出查询者想要查询哪个数据表。
demo:
UriMatcher类用于匹配Uri,它的用法如下:
首先第一步把你需要匹配Uri路径全部给注册上,如下:
//常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码
UriMatcher sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//如果match()方法匹配content://cn.xxt.provider.personprovider/person路径,返回匹配码为1
sMatcher.addURI(“cn.xxt.provider.personprovider”, “person”, 1);//添加需要匹配uri,如果匹配就会返回匹配码
//如果match()方法匹配content://cn.xxt.provider.personprovider/person/230路径,返回匹配码为2
sMatcher.addURI(“cn.xxt.provider.personprovider”, “person/#”, 2);//#号为通配符
switch (sMatcher.match(Uri.parse("content://cn.xxt.provider.personprovider/person/10"))) {
case 1
break;
case 2
break;
default://不匹配
break;
}
注册完需要匹配的Uri后,就可以使用sMatcher.match(uri)方法对输入的Uri进行匹配,如果匹配就返回匹配码,匹配码是调用addURI()方法传入的第三个参数,假设匹配content://cn.xxt.provider.personprovider/person路径,返回的匹配码为1
开启多进程的方法是在AndroidManifest.xml中这样声明:
<provider android:authorities="com.example.liuwangshu.mooncontentprovide. GameProvider"
android:name=".GameProvider"
android:process=":provider">
</provider>
3、在ContentProviderActivity中调用另一个进程GameProvider的方法,这一步就是进行进程间通信的重要一步
public class ContentProviderActivity extends AppCompatActivity {
private final static String TAG = "ContentProviderActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_content_provider);
Uri uri = Uri.parse("content://com.example.liuwangshu.mooncontentprovide.GameProvider");
ContentValues mContentValues = new ContentValues();
mContentValues.put("_id", 2);
mContentValues.put("name", "大航海时代ol");
mContentValues.put("describe", "最好玩的航海网游");
//在这里就调用了getContentResolver方法拿到resolver对象的insert方法,实际上是调用了GameResolver的insert方法
getContentResolver().insert(uri, mContentValues);
Cursor gameCursor = getContentResolver().query(uri, new String[]{"name", "describe"}, null, null, null);
while (gameCursor.moveToNext()) {
Game mGame = new Game(gameCursor.getString(0), gameCursor.getString(1));
Log.i(TAG, mGame.gameName + "---" + mGame.gameDescribe);
}
}
}
其中Game.java如下,实现Parcelable接口:
public class Game implements Parcelable {
public String gameName;
public String gameDescribe;
public Game(String gameName, String gameDescribe) {
this.gameName = gameName;
this.gameDescribe = gameDescribe;
}
protected Game(Parcel in) {
gameName = in.readString();
gameDescribe = in.readString();
}
public static final Creator<Game> CREATOR = new Creator<Game>() {
@Override
public Game createFromParcel(Parcel in) {
return new Game(in);
}
@Override
public Game[] newArray(int size) {
return new Game[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(gameName);
dest.writeString(gameDescribe);
}
}
查看打印日志:
四、 AIDL Service
AIDL是Android中IPC(Inter-Process Communication)方式中的一种,AIDL是Android Interface definition language的缩写,对于小白来说,AIDL的作用是让你可以在自己的APP里绑定一个其他APP的service,这样你的APP可以和其他APP交互。
它相比Broadcast而言,虽然实现上稍微麻烦了一点,但是它的优势就是不会像广播那样在手机中的广播较多时会有明显的时延,甚至有广播发送不成功的情况出现。 注意普通的Service并不能实现跨进程操作,实际上普通的Service和它所在的应用处于同一个进程中,而且它也不会专门开一条新的线程,因此如果在普通的Service中实现在耗时的任务,需要新开线程。要实现跨进程通信,需要借助AIDL(Android Interface Definition Language)。Android中的跨进程服务其实是采用C/S的架构,因而AIDL的目的就是实现通信接口。
.
AIDL的实现,我们下一篇列举案例分析。
上一篇: vue组件间的通信方式
下一篇: CSS 自定义css样式汇总
推荐阅读