首页 > 其他分享 >Android 10.0 截屏流程

Android 10.0 截屏流程

时间:2024-10-24 18:45:34浏览次数:9  
标签:10.0 截图 java screenshot int void 截屏 Android null

通常未通过特殊定制的 Android 系统,截屏都是经过同时按住音量下键和电源键来截屏。本篇文章就只讨论使用这些特殊按键来进行截屏。
这里我们就要明白事件是在哪里进行分发拦截的。通过源码的分析,我们发现是在PhoneWindowManager.java 中。
PhoneWindowManager#interceptKeyBeforeQueueing()
frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java

    @Override
    public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
        if (!mSystemBooted) {
            // If we have not yet booted, don't let key events do anything.
            return 0;
        }
        // 省略部分代码......
        // Handle special keys.
        switch (keyCode) {
             ......
            case KeyEvent.KEYCODE_VOLUME_DOWN:
            case KeyEvent.KEYCODE_VOLUME_UP:
            case KeyEvent.KEYCODE_VOLUME_MUTE: {
                // 按下音量键调用
                handleVolumeKey(event, policyFlags);
                ......
                break;
            }
            ......
            case KeyEvent.KEYCODE_POWER: {
                ......
                if (down) {
                    // 按下电源键将调用
                    interceptPowerKeyDown(event, interactive);
                } else {          
                    interceptPowerKeyUp(event, interactive, canceled);
                }
                break;
            }
        }
        return result;
    }

1、电源键处理

PhoneWindowManager#interceptPowerKeyDown()
frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java

    private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {
        // 省略部分代码......
        // Latch power key state to detect screenshot chord.
        if (interactive && !mScreenshotChordPowerKeyTriggered
                && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
            // power键按下的标志
            mScreenshotChordPowerKeyTriggered = true;
            // 获取 Power 键的触发时间
            mScreenshotChordPowerKeyTime = event.getDownTime();
            // 处理屏幕截图事件
            interceptScreenshotChord();
            // 这个方法应该是消耗、拦截事件的,避免改变音量、铃声等。
            interceptRingerToggleChord();
        }
        // 省略部分代码......
    }

interceptScreenshotChord()该方法下面再说,先介绍电源按键、音量按键的处理。

2、音量键处理

PhoneWindowManager#handleVolumeKey()
frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java

    public void handleVolumeKey(KeyEvent event, int policyFlags) {
        final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
        final int keyCode = event.getKeyCode();
        final boolean interactive = (policyFlags & FLAG_INTERACTIVE) != 0;
        if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
            if (down) {
                // Any activity on the vol down button stops the ringer toggle shortcut
                cancelPendingRingerToggleChordAction();
                if (interactive && !mScreenshotChordVolumeDownKeyTriggered
                        && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
                    // Volume键按下的标志
                    mScreenshotChordVolumeDownKeyTriggered = true;
                    // 获取 Volume 键的触发时间
                    mScreenshotChordVolumeDownKeyTime = event.getDownTime();
                    // 赋值  false 该属性为了防止截屏的时候音量下键生效出现调节音量的 dialog 状态值
                    mScreenshotChordVolumeDownKeyConsumed = false;
                    // 防止触发 Power  键长按功能
                    cancelPendingPowerKeyAction();
                    //处理屏幕截图事件
                    interceptScreenshotChord();
                    // 拦截相关快捷键
                    interceptAccessibilityShortcutChord();
                }
            } else {
                 // 省略部分代码......
            }
        } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
             // 省略部分代码......
        }
        return;
    }

3、截屏事件处理 interceptScreenshotChord()

PhoneWindowManager#interceptScreenshotChord()
frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java

    private void interceptScreenshotChord() {
          /*
           * if 判断参数介绍
           * mScreenshotChordEnabled 其值为mContext.getResources().getBoolean(com.android.internal.R.bool.config_enableScreenshotChord);
           * mScreenshotChordVolumeDownKeyTriggered 音量下键按下时值为true
           * mScreenshotChordPowerKeyTriggered  电源键按下时值为true
           * mA11yShortcutChordVolumeUpKeyTriggered 音量上(加)键抬起时为false , 按下时为true
           **/
        if (mScreenshotChordEnabled
                && mScreenshotChordVolumeDownKeyTriggered && mScreenshotChordPowerKeyTriggered
                && !mA11yShortcutChordVolumeUpKeyTriggered) {
            // 获取当前时间
            final long now = SystemClock.uptimeMillis();
            // 当前时间小于 音量下键按下时间 + 150ms
            // 当前时间小于 power键按下时间 + 150ms
            if (now <= mScreenshotChordVolumeDownKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS
                    && now <= mScreenshotChordPowerKeyTime
                    + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS) {
                boolean inLongScreenshot = Settings.System.getIntForUser(mContext.getContentResolver(),
                        LONGSCREENSHOT_SETTING, 0, UserHandle.USER_CURRENT_OR_SELF) == 1;
                if (hasInPowerUtrlSavingMode() || inLongScreenshot) {
                    return;
                }
      
                // 长按音量下键,达到截屏条件,将该事件消费掉。
                mScreenshotChordVolumeDownKeyConsumed = true;
                // 防止触发 Power  键长按功能
                cancelPendingPowerKeyAction();
                // 设置截图的类型,TAKE_SCREENSHOT_FULLSCREEN 为全屏
                mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN);
                // 截图的方式,(例如:按键、三指下滑 等等)
                mScreenshotRunnable.setScreenshotSource(SCREENSHOT_KEY_CHORD);
                //执行 mScreenshotRunnable
                mHandler.postDelayed(mScreenshotRunnable, getScreenshotChordLongPressDelay());
            }
        }
    }

继续查看ScreenshotRunnable,此时会一步步向下调用,最终到SystemUI。
frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java

    private class ScreenshotRunnable implements Runnable {
        private int mScreenshotType = TAKE_SCREENSHOT_FULLSCREEN;
        private int mScreenshotSource = SCREENSHOT_KEY_OTHER;
        public void setScreenshotType(int screenshotType) {
            mScreenshotType = screenshotType;
        }
        public void setScreenshotSource(int screenshotSource) {
            mScreenshotSource = screenshotSource;
        }
        @Override
        public void run() {
            // 回调到 DisplayPolicy.java
            mDefaultDisplayPolicy.takeScreenshot(mScreenshotType, mScreenshotSource);
        }
    }

DisplayPolicy#takeScreenshot()
frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java

    // 请求截取屏幕截图
    public void takeScreenshot(int screenshotType, int source) {
        if (mScreenshotHelper != null) {
            mScreenshotHelper.takeScreenshot(screenshotType,
                    mStatusBar != null && mStatusBar.isVisibleLw(),
                    mNavigationBar != null && mNavigationBar.isVisibleLw(),
                    source, mHandler, null /* completionConsumer */);
        }
    }

继续往下看ScreenshotHelper#takeScreenshot()
frameworks/base/core/java/com/android/internal/util/ScreenshotHelper.java

    // 请求截取屏幕截图
    public void takeScreenshot(final int screenshotType, final boolean hasStatus,
            final boolean hasNav, int source, @NonNull Handler handler,
            @Nullable Consumer<Uri> completionConsumer) {
        ScreenshotRequest screenshotRequest = new ScreenshotRequest(source, hasStatus, hasNav);
        takeScreenshot(screenshotType, SCREENSHOT_TIMEOUT_MS, handler, screenshotRequest,
                completionConsumer);
    }
    //到了 Binder调用环节, 此为客户端, 服务端为SystemUI中的 TakeScreenshotService
    private void takeScreenshot(final int screenshotType, long timeoutMs, @NonNull Handler handler,
                ScreenshotRequest screenshotRequest, @Nullable Consumer<Uri> completionConsumer) {
            synchronized (mScreenshotLock) {
                final Runnable mScreenshotTimeout = () -> {
                    synchronized (mScreenshotLock) {
                        if (mScreenshotConnection != null) {
                            // 在获取屏幕截图捕获响应之前超时
                            Log.e(TAG, "Timed out before getting screenshot capture response");
                            // 重置连接
                            resetConnection();
                            // 通知截屏错误
                            notifyScreenshotError();
                        }
                    }
                    if (completionConsumer != null) {
                        completionConsumer.accept(null);
                    }
                };
                Message msg = Message.obtain(null, screenshotType, screenshotRequest);
                Handler h = new Handler(handler.getLooper()) {
                    @Override
                    public void handleMessage(Message msg) {
                        switch (msg.what) {
                            case SCREENSHOT_MSG_URI:
                                if (completionConsumer != null) {
                                    completionConsumer.accept((Uri) msg.obj);
                                }
                                handler.removeCallbacks(mScreenshotTimeout);
                                break;
                            case SCREENSHOT_MSG_PROCESS_COMPLETE:
                                synchronized (mScreenshotLock) {
                                    resetConnection();
                                }
                                break;
                        }
                    }
                };
                msg.replyTo = new Messenger(h);
                if (mScreenshotConnection == null || mScreenshotService == null) {
                    // 一个标准的Service连接
                    // config_screenshotServiceComponent == com.android.systemui/com.android.systemui.screenshot.TakeScreenshotService
                    final ComponentName serviceComponent = ComponentName.unflattenFromString(
                            mContext.getResources().getString(
                                    com.android.internal.R.string.config_screenshotServiceComponent));
                    final Intent serviceIntent = new Intent();
                    serviceIntent.setComponent(serviceComponent);
                    ServiceConnection conn = new ServiceConnection() {
                        @Override
                        // 当Service连接成功之后
                        public void onServiceConnected(ComponentName name, IBinder service) {
                            synchronized (mScreenshotLock) {
                                if (mScreenshotConnection != this) {
                                    return;
                                }
                                mScreenshotService = service;
                                Messenger messenger = new Messenger(mScreenshotService);
                                try {
                                    messenger.send(msg);
                                } catch (RemoteException e) {
                                    Log.e(TAG, "Couldn't take screenshot: " + e);
                                    if (completionConsumer != null) {
                                        completionConsumer.accept(null);
                                    }
                                }
                            }
                        }
                        @Override
                        // 当Service断开连接时
                        public void onServiceDisconnected(ComponentName name) {
                            synchronized (mScreenshotLock) {
                                if (mScreenshotConnection != null) {
                                    resetConnection();
                                    // only log an error if we're still within the timeout period
                                    if (handler.hasCallbacks(mScreenshotTimeout)) {
                                        handler.removeCallbacks(mScreenshotTimeout);
                                        notifyScreenshotError();
                                    }
                                }
                            }
                        }
                    };
                    // bindService
                    if (mContext.bindServiceAsUser(serviceIntent, conn,
                            Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
                            UserHandle.CURRENT)) {
                        mScreenshotConnection = conn;
                        handler.postDelayed(mScreenshotTimeout, timeoutMs);
                    }
                } else {
                    // 如果已经连接则直接发送Message
                    Messenger messenger = new Messenger(mScreenshotService);
                    try {
                        messenger.send(msg);
                    } catch (RemoteException e) {
                        Log.e(TAG, "Couldn't take screenshot: " + e);
                        if (completionConsumer != null) {
                            completionConsumer.accept(null);
                        }
                    }
                    handler.postDelayed(mScreenshotTimeout, timeoutMs);
                }
            }
    }

客户端通过向服务端发送 message 来将截屏任务交给 service,由 service 处理后面的操作。
frameworks/base/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java

    private Handler mHandler = new Handler(Looper.myLooper()) {
        @Override
        public void handleMessage(Message msg) {
            // 获取客户端传的 Messenger 对象
            final Messenger callback = msg.replyTo;
            Consumer<Uri> uriConsumer = uri -> {
                Message reply = Message.obtain(null, SCREENSHOT_MSG_URI, uri);
                try {
                    / /Messenger 双向通信,在服务端用远程客户端的 Messenger 对象给客户端发送信息
                    callback.send(reply);
                } catch (RemoteException e) {
                }
            };
            Runnable onComplete = () -> {
                Message reply = Message.obtain(null, SCREENSHOT_MSG_PROCESS_COMPLETE);
                try {
                    callback.send(reply);
                } catch (RemoteException e) {
                }
            };
    
            // 判断用户的设备是否为解锁状态
            // 如果用户的存储被锁定,我们没有地方存储截图,所以跳过它,而不是显示一个误导性的动画和错误通知。
            if (!mUserManager.isUserUnlocked()) {
                Log.w(TAG, "Skipping screenshot because storage is locked!");
                post(() -> uriConsumer.accept(null));
                post(onComplete);
                return;
            }
            ScreenshotHelper.ScreenshotRequest screenshotRequest =
                    (ScreenshotHelper.ScreenshotRequest) msg.obj;
            mUiEventLogger.log(ScreenshotEvent.getScreenshotSource(screenshotRequest.getSource()));
            switch (msg.what) {
                case WindowManager.TAKE_SCREENSHOT_FULLSCREEN:  // 全屏截图
                    // 我们在PhoneWindowManager传入的type为全屏截图,所以需要执行全屏截图流程
                    mScreenshot.takeScreenshot(uriConsumer, onComplete);
                    break;
                case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION:  // 区域截图
                    mScreenshot.takeScreenshot(uriConsumer, onComplete);
                    break;
                case WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE:
                    Bitmap screenshot = BitmapUtil.bundleToHardwareBitmap(
                            screenshotRequest.getBitmapBundle());
                    Rect screenBounds = screenshotRequest.getBoundsInScreen();
                    Insets insets = screenshotRequest.getInsets();
                    int taskId = screenshotRequest.getTaskId();
                    int userId = screenshotRequest.getUserId();
                    ComponentName topComponent = screenshotRequest.getTopComponent();
                    mScreenshot.handleImageAsScreenshot(screenshot, screenBounds, insets,
                            taskId, userId, topComponent, uriConsumer, onComplete);
                    break;
                default:
                    Log.d(TAG, "Invalid screenshot option: " + msg.what);
            }
        }
    };

TakeScreenshotService调用GlobalScreenshot.java的takeScreenshot();
GlobalScreenshot#takeScreenshot()

// GlobalScreenshot.java
    /**
     *截取当前显示的屏幕截图并显示动画。.
     */
    private void takeScreenshot(Consumer<Uri> finisher, Rect crop) {
        // copy the input Rect, since SurfaceControl.screenshot can mutate it
        Rect screenRect = new Rect(crop);
        int rot = mDisplay.getRotation();
        int width = crop.width();
        int height = crop.height();
        takeScreenshot(SurfaceControl.screenshot(crop, width, height, rot), finisher, screenRect,
                Insets.NONE, true);
    }
    private void takeScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect,
            Insets screenInsets, boolean showFlash) {
        // 此方法会清除上一次的截图信息--连续截图行为
        dismissScreenshot("new screenshot requested", true);
        mScreenBitmap = screenshot;
        if (mScreenBitmap == null) {
            // 如果没有Bitmap则报告错误信息
            mNotificationsController.notifyScreenshotError(
                    R.string.screenshot_failed_to_capture_text);
            finisher.accept(null);
            mOnCompleteRunnable.run();
            return;
        }
        if (!isUserSetupComplete()) {
            // 用户设置尚未完成,不应该向用户展示 分享和编辑 , 只显示一个Toast并保存图片
            saveScreenshotAndToast(finisher);
            return;
        }
        // Optimizations
        mScreenBitmap.setHasAlpha(false);
        mScreenBitmap.prepareToDraw();
        onConfigChanged(mContext.getResources().getConfiguration());
        if (mDismissAnimation != null && mDismissAnimation.isRunning()) {
            mDismissAnimation.cancel();
        }
        // 获取焦点
        setWindowFocusable(true);
        // 开始截图后动画
        startAnimation(finisher, screenRect, screenInsets, showFlash);
    }
    /**
     * 截屏后开始动画
     */
    private void startAnimation(final Consumer<Uri> finisher, Rect screenRect, Insets screenInsets,
            boolean showFlash) {
        if (mScreenshotIng == false) {//unisoc: Modify for bug1360276
            mScreenshotIng = true;
     
            // 如果开启了省电模式,显示 toast,以便有一些视觉提示已截取屏幕截图
            PowerManager powerManager =(PowerManager) mContext . getSystemService (Context.POWER_SERVICE);
            if (powerManager.isPowerSaveMode()) {
                Toast.makeText(mContext, R.string.screenshot_saved_title, Toast.LENGTH_SHORT).show();
            }
            mScreenshotHandler.post(() -> {
                if (!mScreenshotLayout.isAttachedToWindow()) {
                    // mScreenshotLayout是截屏的缩略图的父View
                    // mScreenshotLayout 在 GlobalScreenshot.java 的构造方法中初始化。对应布局文件:global_screenshot.xml
                    mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
                }
                // 动画相关的View
                mScreenshotAnimatedView.setImageDrawable(
                        createScreenDrawable(mScreenBitmap, screenInsets));
                setAnimatedViewSize(screenRect.width(), screenRect.height());
                // 显示动画何时开始
                mScreenshotAnimatedView.setVisibility(View.GONE);
                //缩略图显示的View,将native层返回的Bitmap加载到此View上
                mScreenshotPreview.setImageDrawable(createScreenDrawable(mScreenBitmap, screenInsets));
                // 使静态预览不可见(消失),以便我们可以在屏幕上查询其位置
                mScreenshotPreview.setVisibility(View.INVISIBLE);
                mScreenshotHandler.post(() -> {
                mScreenshotLayout.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
                // 创建动画
                mScreenshotAnimation =
                        createScreenshotDropInAnimation(screenRect, showFlash);
                // 保存截图
                saveScreenshotInWorkerThread(finisher, new ActionsReadyListener () {
                    @Override
                    void onActionsReady (SavedImageData imageData) {
                        showUiOnActionsReady(imageData);
                        mScreenshotIng = false;
                    }
                });
                // 播放快门声音以通知我们已截屏
                mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
                if (mScreenshotPreview.isAttachedToWindow()) {
                    mScreenshotPreview.setLayerType(View.LAYER_TYPE_HARDWARE, null);
                    mScreenshotPreview.buildLayer();
                }
                // 开始执行动画
                mScreenshotAnimation.start();
            });
            });
        }
    }
    /**
     * 创建一个新的工作线程并将屏幕截图保存到媒体存储
     */
    private void saveScreenshotInWorkerThread(
            Consumer<Uri> finisher, @Nullable ActionsReadyListener actionsReadyListener) {
        SaveImageInBackgroundData data = new SaveImageInBackgroundData();
        data.image = mScreenBitmap;  // native 层返回的 Bitmap
        data.finisher = finisher;
        data.mActionsReadyListener = actionsReadyListener;
        if (mSaveInBgTask != null) {
            // just log success/failure for the pre-existing screenshot
            // 只需记录预先存在的屏幕截图的成功失败
            mSaveInBgTask.setActionsReadyListener(new ActionsReadyListener() {
                @Override
                void onActionsReady(SavedImageData imageData) {
                    logSuccessOnActionsReady(imageData);
                }
            });
        }
        // 截图的一些信息存储在 SaveImageInBackgroundTask 中构建
        mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data);
        mSaveInBgTask.execute();
    }

到此截屏流程完毕,可以查看下截图的View的xml文件:global_screenshot.xml
frameworks/base/packages/SystemUI/res/layout/global_screenshot.xml

<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/global_screenshot_frame"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ImageView
        android:id="@+id/global_screenshot_actions_background"
        android:layout_height="@dimen/screenshot_bg_protection_height"
        android:layout_width="match_parent"
        android:layout_gravity="bottom"
        android:alpha="0.0"
        android:src="@drawable/screenshot_actions_background_protection"/>
    <!--截屏动画相关的View -->
    <ImageView
        android:id="@+id/global_screenshot_animated_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="top|start"
        android:visibility="gone"
        android:elevation="@dimen/screenshot_preview_elevation"
        android:background="@drawable/screenshot_rounded_corners" />
    <ImageView
        android:id="@+id/global_screenshot_flash"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="gone"
        android:elevation="@dimen/screenshot_preview_elevation"
        android:src="@android:color/white"/>
    <com.android.systemui.screenshot.ScreenshotSelectorView
        android:id="@+id/global_screenshot_selector"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="gone"
        android:pointerIcon="crosshair"/>
    <!-- 此处包含了一个layout, 而缩略图的View就在此layout中,
         截屏右上角的关闭缩略图按钮 也在此layout中 -->
    <include layout="@layout/global_screenshot_static"/>
</FrameLayout>

标签:10.0,截图,java,screenshot,int,void,截屏,Android,null
From: https://blog.csdn.net/u010345983/article/details/135170458

相关文章

  • 【AI+手机】如何利用GPT实现Android软件自动化交互?全面解读MetaGPT Android助手实现原
    大家好,我是同学小张,+v:jasper_8017一起交流,持续学习AI大模型应用实战案例,持续分享,欢迎大家点赞+关注,订阅我的大模型专栏,共同学习和进步。前段时间,AI+手机的组合挺火的。想象一下,你想给某个人发短信,只需要对手机说一句:给xxx发个短信问好。AI自动识别意图,自动给你打开......
  • android开发flutter项目每次运行都很慢的解决方法
    android开发flutter项目每次运行都很慢的解决方法1.修改依赖库访问源为国内阿里云镜像构建慢的原因一般都是卡在远程依赖地址访问的速度问题,比如国外镜像的访问,那就改为国内的,比如阿里云的看看能不能解决问题//阿里云地址配置说明:https://developer.aliyun.com/mvn/guide//......
  • 谷歌地图 | 与 Android 版导航 SDK 集成的最佳实践
    谷歌最近宣布了导航SDK,它可以让您将熟悉的Google地图逐向导航体验无缝集成到您的Android和iOS应用程序中。这篇博文概述了一些最佳实践,您可以使用这些实践为您的Android应用程序使用导航SDK构建流畅、一致且可靠的导航体验。 与导航地图交互与NavigationSDK集......
  • 基于Android的的旅游攻略APP的设计与实现(源码+lw+部署文档+讲解等)
    项目整体介绍基于安卓Android的旅游攻略APP的设计与实现具有重要的现实意义,可以为用户提供便捷的旅游信息查询和规划服务。一、背景随着人们生活水平的提高和旅游需求的增加,旅游市场呈现出蓬勃发展的态势。然而,传统的旅游攻略获取方式存在着信息不全面、更新不及时......
  • 基于Android的的酒店管理APP小程序实现(源码+lw+部署文档+讲解等)
    项目整体介绍基于安卓Android的酒店管理APP和小程序可以为酒店提供更便捷的管理方式和更好的客户服务体验。一、背景随着移动互联网的发展,越来越多的人习惯使用手机进行各种操作,包括酒店预订、入住登记、服务请求等。因此,开发一款基于安卓Android的酒店管理APP......
  • 【Android学习】四大组件
    目录 一、Activity:用户界面的核心二、Service:后台处理任务三、BroadcastReceiver:处理广播消息四、ContentProvider:应用间共享数据 一、Activity:用户界面的核心Activity是Android应用的核心组成部分之一,主要负责展示用户界面,响应用户的交互操作。每个Activity通常......
  • Android MVVM
    AndroidMVVM介绍MVVM(Model-View-ViewModel)是Android开发中常用的一种架构模式。它将应用程序的逻辑分离为三个主要部分:Model(模型)、View(视图)和ViewModel(视图模型),从而使代码更清晰、更易于维护。1.Model(模型)Model代表应用程序的数据和业务逻辑。它负责处理数据的获取、存储和......
  • Android 应用自启动
    Android应用自启动监听系统广播(BroadcastReceiver):通过接收系统的BOOT_COMPLETED广播,可以在设备启动后自动启动应用或服务。你需要在AndroidManifest.xml中注册一个BroadcastReceiver,并监听BOOT_COMPLETED广播。<receiverandroid:name=".MySelfStartingBroadcastReceiver"......
  • 如何将rust日志输出到android终端
    本博客所有文章除特别声明外,均采用CCBY-NC-SA4.0许可协议。转载请注明来自唯你背景在Rust中,使用println!打印日志时,输出实际上是发送到标准输出(stdout),而AndroidLogcat专门用于处理和显示应用程序的日志信息,此环境下标准输出实现被重新定义。这意味着Rust日志输出不......
  • FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库LD
    ijkplayer是一款由B站研发的移动端国产播放器,它基于FFmpeg3.4版本,同时兼容Android和iOS两大移动操作系统。ijkplayer的源码托管地址为https://github.com/bilibili/ijkplayer,截止2024年9月15日,ijkplayer获得3.24万星标数,以及0.81万个分支数,而这还是ijkplayer停止更新6年之后的数据......