zoukankan      html  css  js  c++  java
  • 实现金山卫士的小控件(Widget)

    0.思路:反编译金山的apk,在清单文件中根据android.appwidget.action.APPWIDGET_UPDATE找到入口,进而一步步向下

    1. 自定义类继承自AppWidgetProvider

    public class ProcWidget extends AppWidgetProvider {
    
    }

    2. 在AndroidManifest.xml文件中配置,AppWidgetProvider类继承自BroadcastReceiver,因此配置receiver节点

    <receiver android:name="com.cbooy.widget.ProcWidget" >
        <intent-filter>
            <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
        </intent-filter>
    
        <meta-data
            android:name="android.appwidget.provider"
            android:resource="@xml/process_widget_provider" />
    </receiver>

    3.在res目录下新建xml目录,新建文件process_widget_provider.xml

    <?xml version="1.0" encoding="utf-8"?>
    <appwidget-provider 
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:initialLayout="@layout/process_widget"
        android:minHeight="72.0dip"
        android:minWidth="294.0dip"
       <!--设置更新时间为0,Google会自行判断,此处时间最小为30分钟,因此设置时间小于30分钟会按照30分钟处理-->
    android:updatePeriodMillis="0" />

    4. 在layout下新建布局文件 process_widget.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout 
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/widget_bg_portrait"
        android:gravity="center_vertical" >
    
        <LinearLayout
            android:layout_width="0.0dip"
            android:layout_height="fill_parent"
            android:layout_marginLeft="5.0dip"
            android:layout_weight="1.0"
            android:background="@drawable/widget_bg_portrait_child"
            android:gravity="center_vertical"
            android:orientation="vertical"
            android:paddingBottom="3.0dip"
            android:paddingTop="3.0dip" >
    
            <TextView
                android:id="@+id/process_count"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="10.0dip"
                android:textAppearance="@style/widget_text"
                android:text="正在运行的软件:17" />
    
            <ImageView
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="1.0dip"
                android:layout_marginTop="1.0dip"
                android:background="@drawable/widget_bg_portrait_child_divider" />
    
            <TextView
                android:id="@+id/process_memory"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="10.0dip"
                android:textAppearance="@style/widget_text"
                android:text="可用内存:430.22mb" />
        </LinearLayout>
    
        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal"
            android:orientation="vertical" >
    
            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:gravity="center_vertical" >
    
                <ImageView
                    android:layout_width="20.0dip"
                    android:layout_height="20.0dip"
                    android:src="@drawable/main_icon" />
    
                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="@string/app_name"
                    android:textColor="#ffffffff" />
            </LinearLayout>
    
            <Button
                android:id="@+id/btn_clear"
                android:layout_width="90.0dip"
                android:layout_height="wrap_content"
                android:layout_centerVertical="true"
                android:layout_marginTop="5.0dip"
                android:background="@drawable/function_greenbutton_selector"
                android:text="一键清理"
                android:textColor="@drawable/function_greenbutton_textcolor_selector" />
        </LinearLayout>
    
    </LinearLayout>

    5. 相关资源文件

      style

        <style name="widget_text">
            <item name="android:textSize">16.0dip</item>
            <item name="android:textColor">#ff000000</item>
        </style>

      drawable下资源function_greenbutton_selector.xml

    <?xml version="1.0" encoding="utf-8"?>
    <selector
      xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:state_pressed="true" android:drawable="@drawable/function_greenbutton_pressed" />
        <item android:state_focused="true" android:drawable="@drawable/function_greenbutton_pressed" />
        <item android:drawable="@drawable/function_greenbutton_normal" />
    </selector>

      drawable下资源function_greenbutton_textcolor_selector.xml

    <?xml version="1.0" encoding="utf-8"?>
    <selector
      xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:state_pressed="true" android:color="#80ffffff" />
        <item android:state_focused="true" android:color="#80ffffff" />
        <item android:state_selected="true" android:color="#80ffffff" />
        <item android:color="#ffffffff" />
    </selector>

      drawable下的图片资源

                                                                  

    6. 实现修改widget页面的数据,由于widget是显示的桌面程序中,因此与自己的程序属于两个不同的进程,因此修改数据是跨进程的通信

      补全ProcWidget类

    public class ProcWidget extends AppWidgetProvider {
    
        @Override
        public void onReceive(Context context, Intent intent) {
            super.onReceive(context, intent);
    
            protectServiceRunning(context);
        }
    
        @Override
        public void onUpdate(Context context, AppWidgetManager appWidgetManager,int[] appWidgetIds) {
            super.onUpdate(context, appWidgetManager, appWidgetIds);
    
            protectServiceRunning(context);
        }
    
        @Override
        public void onEnabled(Context context) {
            super.onEnabled(context);
    
            startRefreshWidgetUIService(context);
        }
    
        @Override
        public void onDisabled(Context context) {
            super.onDisabled(context);
    
            stopRefreshWidgetUIService(context);
        }
    
        /**
         * 若用户在后台清理掉了此进程,在widget有任何操作时都会调用此方法,因此可以保护服务开启
         * 
         * @param context
         */
        private void protectServiceRunning(Context context) {
            boolean isRefreshWidgetServiceRunning = ServicesUtil.isServiceRun(
                    context, RefreshWidgetUIService.class.getName());
            if (!isRefreshWidgetServiceRunning) {
                startRefreshWidgetUIService(context);
            }
        }
    
        /**
         * 开启widget刷新UI服务
         * 
         * @param context
         */
        private void startRefreshWidgetUIService(Context context) {
            Intent intent = new Intent(context, RefreshWidgetUIService.class);
    
            context.startService(intent);
        }
    
        /**
         * 停止widget刷新服务
         * 
         * @param context
         */
        private void stopRefreshWidgetUIService(Context context) {
            Intent intent = new Intent(context, RefreshWidgetUIService.class);
    
            context.stopService(intent);
        }
    }

    新建服务,来完成数据的刷新功能,要在清单文件中注册

    public class RefreshWidgetUIService extends Service {
        
        private Timer timer;
        
        private TimerTask timerTask;
        
        private AppWidgetManager widgetManager;
        
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
        
        @Override
        public void onCreate() {
            super.onCreate();
            
            schedultTask();
        }
        
        /**
         * 本地 调度任务,立即执行,3秒钟执行一次
         */
        private void schedultTask() {
            timer = new Timer();
            
            timerTask = new TimerTask() {
                @Override
                public void run() {
                    refreshWidgetData();
                }
            };
            
            timer.schedule(timerTask, 0, 3 * 1000);
        }
        
        /**
         * 刷新数据
         */
        private void refreshWidgetData() {
            widgetManager = AppWidgetManager.getInstance(RefreshWidgetUIService.this);
            
            // 设置更新的组件
            ComponentName componentName = new ComponentName(RefreshWidgetUIService.this,ProcWidget.class);
            
            // 跨进程通信使用的View
            RemoteViews views = new RemoteViews(getPackageName(),R.layout.process_widget);
            
            // 进程管理的工具类,提供getRunningProcSize()获取运行进程的个数,getAvaliMem()获取可用内存大小
            ProcessManage pm = ProcessManage.buildWithContext(RefreshWidgetUIService.this);
            
            views.setTextViewText(R.id.process_count, new StringBuilder("正在运行的进程:").append(pm.getRunningProcSize()));
            
            views.setTextViewText(R.id.process_memory, new StringBuilder("可用内存:").append(pm.getAvaliMem()));
            
            Intent intent = new Intent();
            
            intent.setAction("com.cbooy.mmap.clean_procs");
            
            // 描述一个动作,此动作由另外的进程来执行,此时是桌面发出来一个广播,来接受响应事件
            // FLAG_UPDATE_CURRENT 第二次消息会把第一次的消息给覆盖掉
            PendingIntent pendingIntent = PendingIntent.getBroadcast(RefreshWidgetUIService.this, 0, intent , PendingIntent.FLAG_UPDATE_CURRENT);
            
            views.setOnClickPendingIntent(R.id.btn_clear, pendingIntent );
            
            widgetManager.updateAppWidget(componentName, views);
        }
        
        @Override
        public void onDestroy() {
            super.onDestroy();
            
            timer.cancel();
            
            timerTask.cancel();
            
            timer = null;
            
            timerTask = null;
        }
    }

    7.注意事项

    App必须安装在手机内存中才可以展示,安装在sdcard中则无法展示,若配有android:installLocation="preferExternal" 则修改,或移动回内存中

    8.效果图

    注:以上代码有一些细节可能会有出入,都是一些资源文件类的名字,修改很简单,还有一些清单文件的注册请注意

  • 相关阅读:
    七类网线 支持10gb/s的速度的计算方法
    Linux 压缩 解压缩 命令相关
    [至顶网] Win2019 发布 LTSC 10年支持期
    安装报错信息 改天找人帮忙看下.
    [学习]仿照cnblog 搭建 Oracle RAC 双节点 困.. 后续做不下去了..
    Windows 作为 openssl server端时的处理
    阿里云构建Kafka单机集群环境
    nginx解决跨域
    让网站永久拥有HTTPS
    ORA-01940: cannot drop a user that is currently connected 问题解析
  • 原文地址:https://www.cnblogs.com/cbooy/p/4753294.html
Copyright © 2011-2022 走看看