android使用技巧及踩过的坑-采坑记续集

这篇博客记录了在Android开发中遇到的各种问题及其解决方法,包括adb连接问题、Activity生命周期、内存占用计算、源码分析、命令行操作、权限判断、悬浮窗管理、接口调试、系统升级适配等,同时也涵盖了服务端接口、文件上传、日志分析等多个方面。
摘要由CSDN通过智能技术生成

118、adb 连接设备出现unable to connect devices 问题

通过网络adb连接设备:
adb connect [设备的ip地址]
adb disconnect 或者adb disconnect [设备的ip地址]

未root的手机,不能连接,提示 unable to connect devices
解决方法:
    先usb连接上,输入adb tcpip 5555
    再输入 adb connect 192.168.1.7:5555就可以连接上

119、activity的生命周期回调函数:

onCreate()
onStart():用户可见
onResume():在前台,用户可见
onPause():用户可见
onStop()
            -----onRestart()
onDestory()

打开应用时先后执行了onCreate()->onStart()->onResume三个方法.
按BACK键时,我们这个应用程序将结束,这时候我们将先后调用onPause()->onStop()->onDestory()三个方法.
按HOME键,onPause()->onStop()
再次启动ActivityDemo应用程序时,则先后分别执行了onRestart()->onStart()->onResume()三个方法

当用户点击A中按钮来到B时,假设B全部遮挡住了A,将依次执行A:onPause -> B:onCreate -> B:onStart -> B:onResume -> A:onStop。
此时如果点击Back键,将依次执行B:onPause -> A:onRestart -> A:onStart -> A:onResume -> B:onStop -> B:onDestroy。

120、一张图片在android中如果不压缩,实际占用内存计算

例如一张1440*2448(351K)大小的图片,加载到内存中,不压缩
1440*2448(个像素)*4(RGBA,每个像素有4个颜色值)*8(每个颜色值8bit) = 112803840 bit = 13.45MB(放大40倍左右)

121、今天分析了下activity的启动流程源码分析,大概总结下:

在我们学习AIDL的时候我们activity调用service里面的方法是通过Binder的(就是Service里面有个内部类实现了IBinder接口,在onBind方法中会把这个对象返回给activity里面
    我们在activity里面就可以使用这个Binder类的应用来调用service里面的方法了)。
    
网上摘录:
    在Actvity启动过程中,其实是应用进程与SystemServer进程相互配合启动Activity的过程,其中应用进程主要用于执行具体的Activity的启动过程,回调生命周期方法等操作,
    而SystemServer进程则主要是调用其中的各种服务,将Activity保存在栈中,协调各种系统资源等操作。

    注意细节:
    a.
        问题:
            通过这里的代码我们可以发现,其实我们在Activity中调用startActivity的内部也是调用的startActivityForResult的。
            那么为什么调用startActivityForResult可以在Activity中回调onActivityResult而调用startActivity则不可以呢 
        答案:
            可以发现其主要的区别是调用startActivity内部调用startActivityForResult传递的传输requestCode值为-1,
            也就是说我们在Activity调用startActivityForResult的时候传递的requestCode值为-1的话,那么onActivityResult是不起作用的。 
            实际上,经测试requestCode的值小于0的时候都是不起作用的,所以当我们调用startActivityForResult的时候需要注意这一点。

    b.
        在查看execStartActivity方法之前,我们需要对mInstrumentation对象有一个了解 什么是Instrumentation 
        Instrumentation是android系统中启动Activity的一个实际操作类,也就是说Activity在应用进程端的启动实际上就是Instrumentation执行的,那么为什么说是在应用进程端的启动呢 
        实际上acitivty的启动分为应用进程端的启动和SystemServer服务进程端的启动的,多个应用进程相互配合最终完成了Activity在系统中的启动的,
        而在应用进程端的启动实际的操作类就是Intrumentation来执行的,可能还是有点绕口,没关系,随着我们慢慢的解析大家就会对Instrumentation的认识逐渐加深的。

        可以发现execStartActivity方法传递的几个参数: 
        this,为启动Activity的对象; 
        contextThread,为Binder对象,是主进程的context对象; 
        token,也是一个Binder对象,指向了服务端一个ActivityRecord对象; 
        target,为启动的Activity; 
        intent,启动的Intent对象; 
        requestCode,请求码; 
        options,参数;
        
        public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
            ...
        }
        
    c.
        Binder接口 –> ActivityManagerNative/ActivityManagerProxy –> ActivityManagerService;

        这样,ActivityManagerNative与ActivityManagerProxy相当于一个Binder的客户端而ActivityManagerService相当于Binder的服务端,
        这样当ActivityManagerNative调用接口方法的时候底层通过Binder driver就会将请求数据与请求传递给server端,并在server端执行具体的接口逻辑。
        需要注意的是Binder机制是单向的,是异步的,也就是说只能通过client端向server端传递数据与请求而不同等待服务端的返回,也无法返回.
        
        那如果SystemServer进程想向应用进程传递数据怎么办 
        这时候就需要重新定义一个Binder请求以SystemServer为client端,以应用进程为server端,这样就是实现了两个进程之间的双向通讯。
      
    d.
        这里通过我们刚刚的分析,ActivityManagerNative.getDefault()方法会返回一个ActivityManagerProxy对象,那么我们看一下ActivityManagerProxy对象的startActivity方法:
        这里就涉及到了具体的Binder数据传输机制了,我们不做过多的分析,知道通过数据传输之后就会调用SystemServer进程的ActivityManagerService的startActivity就好了。
        以上其实都是发生在应用进程中,下面开始调用的ActivityManagerService的执行时发生在SystemServer进程。
        
    e.
        Activity生命周期中的第一个生命周期方法终于被我们找到了。。。。也就是说我们在启动一个Activity的时候最先被执行的是栈顶的Activity的onPause方法。
        (一般我们可能认为是新的activity的onCreate方法,通过源码分析可以知道不是的)
        记住这点吧,面试的时候经常会问到类似的问题。
    f.
        Activity的启动流程一般是通过调用startActivity或者是startActivityForResult来开始的

        startActivity内部也是通过调用startActivityForResult来启动Activity,只不过传递的requestCode小于0

        Activity的启动流程涉及到多个进程之间的通讯这里主要是ActivityThread与ActivityManagerService之间的通讯

        ActivityThread向ActivityManagerService传递进程间消息通过ActivityManagerNative,ActivityManagerService向ActivityThread进程间传递消息通过IApplicationThread。

        ActivityManagerService接收到应用进程创建Activity的请求之后会执行初始化操作,解析启动模式,保存请求信息等一系列操作。

        ActivityManagerService保存完请求信息之后会将当前系统栈顶的Activity执行onPause操作,并且IApplication进程间通讯告诉应用程序继承执行当前栈顶的Activity的onPause方法;

        ActivityThread接收到SystemServer的消息之后会统一交个自身定义的Handler对象处理分发;

        ActivityThread执行完栈顶的Activity的onPause方法之后会通过ActivityManagerNative执行进程间通讯告诉ActivityManagerService,栈顶Actiity已经执行完成onPause方法,继续执行后续操作;

        ActivityManagerService会继续执行启动Activity的逻辑,这时候会判断需要启动的Activity所属的应用进程是否已经启动,若没有启动则首先会启动这个Activity的应用程序进程;

        ActivityManagerService会通过socket与Zygote继承通讯,并告知Zygote进程fork出一个新的应用程序进程,然后执行ActivityThread的mani方法;

        在ActivityThead.main方法中执行初始化操作,初始化主线程异步消息,然后通知ActivityManagerService执行进程初始化操作;

        ActivityManagerService会在执行初始化操作的同时检测当前进程是否有需要创建的Activity对象,若有的话,则执行创建操作;

        ActivityManagerService将执行创建Activity的通知告知ActivityThread,然后通过反射机制创建出Activity对象,并执行Activity的onCreate方法,onStart方法,onResume方法;
            (源码看是通过类加载器来加载的,也是反射的一种)

        ActivityThread执行完成onResume方法之后告知ActivityManagerService onResume执行完成,开始执行栈顶Activity的onStop方法;

        ActivityManagerService开始执行栈顶的onStop方法并告知ActivityThread;

        ActivityThread执行真正的onStop方法;
      
自己总结:
    通过源码分析可以看到:
        startActivity(客户端) --- ActivityManagerNative.getDefault() ---  进入到ActivityManagerService里面(服务端)。
        【ActivityManagerNative是一个抽象类继承Binder,具体的实现类是ActivityManager】所以这两个进程是通过ActivityManagerNative这个Binder通信的。
        
        服务端 --- app.thread.scheduleLauncherActivity(IApplicationThread) --- ApplicationThread(是ActivityThread的一个内部类)(客户端)
        【app.thread是一个IApplicationThread,也是一个Binder接口,具体是实现类是ApplicationThread】所以最终调用到了ActivityThread里面的方法了,又回到客户端了
    
        最终就是通过类加载器,加载Activity类,然后调用onCreate方法启动Activity
        
    所以2个进程间通信是通过Binder实现的。具体Binder底层是怎么通信的,还需要继续研究。

122、iperf3.0编译

sudo -s
source /etc/profile
export PATH=/home/hw/CTS/cts/android-ndk-linux/AndroidToolChain/bin:$PATH
make distclean
./configure --host=arm-linux CC=arm-linux-androideabi-gcc CXX=arm-linux-androideabi-g++ CFLAGS=-static CXXFLAGS=-static

tempdir = "/data/data/com.example.test";
test->outfile = stdout;

make

123、android使用su命令

String cmd = "ls -l /data/log/LogService/0/";
		
process = Runtime.getRuntime().exec("su");
DataOutputStream os = new DataOutputStream(process.getOutputStream());
os.writeBytes("ls -l /data/log/LogService/0/\n");//一定要加\n分割
os.writeBytes("exit\n");
os.flush();

//process = Runtime.getRuntime().exec(cmd);
buf = new BufferedReader(new InputStreamReader(process.getInputStream(), "utf-8"));
int status = process.waitFor();//0正常,1无权限,2找不到文件

Log.d("gaoqiang", "status:" + status);

String str = "";
final StringBuffer sb = new StringBuffer();
while ((str = buf.readLine()) != null) {
    Log.d("gaoqiang", str+"\n");
    sb.append(str);
}
Toast.makeText(this, sb.toString(), 1).show();

124、非root的手机,将下载的命令push到手机上执行。

有时候我们需要在app中执行一个命令,例如iperf,我们不想重新写个demo,只是想验证下命令是不是能正常执行。
这个时候我们可以在  /data/local/tmp目录下执行

步骤:
    1.push到/data/local/tmp/下(例如push iperf命令到这个目录下)
    2.chmod 777 iperf
    3./data/local/tmp/iperf -c 192.168.1.5
备注:其它的命令也一样,例如下载busybox也可以执行

125、android traceroute功能实现

在windows下面的命令【tracert】
    C:\Users\CHINA\Desktop>tracert www.tencent.com

    通过最多 30 个跃点跟踪
    到 ssd.tcdn.qq.com [119.147.227.25] 的路由:

      1    <1 毫秒   <1 毫秒   <1 毫秒 192.168.1.1
      2     1 ms     1 ms    <1 毫秒 192.168.0.200
      3     3 ms     5 ms     4 ms  100.64.0.1
      4     3 ms     3 ms     3 ms  253.185.37.59.broad.dg.gd.dynamic.163data.com.cn [59.37.185.2
      5     *        *        *     请求超时。
      6     *        *        *     请求超时。
      7     *        *        *     请求超时。
      8     *        *        *     请求超时。
      9    13 ms    13 ms    13 ms  119.147.227.25

    跟踪完成。
    
在linux下面的命令【traceroute】,但是android里面是没有这个命令的,使用busybox很多需要有root权限。
现在有2种实现方案:
    1、使用交叉编译好的traceroute命令,这个暂时没有找到好的方法。
    2、使用ping去实现。【github有很多这种案例,可以参考】
   
ping -c 1 -W 10 -t 1 www.baidu.com (获取每个节点的ip)
解析出ip地址,查询ip的位置
ping -c 1 - W 10 192.168.1.1 (解析出ping的时延,可以ping多次,window上的tracert命令是ping了3次)

MTU的探测方法
windows系统:

    查看网卡的mtu的值
    netsh interface ipv4 show interfaces
    修改本地的mtu值
    netsh interface ipv4 set subinterface "本地连接 2" mtu=1500 store=persistent

    测试最大MTU值方法
    ping -n 1 -f -l 1472 www.baidu.com
    -f不允许私自拆分数据包
    -l发送指定大小的数据包

android 系统 :
    查看手机mtu值设置
    cat /sys/class/net/wlan0/mtu  
    ifconfig命令也可以查看
    ip ad |grep wlan0 也可以查看
    
    【ping -c 1 -M do -s 1373 www.baidu.com】nova plus手机验证结果
    1|shell@HWMLA:/sys/class/net/wlan0 $ ping -c 1 -M do -s 1373 www.baidu.com
    ping -c 1 -M do -s 1373 www.baidu.com
    PING www.a.shifen.com (163.177.151.109) 1373(1401) bytes of data.
    ping: local error: Message too long, mtu=1400

    --- www.a.shifen.com ping statistics ---
    1 packets transmitted, 0 received, +1 errors, 100% packet loss, time 0ms
    
    【ping -c 1 -M do -s 1373 www.baidu.com】p8max手机验证结果
    shell@HWP8max:/ $ ping -c 1 -M do -s 1373 www.baidu.com
    ping -c 1 -M do -s 1373 www.baidu.com
    PING www.a.shifen.com (163.177.151.109) 1373(1401) bytes of data.
    From 192.168.3.3: icmp_seq=1 Frag needed and DF set (mtu = 1400)

    --- www.a.shifen.com ping statistics ---
    0 packets transmitted, 0 received, +1 errors
        
    只有设置1372才能通过,加上28的包头大小刚好1400。所以测试不了网关的mtu最大值
    
    设置手机的mtu值(需要root手机)
    ip link set dev wlan0 mtu 1500

126、在调试已安装好的应用方法(手机需要root)

有时候安装了app,在启动的时候想打断点调试,如果等点击app启动再在as中启动断点会很快就消失了,断点位置已经运行过了。
这个时候我们可以使用命令来启动指定的activity【am start -D -n  com.hw.smarthome/com.hw.smarthome.about.AgreementActivity】
输入命令的时候页面会弹框提示连接debugger,这个时候在as中把调试器关联上就可以运行断点了

127、对文本指定的字符修改颜色和大小,加粗等的方法

SpannableString SpannableStringBody = new SpannableString(this.getString(R.string.app_policy_msg_new));
SpannableStringBody.setSpan(new StyleSpan(Typeface.BOLD), 29, 84, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
cView.setText(SpannableStringBody);

可以使用SpannableString,可以设置样式。

128、退出app方法:

App.systemExit();

自杀进程
Process.killProcess(Process.myPid());

129、调试本地mysql数据库

启动服务:【net start mysql57】启动这个服务名

130、将一键提单的代码放到linux上使用gradlew编译出现的问题:

a、访问不到gradle里面配置的仓库地址
    解决方法:配置代理服务器

b、提示没有licence的错误 “Warning: License for package Android Support Repository not accepted.”
    原因:这个是由于在uploadLog模块中使用了和linux电脑中sdk不一样的support-v4包,导致需要license。
    解决方法:将版本修改为25.1.1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值