这篇讨论的仅仅是kotlin的安全性
1.简洁
这个从直观上很容易看到.
简洁就意味着实现相同的功能,kotlin 可以用更少的代码.因为 java 的语法相对来说比较负责,所以说编写更长的代码更容易出现更多的错误
既然是进程间的通信肯定就要有两个进程.
这里为了方便两个进程在同一个 app 中来实现,其本质和两个 app 是一样一样的.
要想在 app 中启用多线程,只有一种方式,那就是为 Android 的四大组件指定 process属性.
process的属性值要怎么写呢?
只有图中的两种方式
1.冒号+单词(字母也可以)
2.包名+点(英文中的句号)+单词(字母也可以)
2.序列化
在 Android 中实现序列化有两种方式
2.1 Serializable (java 中的序列化方式)
看一个典型的例子
可以看到非常简单,只需要实现 Serializable 接口就可以了.
那么怎么序列化和反序列化呢?
也非常简单
Book book = new Book("1", "Android");
try {
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("android.txt"));
out.writeObject(book);
out.close();
ObjectInputStream in = new ObjectInputStream(new FileInputStream("android.txt"));
Book newBook = (Book) in.readObject();
in.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
上面的代码是以本地文件为媒介实现的序列化和反序列化.
但是有一点需要注意
虽然 book 和 newbook 的内容完全一样,但是两者并不是同一个对象了.这里还有一个要提一下,可能有眼尖的童鞋已经发现了.就是 serialVersionUID 是干什么呢.
serialVersionUID 即使不指定也是可以实现序列化的.那还写它干什么呢?
且听我慢慢道来
这篇文章是从 AsyncTask 的角度进入,从而分析线程和线程池的文章.
涉及到一下内容.
通过一个模拟下载文件的例子来看.
String url = "";
//Params,Progress,Result
new AsyncTask<String,Integer,File>(){
//下载任务开始之前调用--主线程
@Override
protected void onPreExecute() {
super.onPreExecute();
}
//实际的下载--分线程
@Override
protected File doInBackground(String... params) {
String fileUrl = params[0];
// download file
publishProgress(50);
return new File("");
}
//任务的进度--主线程
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
}
//最后的结果--主线程
@Override
protected void onPostExecute(File s) {
super.onPostExecute(s);
}
}.execute(url);
其实 AsyncTask 的使用无外乎就四个方法而已.
onPreExecute()
在任务开始之前调用–主线程
doInBackground(String... params)
实际的任务–分线程
onProgressUpdate(Integer... values)
任务的进度–主线程
onPostExecute(File s)
最后的结果–主线程
至于 AsyncTask的三个泛型参数
第一个是传入的参数类型
第二个是进度的类型
第三个是结果得类型
通过一个图看明白这三个参数
一般情况下我们都是直接使用资源 Id 来获取资源的,简单而又方便.
例如:
imageView.setImageResource(R.drawable.ic_launcher_background)
但是有些情况下,这种方式就显的有点复杂了.
例如这么一种情况 :
要根据 type 的值来选择对应的图片.
type : 取值范围是0…9
对应的图片为: iconvip0, iconvip1 …. iconvip9.
这时候如果用资源 id 的方式来取图片的话,代码是这样的:
本文目的:
我们都知道 Handler.并且日常使用也是非常的频繁.
那么随之而来的几个问题
#1.Handler 到底是什么呢?
#2.Handler 是用来干什么的?
#3.Handler 的使用(对此熟悉的可直接跳过)
Binder
从以下几个方面来看 binder
Android
: 中的一个类,实现了 IBinder 接口
IPC
: 是 Android 中跨进程通信的一种方式
Framework
:Binder 是 ServiceManager 连接各种 Manager(ActivityManager WindowManager …)和相应 ManagerService 的桥梁
Android 应用层
:Binder 是客户端和服务端进行通信的媒介,当bindService 的时候,服务端会返回一个包含了服务端业务调用的 Binder 对象,通过这个 Binder 对象,客户端就可以获取服务端提供的服务(包括普通服务和 AIDL 服务)或者数据
设备
: 可以理解为一种虚拟的物理设备,设备驱动是/dev/binder,该通信方式在 Linux 中没有
通过 AIDL来看 Binder 的工作机制
先看示例类在包中的位置
示例类
Book.aidl
package com.cml.cmlapplication.ipc;
parcelable Book;
IBookManager.aidl
package com.cml.cmlapplication.ipc;
import com.cml.cmlapplication.ipc.Book;
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
}
Book.java
import android.os.Parcel;
import android.os.Parcelable;
public class Book implements Parcelable {
private int bookId;
private String bookName;
public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}
protected Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
}
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];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(bookId);
dest.writeString(bookName);
}
}
然后编译后,系统会自动生成一个类,位于: build/generated/source/aidl/…/IBookManager.java
我们就分析这个类基本就可了解 Binder上层工作机制.
这个类如下
IBookManager.java
public interface IBookManager extends android.os.IInterface {
//stub 是一个 binder
public static abstract class Stub extends android.os.Binder implements com.cml.cmlapplication.ipc.IBookManager{
//binder 的唯一标识
private static final java.lang.String DESCRIPTOR = "com.cml.cmlapplication.ipc.IBookManager";
public Stub(){
this.attachInterface(this, DESCRIPTOR);
}
/**
* 用于将服务端的 Binder 对象转换成客户端所需的 AIDL接口类型对象
* 同进程: 服务端对象本身
* 不同进程: 系统封装后的 Stub.proxy
*/
public static com.cml.cmlapplication.ipc.IBookManager asInterface(android.os.IBinder obj) {
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.cml.cmlapplication.ipc.IBookManager))) {
return ((com.cml.cmlapplication.ipc.IBookManager)iin);
}
return new com.cml.cmlapplication.ipc.IBookManager.Stub.Proxy(obj);
}
/**
* 提供 Binder 对象
*/
@Override public android.os.IBinder asBinder() {
return this;
}
/**
* 运行位置:服务端的 Binder 线程池
* 客户端发起跨进程的请求时,远程请求会通过底层封装后交由此方法处理。
*
* 方法内部通过 code 来确定客户端的请求目标方法
* 从 data 中获取参数
* 执行目标方法
* 将返回值写入 reply 并返回
*/
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getBookList: {
data.enforceInterface(DESCRIPTOR);
java.util.List<com.cml.cmlapplication.ipc.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook: {
data.enforceInterface(DESCRIPTOR);
com.cml.cmlapplication.ipc.Book _arg0;
if ((0!=data.readInt())) {
_arg0 = com.cml.cmlapplication.ipc.Book.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.cml.cmlapplication.ipc.IBookManager {
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;
}
/**
* 运行位置:客户端
*
* 客户端远程调用此方法时,首先创建_data、_reply 和_result(List)
* 然后将参数写入_data,随后调用 transact()发起远程调用请求并挂起当前线程,
* 此时服务端的 onTransact()方法被调用,直到远程调用返回后,当前线程继续运行并从_reply 中获取返回结果,最后返回
*/
@Override public java.util.List<com.cml.cmlapplication.ipc.Book> getBookList() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.cml.cmlapplication.ipc.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.cml.cmlapplication.ipc.Book.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public void addBook(com.cml.cmlapplication.ipc.Book book) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((book!=null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public java.util.List<com.cml.cmlapplication.ipc.Book> getBookList() throws android.os.RemoteException;
public void addBook(com.cml.cmlapplication.ipc.Book book) throws android.os.RemoteException;
}
类中的注释比较详细了,这里说一下其整体的工作流程
首先客户端发起远程调用通过getBookList(),此方法内部会调用 transact() , 同时当前的线程挂起,等待返回结果,然后此远程调用请求通过系统底层的封装后会走到运行在服务端 Binder 线程池的方法 onTransact()此方法执行完之后会返回结果_reply,此时返回客户端,继续运行被挂起的线程,从_reply 中取出返回结果,并返回.远程调用过程结束.
一张图看 Binder 的工作机制 摘自 Android 开发艺术探索
Android 中的 IPC 方式
1.Bundle
Android 的四大组件中的三个组件:Activity/Service 和 BorderCastReceiver都支持在 Intent 中使用 Bundle 传递数据,因为 Bundle实现了 Parceable 接口所以可以方便的在进程之间传输.当然所要传输的数据必须可以被序列化才行.
2.使用文件共享
两个进程通过读/写同一个文件来交换数据
3.Messenger
信使,其底层实现也是 AIDL
4.AIDL
工作机制见上面的 Binder 工作机制分析
5.ContentProvider
是 Android 中专门用来不同应用之前交换数据的.其底层也是通过 Binder 来实现的.
6.Socket