什么是Android操作系统,所谓的Android:是基于Linux内核的软件平台和操作系统,早期由Google开发,后由开放手机联盟Open Handset Alliance)开发。

Linux系统中进程间通信的方式有:socket, named pipe,message queque, signal,share memory。Java系统中的进程间通信方式有socket, named pipe等。android应用程序理所当然可以应用JAVA的IPC机制实现进程间的通信,
取而代之的是Binder通信。Google为什么要采用这种方式呢,这取决于Binder通信方式的高效率。 Binder通信是通过linux的binder driver来实现的。Binder通信操作类似线程迁移(thread migration),两个进程间IPC看起来就象是一个进程进入另一个进程执行代码然后带着执行的结果返回。
Binder的用户空间为每一个进程维护着一个可用的线程池,线程池用于处理到来的IPC以及执行进程本地消息,Binder通信是同步而不是异步。Android中的Binder通信是基于Service与Client的,所有需要IBinder通信的进程都必须创建一个IBinder接口。
系统中有一个进程管理所有的system service,Android虚拟机不允许用户添加非授权的System service,当然现在源码开发了,我们可以修改一些代码来实现添加底层system Service的目的。
对用户程序来说,我们也要创建server,或者Service用于进程间通信,这里有一ActivityManagerService管理JAVA应用层所有的service创建与连接(connect)。disconnect,所有的 Activity也是通过这个service来启动,加载的。ActivityManagerService也是加载在Systems Servcie中的。
Android虚拟机启动之前系统会先启动service Manager进程,service Manager打开binder驱动,并通知binder kernel驱动程序这个进程将作为System Service Manager。然后该进程将进入一个循环,等待处理来自其他进程的数据。用户创建一个System service后,通过defaultServiceManager得到一个远程ServiceManager的接口。
通过这个接口我们可以调用 addService函数将System service添加到Service Manager进程中,然后client可以通过getService获取到需要连接的目的Service的IBinder对象。这个IBinder是 Service的BBinder在binder kernel的一个参考,所以service IBinder 在binder kernel中不会存在相同的两个IBinder对象。
每一个Client进程同样需要打开Binder驱动程序。对用户程序而言,我们获得这个对象就可以通过binder kernel访问service对象中的方法。Client与Service在不同的进程中,通过这种方式实现了类似线程间的迁移的通信方式,对用户程序而言当调用Service返回的IBinder接口后,访问Service中的方法就如同调用自己的函数。
实现接口时有几个原则:
抛出的异常不要返回给调用者. 跨进程抛异常处理是不可取的。IPC调用是同步的。如果你知道一个IPC服务需要超过几毫秒的时间才能完成地话,你应该避免在Activity的主线程中调用。
也就是IPC调用会挂起应用程序导致界面失去响应. 这种情况应该考虑单起一个线程来处理,能在AIDL接口中声明静态属性。IPC的调用步骤:
1. 声明一个接口类型的变量,该接口类型在.aidl文件中定义。
2. 实现ServiceConnection。
3. 调用ApplicationContext.bindService(),并在ServiceConnection实现中进行传递. 
4. 在ServiceConnection.onServiceConnected()实现中,你会接收一个IBinder实例(被调用的Service). 调用    YourInterfaceName.Stub.asInterface((IBinder)service)将参数转换YourInterface类型。
5. 调用接口中定义的方法。 你总要检测到DeadObjectException异常,该异常在连接断开时被抛出。它只会被远程方法抛出。
6. 断开连接,调用接口实例中的ApplicationContext.unbindService()
IPC 及原理
IBinder接口
IBinder接口是对跨进程的对象的抽象。普通对象在当前进程可以访问,如果希望对象能被其它进程访问,那就必须实现IBinder接口。IBinder接口可以指向本地对象,也可以指向远程对象,调用者不需要关心指向的对象是本地的还是远程。
transact是IBinder接口中一个比较重要的函数,它的函数原型如下:
virtual status_t transact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) = 0;
android中的IPC的基本模型是基于客户/服务器(C/S)架构的。
客户端
请求通过内核模块中转
服务端
如果IBinder指向的是一个客户端代理,那transact只是把请求发送给服务器。服务端的IBinder的transact则提供了实际的服务。
o 客户端
BpBinder是远程对象在当前进程的代理,它实现了IBinder接口。它的transact函数实现如下:
status_t BpBinder::transact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
// Once a binder has died, it will never come back to life.
if (mAlive) {
status_t status = IPCThreadState::self()->transact(
mHandle, code, data, reply, flags);
if (status == DEAD_OBJECT) mAlive = 0;
return status;
}
return DEAD_OBJECT;
}
参数说明:
?  code 是请求的ID号。
?  data 是请求的参数。
?  reply 是返回的结果。
?  flags 一些额外的标识,如FLAG_ONEWAY。通常为0。
transact只是简单的调用了IPCThreadState::self()的transact,在IPCThreadState::transact中:
status_t IPCThreadState::transact(int32_t handle,
uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags)
{
status_t err = data.errorCheck();
flags |= TF_ACCEPT_FDS;
IF_LOG_TRANSACTIONS() {
TextOutput::Bundle _b(alog);
alog << "BC_TRANSACTION thr " << (void*)pthread_self() << " / hand "
<< handle << " / code " << TypeCode(code) << ": "
<< indent << data << dedent << endl;
}
if (err == NO_ERROR) {
LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(),
(flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY");
err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
}
if (err != NO_ERROR) {
if (reply) reply->setError(err);
return (mLastError = err);
}
if ((flags & TF_ONE_WAY) == 0) {
if (reply) {
err = waitForResponse(reply);
} else {
Parcel fakeReply;
err = waitForResponse(&fakeReply);
}
IF_LOG_TRANSACTIONS() {
TextOutput::Bundle _b(alog);
alog << "BR_REPLY thr " << (void*)pthread_self() << " / hand "
<< handle << ": ";
if (reply) alog << indent << *reply << dedent << endl;
else alog << "(none requested)" << endl;
}
} else {
err = waitForResponse(NULL, NULL);
}
return err;
}
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
int32_t cmd;
int32_t err;
while (1) {
if ((err=talkWithDriver()) < NO_ERROR) break;
err = mIn.errorCheck();
if (err < NO_ERROR) break;
if (mIn.dataAvail() == 0) continue;
cmd = mIn.readInt32();
IF_LOG_COMMANDS() {
alog << "Processing waitForResponse Command: "
<< getReturnString(cmd) << endl;
}
switch (cmd) {
case BR_TRANSACTION_COMPLETE:
if (!reply && !acquireResult) goto finish;
break;
case BR_DEAD_REPLY:
err = DEAD_OBJECT;
goto finish;
case BR_FAILED_REPLY:
err = FAILED_TRANSACTION;
goto finish;
case BR_ACQUIRE_RESULT:
{
LOG_ASSERT(acquireResult != NULL, "Unexpected brACQUIRE_RESULT");
const int32_t result = mIn.readInt32();
if (!acquireResult) continue;
*acquireResult = result ? NO_ERROR : INVALID_OPERATION;
}
goto finish;
case BR_REPLY:
{
binder_transaction_data tr;
err = mIn.read(&tr, sizeof(tr));
LOG_ASSERT(err == NO_ERROR, "Not enough command data for brREPLY");
if (err != NO_ERROR) goto finish;
if (reply) {
if ((tr.flags & TF_STATUS_CODE) == 0) {
reply->ipcSetDataReference(
reinterpret_cast(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast(tr.data.ptr.offsets),
tr.offsets_size/sizeof(size_t),
freeBuffer, this);
} else {
err = *static_cast(tr.data.ptr.buffer);
freeBuffer(NULL,
reinterpret_cast(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast(tr.data.ptr.offsets),
tr.offsets_size/sizeof(size_t), this);
}
} else {
freeBuffer(NULL,
reinterpret_cast(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast(tr.data.ptr.offsets),
tr.offsets_size/sizeof(size_t), this);
continue;
}
}
goto finish;
default:
err = executeCommand(cmd);
if (err != NO_ERROR) goto finish;
break;
}
}
finish:
if (err != NO_ERROR) {
if (acquireResult) *acquireResult = err;
if (reply) reply->setError(err);
mLastError = err;
}
return err;
}
这里transact把请求经内核模块发送了给服务端,服务端处理完请求之后,沿原路返回结果给调用者。这里也可以看出请求是同步操作,它会等待直到结果返回为止。
在BpBinder之上进行简单包装,我们可以得到与服务对象相同的接口,调用者无需要关心调用的对象是远程的还是本地的。拿ServiceManager来说:
(frameworks/base/libs/utils/IServiceManager.cpp)
class BpServiceManager : public BpInterface
{
public:
BpServiceManager(const sp& impl)
: BpInterface(impl)
{
}
...
virtual status_t addService(const String16& name, const sp& service)
{
Parcel data, reply;
data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
data.writeString16(name);
data.writeStrongBinder(service);
status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
return err == NO_ERROR ? reply.readInt32() : err;
}
...
};
BpServiceManager实现了 IServiceManager和IBinder两个接口,调用者可以把BpServiceManager的对象看作是一个IServiceManager对象或者IBinder对象。当调用者把BpServiceManager对象当作IServiceManager对象使用时,所有的请求只是对BpBinder::transact的封装。这样的封装使得调用者不需要关心IServiceManager对象是本地的还是远程的了。
客户通过defaultServiceManager函数来创建BpServiceManager对象:
(frameworks/base/libs/utils/IServiceManager.cpp)
sp<IServiceManager> defaultServiceManager()
{
if (gDefaultServiceManager != NULL) return gDefaultServiceManager;
{
AutoMutex _l(gDefaultServiceManagerLock);
if (gDefaultServiceManager == NULL) {
gDefaultServiceManager = interface_cast<IServiceManager>(
ProcessState::self()->getContextObject(NULL));
}
}
return gDefaultServiceManager;
}
先通过ProcessState::self()->getContextObject(NULL)创建一个Binder对象,然后通过interface_cast和IMPLEMENT_META_INTERFACE(ServiceManager, “android.os.IServiceManager”)把Binder对象包装成 IServiceManager对象。原理上等同于创建了一个BpServiceManager对象。
ProcessState::self()->getContextObject调用ProcessState::getStrongProxyForHandle创建代理对象:
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
sp<IBinder> result;
AutoMutex _l(mLock);
handle_entry* e = lookupHandleLocked(handle);
if (e != NULL) {
// We need to create a new BpBinder if there isn't currently one, OR we
// are unable to acquire a weak reference on this current one.  See comment
// in getWeakProxyForHandle() for more info about this.
IBinder* b = e->binder;
if (b == NULL || !e->refs->attemptIncWeak(this)) {
b = new BpBinder(handle);
e->binder = b;
if (b) e->refs = b->getWeakRefs();
result = b;
} else {
// This little bit of nastyness is to allow us to add a primary
// reference to the remote proxy when this team doesn't have one
// but another team is sending the handle to us.
result.force_set(b);
e->refs->decWeak(this);
}
}
return result;
}
如果handle为空,默认为context_manager对象,context_manager实际上就是ServiceManager。
o 服务端
服务端也要实现IBinder接口,BBinder类对IBinder接口提供了部分默认实现,其中transact的实现如下:
status_t BBinder::transact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
data.setDataPosition(0);
status_t err = NO_ERROR;
switch (code) {
case PING_TRANSACTION:
reply->writeInt32(pingBinder());
break;
default:
err = onTransact(code, data, reply, flags);
break;
}
if (reply != NULL) {
reply->setDataPosition(0);
}
return err;
}
PING_TRANSACTION请求用来检查对象是否还存在,这里简单的把 pingBinder的返回值返回给调用者。其它的请求交给onTransact处理。onTransact是BBinder里声明的一个protected类型的虚函数,这个要求它的子类去实现。比如CameraService里的实现如下:
status_t CameraService::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
// permission checks...
switch (code) {
case BnCameraService::CONNECT:
IPCThreadState* ipc = IPCThreadState::self();
const int pid = ipc->getCallingPid();
const int self_pid = getpid();
if (pid != self_pid) {
// we're called from a different process, do the real check
if (!checkCallingPermission(
String16("android.permission.CAMERA")))
{
const int uid = ipc->getCallingUid();
LOGE("Permission Denial: "
"can't use the camera pid=%d, uid=%d", pid, uid);
return PERMISSION_DENIED;
}
}
break;
}
status_t err = BnCameraService::onTransact(code, data, reply, flags);
LOGD("+++ onTransact err %d code %d", err, code);
if (err == UNKNOWN_TRANSACTION || err == PERMISSION_DENIED) {
// the 'service' command interrogates this binder for its name, and then supplies it
// even for the debugging commands.  that means we need to check for it here, using
// ISurfaceComposer (since we delegated the INTERFACE_TRANSACTION handling to
// BnSurfaceComposer before falling through to this code).
LOGD("+++ onTransact code %d", code);
CHECK_INTERFACE(ICameraService, data, reply);
switch(code) {
case 1000:
{
if (gWeakHeap != 0) {
sp h = gWeakHeap.promote();
IMemoryHeap *p = gWeakHeap.unsafe_get();
LOGD("CHECKING WEAK REFERENCE %p (%p)", h.get(), p);
if (h != 0)
h->printRefs();
bool attempt_to_delete = data.readInt32() == 1;
if (attempt_to_delete) {
// NOT SAFE!
LOGD("DELETING WEAK REFERENCE %p (%p)", h.get(), p);
if (p) delete p;
}
return NO_ERROR;
}
}
break;
default:
break;
}
}
return err;
}
由此可见,服务端的onTransact是一个请求分发函数,它根据请求码(code)做相应的处理。
o 消息循环
服务端(任何进程都可以作为服务端)有一个线程监听来自客户端的请求,并循环处理这些请求。
如果在主线程中处理请求,可以直接调用下面的函数:
IPCThreadState::self()->joinThreadPool(mIsMain);
如果想在非主线程中处理请求,可以按下列方式:
sp
proc = ProcessState::self();
if (proc->supportsProcesses()) {
LOGV("App process: starting thread pool.\n");
proc->startThreadPool();
}
startThreadPool的实现原理:
void ProcessState::startThreadPool()
{
AutoMutex _l(mLock);
if (!mThreadPoolStarted) {
mThreadPoolStarted = true;
spawnPooledThread(true);
}
}
void ProcessState::spawnPooledThread(bool isMain)
{
if (mThreadPoolStarted) {
int32_t s = android_atomic_add(1, &mThreadPoolSeq);
char buf[32];
sprintf(buf, "Binder Thread #%d", s);
LOGV("Spawning new pooled thread, name=%s\n", buf);
sp
t = new PoolThread(isMain);
t->run(buf);
}
}
这里创建了PoolThread的对象,实现上就是创建了一个线程。所有的线程类都要实现threadLoop虚函数。PoolThread的threadLoop的实现如下:
virtual bool threadLoop()
{
IPCThreadState::self()->joinThreadPool(mIsMain);
return false;
}
上述代码,简而言之就是创建了一个线程,然后在线程里调用 IPCThreadState::self()->joinThreadPool函数。
下面再看joinThreadPool的实现:
do
{
...
result = talkWithDriver();
if (result >= NO_ERROR) {
size_t IN = mIn.dataAvail();
if (IN < sizeof(int32_t)) continue;
cmd = mIn.readInt32();
IF_LOG_COMMANDS() {
alog << "Processing top-level Command: "
<< getReturnString(cmd) << endl;
}
result = executeCommand(cmd);
}
...
while(...);
这个函数在循环中重复执行下列动作:
talkWithDriver 通过ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr)读取请求和写回结果。
executeCommand 执行相应的请求
在IPCThreadState::executeCommand(int32_t cmd)函数中:
对于控制对象生命周期的请求,像BR_ACQUIRE/BR_RELEASE直接做了处理。
对于BR_TRANSACTION请求,它调用被请求对象的transact函数。
按下列方式调用实际的对象:
if (tr.target.ptr) {
sp<BBinder> b((BBinder*)tr.cookie);
const status_t error = b->transact(tr.code, buffer, &reply, 0);
if (error < NO_ERROR) reply.setError(error);
} else {
const status_t error = the_context_object->transact(tr.code, buffer, &reply, 0);
if (error < NO_ERROR) reply.setError(error);
}
如果tr.target.ptr不为空,就把tr.cookie转换成一个Binder对象,并调用它的transact函数。如果没有目标对象,就调用 the_context_object对象的transact函数。奇怪的是,根本没有谁对the_context_object进行初始化,the_context_object是空指针。原因是context_mgr的请求发给了ServiceManager,所以根本不会走到else语句里来。
o 内核模块
android使用了一个内核模块binder来中转各个进程之间的消息。模块源代码放在binder.c里,它是一个字符驱动程序,主要通过binder_ioctl与用户空间的进程交换数据。其中BINDER_WRITE_READ用来读写数据,数据包中有一个cmd域用于区分不同的请求:
binder_thread_write用于发送请求或返回结果。
binder_thread_read用于读取结果。
从binder_thread_write中调用binder_transaction中转请求和返回结果,binder_transaction的实现如下:
对请求的处理:
通过对象的handle找到对象所在的进程,如果handle为空就认为对象是context_mgr,把请求发给context_mgr所在的进程。
把请求中所有的binder对象全部放到一个RB树中。
把请求放到目标进程的队列中,等待目标进程读取。
如何成为context_mgr呢?内核模块提供了BINDER_SET_CONTEXT_MGR调用:
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
...
case BINDER_SET_CONTEXT_MGR:
if (binder_context_mgr_node != NULL) {
printk(KERN_ERR "binder: BINDER_SET_CONTEXT_MGR already set\n");
ret = -EBUSY;
goto err;
}
if (binder_context_mgr_uid != -1) {
if (binder_context_mgr_uid != current->euid) {
printk(KERN_ERR "binder: BINDER_SET_"
"CONTEXT_MGR bad uid %d != %d\n",
current->euid,
binder_context_mgr_uid);
ret = -EPERM;
goto err;
}
} else
binder_context_mgr_uid = current->euid;
binder_context_mgr_node = binder_new_node(proc, NULL, NULL);
if (binder_context_mgr_node == NULL) {
ret = -ENOMEM;
goto err;
}
binder_context_mgr_node->local_weak_refs++;
binder_context_mgr_node->local_strong_refs++;
binder_context_mgr_node->has_strong_ref = 1;
binder_context_mgr_node->has_weak_ref = 1;
break;
ServiceManager(frameworks/base/cmds/servicemanager)通过下列方式成为了context_mgr进程:
int binder_become_context_manager(struct binder_state *bs)
{
return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}
int main(int argc, char **argv)
{
struct binder_state *bs;
void *svcmgr = BINDER_SERVICE_MANAGER;
bs = binder_open(128*1024);
if (binder_become_context_manager(bs)) {
LOGE("cannot become context manager (%s)\n", strerror(errno));
return -1;
}
svcmgr_handle = svcmgr;
binder_loop(bs, svcmgr_handler);
return 0;
}
o 如何得到服务对象的handle
服务提供者通过defaultServiceManager得到ServiceManager对象,然后调用addService向服务管理器注册。
服务使用者通过defaultServiceManager得到ServiceManager对象,然后调用getService通过服务名称查找到服务对象的handle。
o 如何通过服务对象的handle找到服务所在的进程
0表示服务管理器的handle,getService可以查找到系统服务的handle。这个handle只是代表了服务对象,内核模块是如何通过handle找到服务所在的进程的呢?
对于ServiceManager: ServiceManager调用了binder_become_context_manager使用自己成为context_mgr,所有handle为0的请求都会被转发给ServiceManager。
对于系统服务和应用程序的Listener,在第一次请求内核模块时(比如调用addService),内核模块在一个RB树中建立了服务对象和进程的对应关系。
off_end = (void *)offp + tr->offsets_size;
for (; offp < off_end; offp++) {
struct flat_binder_object *fp;
if (*offp > t->buffer->data_size - sizeof(*fp)) {
binder_user_error("binder: %d:%d got transaction with "
"invalid offset, %d\n",
proc->pid, thread->pid, *offp);
return_error = BR_FAILED_REPLY;
goto err_bad_offset;
}
fp = (struct flat_binder_object *)(t->buffer->data + *offp);
switch (fp->type) {
case BINDER_TYPE_BINDER:
case BINDER_TYPE_WEAK_BINDER: {
struct binder_ref *ref;
struct binder_node *node = binder_get_node(proc, fp->binder);
if (node == NULL) {
node = binder_new_node(proc, fp->binder, fp->cookie);
if (node == NULL) {
return_error = BR_FAILED_REPLY;
goto err_binder_new_node_failed;
}
node->min_priority = fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK;
node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);
}
if (fp->cookie != node->cookie) {
binder_user_error("binder: %d:%d sending u%p "
"node %d, cookie mismatch %p != %p\n",
proc->pid, thread->pid,
fp->binder, node->debug_id,
fp->cookie, node->cookie);
goto err_binder_get_ref_for_node_failed;
}
ref = binder_get_ref_for_node(target_proc, node);
if (ref == NULL) {
return_error = BR_FAILED_REPLY;
goto err_binder_get_ref_for_node_failed;
}
if (fp->type == BINDER_TYPE_BINDER)
fp->type = BINDER_TYPE_HANDLE;
else
fp->type = BINDER_TYPE_WEAK_HANDLE;
fp->handle = ref->desc;
binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE, &thread->todo);
if (binder_debug_mask & BINDER_DEBUG_TRANSACTION)
printk(KERN_INFO "        node %d u%p -> ref %d desc %d\n",
node->debug_id, node->ptr, ref->debug_id, ref->desc);
} break;
请求服务时,内核先通过handle找到对应的进程,然后把请求放到服务进程的队列中。
o C调用JAVA
前面我们分析的是C代码的处理。对于JAVA代码,JAVA调用C的函数通过JNI调用即可。从内核时读取请求是在C代码(executeCommand)里进行了,那如何在C代码中调用那些用JAVA实现的服务呢?
android_os_Binder_init里的JavaBBinder对Java里的Binder对象进行包装。
JavaBBinder::onTransact调用Java里的execTransact函数:
jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,
code, (int32_t)&data, (int32_t)reply, flags);
jthrowable excep = env->ExceptionOccurred();
if (excep) {
report_exception(env, excep,
"*** Uncaught remote exception!  "
"(Exceptions are not yet supported across processes.)");
res = JNI_FALSE;
/* clean up JNI local ref -- we don't return to Java code */
env->DeleteLocalRef(excep);
}
o 广播消息
binder不提供广播消息,不过可以ActivityManagerService服务来实现广播。
(frameworks/base/core/java/android/app/ActivityManagerNative.java)
接收广播消息需要实现接口BroadcastReceiver,然后调用ActivityManagerProxy::registerReceiver注册。
触发广播调用ActivityManagerProxy::broadcastIntent。(应用程序并不直接调用它,而是调用Context对它的包装)

转载于:https://www.cnblogs.com/xiaochao1234/p/3748089.html

android IPC及原理简介相关推荐

  1. android系统更新原理简介

    简介: Android系统更新简而言之就是将升级包从服务器下载到本地然后对当前系统进行升级的过程,主要包括三大块:升级包.应用层.recovery层. 主要功能流程如下如所示: 升级包: 版本编译完成 ...

  2. 【Android】动画原理简介

    动画框架原理 现有的 Android 动画框架是建立在 View 的级别上的,在 View 类中有一个接口 startAnimation 来使动画开始,startAnimation 函数会将一个 An ...

  3. 【Android 异步操作】Handler ( 主线程中的 Handler 与 Looper | Handler 原理简介 )

    文章目录 一.主线程中的 Handler 与 Looper 二.Handler 原理简介 一.主线程中的 Handler 与 Looper Android 系统中 , 点击图标启动一个应用进程 , 就 ...

  4. Android 系统(175)---Android硬件加速原理与实现简介

    Android硬件加速原理与实现简介 在手机客户端尤其是Android应用的开发过程中,我们经常会接触到"硬件加速"这个词.由于操作系统对底层软硬件封装非常完善,上层软件开发者往往 ...

  5. 深度剖析Android IPC原理

    /   今日科技快讯   / 最近俄罗斯技术开发者正在开发一个可以取代谷歌应用商店的国内应用商店NashStore,计划于5月9日发布上线. YouTube和谷歌应用商店本月暂停在俄罗斯市场的所有付费 ...

  6. android开机动画制作与播放原理简介

    android开机动画制作与播放原理简介 谁都想拥有一个华丽漂亮的开机动画,这让人心情舒畅,android是怎么来实现的?怎么制作一个自己的开机动画?这里揭开android开机动画的神秘面纱.

  7. Binder跨进程通信原理(三):Binder IPC实现原理

    1. 动态内核可加载模块 && 内存映射 正如上一章所说, 跨进程通信是需要内核空间做支持的. 传统的 IPC 机制如 管道, Socket, 都是内核的一部分, 因此通过内核支持来实 ...

  8. Android 插件化原理解析——Activity生命周期管理

    之前的 Android插件化原理解析 系列文章揭开了Hook机制的神秘面纱,现在我们手握倚天屠龙,那么如何通过这种技术完成插件化方案呢?具体来说,插件中的Activity,Service等组件如何在A ...

  9. Android代码入侵原理解析(一)

    Original 2017-05-06 付超红 滴滴安全应急响应中心 2017年初,在滴滴安全沙龙上,滴滴出行安全专家--付超红,针对App的攻与防进行了分享.会后大家对这个议题反响热烈,纷纷求详情求 ...

最新文章

  1. Web应用安全审计工具WATOBO
  2. SpringBoot中通过重写WebMvcConfigurer的方法配置静态资源映射实现图片上传后返回网络Url
  3. jquery的$.extend和$.fn.extend作用及区别(—)
  4. Java中集合(六)Queue
  5. linux虚拟机桥接网络配置
  6. 对永磁无刷电机的调速过程
  7. sqlserver55555_sqlserver把小数点后面多余的0去掉
  8. STL中vector和list的区别
  9. c#中datagridview里checkbox的使用方法[转]
  10. SpringMVC介绍
  11. unity开发android游戏
  12. unity connect mysql_unity连接mysql
  13. Honeyview 漫画阅读器 v5.30.zip 下载
  14. windows下创建多个回收站
  15. python文件双击闪退_解决python文件双击运行秒退的问题
  16. DEVC报错[Error] expected initializer before '.'
  17. 微信小程序(1)新闻小应用代码以及总结小程序一些知识点
  18. 短信验证码是什么?在网站中起到什么作用?
  19. 数据库编程:存储过程和储存函数
  20. java 安全警告 关闭_关闭安全警告的两种方法

热门文章

  1. gcc编译的几个重要参数
  2. 数据结构专题二:2.6链表删除结点
  3. 连接串口_气压计PTB330连接HMI串口屏
  4. mac mysql not found_mac版mysql安装后显示mysql: command not found咋整?
  5. curlopt_ssl_verifypeer后https还是验证不过_为什么 HTTPS 需要 7 次握手和 9 倍时延
  6. SQLAlchemy schema.Column
  7. Pandas to_string
  8. 云服务器 ECS > 安全 > 实例RAM角色 > 概述
  9. Spring学习总结(12)——Druid连接池及监控在spring配置
  10. 怎么退出mysql_如何安全地关闭MySQL