转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/111553746
本文出自【赵彦军的博客】
文章目录
1、Android进程
android {
compileSdkVersion 29
buildToolsVersion "29.0.3"
defaultConfig {
applicationId "com.zhaoyanjun"
minSdkVersion 16
targetSdkVersion 29
versionCode 1
versionName "1.0"
}
}
applicationId 为:com.zhaoyanjun
,那项目运行起来,所有的 activity
、service
默认运行在 com.zhaoyanjun
进程.
工具类:
package com.zhaoyanjun;
import java.io.FileInputStream;
import java.io.IOException;
/**
* @author yanjun.zhao
* @time 2020/12/16 11:01 AM
* @desc
*/
public class Process {
/**
* 获取当前进程名
* @return
*/
public static String getCurrentProcessName() {
FileInputStream in = null;
try {
String fn = "/proc/self/cmdline";
in = new FileInputStream(fn);
byte[] buffer = new byte[256];
int len = 0;
int b;
while ((b = in.read()) > 0 && len < buffer.length) {
buffer[len++] = (byte) b;
}
if (len > 0) {
String s = new String(buffer, 0, len, "UTF-8");
return s;
}
} catch (Throwable e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
}
2、修改Android默认进程
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.zhaoyanjun">
<application
android:process=":appProcess"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.RecyclerViewDemo">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
在 application 标签下添加 android:process
属性可以修改Android默认的进程名字。进程的名字为:com.zhaoyanjun:appProcess
, 这个修改是全局的,所有 activity 都默认运行在这个进程中。
3、指定activity、Service进程
activity 指定进程
<activity
android:process=":remote"
android:name=".MainActivity2">
</activity>
service 指定进程
<service
android:process=":remote_service"
android:name=".LibraryService">
</service>
4、activity进程间通信
MainActivity
有一个 button
按钮,点击跳转到 MainActivity2
//MainActivity代码
findViewById<Button>(R.id.bt1).setOnClickListener {
var intent = Intent(this, MainActivity2::class.java)
intent.putExtra("key", "value1")
startActivity(intent)
}
MainActivity2 指定进程
<activity
android:process=":remote"
android:name=".MainActivity2">
</activity>
MainActivity2 逻辑
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
var value = intent.getStringExtra("key")
Log.d("zhaoyanjun", "MainActivity2 进程: ${Process.getCurrentProcessName()} 获取值: $value")
}
日志:
D/zhaoyanjun: MainActivity2 进程: com.zhaoyanjun:remote 获取值: value1
5、AIDL 简介
AIDL的语法十分简单,与Java语言基本保持一致,需要记住的规则有以下几点:
1、AIDL文件以 .aidl
为后缀名
2、 AIDL支持的数据类型分为如下几种:
- 八种基本数据类型:
byte
、char
、short
、int
、long
、float
、double
、boolean
String
,CharSequence
- 实现了
Parcelable
接口的数据类型 - List 类型。
List
承载的数据必须是AIDL
支持的类型,或者是其它声明的AIDL
对象 - Map类型。
Map
承载的数据必须是AIDL
支持的类型,或者是其它声明的AIDL
对象
3、AIDL
文件可以分为两类。一类用来声明实现了Parcelable
接口的数据类型,以供其他AIDL
文件使用那些非默认支持的数据类型。还有一类是用来定义接口方法,声明要暴露哪些接口给客户端调用,定向Tag就是用来标注这些方法的参数值。
3、定向Tag。定向Tag表示在跨进程通信中数据的流向,用于标注方法的参数值,分为 in、out、inout 三种。
- 其中 in 表示数据只能由客户端流向服务端,
- out 表示数据只能由服务端流向客户端,而 inout 则表示数据可在服务端与客户端之间双向流通。此外,如果AIDL方法接口的参数值类型是:基本数据类型、String、CharSequence或者其他AIDL文件定义的方法接口,那么这些参数值的定向 Tag 默认是且只能是 in,所以除了这些类型外,其他参数值都需要明确标注使用哪种定向Tag。定向Tag具体的使用差别后边会有介绍
4、明确导包。在AIDL文件中需要明确标明引用到的数据类型所在的包名,即使两个文件处在同个包名下
6、activity、service 进程间通信 AIDL
创建 IBookAidlInterface.aidl
文件
创建 IBookAidlInterface
完成后,会在 main 目录下,创建 aidl 文件夹,并且会创建同名的包名,如下:
我们把默认的 basicTypes 方法删除,修改如下
interface IBookAidlInterface {
String getTitle();
void setTitle(String title);
}
下面创建 Service , 并指定进程名字:
<service
android:name=".MyService"
android:enabled="true"
android:exported="true"
android:process=":remote" />
MyService 如下:
class MyService : Service() {
private var binder: Binder = object : IBookAidlInterface.Stub() {
override fun getTitle(): String {
return "aidl->title"
}
override fun setTitle(title: String?) {
Log.d("aidl", "service-setTitle:$title")
}
}
override fun onBind(intent: Intent): IBinder {
return binder
}
override fun onCreate() {
super.onCreate()
}
}
MainActivity 绑定Service
class MainActivity : AppCompatActivity() {
private var iBookAidlInterface: IBookAidlInterface? = null
private var serviceConnection: ServiceConnection = object : ServiceConnection {
override fun onServiceDisconnected(name: ComponentName?) {
}
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
iBookAidlInterface = IBookAidlInterface.Stub.asInterface(service)
var name = iBookAidlInterface?.title
Log.d("aidl", "getTitle: $name")
iBookAidlInterface?.title = "我是一个title"
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
bindService()
}
/**
* 绑定服务
*/
private fun bindService() {
var intent = Intent(this, MyService::class.java)
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
}
override fun onDestroy() {
super.onDestroy()
unbindService(serviceConnection)
}
}
运行项目,日志如下:
可以看到,子进程 Service 和 主进程的 Activity 已经可以通信了。
github 地址:https://github.com/zyj1609wz/AndroidMultiProgress
7、AIDL 传递复杂对象
首先创建 Book 类,并且实现 Parcelable
接口。这里要注意,一定要实现 Parcelable
接口,因为只有实现 Parcelable
的复杂对象才能在 AIDL 中传递。
public class Book implements Parcelable {
public String title;
Book() {
}
protected Book(Parcel in) {
title = in.readString();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(title);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<Book> CREATOR = new Creator<Book>() {
@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
}
同时在 aidl 目录中定义 Book.aidl
文件,并且声明 parcelable
属性。Book.aidl
文件如下:
package com.yanjun.progress;
parcelable Book;
下面我们修改 IBookAidlInterface.aidl
的方法,如下:
interface IBookAidlInterface {
String getTitle();
void setTitle(String title);
Book getBook();
void setBook(in Book book);
}
需要注意的是 setBook
方法,一定要声明 Book
对象是 in
属性,表明是 book 对象是从客户端流向服务端。
最后在 Service 里面实现接口就行了,如下:
class MyService : Service() {
private var binder: Binder = object : IBookAidlInterface.Stub() {
override fun getTitle(): String {
return "aidl->title"
}
override fun setTitle(title: String?) {
Log.d("aidl", "service-setTitle:$title")
}
override fun getBook(): Book {
var book = Book()
book.title = "书的名字"
return book
}
override fun setBook(book: Book?) {
}
}
override fun onBind(intent: Intent): IBinder ?{
return binder
}
override fun onCreate() {
super.onCreate()
}
}
demo github 地址:https://github.com/zyj1609wz/AndroidMultiProgress
8、SharedPreferences 跨进程
SharedPreferences
要实现跨进程通信,要遵循文件名字相同的规则。而且是同一个应用,两个进程间。
SharedPreferences
之所以能实现跨进程,是因为SharedPreferences
数据存在SD卡的磁盘中,两个进程共用一个文件。
注意事项:
- 虽然
SharedPreferences
可以实现跨进程,但是Google
官方不建议使用,因为Google
认为多个进程读同一个文件都是不安全的,Android
不保证该模式总是能正确的工作,建议使用ContentProvider
替代多进程之间文件的共享。
额外话题 SharedPreferences 优化:
commit
是把内容同步提交到硬盘的,返回boolean
表明修改是否提交成功apply
先立即把修改提交到内存,然后开启一个异步的线程提交到硬盘,并且如果提交失败,你不会收到任何通知,apply没有返回值 。- 使用了
apply
方式异步写sp
的时候,每次apply()
调用都会新增一个finisher
。在有些系统生命周期事件发生的时候都会去检查已经提交的apply
写操作是否完成,如果没有完成则阻塞主线程,造成ANR
- 当一个文件较大时,首次读取可能会较慢,每次写入也会较慢
- 在不同模块使用多个文件存储的情况下,那么未被使用到的模块,不会被读取进内存
- 当存储的
value
为json
或者html
形式时,由于特殊符号较多,会占用更多的字符
如何解决 ANR :
- 如果处理
SP
的时候,使用commit
方法替代apply
,在保证调用时线程正确处理,并保证同一文件不使用多个线程写入的情况下,不会出现该ANR
。 - 自查是否有一下子修改多个
key-value
且apply
多次的情况,如有,可合并为put
多次但apply
一次
9、广播跨进程
Context.sendBroadcast()
发送的是普通广播,所有订阅者都有机会获得并进行处理
10、ContentProvider跨进程
什么是ContentProvider ?
即 内容提供者,是 Android
四大组件之一
ContentProvider什么作用?
进程间 进行数据交互 & 共享,即跨进程通信
ContentProvider原理
ContentProvider
的底层原理 = Android
中的Binder
机制
ContentProvider优秀文章链接
本来是想总结一下的,但是我发现了一篇写的很好的文章:
建议把文章和评论都看看,很有收获。
Android:关于ContentProvider的知识都在这里了!
Android中使用Contentprovider导致进程被杀死
11、Socket跨进程通信
优点:
- 功能强大,可通过网络传输字节流,支持一对多实时并发通信
缺点:
- 实现细节步骤稍繁琐,不支持直接的RPC
适用场景:
- 网络间的数据交换
12、文件共享跨进程
优点
- 简单易用
缺点:
- 不适用高并发场景,并且无法做到进程间即时通信
适用场景:
- 适用于无关发的情况下,交换简单的数据,对实时性要求不高的场景。
13、Bundle跨进程
优点
- 简单易用
缺点:
- 只能传输Bundle支持的数据类型
适用场景:
- 四大组件间的进程间通信
14、MMKV
MMKV
是国内微信团队开源的高性能 key-value
组件,良好的支持Android
跨进程通信,具体使用方法就自己看文档吧,这里不赘述了。
github: https://github.com/Tencent/MMKV/
中文文档:MMKV中文文档