ShutdownThread - 动画 & 音乐

本文详细解析了Android原生系统的关机流程,并介绍了如何在关机过程中加入自定义动画及声音效果。文章深入探讨了ShutdownThread类中的beginShutdownSequence方法和run方法,展示了如何调整这些方法来优化关机体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

      在android原生的关机流程里面是没有声音和 自定义动画的借口,因此为了实现这两个功能需要找到相应的地方并采用合适的方式进行处理。

 

 

在关机的ShutdownThread类里面有两个很重要的方法,一个是beginShutdownSequence,解析如下:

 

    private static void beginShutdownSequence(Context context) {

        synchronized (sIsStartedGuard) {

            sIsStarted = true;

        }

 

        // throw up an indeterminate system dialog to indicate radio is

        // shutting down.

        //动画开始 

        //这里也是可以加入声音的播放流程,(这里要考虑到几点:声音与动画的同步;需要避免蓝牙等断开操作导致的声音不连续)

        ProgressDialog pd = new ProgressDialog(context);

        pd.setTitle(context.getText(com.android.internal.R.string.power_off));

        pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));

        pd.setIndeterminate(true);

        pd.setCancelable(false);

        pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);

        if (!context.getResources().getBoolean(

                com.android.internal.R.bool.config_sf_slowBlur)) {

            pd.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);

        }

 

        pd.show();

      

 //动画结束,需要在这里加入自定的view并进行显示。这里的view需要注意横竖屏的转化,让其显示唯一,后面的代码则是启动关机的动作。

 

        // start the thread that initiates shutdown

        sInstance.mContext = context;

        sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);

        sInstance.mWakeLock = null;

        if (sInstance.mPowerManager.isScreenOn()) {

            try {

                sInstance.mWakeLock = sInstance.mPowerManager.newWakeLock(

                        PowerManager.FULL_WAKE_LOCK, "Shutdown");

                sInstance.mWakeLock.acquire();

            } catch (SecurityException e) {

                Log.w(TAG, "No permission to acquire wake lock", e);

                sInstance.mWakeLock = null;

            }

        }

        sInstance.mHandler = new Handler() {

        };

        sInstance.start();

    }

 

 

 

第二个就是run方法,该方法决定了一个完整的关机流程所需要的时间。设置时间为最少10秒,其实该段代码在无声音和动画的情况下可以在5秒内执行完毕。如果考虑到声音就要注意了,因为很有可能产生破音(就是关机的最后一刻会产生比较尖锐的声音)

 

    public void run() {
        boolean bluetoothOff;
        boolean radioOff;

        BroadcastReceiver br = new BroadcastReceiver() {
            @Override public void onReceive(Context context, Intent intent) {
                // We don't allow apps to cancel this, so ignore the result.
                actionDone();
            }
        };
        
        Log.i(TAG, "Sending shutdown broadcast...");
        
        // First send the high-level shut down broadcast.
        mActionDone = false;
        mContext.sendOrderedBroadcast(new Intent(Intent.ACTION_SHUTDOWN), null,
                br, mHandler, 0, null, null);
  
      //从这里开始
        final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
        synchronized (mActionDoneSync) {
            while (!mActionDone) {
                long delay = endTime - SystemClock.elapsedRealtime();
                if (delay <= 0) {
                    Log.w(TAG, "Shutdown broadcast timed out");
                    break;
                }
                try {
                    mActionDoneSync.wait(delay);
                } catch (InterruptedException e) {
                }
            }
        }
    
    //到这里结束,这段时间的延迟是可以缩短的,当然是会有些问题。当然如果是为了更快的关机,这些问题就可以通过动画和声音避免掉。
  
      Log.i(TAG, "Shutting down activity manager...");
      
        final IActivityManager am =
            ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));
        if (am != null) {
            try {
                am.shutdown(MAX_BROADCAST_TIME);
            } catch (RemoteException e) {
            }
        }

          //从这里开始

        final ITelephony phone =
                ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
        final IBluetooth bluetooth =
                IBluetooth.Stub.asInterface(ServiceManager.checkService(
                        BluetoothAdapter.BLUETOOTH_SERVICE));
        
        final IMountService mount =
                IMountService.Stub.asInterface(
                        ServiceManager.checkService("mount"));
        //到这里结束,可以通过以上的方式得到一只运行在后台的系统服务。蓝牙耳机也就是通过这种方式获取,然后再判断声音如何播放
        try {
            bluetoothOff = bluetooth == null ||
                           bluetooth.getBluetoothState() == BluetoothAdapter.STATE_OFF;
            if (!bluetoothOff) {
                Log.w(TAG, "Disabling Bluetooth...");
                bluetooth.disable(false);  // disable but don't persist new state
            }
        } catch (RemoteException ex) {
            Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
            bluetoothOff = true;
        }
        //
        try {
            radioOff = phone == null || !phone.isRadioOn();
            if (!radioOff) {
                Log.w(TAG, "Turning off radio...");
                phone.setRadio(false);
            }
        } catch (RemoteException ex) {
            Log.e(TAG, "RemoteException during radio shutdown", ex);
            radioOff = true;
        }

        Log.i(TAG, "Waiting for Bluetooth and Radio...");
        
        // Wait a max of 32 seconds for clean shutdown
        for (int i = 0; i < MAX_NUM_PHONE_STATE_READS; i++) {
            if (!bluetoothOff) {
                try {
                    bluetoothOff =
                            bluetooth.getBluetoothState() == BluetoothAdapter.STATE_OFF;
                } catch (RemoteException ex) {
                    Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
                    bluetoothOff = true;
                }
            }
            if (!radioOff) {
                try {
                    radioOff = !phone.isRadioOn();
                } catch (RemoteException ex) {
                    Log.e(TAG, "RemoteException during radio shutdown", ex);
                    radioOff = true;
                }
            }
            if (radioOff && bluetoothOff) {
                Log.i(TAG, "Radio and Bluetooth shutdown complete.");
                break;
            }
            SystemClock.sleep(PHONE_STATE_POLL_SLEEP_MSEC);
        }

        // Shutdown MountService to ensure media is in a safe state
        IMountShutdownObserver observer = new IMountShutdownObserver.Stub() {
            public void onShutDownComplete(int statusCode) throws RemoteException {
                Log.w(TAG, "Result code " + statusCode + " from MountService.shutdown");
                actionDone();
            }
        };

        Log.i(TAG, "Shutting down MountService");
        // Set initial variables and time out time.
        mActionDone = false;
        final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME;
        synchronized (mActionDoneSync) {
            try {
                if (mount != null) {
                    mount.shutdown(observer);
                } else {
                    Log.w(TAG, "MountService unavailable for shutdown");
                }
            } catch (Exception e) {
                Log.e(TAG, "Exception during MountService shutdown", e);
            }
            while (!mActionDone) {
                long delay = endShutTime - SystemClock.elapsedRealtime();
                if (delay <= 0) {
                    Log.w(TAG, "Shutdown wait timed out");
                    break;
                }
                try {
                    mActionDoneSync.wait(delay);
                } catch (InterruptedException e) {
                }
            }
        }

        if (mReboot) {
            Log.i(TAG, "Rebooting, reason: " + mRebootReason);
            try {
                Power.reboot(mRebootReason);
            } catch (Exception e) {
                Log.e(TAG, "Reboot failed, will attempt shutdown instead", e);
            }
        } else if (SHUTDOWN_VIBRATE_MS > 0) {
            // vibrate before shutting down
            Vibrator vibrator = new Vibrator();
            vibrator.vibrate(SHUTDOWN_VIBRATE_MS);
            // vibrator is asynchronous so we need to wait to avoid shutting down too soon.
            try {
                Thread.sleep(SHUTDOWN_VIBRATE_MS);
            } catch (InterruptedException e) {
            }
        }
        //最后在这里一定要注意,声音是否播放完毕。如果没有完毕则要延时后再执行下面的命令,否则会产生破音。
        // Shutdown power
        Log.i(TAG, "Performing low-level shutdown...");
        Power.shutdown();
    }

 

     run方法的整个关机过程包括上面的方法不能产生任何错误,即便是try catch处理过也不行,这样会导致机器调用reboot的命令,很无奈的!!  例如Thread.sleep就有可能产生错误,源码中就有这样的隐患。建议使用SystemClock.sleep替换,这样就可以避免机器重启的隐患。

 

 

 

 

&lt;think&gt;我们参考了引用[2]中关于开机logo修改的方法,因为关机动画的实现可能与开机动画类似。同时,引用[3]和[4]提供了关机流程的相关信息,这有助于我们理解在关机过程中插入动画的时机。 在高通平台上实现关机动画,通常有两种方法: 1. 修改系统源码,在关机流程中调用动画播放器。 2. 替换关机动画资源文件(类似于开机动画)。 根据引用[3]和[4],Android关机流程会执行`android_reboot`函数,最终调用`reboot(RB_POWER_OFF)`。在关机前,我们可以插入播放动画的步骤。 但是,请注意,关机动画的播放需要在系统完全关闭之前完成,并且不能影响正常的关机流程。 **解决方案步骤:** 1. **准备关机动画文件**: 关机动画通常是一个压缩包,包含一系列图片和描述文件`desc.txt`。这个文件的结构与开机动画类似。例如,我们可以创建一个名为`shutdownanimation.zip`的压缩包,放在系统某个目录下(如`/system/media/`)。 2. **修改系统代码以支持关机动画**: 在关机流程中,我们需要在真正执行关机命令之前播放动画。参考引用[4],关机流程中会弹出一个关机菜单,当用户确认关机后,会进入关机流程。我们可以在执行`reboot(RB_POWER_OFF)`之前启动动画播放。 具体修改位置可能在`ShutdownThread.java`中。例如,在`shutdownInner`方法中,在调用`PowerManagerService`的`shutdown`方法之前,启动一个服务来播放动画。 但是,由于关机过程需要卸载文件系统,因此动画播放必须在只读文件系统挂载之前完成。因此,我们需要将动画文件放在一个不会被卸载的分区(比如`/system`分区)中,并在关机流程早期播放动画。 3. **修改关机流程代码**: 在`frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java`中,找到`run`方法,在`beginShutdown`之后,我们可以启动动画播放。 伪代码修改如下: ```java // 在run()方法中 ... beginShutdown(); // 启动关机动画 playShutdownAnimation(); ... ``` 然后,我们需要实现`playShutdownAnimation`方法,该方法可以通过调用一个本地服务或者直接执行一个命令来播放动画。 实际上,Android系统本身可能没有内置关机动画播放功能,因此我们需要参考开机动画的播放机制(`BootAnimation`服务)来实现一个类似的`ShutdownAnimation`服务。 4. **实现关机动画服务**: 我们可以参考开机动画的代码(`BootAnimation`),编写一个`ShutdownAnimation`服务。该服务同样是一个本地服务,通过读取`shutdownanimation.zip`文件来播放动画。 在`ShutdownThread`中,我们可以通过发送意图或者直接启动这个服务来播放动画。 5. **修改权限和资源**: 确保动画文件有正确的权限(通常为644),并且服务有权限读取该文件。 6. **测试**: 由于修改了系统核心代码,需要重新编译系统,并刷机测试。 **注意事项**: - 由于关机过程需要保证可靠性,动画播放时间不能过长,否则会导致关机时间过长,影响用户体验。 - 如果动画播放失败,应该能够跳过并继续关机流程。 **示例代码片段**: 在`ShutdownThread.java`的`run`方法中,添加如下代码: ```java private void playShutdownAnimation() { try { // 启动关机动画服务 Intent intent = new Intent(); intent.setClassName(&quot;com.android.systemui&quot;, &quot;com.android.systemui.ShutdownAnimation&quot;); mContext.startService(intent); // 为了确保动画有足够时间开始,可以稍微等待一下 Thread.sleep(500); } catch (Exception e) { Log.e(TAG, &quot;Failed to play shutdown animation&quot;, e); } } ``` 然后,在`ShutdownAnimation`服务中,我们需要实现类似`BootAnimation`的渲染逻辑,但使用`shutdownanimation.zip`作为资源。 **替代方案**: 如果不想修改系统代码,可以尝试在系统关机前通过应用层播放动画。但是,这种方法不可靠,因为应用层在关机过程中会被杀死。 因此,推荐在系统底层添加关机动画服务。 **总结**: 实现高通平台Android关机动画需要修改系统源码,添加关机动画服务,并在关机流程中适时调用。这需要对Android关机流程和动画播放机制有深入理解。 &sect;&sect;相关问题&sect;&sect; 1. 如何制作Android系统的关机动画压缩包? 2. 在Android系统中,如何添加一个新的系统服务(如ShutdownAnimation)? 3. 如何在高通平台Android源码中修改关机流程?&lt;/think&gt;### 高通平台 Android 实现关机动画的解决方案 在高通平台实现 Android 关机动画,可通过以下两种方案实现,核心是修改系统底层的关机流程并添加动画资源: --- #### **方案一:修改系统关机流程(推荐)** 1. **添加关机动画服务** - 在 `frameworks/base/services/core/java/com/android/server/power/` 创建 `ShutdownAnimation.java` - 核心代码参考开机动画服务 `BootAnimation`: ```java public class ShutdownAnimation extends Thread { @Override public void run() { // 加载关机动画资源 /system/media/shutdownanimation.zip playAnimation(); } } ``` 2. **集成到关机流程** - 修改 `ShutdownThread.java`: ```java // 在 shutdownInner() 方法中添加 if (mReboot) { // 重启逻辑 } else { new ShutdownAnimation().start(); // 启动关机动画 PowerManagerService.lowLevelShutdown(); // 调用底层关机 } ``` - 同步修改 `android_reboot.c` 确保动画播放完毕再断电[^3] 3. **添加资源文件** - 创建关机动画包:`shutdownanimation.zip`(包含 PNG 序列帧和 `desc.txt` 描述文件) - 部署路径:`/system/media/` - 权限设置:`chmod 644 /system/media/shutdownanimation.zip` --- #### **方案二:替换系统资源(快速实现)** 1. **利用现有开机动画机制** - 将关机动画重命名为 `bootanimation.zip` - 在关机流程中临时替换文件: ```java // ShutdownThread.java 中增加 FileUtils.copyFile( new File(&quot;/custom/shutdownanimation.zip&quot;), new File(&quot;/system/media/bootanimation.zip&quot;) ); ``` 2. **修改高通专属路径** - 针对高通平台的特殊路径: ```bash /vendor/qcom/proprietary/qrdplus/Extension/apps/ShutdownAnimation ``` - 在此目录添加关机动画资源[^2] --- #### **关键配置** 1. **权限声明** - 在 `AndroidManifest.xml` 添加系统权限: ```xml &lt;uses-permission android:name=&quot;android.permission.SHUTDOWN&quot; /&gt; &lt;uses-permission android:name=&quot;org.codeaurora.permission.POWER_OFF_ALARM&quot; /&gt; [^1] ``` 2. **内核级修改** - 确保内核支持关机延时: ```c // kernel/reboot.c kernel_power_off_prepare(); mdelay(3000); // 预留3秒动画时间 ``` 3. **动画规范** - 帧率控制:`desc.txt` 示例: ``` 720 1280 30 p 1 0 part0 p 0 0 part1 ``` --- #### **验证步骤** 1. 编译刷写修改后的系统镜像 2. 触发关机命令: ```bash adb shell reboot -p ``` 3. 通过串口日志确认动画加载: ``` I/ShutdownAnimation: Playing shutdown animation... ``` &gt; **注意事项**: &gt; 1. 动画时长需控制在 3 秒内避免用户感知延迟 &gt; 2. 优先使用方案一,避免开机动画被覆盖 &gt; 3. 高通平台需检查 `RB_POWER_OFF` 的硬件兼容性[^3][^4]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值