Android Activity是怎么画出来的

Android画矩形小程序 Android 画图 阅读详情

Activity是在onResume里显示出来的,下面看下具体的流程。

 

ActivityThread.java

    final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {

// ...
           if (r.window == null && !a.mFinished && willBeVisible) {
                r.window = r.activity.getWindow();
                View decor = r.window.getDecorView();
                decor.setVisibility(View.INVISIBLE);
                ViewManager wm = a.getWindowManager();
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
                if (a.mVisibleFromClient) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l); //关键
                }
//...

}

关键的函数是wm.addView,这个wm是啥呢?

ViewManager.java中可以看到,就是一接口,那肯定要找到实现他的类,搜索下发现是WindowManager.java,可是这货里也没有具体的,继续找实现Windomanager的家伙。

最后可以找到实际是WindowManagerImpl.java实现了这个方法。

WindowManagerImpl.java

  

  private void addView(View view, ViewGroup.LayoutParams params, boolean nest)
    {
    //...


            root = new ViewRoot(view.getContext());
            root.mAddNesting = 1;

            view.setLayoutParams(wparams);
            
            if (mViews == null) {
                index = 1;
                mViews = new View[1];
                mRoots = new ViewRoot[1];
                mParams = new WindowManager.LayoutParams[1];
            } else {
                index = mViews.length + 1;
                Object[] old = mViews;
                mViews = new View[index];
                System.arraycopy(old, 0, mViews, 0, index-1);
                old = mRoots;
                mRoots = new ViewRoot[index];
                System.arraycopy(old, 0, mRoots, 0, index-1);
                old = mParams;
                mParams = new WindowManager.LayoutParams[index];
                System.arraycopy(old, 0, mParams, 0, index-1);
            }
            index--;

            mViews[index] = view;
            mRoots[index] = root;
            mParams[index] = wparams;
        }
        // do this last because it fires off messages to start doing things
        root.setView(view, wparams, panelParentView);


}


如果不深究里面每一句话的意思,大体上看来就是new了一个ViewRoot,然后把各种参数传入,调用它的setView方法。

这里我们有必要看一下ViewRoot,这货虽然看上去是个View穷屌丝,其实是个高帅富:

ViewRoot.java

public final class ViewRoot extends Handler implements ViewParent,
        View.AttachInfo.Callbacks {

看这声明,原来本体就是个handle,实现了一些方法。

看下里面的成员变量,比较关键的是

    static IWindowSession sWindowSession;

    private final Surface mSurface = new Surface();
看看他的构造函数:

    public ViewRoot(Context context) {
        super();

        if (MEASURE_LATENCY && lt == null) {
            lt = new LatencyTimer(100, 1000);
        }

        // For debug only
        //++sInstanceCount;

        // Initialize the statics when this class is first instantiated. This is
        // done here instead of in the static block because Zygote does not
        // allow the spawning of threads.
        getWindowSession(context.getMainLooper());
        
        mThread = Thread.currentThread();
        mLocation = new WindowLeaked(null);
        mLocation.fillInStackTrace();
        mWidth = -1;
        mHeight = -1;
        mDirty = new Rect();
        mTempRect = new Rect();
        mVisRect = new Rect();
        mWinFrame = new Rect();
        mWindow = new W(this, context);
        mInputMethodCallback = new InputMethodCallback(this);
        mViewVisibility = View.GONE;
        mTransparentRegion = new Region();
        mPreviousTransparentRegion = new Region();
        mFirst = true; // true for the first time the view is added
        mAdded = false;
        mAttachInfo = new View.AttachInfo(sWindowSession, mWindow, this, this);
        mViewConfiguration = ViewConfiguration.get(context);
        mDensity = context.getResources().getDisplayMetrics().densityDpi;
    }


通过getWindowSession方法得到了Session的本地代理IWindowSession,并保存在变量sWindowSession中

  

    public static IWindowSession getWindowSession(Looper mainLooper) {
        synchronized (mStaticInit) {
            if (!mInitialized) {
                try {
                    InputMethodManager imm = InputMethodManager.getInstance(mainLooper);
                    sWindowSession = IWindowManager.Stub.asInterface(
                            ServiceManager.getService("window"))
                            .openSession(imm.getClient(), imm.getInputContext());
                    mInitialized = true;
                } catch (RemoteException e) {
                }
            }
            return sWindowSession;
        }
    }


这里简单的说下,

IWindowManager.Stub.asInterface(
                            ServiceManager.getService("window"));

就是通过ServiceManager得到windowManagerService的本地代理,并通过这个本地代理来调用远程WMS的方法openSession,返回Session对应的本地代理对象IWindowSession。

而 一开始的    private final Surface mSurface = new Surface();调用了无参的构造函数,实际上没有干啥活,当前的surface是无效的。

    public Surface() {
        if (DEBUG_RELEASE) {
            mCreationStack = new Exception();
        }
        mCanvas = new CompatibleCanvas();
    }

接着继续看setView();

    public void setView(View view, WindowManager.LayoutParams attrs,
            View panelParentView) {
//...
                requestLayout();
                mInputChannel = new InputChannel();
                try {
                    res = sWindowSession.add(mWindow, mWindowAttributes,
                            getHostVisibility(), mAttachInfo.mContentInsets,
                            mInputChannel);
                } catch (RemoteException e) {
                    mAdded = false;
                    mView = null;
                    mAttachInfo.mRootView = null;
                    mInputChannel = null;
                    unscheduleTraversals();
                    throw new RuntimeException("Adding window failed", e);
                } finally {
                    if (restore) {
                        attrs.restore();
                    }
                }
//...
}


requestLayout()和sWindowSession.add方法是最关键的。我们分别来看。

requestLayout只是给ViewRoot的handle发送DO_TRAVERSAL消息,handle收到消息后,会调用performTraversals()

这个方法巨长无比,简直不忍直视,我们主要是看和surface的联系,就挑着看。

    private void performTraversals() {
      relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
      draw(fullRedrawNeeded);
}

 

 

 

看看relayoutWindow(),这个方法里对surface有了重新的赋值:

    private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
            boolean insetsPending) throws RemoteException {

        float appScale = mAttachInfo.mApplicationScale;
        boolean restore = false;
        if (params != null && mTranslator != null) {
            restore = true;
            params.backup();
            mTranslator.translateWindowLayout(params);
        }
        if (params != null) {
            if (DBG) Log.d(TAG, "WindowLayout in layoutWindow:" + params);
        }
        mPendingConfiguration.seq = 0;
        //Log.d(TAG, ">>>>>> CALLING relayout");
        int relayoutResult = sWindowSession.relayout(
                mWindow, params,
                (int) (mView.mMeasuredWidth * appScale + 0.5f),
                (int) (mView.mMeasuredHeight * appScale + 0.5f),
                viewVisibility, insetsPending, mWinFrame,
                mPendingContentInsets, mPendingVisibleInsets,
                mPendingConfiguration, mSurface);
        //Log.d(TAG, "<<<<<< BACK FROM relayout");
        if (restore) {
            params.restore();
        }
        
        if (mTranslator != null) {
            mTranslator.translateRectInScreenToAppWinFrame(mWinFrame);
            mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets);
            mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets);
        }
        return relayoutResult;
    }

实际通过Session的本地代理Binder  sWindowSession来调用了远程的方法:

WindowManagerService.java

       public int relayout(IWindow window, WindowManager.LayoutParams attrs,
                int requestedWidth, int requestedHeight, int viewFlags,
                boolean insetsPending, Rect outFrame, Rect outContentInsets,
                Rect outVisibleInsets, Configuration outConfig, Surface outSurface) {
            //Log.d(TAG, ">>>>>> ENTERED relayout from " + Binder.getCallingPid());
            int res = relayoutWindow(this, window, attrs,
                    requestedWidth, requestedHeight, viewFlags, insetsPending,
                    outFrame, outContentInsets, outVisibleInsets, outConfig, outSurface);
            //Log.d(TAG, "<<<<<< EXITING relayout to " + Binder.getCallingPid());
            return res;
        }

这里把传入的surface叫做outSruface是别有深意的。

继续分析

    public int relayoutWindow(Session session, IWindow client,
            WindowManager.LayoutParams attrs, int requestedWidth,
            int requestedHeight, int viewVisibility, boolean insetsPending,
            Rect outFrame, Rect outContentInsets, Rect outVisibleInsets,
            Configuration outConfig, Surface outSurface) {

//...
            WindowState win = windowForClientLocked(session, client, false);
//...
                try {
                    Surface surface = win.createSurfaceLocked();
                    if (surface != null) {
                        outSurface.copyFrom(surface);//通过windowState来创建一个新的surface并返回给传入的surface,这样surface和native的surface就联系起来了。
                        win.mReportDestroySurface = false;
                        win.mSurfacePendingDestroy = false;
                        if (SHOW_TRANSACTIONS) Slog.i(TAG,
                                "  OUT SURFACE " + outSurface + ": copied");
                    } else {
                        // For some reason there isn't a surface.  Clear the
                        // caller's object so they see the same state.
                        outSurface.release();
                    }
                } catch (Exception e) {
                    mInputMonitor.updateInputWindowsLw();
                    
                    Slog.w(TAG, "Exception thrown when creating surface for client "
                             + client + " (" + win.mAttrs.getTitle() + ")",
                             e);
                    Binder.restoreCallingIdentity(origId);
                    return 0;
                }
//...
}

relayoutWindow中首先是取出了Clinet对应的windowstate对象,通过他去调用createSurfaceLocked,并返回给传入的surface,到这里,ViewRoot里的mSurface就有了灵魂!

每一个Client都有唯一的一个windowstate对象。ViewRoot-mWindow------WindowState都是一一对应的关系,这个对象是什么时候被创建的? 下面会降到

接下来继续分析createSurfaceLocked

        Surface createSurfaceLocked() {
//......
                try {
                    mSurface = new Surface(
                            mSession.mSurfaceSession, mSession.mPid,
                            mAttrs.getTitle().toString(),
                            0, w, h, mAttrs.format, flags);
                    if (SHOW_TRANSACTIONS) Slog.i(TAG, "  CREATE SURFACE "
                            + mSurface + " IN SESSION "
                            + mSession.mSurfaceSession
                            + ": pid=" + mSession.mPid + " format="
                            + mAttrs.format + " flags=0x"
                            + Integer.toHexString(flags)
                            + " / " + this);
                } catch (Surface.OutOfResourcesException e) {
                    Slog.w(TAG, "OutOfResourcesException creating surface");
                    reclaimSomeSurfaceMemoryLocked(this, "create");
                    return null;
                } catch (Exception e) {
                    Slog.e(TAG, "Exception creating surface", e);
                    return null;
                }
//......
}

在这里调用了surface的有参的构造函数

surface.java

    /**
     * create a surface with a name
     * {@hide}
     */
    public Surface(SurfaceSession s,
            int pid, String name, int display, int w, int h, int format, int flags)
        throws OutOfResourcesException {
        if (DEBUG_RELEASE) {
            mCreationStack = new Exception();
        }
        mCanvas = new CompatibleCanvas();
        init(s,pid,name,display,w,h,format,flags);
        mName = name;
    }


和无参的不一样,这里调用了native的init

android_view_surface.cpp

static void Surface_init(
        JNIEnv* env, jobject clazz, 
        jobject session,
        jint pid, jstring jname, jint dpy, jint w, jint h, jint format, jint flags)
{
    if (session == NULL) {
        doThrow(env, "java/lang/NullPointerException");
        return;
    }
    
    SurfaceComposerClient* client =
            (SurfaceComposerClient*)env->GetIntField(session, sso.client);//获取出session中的SurfaceComposerClient对象,这个对象是怎么来//的?下文会讲到

    sp<SurfaceControl> surface;
    if (jname == NULL) {
        surface = client->createSurface(pid, dpy, w, h, format, flags);//调用SurfaceComposerClient方法,创建一个SurfaceControl对象,该对//象封装了Isurface和SurfaceComposerClient的一些方法,方便Client调用
    } else {
        const jchar* str = env->GetStringCritical(jname, 0);
        const String8 name(str, env->GetStringLength(jname));
        env->ReleaseStringCritical(jname, str);
        surface = client->createSurface(pid, name, dpy, w, h, format, flags);
    }

    if (surface == 0) {
        doThrow(env, OutOfResourcesException);
        return;
    }
    setSurfaceControl(env, clazz, surface);//保存surfacecontrol对象
}

surfacecomposerclient和surfaceflinger的关系比较复杂,后面会另写blog来讲。

到这里surface就在native空间有了对应的surfacecontrol,就可以通过surfacecontrol来调用surfacefilnger的方法了。

回到Viewroot,接下来就是draw()方法

    private void draw(boolean fullRedrawNeeded) {
        Surface surface = mSurface;
        if (!dirty.isEmpty() || mIsAnimating) {
            Canvas canvas;//画布
            try {
                int left = dirty.left;
                int top = dirty.top;
                int right = dirty.right;
                int bottom = dirty.bottom;
                canvas = surface.lockCanvas(dirty);//surface锁定画布并返回,然后在canvas上画出各种元素

                if (left != dirty.left || top != dirty.top || right != dirty.right ||
                        bottom != dirty.bottom) {
                    mAttachInfo.mIgnoreDirtyState = true;
                }

                // TODO: Do this in native
                canvas.setDensity(mDensity);
            } catch (Surface.OutOfResourcesException e) {
                Log.e(TAG, "OutOfResourcesException locking surface", e);
                // TODO: we should ask the window manager to do something!
                // for now we just do nothing
                return;
            } catch (IllegalArgumentException e) {
                Log.e(TAG, "IllegalArgumentException locking surface", e);
                // TODO: we should ask the window manager to do something!
                // for now we just do nothing
                return;
            }

            try {
                if (!dirty.isEmpty() || mIsAnimating) {
                    long startTime = 0L;

                    if (DEBUG_ORIENTATION || DEBUG_DRAW) {
                        Log.v(TAG, "Surface " + surface + " drawing to bitmap w="
                                + canvas.getWidth() + ", h=" + canvas.getHeight());
                        //canvas.drawARGB(255, 255, 0, 0);
                    }

                    if (Config.DEBUG && ViewDebug.profileDrawing) {
                        startTime = SystemClock.elapsedRealtime();
                    }

                    // If this bitmap's format includes an alpha channel, we
                    // need to clear it before drawing so that the child will
                    // properly re-composite its drawing on a transparent
                    // background. This automatically respects the clip/dirty region
                    // or
                    // If we are applying an offset, we need to clear the area
                    // where the offset doesn't appear to avoid having garbage
                    // left in the blank areas.
                    if (!canvas.isOpaque() || yoff != 0) {
                        canvas.drawColor(0, PorterDuff.Mode.CLEAR);
                    }

                    dirty.setEmpty();
                    mIsAnimating = false;
                    mAttachInfo.mDrawingTime = SystemClock.uptimeMillis();
                    mView.mPrivateFlags |= View.DRAWN;

                    if (DEBUG_DRAW) {
                        Context cxt = mView.getContext();
                        Log.i(TAG, "Drawing: package:" + cxt.getPackageName() +
                                ", metrics=" + cxt.getResources().getDisplayMetrics() +
                                ", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo());
                    }
                    int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
                    try {
                        canvas.translate(0, -yoff);
                        if (mTranslator != null) {
                            mTranslator.translateCanvas(canvas);
                        }
                        canvas.setScreenDensity(scalingRequired
                                ? DisplayMetrics.DENSITY_DEVICE : 0);
                        mView.draw(canvas);//调用传入decorView的draw方法
                    } finally {
                        mAttachInfo.mIgnoreDirtyState = false;
                        canvas.restoreToCount(saveCount);
                    }

                    if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {
                        mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING);
                    }

                    if (SHOW_FPS || Config.DEBUG && ViewDebug.showFps) {
                        int now = (int)SystemClock.elapsedRealtime();
                        if (sDrawTime != 0) {
                            nativeShowFPS(canvas, now - sDrawTime);
                        }
                        sDrawTime = now;
                    }

                    if (Config.DEBUG && ViewDebug.profileDrawing) {
                        EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime);
                    }
                }

            } finally {
                surface.unlockCanvasAndPost(canvas);//保存并解锁
            }
        }
//..
}



这里就会调用View的draw,将数据画到canvas上。


我们回头看setView里的第二个方法:sWindowSession.add。

不知道大家看上面的时候有没有觉得奇怪,viewroot对应的windowstate是什么时候创建的?

就在add里。

IWindowSession的add方法就是Session的本地proxy方法,通过Binder机制后实际会调用到Session的add方法:

    public int add(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, Rect outContentInsets, InputChannel outInputChannel) {
        return mService.addWindow(this, window, seq, attrs, viewVisibility, outContentInsets,
                outInputChannel);
    }
直接调用WindowManagerService的addWindow方法:

    public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility,
            Rect outContentInsets, InputChannel outInputChannel) {
//...
            win = new WindowState(this, session, client, token,
//...
            win.attach();
            mWindowMap.put(client.asBinder(), win);}

其中和windowstate的创建相关的主要是这三句话,首先new了一个windowstate对象,其次调用它的attach方法,再其次将他放入全局的表中(和client一一对应)。

我们看下他的attach方法:


 

    void attach() {
        if (WindowManagerService.localLOGV) Slog.v(
            WindowManagerService.TAG, "Attaching " + this + " token=" + mToken
            + ", list=" + mToken.windows);
        mSession.windowAddedLocked();
    }
mSession就是上文传入的session:

    void windowAddedLocked() {
        if (mSurfaceSession == null) {
            if (WindowManagerService.localLOGV) Slog.v(
                WindowManagerService.TAG, "First window added to " + this + ", creating SurfaceSession");
            mSurfaceSession = new SurfaceSession();//初始化surfacesession
            if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(
                    WindowManagerService.TAG, "  NEW SURFACE SESSION " + mSurfaceSession);
            mService.mSessions.add(this);//将session加入WMS的全局表中
        }
        mNumWindow++;
    }

这里又看到了一个另外的对象--surfaceSession:

    public SurfaceSession() {
        init();
    }
init是native的方法,本尊是在android_view_surface.cpp中的
static void SurfaceSession_init(JNIEnv* env, jobject clazz)
{
    sp<SurfaceComposerClient> client = new SurfaceComposerClient;
    client->incStrong(clazz);
    env->SetIntField(clazz, sso.client, (int)client.get());
}

到这里我们就应该明白了上文中创建surfacecontrol的SurfaceComposerClient是哪里来的吧!



安卓Activity劫持与反劫持 安卓Activity劫持与反劫持 0x00前言 近日,挪威一家APP安全公司Promon发现并报道了一个特性漏洞已经被多个恶意应用利用的分析报告,他们通过监测发现该漏洞使恶意软件可以伪装成任何合法的应用程序,从而使黑客可以访问私人短信和照片,窃取受害者的登录凭据,跟踪位置或记录电话对话,甚至可以通过手机摄像头和麦克风进行监视。 研究人员将该漏洞命名为StrandHogg,这是北欧人的一种北欧海盗... 阅读详情

相关推荐

Activity页面的绘制流程及移动端跨平台开发(Android)

React Native是一种流行的跨平台开发框架,基于JavaScript语言,可以实现使用相同的代码库开发Android和iOS应用。在Activity的onCreate()方法中,可以通过findViewById()方法来获取布局文件中定义的控件,并进行事件绑定和数据操作。为了提高开发效率和代码复用性,可以采用跨平台开发框架,使得开发人员可以使用相同的代码在多个平台上运行。通过了解Activity页面的绘制流程和跨平台开发框架,开发人员可以更好地进行移动应用开发,提高开发效率和代码复用性。

ZrilKwargs的博客 267

【Activity】绘制流程图方式(部署,启动不讨论)

activity绘制流程图大概有三种方式(可能还有其他的,我不知道,但这三种是最长用的) 归结起来份两类: 第一类:利用开发工具插件绘制(eclipse、idea等) 第二类:activity自带的ActivitiModeler 下面详细介绍 第一类:开发工具插件绘制 idea中安装插件(我的版本2020.2.3) 在idea中安装插件actiBpm(新版本下载不到) 如果搜索不到可在idea官网中搜索插件下载 在官方插件商城中下载后本地安装...

一步一个脚印_binding 6859

流程图(Activity)绘制

概述 流程是完成一系列有序动作的概述。每一个节点动作的结果将对后面的具体操作步骤产生影响。信息化系统中流程的功能完全等同于纸上办公的层级审批,尤其在oa系统中各类电子流提现较为明显。一般的步骤为: ① 申请者发起申请。 ② 各级领导审批。一般由低级别往高级别审批。 ③ 每一级别审批结果将影响或者决定申请结果。若下一节点非结束节点,此节点若审批通过将转给下一节点审批;若此节点审批不通过此...

Chief_fly的博客 7979

Android源码解析(十八)-->Activity布局绘制流程

这篇文章是承接上一篇文章来写的,大家都知道Activity在Android体系中扮演者一个界面展示的角色,通过上一篇文章的分析,我们知道Activity是通过Window来控制界面的展示的,一个Window对象就是一个窗口对象,而每个Activity中都有一个相应的Window对象,所以说一个Activity对象也就可以说是一个窗口对象,而Window只是控制着界面布局文件的加载过程,那么界面布局

一片枫叶的专栏 1万+

Activity工作流在线绘制流程图

分享一个web端绘制流程图,感觉很实用。下载解压后直接导入eclipse,运行方式 HTTP Preview,访问地址http://localhost:8080/estd/index.html 希望能帮助到大家。

QGC二次开发实战指南(一):从零构建自定义地面站界面

本文详细介绍了QGC二次开发实战指南,从零开始构建自定义地面站界面。通过QT和QML技术结合MAVLINK协议,开发者可以打造适合特定业务场景的无人机监控系统,提升操作效率与用户体验。文章包含环境搭建、界面设计、数据绑定等完整开发流程,是QGC二次开发的实用教程。

weixin_28693683的博客 496

android activity中画任意直线

这里的画直线不是通过新建一个View类,在View的画布中画,而是直接在activity中画。 package lizm.lt; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canv

lizhenmingdirk的专栏 1万+

Arduino平台软硬件原理及使用——Arduino Uno板卡的使用

Arduino Uno板卡的基本使用

洪博hopes的博客 4173

android+拖动画直线,android activity中画任意直线

这里的画直线不是通过新建一个View类,在View的画布中画,而是直接在activity中画。package lizm.lt;import android.app.Activity;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Canvas;import an...

weixin_42352981的博客 630

Ansys Lumerical 启动时出现 Error: Could not connect to Ansys license server specified at 1055@ 的另一种解决办法

昨天在关闭了中之后 就能正常打开 FDTD 了,结果今天想开启出现了同样的报错:我猜测昨天能启动的原因在于关闭之后要重启电脑,并且在重启之后我第一时间打开 lumerical 测试因此端口没有被占用,今天是在电脑启动之后一段时间才打开所以又遇到了端口占用的问题。

qq_51404184的博客 9549

基于Simulink的自适应反步法(Adaptive Backstepping)控制​

本文针对非线性系统控制中未知参数和强非线性耦合的挑战,提出基于Simulink的自适应反步法解决方案。通过融合参数在线估计与反步递归设计,该方法显著提升了永磁同步电机(PMSM)速度控制的鲁棒性,使跟踪误差从20%降至3%,参数估计收敛时间小于0.5秒。文章详细阐述了从算法推导到Simulink实现的完整流程,包括系统架构设计、核心算法推导(以PMSM为例)、模块化建模方法、参数优化策略以及工程部署建议。提供的自适应控制器MATLAB代码和关键参数表可直接应用于实际工程,为电机驱动、机械臂等非线性系统控制提

xiaoheshang_123的博客 101

Android给View画边框

Android给View画边框实现。

为梦想而努力(马英才) 1万+

29、Power BI 中行级安全(RLS)的实现与应用

本文详细介绍了在Power BI中实现行级安全(RLS)的方法,包括静态RLS和动态RLS的实现流程、适用场景及对比分析。通过具体业务场景,展示了如何创建安全角色、定义规则、验证角色、发布模型以及分配成员。此外,还探讨了更复杂的动态RLS场景,如基于父子层次结构的数据访问控制和从外部源获取用户信息的实现方式。文章最后总结了最佳实践建议及未来发展趋势,为企业在Power BI中高效、安全地实施数据访问控制提供了全面指导。

terraform7cloud的博客 195

【Activity】安卓Android开发中的Activity、Fragment、Adapter

我最近刚刚接触Android开发,对这个方面的开发可以说是一无所知,所以想着尝试开始理解。在这段时间的摸爬滚打里面,我发现了其中Activity、Fragment、Adapter三种非常常用,所以在这里进行一下我自己的理解。

weixin_67898144的博客 2792

要理解成本中心与利润中心的根本区别与适用场景,需从 “责任范围、考核核心、管理目标” 三个核心维度切入,同时明确二者在企业管理体系中的联动关系

摘要: 成本中心与利润核心区别在于责任范围、考核目标和管理权限。成本中心仅对成本负责(如生产、行政部门),考核成本控制能力;利润中心需同时管控成本与收入(如独立产品线、区域分公司),以利润为核心指标。选择标准取决于单元是否拥有收入决策权:无收入权的支持/执行部门设为成本中心,能独立经营、核算盈亏的单元设为利润中心。二者协同运作,成本中心为利润中心提供基础支持,利润中心的需求动态影响成本中心资源配置,共同实现企业整体目标。划分本质是确保考核指标与责任范围匹配,避免管理失效。

金牌架构师 657

ChatGPT搭配Claude 4写文献综述效果出奇的好!四步法直接写出逻辑清晰、层次分明的文献综述(附复制即用专业提示词)

请梳理【研究领域】中核心理论的发展演进过程,包括每一阶段理论的核心观点、提出背景与推动力,并指出理论演化中的关键节点或范式转变。请根据以下文献摘要内容,帮助我梳理该主题的研究趋势,包括:常见研究视角、研究方法的演变、研究对象的变化方向等。请根据以下研究空白与我已有的研究基础,帮我生成一个清晰的研究定位,包括:研究主题、研究对象、方法路径和理论框架。先要明确这个研究领域中一个主导或热门的理论,比如社会支持理论、认知负荷理论等,强调这个理论内部的不一致性、适用性争议,通过交代这⼀争议的背景,引发读者兴趣。

智能AI让学术科研更容易 568

【小王的安卓之路】Android安卓入门之Activity(活动)及其生命周期

- 1.Activity是什么? - 2.Activity的七大生命周期 - 3.Activity的生老病死 - 4.多个Activity交互时生命周期的变化 - 5.Activity的主要作用

小王的学习blog 4万+

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

【Android】安卓四大组件之活动(Activity) 详解:从基础到进阶

在安卓应用开发的世界里,Activity 作为安卓四大组件之一,是用户与应用进行交互的重要窗口,它承载着应用的界面展示与用户操作响应功能。无论是打开一款 APP 看到的主界面,还是点击按钮后弹出的新页面,都是 Activity 在发挥作用。本文将深入剖析 Activity,带你全面了解它的原理、使用方法及进阶技巧

m0_59734531的博客 3871

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

安卓application和activity启动流程

安卓application启动流程 源码分析 ActivityThread初始化: public static void main(String[] args) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain"); // Install selective syscall interception AndroidOs.install(); /

weixin_47285644的博客 1667

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

Android 安卓栈管理Activity工具以及使用方法

安卓开发中不可缺的,可以在Application初始化的时候建立,也可以创建一个单例来管理,下面是创建了单例模式的类来管理,给出了几个常用的,可以根据自己的业务需求再添加,注释有详细标明!

2401_85730375的博客 727

安卓Activity的启动模式

Activity的启动模式

qq_37465077的博客 2554

安卓 2.1 Activity的简述

移动应用体验与桌面体验的不同之处在于,用户与应用的互动并不总是在同一位置开始,而是经常以不确定的方式开始。而Activity 类的目的就是促进这种范式的实现,我们来学习一下

m0_65489276的博客 1713

安卓统一添加activity切换动画

安卓统一添加activity切换动画背景说明在实际 的应用开发过程中各种动画的使用不仅能使用户感觉起来更加流畅而且合适的动画效果可以帮助用户快速的熟悉应用操作,对提升用户体验来说,动画是必不可少的。在安卓2.0之后,安卓提供了一个很方便的API用来设置安卓界面切换动画:overridePendingTransition(EnterAnim, ExitAnim);发现问题但是这里有一个缺点,

GcsSloop 3222
上一篇: android TP虚拟按键
下一篇: android学习笔记 匿名Binder
有梦想的胖子
博客等级 码龄16年 51粉丝 26原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值