原文作者:铸道的汉子 原文博客: samurai.blog.chinaunix.net 原文地址: http://blog.chinaunix.net/uid-26926660-id-3201895.html --------------------------------------------------------------------------

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关机流程的介绍网上很多,现在摘抄一段如下:

                
  1. 关机动作从按键触发中断,linuxkernel层给androidframework层返回按键事件进入framework层,再从framework层到kernel层执行kernel层关机任务。
  2. 长按键对应的handler代码:
  3. frameworks/policies/base/phone/com/android/internal/policy/impl/PhoneWindowManager.java
  4. RunnablemPowerLongPress;
  5. privatefinalRunnablemPowerLongPress=newRunnable(){
  6. publicvoidrun(){
  7. if(!mPowerKeyHandled){
  8. mPowerKeyHandled=true;
  9. performHapticFeedbackLw(null,HapticFeedbackConstants.LONG_PRESS,false);
  10. sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
  11. showGlobalActionsDialog();
  12. }
  13. }
  14. };
  15. mPowerLongPress启动关机对话框
  16. (frameworks/policies/base/phone/com/android/internal/policy/impl/GlobalActions.java)
  17. 如果我们选择PowerOFF’,会调用ShutdownThread.shutdown.启动关机线程执行关机动作。
  18. frameworks/base/core/java/com/android/internal/app/ShutdownThread.java
  19. 真正关机流程:
  20. (1)广播全局事件,ACTION_SHUTDOWNIntent
  21. (2)shutdownActivityManager服务
  22. (3)停止蓝牙服务
  23. (4)停止电话服务(radiophoneservice)
  24. (5)停止mount服务
  25. (6)调用Power.shutdown()进入native层
  26. frameworks/base/core/java/android/os/Power.java
  27. power的native实现代码:
  28. frameworks/base/core/jni/android_os_Power.cpp
  29. staticvoidandroid_os_Power_shutdown(JNIEnv*env,jobjectclazz)
  30. {
  31. sync();
  32. #ifdefHAVE_ANDROID_OS
  33. reboot(RB_POWER_OFF);
  34. #endif
  35. }
  36. sync,reboot为linux系统调用,进入linux内核关机流程。
  37. 完毕。

仔细按照上面说的流程跟下去,确实是这样的,只不过根据产品的不同,会有一定的修改,例如产品是平板电脑,就会比手机少很多废话对话框,如果是智能电视,则又会有不同,对于这个任务来说,比较重要的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. Intentshutdown=newIntent(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摘录如下:

                
  1. <receiverandroid:name="AppWidget">
  2. <intent-filter>
  3. <action
  4. android:name="android.appwidget.action.APPWIDGET_UPDATE">
  5. </action>
  6. </intent-filter>
  7. <meta-dataandroid:name="android.appwidget.provider"
  8. android:resource="@xml/appwidget01"/>
  9. <intent-filter>
  10. <actionandroid:name="com.android.shutdownapp"></action>
  11. </intent-filter>
  12. </receiver>

Appwidget的处理片段如下:

                
  1. @Override
  2. publicvoidonReceive(Contextcontext,Intentintent)
  3. {
  4. if(intent.getAction().equals(broadCastString))
  5. {
  6. RemoteViewsremoteViews=newRemoteViews(context.getPackageName(),R.layout.appwidgetlayout);
  7. remoteViews.setTextViewText(R.id.btnSend,"shutdown");
  8. AppWidgetManagerappWidgetManager=AppWidgetManager.getInstance(context);
  9. ComponentNamecomponentName=newComponentName(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文件如下:

                
  1. LOCAL_PATH:=$(callmy-dir)
  2. include$(CLEAR_VARS)
  3. LOCAL_MODULE_TAGS:=optional
  4. LOCAL_SRC_FILES:=$(callall-subdir-java-files)
  5. LOCAL_PACKAGE_NAME:=ShutdownAppWidget
  6. LOCAL_CERTIFICATE:=platform
  7. include$(BUILD_PACKAGE)

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

                
  1. <?xmlversion="1.0"encoding="utf-8"?>
  2. <manifestxmlns:android="http://schemas.android.com/apk/res/android"
  3. package="com.android.shutdownapp"
  4. android:sharedUserId="android.uid.system">
  5. <uses-permissionandroid:name="android.permission.INTERNAL_SYSTEM_WINDOW"/>
  6. <uses-permissionandroid:name="android.permission.BIND_APPWIDGET"/>
  7. <applicationandroid:icon="@drawable/icon"android:label="@string/app_name">
  8. <activityandroid:name=".ShutdownApp"
  9. android:label="@string/app_name">
  10. <intent-filter>
  11. <actionandroid:name="android.intent.action.MAIN"/>
  12. <categoryandroid:name="android.intent.category.LAUNCHER"/>
  13. </intent-filter>
  14. </activity>
  15. <receiverandroid:name="AppWidget">
  16. <intent-filter>
  17. <actionandroid:name="android.appwidget.action.APPWIDGET_UPDATE"></action>
  18. </intent-filter>
  19. <meta-dataandroid:name="android.appwidget.provider"
  20. android:resource="@xml/appwidget01"/>
  21. <intent-filter>
  22. <actionandroid:name="com.android.shutdownapp"></action>
  23. </intent-filter>
  24. </receiver>
  25. </application>
  26. </manifest>


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

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

这个文件的摘录如下:

                
  1. <favoritesxmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher">
  2. <!--Far-leftscreen[0]-->
  3. <!--Leftscreen[1]-->
  4. <appwidget
  5. launcher:packageName="com.android.settings"
  6. launcher:className="com.android.settings.widget.SettingsAppWidgetProvider"
  7. launcher:screen="1"
  8. launcher:x="0"
  9. launcher:y="3"
  10. launcher:spanX="4"
  11. launcher:spanY="1"/>
  12. <!--Middlescreen[2]-->
  13. <appwidget
  14. launcher:packageName="com.android.deskclock"
  15. launcher:className="com.android.alarmclock.AnalogAppWidgetProvider"
  16. launcher:screen="2"
  17. launcher:x="1"
  18. launcher:y="0"
  19. launcher:spanX="2"
  20. launcher:spanY="2"/>


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

4.效果
AppWidget显示如下:

按下后效果如下:

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

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

--------------------------------------------------------------------------

原文作者:铸道的汉子 原文博客: samurai.blog.chinaunix.net 原文地址: http://blog.chinaunix.net/uid-26926660-id-3201895.html

更多相关文章

  1. Android(安卓)Studio如何引用外部Library工程
  2. android 底部菜单写法之FragmentTabHost
  3. react-native开发实例之替换默认logo——android实现
  4. Android(安卓)JNI和NDK学习(3)--Android.mk分析
  5. Android发送邮件
  6. android上传图片至服务器
  7. android 自定义progressbar 样式
  8. Android屏幕截图并保存截取屏幕的图片到指定文件
  9. Android(安卓)编译App报错 找不到android.support.annotation.Ke

随机推荐

  1. 初出茅庐
  2. Android之Adapter用法总结
  3. Android(安卓)的上下文菜单: Context Menu
  4. Android(安卓)NDK探究奥秘一:Android(安卓
  5. Android中Adapter用法总结
  6. 关于android的tween animation
  7. 浅析android系统设计中的回调思想
  8. shape基本用法及全部属性定义
  9. Android多媒体学习三:实现自己的Camera
  10. 添加xmlns:android="http://schemas.andr