Android开发中小问题汇总四

作者: 闲庭 | 来源:发表于2018-07-03 11:35 被阅读34次

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

【Android开发中小问题汇总目录】
【Android开发中小问题汇总一】
【Android开发中小问题汇总二】
【Android开发中小问题汇总三】
【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);
}

相关文章

网友评论

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

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