工作笔记之Android记录


工作笔记

工作笔记用于记录Android开发过程中遇到的疑难点和难以解决的点,特此记录。

打包出现异常

java.lang.NoSuchMethodError: No static method asAttributeSet(Lorg/xmlpull/v1/a;)Landroid/util/AttributeSet; in class Landroid/util/Xml; or its super classes (declaration of 'android.util.Xml' appears in /system/framework/framework.jar)

原因:xml解析异常;解决方案是在混淆文件中配置该类,防止被混淆了之后找不到。

-dontwarn org.xmlpull.v1.XmlPullParser
-dontwarn org.xmlpull.v1.XmlSerializer
-keep class org.xmlpull.v1.* {*;}

As安装Apk出现问题

../build/outputs/apk/app-debug.apk does not exist on disk.
解决方案:

  • clean projects
  • rebuild Projects
  • Sync Projects with Gradle Files
  • Invalidate and restart
  • 清楚Android的配置 重新安装配置即可

键盘和布局问题

  1. 方法一:在项目的AndroidManifest.xml文件中界面对应的里加 android:windowSoftInputMode="adjustPan|stateHidden"
  2. 方法二:在你的Activity中的oncreate中setContentView之前写上这个代码getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
  3. 方法三:把顶级的layout替换成ScrollView,或者说在顶级的Layout上面再加一层ScrollView的封装。这样就会把软键盘和输入框一起滚动了,软键盘会一直处于底部。

实用的API

  • SystemClock.elapsedRealtime() 设备开机到现在的时间
  • System.currentTimeMillis() 当前时间,和系统时间有关联
  • Caused by: javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake 网络连接出现问题,更换网络

提示Apache的类找不到

java.lang.ClassNotFoundException:Didn't find class "org.apache.http.util.ByteArrayBuffer"

原因是使用了apache.http中的ByteArrayBuffer,但是Android高版本已经不使用apache.http,因此将ByteArrayBuffer 替换成ByteArrayOutputStream即可。

安装Apk

//安装Apk意图
    private static Intent installApkIntent(Context context, File file) {
        if (file == null) return null;
        if (!file.exists() && !file.isFile()) return null;
        Intent intent = new Intent(Intent.ACTION_VIEW);
        String type = "application/vnd.android.package-archive";
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
        }
        Uri uri = AppPathUtil.getFileToUri(context, file);
        context.getApplicationContext().grantUriPermission(context.getPackageName(), uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
        intent.setDataAndType(uri, type);
        return intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    }
//Android O的时候需要申请安装权限
    /**
     * 跳转到设置-允许安装未知来源-页面
     * 注意这个是8.0新API
     */
    @RequiresApi(api = Build.VERSION_CODES.O)
    public static void startInstallPermissionSettingActivity(Activity mActivity, String appId) {
        Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.setData(Uri.parse("package:" + appId));
        mActivity.startActivity(intent);
    }
//两个整合在一起
  /**
     * App 安装
     *
     * @param activity 上下文
     * @param filePath 文件路径
     * @return 返回true 请开启安装未知应用来源的权限!或开启安装  false代表需要重新尝试
     */
    public static boolean installApk(Activity activity, String filePath) {
        if (null == filePath) {
            return false;
        }
        return installApk(activity, new File(filePath));
    }

    /**
     * App 安装
     *
     * @param activity 上下文
     * @param file     文件路径
     * @return 返回true 请开启安装未知应用来源的权限!或开启安装  false代表需要重新尝试
     */
    public static boolean installApk(Activity activity, File file) {
        if (null == file) {
            return false;
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            if (!activity.getPackageManager().canRequestPackageInstalls()) {
                startInstallPermissionSettingActivity(activity, activity.getPackageName());
                Toaster.info(activity, "请开启安装未知应用来源的权限!");
                return true;
            }
        }
        Intent intent = installApkIntent(activity, file);
        if (null != intent) {
            activity.startActivity(intent);
            return true;
        }
        return false;
    }

AS打开项目的时候提示branch 142

 Uninitialized object exists on backward branch 142

出现这个的原因是项目的JDK版本和AS的编译版本冲突,多次条件AS的编译版本即可。

AS运行包含NDK的项目

A problem occurred configuring project ':app'. > java.lang.NullPointerException (no error message)

原因是AS查找本地NDK-Bundle出现问题,找不到NDK的配置导致。
在项目的配置中查找NDK的配置,如果不行可以进行手动配置,比如我的配置是

sdk.dir=D\:\\sdk
ndk.dir=D\:\\sdk\\ndk\\21.1.6352462

App的周期性弹窗

最近的项目要求用户在发起过问题之后就会发送每隔15秒的一个弹窗入口来引导用户付款。能打开这个功能的前提时用户点击了咨询的入口,然后我这边就要打开这个功能。
这个需要现在回想起来有一个点没有确认清除,是15秒的倒计时的循环弹出还是只在主界面进行弹出。如果按照15的间隔开始弹出的话,我设计的是通过监控ActivityLifeCallback来触发弹窗,这个初衷导致我在后面出现了不少的坑。因为功能的入口在一个网页的界面,通过JS来告诉我啥时候启动。但是如果在这个网页中启动过了之后的话,在下一个界面中如果用户点击了支付的弹窗和支付完成的话是不能再次出现弹窗的,不然用户就会收到频繁的弹窗提示。我一开始就把路给走窄啦,其实只需要在JS告诉我启动了弹窗的功能之后,我就在这里开始循环触发弹窗就行啦,结果导致我周五和周一这两天都在弄这个逻辑,一方面是我把路走窄啦,一方面是开始设计的初衷在我的思维惯性里已经是最好的啦,其他的方案对我来说都不优雅。

ActivityLoopUtil {
  uiHandler;//初始化
 public static void startLoop(){
    if(剩余次数>0){
     //开启  
     uiHandler. sendMessageDelayed(CODE,1000*10);
  }
 }
public void dispatchMessage(Message msg) {
    if(CODE==msg.what){
      //判断当前是否满足弹窗的条件,不满足的话 再次发送延迟消息
      //满足的话就弹出,并在弹出的界面吧次数减一  
  }
}
 public static void sopLoop(){
    uiHandler.removeMessage(CODE);
 }

HTTP的Content-Type问题

在一次和同事关于接口的问题中,发现了怎么使用都不行,对比了请求的数据之后发现,原来是后台设置了Content-Type中consumes是application/json;charset=Utf-8,而我这边传值的是application/json; charset=Utf-8两者中间就多了一个空格,因为这个问题我调试了很久,还一直以为是我网络库的问题,我问后台为什么要这么设置,不能修改吗?后台说不能,定死了。那我就很奇怪啦,怎么会有这种的类型呢,难倒我平时的设置不对吗?这次问题排查多亏了抓包,这个在PostMan中也没有发现这个问题,因为我在PostMan也是输入的和后台不一样。所以这个东西就很奇怪。通过抓包我还帮助IOS的一个同事解决请求总是失败的问题。熟悉抓包工具确实能解决前后台的小细节问题。反证后面的常用了抓包来解决问题,快速定位问题和解决问题才是作为一个开发人员的目标,至于你说你要当老板的话,也是要追求效率的。两个一点也不冲突,解决问题的能力一方面靠经验,一方面也考个人脑袋的灵活性。