Android内的IPC其他方法

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();
            }
        }

新需求:需要满足服务进程新增书籍后,希望接收相关书籍的客户进程可以接收到通知

  1. 创建一个新书通知功能的接口,这个接口是供服务端进程调用的
    这个接口为什么使用aidl文件来创建:aidl接口只能使用aidl接口,不能使用普通接口
import com.app.typicallifecycle.Book;

interface IOnNewBookArrived {
    void OnNewBookArrvied(in Book book);
}
  1. 添加两个新功能给用户端进程调用,用来选择是否启用通知服务
    为什么这里要将IOnNewBookArrived对象作为参数:客户端进程需要和服务端进程建立联系,通过在OnNewBookArrvied方法内部封装发送message到固定handler的方法
interface BookManager {

    void addBooks(in Book book);
    List<Book> getBook();
    void registerNewBookArrivedListener(IOnNewBookArrived listener);
    void unreregisterNewBookArrivedListener(IOnNewBookArrived listener);
}
  1. 客户端进程内实现这个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地址

注意几个小问题:

  1. context对象,一般从oncreate中使用getContext方法获取,组件创建的时候,生成相应的上下文环境(可以看作组件对象)
  2. 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);
    }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345

推荐阅读更多精彩内容