zoukankan      html  css  js  c++  java
  • (转)Android关机AppWidget的实现

    1.背景
       转到Android组多日,总有些空虚感,因为之前在WinCE都是做的很底层的驱动,像显示驱动、USB Device驱动、USB Host EHCI、OHCI、2D加速驱动,显示驱动还使用到了NEON机器码!没错是机器码,因为VS2005的ARMASM编译器不支持Cortex-A8才有的NEON指令,所以只好写机器码代替。而在Android这边因为刚刚入手,Framework都不是很熟,只能先做一些简单的任务,这周主要就是完成一个关机的Appwidget。
       Appwidget直译是窗口小部件,类似Win7系统里面桌面中的小闹钟、日历等,在Android中可以自由拖放。下面是一个闹钟的Appwidget。

      好了,废话不多说,我们先分析怎么实现这些功能。
    2.分析
    2.1Android关机流程
       Android关机流程的介绍网上很多,现在摘抄一段如下:

    关机动作从按键触发中断,linux kernel层给android framework层返回按键事件进入 framework层,再从 framework层到kernel层执行kernel层关机任务。
    长按键对应的handler代码:
    frameworks/policies/base/phone/com/android/internal/policy/impl/PhoneWindowManager.java
    Runnable mPowerLongPress;
    private final Runnable mPowerLongPress = new Runnable() {
    public void run() {
    if (!mPowerKeyHandled) {
    mPowerKeyHandled = true;
    performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
    sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
    showGlobalActionsDialog();
    }
    }
    };
    mPowerLongPress 启动关机对话框
    (frameworks/policies/base/phone/com/android/internal/policy/impl/GlobalActions.java)
    如果我们选择Power OFF’,会调用 ShutdownThread.shutdown. 启动关机线程执行关机动作。
    frameworks/base/core/java/com/android/internal/app/ShutdownThread.java
    真正关机 流程:
    (1)广播全局事件, ACTION_SHUTDOWN Intent
    (2)shutdown ActivityManager 服务
    (3) 停止蓝牙服务
    (4) 停止 电话服务 (radio phone service)
    (5)停止mount 服务
    (6) 调用 Power.shutdown() 进入native 层
    frameworks/base/core/java/android/os/Power.java
    power的native实现代码:
    frameworks/base/core/jni/android_os_Power.cpp
    static void android_os_Power_shutdown(JNIEnv *env, jobject clazz)
    {
    sync();
    #ifdef HAVE_ANDROID_OS
    reboot(RB_POWER_OFF);
    #endif
    }
    sync, reboot 为linux系统调用,进入linux内核关机流程。
    完毕。
     

    仔细按照上面说的流程跟下去,确实是这样的,只不过根据产品的不同,会有一定的修改,例如产品是平板电脑,就会比手机少很多废话对话框,如果是智能电视,则又会有不同对于这个任务来说,比较重要的ShutdownThread.java这个文件,这个文件启动了关机的对话框,关机对话框效果如图:

    PhoneWindowManager中调用ShutdownThread的代码如下:

    1 mPowerKeyHandled = true;
    2 performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
    3 sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
    4 ShutdownThread.shutdown(mContext, true);

    传入第一个参数不解释,第二个参数是是否显示对话框,true就是显示


    2.2应用中关机的方法

      凭借网络这个好老师,我找到了好几种实现关机的方法,一是通过向控制台写shutdown命令完成,Android是建立在Linux基础上的,所以这种方法需要Root。
      二是启动ShutdownThread中的对话框,PhoneWindowManager直接通过一句
    ShutdownThread.shutdown(mContext, true);就启动了,后面我也试过这种方法,确实可以启动,但是点击确定却一直关不了机。所以靠谱的方法还是通过Intent启动。方法如下

    1 Intent shutdown = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
    2 shutdown.putExtra(Intent.EXTRA_KEY_CONFIRM, true);
    3 shutdown.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
    4 startActivity(shutdown);

       这上面的一些Flag值AndroidSDK中并没有公开,所以工程要放在Android源码中才能编译通过,除此之外还需要设置系统权限,需要通过修改AndroidManfast.xml文件实现。


    2.3Appwidget的框架
       
    个人感觉AppWidget像是嵌入launcher中的View,和AppWidget本身Activity的不是运行在同一个进程中,所以控制Appwidget都需要用RemoteViews控制。
        桌面上有多个不同的AppWidget,如何得知哪一个被激活呢,这主要通过广播完成,广播发出的是在AndroidManfast.xml中定义好的android:name,接收时需要在OnReceive()方法中进行判断。
        AndroidMainfast.xml摘录如下:

    <receiver android:name="AppWidget">
                <intent-filter>
                <action 
                    android:name="android.appwidget.action.APPWIDGET_UPDATE">
                </action>
                </intent-filter>
                    <meta-data android:name="android.appwidget.provider"
                                android:resource="@xml/appwidget01" />
                 <intent-filter>
                    <action android:name="com.android.shutdownapp"></action>
                </intent-filter>
    </receiver>

       Appwidget的处理片段如下:

     1 @Override
     2     public void onReceive(Context context, Intent intent)
     3     {
     4         if (intent.getAction().equals(broadCastString))
     5         {                
     6             RemoteViews remoteViews = new RemoteViews(context.getPackageName(),R.layout.appwidgetlayout);
     7             remoteViews.setTextViewText(R.id.btnSend, "shutdown");    
     8             AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
     9             ComponentName componentName = new ComponentName(context,AppWidget.class);
    10             appWidgetManager.updateAppWidget(componentName, remoteViews);
    11         }
    12         super.onReceive(context, intent);
    13     }

    3.实现

    3.1建立Appwidget工程
       
    同建立普通Activity工程一样,不过要手动添加一些XML文件,这里有一篇写的很不错的博文,推荐一下,可以按照他介绍的方法一步步建立工程:
            http://www.cnblogs.com/qianlifeng/archive/2011/03/26/1996407.html

    3.2把Eclipse工程加入到Android工程中
       
    建立了Demo之后,需要将其添加到Android工程目录中去编译,因为我们调用了一些SDK中并没有公开的方法,建议拷贝到的目录是\android4.0.1\development\apps下,然后删除一些Eclipse的文件,只留下\res、\src、\AndroidManifast.xml这几个文件和目录。
       接着我们需要一个Android.mk文件,简单,到同级的其他工程中拷贝一个就是,但需要修改LOCAL_PACKAGE_NAME := 为你工程的名字,另外还要有LOCAL_CERTIFICATE := platform这一行存在,我的MK文件如下:

    LOCAL_PATH:= $(call my-dir)
    include $(CLEAR_VARS)
    LOCAL_MODULE_TAGS := optional
    LOCAL_SRC_FILES := $(call all-subdir-java-files)
    LOCAL_PACKAGE_NAME := ShutdownAppWidget
    LOCAL_CERTIFICATE := platform
    include $(BUILD_PACKAGE)

    3.3添加工程的系统属性
       
    因为调用到了系统对话框,所以整个工程需要系统属性,这通过修改AndroidManifastwen
    文件实现,修改后的文件内容如下(注意其中绿色的内容):

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.android.shutdownapp"
         android:sharedUserId="android.uid.system">
    
        <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW "/>
        <uses-permission android:name="android.permission.BIND_APPWIDGET" />
        
        <application android:icon="@drawable/icon" android:label="@string/app_name">
            <activity android:name=".ShutdownApp"
                      android:label="@string/app_name">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
            <receiver android:name="AppWidget">
                <intent-filter>
                    <action android:name="android.appwidget.action.APPWIDGET_UPDATE"></action>
                </intent-filter>
                <meta-data android:name="android.appwidget.provider"
                    android:resource="@xml/appwidget01" />
                <intent-filter>
                    <action android:name="com.android.shutdownapp"></action>
                </intent-filter>
            </receiver>
        </application>
        
    </manifest>

    3.4把Appwidget添加到launcher中
        添加到Launcher中可以实现烧写镜像后AppWidget就存在于界面上,不需要人手工去拖,对应的配置文件在以下的位置:

    点击(此处)折叠或打开

    1. /android/packages/apps/Launcher2/res/xml/default_workspace.xml

        这个文件的摘录如下:

    <favorites xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher">
        <!-- Far-left screen [0] -->
    
        <!-- Left screen [1] -->
        <appwidget
            launcher:packageName="com.android.settings"
            launcher:className="com.android.settings.widget.SettingsAppWidgetProvider"
            launcher:screen="1"
            launcher:x="0"
            launcher:y="3"
            launcher:spanX="4"
            launcher:spanY="1" />
    
        <!-- Middle screen [2] -->
        <appwidget
            launcher:packageName="com.android.deskclock"
            launcher:className="com.android.alarmclock.AnalogAppWidgetProvider"
            launcher:screen="2"
            launcher:x="1"
            launcher:y="0"
            launcher:spanX="2"
            launcher:spanY="2" />

          文件中已经有配置的例子,可以按照其中的例子自己去配,需要主要的是其中的坐标不是像素,而是一些定义好的点,在点击屏幕安放AppWidget时就可以看见一些点,这些就是对应的坐标位置。


    4.效果
    AppWidget显示如下:

    按下后效果如下

    还不太完善,显示了“Android系统”的背景还不知如何去掉,另外界面也不太友好

    5.总结
      
    和同事沟通确实很重要,之前一直不知道怎样在Launcher中配置Appwidget,结果问了一个阅读过Launcher的同事很快就找到方法了。
       另外发现自己有点太过于依赖网络,这点确实需要改正一下,还是自己思考的进步快。

    转自:http://blog.chinaunix.net/uid-26926660-id-3201895.html

  • 相关阅读:
    [Apache commons系列]DBUtils 示例代码
    关于syslog
    Phoenix Tips (1)
    Phoenix 常用函数
    Phoenix 的数据类型
    最简单的Phoenix 访问 HBase例子
    SVNKIT 主要方法
    Linux 集群时钟同步
    Linux 集群时钟同步
    Linux 下定时提交SVN
  • 原文地址:https://www.cnblogs.com/chenbin7/p/2618540.html
Copyright © 2011-2022 走看看