当前活动 监控android_监控回放 android - CSDN
  • 1,监控当前应用生命周期 2,调试应用,接手一个庞大的应用,快速知道当前是那个activity ,或者 fragment(会在另一篇中讲) 3,如果你正常开发SDK ,当时你又想获取当前的页面的activity  4,不修改源码即可在...

    说下场景:

    1,监控当前应用生命周期

    2,调试应用,接手一个庞大的应用,快速知道当前是那个activity ,或者 fragment(会在另一篇中讲)

    3,如果你正常开发SDK ,当时你又想获取当前的页面的activity 

    4,不修改源码即可在生命周期内添加自己的业务逻辑

    当然你能难道app activity 生命周期,可以做很多业务,看我们自己怎么想了。

     

    说了这么多, 今天要说的东西就是 Application    registerActivityLifecycleCallbacks

     

    看下方法签名:

    public void registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks callback);

     

    这方法能监控整个app 的activity    所以在application 中,看下 Application.ActivityLifecycleCallbacks  

    public interface ActivityLifecycleCallbacks {
        void onActivityCreated(Activity var1, Bundle var2);
    
        void onActivityStarted(Activity var1);
    
        void onActivityResumed(Activity var1);
    
        void onActivityPaused(Activity var1);
    
        void onActivityStopped(Activity var1);
    
        void onActivitySaveInstanceState(Activity var1, Bundle var2);
    
        void onActivityDestroyed(Activity var1);
    }
    

    位于Applicaiton内部类  是一个接口,只要接触过的android 的小姐姐,小哥哥们,是不是看着这些函数相当熟悉 。是不是涵盖activity  重要的生命周期函数 下面对应对比一下。

    ActivityLifecycleCallbacks
    Activity
    onActivityCreated
    onCreate
    onActivityStarted
    onStart
    onActivityResumed
    onResume
    onActivityPaused
    onPause
    onActivityStopped
    onStop
    onActivitySaveInstanceState onSaveInstanceState
    onActivityDestroyed
    onDestroy

    通过这个表已经说明了一切,既然我们拿到Activity生命周期,其实就可以说在activity 可以插入我们想做的一切逻辑了;

    写一个简单的例子比如我们想在 一个activity onCreate()函数中插入一个弹窗,那么此时你又拿不到这个这个Activity 怎么办

    第一步  我们肯定只要要操作那个activity ,但是具体的类名不知道怎么办 ,我们通过app 直接操作,看日志输出你就可以得到当前activity 类的全路径名称了。 


     

    第二步,我们既然能拿到activity 的类名,有知道了这个activity 的生命周期那就好办了。 剩下的工作就和平时弹窗一下,就是弹出dialog

     

    这样就可以完成我们的需求了  ,睿智聪明的你我相信你已经明白怎么使用了!

    展开全文
  • 目前主流的监控Foreground Activity的方法,主要有以下三种,各有利弊,还没有找到一个彻底的完美解决方案! 一、使用 AccessibilityService You can detect the currently active window by using an ...

    目前主流的监控Foreground Activity的方法,主要有以下三种,各有利弊,还没有找到一个彻底的完美解决方案!

    一、使用 AccessibilityService

    • 通过 AccessibilityService可以监控当前Active Window
    • 通过回调函数 onAccessibilityEven 检查事件类型 TYPE_WINDOW_STATE_CHANGED 获知当前 window 改变了
    • 通过调用 PackageManager.getActivityInfo() 检查当前的window 是不是一个Activity

    1. 优点:

    • 经过测试Android 2.2 (API 8) ~ Android 5.1.0(API 22) 都可以正常工作,更高版本的还没有测试过
    • 不需要轮询机制,非常省电
    • 不需要申明 GET_TASKS 权限

    2. 缺点:

    • 用户必须手动在手机的设置页面开启 Android's accessibility settings 才能收到 AccessibilityEvent事件
    • 当用户手动在手机设置页面关闭 Accessibility setting后,AccessibilityService就会停止运行。你也可以在service 里面用代码 stop itself,但是一旦在代码里面停止service之后,目前还没有找到什么办法能够再次开启。
    • 当用户在设置页面开启 AccessibilityService的时候,有些第三方应用会弹出一个悬浮窗盖住屏幕,导致用户无法点击确认按钮。例如 Velis Auto Brightness 和 Lux 这些App就会干这种事。 目前还不知道如果规避这些第三方应用的恶心行为。
    • AccessibilityService 是被动触发的,无法主动获知当前Activity 的信息,除非当前Activity有所改变

    3. 代码示例

    public class WindowChangeDetectingService extends AccessibilityService {
    
        @Override
        protected void onServiceConnected() {
            super.onServiceConnected();
    
            //Configure these here for compatibility with API 13 and below.
            AccessibilityServiceInfo config = new AccessibilityServiceInfo();
            config.eventTypes = AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
            config.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
    
            if (Build.VERSION.SDK_INT >= 16)
                //Just in case this helps
                config.flags = AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
    
            setServiceInfo(config);
        }
    
        @Override
        public void onAccessibilityEvent(AccessibilityEvent event) {
            if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
                ComponentName componentName = new ComponentName(
                    event.getPackageName().toString(),
                    event.getClassName().toString()
                );
    
                ActivityInfo activityInfo = tryGetActivity(componentName);
                boolean isActivity = activityInfo != null;
                if (isActivity)
                    Log.i("CurrentActivity", componentName.flattenToShortString());
            }
        }
    
        private ActivityInfo tryGetActivity(ComponentName componentName) {
            try {
                return getPackageManager().getActivityInfo(componentName, 0);
            } catch (PackageManager.NameNotFoundException e) {
                return null;
            }
        }
    
        @Override
        public void onInterrupt() {}
    }

    4. Merge this into your AndroidManifest.xml

    <application>
        <service
            android:label="@string/accessibility_service_name"
            android:name=".WindowChangeDetectingService"
            android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
            <intent-filter>
                <action android:name="android.accessibilityservice.AccessibilityService"/>
            </intent-filter>
            <meta-data
                android:name="android.accessibilityservice"
                android:resource="@xml/accessibilityservice"/>
        </service>
    </application>

    5. Put this in res/xml/accessibilityservice.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <!-- These options MUST be specified here in order for the events to be received on first
     start in Android 4.1.1 -->
    <accessibility-service
        xmlns:tools="http://schemas.android.com/tools"
        android:accessibilityEventTypes="typeWindowStateChanged"
        android:accessibilityFeedbackType="feedbackGeneric"
        android:accessibilityFlags="flagIncludeNotImportantViews"
        android:description="@string/accessibility_service_description"
        xmlns:android="http://schemas.android.com/apk/res/android"
        tools:ignore="UnusedAttribute"/>

    6. Enabling the Service

    每个用户必须手动到手机设置页面开启 AccessibilityService 这个service才会被调用到。查看具体实现:this StackOverflow answer 

    二、使用 ActivityManager

    使用 ActivityManager有一定版本限制。官方文档有详细的说明:on API-21 as of LOLLIPOP, ActivityManager.getRunningTasks() is no longer available to third party applications: the introduction of document-centric recents means it can leak person information to the caller. For backwards compatibility, it will still return a small subset of its data: at least the caller's own tasks, and possibly some other tasks such as home that are known to not be sensitive.

    网上有人说可以用下面的方法分别调用不同的API,兼容不同的版本:

    • Pre-Lollipop: ActivityManager.getRunningTasks 
    • Lollipop: ActivityManager.getRunningAppProcesses 
    在 Android5.0 (API-21)及以上,getRunningTasks将只返回自己和 launcher,getRunningTasks 无法正确判断当前应用是否为front, 不同版本测试的结果也不一样:
    在 Android 5.0版本:
    • 1. 当用户在当前App页面时,runningTasksInfos.get(0) = 当前App
    • 2. 当用户回到launch页面时,runningTasksInfos.get(0) = launcher
    • 3. 当用户在其它App页面时,runningTasksInfos.get(0) = launcher
    在 Android 5.1, 6.0 以上版本:
    • 1. 当用户在钱盾页面时,runningTasksInfos.get(0) = 当前App
    • 2. 当用户回到launch页面时,runningTasksInfos.get(0) = launcher
    • 3. 当用户在其它App页面时,runningTasksInfos.get(0) = 当前App
    对于getRunningAppProcesses,经过我自己的测试:
    • 在Android 5.0(API-21) 版本getRunningAppProcesses可以获取所有应用的process,可以通过其来判断top package; 但是在 Android 5.1.0 (API-22)及其以上Android 6.0 (API-23)永远只返回应用自身的process,不能用来判断任意top package,但是可以用来判断当前App应用是否在前台:
    • 官方文档申明 getRunningAppProcesses() 只是用于debugging and management user interfaces,有一定的局限性,说不定哪天就不支持了
    • 依赖反射调用: ActivityManager.RunningAppProcessInfo.processState
    • 无法监控 App switcher activity.

    3. 代码示例:

    public class CurrentApplicationPackageRetriever {
        private final Context context;
        public CurrentApplicationPackageRetriever(Context context) {
            this.context = context;
        }
        public String[] get() {
            if (Build.VERSION.SDK_INT < 21)
                return getPreLollipop();
            else
                return getLollipop();
        }
        private String[] getPreLollipop() {
            @SuppressWarnings("deprecation")
            List<ActivityManager.RunningTaskInfo> tasks =
                activityManager().getRunningTasks(1);
            ActivityManager.RunningTaskInfo currentTask = tasks.get(0);
            ComponentName currentActivity = currentTask.topActivity;
            return new String[] { currentActivity.getPackageName() };
        }
    
        private String[] getLollipop() {
            final int PROCESS_STATE_TOP = 2;
            try {
                Field processStateField = ActivityManager.RunningAppProcessInfo.class.getDeclaredField("processState");
    
                List<ActivityManager.RunningAppProcessInfo> processes =
                    activityManager().getRunningAppProcesses();
                for (ActivityManager.RunningAppProcessInfo process : processes) {
                    if (
                        // Filters out most non-activity processes
                        process.importance <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
                        &&
                        // Filters out processes that are just being
                        // _used_ by the process with the activity
                        process.importanceReasonCode == 0
                    ) {
                        int state = processStateField.getInt(process);
    
                        if (state == PROCESS_STATE_TOP)
                            /*
                             If multiple candidate processes can get here,
                             it's most likely that apps are being switched.
                             The first one provided by the OS seems to be
                             the one being switched to, so we stop here.
                             */
                            return process.pkgList; 
                    }
                }
            } catch (NoSuchFieldException | IllegalAccessException e) {
                throw new RuntimeException(e);
            }
            return new String[] { };
        }
    
        private ActivityManager activityManager() {
            return (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        }
    }
    

    4. 添加 GET_TASKS 权限到 AndroidManifest.xml:

    <!--suppress DeprecatedClassUsageInspection -->
    <uses-permission android:name="android.permission.GET_TASKS" />

    三、通过反射"android.app.ActivityThread"的方式获取Top Activity

    public static Activity getRunningActivity() {
        try {
            Class activityThreadClass = Class.forName("android.app.ActivityThread");
            Object activityThread = activityThreadClass.getMethod("currentActivityThread")
                    .invoke(null);
            Field activitiesField = activityThreadClass.getDeclaredField("mActivities");
            activitiesField.setAccessible(true);
            ArrayMap activities = (ArrayMap) activitiesField.get(activityThread);
            for (Object activityRecord : activities.values()) {
                Class activityRecordClass = activityRecord.getClass();
                Field pausedField = activityRecordClass.getDeclaredField("paused");
                pausedField.setAccessible(true);
                if (!pausedField.getBoolean(activityRecord)) {
                    Field activityField = activityRecordClass.getDeclaredField("activity");
                    activityField.setAccessible(true);
                    return (Activity) activityField.get(activityRecord);
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    
        throw new RuntimeException("Didn't find the running activity");
    }
    1. 在有些手机上测试时,发现 It's API > 19,只能获取当前App的Activities
    2. 另外在当用“Map” 替换“ArrayMap”发现在4.3的手机可以工作正常. 更高版本的手机还没有测试过

    四、通过 UsageStatsManager -> queryUsageStats方法获取Top Activity

    调用 queryUsageStats 方法,有一个限制:需要用户到手机的设置页面 Security->Apps 开启Usage的访问权限!

        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
            UsageStatsManager usm = (UsageStatsManager) getSystemService("usagestats");
            long time = System.currentTimeMillis();
            List<UsageStats> appList = usm.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, time - 1000 * 1000, time);
            if (appList != null && appList.size() > 0) {
                SortedMap<Long, UsageStats> mySortedMap = new TreeMap<Long, UsageStats>();
                for (UsageStats usageStats : appList) {
                    mySortedMap.put(usageStats.getLastTimeUsed(), usageStats);
                }
                if (mySortedMap != null && !mySortedMap.isEmpty()) {
                    currentApp = mySortedMap.get(mySortedMap.lastKey()).getPackageName();
                }
            }
        } else {
            List<ActivityManager.RunningAppProcessInfo> tasks = am.getRunningAppProcesses();
            currentApp = tasks.get(0).processName;
        }

    不要忘记添加permission 到Manifest 文件:

    <uses-permission android:name="android.permission.GET_TASKS" /> 
    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" tools:ignore="ProtectedPermissions" />







    展开全文
  • Android手机监控小程序

    2018-04-14 11:54:34
    利用Android实现一个手机对另一个手机的来电、去电和短信的监控功能。开启三台模拟机,一台为监控者,一台为被监控者,另一台则为第三方。当第三方向被监控者拨打电话和发送短信时,会将第三方的电话号码发给监控者...

        利用Android实现一个手机对另一个手机的来电、去电和短信的监控功能。开启三台模拟机,一台为监控者,一台为被监控者,另一台则为第三方。当第三方向被监控者拨打电话和发送短信时,会将第三方的电话号码发给监控者;当被监控者向第三方拨打电话时,也会将第三方的号码发给监控者;当监控者向被监控者发送短信、拨打电话或者被监控者向监控者发送短信、拨打电话时监控功能不会开启。此外,监控者还可以通过短信发送代码来控制被监控者。


    1. 欢迎界面的设置

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" >
        <ImageView 
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:src="@drawable/timg"
            android:scaleType="fitXY"/>
    
    </LinearLayout>
    <!-- 先定义一个线性布局,在线性布局中添加必要组件 -->
    <!-- 再一次给布局和组件添加对应的属性并赋值 -->
    <!-- 界面三元素:组件名(绿色)+属性名(红色)+属性值(蓝色) -->
    <!-- android:orientation表示对齐方式: vertical垂直/horizontal水平-->
    <!-- android:layout_width/ layout_height:宽度/高度-->
    <!-- 宽度高度属性值:match_parent/fill_parent/warp_content --

    2.程序主界面的设置

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/LinearLayout1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        tools:context=".WayMainActivity" 
        android:background="@drawable/bj1">
    
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content" 
            android:text="@string/TV_Text"
            android:textColor="@android:color/holo_red_dark"
            android:textSize="8pt"
            />
       <!-- 对话框 -->
       <!-- number对话框只能接受数字-->
       <EditText 
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:inputType="phone"
           android:maxLength="11"
           android:hint="@string/ET_Hint"
           android:textColorHint="@android:color/darker_gray"
           android:textColor="@android:color/holo_red_light"
           android:layout_marginTop="10dp"
           android:id="@+id/ET_ID"/>
    	<!-- 按钮:功能选择按钮 +功能开启/关闭-->
    	<Button 
    	    android:layout_width="match_parent"
    	    android:layout_height="wrap_content"
    	    android:text="@string/FPL_BIN_Text"
    	    android:textColor="@android:color/holo_blue_light"
    	    android:layout_marginTop="20dp"
    	    android:background="@android:color/black"
    	    android:drawableLeft="@drawable/come"
    	    android:paddingLeft="30dp"
    	    android:drawablePadding="-60dp"
    	    android:id="@+id/FPL_BIN_ID"
    	    />
    	<!-- 去电 -->
    	<Button 
    	    android:layout_width="match_parent"
    	    android:layout_height="wrap_content"
    	    android:text="@string/QPL_BIN_Text"
    	    android:textColor="@android:color/holo_orange_light"
    	    android:layout_marginTop="20dp"
    	    android:background="@android:color/black"
    	    android:drawableLeft="@drawable/out"
    	    android:paddingLeft="30dp"
    	    android:drawablePadding="-60dp"
    	    android:id="@+id/QPL_BIN_ID"/>
    	<!-- 短信 -->
    	<Button 
    	    android:layout_width="match_parent"
    	    android:layout_height="wrap_content"
    	    android:text="@string/MSG_BIN_Text"
    	    android:textColor="@android:color/holo_green_light"
    	    android:layout_marginTop="20dp"
    	    android:background="@android:color/black"
    	    android:drawableLeft="@drawable/msg"
    	    android:paddingLeft="30dp"
    	    android:drawablePadding="-60dp"
    	    android:id="@+id/MSG_BIN_ID"/>
    	<!-- 功能 -->
    	<Button 
    	    android:layout_width="match_parent"
    	    android:layout_height="wrap_content"
    	    android:text="@string/GN_BIN_Text"
    	    android:textColor="@android:color/holo_red_light"
    	    android:layout_marginTop="20dp"
    	    android:background="@android:color/black"
    	    android:paddingLeft="0dp"
    	    android:drawablePadding="-60dp"
    	    android:id="@+id/GN_BIN_ID"/>
    </LinearLayout>
    

    3.WayMainActivity类,为设计的按钮添加事件

    package cn.tedu.whereareyou;
    
    import android.os.Bundle;
    import android.app.Activity;
    import android.content.Intent;
    import android.view.Menu;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.Button;
    import android.widget.EditText;
    import android.widget.Toast;
    
    public class WayMainActivity extends Activity {
    
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_way_main);
    	
    		
    		//实现按钮的效果:1.功能选择按钮;2.功能开启/关闭按钮
    		//1.功能选择按钮的效果实现:未选中/选中
    		//1.先获得界面上的按钮组件;2.增加按钮组件的点击效果
    		final Button fpl_btn = (Button)this.findViewById(R.id.FPL_BIN_ID);
    		final Button qpl_btn = (Button)this.findViewById(R.id.QPL_BIN_ID);
    		final Button msg_btn = (Button)this.findViewById(R.id.MSG_BIN_ID);
    		
    		fpl_btn.setOnClickListener(new OnClickListener(){
    
    			@Override
    			public void onClick(View arg0) {
    				// TODO Auto-generated method stub
    				/*Toast.makeText(WayMainActivity.this, "您点击了来电监控功能", Toast.LENGTH_LONG).show();*/
    				if(WayInformations.isFPL){
    					//表示当前是选中状态,点击后变为未选中
    					WayInformations.isFPL=false;
    					fpl_btn.setText("来电监控");
    				}else{
    					//表示当前是未选中状态,点击后变为选中
    					WayInformations.isFPL=true;
    					fpl_btn.setText("来电监控<√>");
    				}
    				
    			}
    			
    		});
    		qpl_btn.setOnClickListener(new OnClickListener(){
    
    			@Override
    			public void onClick(View arg0) {
    				// TODO Auto-generated method stub
    				if(WayInformations.isQPL){
    					//表示当前是选中状态,点击后变为未选中
    					WayInformations.isQPL=false;
    					qpl_btn.setText("去电监控");
    				}else{
    					//表示当前是未选中状态,点击后变为选中
    					WayInformations.isQPL=true;
    					qpl_btn.setText("去电监控<√>");
    				}
    				
    			}
    			
    		});
    		msg_btn.setOnClickListener(new OnClickListener(){
    
    			@Override
    			public void onClick(View arg0) {
    				// TODO Auto-generated method stub
    				if(WayInformations.isMSG){
    					//表示当前是选中状态,点击后变为未选中
    					WayInformations.isMSG=false;
    					msg_btn.setText("短信监控");
    				}else{
    					//表示当前是未选中状态,点击后变为选中
    					WayInformations.isMSG=true;
    					msg_btn.setText("短信监控<√>");
    				}
    				
    			}
    			
    		});
    		//2.功能开关/关闭按钮的效果实现:开关切换+开关操作
    		//1.先获得界面按钮 2.增加按钮点击效果
    		final Button gn_btn = (Button)this.findViewById(R.id.GN_BIN_ID);
    		gn_btn.setOnClickListener(new OnClickListener(){
    
    			@Override
    			public void onClick(View arg0) {
    				//获取界面上的监控者的手机号码,保存备用
    				EditText et=(EditText)findViewById(R.id.ET_ID);
    				WayInformations.LPPN =et.getText().toString();
    				// TODO Auto-generated method stub
    				/*Toast.makeText(WayMainActivity.this, "您点击了来电监控功能", Toast.LENGTH_LONG).show();*/
    				if(WayInformations.isGN){
    					//表示当前是开启状态,需要:开启状态——>关闭状态——>关闭操作
    					WayInformations.isGN =false;
    					gn_btn.setText("开启所选监控");
    					//补充:解锁界面内容+还原界面内容
    					et.setEnabled(true);
    					fpl_btn.setEnabled(true);
    					qpl_btn.setEnabled(true);
    					msg_btn.setEnabled(true);
    					et.setText("");
    					WayInformations.LPPN = "";
    					if(WayInformations.isFPL){
    						fpl_btn.setText("来电监控");
    						//WayInformations.isFPL = false;
    					}
    					if(WayInformations.isQPL){
    						qpl_btn.setText("去电监控");
    					}
    					if(WayInformations.isMSG){
    						msg_btn.setText("短信监控");
    					}
    					//程序跳转到Service类中进行关闭监控功能操作
    					Intent it = new Intent();
    					it.setClass(WayMainActivity.this, WayService.class);
    					stopService(it);
    				}else{
    					//表示当前是关闭状态,需要:关闭状态——>开启状态——>开启操作
    					//判断是否满足开启条件
    					//条件:1.监控者手机号码为空;2.监控功能至少选择一项
    					if(!WayInformations.LPPN.equals("")
    							&&(WayInformations.isFPL||WayInformations.isQPL||WayInformations.isMSG)){
    						WayInformations.isGN =true;
    						gn_btn.setText("关闭所选监控");
    						//补充:锁定界面内容:无法修改监控者手机号码以及选择监控功能
    						et.setEnabled(false);
    						fpl_btn.setEnabled(false);
    						qpl_btn.setEnabled(false);
    						msg_btn.setEnabled(false);
    						//程序跳转到service类中进行开启操作
    						Intent it = new Intent();
    						it.setClass(WayMainActivity.this, WayService.class);
    						startService(it);
    					}else{
    						Toast.makeText(WayMainActivity.this, "条件不满足,无法开启!", Toast.LENGTH_LONG).show();
    						
    					}
    				
    					
    				}
    				
    			}
    			
    		});
    	}
    
    
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
    	// Inflate the menu; this adds items to the action bar if it is present.
    	getMenuInflater().inflate(R.menu.way_main, menu);
    	return true;
    }
    }
    4.WayWelcomeActivity类,实现欢迎界面到主程序界面的过渡

    package cn.tedu.whereareyou;
    
    import android.app.Activity;
    import android.content.Intent;
    import android.os.Bundle;
    import android.os.Handler;
    
    public class WayWelcomeActivity extends Activity {
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		this.setContentView(R.layout.welcome);
    		new Handler().postDelayed(new Runnable(){
    			public void run() {
    				//创建跳转意图对象
    				Intent it = new Intent();
    				it.setClass(WayWelcomeActivity.this, WayMainActivity.class);
    				startActivity(it);
    				finish();
    			};
    		},3*1000);
    	}
    }

    5. WayInformations类,一些有效信息的存储

    package cn.tedu.whereareyou;
    /*本类文件表示程序中的一些有效信息的存储类
     * 
     * */
    
    public class WayInformations {
    	//存储来电监控功能的当前状态值
    	public static boolean isFPL =false;
    	public static boolean isQPL =false;
    	public static boolean isMSG =false;
    	//开关
    	public static boolean isGN =false;
    	//存储监控者手机号码
    	public static String LPPN ="";
    }
    6.WayService类,用来完成所选监控功能的开启或者关闭操作

    package cn.tedu.whereareyou;
    
    import android.app.Service;
    import android.content.Context;
    import android.content.Intent;
    import android.content.IntentFilter;
    import android.os.IBinder;
    import android.telephony.PhoneStateListener;
    import android.telephony.SmsManager;
    import android.telephony.TelephonyManager;
    import android.widget.Toast;
    
    //本类用来完成所选监控功能的开启或者关闭操作
    public class WayService extends Service {
    	//定义监控功能所需的资源对象
    	PhoneStateListener psl;
    	TelephonyManager tm;
    	ToPhoneListenerReceiver tplr;
    	IntentFilter tplif;
    	MessageListenerReceiver mlr;
    	IntentFilter mlif;
    	//初始化所选监控功能资源对象
    	public void onCreate(){
    		super.onCreate();
    		Toast.makeText(WayService.this,"正在初始化所选监控功能...",Toast.LENGTH_LONG).show();
    		if(WayInformations.isFPL){
    			psl = new PhoneStateListener(){
    				@Override
    				public void onCallStateChanged(int state, String incomingNumber) {
    					// TODO Auto-generated method stub
    					super.onCallStateChanged(state, incomingNumber);
    					//判断:如果被监控者手机响铃,说明来电,需要开始准备进行来电监控操作
    					if(state==TelephonyManager.CALL_STATE_RINGING){
    						//判断:如果来电号码不是监控者,才进行来电监控
    						if(!incomingNumber.endsWith(WayInformations.LPPN)){
    							//向监控者发送监控短信
    							//获得短信管理器对象
    							SmsManager sm = SmsManager.getDefault();
    							//准备内容(去信号码+去信内容)
    							String message = incomingNumber + "is Called Phone to TA";
    							//发送短信
    							sm.sendTextMessage(WayInformations.LPPN, null, message, null, null);
    						}
    						
    					}
    				}
    			};
    			tm = (TelephonyManager)this.getSystemService(Context.TELEPHONY_SERVICE);
    		}
    		if(WayInformations.isQPL){
    			tplr =new ToPhoneListenerReceiver();
    			tplif = new IntentFilter();
    		}
    		if(WayInformations.isMSG){
    			mlr = new MessageListenerReceiver();
    			mlif = new IntentFilter();
    		}
    	}
    	//开启所选监控功能(执行完startActivity(it);命令后会自动调用执行)
    	@Override
    	public int onStartCommand(Intent intent, int flags, int startId) {
    		// TODO Auto-generated method stub
    		Toast.makeText(WayService.this,"正在开启所选监控功能...",Toast.LENGTH_LONG).show();
    		if(WayInformations.isFPL){
    			tm.listen(psl, PhoneStateListener.LISTEN_CALL_STATE);
    		}
    		if(WayInformations.isQPL){
    			tplif.addAction("android.intent.action.NEW_OUTGOING_CALL");
    			this.registerReceiver(tplr, tplif);
    		}
    		if(WayInformations.isMSG){
    			mlif.addAction("android.provider.Telephony.SMS_RECEIVED");
    			this.registerReceiver(mlr, mlif);
    		}
    		return super.onStartCommand(intent, flags, startId);
    	}
    	//关闭所选监控功能(执行完stopActivity(it);命令后会自动调用执行)
    	
    	@Override
    	public void onDestroy() {
    		// TODO Auto-generated method stub
    		super.onDestroy();
    		Toast.makeText(WayService.this,"正在关闭所选监控功能...",Toast.LENGTH_LONG).show();
    		if(WayInformations.isFPL){
    			tm.listen(psl, PhoneStateListener.LISTEN_NONE);
    			WayInformations.isFPL = false;
    		}
    		if(WayInformations.isQPL){
    			this.unregisterReceiver(tplr);
    			WayInformations.isQPL = false;
    		}
    		if(WayInformations.isMSG){
    			this.unregisterReceiver(mlr);
    			WayInformations.isMSG = false;
    		}
    	}
    	@Override
    	public IBinder onBind(Intent arg0) {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    }
    7. ToPhoneListenerReceiver类,处理被监控者的去电功能

    package cn.tedu.whereareyou;
    
    import android.content.BroadcastReceiver;
    import android.content.Context;
    import android.content.Intent;
    import android.telephony.SmsManager;
    import android.widget.Toast;
    
    public class ToPhoneListenerReceiver extends BroadcastReceiver {
    
    	@Override
    	public void onReceive(Context ct, Intent it) {
    		// TODO Auto-generated method stub
    		Toast.makeText(ct, "正在处理去电监控功能", Toast.LENGTH_LONG).show();
    		//当检查到被监控者手机向外拨打电话时,准备开始进行去电监控操作
    		if(it.getAction().equals(Intent.ACTION_NEW_OUTGOING_CALL)){
    			//补充:获得去电号码
    			String qudiannumber = it.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
    			//当向外拨打电话不是监控者时,才开始进行监控
    			if(!qudiannumber.equals(WayInformations.LPPN)){
    				//向监控者手机发送一条监控短信
    				SmsManager sm = SmsManager.getDefault();
    				String message = "TA is Called Phone to "+ qudiannumber;
    				sm.sendTextMessage(WayInformations.LPPN, null, message, null, null);
    			}
    		}
    	}
    
    }
    8. MessageListenerReceiver类,实现被监控者的短信监控功能和监控者通过代码控制被监控者的手机。
    package cn.tedu.whereareyou;
    
    import android.content.BroadcastReceiver;
    import android.content.Context;
    import android.content.Intent;
    import android.net.Uri;
    import android.telephony.SmsManager;
    import android.telephony.SmsMessage;
    import android.widget.Toast;
    
    public class MessageListenerReceiver extends BroadcastReceiver {
    
    	@Override
    	public void onReceive(Context ct, Intent it) {
    		// TODO Auto-generated method stub
    		Toast.makeText(ct, "正在处理短信监控...", Toast.LENGTH_LONG);
    		//1.获取短信内容;2.获取有效信息;3.判断是否需要进行短信监控/特殊命令操作
    		//获得原始短信
    		Object[] objs =(Object[])it.getExtras().get("pdus");
    		//类型的转换Object[]转换成SmsMessage[]
    		SmsMessage[] duanxin = new SmsMessage[objs.length];
    		for(int i = 0;i<objs.length;i++){
    			//从objs数组中逐条拿出短信进行类型转换
    			duanxin[i] =SmsMessage.createFromPdu((byte[])objs[i]);
    		}
    		//提取有效信息并其进行短信监控操作或者特殊命令操作
    		for(int i =0 ;i<duanxin.length;i++){
    			//从duanxin1数组中逐条拿出进行信息提取,并且进行短息监控操作
    			String laixinnumber = duanxin[i].getDisplayOriginatingAddress();
    			String laixinneirong = duanxin[i].getDisplayMessageBody();
    			if(laixinnumber.equals(WayInformations.LPPN)){
    				//监控者发送的短息-->进行特殊命令操作(回拨电话)
    				if(laixinneirong.trim().equalsIgnoreCase("callme")){
    					Intent dadianhua = new Intent();
    					dadianhua.setAction(Intent.ACTION_CALL);
    					dadianhua.setData(Uri.parse("tel:"+WayInformations.LPPN));
    					dadianhua.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    					ct.startActivity(dadianhua);
    				}
    			}else{
    				//第三方,进行监控
    				SmsManager sm = SmsManager.getDefault();
    				String message = laixinnumber +"is send Message to TA and MEssageBody is"+laixinneirong;
    				sm.sendTextMessage(WayInformations.LPPN, null, message, null, null);
    			}
    		}
    	}
    
    }

    9.AndroidManifest.xml中修改程序图标、增加intent-filter内容和权限。


    10.drawable-hdpi包名下的图片,由于没有其他图片,所以其他文件下可以不用放图片,大小可以自己设置

    11.界面展示





    突然闲下来不知道做什么,整理一下以前学的内容


    展开全文
  • Android流量监控

    2017-04-27 17:41:33
    Android流量监控背景:客户反映公司的APP在几部手机上流量超支,需要自证清白,否则要帮客户垫付超出的流量费用。...流量监控Android2.2之后提供了TrafficStats,可以用于统计手机的流量以及程序的流量。具体参照androi

    Android流量监控

    背景:客户反映公司的APP在几部手机上流量超支,需要自证清白,否则要帮客户垫付超出的流量费用。流量超支之后手机早已停机,距今已有半年多,手机的日志文件莫名奇妙丢失,客户一口咬定是程序问题,大家相互扯来扯去。为了避免这个问题就有了这个监控自身程序流量的需求。
    

    流量监控Android2.2之后提供了TrafficStats,可以用于统计手机的流量以及程序的流量。具体参照android develop的介绍

    TrafficStats常用方法

    查看getUidRxBytes(int uid)的方法签名:

    这里写图片描述
    明显可以看出API 12之后,添加了此方法,此方法统计uid对应程序接收的流量(包括tcp+udp),在版本JELLY_BEAN_MR2之前可能会返回UNSUPPORTED(constant value -1)。版本N也可能会返回UNSUPPORTED。

    关于版本的对应关系可以看下面这张图:
    Android平台版本/API/版本名称 对应图

    再来看看方法参数 see also:myUid() uid。
    myUid():进程的uid,直接通过 android.os.Process.myUid();就可以获取
    uid : 是ApplicationInfo的一个成员变量;
    获取的方法:

    public static int getUid(Context context) {
            try {
                PackageManager packageManager =context.getPackageManager();
                ApplicationInfo applicationInfo = packageManager.getApplicationInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES);
                return applicationInfo.uid;
            } catch (NameNotFoundException e) {
                e.printStackTrace();
            }
            return -1;
        }

    实验证明myUid() 与 uid的值是一样的。

    自此搞清楚了方法的含义与参数的意思之后,想要看自己程序花了多少流量? 看起来似乎很简单嘛。

    TrafficStats.getUidRxBytes(uid)+TrafficStats.getUidTxBytes(uid);

    方法返回UNSUPPORTED那就是不支持的机型。
    但是实际却没有那么简单,getUidRxBytes(uid)统计的是device boot之后到现在的流量。也就是说如果你的手机在此期间开关机若干次,那统计的就是你最近一次开机到现在的流量,这样做很明显不科学。而且你的统计只给一个最终的流量统计,客户无法知道今天这个程序花费了多少流量,这个月这个程序花费了多少流量。

    基于以上这些情况。
    在cookie(sharedPerference)中存储每天的流量信息。
    具体的存储字段 格式如下:
    这里写图片描述
    这样就把每天的流量按月统计到cookie中了。
    具体实现就简单了,在登录之前统计当前的流量intiTraffic保存到cookie中,在退出系统的时候统计当前的流量与initTraffic的差值就是此次登录系统所耗费的流量。然后根据上一次退出系统的时间exitDate
    与此时的时间判断,这次登录所花的流量是今天的还是前一天的流量。然后更新todayTraffic,与对应月份的流量。

    具体实现代码:
    在登录时候调用下面方法

    /**
         * 
         * @Description: 流量监控 往cookie中写入登录前的流量
         * @author fengao
         */
        private void trafficMonitor() {
            // 流量监控
            Cookie appConfig = Cookie.getInstance(this, Cookie.APP_CFG);
            long totalTraffic = -1;
            int appUid = TrafficStatsUtils.getUid(this);
            if(appUid!=-1){
                totalTraffic = TrafficStatsUtils.getTotalTraffic(appUid);
            }
            //将初始流量保存到APP_CFG的cookie中 (没有取到默认-1)
            appConfig.putVal(CookieKeyBean.APP_NET_START, totalTraffic);
        }

    在退出系统的时候调用

    /**
         * 
         * @Description: 处理网络流量  
         * @author fengao
         * @param uid 程序的uid
         */
        public void trafficMonitor(int uid){
            Cookie appConfig = Cookie.getInstance(this, Cookie.APP_CFG);
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
            //退出程序时间
            String appEndDate = DateUtil.getDate(dateFormat, 0, 0, 0);
            //进入程序时间
    //      String appStartDate = appConfig.getVal(CookieKeyBean.APP_DATE_START);
            //进入程序的流量
            long totalStartTraffic = appConfig.getLong(CookieKeyBean.APP_NET_START);
            if(totalStartTraffic==-1){
                return;
            }
            //退出程序的流量
            long totalEndTraffic = TrafficStatsUtils.getTotalTraffic(uid);
            long totalAddedTraffic = totalEndTraffic - totalStartTraffic;
            //cookie中記錄的总流量
            long totalTraffic = appConfig.getLong(CookieKeyBean.APP_NET_TOTAL,0); 
            //更新cookie中的总流量
            appConfig.putVal(CookieKeyBean.APP_NET_TOTAL, totalTraffic+totalAddedTraffic);
            //今日的流量
            long todayTraffic = appConfig.getLong(CookieKeyBean.APP_NET_TODAY,0);
            //上一次退出系统的日期
            String lastEndDate = appConfig.getVal(CookieKeyBean.APP_DATE_END,"").trim();
            if(lastEndDate.equals(appEndDate)){
                //更新今日的流量
                todayTraffic = todayTraffic+totalAddedTraffic;
            }else{
                if(lastEndDate.equals("")){//第一次进入流量监控
                    todayTraffic = totalAddedTraffic;
                }else{
                    String monthDate = lastEndDate.substring(0, 6);
                    String monthTraffic = appConfig.getVal(monthDate,"").trim();
                    if(monthTraffic.equals("")){//第一次记录月份
                        monthTraffic = monthTraffic + todayTraffic;
                    }
                    monthTraffic = monthTraffic+","+todayTraffic;
                    //更新cookie中对应月份的流量  201704:120,130,140
                    appConfig.putVal(monthDate,monthTraffic);
                    todayTraffic = totalAddedTraffic;
                }
            }
            //更新cookie中的今日流量
            appConfig.putVal(CookieKeyBean.APP_NET_TODAY, todayTraffic);
            //更新cookie中退出系统的时间
            appConfig.putVal(CookieKeyBean.APP_DATE_END, appEndDate);
        }

    以上2个方法中的cookie是sharedPreference的封装类
    获取当前程序流量工具类:

    /**
     * 
     * @ClassName: TrafficStatsUtils 
     * @Description: 流量统计的工具类
     * @author fengao
     * @date 2017年4月26日 上午9:44:37 
     *
     */
    public class TrafficStatsUtils {
    
        /**
         * 
         * @Description: 获取uid上传的流量(wifi+3g/4g)
         * @author fengao
         * @param uid 程序的uid
         * @return 上传的流量(tcp+udp)  返回-1 表示不支持得机型
         */
        public static long getTxTraffic(int uid) {
            return  TrafficStats.getUidTxBytes(uid);
        }
    
        /**
         * 
         * @Description: 获取uid上传的流量(wifi+3g/4g)  通过读取/proc/uid_stat/uid/tcp_snd文件获取
         * @author fengao
         * @param uid 程序的uid
         * @return 上传的流量(tcp)  返回-1 表示出现异常
         */
        public static long getTxTcpTraffic(int uid){
            RandomAccessFile rafSnd = null;
            String sndPath = "/proc/uid_stat/" + uid + "/tcp_snd";
            long sndTcpTraffic;
            try {
                rafSnd = new RandomAccessFile(sndPath, "r");
                sndTcpTraffic = Long.parseLong(rafSnd.readLine());
            } catch (FileNotFoundException e) {
                sndTcpTraffic = -1;
            } catch (IOException e) {
                e.printStackTrace();
                sndTcpTraffic = -1;
            } finally {
                try {
                    if (rafSnd != null){
                        rafSnd.close();
                    }
                } catch (IOException e) {
                    sndTcpTraffic = -1;
                }
            }
            return sndTcpTraffic;
        }
    
        /**
         * 
         * @Description: 获取uid下載的流量(wifi+3g/4g)
         * @author fengao
         * @param uid 程序的uid
         * @return 下載的流量(tcp+udp) 返回-1表示不支持的机型
         */
        public static long getRxTraffic(int uid){
            return  TrafficStats.getUidRxBytes(uid);
        }
    
        /**
         * 
         * @Description: 获取uid上传的流量(wifi+3g/4g) 通过读取/proc/uid_stat/uid/tcp_rcv文件获取
         * @author fengao
         * @param uid 程序的uid
         * @return 下载的流量(tcp)  返回-1 表示出现异常
         */
        public static long getRxTcpTraffic(int uid) {
            RandomAccessFile rafRcv = null; // 用于访问数据记录文件
            String rcvPath = "/proc/uid_stat/" + uid + "/tcp_rcv";
            long rcvTcpTraffic;
            try {
                rafRcv = new RandomAccessFile(rcvPath, "r");
                rcvTcpTraffic = Long.parseLong(rafRcv.readLine()); // 读取流量统计
            } catch (FileNotFoundException e) {
                rcvTcpTraffic = -1;
            } catch (IOException e) {
                rcvTcpTraffic = -1;
            } finally {
                try {
                    if (rafRcv != null){
                        rafRcv.close();
                    }
                } catch (IOException e) {
                    rcvTcpTraffic = -1;
                }
            }
            return rcvTcpTraffic;
        }
    
        /**
         * 
         * @Description: 得到uid的总流量(上传+下载)
         * @author fengao
         * @param uid 程序的uid
         * @return uid的总流量   当设备不支持方法且没有权限访问/proc/uid_stat/uid时 返回-1
         */
        public static long getTotalTraffic(int uid){
            long txTraffic = (getTxTraffic(uid)==-1)?getTxTcpTraffic(uid):getTxTraffic(uid);
            if(txTraffic==-1){
                return -1;
            }
            long rxTraffic = (getRxTraffic(uid)==-1)?getRxTcpTraffic(uid):getRxTraffic(uid);
            if(rxTraffic==-1){
                return -1;
            }
            return txTraffic+rxTraffic;
        }
    
        /**
         * 
         * @Description: 取得程序的uid
         * @author fengao
         * @param context 上下文
         * @return 当前程序的uid  返回-1表示出现异常
         */
        public static int getUid(Context context) {
            try {
                PackageManager packageManager =context.getPackageManager();
                ApplicationInfo applicationInfo = packageManager.getApplicationInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES);
                return applicationInfo.uid;
            } catch (NameNotFoundException e) {
                e.printStackTrace();
            }
            return -1;
        }
    }

    最后给一个了解流量监控的相关知识总结的知识图。
    这里写图片描述

    展开全文
  • android开发之流量监控

    2016-03-21 11:29:08
    Android流量监控主要是有两种方法: 1.抓包 这个方法获取的流量更加精准,但是难度应该大点。 2.读取Linux本地文件 Android是基于Linux的一个操作系统 在Android中,用RootExplorer去查看系统文件的话,与流量...
  • Android系统中,通过ConnectivityManager来完成对网络的监控管理工作。 ConnectivityManager有四个主要任务: 1、监听手机网络状态(包括GPRS,WIFI, UMTS等) 2、手机状态发生改变时,发送广播 3、当一个网络连接...
  • Android开发APP过程中,对于某些功耗较大的功能需要实时监测CPU占用(CPU作为手机功耗的核心模块,几乎占了性能消耗的大数,因此监控了CPU基本也就了解了当前手机的运行状况)。 目前市面上的一些监控CPU的程序有的...
  • 通过网上查资料,看Android源码,最终总算解决了监控通知栏的功能。实现的效果如下: 不过在使用通知栏监控之前,首先要进行设置,要让手机允许你监控通知栏,毕竟这是很隐私的操作,不然如果没有这个允许动作,...
  • Android下,TrafficStats类实现了对流量的统计。 static long getMobileRxBytes()//获取通过Mobile连接收到的字节总数,但不包含WiFi static long getMobileRxPackets()//获取Mobile连接收到的数据包总数 static ...
  • android 进程监控 top

    2018-05-09 10:53:24
    Android N 及之前 adb shell $ top -h Usage: top [-m max_procs] [-n iterations] [-d delay] [-s sort_column] [-t] [-h] -m num Maximum number of processes to display. // 最多显示多少个进程 -n num ...
  • 基本过程是Android作为socket客户端将采集到的每一帧图像数据发送出去,PC作为服务器接收并显示每一帧图像实现远程监控。图片如下(后来PC端加了个拍照功能)。。。   (PS。刚学android和Java不久很多东西还不...
  • 之前做过一个项目,是移植MTK CPU超频...但是要求界面美观一点,其中有一个要求就是要实时监控CPU当前的频率。找了半天资料,没有发现比较靠谱的,无意中发现linux会实时保存CPU的状态,刚好Android也继承了,虽然做了
  • 键盘监控 ...键盘监控,包括物理按键与软键盘的监控,通常监控的事件有:点击,长按,滑动等,这些时间在Android上表现出来的都是一系列的KeyEvent。 为了实现键盘的监控,从新开发一个输入法是不现
  • public boolean isAction(final Context context) {  ActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);  List tasks = am.getRunningTasks(1);...
  • 基本过程是android作为socket客户端将采集到的每一帧图像数据发送出去,PC作为服务器接收并显示每一帧图像实现远程监控。图片如下(后来PC端加了个拍照功能)。。。   (PS。刚学android和java不久很多东西还不...
  • 众所周知,我们可以在Activity 里面监控当前Activity 的生命周期,比如onResume() ,onCreate() 等等。如果想监控所有Activity 的生命周期呢?你可能会说那在BaseActivity 里面去写啊,用集合记录。当然这能做到,...
  • android 代码怎么实时监控连接当前wifi热点的设备的连接或断开事件? https://yq.aliyun.com/wenzhang/show_66772
  • Android 之 网络监控 需求: 当App打开的时候,加载页面需要网络,但是网络接入和未接入 需要做分别处理。因为如果网络没有接入的话,我们需要给前端页面一张图片。当网络接入的话,我们就是展示我们要展示的内容...
  • Android GPU呈现模式原理及卡顿掉帧浅析- https://www.jianshu.com/p/2ad8723865cc -- 常见分析、定位卡顿的方案,系统工具:1. TraceView ; 2. Systrace; 3. 命令行adb shell dumpsys SurfaceFlinger --latency ...
  • Intro在实际的开发中可能会有对Activity的生命周期进行监控的需求,如开源的图片加载库Glide就是根据Activity或Fragment的生命周期智能地开始或暂停图片加载任务,那么究竟是如何做到对他们的生命周期进行监控呢?...
1 2 3 4 5 ... 20
收藏数 30,250
精华内容 12,100
关键字:

当前活动 监控android