ShutdownThread - 动画 & 音乐

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

 

      在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;嗯,用户这次聚焦在ShutdownThread.sIsStarted这个静态布尔变量上,看来ta正在深入分析Android关机流程。从引用[1][2][4]可以确认这个类属于系统关机核心逻辑,但具体字段未被直接引用,需要结合上下文推导。 用户应该是个Android系统开发者,可能在调试关机异常或者定制关机流程。ta追问的sIsStarted显然是个状态标识,这类变量在系统代码中通常用于防止重复初始化&mdash;&mdash;就像关机这种只能触发一次的操作。 回忆Android源码模式,类似sIsStarted的字段往往配合synchronized使用。比如在ShutdownThread.shutdownInner()里肯定有这样的检查: ```java if (sIsStarted) { Log.w(TAG, &quot;Request to shutdown already running&quot;); return; } ``` 这样设计能避免关机过程中重复调用导致的并发问题。用户可能遇到的是关机流程被意外触发两次的bug,或者想自定义关机前置条件。 另外从引用[2]的reboot()参数看,这个状态量应该也控制关机动画的显示逻辑。当sIsStarted=true时,后续的进度条更新和动画播放需要依赖这个状态。不过具体到代码实现,还是建议用户直接查阅frameworks/base/services/java/com/android/server/power/ShutdownThread.java最可靠。 用户没有提Android版本,但引用[4]提到Android 10,需要注意不同版本实现可能有差异。比如Android 12之后关机流程改用了ShutdownCheckPoint机制,但sIsStarted的基本作用应该保留。 &lt;/think&gt;根据提供的引用信息及Android关机流程分析,`ShutdownThread.sIsStarted`的作用及相关信息如下: --- ### **作用** 1. **状态跟踪** `sIsStarted`是`ShutdownThread`类的静态布尔变量(`static boolean`),**用于标记关机/重启流程是否已启动**。其核心作用是**防止重复触发关机操作**,确保系统在同一时刻仅执行一次关机或重启流程。 2. **并发控制** 在多线程场景(如同时收到多个关机请求)中,通过检查`sIsStarted`的值: ```java if (sIsStarted) { Log.w(TAG, &quot;Request to shutdown already running, ignoring.&quot;); return; // 直接退出,避免重复执行 } ``` 可有效避免因并发调用导致的流程冲突或资源竞争问题。 --- ### **使用场景** 1. **关机/重启入口函数** 在`shutdown()`或`reboot()`方法中**初始化流程前**进行状态检查: ```java public static void shutdown(Context context, boolean confirm) { if (sIsStarted) { // 检查状态 return; // 已启动则退出 } sIsStarted = true; // 标记为已启动 // 执行关机逻辑 } ``` 2. **关机动画与进度控制** 在绘制关机动画或更新进度条时,需依赖`sIsStarted`判断当前是否处于有效关机流程中,避免无效资源占用。 --- ### **原理** - **静态变量特性** 由于`sIsStarted`是静态变量(`static`),其生命周期与类绑定而非对象实例。因此在整个进程内**全局唯一**,所有对`ShutdownThread`的调用共享同一状态标识。 - **线程安全设计** 关机入口方法(如`shutdownInner()`)通常通过**同步锁(`synchronized`)** 保护`sIsStarted`的修改与读取,确保原子性操作: ```java private static synchronized void shutdownInner(Context context, boolean confirm) { if (sIsStarted) return; sIsStarted = true; // 后续逻辑 } ``` --- ### **关联代码逻辑** 从引用[2]的`shutdown()`/`reboot()`方法可推断其典型流程: 1. 设置关机参数(如`mReboot`、`mReason`)。 2. 调用`shutdownInner()`,其中包含对`sIsStarted`的检查与更新。 3. 若`sIsStarted=true`,则跳过后续操作;否则执行动画加载、服务终止等关机逻辑[^2][^4]。 --- ### **潜在问题** 若`sIsStarted`未被正确重置(如关机中途发生异常),可能导致后续无法再次触发关机。但系统关机属于单次进程操作,通常无需主动重置。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值