Binder or AIDL的最简单实践
1.前言:
在Android开发中多进程的合理使用+进程间通信的IPC是一个比较难的点。特别是Android特有的Binder机制,非常复杂,对于应用层开发的初级开发工程师强求深入理解Binder机制是不现实的。
其实Android 的开发人员已经为我们考虑到,提供了方便我们使用Binder的方法。
这就是AIDL: Android Interface definition language Android接口定义语言
我们按照要求简单书写一个AIDL文件,当前的IDE,比如Android Sudio 会根据这个自动生成一个用于Binder通信的Java类
通过这个类,我们就不需要关心如何通过Java代码实现Binder通信,我们只要会使用编写的AIDL文件自动生成的类即可
很难理解?
原理是这样的:如果我们要通过Java写Binder的IPC通信代码,对于菜鸟的我们很难写出来,又因为其实Java代码进行Binder通信过程都是类似的,所以谷歌就创造了AIDL文件,我们写好AIDL文件,就帮我们生成对应的Java代码,省去我们理解Binder的抽象过程(电脑帮我们写代码,哈哈)
2.从一个最简单的AIDL实例出发:
在应用层开发中,使用到AIDL的常见场景通常如下:
我们有一个Service,举例:下载文件的DownloadService,为了:
- 不让这个DownloadService占用App的UI进程内存资源
- DownService奔溃不影响App的UI进程
我们需要在AndroidManifest里面注册DownloadService的时候通过 android:process=":remote"指定这个Service运行在一个
独立的私有进程(进程名为应用包名:remote)中(请百度 android:process=""的用法)
<service
android:name=".DownloadService"
android:process=":remote" />
- 因为DownloadService运行在一个独立进程,App默认的UI进程需要和它通信就是IPC,具体来说就是使用Binder
3.下面一步步演示具体的实践步骤:
我们希望有一个MainActivity代表当前的App的进程,DownloadService运行在另外的一个进程
然后:
MainActivity可以通过bindService 绑定DownloadService
然后Binder实现IPC(进程间通信)调用DownloadService的两个功能:
public List<DownloadTask> getDownloadTasks();//获取当前所有的下载任务
public void addDownloadTask(DownloadTask task)//新增一个下载任务
3.1新建两个组件:MainActivity和DownloadService
MainActivity.java
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
DownloadService.java
public class DownloadService extends Service {
private static final String TAG = "DownloadService";
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;//注意:这里我们后面需要返回一个IBinder
}
@Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.lijian.binderdemo">
<application
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/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".DownloadService"
android:process=":remote" />
</application>
</manifest>
3.2创建代表下载任务的JavaBean DownloadTask
/**
* 下载任务的JavaBean
* 为了Binder传输,必须实现Parcelable序列化
* Created by lijian on 2017/3/23.
*/
public class DownloadTask implements Parcelable {
public int taskId;
public String fileName;
public String downloadUrl;
public DownloadTask(int taksId, String fileName, String downloadUrl) {
this.taskId = taksId;
this.fileName = fileName;
this.downloadUrl = downloadUrl;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(this.taskId);
dest.writeString(this.fileName);
dest.writeString(this.downloadUrl);
}
protected DownloadTask(Parcel in) {
this.taskId = in.readInt();
this.fileName = in.readString();
this.downloadUrl = in.readString();
}
public static final Parcelable.Creator<DownloadTask> CREATOR = new Parcelable.Creator<DownloadTask>() {
@Override
public DownloadTask createFromParcel(Parcel source) {
return new DownloadTask(source);
}
@Override
public DownloadTask[] newArray(int size) {
return new DownloadTask[size];
}
};
@Override
public String toString() {
return "DownloadTask{" +
"taskId=" + taskId +
", fileName='" + fileName + '\'' +
", downloadUrl='" + downloadUrl + '\'' +
"}\n";
}
}
观察DownloadTask代码,可以看到DownloadTask实现了序列化接口,
因为DownloadTask是需要IPC,在两个进程之间传输的,经过
对象-》序列化-》反序列化-》重新生成一个对象的过程
注意:由于这个DownloadTask需要在AIDL中使用,所以我们需要在AIDL声明它:
也就是DownloadTask.aidl
// DownloadTask.aidl
package com.lijian.binderdemo;//AIDL需要声明包名
// Declare any non-default types here with import statements
parcelable DownloadTask;
3.3创建IDownload.aidl
aidl名称是Android interface defined language,顾名思义,aidl是用于定义接口的,然后,编译器自动帮助我们生成用于Binder IPC的代码
// IDownload.aidl
package com.lijian.binderdemo;
// Declare any non-default types here with import statements
import com.lijian.binderdemo.DownloadTask;
interface IDownload {
List<DownloadTask> getTasks();
void addTask(in DownloadTask task);//使用in 表明这是一个输入的变量
}
有代码可以看到我们定义了一个接口 IDownload
它声明了两个方法
List getTasks();
void addTask(in DownloadTask task);
build一下,奇迹发生了:
这里生成了一个IDownload的Java接口,它是根据我们编写的IDownload.aidl自动生成的,它的作用是方便Binder进行IPC通信
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: G:\\Android_demo\\BinderDemo\\app\\src\\main\\aidl\\com\\lijian\\binderdemo\\IDownload.aidl
*/
package com.lijian.binderdemo;
public interface IDownload extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.lijian.binderdemo.IDownload
{
private static final java.lang.String DESCRIPTOR = "com.lijian.binderdemo.IDownload";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.lijian.binderdemo.IDownload interface,
* generating a proxy if needed.
*/
public static com.lijian.binderdemo.IDownload asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.lijian.binderdemo.IDownload))) {
return ((com.lijian.binderdemo.IDownload)iin);
}
return new com.lijian.binderdemo.IDownload.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@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_getTasks:
{
data.enforceInterface(DESCRIPTOR);
java.util.List<com.lijian.binderdemo.DownloadTask> _result = this.getTasks();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addTask:
{
data.enforceInterface(DESCRIPTOR);
com.lijian.binderdemo.DownloadTask _arg0;
if ((0!=data.readInt())) {
_arg0 = com.lijian.binderdemo.DownloadTask.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addTask(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.lijian.binderdemo.IDownload
{
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 java.util.List<com.lijian.binderdemo.DownloadTask> getTasks() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.lijian.binderdemo.DownloadTask> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getTasks, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.lijian.binderdemo.DownloadTask.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public void addTask(com.lijian.binderdemo.DownloadTask task) 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 ((task!=null)) {
_data.writeInt(1);
task.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addTask, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_getTasks = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addTask = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public java.util.List<com.lijian.binderdemo.DownloadTask> getTasks() throws android.os.RemoteException;
public void addTask(com.lijian.binderdemo.DownloadTask task) throws android.os.RemoteException;
}
代码看似很困惑,其实不然,层次还是很分明的,我们一层一层剥离代码分析
3.3.1 IDownload Interface
public interface IDownload extends android.os.IInterface{
//省略代码
public java.util.List<com.lijian.binderdemo.DownloadTask> getTasks() throws android.os.RemoteException;
public void addTask(com.lijian.binderdemo.DownloadTask task) throws android.os.RemoteException;
}
根据我们定义的IDownload.aidl文件,里面声明了两个方法,所以生成了一个Java代码
IDownload Interface ,extends android.os.IInterface,
android.os.IInterface Binder接口的基础class,在定义一个新的Binder Interface,必须继承这个接口 IInterface
/**
* Base class for Binder interfaces. When defining a new interface,
* you must derive it from IInterface.
*/
public interface IInterface
{
/**
* Retrieve the Binder object associated with this interface.
* You must use this instead of a plain cast, so that proxy objects
* can return the correct result.
*/
public IBinder asBinder();
}
IDownload java声明了我们定义在对应的AIDL的方法,唯一不同的是声明了这些方法可能会抛出 Remote调用的的Exception异常
public java.util.List<com.lijian.binderdemo.DownloadTask> getTasks() throws android.os.RemoteException;
public void addTask(com.lijian.binderdemo.DownloadTask task) throws android.os.RemoteException;
3.3.2 public static abstract class Stub
在上面我们曾经说过Client调用的其实是影子,真身在Server进程。
IDownload 有静态内部类辅助我们的在Client操作影子,在Server创建真身:
它就是public static abstract class Stub
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.lijian.binderdemo.IDownload
{
private static final java.lang.String DESCRIPTOR = "com.lijian.binderdemo.IDownload";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.lijian.binderdemo.IDownload interface,
* generating a proxy if needed.
*/
public static com.lijian.binderdemo.IDownload asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.lijian.binderdemo.IDownload))) {
return ((com.lijian.binderdemo.IDownload)iin);
}
return new com.lijian.binderdemo.IDownload.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@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_getTasks:
{
data.enforceInterface(DESCRIPTOR);
java.util.List<com.lijian.binderdemo.DownloadTask> _result = this.getTasks();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addTask:
{
data.enforceInterface(DESCRIPTOR);
com.lijian.binderdemo.DownloadTask _arg0;
if ((0!=data.readInt())) {
_arg0 = com.lijian.binderdemo.DownloadTask.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addTask(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.lijian.binderdemo.IDownload
{
//省略代码
}
static final int TRANSACTION_getTasks = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addTask = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
为了更清晰
可以看到里面就
三个常量
- private static final java.lang.String DESCRIPTOR = "com.lijian.binderdemo.IDownload";
- static final int TRANSACTION_getTasks = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
- static final int TRANSACTION_addTask = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
Stub的构造方法
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
一个静态方法:public static com.lijian.binderdemo.IDownload asInterface(android.os.IBinder obj)
这个方法可以把一个IBinder转换为 com.lijian.binderdemo.IDownload interface
/**
* Cast an IBinder object into an com.lijian.binderdemo.IDownload interface,
* generating a proxy if needed.
*/
public static com.lijian.binderdemo.IDownload asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.lijian.binderdemo.IDownload))) {
return ((com.lijian.binderdemo.IDownload)iin);//说明这是一个本地调用而不是IPC调用,直接返回真身即可,真身直接调用,不需要binder,效率更高
}
return new com.lijian.binderdemo.IDownload.Stub.Proxy(obj);//说明这是是IPC调用,返回一个Proxy,代理类,通过他进行Binder 调用
}
两个成员方法:
- public android.os.IBinder asBinder()
@Override
public android.os.IBinder asBinder(){
return this;
}
- public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
这个方法运行在Server端的Binder线程池,观察它的参数:
int code:用于标识不同的方法
android.os.Parcel data:用于传递Client的参数
android.os.Parcel reply,用于写入Server的返回值
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException{
switch (code)//通过code Client通过Binder调用的是哪一个方法
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getTasks:
{
data.enforceInterface(DESCRIPTOR);
java.util.List<com.lijian.binderdemo.DownloadTask> _result = this.getTasks();//这个是接口的具体实现
reply.writeNoException();//写入Binder remote调用没有异常
reply.writeTypedList(_result);//写入返回值
return true;
}
case TRANSACTION_addTask:
{
data.enforceInterface(DESCRIPTOR);
com.lijian.binderdemo.DownloadTask _arg0;
if ((0!=data.readInt())) {
_arg0 = com.lijian.binderdemo.DownloadTask.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addTask(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
总结:
分析了IDownload.java 后,我们可以看到通过IDownload.aidl生成的代码,其实最终我们需要的也只是java代码,也就是说,如果我们熟悉了自动生成代码的套路,其实,不需要aidl,手写Java代码实现AIDL调用也是可以的。
4. 生成的IDownload.java 接口的使用:
4.1 Server端:
public class DownloadService extends Service {
private static final String TAG = "DownloadService";
private List<DownloadTask> mTasks = new CopyOnWriteArrayList<>();
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
//注意这个方法,这就是定义的接口具体实现,理所当然的具体功能应该有Server实现
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new IDownload.Stub() {
@Override
public List<DownloadTask> getTasks() throws RemoteException {
return mTasks;
}
@Override
public void addTask(DownloadTask task) throws RemoteException {
if (mTasks != null) {
mTasks.add(task);
}
}
};
}
@Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
}
4.2 Client端:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = "MainActivity";
private IDownload download;//注意这里
private Button addTaskBtn;
private TextView addTaskInfo;
private Button getTasksBtn;
private TextView getTasksInfo;
private int index = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
bindDownloadService();
setContentView(R.layout.activity_main);
setupView();
}
private void setupView() {
addTaskBtn = (Button) findViewById(R.id.btn_add_task);
getTasksBtn = (Button) findViewById(R.id.btn_get_tasks);
addTaskInfo = (TextView) findViewById(R.id.tv_add_task_info);
getTasksInfo = (TextView) findViewById(R.id.tv_tasks_info);
addTaskBtn.setOnClickListener(this);
getTasksBtn.setOnClickListener(this);
}
private void bindDownloadService() {
Intent intent = new Intent(this, DownloadService.class);
bindService(intent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
download = IDownload.Stub.asInterface(service);//我们把bindService返回的 IBinder Service转换为一个接口实例
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}, Context.BIND_AUTO_CREATE);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_get_tasks:
getTasks();
break;
case R.id.btn_add_task:
addTask();
break;
default:
break;
}
}
private void addTask() {
if (download != null) {
DownloadTask task = new DownloadTask(index++, "下载文件" + index, "www.baidu.com/" + index + ".txt");
try {
download.addTask(task);//这里就可以调用接口实例,看到没有,Binder的IPC操作对于Client端来说就是简化为调用一个对象了
addTaskInfo.setText(task.toString());
} catch (RemoteException e) {
Log.w(TAG, e.toString());//Binder调用可能尝试RemoteException,需要捕获异常
}
}
}
private void getTasks() {
if (download != null) {
try {
List<DownloadTask> tasks = download.getTasks();/这里就可以调用接口实例
getTasksInfo.setText(tasks == null ? "no task" : tasks.toString());
} catch (RemoteException e) {
Log.w(TAG, e.toString());
}
}
}
}
总结:
public class DownloadService extends Service {
private static final String TAG = "DownloadService";
private List<DownloadTask> mTasks = new CopyOnWriteArrayList<>();
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
//注意这个方法,这就是定义的接口具体实现,理所当然的具体功能应该有Server实现
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new IDownload.Stub() {
@Override
public List<DownloadTask> getTasks() throws RemoteException {
return mTasks;
}
@Override
public void addTask(DownloadTask task) throws RemoteException {
if (mTasks != null) {
mTasks.add(task);
}
}
};
}
@Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
}
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = "MainActivity";
private IDownload download;//注意这里
private Button addTaskBtn;
private TextView addTaskInfo;
private Button getTasksBtn;
private TextView getTasksInfo;
private int index = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
bindDownloadService();
setContentView(R.layout.activity_main);
setupView();
}
private void setupView() {
addTaskBtn = (Button) findViewById(R.id.btn_add_task);
getTasksBtn = (Button) findViewById(R.id.btn_get_tasks);
addTaskInfo = (TextView) findViewById(R.id.tv_add_task_info);
getTasksInfo = (TextView) findViewById(R.id.tv_tasks_info);
addTaskBtn.setOnClickListener(this);
getTasksBtn.setOnClickListener(this);
}
private void bindDownloadService() {
Intent intent = new Intent(this, DownloadService.class);
bindService(intent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
download = IDownload.Stub.asInterface(service);//我们把bindService返回的 IBinder Service转换为一个接口实例
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}, Context.BIND_AUTO_CREATE);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_get_tasks:
getTasks();
break;
case R.id.btn_add_task:
addTask();
break;
default:
break;
}
}
private void addTask() {
if (download != null) {
DownloadTask task = new DownloadTask(index++, "下载文件" + index, "www.baidu.com/" + index + ".txt");
try {
download.addTask(task);//这里就可以调用接口实例,看到没有,Binder的IPC操作对于Client端来说就是简化为调用一个对象了
addTaskInfo.setText(task.toString());
} catch (RemoteException e) {
Log.w(TAG, e.toString());//Binder调用可能尝试RemoteException,需要捕获异常
}
}
}
private void getTasks() {
if (download != null) {
try {
List<DownloadTask> tasks = download.getTasks();/这里就可以调用接口实例
getTasksInfo.setText(tasks == null ? "no task" : tasks.toString());
} catch (RemoteException e) {
Log.w(TAG, e.toString());
}
}
}
}
通过文字来阐述解析是比较困难的,具体可以参考Github上的代码:
https://github.com/bylijian/BinderDemo
注意,请查看git记录,这个是比较早的提交,和最新的代码不一样,具体参考截图高亮部分
另外,这个Demo还有更多的的演示代码,都值得看一看。
Binder or AIDL的最简单实践的更多相关文章
- Thrift简单实践
0.什么是RPC RPC(Remote Procedure Call - 远程过程调用),是通过网络从远程计算机上请求服务,而不需要了解底层网路技术的细节.简单点说,就是像调用本地服务(方法)一样调用 ...
- Java 异步处理简单实践
Java 异步处理简单实践 http://www.cnblogs.com/fangfan/p/4047932.html 同步与异步 通常同步意味着一个任务的某个处理过程会对多个线程在用串行化处理,而异 ...
- Android 设计随便说说之简单实践(合理组合)
上一篇(Android 设计随便说说之简单实践(模块划分))例举了应用商店设计来说明怎么做模块划分.模块划分主要依赖于第一是业务需求,具体是怎么样的业务.应用商店则包括两个业务,就是向用户展示appl ...
- c#中,委托Func的简单实践
c# 委托Func的简单实践最近才真正的接触委托,所以针对Func类型的委托,做一个实践练习. 首先说一些我对委托的初级理解:"就是把方法当做参数,传进委托方法里". 我平时用到的 ...
- kafka原理和实践(二)spring-kafka简单实践
系列目录 kafka原理和实践(一)原理:10分钟入门 kafka原理和实践(二)spring-kafka简单实践 kafka原理和实践(三)spring-kafka生产者源码 kafka原理和实践( ...
- SQL知识以及SQL语句简单实践
综述 大家都知道SQL是结构化查询语言,是关系数据库的标准语言,是一个综合的,功能极强的同时又简洁易学的,它集级数据查询(Data Quest),数据操纵(Data Manipulation),数据定 ...
- Android : 跟我学Binder --- (2) AIDL分析及手动实现
目录: Android : 跟我学Binder --- (1) 什么是Binder IPC?为何要使用Binder机制? Android : 跟我学Binder --- (2) AIDL分析及手动实现 ...
- android 进程间通信 messenger 是什么 binder 跟 aidl 区别 intent 进程间 通讯? android 消息机制 进程间 android 进程间 可以用 handler么 messenger 与 handler 机制 messenger 机制 是不是 就是 handler 机制 或 , 是不是就是 消息机制 android messenge
韩梦飞沙 韩亚飞 313134555@qq.com yue31313 han_meng_fei_sha messenger 是什么 binder 跟 aidl 区别 intent 进程间 通讯 ...
- ZooKeeper分布式锁简单实践
ZooKeeper分布式锁简单实践 在分布式解决方案中,Zookeeper是一个分布式协调工具.当多个JVM客户端,同时在ZooKeeper上创建相同的一个临时节点,因为临时节点路径是保证唯一,只要谁 ...
随机推荐
- win10环境下搭建虚拟环境和 virtualenvwrapper-win 使用
1. 安装 virtualenv pip install virtualenv 2. virtualenv基本操作 cd path/dir # 跳转到dir目录 virtualenv env # 在d ...
- c语言-勒让德多项式求解+时间测定
#include<iostream>#include<ctime>#include<cstdlib>using namespace std; float LRD(i ...
- 简明依赖注入(Dependency Injection)
前言 这是因特奈特上面不知道第几万篇讲依赖注入(Dependency Injection)的文章,但是说明白的却寥寥无几,这篇文章尝试控制字数同时不做大多数. 首先,依赖注入的是一件很简单的事情. 为 ...
- ssh访问流程
SSH是英文Secure Shell的简写形式.通过使用SSH,你可以把所有传输的数据进行加密,这样"中间人"这种攻击方式就不可能实现了,而且也能够防止DNS欺骗和IP欺骗.使用S ...
- springmvc.xml配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...
- Vue项目中用的Jquery.js和easing.js做的抛物线
如果项目中还用到自有的$ <script> jQuery.noConflict();</script> //星星掉落movePathNew(args) { let self = ...
- Java 的 List 与 Scala 的 Seq 相互转换
1. List 转 Seq: List<String> tmpList = new ArrayList<>(); tmpList.add("abc"); S ...
- Train Problem II(卡特兰数 组合数学)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1023 Train Problem II Time Limit: 2000/1000 MS (Java/ ...
- 性能测试工具Jmeter13-Jmeter跨线程组调用token
1.正则表达式或者json提取器(我是用json提取器提取的),提取token 2.添加后置处理器BeanShell PostProcessor,然后输入以下函数 3.添加HTTP信息头管理器,写入函 ...
- JS模块加载系统设计V1
一.require模块 +function() { var path = location.protocol + "//" + location.host +"/Java ...