Android Binder跨进程与非跨进程的传输异同源码分析

Android4.0 Bitmap Parcel传输源码分析 很久之前就看到有网友遇到用Parcel传Bitmap的时候,会遇到因为图片太大而报错,都在讨论传输Bitmap的时候的大小限制,但是实际上应该只有在4.0之前会有限制,4.0之后图片传输采用了Blob来传输,最终会使用ashmem来传递占用内存大的数据。下面分别介绍4.0前后Parcel对图片传输的异同。 阅读详情

前两天看到Service的onBind函数返回值的三种情况(扩展Binder对象,Messenger,aidl),一直在想他们内部的实现有什么不一样的,网上很多文章都介绍了Service的绑定过程,但是并没有介绍对于跨进程与非跨进程,对于不同的返回值,其具体有什么区别,以及具体是怎么实现的。这篇文章就根据源码分析Android究竟是在哪部分来控制跨进程与非跨进程Binder的传输的,Binder究竟是怎么传输的。

首先看一下Service的绑定中,Binder跨进程与非跨进程的区别代码中的体现。

Service跨进程绑定与非跨进程绑定的表象

我们都知道如果使用aidl跨进程在onServiceConnection函数中需要这样使用new TestInterface.Stub.Proxy(binder)(TestInterface是aidl声明的接口,并且在Service的onBind中返回new TestInterface.Stub())。如果是普通扩展Binder对象,那么直接强制类型转换就可以了TestBinder(binder)(TestBinder是Binder子类,并且在Service的onBind中返回new TestBinder())。如果想知道onServiceConnection的参数binder具体是怎么样的,其实直接在ServiceConnection的onServiceConnection回调函数中,以及在Service的onBind中分别打印一下Binder就好了,如下代码

//Service onBind
public Binder onBind(){
    Binder binder ; //这里并没有初始化,可以使扩展Binder,也可以是aidl的Stub
    Log.i("TestLog","onBind: "+binder.hashCode()+","+binder.toString());
    return binder;
}

//ServiceConnection
ServiceConnection connection = new ServiceConnection(){
    public void onServiceConnection(ComponentName component, Binder binder){
        Log.i("TestLog","onServiceConnection: "+binder.hashCode()+","+binder.toString());//跨进程时会binder是一个BinderProxy对象,非跨进程时跟onBind返回的对象一模一样。
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

通过设置Service的android:process,可以将Service远程进程与相同进程进行实验。

通过log可以发现,如果是跨进程的话,在ServiceConnection的onServiceConnection返回的结果是一个BinderProxy对象,与onBind中打印的结果是不一致的。如果是非跨进程,在onServiceConnection与onBind的打印的内容是一模一样的。使用aidl跨进程与非跨进程,使用扩展Binder非跨进程都是一样的。这里跟onBind是返回扩展Binder,返回扩展aidl自动产生Stub类还是Messenger是没有关系的,只跟是否跨进程有关。

从这里可以看出,如果是跨进程则返回BinderProxy,非跨进程则返回原来在onBind中返回的对象。我困惑的问题是,Android是怎么实现这个根据不同的进程来返回不同的结果呢?因为是Service绑定,所以先看一下Service的绑定过程。

Service绑定过程

绑定过程

首先简单介绍一下Service的绑定过程: 
1. Activity通过bindService请求绑定相应的Service(由Intent指定),并设置了ServiceConnection回调接口。 
2. 实际上会调用ContextImpl.bindService,然后调用ActivityManagerNative.getDefault().bindService,也就是通过IPC调用ActivityManagerService的bindService函数,之后会调用ActiveServices的bindServiceLocked。 
3. 在bindServiceLocked中,先通过retrieveServiceLocked创建ServiceRecord,这个就是对应着我们想要绑定的Service。 
4. 然后根据结果创建ConnectionRecord,将ServiceConnection保存在这里面。 
5. 判断指定的Service是否启动,如果没有则启动服务,通过ApplicationThreadProxy(app.thread)远程调用ApplicationThread的scheduleCreateService来发送Message给主线程消息循环(ActivityThread,ActivityThread.H),启动服务。如果是远程进程服务,ActivityManagerService会开一个新的进程(startProcessLocked),然后将Service运行在新的Process中。 
6. 绑定服务,通过ApplicationThreadProxy(app.thread)远程调用ApplicationThread的scheduleBindService来发送Message给主线程消息循环(ActivityThread,ActivityThread.H),绑定服务(handleBindService)。这里看一下源码:

try {
    if (!data.rebind) {
        IBinder binder = s.onBind(data.intent);
        ActivityManagerNative.getDefault().publishService(
            data.token, data.intent, binder);
    } else {
        s.onRebind(data.intent);
        ActivityManagerNative.getDefault().serviceDoneExecuting(
            data.token, 0, 0, 0);
    }
    ensureJitEnabled();
} catch (RemoteException ex) {
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  1. 在绑定服务中,调用Service的onBind方法得到Binder,并且通过ActivityManagerNative.getDefault().publishService发布服务。
  2. 在publishServcie中使用ServiceRecord获取ConnectionRecord,最终调用保存在ConnectionRecord里面的ServiceConnection的onServiceConnection函数。其中ServiceRecord,ServiceConnection都是Binder类,可以通过IPC访问。

这里只是简单介绍一下绑定Service的流程,也并没有把具体对象名称写出来。如果想要更详细的了解整个过程可以去看一下老罗的博客,其实最好是照着别人的博客看一下源码。

绑定过程是否处理了不同onBind返回值

我一直觉得是在绑定服务的过程中会针对不同的onBind返回值有不同的处理,但实际上并没有。看第6步中的源码就知道了,只是把onBind的返回值当作IBinder来看。另外上面的绑定过程中,并没有针对不同的IBinder类型进行特殊处理。我想实际上在onBind函数中返回一个BinderProxy也是OK的。

既然绑定过程中没有对不同进程进行处理,那么只能看看更底层的传输过程了,Android中传输都是用Parcel类型。所以只能看看Parcel传输数据的过程中是否有特殊处理。而实际上确实是在Parcel中有体现出来了跨进程与非跨进程的区别。下面看看Parcel读取和写入Binder。

Parcel中写入读取Binder

看一下Parcel关于Binder的接口:

public final void writeStrongBinder(IBinder val) {
    nativeWriteStrongBinder(mNativePtr, val);
}
public final IBinder readStrongBinder() {
    return nativeReadStrongBinder(mNativePtr);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

Parcel写入Binder

Parcel写入Binder是通过writeStrongBinder的,Java层的nativeWriteStrongBinder是一个native函数,对应的JNI实现为:

static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)
{
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != NULL) {
        const status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object));
        if (err != NO_ERROR) {
            signalExceptionForError(env, clazz, err);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

其中ibinderForJavaObject是将java层的对象转换成native层的Binder对象,实际上对应的是JavaBBinder。

然后native层的parcel会调用它的writeStrognBinder:

status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
{
    return flatten_binder(ProcessState::self(), val, this);
}
  • 1
  • 2
  • 3
  • 4

最终调用flatten_binder:

status_t flatten_binder(const sp<ProcessState>& /*proc*/,
    const sp<IBinder>& binder, Parcel* out)
{
    flat_binder_object obj;

    obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
    if (binder != NULL) {
        IBinder *local = binder->localBinder();   //JavaBBinder返回的是this,也就是自己
        if (!local) {
            BpBinder *proxy = binder->remoteBinder();
            if (proxy == NULL) {
                ALOGE("null proxy");
            }
            const int32_t handle = proxy ? proxy->handle() : 0;
            obj.type = BINDER_TYPE_HANDLE;
            obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
            obj.handle = handle;
            obj.cookie = 0;
        } else {// 写入JavaBBinder将会对应这一段。
            obj.type = BINDER_TYPE_BINDER;
            obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
            obj.cookie = reinterpret_cast<uintptr_t>(local);   //把对应的JavaBBinder的指针转换成整形uintptr_t
        }
    } else {
        obj.type = BINDER_TYPE_BINDER;
        obj.binder = 0;
        obj.cookie = 0;
    }

    return finish_flatten_binder(binder, obj, out); //finish_flatten_binder是将obj写入到out里面。
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

这里就根据是本地binder还是远程binder,对Binder的写入采取了两种不同的方式。Binder如果是JavaBBinder,则它的localBinder会返回localBinder,如果是BpBinder则localBinder会为null。我们写入的时候,ibinderForJavaObject就返回的是JavaBBinder。flat_binder_object是Binder写入的时候的对象,它对应着Binder。handle表示Binder对象在Binder驱动中的标志,比如ServiceManager的handle为0。type表示当前传输的Binder是本地的(同进程),还是一个proxy(跨进程)。binder,cookie保存着Binder对象的指针。finish_flatten_binder会将obj写入到out里面,最终写入到Binder驱动中,写入部分也就完了。从上面的流程可以看出,在写入的时候,Parcel会针对不同的Binder(BBinder/JavaBBinder,BpBinder)有不同的处理,而他们确实就是对应着跟Service端Binder是同一个进程的还是不同进程的情况。

Parcel读取Binder

接下来就是读取了,同样的,Java层的Parcel也是通过native函数来读取的。在这里我们从最底层开始分析,首先从unflatten_binder开始:

unflatten_binder:

status_t unflatten_binder(const sp<ProcessState>& proc,
    const Parcel& in, sp<IBinder>* out)
{
    const flat_binder_object* flat = in.readObject(false);

    if (flat) {
        switch (flat->type) {
            case BINDER_TYPE_BINDER:
                *out = reinterpret_cast<IBinder*>(flat->cookie);
                return finish_unflatten_binder(NULL, *flat, in);
            case BINDER_TYPE_HANDLE:
                *out = proc->getStrongProxyForHandle(flat->handle);
                return finish_unflatten_binder(
                    static_cast<BpBinder*>(out->get()), *flat, in);
        }
    }
    return BAD_TYPE;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

首先从Binder驱动中读取一个flat_binder_object—flat。flat的处理是关键,它会根据flat->type的值分别处理,如果是BINDER_TYPE_BINDER,则使用cookie中的值强制转换成指针。如果是BINDER_TYPE_HANDLE,则使用Proxy,getStringProxyForHandle会返回BpBinder。而调用unflatten_binder的是native层的Parcel的readStringBinder。

sp<IBinder> Parcel::readStrongBinder() const
{
    sp<IBinder> val;
    unflatten_binder(ProcessState::self(), *this, &val);
    return val;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

调用readStrongBinder的是jni函数的实现:

static jobject android_os_Parcel_readStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr)
{
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != NULL) {
        return javaObjectForIBinder(env, parcel->readStrongBinder());
    }
    return NULL;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

javaObjectForIBinder与ibinderForJavaObject相对应,把IBinder对象转换成对应的Java层的Object。这个函数是关键。看看它的实现:

jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val)
{
     if (val == NULL) return NULL;

     if (val->checkSubclass(&gBinderOffsets)) {  //如果是本地的,那么会直接进入这部分代码,因为这个val是写入的时候同一个对象,gBinderOffsets也是一致。如果val是一种proxy对象,则不然,会继续往下执行找到一个Proxy对象。
         // One of our own!
         jobject object = static_cast<JavaBBinder*>(val.get())->object();
         LOGDEATH("objectForBinder %p: it's our own %p!\n", val.get(), object);
         return object;
     }

     // For the rest of the function we will hold this lock, to serialize
     // looking/creation of Java proxies for native Binder proxies.
     AutoMutex _l(mProxyLock);

     // Someone else's...  do we know about it?
     jobject object = (jobject)val->findObject(&gBinderProxyOffsets);
     if (object != NULL) {
        jobject res = jniGetReferent(env, object);
        if (res != NULL) {
            ALOGV("objectForBinder %p: found existing %p!\n", val.get(), res);
            return res;
        }
        LOGDEATH("Proxy object %p of IBinder %p no longer in working set!!!", object, val.get());
        android_atomic_dec(&gNumProxyRefs);
        val->detachObject(&gBinderProxyOffsets);
        env->DeleteGlobalRef(object);
    }

    object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor);//gBinderProxyOffsets保存的是Java层BinderProxy的信息,这里也是创建BinderProxy。
    if (object != NULL) {
        LOGDEATH("objectForBinder %p: created new proxy %p !\n", val.get(), object);
        // The proxy holds a reference to the native object.
        env->SetLongField(object, gBinderProxyOffsets.mObject, (jlong)val.get());
        val->incStrong((void*)javaObjectForIBinder);

        // The native object needs to hold a weak reference back to the
        // proxy, so we can retrieve the same proxy if it is still active.
        jobject refObject = env->NewGlobalRef(
                env->GetObjectField(object, gBinderProxyOffsets.mSelf));
        val->attachObject(&gBinderProxyOffsets, refObject,
                jnienv_to_javavm(env), proxy_cleanup);

        // Also remember the death recipients registered on this proxy
        sp<DeathRecipientList> drl = new DeathRecipientList;
        drl->incStrong((void*)javaObjectForIBinder);
        env->SetLongField(object, gBinderProxyOffsets.mOrgue, reinterpret_cast<jlong>(drl.get()));

        // Note that a new object reference has been created.
        android_atomic_inc(&gNumProxyRefs);
        incRefsCreated(env);
    }

    return object;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
//IBinder的checkSubclass。
checkSubclass(const void* subclassID) const
{
    return subclassID == &gBinderOffsets;
}
  • 1
  • 2
  • 3
  • 4
  • 5

上面的处理中,如果是跟Service是同一个进程,也就是val是JavaBBinder。那么在checkSubclass中,它所包含的gBinderOffsets指针与参数传入的gBinderOffsets的指针必然是同一个值,则满足if条件。直接将指针强制转换成JavaBBinder,返回对应的jobject。如果是不同的进程,那么val也就会是BpBinder,最终会返回一个BinderProxy。不同的进程这一部分网上很多介绍Binder的文章都介绍了,可以参阅老罗或邓凡平的书。

Parcel读取写入总结

上面介绍了Parcel整个写入读取的流程,最后代替Binder传输的是flat_binder_object。在native的Parcel中,根据跨进程还是非跨进程,flat_binder_object的值是不一样的: 
1. 跨进程的时候flat_binder_object的type为BINDER_TYPE_HANDLE, 
2. 非跨进程的时候flat_binder_object的type为BINDER_TYPE_BINDER。

在这里已经可以发现跨进程与非跨进程的时候传输的数据的区别了。客户端的Parcel在读取Binder的时候,根据是flat_binder_object的type的值进行区分对待,返回不同的内容。而写入的时候也是一样的,根据是否是Proxy,来决定写入HANDLE还是BINDER。

最终这些内容都会通过ioctl与Binder驱动进行数据通信。所以最终处理不同进程之间的Binder数据传输处理的也只能是Binder驱动了。

Binder驱动的处理

关于如何判断读取的时候是跟Service同一个进程还是不同的进程,脑子里根据操作系统的知识想想基本就能够想到怎么去实现了。因为每个进程进行系统调用陷入内核的时候,内核的当然是可以知道当前进入内核空间的进程的信息了啦,这样就可以判断当前请求读取信息的是跟Service同一个进程还是不同的进程了。

实际上Binder驱动保存着Service端的Binder地址和handle的信息,将两者相互映射,根据不同的服务端进程和客户端进程来区别处理。这篇文章详细介绍了Binder驱动深入分析Android Binder 驱动。

总结

实际上真正控制跨进程与非跨进程返回Binder类型的,不是绑定服务的过程,而是Binder驱动。如果是与Service端同一个进程则返回Binder(在底层是Binder指针),如果是不同的进程则返回handle。Binder对象传入Binder驱动最底层是转化为flat_binder_object对象传递的。Parcel是根据从驱动中读取的数据作出不同的处理,如果从Binder驱动中读出的flat_binder_object的type为BINDER_TYPE_HANDLE,则创建BpBinder,在JAVA层创建BinderProxy返回,如果读出的flat_binder_object的type为BINDER_TYPE_BINDER则直接使用cookie的指针,将它强制转化为JavaBBinder,在JAVA层为原来Service的Binder对象(相同进程)。

这样每次从Binder驱动中读取IBinder对象,都会这样:不同进程则用handle,同进程则用Service端的Binder地址。每个Binder对象的接口参数里面可能还有Binder变量,但同样按照上面的方式传输。其实如果是handle的话,通信就需要通过Binder驱动去做。

虽然是从Service的绑定过程中想到这个问题,但是Service的绑定过程并没有针对跨进程与非跨进程对Binder传输有过处理,所以Android中任何地方的Binder跨进程与非跨进程的传输都是这样的。从Binder驱动读取Binder对象的时候,Binder驱动判断请求的进程与Service端的进程是否相同,如果相同则Binder驱动传出的数据为Service端Binder的指针(flat_binder_object的type为BINDER),如果不同则Binder驱动传出的数据为Service端Binder的handle(flat_binder_object的type为HANDLE)。

Android-Binder机制及AIDL使用,终局之战 没有稳定的工作,只有稳定的能力。 又到了万物复苏的季节,在程序猿这个行当里,作为 Android 开发出生的,在经历了八年的脱发生涯后,有了越来越多的想法和感触 趋势 随着各类移动跨平台的兴起,在 ReactNative 、Flutter 、Weex 等框架的加持下,Android 开发的能力已不再局限于 Android 领域,而各大厂商近些年也针对跨平台的不足进行逐步完善: ReactNaitve 底层重构的新架构 Fabric,将管理器等往 C++ 迁移实现同步操作和性能优化; Flutter 1. 阅读详情

相关推荐

遥感大模型的深度学习从CNN到Vision Transformer以及混合架构的演进之路

深度学习,特别是以卷积神经网络(CNN)和Transformer为代表的“大模型”技术,正以其强大的表征学习能力,深刻地重塑着遥感智能解译的整个技术栈与认知范式。本文将系统性地梳理遥感大模型从局部卷积的奠基,到全局注意力的革命,再到二者融合的混合架构演进这一波澜壮阔的历程。我们将深入剖析每个阶段的核心技术原理,结合遥感数据特性与实际应用案例,详细阐述其在分类、检测、分割等核心任务中的具体应用。

panshengwu的博客 1139

Service的绑定中,Binder跨进程与非跨进程的区别

客户端在执行bindService的时候,成功绑定服务之后,会回调MyConnection的onServiceConnected(),并且传回了服务端的通信接口抽象类Stub,抽象类Stub是继承自Binder对象,Binder对象是IBinder的实现类。 此IBinder即服务onBind()时返回的IBinder。测试在ServiceConnection的onServiceConnect

youyoulanyouyou的博客 2270

wifi/BT芯片RTL8723DS驱动移植和测试之一wifi篇

本文主要介绍如何在stm32mp157中移植并测试芯片RTL8723DS的wifi功能。

汉尼拔勇闯天涯 8741

Android跨进程通信:图文详解 Binder机制 原理

前言 如果你接触过 跨进程通信 (IPC),那么你对Binder一定不陌生 虽然 网上有很多介绍 Binder的文章,可是存在一些问题:浅显的讨论Binder机制 或 一味讲解 Binder源码、逻辑不清楚,最终导致的是读者们还是无法形成一个完整的Binder概念 本文采用 清晰的图文讲解方式,按照 大角度 -&gt; 小角度 去分析Binder,即: 先从 机制、模型的角度 去分析 整个B...

Carson带你学Android 14万+

TOD和PPS精确时间同步技术

授时

u010783226的专栏 1万+

OctoMag: An Electromagnetic System for 5-DOF Wireless Micromanipulation

电磁线圈组驱动经典系统,可以作为参考。 OctoMag: 用于5自由度无线微操作的一个电磁铁系统 OctoMag: An Electromagnetic System for 5-DOF Wireless Micromanipulation [1] Paper Link Authors: J. J. Abbott, etc. 2010, TRO IEEE Transactions on Robot...

XU Yangxin的博客 1665

Activty与Service通信的所有情况(相同进程、不同进程)

一、使用Messenger    上面的方法只能在同一个进程里才能用,如果要与另外一个进程的Service进行通信,则可以用Messenger。     其实实现IPC(Inter-Process Communication,进程间通信)的方式,还有AIDL,但推荐使用Messenger,有两点好处:       1. 使用Messenger方式比使用AIDL的方式,实现起来要简

会一切 1058

全能媒体工具深度测评(下篇一):AI图文生成+全能解压+图片去文字,一键搞定素材精修

解压50多种格式,文生图,图生图

cs821984831的博客 40

Android-Binder机制及AIDL使用,已拿offer

前言 一个Android开发的朋友跟我谈他面试经历,上周去爱奇艺面试,其他问题都答得不错,面试官问了一个哈夫曼算法的题没答出来,后来面试官很明确的说,我们还是想找一个会些算法基础的。 如果之前有学过数据结构和算法,建议大家不定时的去刷刷算法题 因为从面试的角度来讲,目前 BAT 和 TMD 等一线互联网企业或多或少都会有几个算法题,而对应届毕业生来说,算法的要求度则更高!这里可以分享一下我收集整理的数据结构算法题含答案。 Android 相关 1. Android 之 SharedPreferences

m0_56146626的博客 181

uniapp - 解决苹果ios真机运行报错:错误:请查看是否设备未加入到证书列表或者确认证书类型是否匹配(HbuilderX运行IOS手机出现问题,uniapp运行到IOS真机提示请查看是否设备未)

uni-app,ios苹果,App平台端,uniapp运行到IOS真机提示错误:请查看是否设备未加入到证书列表或者确认证书类型是否匹配,UUID在哪里,uniApp运行到ios真机报错解决,安装自定义基座App失败,没找到有计划资源,证书、标识符和描述文件,证书标识符和描述文件下面没有选项怎么回事,HBuilder真机调试报错请查看是否设备未加入到证书列表或者确认证书类型是否匹配,UniApp运行到IOS真机提示,授权的设备才可以直接安装基座,添加调试的IOS设备,在Devices中,添加手机设备UUID

🏆 前端领域优质创作者 1万+

39、信号流图:解决线性代数方程组的图论方法

本文介绍了信号流图理论在求解线性代数方程组中的应用,重点阐述了Coates方法和Mason方法的原理、步骤及对比分析。通过图论视角,将矩阵运算转化为对有向图结构的分析,利用1-因子、1-阶连接、前向路径和反馈回路等概念,推导出方程组解的拓扑公式。文章结合具体实例演示两种方法的计算过程,并总结其在电路分析、控制系统和通信系统等领域的拓展应用,展示了信号流图理论的直观性与有效性。

pear55的博客 88

android移动开发基础题库,安卓面试问题和答案

好了,今天的分享就到这里,如果你对在面试中遇到的问题,或者刚毕业及工作几年迷茫不知道该如何准备面试并突破现状提升自己,对于自己的未来还不够了解不知道给如何规划,可以来看看同行们都是如何突破现状,怎么学习的,来吸收他们的面试以及工作经验完善自己的之后的面试计划及职业规划。,主要还是希望大家在如今大环境不好的情况下面试能够顺利一点,希望可以帮助到大家~(img-HgeE21X0-1713734991739)]这里放上一部分我工作以来以及参与过的大大小小的面试收集总结出来的一套。

2401_84408418的博客 841

CAN轴控制流【6041,6040】

【代码】CAN轴控制流【6041,6040】

cfqq1989的博客 1783

Android原生开发如何深入进阶?小白也能看明白

开头 最近有粉丝反应,不想做安卓了,有朋友转到前端了,安卓不行了,问我怎么办? 自从RN,Weex这种跨平台编程语言出来以后,安卓将死的言论总是不绝于耳。随着颇有摧枯拉朽之势Flutter的出现,加之近几年的裁员潮,使这种论调好像更加可信。 恍惚间会让人想到2010年,安卓初兴,一切还是桌面应用开发的天下,当时的语言还集中中JAVA、HTML、MFC、.NET、C#,各个语言为了争夺桌面应用开发第一位,也是拼得你死我活。当时的安卓系统还是方兴未艾,谁也不知道,它会引领着另一个时代。 几年过去了,桌面开发已经

m0_56175058的博客 245

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

Android-Binder机制及AIDL使用,进阶学习

一、图片 1、图片库对比 2、LRUCache原理 3、图片加载原理 4、自己去实现图片库,怎么做? 5、Glide源码解析 6、Glide使用什么缓存? 7、Glide内存缓存如何控制大小? 二、网络和安全机制 1.网络框架对比和源码分析 2.自己去设计网络请求框架,怎么做? 3.网络请求缓存处理,okhttp如何处理网络缓存的 4.从网络加载一个10M的图片,说下注意事项 5.TCP的3次握手和四次挥手 6.TCP与UDP的区别 7.TCP与UDP的应用 8.HTTP协议 9.HTTP1.0与2.0的区

m0_57205294的博客 312

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Android端简单易用的SPI框架,社招面试心得

前言 今年是转折的一年,很多学android开发的小伙伴失业了,虽找到了一份工作,但高不成低不就,下半年金九银十有想法更换一份工作,很多需要大厂面试经验和大厂面试真题的小伙伴,想提前准备刷下题。接下来分享一份我的字节跳动、阿里巴巴、百度、小米等大厂面试经验和总结。(文末附真题解析大全) Android 相关 1. Android 之 SharedPreferences 内部原理浅析 2. Android 源码分析-消息队列和 Looper 3. Android 源码分析—带你认识不一样的 AsyncTask

m0_56169789的博客 168

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

2024Android笔试题总结,android组件化和插件化

看到这篇文章的人不知道有多少是和我一样的Android程序员。35岁,这是我们这个行业普遍的失业高发阶段,这种情况下如果还不提升自己的技能,进阶发展,我想,很可能就是本行业的职业生涯的终点了。我们要有危机意识,切莫等到一切都成定局时才开始追悔莫及。只要有规划的,有系统地学习,进阶提升自己并不难,给自己多充一点电,你才能走的更远。千里之行始于足下。这是上小学时,那种一元钱一个的日记本上每一页下面都印刷有的一句话,当时只觉得这句话很短,后来渐渐长大才慢慢明白这句话的真正的含义。

2401_84408814的博客 1034

2579页阿里P8Android学习笔记在互联网上火了,完整版开放下载(1)

这里我希望可以帮助到大家提升进阶。Android学习PDF+架构视频+面试文档+源码笔记高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料这几块的内容。非常适合近期有面试和想在技术道路上继续精进的朋友。喜欢本文的话,不妨给我点个小赞、评论区留言或者转发支持一下呗~《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

2401_84132544的博客 610

Android面试超详细知识点!音视频学习指南来咯,大厂内部资料_android 音视频开发 面试点总结

本部分内容是关于Android进阶的一些知识总结,涉及到的知识点比较杂,不过都是面试中几乎常问的知识点,也是加分的点。在面试的过程中,结合具体自身实践经历,才能更加深入透彻的描绘出来。14.说一款你认为当前比较火的应用并设计(比如:直播APP,P2P金融,小视频等)12.说说EventBus作用,实现方式,代替EventBus的方式。8.用到的一些开源框架,介绍一个看过源码的,内部实现过程。11.RxJava的作用,与平时使用的异步操作来比的优缺点。7.适配器模式,装饰者模式,外观模式的异同?

2401_89722475的博客 647

Android开发最佳实践手册全网独一份,附大厂真题面经

前言 在去年10月份的时候入职了一家外企,恰逢最近几周公司大规模招聘Android开发,有幸参与了十几场面试,遂有感而发。在其中也从面试官的方面有了一些想法,希望分享给大家。 本来当做面试官对于我来说是一件比较新奇的事,但通过几场面试下来,感觉自己对面试有了更为立体的感觉。希望下面的分享能给大家带来一点点的帮助。以下是正文: 首先说一下感想,在一个整个的面试过程中其实有几个关键的点,关键的点掌握的好有很大的几率通过面试 Android 相关 1. Android 之 SharedPreferences 内部

m0_56175058的博客 144

安卓ndk开发!Android开发核心知识笔记共2100页,顺利通过阿里Android岗面试

并发编程之线程的中止 万事万物有始就有终,当我们新启了一个线程后,线程自然也有结束的那一刻。一般来说,线程如果是run方法执行完成了,或者是抛出了一个未处理的异常导致线程提前结束,我们都称为线程的自然终止。对于我们来说,如何才能安全自行中止我们的线程呢? 一、图片 1、图片库对比 2、LRUCache原理 3、图片加载原理 4、自己去实现图片库,怎么做? 5、Glide源码解析 6、Glide使用什么缓存? 7、Glide内存缓存如何控制大小? 二、网络和安全机制 1.网络框架对比和源码分析 2.自己去设计

CHAMPION8888的博客 136

androidstudio开发实战pdf,android界面开发经典书籍

在这里小编整理了一份Android大厂常见面试题,和一些Android架构视频解析,都已整理成文档,全部都已打包好了,希望能够对大家有所帮助,在面试中能顺利通过。喜欢本文的话,不妨顺手给我点个小赞、评论区留言或者转发支持一下呗《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

asvxc324deas的博客 1097

Android开发社招面试总结,最新阿里Android高级面试题及答案

深入理解Flutter应用启动**

2401_85030470的博客 394

android游戏开发大全第四版,,干货分享

1. Android 之 SharedPreferences 内部原理浅析 2. Android 源码分析-消息队列和 Looper 3. Android 源码分析—带你认识不一样的 AsyncTask 4. Android 性能优化之使用线程池处理异步任务 5. Android Context 完全解析,你所不知道的 Context 的各种细节 6. LocalBroadcastManager 的实现原理,与 BroadcastReceiver 异同 7. Android进程间通信(IPC)机制Binde

2401_83642079的博客 903
上一篇: Dagger 2 完全解析(六),dagger.android 扩展库的使用
下一篇: Prefetch Text Layout in RecyclerView
candyguy242
博客等级 码龄17年 63粉丝 37原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值