Bundle
实现了parcelable序列化的类,可以跨进成通信
例如在activity和service中跨进程通信
Bundle bundle = new Bundle();
bundle.putString("a","a");
intent.putExtras(bundle);
// bindService(intent, myserviceconnection, BIND_AUTO_CREATE);如果不需要调用binder获取服务就不用使用bindService方法
startService(intent);
service端
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Bundle bundle = intent.getExtras();
Log.e("xxxxx", "bundle传递的值:"+(String) bundle.get("a"));
return super.onStartCommand(intent, flags, startId);
}
文件共享
可以将数据、对象写入一个共享文件(外部存储位置即可)中,多个进程就可以同时读写这个文件,windows上如果文件加了排斥锁,就会导致外部线程不能读写,但是linux没有这种限制。但是同时多谢可能会产生一些问题,还是需要注意的。
尽量避免并发读写
还有另一种轻量级的文件SharedPreferences文件,当他设置为全局模式,也是一种共享文件
创建SharedPreferences文件
SharedPreferences sharedPreferences = getSharedPreferences("a.xml",Context.MODE_WORLD_WRITEABLE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("a","a");
editor.commit();
另外一个进程判断文件是否可读
File file = new File("/data/data/com.app.typicallifecycle/shared_prefs/a.xml.xml");
Log.e("xxxxx","is this file readable? "+ String.valueOf(file.canRead()));
结果返回:E/xxxxx: is this file readable? true
Messager(信使)
这个Messenger
类是通过message
来传递消息数据,可以从源码中看出。所以它是不能并发来进程通信的
public void send(Message message) throws RemoteException {
mTarget.send(message);
}
并且它的底层也是通过AIDL实现的。看Messenger.java
代码,从Stub.asInterface
就已经很熟悉了对吧,然后我们再看看IMessenger
这个android/platform/frameworks/base/android-9.0.0_r21/./core/java/android/os/IMessenger.aidl
文件就可以完全确定了
....
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target);
}
在传递数据方面,有arg1,arg2,obj,Bundle,replyTo。obj可以传递由aidl生成的自定义的数据对象,Bundle也可以并且基础类型也比obj稍微多一些。下面是一个使用Bundle传递数据的实现
简单实例:跨进成发消息
客户端(这里的客户端、服务端就是两个进程):发起绑定请求,成功连接上就通过信使Messenger对象传递字符数据
private ServiceConnection myserviceconnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//连接上服务的回调函数
Log.e("xxxxx", name.getClassName());
//从传过来的ibinder对象中获取一个信使messenger对象
Messenger messenger001 = new Messenger(service);
Message message = new Message();
Bundle bundle = new Bundle();
bundle.putString("a","i am client!");
message.what=0;
message.setData(bundle);
try {
messenger001.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
try {
service.linkToDeath(deathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
}
服务端接收数据
public class AIDLService extends Service {
private Messenger messenger = new Messenger(new messageHanler());
@Nullable
@Override
public IBinder onBind(Intent intent) {
//getBinder返回一个messenger和其handler通信的binder
return messenger.getBinder();
}
class messageHanler extends Handler{
@Override
public void handleMessage(Message msg) {
if(msg.what==0){
String u1 = msg.getData().getString("a");
Log.e("xxxxx", "Receive from client:" + u1);
}
}
}
}
进程双方互相可以通信
客户端,相比较上面的代码做了一些修改,主要是引入了replyTo
回复信使这个方法,客户端来创建这个回复Messenger,并把它通过message传递到服务端,服务端接收此messenger,通过它回复客户端的消息
private Messenger messenger = new Messenger(new clientHandler());
private class clientHandler extends Handler{
@Override
public void handleMessage(Message msg) {
if(msg.what==0){
String u1 = msg.getData().getString("a");
Log.e("xxxxx","receive from server:"+u1);
}
}
}
private ServiceConnection myserviceconnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//连接上服务的回调函数
Log.e("xxxxx", name.getClassName());
//从传过来的ibinder对象获取一个信使messenger对象
Messenger messenger001 = new Messenger(service);
Message message = Message.obtain(null, 0);
Bundle bundle = new Bundle();
bundle.putString("a","i am client!");
message.setData(bundle);
//设置客户端的handler
message.replyTo = messenger;
try {
messenger001.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
try {
service.linkToDeath(deathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
}
服务端
class messageHanler extends Handler{
@Override
public void handleMessage(Message msg) {
if(msg.what==0){
//客户端的信使Messenger,通过客户端传过来的信使来发送消息
Messenger client = msg.replyTo;
String u1 = msg.getData().getString("a");
Log.e("xxxxx", "Receive from client:" + u1);
//从全局池中获取一个message对象,来防止重复创建,消耗内存
Message message = Message.obtain(null, 0);
Bundle bundle = new Bundle();
bundle.putString("a","i am server and i will reply you after a few minutes");
message.setData(bundle);
try {
client.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
AIDL
在上一篇已经说过,binder是在线程池中来处理多个进程的并发请求,所以在AIDL接口方法中考虑处理线程并发的情况,这样就引入了CopyOnWriteArrayList
CopyOnWriteArrayList
官方解释:是ArrayList的安全线程的变体,支持多线程并发读写操作(是由底层数组副本来实现的),最终返回的是ArrayList对象
- A thread-safe variant of {@link java.util.ArrayList} in which all mutative
- operations ({@code add}, {@code set}, and so on) are implemented by
- making a fresh copy of the underlying array.
服务端(只需要把数据)
private CopyOnWriteArrayList<Book> books = new CopyOnWriteArrayList<Book>();
private final BookManager.Stub bookManager = new BookManager.Stub() {
@Override
public void addBooks(Book book) throws RemoteException {
books.add(book);
}
@Override
public List<Book> getBook() throws RemoteException {
return books;
}
};
@Override
public void onCreate() {
Log.e("xxxxx", "服务创建了");
books.add(new Book(1,"第一本书"));
books.add(new Book(2,"第二本书"));
super.onCreate();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
//getBinder返回一个messenger和其handler通信的binder
return bookManager;
}
客户端(连接到服务端进程上时):请求返回books(在服务端时,是CopyOnWriteArrayList类型)对象的类型,返回结果好是
ArrayList
private ServiceConnection myserviceconnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//连接上服务的回调函数
Log.e("xxxxx", name.getClassName());
bookManager = BookManager.Stub.asInterface(service);
try {
List<Book> books = (List<Book>) bookManager.getBook();
Log.e("xxxxx","类名:" + books.getClass().getCanonicalName());
Log.e("xxxxx","书库存书:" + books.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
try {
service.linkToDeath(deathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
}
新需求:需要满足服务进程新增书籍后,希望接收相关书籍的客户进程可以接收到通知
- 创建一个新书通知功能的接口,这个接口是供服务端进程调用的
这个接口为什么使用aidl文件来创建:aidl接口只能使用aidl接口,不能使用普通接口
import com.app.typicallifecycle.Book;
interface IOnNewBookArrived {
void OnNewBookArrvied(in Book book);
}
- 添加两个新功能给用户端进程调用,用来选择是否启用通知服务
为什么这里要将IOnNewBookArrived对象作为参数:客户端进程需要和服务端进程建立联系,通过在OnNewBookArrvied方法内部封装发送message到固定handler的方法
interface BookManager {
void addBooks(in Book book);
List<Book> getBook();
void registerNewBookArrivedListener(IOnNewBookArrived listener);
void unreregisterNewBookArrivedListener(IOnNewBookArrived listener);
}
- 客户端进程内实现这个OnNewBookArrvied方法的封装,并作为参数传给服务端进程来让服务端进程发送message进行提醒服务
public class MainActivity extends AppCompatActivity {
private static final String TAG = "xxxxx";
private static final int MESSAGE_NEWBOOK_ARRIVED = 1;
private EditText editText;
private Button button;
private Button button1;
private Context context =getGlobalContext.getContext();
private BookManager bookManager;
private Intent intent;
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
if (msg.what==MESSAGE_NEWBOOK_ARRIVED){
Log.e(TAG, "客户端收到新书新增通知!");
}
}
};
private ServiceConnection myserviceconnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//连接上服务的回调函数
Log.e("xxxxx", name.getClassName());
bookManager = BookManager.Stub.asInterface(service);
try {
List<Book> books = (List<Book>) bookManager.getBook();
Log.e("xxxxx","类名:" + books.getClass().getCanonicalName());
Log.e("xxxxx","书库存书:" + books.toString());
//注册新书新增通知
bookManager.registerNewBookArrivedListener(iOnNewBookArrived);
} catch (RemoteException e) {
e.printStackTrace();
}
try {
service.linkToDeath(deathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
}
/*
实例化一个IOnNewBookArrived接口对象,作为参数返回到远程进程中,一旦远端进程有新的书籍对象新增,就会调用
这个对象的方法,发送message消息,这边接收到就会弹出提示
*/
private IOnNewBookArrived iOnNewBookArrived = new IOnNewBookArrived.Stub() {
@Override
public void OnNewBookArrvied(Book book) throws RemoteException {
//发送消息到由getTarget方法指定的handler
handler.obtainMessage(MESSAGE_NEWBOOK_ARRIVED, book).sendToTarget();
}
};
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.e("xxxxx", "onCreate: 活动创建");
super.onCreate(savedInstanceState);
editText = findViewById(R.id.editText);
setContentView(R.layout.activity_main);
button = findViewById(R.id.button);
button1 = findViewById(R.id.removeservice);
intent = new Intent("AIDLservice");
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
bindService(intent, myserviceconnection, BIND_AUTO_CREATE);
}
});
服务端进程,模拟每5秒增加一本书,来提醒所有开启提醒服务的客户进程
public class AIDLService extends Service {
private String TAG = "xxxxx";
private CopyOnWriteArrayList<Book> books = new CopyOnWriteArrayList<Book>();
private CopyOnWriteArrayList<IOnNewBookArrived> listeners = new CopyOnWriteArrayList<IOnNewBookArrived>();
private Messenger messenger = new Messenger(new messageHanler());
private final BookManager.Stub bookManager = new BookManager.Stub() {
@Override
public void addBooks(Book book) throws RemoteException {
books.add(book);
}
@Override
public List<Book> getBook() throws RemoteException {
return books;
}
@Override
public void registerNewBookArrivedListener(IOnNewBookArrived listener) throws RemoteException {
if(listeners.contains(listener)){
Log.e(TAG, "已经注册过提醒服务了");
}else{
listeners.add(listener);
}
}
@Override
public void unreregisterNewBookArrivedListener(IOnNewBookArrived listener) throws RemoteException {
if(listeners.contains(listener)){
listeners.remove(listener);
Log.e(TAG, "注销提醒服务成功!");
}else {
Log.e(TAG, "未找到需要注销的提醒服务");
}
}
};
@Override
public void onCreate() {
try {
Log.e("xxxxx", "服务创建了");
books.add(new Book(1,"第一本书"));
books.add(new Book(2,"第二本书"));
delayAddBook();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
}
super.onCreate();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
//getBinder返回一个messenger和其handler通信的binder
return bookManager;
}
private void delayAddBook() throws InterruptedException, RemoteException {
new Thread(new Runnable() {
@Override
public void run() {
for (int i=books.size(); i<20; i++){
try {
Thread.sleep(5000);
Book book = new Book(i, String.valueOf(i)+"号新书");
books.add(book);
newBookArrived(book);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}).start();
}
//添加新书,发起通知
private void newBookArrived(Book book) throws RemoteException {
if (listeners.size() != 0){
for (int i=0; i<listeners.size(); i++){
listeners.get(i).OnNewBookArrvied(book);
}
}
}
出现新问题:点击返回键,出现异常,找不到该binder连接Unbind failed: could not find connection for android.os.BinderProxy@4a822e38
原因:客户进程传过去的对象iOnNewBookArrived
不是同一个对象
@Override
protected void onDestroy() {
Log.e("xxxxx", "onDestroy:销毁activity");
try {
bookManager.unreregisterNewBookArrivedListener(iOnNewBookArrived);
} catch (RemoteException e) {
e.printStackTrace();
}
super.onDestroy();
}
}
解决:使用RemoteCallbackList,专门负责清理跨进成回调接口对象,虽然跨进程传递对象,会生成两个不同的对象,但是他们是使用同一个Binder进行通信的,所以根据这个特点,注册的时候使用map
的类型保存binder对象,和listener这个回调方法对象
,注销的时候,根据binder对象,注销器所有的listener回调方法对象。当然如果存在回调的方法不止一个这种情况,他们将是不同的两个RemoteCallbackList对象
替换的代码有,这些接口对象都需要替换:
private CopyOnWriteArrayList<IOnNewBookArrived> listeners = new CopyOnWriteArrayList<IOnNewBookArrived>();
替换成:
private RemoteCallbackList<IOnNewBookArrived> listeners = new RemoteCallbackList<IOnNewBookArrived>();
都已经封装成方法,直接调用
@Override
public void registerNewBookArrivedListener(IOnNewBookArrived listener) throws RemoteException {
if(listeners.contains(listener)){
Log.e(TAG, "已经注册过提醒服务了");
}else{
listeners.add(listener);
}
}
@Override
public void unreregisterNewBookArrivedListener(IOnNewBookArrived listener) throws RemoteException {
if(listeners.contains(listener)){
listeners.remove(listener);
Log.e(TAG, "注销提醒服务成功!");
}else {
Log.e(TAG, "未找到需要注销的提醒服务");
}
}
};
替换成:
@Override
public void registerNewBookArrivedListener(IOnNewBookArrived listener) throws RemoteException {
listeners.register(listener);
}
@Override
public void unreregisterNewBookArrivedListener(IOnNewBookArrived listener) throws RemoteException {
listeners.unregister(listener);
}
//添加新书,发起通知
private void newBookArrived(Book book) throws RemoteException {
if (listeners.size() != 0){
for (int i=0; i<listeners.size(); i++){
listeners.get(i).OnNewBookArrvied(book);
}
}
}
替换成:
//添加新书,发起通知
private void newBookArrived(Book book) throws RemoteException {
int listenerNumber = listeners.beginBroadcast();
for(int i=0; i<listenerNumber; i++){
listeners.getBroadcastItem(i).OnNewBookArrvied(book);
}
}
RemoteCallbackList工作原理
根据android艺术编程
上的讲解,使用binder来查询回调方法对象,这里有一个问题,同一个binder对应多个回调,即同一个binder多个RemoteCallbackList
对象,应该还有别的参数来帮助找到同一个binder下的不同对象
ContentProvider
注意几个点:
1.如果是在同一个应用
上开启另一个进程的情况,是不用考虑这些属性的设置的,都可以访问
2.外部应用
调用contentprovider,需要android:exported="true"
、不要设权限
,只要满足这两点,就可以跨进程调用contentprovider(经过测试:android5.0夜神模拟器)
现在写代码:一个接入SQLlit数据库的ContentProvider组件
1.首先要用到一个SQLiteOpenHelper
类,官网介绍说这是一个帮助类,帮助数据库的创建和版本更新
,所以说这个类是给应用自己用的,不是给用户提供访问接口的,毕竟用户需要的只需要增删该查接口,不是这些复杂的功能。
public class mSqlLiteHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "book_provider.db";
private static final int DATABASE_VERSION = 1;
private static final String table_name_book = "book";
private static final String table_name_user = "user";
private String create_table_book = "CREATE TABLE IF NOT EXISTS "
+table_name_book+ "_id INTEGER PRIMARY KEY, book_name TEXT)";
private String create_table_user = "CREATE TABLE IF NOT EXISTS "
+table_name_user+ "_id INTEGER PRIMARY KEY, username TEXT, sex INT";
//实例化数据库对象的时候,传入名称、版本号
public mSqlLiteHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) {
super(context, DATABASE_NAME, factory, DATABASE_VERSION);
}
//数据库第一次创建时调用,创建表格等初始化操作
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(create_table_book);
db.execSQL(create_table_user);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
2.使用这个类创建好数据库之后,就可以使用ContentProvider
将需要对外提供的数据库,开放其接口,但是ContentProvider需要访问Uri地址,所以需要给数据库生成一个Uri地址
注意几个小问题:
- context对象,一般从oncreate中使用
getContext
方法获取,组件创建的时候,生成相应的上下文环境(可以看作组件对象) - SQL语句书写是注意单词之间留空格
mContentProvider.java
:使用mSqlLiteHelper类,生成、访问、更改数据库,作为对外开放的接口
public class mContentProvider extends ContentProvider {
private static final String HOST = "com.app.typicallifecycle.provider";
private static final String TAG = "xxxxx";
private static final int uri_book_code = 0;
private static final int uri_user_code = 1;
private static Context mcontext;
private static SQLiteDatabase sqLiteDatabase = null;
String table_name = null;
private static mContentObserver contentObserver = new mContentObserver(handler);;
//生成两个Uri
private static UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
uriMatcher.addURI(HOST, "book", uri_book_code);
uriMatcher.addURI(HOST, "user", uri_user_code);
}
private static final Uri uri_book = Uri.parse("content://"+HOST+ "/book");
private static final Uri uri_user = Uri.parse("content://"+HOST+ "/user");
/**
* 根据传入的Uri对象,获取uri code,然后根据这个code,来匹配相应的数据库表
*
* @ param uriobject 传入的uri对象
* @ return table name 数据库表的名称
*/
private String getTableName(Uri uri){
String table_name = null;
switch (uriMatcher.match(uri)){
case uri_book_code:
table_name = mSqlLiteHelper.table_name_book;
break;
case uri_user_code:
table_name = mSqlLiteHelper.table_name_user;
break;
default:break;
}
return table_name;
}
/**
* 数据库的初始化操作.
* 创建数据库、表。向表内插入数据
*
* @return database object 一个数据库对象
*/
private void init_database(){
sqLiteDatabase = new mSqlLiteHelper(mcontext).getWritableDatabase();
sqLiteDatabase.execSQL("delete from " + mSqlLiteHelper.table_name_book);
sqLiteDatabase.execSQL("delete from " + mSqlLiteHelper.table_name_user);
sqLiteDatabase.execSQL("insert into " + mSqlLiteHelper.table_name_book + " values(1, 'Android');");
sqLiteDatabase.execSQL("insert into " + mSqlLiteHelper.table_name_book + " values(2, 'IOS');");
sqLiteDatabase.execSQL("insert into " + mSqlLiteHelper.table_name_user + " values(1, '安卓', 1);");
sqLiteDatabase.execSQL("insert into " + mSqlLiteHelper.table_name_user + " values(2, '乔布斯', 1);");
}
@Override
public boolean onCreate() {
mcontext = getContext();
init_database();
return true;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection,
@Nullable String[] selectionArgs, @Nullable String sortOrder) {
Log.e(TAG, "Current Thread:"+Thread.currentThread().getName());
table_name = getTableName(uri);
if(table_name!=null)
return sqLiteDatabase.query(table_name, projection, selection, selectionArgs, null,
null, sortOrder, null);
else throw new IllegalArgumentException("Illegal Uri: "+uri);
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
table_name = getTableName(uri);
if (uri==null){
throw new Resources.NotFoundException("Not found this URI: "+uri);
}
Log.e(TAG, table_name);
sqLiteDatabase.insert(table_name, null, values);
Log.e(TAG, "OldURI:" + uri);
getContext().getContentResolver().notifyChange(uri, null);
Log.e(TAG, "New Uri: "+uri);
return uri;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
table_name = getTableName(uri);
if (uri==null){
throw new Resources.NotFoundException("Not found this URI: "+uri);
}
int deleted_number = sqLiteDatabase.delete(table_name, selection, selectionArgs);
Log.e(TAG, "删除了:" + String.valueOf(deleted_number) + " 行数据");
//通知注册了ContentObserver的对象,此URI发生了更改
getContext().getContentResolver().notifyChange(uri, null);
return deleted_number;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
table_name = getTableName(uri);
if (uri==null){
throw new Resources.NotFoundException("Not found this URI: "+uri);
}
int updata_number = sqLiteDatabase.update(table_name, values, selection, selectionArgs);
Log.e(TAG, "修改了" + String.valueOf(updata_number)+ "行数据");
return updata_number;
}
}
mContentObserver.java
package com.app.typicallifecycle;
import android.database.ContentObserver;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
public class mContentObserver extends ContentObserver {
private static final String TAG = "xxxxx";
private static int URI_ONCHANGED = 0;
private static Handler mhandler;
/**
* Creates a content observer.
*
* @param handler The handler to run {@link #onChange} on, or null if none.
*/
public mContentObserver(Handler handler) {
super(handler);
mhandler = handler;
}
@Override
public void onChange(boolean selfChange) {
Log.e(TAG, "uri 发生了更改");
mhandler.obtainMessage(URI_ONCHANGED).sendToTarget();
super.onChange(selfChange);
}
}
MainActivity.java
:跨进成访问ContentProvider
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.e("xxxxx", "onCreate: 活动创建");
super.onCreate(savedInstanceState);
editText = findViewById(R.id.editText);
setContentView(R.layout.activity_main);
button = findViewById(R.id.button);
button1 = findViewById(R.id.removeservice);
intent = new Intent("AIDLservice");
new String();
RemoteCallbackList<IUnregisterNotification> remoteCallbackList = new RemoteCallbackList<IUnregisterNotification>();
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Uri uri = Uri.parse("content://com.app.typicallifecycle.provider/book");
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
user u1 = new user();
while(cursor.moveToNext()){
u1.id = cursor.getInt(0);
u1.book_name = cursor.getString(1);
Log.e(TAG, "查询编号为:"+String.valueOf(u1.getId()) + ",书籍名称:"+u1.getBook_name());
}
registerObserver(uri);
ContentValues contentValues = new ContentValues();
contentValues.put("_id", 3);
contentValues.put("book_name", "第一行代码");
getContentResolver().insert(uri, contentValues);
getContentResolver().delete(uri, "_id=?", new String[]{"3"});
ContentValues newcontentValues = new ContentValues();
newcontentValues.put("_id", 1);
newcontentValues.put("book_name", "第二行代码");
getContentResolver().update(uri, newcontentValues, "_id=1", null);
}
});
private void registerObserver(Uri uri){
contentObserver = new mContentObserver(new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 0:
Log.e(TAG, "ContentObserver: 接受到了来自跨进成的消息");
break;
default:break;
}
}
});
getContentResolver().registerContentObserver(uri, false, contentObserver);
}