这种面试中常被问到的题,居然在实际编程中真的遇到了。
这是在什么场景下出现的呢?
在多type的RecyclerView的使用中,有这样一个场景:某一个item涉及到了三个接口请求。也就是说要请求三个接口,才能得到这个item的全部数据。编程之初,我就是请求三次,这样问题就出现了,这个item会在页面加载完成后闪三次。这极大的影响了用户体验。
于是我就提出了本文这个问题:同时发出多个接口请求,如何在所有接口都请求完成之后再刷新UI?
经过一番思索,我想出了两个办法:
(1)一共有三个接口,那就在第一个接口请求成功之后,去请求下一个接口
(2)定义一个全局变量,在要发出接口请求时,置为0;然后发出一个接口请求,值加1,并判断该值是否为3;如果为3,刷新UI.
两种方案对比一下,最终选择了第二种。
public class CheckDoubleClick {
private static long lastClickTime = 0;
public static boolean isFastDoubleClick() {
long time = System.currentTimeMillis();
long timeD = time - lastClickTime;
if (0 < timeD && timeD < 500) {
return true;
}
lastClickTime = time;
return false;
}
}
以上代码非常简单易用
Iterator iterator = scheduleDateList.iterator();
while (iterator.hasNext()) {
ScheduleDate date = iterator.next();
if (date == null || !TextUtils.isEmpty(date.getFestival())) {
iterator.remove();
}
}
如果要是采用for循环,有可能会出现某些元素删除不掉的坑。
因为list每remove掉一个元素后,后面的元素会向前移动,此时如果执行i+1,则刚刚被移过来的元素没有被读取。
要解决这个问题,除了用iterator,也可以从后向前遍历。
//相关权限申请
private void requestPermission() {
if (Build.VERSION.SDK_INT >= 23) {
final List permissionsList = new ArrayList<>();
addPermission(permissionsList, Manifest.permission.READ_CONTACTS);
if (permissionsList.size() > 0) {
requestPermissions(permissionsList.toArray(new String[permissionsList.size()]), CONTACTS_PERMISSION_REQUEST_CODE);
} else {
openContacts();
}
}
}
private void openContacts() {
startActivityForResult(new Intent(Intent.ACTION_PICK,
ContactsContract.Contacts.CONTENT_URI), CONTACTS_REQUEST_CODE);
}
private boolean addPermission(List permissionsList, String permission) {
if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
permissionsList.add(permission);
return false;
}
return true;
}
当一个单例对象需要持有context,应该使用getApplicationContext,否则会引起内存泄漏。所以原则上,我们应该尽量用getApplicationContext!那么,有没有什么场景下不要用getApplicationContext呢?在启动activity时,最好不要使用,因为applicationContext是没有任务栈的。
1、targetSdkVersion:表示最高能使用的api新特性。比如targetSdkVersion是19,那即使使用了api 23的新特性,实际运行时,也不会使用该特性。
2、compileSdkVersion:告诉gradle使用哪个版本的sdk编译应用。
3、buildToolVersion:构建工具的版本。构建工具包括:aapt、dx等。
这个时候一定要考虑数据同步性的问题。在做日历页的小程序分享需求时,有两个接口:一个接口控制入口是否展示,一个接口负责构建分享content。我在后面请求完第一个接口后,认为我只要更新数据层就好了。可是这样很可能有问题:当入口应该展示了,可是我没有刷新UI,导致入口并没有展示出来!
需要一个成员变量标识第一次inflate。只有当第一次inflate时,
var viewStub = mRootView.findViewById(R.id.wechat_stub) as? ViewStub
viewStub?.inflate()
后面就不能再inflate了,如果用到这个view,只需要用inflatedId找到这个View就行了。如下所示:
var viewStub = mRootView.findViewById(R.id.wechat_stub) as? ViewStub
viewStub?.inflate()
mRootView.findViewById(R.id.wechat_import).setOnClickListener {
it.visibility = View.GONE
}
当时有这样一个需求,如果当前定位在服务端下发的几个城市中,那就展示瀑布流,否则展示产品列表。我当时是这么做的:同时请求10城接口和瀑布流接口,如果是10城,就把瀑布流隐藏掉。可是这样存在接口谁先返回谁后返回的问题,于是我这边就设置了一个标志位。可是!!在切换城市,需要重新刷新页面的时候,我并没有把这个标志位重置,导致逻辑出错。
(1)
在线上可能存在一种情况:接口吐出来的数据不足3行,而这里的双重for循环还在按着3乘以3的结构布局。我写这行代码的时候显然已经考虑到了这种情况,只是这只考虑了空指针异常,并没有考虑到数组越界的异常。改进后的代码如下:
经验总结:数组遍历时,除了考虑空指针异常,也应该考虑数组越界异常。
(2)在日常开发中,接口常常会返回给我们一个数组,而这个数组只有一个元素。对于这种异常处理,我常常会直接对get(0)做下判空处理。然而,异常情况下数组元素可能为0啊。这个时候就会报数组越界的错。
(1)在开发周边游首页时,我在页面加载时给成员变量saleCityId进行赋值,
saleCityID = CurrentCityManager.getSaleCityId();
可是在后面某个操作里,我没有去更新这个成员变量的值,导致用到这个变量的url跳转都出现了问题。
经验总结:对于这种情况,没有必要定义成员变量,在用到售卖站城市id时,直接调用CurrentCityManager.getSaleCityId()方法即可。
(1)这个错误是在字符串转化为数字时出现的。
首先,字符串是如上图这样得到的。在这之前都做过异常处理,所以这边就直接转为带有一位小数的字符串。然而,在转化为数字时就会报错!经过反复排查,终于定位到问题!原来,当用户手机语言设置为波兰语时,如上方法转化为的带有一位小数的字符串,是用“,”拼接,如“5,4”。而不是我们能理解的“5.4”。经过查询,波兰语中,就是用逗号连接整数和小数的。如要正确转化,请改成如下代码:
经验总结:不管何时,只要字符串转数字,至少要加Try catch!!
在上线前,测试告诉我说给跳转到下个页面的url多加一个字段。于是,我就这样加了。。。
可是,自由行sdp工具化页面url和跟团游页面url的参数我不是用的一个变量,一个是用的query,一个是用的一个方法getSdpToolQuery。可是当我新增字段的时候,我看到query这个变量,就天真地以为这三个页面用的同一个query,于是就加错了。
经验总结:重读《重构》!自己的编程规范还需要进一步升级!!遇到这种情况,到底该不该用两个query呢?