Android开发小问题汇总二

作者: 闲庭 | 来源:发表于2018-10-15 10:10 被阅读9次

    此排序没有任何优先级或者重要程度。
    此笔记只为记录平时开发中碰到的经常用到确不太注意的一些问题,每次用过就忘记,还要重新搜索解决方案,所以在此积累下平时开发中碰到的一些常用而又容易忘记的简单小bug。

    本来想一直在同一篇文章中不断更新,可发现简书没办法发布篇幅太大的文章,所以拆开记录,虽然这样零散了,可能不太好查找,但是我会附上对应的链接。

    Android开发中小问题汇总一

    31、查看android手机中安装apk的包名等信息

    • 方法一:
      进入cmd窗口,输入adb shell,进入手机,在输入ls /data/data,即能显示出手机中安装apk的包名。(需要root权限)
    • 方法二:
      查看手机中非系统的apk包名信息,adb shell pm list package -3,这个命令很实用。这和命令后面不加-3表示查看手机中使用的apk包名。
    • 方法三:
      在代码中获取Android设备中apk的包名等信息。
       /*获取Android手机中安装的应用*/
        public static void getAllAppPackages(Context context) {
            Intent intent = new Intent(Intent.ACTION_MAIN, null);
            intent.addCategory(Intent.CATEGORY_LAUNCHER);
            List<ResolveInfo> apps = context.getPackageManager().queryIntentActivities(intent, 0);
            //for循环遍历ResolveInfo对象获取包名和类名
            for (int i = 0; i < apps.size(); i++) {
                ResolveInfo info = apps.get(i);
                String packageName = info.activityInfo.packageName;
                CharSequence cls = info.activityInfo.name;
                CharSequence name = info.activityInfo.loadLabel(context.getPackageManager());
                Log.e(Constant.LOG_TAG,name+"----"+packageName+"----"+cls);
            }
        }
      

    32、在使用AndroidStudio打包过程中会出现以下错误:"XXX" is not translated in “en” (English) [MissingTranslation]
    这个问题是在打包是如果API兼容到7.0及以上,如果Stringxml资源文件没有配置其他语言项时,会出现如此的错误;

    • 解决方案:在app的build.gradle文件中的android{}中加入如下配置然后刷新gradle文件再打包即可:
      android{
        ...
        ...   
       lintOptions {
              checkReleaseBuilds false
              abortOnError false
          }
      }
      

    33、为什么ScrollView嵌套ListView,ListView只显示一个Item的高度?
    这个问题估计大家面试的时候问到滑动冲突的时候可能都会知道这个,可能问到为什么的时候,估计不细心的人就不知道为什么了,下面简单介绍下:

    • ListView的onMeasure()方法如何计算高的:
      @Override
      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
          // Sets up mListPadding
          super.onMeasure(widthMeasureSpec, heightMeasureSpec);
          ....... //此处省略部分diamante
          if (heightMode == MeasureSpec.UNSPECIFIED) {
              heightSize = mListPadding.top + mListPadding.bottom + childHeight +
                      getVerticalFadingEdgeLength() * 2;
          }
      
          if (heightMode == MeasureSpec.AT_MOST) {
              // TODO: after first layout we should maybe start at the first visible position, not 0
              heightSize = measureHeightOfChildren(widthMeasureSpec, 0, NO_POSITION, heightSize, -1);
          }
      
          setMeasuredDimension(widthSize, heightSize);
          mWidthMeasureSpec = widthMeasureSpec;
      }
      
      可以看到当heightMode == MeasureSpec.UNSPECIFIED时,此事listview的高度则为一行item的高度加上一些padding值。至于heightMode为什么为MeasureSpec.UNSPECIFIED接着往下面看。
    • ScrollView重写了measureChild和measureChildWithMargins方法,在测量自己子View的时候会将高的Mode改成MeasureSpec.UNSPECIFIED。
      @Override
      protected void measureChild(View child, int parentWidthMeasureSpec,
              int parentHeightMeasureSpec) {
          ViewGroup.LayoutParams lp = child.getLayoutParams();
      
          int childWidthMeasureSpec;
          int childHeightMeasureSpec;
      
          childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft
                  + mPaddingRight, lp.width);
          final int verticalPadding = mPaddingTop + mPaddingBottom;
          childHeightMeasureSpec = MeasureSpec.makeSafeMeasureSpec(
                  Math.max(0, MeasureSpec.getSize(parentHeightMeasureSpec) - verticalPadding),
                  MeasureSpec.UNSPECIFIED);
      
          child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
      }
      
      @Override
      protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed,
              int parentHeightMeasureSpec, int heightUsed) {
          final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
      
          final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                  mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
                          + widthUsed, lp.width);
          final int usedTotal = mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin +
                  heightUsed;
          final int childHeightMeasureSpec = MeasureSpec.makeSafeMeasureSpec(
                  Math.max(0, MeasureSpec.getSize(parentHeightMeasureSpec) - usedTotal),
                  MeasureSpec.UNSPECIFIED);
      
          child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
      }
      
      通过代码可以看到Scrollview在测量子View的时候会将对应子View高的Mode改成MeasureSpec.UNSPECIFIED,所以即当ScrollView嵌套ListView,ListView只显示一个Item的高度。
    • 如果解决这种情况下listview显示问题呢?
      将ListView的Mode改成AT_MOST即可。
      public class MyListView extends ListView {
          public FrcListView(Context context, AttributeSet attrs) {
              super(context, attrs);
          }
      
          @Override
          protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
                 int heightSpec =MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,MeasureSpec.AT_MOST);
                 super.onMeasure(widthMeasureSpec, heightSpec);
      }
      

    34、在使用SharedPreferences使用泛型保存list数据时,碰到com.google.gson.internal.LinkedTreeMap cannot be cast to XXX问题。

    • 正常情况
      public static List<String> getDataList(String tag) {
            List<class> data =new ArrayList<class>();
            SharedPreferences sp = getSharedPreferences();
            String jsonString = sp.getString(tag, "");
            Gson gson =newGson();
            data =  gson.fromJson(jsonString, new TypeToken<List<String>>() {
            }.getType());
            return data;
        }
      
      但是如果使用泛型的方式就会报如下错误 :java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to xxx
    • 解决方案:
      public <T> List<T> getDataList(String tag,Class<T[]> clazz) {
            List<T> datalist=new ArrayList<T>();
            String strJson = preferences.getString(tag, null);
            if (null == strJson) {
                return datalist;
            }
            Gson gson = new Gson();
            datalist = Arrays.asList(gson.fromJson(strJson,clazz));
            return datalist;
        }
      

    35、Android 8.0适配报错:Only fullscreen opaque activities can request orientation

    • 方案一:
      找到你设置透明的Activity,然后在对应的theme中将android:windowIsTranslucent改为false。
      <item name="android:windowIsTranslucent">false</item>
      <item name="android:windowDisablePreview">true</item>
      
    • 方案二:
      在AndroidManifest.xml中找到设置透明的Activity去除android:screenOrientation="portrait".

    36、Android 8.0适配问题:安装apk权限,更新时下载apk后,不能调出安装界面
    在 Android 8.0 中,安装未知应用权限提高了安装未知来源应用时的安全性。此权限与其他运行时权限一样,会与应用绑定,在安装时进行提示,确保用户授予使用安装来源的权限后,此权限才会提示用户安装应用。在运行 Android 8.0 或更高版本的设备上使用此权限时,恶意下载程序将无法骗取用户安装未获得预先授权的应用,所以我们需要加入安装apk文件的权限。
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

    37、Android 8.0适配问题:通知栏无法弹出推送消息
    NotificationChannel是android8.0新增的特性,如果App的targetSDKVersion>=26,没有设置channel通知渠道的话,就会导致通知无法展示。

    import android.annotation.TargetApi;
    import android.app.Notification;
    import android.app.NotificationChannel;
    import android.app.NotificationManager;
    import android.app.PendingIntent;
    import android.content.Context;
    import android.content.Intent;
    import android.graphics.BitmapFactory;
    import android.os.Build;
    import android.provider.Settings;
    import android.support.annotation.RequiresApi;
    
    import aihuishou.aihuishouapp.R;
    
    import static android.content.Context.NOTIFICATION_SERVICE;
    
    /**
     * 类名称:NotificationUtil
     * 创建者:Create by liujc
     * 创建时间:Create on 2018/6/1 16:46
     * 描述:通知栏相关工具类
     */
    public class NotificationUtil {
        /**
         * 创建通知栏
         * @param context
         * @param notificationId
         * @param intent
         * @param ticker
         * @param title
         * @param content
         */
        public static void createNotification(Context context, int notificationId,Intent intent,String ticker,
                                                     String title,String content){
            Notification.Builder mBuilder = new Notification.Builder(context);
            PendingIntent resultPendingIntent = PendingIntent.getActivity(
                    context, notificationId, intent, PendingIntent.FLAG_UPDATE_CURRENT);
            mBuilder.setContentIntent(resultPendingIntent);
            long[] vibrate = new long[]{0, 500, 1000};
            mBuilder.setWhen(System.currentTimeMillis())// 通知产生的时间,会在通知信息里显示
                    .setTicker(ticker)
                    .setContentTitle(title)
                    .setContentText(content)
                    .setOngoing(false)
                    .setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.mipmap.launcher))
                    .setVibrate(vibrate)
                    .setAutoCancel(true)
                    .setSmallIcon(R.mipmap.launcher);
            NotificationManager mNotifyMgr =
                    (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                NotificationChannel mChannel = createNotificationChannel(CommonUtil.getChannelName(context),
                        "消息推送", NotificationManager.IMPORTANCE_DEFAULT);
                mNotifyMgr.createNotificationChannel(mChannel);
                mBuilder.setChannelId(CommonUtil.getChannelName(context));
    //            mBuilder.setNumber(2);
            }
            Notification notification = mBuilder.build();
            mNotifyMgr.notify(notificationId, notification);
        }
    
        @TargetApi(Build.VERSION_CODES.O)
        public static NotificationChannel createNotificationChannel(String channelId, String channelName, int importance) {
            NotificationChannel channel = new NotificationChannel(channelId, channelName, importance);
            channel.setShowBadge(false);
            return channel;
        }
        /**
         * 重定向到通知渠道的设置
         * @param context
         * @param channelId  通知渠道的id
         */
        public static void jumpToNotificationChannelSetting(Context context, String channelId){
            Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS);
            intent.putExtra(Settings.EXTRA_CHANNEL_ID,channelId);
            intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.getPackageName());
            context.startActivity(intent);
        }
        /**
         *
         * @param context
         * @param channelId   通知渠道的id
         */
        @RequiresApi(api = Build.VERSION_CODES.O)
        public static void deleteNotificationByChannelId(Context context, String channelId){
            NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
            mNotificationManager.deleteNotificationChannel(channelId);
        }
    }
    
    

    38、Fragment报错:android.support.v4.app.Fragment$InstantiationException: Unable to instantiate fragment make sure class name exists, is public, and has an empty constructor that is public
    解决方案:参考 https://stackoverflow.com/questions/10450348/do-fragments-really-need-an-empty-constructor

    39、Fragment 重叠 问题
    调查原因:
    主要还是因为Fragment的状态保存机制,当系统内存不足时,Fragment的主Activity被回收,Fragment的实例并没有随之被回收。
    Activity被系统回收时,会主动调用onSaveInstance()方法来保存视图层(View Hierarchy),所以当Activity通过导航再次被重建时,之前被实例化过的Fragment依然会出现在Activity中,然而从上述代码中可以明显看出,再次重建了新的Fragment,综上这些因素导致了多个Fragment重叠在一起。

    解决方案:
    在对应的activity中重写onSaveInstanceState方法,如下:

    //解决fragment
    @SuppressLint("MissingSuperCall")
    @Override
    public void onSaveInstanceState(Bundle outState) {
        //如果用以下这种做法则不保存状态,再次进来的话会显示默认的tab
        //  super.onSaveInstanceState(outState);
    }
    

    40、ARouter there's no route matched解决方法
    path都是”/app/xxxx/”,Aouter 要求path必须有至少两级的路径,是因为Arouter在寻找route的时候,是通过第一级路径,也就是这里的”app”来寻找的。Aouter通过”app”找到了route,并且在groupIndex中删除了这个路径,代表已经加载到了内存。
    不同的module使用了相同的一级路径,在Arouter第一次寻找到route的时候便删除了这个一级路径的group,因为一级路径的重复,再调用另一个module的一级路径是”app”的路由时,由于之前Warehouse.groupsIndex已经删除,便导致了there’s no route matched的错误。

    41、Android WebView允许web使用时html5自适应屏幕标签
    解决方案:

    settings.setUseWideViewPort(true); 
    settings.setLoadWithOverviewMode(true);
    
    • Html5中常用的 viewport meta 如下:
      <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
      1. width : 控制viewport的大小,可以指定一个值,如600, 或者特殊的值,如device-width为设备的宽度(单位为缩放为100%的CSS的像素)
      2. height : 和width相对应,指定高度
      3. initial-scale : 初始缩放比例,页面第一次加载时的缩放比例
      4. maximum-scale : 允许用户缩放到的最大比例,范围从0到10.0
      5. minimum-scale : 允许用户缩放到的最小比例,范围从0到10.0
      6. user-scalable : 用户是否可以手动缩放,值可以是:①yes、 true允许用户缩放;②no、false不允许用户缩放

    42、android layout_gravity属性设置失效的问题
    调查原因:

    • 当父布局LinearLayout设置android:orientation="vertical"时, 只有水平方向的leftrightcenter_horizontal设置起作用,垂直方向的设置不起作用。
    • 当父布局LinearLayout设置android:orientation="horizontal" 时, 只有垂直方向的topbottomcenter_vertical设置才起作用,水平方向的设置不起作用。

    43、Android Studio 自动导包无效及其他快捷键无效问题
    问题描述:突然Android Studio不知道怎么回事,自动导包功能,快速查找类等快捷键也无效。
    解决方案:清理Android Studio的缓存,选择工具栏 File --> Invalidate Caches /Restart... --> Invalidate and Restart 重启Android Studio即可。

    44、Failed to resolve: com.android.support:appcompat-v7:27.+ 报错解决方法
    问题描述: 用Android Studio新建一个android项目完成后报错Failed to resolve: com.android.support:appcompat-v7:27.+
    解决方案:
    新建Android项目中的app -> build.gradledependencies 默认配置如下:

    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
            exclude group: 'com.android.support', module: 'support-annotations'
        })
        compile 'com.android.support:appcompat-v7:27.+'
        compile 'com.android.support.constraint:constraint-layout:1.0.2'
        testCompile 'junit:junit:4.12'
    }
    

    针对Android Studio 2.3版本应该在项目根目录下 Project -> build.gradle 中的 allprojects配置如下:

    allprojects {
        repositories {
            jcenter()
            maven { url "https://maven.google.com" } //(新增)
        }
    }
    

    针对Android Studio 3.0版本应该在项目根目录下 Project -> build.gradle 中的 allprojects配置如下:

    allprojects {
        repositories {
            jcenter()
            google()
        }
    }
    

    相关文章

      网友评论

        本文标题:Android开发小问题汇总二

        本文链接:https://www.haomeiwen.com/subject/cggxzftx.html