Android開發小問題彙總二

此排序沒有任何優先級或者重要程度。
此筆記只爲記錄平時開發中碰到的經常用到確不太注意的一些問題,每次用過就忘記,還要重新搜索解決方案,所以在此積累下平時開發中碰到的一些常用而又容易忘記的簡單小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()
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章