2017-04-21 17:44:29 oqqMiHu123 阅读数 351
  • Android峰会】如何高效高质进行Android技术开发

    如何高效高质进行Android技术开发视频培训教程,该课程内容涉及Android视频开发技术,Android代码风格、移动端APM性能监控、应用质量保障之道、开发应用程序的安全技术,移动安全,使用Kotlin开发Android应用教程。该教程集结了Android开发界中年轻有活力,热爱分享的一线讲师,专家,针对Android开发从多个角度各抒己见,既有深入细节的视频硬解稳定性和性能监控的主题,还有关注软件质量,利用新语言打造美丽代码,以及构建更安全应用的心声呼唤。

    3541 人正在学习 去看看 CSDN讲师

需求

HTC 510 Android手机上监控微信的PC端登陆请求界面(好绕口,其实就是pc上要登陆,需要手机端点验证界面)如下:
这里写图片描述
当出现这个界面时,点击登陆按钮。

分析

功能不复杂,分两步:

  1. 监控出现登陆界面
  2. 发出点击命令

监控界面使用service后台运行,定时查看当前界面是否是登陆界面。查看当前界面代码如下:

ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
                    ActivityManager.RunningTaskInfo info = manager.getRunningTasks(1).get(0);
                    String className = info.topActivity.getClassName();              //完整类名
                    Log.d(TAG, "className="+className);
                    if(className.equalsIgnoreCase("com.tencent.mm.plugin.webwx.ui.ExtDeviceWXLoginUI"))
                    {
                        Log.d(TAG, "action");
                        action.sendTap(235, 609);
                    }

自动点击登陆按钮代码如下:

    public void init() {
        try {
            process = Runtime.getRuntime().exec("su");
            outputStream = new PrintStream(new BufferedOutputStream(process.getOutputStream(), 8192));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
        public void sendTap(int x, int y) {
        // TODO Auto-generated method stub
        if (!isValid()) {
            return;
        }
        AsyncExec("input tap " + x + " " + y);
    }

点击命令需要root权限。

这样基本代码已完成。it’s over?答案是肯定的。实际运行中,会发现service经常被干掉了。

守护雅典娜

如何守护service?
网上教程很多,大多数招都用了。并不能彻底解决,一旦内存吃紧,service就被停掉。
比如这些招:

1. service中重写onStartCommand方法,这个方法有三个返回值, START_STICKY是service被kill掉后自动
public int onStartCommand(Intent intent, int flags, int startId) {       
   return START_STICKY;   
}
2. 配置android:persistent="true" 
3. setForeground(true); 
4. android:process=”com.xxx.xxxservice”配置到单独的进程中

以上的方法要么只是提升service优先级或者存活率。

最后还有一招也是最开始想到的一招,把应用作为系统应用。

应用系统化

系统应用都在system/app下,只要将应用放入此目录下就可以了。于是打个命令

adb push path/xx.apk system/app

然后呢,提示没有权限。是不是忘记打adb remount了呢。然too。
看来系统跟之前的不一样。 突然看到手机里有个Root Explorer应用,申请系统权限后可以操作system/app。so,曲线救国,

  1. 将xx.apk放入到sdcard中
  2. 用Root Explorer将xx.apk搬运到system/app中

然后adb reboot。
效果:后台程序稳定跑起来。

跑一会又发现问题,屏幕会灭掉,灭屏后微信不接收消息。同样包括登陆界面。

持久亮屏

如果是Activity,可以使用最新的两种亮屏手段:
一:

getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,    WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

二:

PowerManager powerManager = null; WakeLock wakeLock = null;

 @Override protected void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  setContentView(R.layout.activity_main_1);

  this.powerManager = (PowerManager) this    .getSystemService(Context.POWER_SERVICE);  this.wakeLock = this.powerManager.newWakeLock(    PowerManager.FULL_WAKE_LOCK, "My Lock");

而后台只能用第二种。
ok,终于正常跑了。

开机自启动

使用广播监听,因为是系统应用

    public void onReceive(final Context context, Intent intent) {
        Log.d("BootReceiver action:",intent.getAction());
//        if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
            Intent rootIntent = new Intent(context, RootService.class);
            rootIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startService(rootIntent);
//            context.startActivity(new Intent(context, MainActivity.class));
//        }
    }
   <receiver android:name=".BootReceiver" >
            <intent-filter android:priority="2147483647" >
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <action android:name="android.intent.action.ALARM_ACTION" />
            </intent-filter>
            <intent-filter android:priority="2147483647" >
                <action android:name="android.intent.action.MEDIA_MOUNTED" />
                <action android:name="android.intent.action.MEDIA_UNMOUNTED" />
                <data android:scheme="file" />
            </intent-filter>

            <intent-filter >
                <action android:name="android.intent.action.PACKAGE_RESTARTED" />
                <action android:name="android.intent.action.USER_PRESENT" />
            </intent-filter>
        </receiver>

总结

开始本来想使用xposed做,但是刚xposed刚接触,短期内也没有解决思路。so最后使用常规应用解决。

非常规应用,耗电杠杠的。

守护雅典娜据说有大招叫双守护进程,微信上用的,有机会试试。

2018-08-04 12:30:57 tiandiyinghun 阅读数 87272
  • Android峰会】如何高效高质进行Android技术开发

    如何高效高质进行Android技术开发视频培训教程,该课程内容涉及Android视频开发技术,Android代码风格、移动端APM性能监控、应用质量保障之道、开发应用程序的安全技术,移动安全,使用Kotlin开发Android应用教程。该教程集结了Android开发界中年轻有活力,热爱分享的一线讲师,专家,针对Android开发从多个角度各抒己见,既有深入细节的视频硬解稳定性和性能监控的主题,还有关注软件质量,利用新语言打造美丽代码,以及构建更安全应用的心声呼唤。

    3541 人正在学习 去看看 CSDN讲师

说下场景:

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

 

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

2015-12-23 22:14:37 IO_Field 阅读数 1604
  • Android峰会】如何高效高质进行Android技术开发

    如何高效高质进行Android技术开发视频培训教程,该课程内容涉及Android视频开发技术,Android代码风格、移动端APM性能监控、应用质量保障之道、开发应用程序的安全技术,移动安全,使用Kotlin开发Android应用教程。该教程集结了Android开发界中年轻有活力,热爱分享的一线讲师,专家,针对Android开发从多个角度各抒己见,既有深入细节的视频硬解稳定性和性能监控的主题,还有关注软件质量,利用新语言打造美丽代码,以及构建更安全应用的心声呼唤。

    3541 人正在学习 去看看 CSDN讲师

Android项目中,客户端与服务器不时的通过网络对数据交互。访问服务器前,判断当前网络是否可用,以便给予用户提醒。

Android系统中,通过ConnectivityManager来完成对网络的监控管理工作。

ConnectivityManager有四个主要任务:
1、监听手机网络状态(包括GPRS,WIFI, UMTS等)
2、手机状态发生改变时,发送广播
3、当一个网络连接失败时进行故障切换
4、为应用程序提供可以获取可用网络的高精度和粗糙的状态

ConnectivityManager常用的方法:

1.NetworkInfo getActiveNetworkInfo()  // 返回NetworkInfo,当前网络连接的信息
2.NetworkInfo[] getAllNetworkInfo()  //  返回NetworkInfo[],当前全部网络连接的信息
3.NetworkInfo getNetworkInfo(int networkType)   // 返回一个指定网络的连接信息
4.int getNetworkPreference()  // 返回首选网络连接的网络类型
5.void setNetworkPreference(int preference)  // 设置首选网络连接的网络类型
6.boolean isActiveNetworkMetered() //  判断当前网络是否收费
7.boolean isNetworkTypeValid(int networkType)   // 当前网络是否可用

NetworkInfo类的字段,凭个人感觉判定的,也不知道是否准确。
type: 网络类型    
state: 连接状态
reason: (unspecified), 网络异常的原因
extra: 网络信息(网络的名称)
roaming: 移动连接,是否漫游
failover: 故障转移策略
isAvailable: 网络是否可用
isConnectedToProvisioningNetwork: 网络异常是否启用可用络

实时监听网络的步骤

1.定义一个Receiver重载其中的onReceive函数,在其中完成所需要的功能,如根据WIFI和GPRS是否断开

    private class ConnectivityManagerReceiver extends BroadcastReceiver {

        public void onReceive(Context context, Intent intent) {
            ConnectivityManager mManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo mobNetInfo = mManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
            NetworkInfo wifiNetInfo = mManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);

            Log.i("Teaphy", "mobNetInfo: " + mobNetInfo.toString());
            Log.i("Teaphy", "wifiNetInfo: " + wifiNetInfo.toString());

            if (!mobNetInfo.isConnected() && !wifiNetInfo.isConnected()) {
                tv_netStat.setText("当前网络状态:不可用");
            } else {
                if (mobNetInfo.isConnected()) {
                    tv_netStat.setText("当前网络状态:数据连接可用");
                } else {
                    tv_netStat.setText("当前网络状态:数据连接不可用");
                }

                if (wifiNetInfo.isConnected()) {
                    tv_netStat.setText(tv_netStat.getText().toString() + ",wifi可用");
                } else {
                    tv_netStat.setText(tv_netStat.getText().toString() + ",wifi不可用");
                }
            }

        }
    }

2.调用系统服务ConnectivityManager.CONNECTIVITY_ACTION,监控网络状态的改变并发送广播。在适当的地方注册Receiver,可以在程序中注册,在onCreate中调用如下函数即可:

 IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
 mReceiver = new ConnectivityManagerReceiver();
 registerReceiver(mReceiver, intentFilter);
4.在Activity关闭时,注销广播

private  void onDestroy(){
	this.unregisterReceiver(myReceiver);
}
5.监控网络时,需要在AndroidManifest.xml 添加相应的权限,否则会报错

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET" />








2017-02-22 23:26:49 u010784887 阅读数 503
  • Android峰会】如何高效高质进行Android技术开发

    如何高效高质进行Android技术开发视频培训教程,该课程内容涉及Android视频开发技术,Android代码风格、移动端APM性能监控、应用质量保障之道、开发应用程序的安全技术,移动安全,使用Kotlin开发Android应用教程。该教程集结了Android开发界中年轻有活力,热爱分享的一线讲师,专家,针对Android开发从多个角度各抒己见,既有深入细节的视频硬解稳定性和性能监控的主题,还有关注软件质量,利用新语言打造美丽代码,以及构建更安全应用的心声呼唤。

    3541 人正在学习 去看看 CSDN讲师

最近在做的项目有个流量监控的功能在7.0上不能正常运行,具体的来说就是:悬浮窗显示实时网速。
这个功能7.0之前一直都是ok的,但在7.0上显示网速一直为0 kb/s。要知道为什么就得知道该功能实现的原理,其实很简单,就是获取linux内核下的两个文件,这两个文件里的内容是当前网络的上行网速和下行网速, 我们只要将它读出来即可。

先明确几个linux内核的重要路径:

/proc/net/route

这个路径下表明正在链接的网路, 比如你的手机正在连接WIFI,则route文件的首字段Iface有wlan0字段; 如果是数据流量, Iface则是rmnet_data0 (具体显示标示名根据手机内核可能会有不同)

/proc/net/dev

/sys/class/net/eth1/statistics/...

在7.0之前是可以通以下两个文件读取流量:

/sys/class/net/eth1/statistics/rx_bytes

/sys/class/net/eth1/statistics/tx_bytes

2019-05-13 12:18:31 duguju 阅读数 1098
  • Android峰会】如何高效高质进行Android技术开发

    如何高效高质进行Android技术开发视频培训教程,该课程内容涉及Android视频开发技术,Android代码风格、移动端APM性能监控、应用质量保障之道、开发应用程序的安全技术,移动安全,使用Kotlin开发Android应用教程。该教程集结了Android开发界中年轻有活力,热爱分享的一线讲师,专家,针对Android开发从多个角度各抒己见,既有深入细节的视频硬解稳定性和性能监控的主题,还有关注软件质量,利用新语言打造美丽代码,以及构建更安全应用的心声呼唤。

    3541 人正在学习 去看看 CSDN讲师

Android开发APP过程中,对于某些功耗较大的功能需要实时监测CPU占用(CPU作为手机功耗的核心模块,几乎占了性能消耗的大数,因此监控了CPU基本也就了解了当前手机的运行状况)。

目前市面上的一些监控CPU的程序有的是针对某些机型的CPU(比如高通针对骁龙芯片的Trepen,MTK针对联发科芯片的Mali),有的只能监控整体CPU,而无法针对某个应用的占用进行监控(比如PerfMon);

而Android studio自带的Android Profiler工具在更新到某个版本之后就只支持API 21之后的机型了,比较旧的机型无法使用;更为严重的是,在本人实测多款机型中发现,开启Android Profiler会开启一个debug监控进程,这个监控进程对于某些机型(小米居多)的真实CPU占用是有影响的,一开启的CPU占用会迅速升高,甚至会出现可见性的卡顿情况;

 

针对这种难题,本人在查询多方资料,选择不影响实际性能的方式,实现了一个针对全机型适用的CPU监测工具,github地址:https://github.com/duguju/AndroidCPUCollector

 

核心类是CPUCollector:

/*
 * Copyright (C) 2019 jjoeyang. All Rights Reserved.
 */
package com.yzz.cpucollector;

import android.util.Log;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

/**
 * CPU占用统计类
 * 单例,进入APP时通过setPkgName()设置应用包名,调用getCPURate()即可输出CPU统计信息,退出时调用release()
 *
 * Created by jjoeyang on 19/5/6
 */
public class CPUCollector {
    public static final String TAG = CPUCollector.class.getSimpleName();
    private String mPkgName = null;
    private String mLastCPU = "";
    private Thread mCpuThread;
    private boolean enableCPU = true;
    private boolean isRunning = false;
    private boolean doCollect = false;
    private static final String COMMAND_SH = "sh";
    private static final String COMMAND_LINE_END = "\n";
    private static final String COMMAND_EXIT = "exit\n";

    private int maxFrameCount = 5; // 统计的总次数,可修改
    private int resultFrameTimes = 0; // 统计次数
    private double resultAVGValue;
    private String mAvgCPUValue;

    private static CPUCollector mInstance = null;

    private CPUCollector() {
        if (mCpuThread == null) {
            mCpuThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    while (enableCPU) {
                        if (doCollect && !isRunning) {
                            doCollect = false;
                            isRunning = true;
                            mLastCPU = getCPUFromTopCMD();
                            isRunning = false;
                        } else {
                            try {
                                Thread.sleep(10);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            });
            mCpuThread.setName("CpuCollectorThread");
            mCpuThread.start();
        }
    }

    public static synchronized CPUCollector getInstance() {
        if (mInstance == null) {
            mInstance = new CPUCollector();
        }
        return mInstance;
    }

    public void setPkgName(String pkgName) {
        mPkgName = pkgName;
    }

    public String getCPURate() {
        if (!isRunning && mPkgName != null) {
            doCollect = true;
        }
        return mLastCPU;
    }

    public String getAvgCPU() {
        return mAvgCPUValue;
    }

    public void release() {
        mPkgName = null;
        mAvgCPUValue = null;
        enableCPU = false;
        mCpuThread = null;
    }

    private String getCPUFromTopCMD() {
        String cpu = "";
        List<String> result = execute("top -n 1 -s cpu | grep " + mPkgName);
        if (result != null && result.size() > 0) {
            String r = result.get(0);
            if (r != null && r.contains("%")) {
                int end = r.indexOf("%");
                int start = -1;
                for (int i = end; i >= 0; i--) {
                    if (Character.isWhitespace(r.charAt(i))) {
                        start = i;
                        break;
                    }
                }
                if (start >= 0) {
                    cpu = r.substring(start, end);
                    calculateAVGValue(Double.parseDouble(cpu));
                }
            }
        }
        return cpu;
    }

    /**
     * 执行单条命令
     *
     * @param command
     * @return
     */
    private List<String> execute(String command) {
        return execute(new String[]{command});
    }

    /**
     * 可执行多行命令(bat)
     *
     * @param commands
     * @return
     */
    private List<String> execute(String[] commands) {
        List<String> results = new ArrayList<String>();
        int status = -1;
        if (commands == null || commands.length == 0) {
            return null;
        }
        Process process = null;
        BufferedReader successReader = null;
        BufferedReader errorReader = null;
        StringBuilder errorMsg = null;
        DataOutputStream dos = null;
        try {
            process = Runtime.getRuntime().exec(COMMAND_SH);
            dos = new DataOutputStream(process.getOutputStream());
            for (String command : commands) {
                if (command == null) {
                    continue;
                }
                dos.write(command.getBytes());
                dos.writeBytes(COMMAND_LINE_END);
                dos.flush();
            }
            dos.writeBytes(COMMAND_EXIT);
            dos.flush();

            status = process.waitFor();

            errorMsg = new StringBuilder();
            successReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
            String lineStr;
            while ((lineStr = successReader.readLine()) != null) {
                results.add(lineStr);
            }
            while ((lineStr = errorReader.readLine()) != null) {
                errorMsg.append(lineStr);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (dos != null) {
                    dos.close();
                }
                if (successReader != null) {
                    successReader.close();
                }
                if (errorReader != null) {
                    errorReader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

            if (process != null) {
                process.destroy();
            }
        }
        Log.d(TAG, (String.format(Locale.CHINA, "execute command end, errorMsg:%s, and status %d: ",
                errorMsg, status)));
        return results;
    }

    private void calculateAVGValue(double resultTime) {
        if (resultFrameTimes >= maxFrameCount) {
            if (resultFrameTimes == maxFrameCount) {
                resultFrameTimes++;
            }
            mAvgCPUValue = String.format("%.2f", resultAVGValue);
            resultFrameTimes = 0;
            resultAVGValue = 0;
            return;
        }
        resultFrameTimes++;
        double allResultTime = (resultFrameTimes - 1) * resultAVGValue;
        resultAVGValue = (allResultTime + resultTime) / resultFrameTimes;
    }
}

核心原理就是通过代码方式,固定间隔调用adb监控CPU的命令(adb shell top -m 100 -n 1 -s cpu),从而输出当前应用的CPU占用情况;支持获取当前占用并进行N帧的平均统计。具体API调用及使用方式可参考github中的Demo

第一版是log输出的CPU信息,Demo中过滤以下log:

05-13 12:03:48.914 12240-12240/com.yzz.cpucollector E/duguju-cpu: 当前CPU占用: 4%  平均:5.2%

今后会做一些界面、操作优化等更新工作,敬请期待~

没有更多推荐了,返回首页