Android開發小問題彙總二
此排序沒有任何優先順序或者重要程度。
此筆記只為記錄平時開發中碰到的經常用到確不太注意的一些問題,每次用過就忘記,還要重新搜尋解決方案,所以在此積累下平時開發中碰到的一些常用而又容易忘記的簡單小bug。
本來想一直在同一篇文章中不斷更新,可發現簡書沒辦法釋出篇幅太大的文章,所以拆開記錄,雖然這樣零散了,可能不太好查詢,但是我會附上對應的連結。
ofollow,noindex">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" />
- width : 控制viewport的大小,可以指定一個值,如600, 或者特殊的值,如device-width為裝置的寬度(單位為縮放為100%的CSS的畫素)
- height : 和width相對應,指定高度
- initial-scale : 初始縮放比例,頁面第一次載入時的縮放比例
- maximum-scale : 允許使用者縮放到的最大比例,範圍從0到10.0
- minimum-scale : 允許使用者縮放到的最小比例,範圍從0到10.0
- user-scalable : 使用者是否可以手動縮放,值可以是:①yes、 true允許使用者縮放;②no、false不允許使用者縮放
42、android layout_gravity屬性設定失效的問題
調查原因:
-
當父佈局LinearLayout設定
android:orientation="vertical"
時, 只有水平方向的left
,right
,center_horizontal
設定起作用,垂直方向的設定不起作用。 -
當父佈局LinearLayout設定
android:orientation="horizontal"
時, 只有垂直方向的top
,bottom
,center_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.gradle
中dependencies
預設配置如下:
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() } }