【21-8-6笔记】专题研究:广播/Service中实现自动模拟APP的操作

一、当注册了静态广播Receiver,接收到全局的action时,会报错:Background execution not allowed

解决方法:1)加上flag:intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
adb发送的广播则加上:-f
2)加上包名:Intent.setPackage(“com.example.xxx.myReceiver”);
adb发送的广播则加上:-n “com.example.xxx/com.example.xxx.myReceiver”
参考:Background execution not allowed 广播无法接收问题的解决方法
Android adb shell am 模拟发送 广播(带 Action、Flag、Extra)

二、在Receiver内无法将context转换为Activity问题:Caused by: java.lang.ClassCastException: android.app.ReceiverRestrictedContext cannot be cast to android.app.Activity

解决方法:1)只能将广播转为动态广播!然后context就是当前加载类的activity
参考:
ReceiverRestrictedContext无法强制转换为android.a…

三、构建简易测试APP - 额外新建(暂F)

1)先在AndroidStudio中正常地新建一个带Activity的空白APP
2)由于在测试程序中,需要获取到测试模块的View_ID来进行模拟操作,有如下方法:
······①直接将要测试模块的 "res"文件夹整个复制到测试APP的src -main文件夹中(不推荐,可能要删除部分资源)
······② 或者,(该方法不推荐,可能出现build失败)构建AAR: 在要测试模块的build.gradle中将apply plugin改为:apply plugin: 'com.android.library',若在该文件中有定义‘applicationId’或者‘applicationVariants’属性,请先删去,同时还要删去配置中的APP定义 。 最后rebuild生成aar:
在这里插入图片描述在这里插入图片描述
······③ (*有问题!)直接在测试模块的build文件夹中,搜索“R.txt”,在将此R.txt复制到测试app的lib文件夹中,并在build.gradle中的android{}中依赖lib文件夹
·····④在上③的基础上,(暂时只能如此)按住Ctrl键将鼠标放在测试代码的R.id.xxx上,会显示它的int值,再将这个R.id.xxx改为这个int值,此时会报错,按alt+Enter键添加注解“@SuppressLint("ResourceType")

四、同项目新建测试app模块(暂F)

1)在要测试模块的同项目内,新建一个module:空白的app
2)依赖模仿要测试模块的build.gradle
3)新建一个简单的MainActivity.java,不需要什么实质的内容
4)新建一个广播MyReceiver.java,并在xml中注册静态接收和activity:

 <receiver
        android:name=".MyReceiver"
        android:enabled="true"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED"/>
            <action android:name="com.testSetting"/>
        </intent-filter>
    </receiver>
<activity
        android:name=".MainActivity"
        android:label="@string/app_name"
        android:launchMode="singleTask">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

5)在测试模块的build文件夹中,搜索“R.txt”,在将此R.txt复制到测试app的lib文件夹中,并在build.gradle中的android{}中依赖lib文件夹:

  repositories {
        flatDir {
            dirs 'libs'  // 声明添加libs文件夹为库
        }
    }

6)(暂时只能如此)按住Ctrl键将鼠标放在测试代码的R.id.xxx上,会显示它的int值,再将这个R.id.xxx改为这个int值,此时会报错,按alt+Enter键添加注解“@SuppressLint("ResourceType")

五、直接在测试模块下创建广播接收器Reciever并注册静态监听

1)在测试模块xxx内创建广播接收器MyReceiver,并在AndroidManifest.xml内注册静态监听:
在这里插入图片描述
2)(效果不是很理想)在MyReceiver.java的onReceive()方法内编写接收到Intent后的操作,如启动应用:

  ComponentName cmp = new ComponentName("com.xx.setting","com.desaysv.xx.SettingActivity");
            Intent intent_start = new Intent(context,SettingActivity.class);
            intent_start.setComponent(cmp);
            intent_start.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); //新任务
            intent_start.addCategory(Intent.CATEGORY_LAUNCHER); 
            intent_start.setAction(Intent.ACTION_MAIN);    //主活动
           context.startActivity(intent_start);

3)现问题点:可以正常启动应用,但因为onReceive()内无法获取到当前Activity(此时的context还是接收器类型的),而无法通过Activity.findViewById检索界面上的View
4)问题3可以在主Acticity里加入静态的instan = this 给外部调用来解决,但仍需要复测“2)”启动应用的效果

六、将MyReceiver放在测试模块内,并在APP启动时再注册广播动态监听

1)先编写好MyReceiver类,再在APP的主Activity内的onCreate中动态注册监听:

        IntentFilter filter = new IntentFilter();
        filter.addAction("com.xx.grepStart");
        filter.addAction("com.xx.grepTest");
        this.registerReceiver(MyReceiver.getInstance(), filter);

2)在MyReceiver类的onReceive()内编写相应的自动化动作,如模拟点击、输入:tvEdit.performClick();tvName.setText("testDevice");
3)测试主要使用adb执行:
①先启动测试模块的APP:

adb shell am start -n com.xx.setting/com.xx.setting.SettingActivity

②再根据onReceive()内的intent监听方式,发送相应的广播,以成功调用到onReceive()内的动作。

七、外部接口调用问题

1)先使用ui操作切换到该外部接口的应用场景:如xxBtView.performClick()切换到BT场景
2)因为切换场景考虑到机器的延迟,所以将实际调用外部接口的方法放置于新建的多线程中,等待场景切换完成再执行。
多线程方法有:①(不推荐)

activity.runOnUiThread(new Runnable() {               
            @Override 
            public void run() {  
                // TODO Auto-generated method stub  
            	int i = 30;
            	textView.setText(""+i+" s");
            	}
            }  
        });  

参考:android开发之多线程实现方法概述

② 其他方法:如Handler,匿名线程等(推荐):
参考全面详解Android实现多线程的几种方式

八、adb logcat中文乱码

1)直接在win的cmd内输入 chcp 65001
参考:Android logcat输出中文乱码

九、安卓实机中的反射方法

1)先在MyReceiver类中定义一个静态MyReceiver类的instance,并在onReceiver()方法内加上:instance = this;
2)在Handler异步中应用反射方法:

clz = instance.getClass();
 method_ = clz.getMethod(testMethod, null); 
 //testMethod为adb传入的string类型参数
 method_.invoke(instance);

参考:Android反射机制
java反射及Method的Invoke方法(转载)

十、部分测试中,调用外部接口或者模拟操作时会报空异常,建议嵌套异步使用:

如在测试方法里再多加一层异步:
在这里插入图片描述

以上。
参考来源:
Android View事件----全解

十一、安卓线程同步问题总结

1、synchronized :
1)某个线程得到了对象锁之后,该对象的所有同步方法是锁定的,其他线程是无法访问的。(对象锁)
2)线程是交替执行的,说明如果某个线程得到了对象锁,但是另一个线程还是可以访问没有进行同步的方法或者代码。(对象锁)
3)由于静态方法是类所有对象共用的,所以进行同步后,该静态方法的锁也是所有对象唯一的。(类锁)
4)类锁和对象锁是不一样的锁,是互相独立的,可同时运行。(Both)
参考:Java中synchronized同步锁用法及作用范围

2、wait()和notify()机制
1)wait()暂停,notify()开始。
2)需要注意的概念是:

#调用obj的wait(), notify()方法前,必须获得obj锁,也就是必须写在synchronized(obj) {…} 代码段内。

#调用obj.wait()后,线程A就释放了obj的锁,否则线程B无法获得obj锁,也就无法在synchronized(obj) {…} 代码段内唤醒A。

#当obj.wait()方法返回后,线程A需要再次获得obj锁,才能继续执行。

#如果A1,A2,A3都在obj.wait(),则B调用obj.notify()只能唤醒A1,A2,A3中的一个(具体哪一个由JVM决定)。

#obj.notifyAll()则能全部唤醒A1,A2,A3,但是要继续执行obj.wait()的下一条语句,必须获得obj锁,因此,A1,A2,A3只有一个有机会获得锁继续执行,例如A1,其余的需要等待A1释放obj锁之后才能继续执行。

#当B调用obj.notify/notifyAll的时候,B正持有obj锁,因此,A1,A2,A3虽被唤醒,但是仍无法获得obj锁。直到B退出synchronized块,释放obj锁后,A1,A2,A3中的一个才有机会获得锁继续执行。

参考:Android多线程设计模式之-wait()和notify()机制

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值