Android点将台:你敢摸我猫 [- IPC -]

栏目: IOS · Android · 发布时间: 5年前

内容简介:2.再从感性上类比一下三个按钮跳转三个Activity,布局就不贴了。只贴一个MainActivity0,其他两个MainActivity1,MainActivity22.
Android点将台:你敢摸我猫 [- IPC -]

2.再从感性上类比一下 进程和线程

如果手机是地球,进程就像一家公司,公司使用着地球的资源,也在地球上扮演一个独立的个体,实现自己特有的功能与价值。
而线程就像公司里的人,可以共享公司的公共资源,处理属于自己的任务,实现自身的功能与价值。
可以说进程(公司)是给线程(人)一个运行(工作)的环境。于此同时进程也获得了它的地位。

所以一个进程至少要一个线程来完成任务。线程销毁后,里面的进程也就失业拜拜了。
比如某公司的人(线程)集体罢工(崩溃),那公司无论曾经叫什么,都没有意义。公司(进程)倒闭了,再多的线程(人)也没卵用。

多进程就像若干个公司联盟做一个项目,这时候各个公司的内部资源(静态成员、单例等)就不再适用,
就像别的公司人到你公司吃你的零食,敲你键盘,摸你猫,你给吗? 不给,坚决不给。
然后那人非要吃你零食,敲你键盘,摸你猫,还搞出个职位叫IPC,说什么跨进程间通信。TM说白了就是专门抢你零食,抢你猫,你说气不气人。
复制代码

3.最后走一波概念

IPC(Inter-Process Communication): 进程间通信或者跨进程通信
进程:指的一个执行单元,在PC和移动设备上指的是一个程序或者一个应用。
线程:在操作系统中,线程是CPU调度的最小单元,也是一种有限的系统资源。
进程与线程关系:一个进程可以包含多个线程,因此进程和线程是包含被包含的关系。
复制代码

二、如何在一个应用创建多个进程

1.三个测试Activity

三个按钮跳转三个Activity,布局就不贴了。只贴一个MainActivity0,其他两个MainActivity1,MainActivity2

Android点将台:你敢摸我猫 [- IPC -]
class MainActivity0 : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        title="MainActivity0"
        to_one.setOnClickListener {
            startActivity(Intent(this, MainActivity0::class.java))
        }
        to_two.setOnClickListener {
            startActivity(Intent(this, MainActivity1::class.java))
        }
        to_three.setOnClickListener {
            startActivity(Intent(this, MainActivity2::class.java))
        }
    }
}
复制代码

2. AndroidManifest.xml 配置文件

私有进程: 有: ---------- 全局进程: 没有: 名字可以随便取,只要唯一

<activity android:name=".MainActivity1"
          android:process=":ipc">
</activity>
<activity android:name=".MainActivity2"
          android:process="com.toly1994.ipc.test">
</activity>
复制代码

三、多进程与单进程的区别

1.打开Activity1时

不加的话,直接通过窗口管理器来显示Activity1

Android点将台:你敢摸我猫 [- IPC -]

加的话,会在孵化一个进程。zygote64的日志很多,下面只是一小部分。

不清楚Activity启动和View加载过程的小伙伴,可以看一下这个日志,也许会有帮助

比如下面完美呈现了LayoutInflater是怎么运行的,再跟着源码走一走,你会有所收获

Android点将台:你敢摸我猫 [- IPC -]

然后发现确实是多了两个,名字也能对应上

Android点将台:你敢摸我猫 [- IPC -]

2.Application的多次实例化

既然开一个进程会孵化一次,ActivityThread的main方法被触发,Application自然会被新建

喵了个咪的,创建了三个,一个进程一个。这显然值得注意,自定义Application初始化第三方库什么的

Android点将台:你敢摸我猫 [- IPC -]
public class CatApplication extends Application {
    private static final String TAG = "CatApplication";

    @Override
    public void onCreate() {
        super.onCreate();
        Log.e(TAG, "onCreate: 创建了小猫土土");
    }
}
复制代码

3.静态成员变量无法在不同进程获取

public class CatManager {
    public static Cat cat = new Cat();

    public CatManager() {
        cat = new Cat();
        cat.color = "灰色" + Math.random();
        cat.name = "土土";
    }
}

---->[MainActivity0#oncreate]------------------
CatManager()

---->[MainActivity1#oncreate]------------------
Log.e("CatManager", ": "+CatManager.cat.color);//null 

|--- 说明在MainActivity1里已经初始化的静态成员变量无法在MainActivity2(另一个进程)使用
|--- 如果将MainActivity2的process去掉可以打印:灰色0.22701789806635642
|--- 这就尴尬了,我的唯一玩到666的单例肿么办?
复制代码

4.单例模式会怎么样?

新建一个Cat(猫)和CatManager(铲屎官)的类

---->[Cat]------------------------------------
public class Cat {
    public String name;
    public String color;

    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' +
                ", color='" + color + '\'' +
                '}';
    }
}

---->[CatManager]------------------------------------
public class CatManager {
    private volatile static CatManager sCatManager;
    private static Cat cat=new Cat();

    private CatManager() {
    }

    public static CatManager newInstance() {
        if (sCatManager == null) {
            synchronized (CatManager.class) {
                if (sCatManager == null) {
                    sCatManager = new CatManager();
                    Log.e("CatApplication", "newInstance: ");

                    cat.color = "灰色"+Math.random();
                    cat.name = "土土";
                }
            }
        }
        return sCatManager;
    }

    public Cat getCat() {
        return cat;
    }
}

---->[CatApplication]------------------------------------
public class CatApplication extends Application {
    private static final String TAG = "CatApplication";

    @Override
    public void onCreate() {
        super.onCreate();
        CatManager manager = CatManager.newInstance();
        Log.e("CatApplication", manager.getCat().toString());
    }
}
复制代码
2019-05-08 10:18:02.482 25524-25524/? E/CatApplication: newInstance: 
2019-05-08 10:18:02.482 25524-25524/? E/CatApplication: Cat{name='土土', color='灰色0.8695394451026908'}
2019-05-08 10:18:04.761 25561-25561/com.toly1994.ipc:ipc E/CatApplication: newInstance: 
2019-05-08 10:18:04.761 25561-25561/com.toly1994.ipc:ipc E/CatApplication: Cat{name='土土', color='灰色0.9824119267379914'}
2019-05-08 10:18:07.096 25597-25597/com.toly1994.ipc.test E/CatApplication: newInstance: 
2019-05-08 10:18:07.096 25597-25597/com.toly1994.ipc.test E/CatApplication: Cat{name='土土', color='灰色0.18620946012650275'}
复制代码

可见单例也没有卵用了,每次开启进程都会执行到newInstance,导致单例的失调。

5.小结:多进程带来的问题(老生常谈)

Application会多次创建:开启一个进程其实就等同于开多一个Application
静态成员和单例模式完全失效(处于不同的内存块(进程),拥有各自的副本) 
SharedPreferences的可靠性降低:因为SharedPreferences不支持两个进程同时去读写xml文件 
线程同步机制完全失效(同一差不多) 
复制代码

三、IPC的几种形式

为了多公司联盟(多进程)间的和谐,现在决定牺牲猫,让它可以被过各公司(进程)共享

[1].通过Intent传递Bundle对象通信: 简单,数据类型局限,用于组件间传递数据
[2].使用共享文件通信: 简单,实时性差,不适合高并发
[3].使用Messenger通信: 支持一对多串行通信,支持实时通信,不支持RPC
[4].使用AIDL通信: 支持一对多并发通信,适用于,一对多通信且有RPC需求
[5].使用ContentProvider: 支持一对多并发数据共享
[6].使用Socket: 可以通过网络传输字节流,支持一对多并发实时通信
复制代码

0.现在的CatManager类和Cat类

既然单例不能用,就不用。这里默认开局一只猫。Cat实现序列化接口Serializable

public class CatManager {
    private static List<Cat> cats = new ArrayList<>();
    public CatManager() {
        Cat tutu = new Cat();
        tutu.color = "灰色" + Math.random();
        tutu.name = "土土";
        add(tutu);
    }
    public void add(Cat cat) {
        cats.add(cat);
    }
    public Cat getCatAt(int index) {
        return cats.get(index);
    }
}

public class Cat implements Serializable {
    public String name;
    public String color;

    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' +
                ", color='" + color + '\'' +
                '}';
    }
}
复制代码

1.IPC之 Intent发送Bundle对象通信

1-1.Serializable序列化对象实现

---->[MainActivity0#oncreate]------------------
to_two.setOnClickListener {
    val cat = CatManager().getCatAt(0)
    val bundle = Bundle()//创建Bundle对象
    bundle.putSerializable("cat", cat)//把猫装到Bundle里,贴个标签cat
    val intent = Intent(this, MainActivity1::class.java)
    intent.putExtras(bundle)
    startActivity(intent)
}

---->[MainActivity1#oncreate]------------------
val cat = intent.extras?.get("cat") as Cat //把Bundle用打开标签cat,然后猫到手
Log.e("MainActivity1", ": " + cat.name)//MainActivity1可以对猫为所欲为,IPC 通信完成
复制代码

注:当然你也可以直接通过Intent发送序列化(Serializable)对象,源码瞄一眼,都是通过Bundle的,并无本质区别

public @NonNull Intent putExtra(String name, Serializable value) {
    if (mExtras == null) {
        mExtras = new Bundle();
    }
    mExtras.putSerializable(name, value);
    return this;
}
复制代码

1-2.Parcelable序列化对象实现

Android里说Serializable,怎么能少得了同胞兄弟 Parcelable 呢,两者都是对象序列化的手段

两者的详细比较这里就不赘述,详见: Android点将台的Intent篇 。什么是序列化和反序列化,个人理解如下:

比如我家有个大的衣柜(对象),现在要搬家,一下子搬不走,怎么办?
把每块板贴个标签,然后拆了,一块块摆好,然后就能运走了,这叫序列化。
然后到新家里,把板再一块块地拼起来,然后大衣柜(对象)就又回来了,这叫反序列化。

上面说的是物质对象的运输过程,那么信息/数据对象也可以这么类比,思想上是[怎么好运和拼装还原]
Serializable和Parcelable不影响序列化的概念,只是手段不同,就像是卡车运还是飞机运一样
Serializable和Parcelable接口代表这东西可拆,是一种可拆保证。要什么都乱拆,你家猫拆个试试。
下面直播拆猫:AS自动生成Parcelable相关代码,可以省我们一些事,but,请千万要了解一下他们是干嘛用的
复制代码
Android点将台:你敢摸我猫 [- IPC -]
---->[MainActivity0#oncreate]------------------
to_two.setOnClickListener {
    val cat = CatManager().getCatAt(0)
    val bundle = Bundle()//创建Bundle对象
    bundle.putParcelable("cat", cat)//把猫装到Bundle里,贴个标签cat
    val intent = Intent(this, MainActivity1::class.java)
    intent.putExtras(bundle)
    startActivity(intent)
}

---->[MainActivity1#oncreate]------------------
val cat = intent.extras?.get("cat") as Cat //把Bundle用打开标签cat,然后猫到手
Log.e("MainActivity1", ": " + cat.name)//MainActivity1可以对猫为所欲为,IPC 通信完成
复制代码

2.IPC之 文件共享进行通信

把对象写入文件,然后通过文件反序列化出对象,给MainActivity2

(文件读写无论是效率还是多线程的不行,所以这里只是了解一下)

---->[MainActivity0#oncreate]------------------
to_three.setOnClickListener {
    val cat = CatManager().getCatAt(0)
    val file = File(cacheDir, "cat.obj")
    val oos = ObjectOutputStream(FileOutputStream(file))
    oos.writeObject(cat)
    oos.close()
    startActivity(Intent(this, MainActivity2::class.java))
}

---->[MainActivity2#oncreate]------------------
val file = File(cacheDir, "cat.obj")
val ois = ObjectInputStream(FileInputStream(file))
val cat = ois.readObject() as Cat//反序列化生成对象
ois.close()
Log.e("MainActivity1", ": " + cat.name)//MainActivity1可以对猫为所欲为,IPC 通信完成
复制代码

可能到这你觉得IPC不就是传个对象吗?好像没什么大不了的

3.IPC之 Messenger通信

3-1:Messenger是什么?

从都构造函数来看,是和Binder有关

private final IMessenger mTarget;

public Messenger(Handler target) {
    mTarget = target.getIMessenger();
}

public Messenger(IBinder target) {
    mTarget = IMessenger.Stub.asInterface(target);
}
复制代码

3-2:可以获取一个Ibinder对象

如此看来mTarget即IMessenger类很像一个AIDL接口

public IBinder getBinder() {
    return mTarget.asBinder();
}
复制代码

3-3:Messenger的使用

既然是Ibinder对象,可以用在绑定服务中。既然个公司(摸)的人都要猫,干脆来个服务端。

谁(客户端)想来摸一下都可以,核心是Messenger发送消息,Service里接收消息

Android点将台:你敢摸我猫 [- IPC -]
Android点将台:你敢摸我猫 [- IPC -]
---->[CatService]------------------
public class CatService extends Service {
    @Override
    public IBinder onBind(Intent intent) {
        return new Messenger(new Handler(msg -> {
            //接收客户端数据/信息/对象
            String data = msg.getData().getString("request");
            Log.e("MessengerActivity", "handleMessage: " + data);
            
            //向客户端发送数据/信息/对象
            Messenger client = msg.replyTo;
            Message message = Message.obtain();
            Cat cat = new CatManager().getCatAt(0);
            Bundle bundle = new Bundle();//创建Bundle对象
            bundle.putParcelable("cat", cat);//把猫装到Bundle里,贴个标签cat
            message.setData(bundle);
            try {
                client.send(message);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            return false;
        })).getBinder();
    }
}

---->[将服务单独放在一个进程]--------------
<service android:name=".CatService"
         android:process="com.toly1994.ipc.service.cat"/>

---->[MessengerActivity]------------------
public class MessengerActivity extends AppCompatActivity {
    private static final String TAG = "MessengerActivity";
    private ServiceConnection conn;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        conn = new ServiceConnection() {
            private Messenger messenger;
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                messenger = new Messenger(service);
                Message message = Message.obtain();
                Bundle bundle = new Bundle();
                bundle.putString("request", "来自客户端:给我一只猫");
                message.setData(bundle);
                message.replyTo = new Messenger(new Handler((msg) -> {//服务端回应监听
                    Bundle data = msg.getData();
                    data.setClassLoader(getClass().getClassLoader());
                    Cat cat = (Cat) (data.get("cat"));
                    Log.e(TAG, "来自服务端: "+cat);
                    return false;
                }));
                try {
                    messenger.send(message);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
            @Override
            public void onServiceDisconnected(ComponentName name) {
            }
        };
        bindService(new Intent(this, CatService.class), conn, Context.BIND_AUTO_CREATE);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(conn);
    }
}
复制代码

注意这里有一个坑,一开始读不出来异常如下,看名字感觉是类加载器的锅,貌似找不到Cat类

解决: data.setClassLoader(getClass().getClassLoader());

Android点将台:你敢摸我猫 [- IPC -]

流程基本如下,并不知道两个Handler和三个Messenger,还有皮球一样乱跑的Message有没有把你绕晕

Android点将台:你敢摸我猫 [- IPC -]

4.IPC之 AIDL通信

这个不怎么想说,在 Android点将台:金科玉律[-AIDL-] 里已经讲得很详细了,为了完整一点,这里稍微再说一下吧。

4-1:定义接口: ICatService

简单一点,就定义一个喂养的方法

Android点将台:你敢摸我猫 [- IPC -]

4-2:自动生成的类

类之间的关系基本如下:

Android点将台:你敢摸我猫 [- IPC -]
package com.toly1994.ipc;
public interface ICatService extends android.os.IInterface {
    public static abstract class Stub extends android.os.Binder implements com.toly1994.ipc.ICatService {
        private static final java.lang.String DESCRIPTOR = "com.toly1994.ipc.ICatService";
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }
        //通过IBinder获取ICatService对象,绑定客户端时使用
        public static com.toly1994.ipc.ICatService asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.toly1994.ipc.ICatService))) {
                return ((com.toly1994.ipc.ICatService) iin);
            }
            return new com.toly1994.ipc.ICatService.Stub.Proxy(obj);
        }

        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        @Override//此方法运行在服务端Binder线程池中,客户端发起跨进程请求时,远程请求通过系统底层封装后交由此方法处理
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            java.lang.String descriptor = DESCRIPTOR;
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(descriptor);
                    return true;
                }
                case TRANSACTION_feed: {
                    data.enforceInterface(descriptor);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    this.feed(_arg0);
                    reply.writeNoException();
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }
        //代理类,当客户端访问服务端时,客户端通过代理类生成一个ICatService对象 
        private static class Proxy implements com.toly1994.ipc.ICatService {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            @Override
            public void feed(java.lang.String aString) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeString(aString);
                    mRemote.transact(Stub.TRANSACTION_feed, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_feed = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }

    public void feed(java.lang.String aString) throws android.os.RemoteException;
}

复制代码

4-3:使用ICatService创建FeedCatService

优势在于客户端绑定服务是通过: ICatService.Stub.asInterface(service) 获取ICatService对象

就可以调用ICatService接口方法。这样只暴露接口,可以限制客户端对小猫的操作,客户端即玩了,又不能为所欲为。

---->[FeedCatService]--------------------------------------
public class FeedCatService extends Service {
    private static final String TAG = "FeedCatService";
    private Binder binder = new ICatService.Stub() {
        @Override
        public void feed(String aString) throws RemoteException {
            Log.e(TAG, "feed: 你已喂" + aString + "给小猫土土了");
        }
    };

    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }
}

---->[将服务单独放在一个进程]---------------------------------
<service android:name=".FeedCatService"
         android:process="com.toly1994.ipc.service.feed.cat"/>
         
---->[AidlActivity]---------------------------------
public class AidlActivity extends AppCompatActivity {
    private ServiceConnection conn;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        conn = new ServiceConnection() {
            private ICatService catService;
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                catService = ICatService.Stub.asInterface(service);
                try {
                    catService.feed("鱼");
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
            @Override
            public void onServiceDisconnected(ComponentName name) {
            }
        };
        bindService(new Intent(this, FeedCatService.class), conn, Context.BIND_AUTO_CREATE);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(conn);
    }
}
复制代码

5.IPC之 使用ContentProvider通信

5.1:数据库辅助

private static String DATABASE_NAME = "cat.db";//数据库名
    private static int DATABASE_VERSION = 1;//数据库版本
    private volatile static CatDatabaseHelper sInstance;
    //双检锁单例
    public static synchronized CatDatabaseHelper getInstance(Context context) {
        if (sInstance == null) {
            synchronized (CatDatabaseHelper.class) {
                if (sInstance == null) {
                    sInstance = new CatDatabaseHelper(context);
                }
            }
        }
        return sInstance;
    }
    public CatDatabaseHelper(@Nullable Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }
    @Override
    public void onCreate(SQLiteDatabase db) {
        createSwordTable(db);
    }
    /**
     * 创建sword表
     *
     * @param db SQLiteDatabase
     */
    private void createSwordTable(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE cat (" +
                "id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," +
                "name VARCHAR(32) NOT NULL," +
                "color VARCHAR(32) NOT NULL" +
                "); ");
    }
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }
}
复制代码

5.2:继承ContentProvider

public class CatContentProvider extends ContentProvider {
    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    private static final int CAT_QUERY = 0;
    private static final int CAT_INSERT = 1;
    private static final int CAT_UPDATE = 2;
    private static final int CAT_DELETE = 3;
    private static final String TABLE_NAME = "cat";
    static {
        //给当前sUriMatcher添加匹配规则
        sUriMatcher.addURI("toly1994.com.cat", "query", CAT_QUERY);
        sUriMatcher.addURI("toly1994.com.cat", "insert", CAT_INSERT);
        sUriMatcher.addURI("toly1994.com.cat", "update", CAT_UPDATE);
        sUriMatcher.addURI("toly1994.com.cat", "delete", CAT_DELETE);
    }
    private SQLiteOpenHelper mOpenHelper;
    @Override
    public boolean onCreate() {
        mOpenHelper = CatDatabaseHelper.getInstance(getContext());
        return true;
    }
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        //进行uri匹配
        int result = sUriMatcher.match(uri);
        if (result == CAT_QUERY) {
            SQLiteDatabase db = mOpenHelper.getReadableDatabase();
            return db.query(TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder);
        } else {
            throw new IllegalStateException(" query Uri 错误");
        }
    }
    @Override
    public String getType(Uri uri) {
        return null;
    }
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        //进行uri匹配
        int result = sUriMatcher.match(uri);
        if (result == CAT_INSERT) {
            SQLiteDatabase db = mOpenHelper.getWritableDatabase();
            Long insert = db.insert(TABLE_NAME, null, values);
            //uri:数据发送变化,通过uri判断调用哪个内容观察者
            //第二个参数:内容观察者对象  如果传null 则注册了整个uri的内容观察者皆可以收到通知
            getContext().getContentResolver().notifyChange(uri, null);
            db.close();
            return Uri.parse(String.valueOf(insert));
        } else {
            throw new IllegalStateException("insert Uri 错误");
        }
    }
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        //进行uri匹配
        int result = sUriMatcher.match(uri);
        if (result == CAT_DELETE) {
            SQLiteDatabase db = mOpenHelper.getWritableDatabase();
            int delete = db.delete(TABLE_NAME, selection, selectionArgs);
            db.close();
            return delete;
        } else {
            throw new IllegalStateException("delete Uri  错误");
        }
    }
    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        //进行uri匹配
        int result = sUriMatcher.match(uri);
        if (result == CAT_UPDATE) {
            SQLiteDatabase db = mOpenHelper.getWritableDatabase();
            int update = db.update(TABLE_NAME, values, selection, selectionArgs);
            db.close();
            return update;
        } else {
            throw new IllegalStateException("update Uri 错误");
        }
    }
}

---->[配置]---------------------------------------------
<provider android:authorities="toly1994.com.cat"
          android:name=".CatContentProvider"
          android:exported="true"
          android:process="com.toly1994.ipc.provider.cat"
/>
复制代码

5.3:使用

这样不同的进程就可以通过getContentResolver来操作数据库了

public class ProviderActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        insert(getContentResolver());
    }
    /**
     * 插入测试
     *
     * @param resolver
     */
    private void insert(ContentResolver resolver) {
        Uri uri = Uri.parse("content://toly1994.com.cat/insert");
        ContentValues values = new ContentValues();
        values.put("name", "土土");
        values.put("color", "灰色");
        resolver.insert(uri, values);
    }
}
复制代码
Android点将台:你敢摸我猫 [- IPC -]

6.使用Socket

Socket可以让两个设备间的通信,两个进程自然也不在话下

这里不深入,只是客户端发一句话,服务端接收一下

---->[SocketService]---------------------------------
public class SocketService extends Service {
    private static final String TAG = "SocketService";
    private boolean quit = false;
    
    @Override
    public void onCreate() {
        Log.e(TAG, "onCreate: ");
        FeedServer feedServer = new FeedServer();
        new Thread(feedServer).start();
        super.onCreate();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    private class FeedServer implements Runnable {

        @Override
        public void run() {
            ServerSocket serverSocket = null;
            try {
                serverSocket = new ServerSocket(8080);
                Log.e(TAG, "serverSocket onCreate: ");
            } catch (IOException e) {
                e.printStackTrace();
            }

            while (!quit) {
                try {
                    Socket socket = serverSocket.accept();
                    //接收客户端消息
                    BufferedReader br = new BufferedReader(
                            new InputStreamReader(socket.getInputStream()));
                    String s = br.readLine();
                    Log.e(TAG, "来自客户端: " + socket.getInetAddress() + "说:" + s);

                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

---->[将服务单独放在一个进程]---------------------------------
<service android:name=".SocketService"
         android:process="com.toly1994.ipc.service.socket.feed.cat"/>
         
---->[ServerActivity]---------------------------------
public class ServerActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        startService(new Intent(this, SocketService.class));
        findViewById(R.id.to_one).setOnClickListener(v -> {
            new Thread(() -> {
                Socket socket = null;
                try {
                    socket = new Socket("localhost", 8080);
                    //客户端请求
                    BufferedWriter bw = new BufferedWriter(
                            new OutputStreamWriter(socket.getOutputStream()));
                    bw.write("我要猫");
                    bw.flush();
                    bw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();

        });

    }
}
复制代码

OK,本文就先这样, 《Android开发艺术探索》 是本不错的书,有多瞄几眼的价值。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Two Scoops of Django

Two Scoops of Django

Daniel Greenfeld、Audrey M. Roy / CreateSpace Independent Publishing Platform / 2013-4-16 / USD 29.95

Two Scoops of Django: Best Practices For Django 1.5 is chock-full of material that will help you with your Django projects. We'll introduce you to various tips, tricks, patterns, code snippets, and......一起来看看 《Two Scoops of Django》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具