[Android] 优雅的处理登录页面跳转逻辑

一般有用户系统的应用都会有以下两种需求:
1、在执行某个动作时需要判断当前用户是否登录,如果没有登录则跳转至登录页面,登录成功后返回原页面但不执行任何操作,如果已经登录则直接执行相应的操作。
2、在执行某个动作时需要判断当前用户是否登录,如果没有登录则跳转至登录页面,登录成功后返回原页面继续执行相应的操作,如果已经登录则直接执行相应的操作。
但往往一个应用中会有很多地方需要有这样的判断逻辑,所以直觉告诉我们应该把这一重复的处理逻辑封装一下:

1、定义工具类LoginUtil.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class LoginUtil extends Activity {

  private int REQUEST_CODE_LOGIN = 1;
  static LoginCallback mCallback;

  public interface LoginCallback {
    void onLogin();
  }

  @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Intent intent = new Intent(this, LoginActivity.class);
    startActivityForResult(intent, REQUEST_CODE_LOGIN);
  }

  public static void checkLogin(Context context, LoginCallback callback) {
    //此处检查当前的登录状态
    boolean login = AccountMgr.get().isLogin();
    if (login) {
      callback.onLogin();
    } else {
      mCallback = callback;
      Intent intent = new Intent(context, LoginUtil.class);
      context.startActivity(intent);
    }
  }

  public static void checkLogin(Context context, LoginCallback logged, LoginCallback callback) {
    //此处检查当前的登录状态
    boolean login = AccountMgr.get().isLogin();
    if (login) {
      logged.onLogin();
    } else {
      mCallback = callback;
      Intent intent = new Intent(context, LoginUtil.class);
      context.startActivity(intent);
    }
  }

  @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    finish();
    if (requestCode == REQUEST_CODE_LOGIN && resultCode == RESULT_OK && mCallback != null) {
      mCallback.onLogin();
    }
    mCallback = null;
  }
}
2、在AndroidManifest.xml里配置此activity的主题
1
2
3
<activity
  android:name="com.xxx.base.utils.LoginUtil"
  android:theme="@style/NoDisplay" />

注意:在values中创建主题:

1
<style name="NoDisplay" parent="android:Theme.NoDisplay"/>

在values-v23中创建适配主题:

1
<style name="NoDisplay" parent="android:Theme.Translucent.NoTitleBar"/>

3、在登录页面成功登录后执行下面语句:
1
2
3
4
5
@Subscribe(threadMode = ThreadMode.MAIN)
  public void onEventMainThread(AccountEvent.LoginEvent event) {
    setResult(Activity.RESULT_OK);
    finish();
  }
4、在需要判断登录的地方直接调用下面两种重载方法即可:
1
2
3
4
5
LoginUtil.checkLogin(getActivity(), new LoginUtil.LoginCallback() {
      public void onLogin() {
        //已经登录和未登录状态下进行的操作
      }
    });
1
2
3
4
5
LoginUtil.checkLogin(getActivity(), new LoginUtil.LoginCallback() {
      @Override public void onLogin() {
        //已经登录状态进行的操作
      }
    }, null);

关注FullStackEngineer的公众号,更多分享

Share Comments

[两周阅读清单] Realm、单元测试、Rxjava、读书

为什么这次是两周呢?是因为上周去南京浪了所以没更😂

文章

Android单元测试系列文章
这系列文章是蘑菇街的小创发表的,应该是目前为止我知道的讲Android单元测试最系统全面的了,大家关于一切单元测试的问题和疑惑都可以在这里找到一些参考。

是时候学习RxJava了
这篇文章讲了RxJava的多种使用场景,有很多有趣的用法。

更优雅的 Android 发布自动版本号方案
如果你的项目是用 Git 管理的,并且恰巧又是使用 Gradle 编译,那么这里有一种更加优雅的自动版本管理方法。

『直播』大时代
之前一直不理解为什么直播能火,看了一些文章之后慢慢有了一些认识了。

书单

《人类简史》
这是最近一段时间读过的书中感觉最好的一本了,不要被它的标题迷惑了,其实它不是一本单纯讲历史的书籍,其中涵盖了很多社会人文科学自然等多领域的知识和他们之间内在的一些联系,时不时就应该充实一下自己的精神世界,哈哈。

开源库

Realm
这个库更新的挺快的,现在正式版已发亲测非常高效,而且最近在WWDC上也提到了。

FileDownloader
这是流利说团队开源的一个下载引擎的库,提的issue回复很快,同时亲测很好使。


FullStackEngineer的公众号,更多分享

Share Comments

[Android] ViewPager的notifyDataSetChanged刷新无效果的解决方案

最近在开发中遇到了一个问题:ViewPager设置的PagerAdapter调用notifyDataSetChanged()时界面无刷新以至于影响到功能的实现。不过有一个很傻的方法倒是可以解决就是给Viewpager重新设置一次适配器,下面我来分享一下如何优雅的解决这个问题吧。

大家进入ViewPager的源码可以看到下面的代码段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/**
* Set a PagerAdapter that will supply views for this pager as needed.
*
* @param adapter Adapter to use
*/
public void setAdapter(PagerAdapter adapter) {
......

if (mAdapter != null) {
if (mObserver == null) {
mObserver = new PagerObserver();
}
mAdapter.setViewPagerObserver(mObserver);
......
}

......
}

private class PagerObserver extends DataSetObserver {
@Override
public void onChanged() {
dataSetChanged();
}
@Override
public void onInvalidated() {
dataSetChanged();
}
}

void dataSetChanged() {
......

for (int i = 0; i < mItems.size(); i++) {
final ItemInfo ii = mItems.get(i);
final int newPos = mAdapter.getItemPosition(ii.object);

if (newPos == PagerAdapter.POSITION_UNCHANGED) {
continue;
}

if (newPos == PagerAdapter.POSITION_NONE) {
mItems.remove(i);
i--;

if (!isUpdating) {
mAdapter.startUpdate(this);
isUpdating = true;
}

mAdapter.destroyItem(this, ii.position, ii.object);
needPopulate = true;

if (mCurItem == ii.position) {
// Keep the current item in the valid range
newCurrItem = Math.max(0, Math.min(mCurItem, adapterCount - 1));
needPopulate = true;
}
continue;
}

......

}

}

意思是如果item的位置如果没有发生变化,则返回POSITION_UNCHANGED;如果item的位置已经不存在了,则回了POSITION_NONE。

解决方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private class SetDialogAdapter extends PagerAdapter {

private int mChildCount = 0;

@Override public void notifyDataSetChanged() {
mChildCount = getCount();
super.notifyDataSetChanged();
}

@Override public int getItemPosition(Object object) {
if (mChildCount > 0) {
mChildCount--;
return POSITION_NONE;
}
return super.getItemPosition(object);
}
}

我们覆盖getItemPosition()方法,当调用notifyDataSetChanged时,让getItemPosition方法强行返回POSITION_NONE,从而让ViewPager重绘所有item。


FullStackEngineer的公众号,更多分享

Share Comments

[Android] 6.0 系统权限适配实践

Android 6.0也已经出来有一段时间了,其中的权限模式从一开始的全部列出授予,到后来的运行时动态申请,这对开发者来说是一个重要的变化,今天我来分享一下具体的实践过程。

首先检查一下你的项目中 targetSdkVersion,如果是 23及以上,则必须适配新的权限模式;如果是 23以下,则还是统一在安装时全部申请权限。然后你还需要了解哪些权限是危险权限、特殊权限以及正常权限。

接下来我们需要准备两个类:
PermissionsChecker.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* 检查权限的工具类
*
* @author mafei
*/
public class PermissionsChecker {
private final Context mContext;

public PermissionsChecker(Context context) {
mContext = context.getApplicationContext();
}

// 判断权限集合
public boolean lacksPermissions(String... permissions) {
for (String permission : permissions) {
if (lacksPermission(permission)) {
return true;
}
}
return false;
}

// 判断是否缺少权限
private boolean lacksPermission(String permission) {
return ContextCompat.checkSelfPermission(mContext, permission)
== PackageManager.PERMISSION_DENIED;
}
}

PermissionsType.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
* Created by mafei on 16/3/31.
*/
public class PermissionsType {

/**
* 读取手机权限
*/
public static final int READ_PHONE_STATE_CODE = 1;
/**
* 获取相机权限
*/
public static final int CAMERA_CODE = 2;

/**
* 获取存储权限
*/
public static final int WRITE_EXTERNAL_STORAGE_CODE = 3;

public static class PermissionsTypeExtend {

public static String toDescription(int type) {
switch (type) {
case PermissionsType.READ_PHONE_STATE_CODE:
return "需要在系统“权限”中打开“电话”开关,才能更好的为你服务";
case PermissionsType.CAMERA_CODE:
return "需要在系统“权限”中打开“相机”开关,才能相机拍照";
case PermissionsType.WRITE_EXTERNAL_STORAGE_CODE:
return "需要在系统“权限”中打开“存储”开关,才能离线缓存";
default:
return "需要在系统“权限”中打开相关权限,才能更好的为你服务";
}
}
}
}

准备好这两个类之后,就可以在你需要进行权限申请和控制的地方写下面的代码了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
private PermissionsChecker mPermissionsChecker; // 权限检测器
// 所需的全部权限
static final String[] PERMISSIONS = new String[] {
android.Manifest.permission.WRITE_EXTERNAL_STORAGE
};

@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_movie_detail);
mPermissionsChecker = new PermissionsChecker(this);
}

if (VersionSDK.isMarshmallowOrHigher()) {
String[] permissions = PERMISSIONS;
if (mPermissionsChecker.lacksPermissions(permissions)) {
requestPermissions(permissions, PermissionsType.WRITE_EXTERNAL_STORAGE_CODE);
} else {
//需要权限才能操作的代码
}
} else {
//需要权限才能操作的代码
}

@Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
if (requestCode == PermissionsType.WRITE_EXTERNAL_STORAGE_CODE) {
for (int i = 0; i < permissions.length; i++) {
String permission = permissions[i];
int grantResult = grantResults[i];

if (permission.equals(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
if (grantResult == PackageManager.PERMISSION_GRANTED) {
movieDetailIvCache.setSelected(true);
} else {
ToastUtils.showShortToast("需要存储权限");
}
}
}
}
}


FullStackEngineer的公众号,更多分享

Share Comments

[一周阅读清单] 开发神器推荐

『一周阅读清单』第五期与大家见面了,这周最大的新闻莫过于Google I/O 2016大会了,虽然自己没能到现场但各种现场的照片和新闻已经刷爆了朋友圈,其中比较吸引我的是Firebase、Android Instant Apps和Google Home,好了这次推荐几款开发神器。

Stetho
Chrome的Developer Tools对于WEB开发者来讲几乎是一个神器,而Chrome Store里也有无奇不有的插件,如果Chrome能调安卓应用应该是一件美好的事,而Facebook开源的一个工具Stetho让Chrome调试android 应用不再是一个梦。安卓在调试时,有时候需要查看数据库,SharePreference等,而这个前提是必须root,另一方面,andoird的网络方面的抓包调试显得很困难,而这一切,Stetho都为我们轻轻松松地解决。除了使用android集成环境里的工具查看view树,使用Stetho也可以做到。

LeakCanary
著名的开源组织 Square 开发了一套 Android 和 Java 内存泄露检测库。LeakCanary 是一个检测内存泄露的开源类库。你可以在 debug 包种轻松检测内存泄露。

Dex-Method-Counts
dex-method-counts来统计包中的总体数量,使用方法很简单,只需执行下面语句即可: java -jar path\dex-method-counts.jar path\App.apk。

Android-Butterknife-Zelezny
Android Butterknife Zelezny这个插件,事实上这是个Android Studio的plugin,他可以让你在添加Butterkinfe注解时偷偷懒,直接点击几下鼠标既可以完成注解的增加,同时还是图形化的操作,可以说,大大的减轻了开发负担。尤其是当你的layout中有很多很多的view需要通过findviewbyid来获得引用时。

Android-Parcelable-Plugin
手动实现Parcelable接口,往往需要写多些代码去实现,android-parcelable-plugin.可以让你几秒钟实现 Parcelable接口,让开发变得更开心。

AndroidWiFiADB
你还在为在使用真机测试时,缺乏数据线而发愁吗?你还在为同事踩烂了你的一根数据线而耿耿于怀吗?(说实话我今天就被踩烂了一根) 你还在为每次用数据线连接真机测试时,都得抽拔抽拔而不耐烦吗?AndroidWiFiADB 通过WiFi 进行adb调试, 摆脱数据线。

GsonFormat
这是一个根据JSONObject格式的字符串,自动生成实体类参数。

ACRA
ACRA是一个可以让安卓应用自动发出GoogleDoc格式崩溃报告的函数库。安卓开发者可以通过ACRA获取应用崩溃或者错误行为的数据。如果有一个崩溃发生,你的应用不会越过已有的系统崩溃提醒或者报告功能来添加用户提醒。如果使用Toast、状态提醒条或者直接对话框模式,这个“强制关闭”的对话框不会再显示,就算设备上原生系统的提醒功能开启也不能发送一个另外的报告。


FullStackEngineer的公众号,更多分享

Share Comments

[一周阅读清单] 在线小工具推荐

『一周阅读清单』第四期与大家见面了,这周我大部分外业余时间都在看Python爬虫和JavaScript,所以今天给大家推荐一些之前收藏的实用在线小工具吧。

在线代码生成器,提高开发效率
Android Layout Finder
只需粘贴你的Android布局代码在第一个文本框,选择你需要的功能,就会自动生成代码。

Android KickstartR
帮助您快速创建 Android应用程序并使用最流行的库进行配置。 它创建和配置你的项目给你。只专注于代码!

Android Button Maker
在线生成shape drawable XML代码 ,提供图形界面调试,更加直观。

Android Layout Parser工具
输入你的字段的前缀,选择你的XML中的范围并点击生成,自动生成java代码。

Android资源生成分析工具
Android Asset Studio
icon制作(桌面icon,通知栏icon等),9patch图片制作,ActionBar样式等相关的工具。

Android Action Bar Style Generator
自定义生成自己风格主题,图形化界面,直接下载相关的代码和资源。

Simple Nine-Patch Generator
快速生成不同分辨率的资源。

Android Holo Colors Generator
在线生成android的组件,如EditText、Radio等等,通过自定义自己的颜色风格。图形界面,简单可观。

Android Assets Viewer
通过上传的自己的drawable resources,是否达到设计师的视觉效果。

Android SVG To VectorDrawable
一个可以将SVG图片转换为Vector Drawable xml文件的在线工具。

计算转换工具
Android Dp Px Calculator
dp px 相互转换。

DP-PX-Converter
自定义生成自己风格主题,图形化界面,直接下载相关的代码和资源。

Androidpixels
不同分辨率下的转换数值。

Pixelcalc
Android pixel 计算器。

源码搜索引擎
grepcode.com是一个Java源码搜索引擎,对于查看Android代码也不例外。并且支持多个API版本快速切换查看。如果你的IDE关联本地代码后,让机器累的喘不过气来,那么就试一试这个在线的工具吧。
Grepcode
支持多个API版本快速切换查看。

在线反编译
当你还在花时间切换不同的反编译工具时,一个在线反编译网站应运而生,它就是Android APK Decompiler,只需上传要反编译的apk包,无需多时,源码可以下载下来了。
Android APK Decompoler
有两个在线反编译,简单容易,喜欢哪个用哪个。

Google Play(无需登录)
由于一些你懂的原因,国内无法直接访问Google Play商店。而且下载Google Play商店还是需要登陆谷歌账户,以国内的网络,下载成功简直是太困难了。这里介绍一款不需要账户国内即可访问的Web工具。可以通过输入包名或者Google Play地址即可下载。
快速下载Google Play应用
下载Google Play的应用。

进制转换
Android中所有的资源都有一个对应的资源ID,资源ID的类型为16进制的整数。有些时候特殊的场合处理资源ID,为了调试需要进行进制转换,比如16进制转常用的10进制。不用自己算,使用下面的工具就可以轻松搞定。
进制转换
在线快速转换,比如16进制转常用的10进制。

JSON格式化
在CS应用中,客户端和服务器端通常使用json作为数据交换格式。当分析的时候,我们必然是将raw数据转换成可读性更高的。快来使用这个强大的工具吧。
JSON格式化
转成标准json格式。

API 市场
收录网站各个领域的开发API,比如图片识别,语音合成,OCR等等一些生活常用的API开放接口,不需要后台也可以做一款使用的应用。
API 市场
图片识别,语音合成,OCR等等一些生活常用的API开放接口。

在线UML工具
在线画流程图,如果你在家里用的MAC OX ,但是在公司用的是Linux ,那么导致UML图的转移不方便,这个在线的UML画图解决了一切问题。
在线UML工具
UML工具 、类图、流程图等等。

公开图标库
国内功能很强大且图标内容很丰富的矢量图标库,提供矢量图标下载、在线存储、格式转换等功能。
公开图标库
设计和前端开发的便捷工具。

批量图片压缩
当图片很大的时候,又不想麻烦设计师,那么这个网站很适合你,批量压缩各种图片,直接把图片拖拉进去就行。
批量图片压缩
批量图压缩。

RGB转换
通常UI设计师都会给开花童鞋色值,当疏忽的时候,我们可以使用截图软件得到10进制的三个值,然后将其转换成色值。这里有一个便捷的RGB工具。
RGB转换
RGB转换。


FullStackEngineer的公众号,更多分享

Share Comments