void checkThread() { if (mThread != Thread.currentThread()) { throw new CalledFromWrongThreadException( "Only the original thread that created a view hierarchy can touch its views."); } } 複製代碼
在執行onCreate的時候這個判斷並無執行到html
在 fragment#onResume 中從新調整 window 佈局java
android.view.WindowManager.LayoutParams lp = window.getAttributes();
lp.width = WindowManager.LayoutParams.MATCH_PARENT;
lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
window.setAttributes(lp);
複製代碼
<item name="android:windowIsFloating">true</item> 複製代碼
此時 window 爲 wrap_content,若是出現左右空白,則考慮使用上個問題的方案。linux
在線上項目中咱們遇到一個場景:當應用按下 Home 退回後臺,而後過一段時間以後從後臺拉起咱們的項目。極少數機型在主頁進行多個 fragment 的切換時出現了 fragment 的重疊。通過定位以後發現,這些機型的運存偏小,性能誤差,出現這種現象的緣由是因爲內存的壓力的緣由,系統並不知後臺的程序哪個才須要保持運行,就會嘗試回收內存佔用較大的頁面,當咱們的頁面被系統銷燬時,fragmentActivity#onSaveInstanceState 被執行並保存了一些瞬態信息,好比界面 fragment 的視圖信息。當咱們再次拉起應用的時候,會讓原來的 fragmentActivity 重建並從新構建了一個新的 fragment ,此時會疊加到已經被恢復的 fragment 之上致使重疊。android
比較暴力的作法是不讓 activity 保存狀態,好比git
@Override
public void onSaveInstanceState(Bundle outState) {
//直接不調用 super.onSaveInstanceState(outState);
//或者直接傳遞空數據
super.onSaveInstanceState(new Bundle());
}
複製代碼
比較優雅的作法是,好比github
@Override public void onSaveInstanceState(Bundle outState) { getSupportFragmentManager().putFragment(outState, you_key, CusFragment); super.onSaveInstanceState(outState); } //在onCreate的時候判斷是否已經存在保存的信息 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (savedInstanceState != null) { CusFragment fragment = (CusFragment)getSupportFragmentManager().getFragment(savedInstanceState, you_key); } else { //init CusFragment } } 複製代碼
出現 TransactionTooLargeException
異常時,由於線上咱們使用了 FragmentStatePagerAdapter 做爲 fragment 適配器爲了儘量過緩存下瀏覽過的 fragment 以得到更好的體驗,承載多個 FragmentStatePagerAdapter#saveState 會被調用並對每個 fragment 的 bundle 數據進行保存。因爲咱們的 bundle 較大,而且保存下來的 bundle 並不會由於 fragment 被銷燬而銷燬,因此須要保存的 bundle 數據會一直增加,直到出現TransactionTooLargeException
異常. 咱們參考stackoverflow相關問題 直接重載 saveState 丟棄 states 內容。面試
public Parcelable saveState() { Bundle bundle = (Bundle) super.saveState(); bundle.putParcelableArray("states", null); // Never maintain any states from the base class, just null it out return bundle; } 複製代碼
另外推薦 toolargetool 工具能夠在開發中實時觀測頁面內存變化。sql
notifyItemRemoved
方法並不會移除列表的數據源的數據項致使數據源中的數據與列表Item數目不一致,須要同步刷新數據源。數據庫
由於recyclerview
存在ItemAnimator,且在刪除/更新/插入Item時會觸發,可設置不支持該動畫便可。後端
((SimpleItemAnimator)recyclerView.getItemAnimator()).setSupportsChangeAnimations(false); 複製代碼
這個問題通過定位存在於 viewholder 中的某個 view 可能提早獲取到焦點。 同時在。alibaba-vlayout 庫中也發現有人反饋改問題。 issues-255 解決的方法是在 Recyclerview中外層父佈局中添加 android:descendantFocusability="blocksDescendants"
用於父佈局覆蓋 Recyclerview 優先搶佔焦點。
緣由是在 findOneVisibleChild
計算出來的 start 和 end 已經超過了 reclclerview 的 start 和 end.通過研究源碼獲得如下。
findFirstCompletelyVisibleItemPosition -> -1
findLastCompletelyVisibleItemPosition -> -1
findFirstVisibleItemPosition -> 正常
findLast
複製代碼
緣由是在構建 Holder 的時候,獲取的 view 視圖的時候 LayoutInflater#inflate() 最後的參數傳遞錯誤,若是傳遞的 null,則默認 xml 佈局加載後的 View 的寬高信息失效
@Override public MyHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(context).inflate(R.layout.item_layout,parent, false); MyHolder holder = new MyHolder(view); return holder; } 複製代碼
新版本已不存在這個問題。
public class ClickMovementMethod implements View.OnTouchListener { private LongClickCallback longClickCallback; public static ClickMovementMethod newInstance() { return new ClickMovementMethod(); } @Override public boolean onTouch(final View v, MotionEvent event) { if (longClickCallback == null) { longClickCallback = new LongClickCallback(v); } TextView widget = (TextView) v; // MovementMethod設爲空,防止消費長按事件 widget.setMovementMethod(null); CharSequence text = widget.getText(); Spannable spannable = Spannable.Factory.getInstance().newSpannable(text); int action = event.getAction(); if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_UP) { int x = (int) event.getX(); int y = (int) event.getY(); x -= widget.getTotalPaddingLeft(); y -= widget.getTotalPaddingTop(); x += widget.getScrollX(); y += widget.getScrollY(); Layout layout = widget.getLayout(); int line = layout.getLineForVertical(y); int off = layout.getOffsetForHorizontal(line, x); ClickableSpan[] link = spannable.getSpans(off, off, ClickableSpan.class); if (link.length != 0) { if (action == MotionEvent.ACTION_DOWN) { v.postDelayed(longClickCallback, ViewConfiguration.getLongPressTimeout()); } else { v.removeCallbacks(longClickCallback); link[0].onClick(widget); } return true; } } else if (action == MotionEvent.ACTION_CANCEL) { v.removeCallbacks(longClickCallback); } return false; } private static class LongClickCallback implements Runnable { private View view; LongClickCallback(View view) { this.view = view; } @Override public void run() { // 找到可以消費長按事件的View View v = view; boolean consumed = v.performLongClick(); while (!consumed) { v = (View) v.getParent(); if (v == null) { break; } consumed = v.performLongClick(); } } } } textView.setOnTouchListener(ClickMovementMethod.newInstance()); 複製代碼
重寫ViewPager onTouchEvent 和 onInterceptTouchEvent 並返回false,不處理任何滑動事件
@Override public boolean onTouchEvent(MotionEvent arg0) { return false; } @Override public boolean onInterceptTouchEvent(MotionEvent arg0) { return false; } 複製代碼
imageViewPager 爲普通的 Viewpager 對象
imageListInfo爲存放圖片信息的list,imageShowHeight爲業務須要顯示高度,經過切換時動態計算調整
imageViewPager.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { imageViewPager.getViewTreeObserver().removeOnGlobalLayoutListener(this); //根據viewpager的高度,拉伸顯示圖片的寬度調整高度。 ViewGroup.LayoutParams layoutParams = imageViewPager.getLayoutParams(); layoutParams.height = imageListInfo.imageShowHeight[0]; imageViewPager.setLayoutParams(layoutParams); } }); imageViewPager.setAdapter(imagePagerAdapter); imageViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { if (position == imageListInfo.getImageListSize() - 1) { return; } int height = (int) (imageListInfo.imageShowHeight[position] * (1 - positionOffset) + imageListInfo.imageShowHeight[position + 1] * positionOffset); ViewGroup.LayoutParams params = imageViewPager.getLayoutParams(); params.height = height; imageViewPager.setLayoutParams(params); } @Override public void onPageSelected(int position) { if (!clickListBySelf) { toSelectIndex(imageListInfo.selected, position); } } @Override public void onPageScrollStateChanged(int state) { } }); 複製代碼
CoordinatorLayout.Behavior behavior =((CoordinatorLayout.LayoutParams)mAppBarLayout.getLayoutParams()).getBehavior(); if (behavior instanceof AppBarLayout.Behavior) { AppBarLayout.Behavior appBarLayoutBehavior = (AppBarLayout.Behavior) behavior; int topAndBottomOffset = appBarLayoutBehavior.getTopAndBottomOffset(); if (topAndBottomOffset != 0) { appBarLayoutBehavior.setTopAndBottomOffset(0); } 複製代碼
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams(); AppBarLayout.Behavior behavior = (AppBarLayout.Behavior) params.getBehavior(); behavior.setDragCallback(new AppBarLayout.Behavior.DragCallback() { @Override public boolean canDrag(@NonNull AppBarLayout appBarLayout) { return false; } }); 複製代碼
Edittext
監聽未獲取焦點的Edittext的點擊事件,第一次點擊觸發OnFocusChangeListener,在獲取焦點的狀況下才能響應onClickListener
在xml佈局中對listview或gridview設置Android:choiceMode="singleChoice",並使用state_activated狀態來代替state_selected狀態。(2016.12.10)
在xml定義的Button中,添加如下樣式定義
style="?android:attr/borderlessButtonStyle" 複製代碼
前者在按下並擡起時發生,後者有一個附加條件時Android會確保點擊以後在短期內沒有再次點擊纔會觸發。經常使用於若是須要監聽單擊和雙擊事件。
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" > //左 <item> <rotate android:fromDegrees="45" android:pivotX="85%" android:pivotY="135%"> <shape android:shape="rectangle"> <size android:width="16dp" android:height="16dp" /> <solid android:color="#7d72ff" /> </shape> </rotate> </item> //右 <item> <rotate android:fromDegrees="45" android:pivotX="15%" android:pivotY="-35%"> <shape android:shape="rectangle"> <size android:width="16dp" android:height="16dp" /> <solid android:color="#7d72ff" /> </shape> </rotate> </item> //上/正 <item> <rotate android:fromDegrees="45" android:pivotX="-40%" android:pivotY="80%"> <shape android:shape="rectangle"> <size android:width="16dp" android:height="16dp"/> <solid android:color="#7d72ff"/> </shape> </rotate> </item> //下 <item> <rotate android:fromDegrees="45" android:pivotX="135%" android:pivotY="15%"> <shape android:shape="rectangle"> <size android:width="16dp" android:height="16dp"/> <solid android:color="#7d72ff"/> </shape> </rotate> </item> 複製代碼
在大神 app 信息流快捷評論模塊中,在交付快捷評論動畫的時候發現,使用屬性動畫實現的抖動效果在部分機型上出現閃爍。而咱們的實現抖動效果是經過 View.ROTATION
來實現的。通過研究,部分機型由於硬件加速的緣由致使的。爲動畫 view 進行如下設置
view.setLayerType(View.LAYER_TYPE_HARDWARE,null);
複製代碼
不一樣於其餘 ViewGroup 控制子 View 的排版,ConstraintLayout 須要構建 ConstraintSet
對象來粘合。 在手動添加子 View 的場景下,能夠經過 ConstraintSet#clone(ConstraintLayout constraintLayout)
來克隆當前已有 ConstraintLayout 的排版信息,而後最後調用 ConstraintSet#applyTo(ConstraintLayout constraintLayout)
確認最終的排版信息。
在大神信息流中,有一些卡片信息須要設置單行縮略。在 MTL 兼容測試過程當中發現有一些機型顯示異常,通過概括及校驗,這部分機型的版本都是 < 6.0。 經過在 stackoverflow 也找到了相同的問題場景 text ellipsize behavior in android version < 6.0 . 針對這部分版本的手機,咱們須要在設置單行的時候把 android:maxLines="1"
改爲 android:singleLine="true"
。即便 IDE 提示該 API 已通過期了!
android:focusable="true" android:focusableInTouchMode="true" 複製代碼
從 How to remove all notifications when an android app (activity or service) is killed? 的諸多討論中學習到, Service#onTaskRemoved
是咱們的App被清理以後Service的回調。嘗試過一下方法並不能達到清除的效果。
@Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
NotificationManager nManager = ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE));
nManager.cancelAll();
}
複製代碼
在線上應用中,因爲咱們的通知相似於將軍令這種有定時更新的功能,須要完全乾掉全部serivce承載的功能,下面方法可行
@Override public void onTaskRemoved(Intent rootIntent) { super.onTaskRemoved(rootIntent); stopSelf(); stopForeground(true); } 複製代碼
因爲咱們在優化 Application 啓動時間時,打算移除 applciation 全部有關靜態申明的變量,其中就包含全局 context 這個變量。咱們參考的是 leakCanary 庫的作法,使用 ContentProvider 來承載全局 context 的獲取,緣由是在 ActivityThread 的初始化流程中,ContentProvider#onCreate() 是在 Application#attachBaseContext(Context) 和 Application#onCreate() 之間的。因此獲取的 context 是有效的
class ContextProvider : ContentProvider() { companion object { private lateinit var mContext: Context private lateinit var mApplication: Application fun getGlobalContext(): Context = mContext fun getGlobalApplication(): Application = mApplication } override fun onCreate(): Boolean { mContext = context!! mContext = context!!.applicationContext as Application return false } override fun insert(uri: Uri, values: ContentValues?): Uri? = null override fun query(uri: Uri, projection: Array<out String>?, selection: String?, selectionArgs: Array<out String>?, sortOrder: String?): Cursor? = null override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array<out String>?): Int = -1 override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int = -1 override fun getType(uri: Uri): String? = null } //manifest申明 <!-- Context提供者 --> <provider android:name=".ContextProvider" android:authorities="${your_application_id}.contextprovider" android:exported="false" /> 複製代碼
@GET( BASE_URL + "index/login" ) Observable< LoginResult > requestLogin( @QueryMap(encoded = true) Map< String, String > params ); final Map< String, String > paramsMap = new ParamsProvider.Builder(). append( "username", account ). append( "password",URLEncoded(password) ). 複製代碼
好比登錄,encode = true 表示未對特殊字符進行url編碼,默認是false。
通常而言,若是要在多線程環境下使用數據庫,則確保多個線程中使用的是同一個SQLiteDataBase對象,該對象對應一個db文件。
特殊狀況,若是多個 SQLiteDataBase 打開同一個 db 文件,同時使用不一樣線程同時寫(insert,update,exexSQL)會致使在 SQLiteStatement.native_execute
方法時可能致使異常。這個異常來自本地方法裏面,僅僅在Java對有對 SQLiteDataBase 進行同步鎖保護。可是多線程讀(query)返回的事 SQLiteCursor保存查詢條件並無馬上執行查詢,僅僅在須要時加載部分數據,能夠多線程不一樣 SQLiteDataBase 進行讀。
若是要處理上述問題,可使用 「一個線程寫,多個線程同時讀,每一個線程都用各自SQLiteOpenHelper。」
在android 3.0版本以上 打開 enableWriteAheadLogging。當打開時,它容許一個寫線程與多個讀線程同時在一個SQLiteDatabase上起做用。實現原理是寫操做實際上是在一個單獨的文件,不是原數據庫文件。因此寫在執行時,不會影響讀操做,讀操做讀的是原數據文件,是寫操做開始以前的內容。在寫操做執行成功後,會把修改合併會原數據庫文件。此時讀操做才能讀到修改後的內容。可是這樣將花費更多的內存。
Intent 傳輸數據的機制中,用到了 Binder。Intent 中的數據,會做爲 Parcel 被存儲在 Binder 的事務緩衝區(Binder transaction buffer)中的對象進行傳輸.而這個 Binder 事務緩衝區具備一個有限的固定大小,當前爲 1MB。你可別覺得傳遞 1MB 如下的數據就安全了,這裏的 1MB 空間並非當前操做獨享的,而是由當前進程所共享。也就是說 Intent 在 Activity 間傳輸數據,自己也不適合傳遞太大的數據.
參考阿里 《Android 開發者手冊》 對於Activity間數據通信數據較大,避免使用Intent+Parcelable的方式,能夠考慮使用EventBus等代替方案,避免 TransactionTooLargeException
。EventBus使用黏性事件來解決,可是針對Activity重建又能拿到Intent而EventBus則不能夠,因此須要根據業務來調整。
popupWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
複製代碼
參考 github.com/zhitaocai/T… 項目,可是在小米3或者小米Note(4.4.4)手機上
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
複製代碼
mContext 須要使用 ApplicationContext 才能生效。
因爲谷歌把 Toast 設置爲系統消息權限,能夠參考 Android關閉通知消息權限沒法彈出Toast的解決方案 維護本身的消息隊列.
在 AndroidManifest.xml 中聲明一下 meta-data
<meta-data android:name="andriod.max_aspect" android:value="ratio_float"/> 複製代碼
或者使用 android:maxAspectRatio="ratio_float"(API LEVEL 26)
ratio_float 通常爲屏幕分辨率高寬比。 其餘好比凹槽區域,圓角切割等問題能夠參考市面上最新的vivo機型對應的 vivo開放平臺 文檔。
因爲部分華爲中,若是app註冊超過500個BroadcastReceiver就會拋出 「 Register too many Broadcast Receivers 」 異常。經過分析發現其內部有一個白名單,本身能夠經過建立一個新的app,使用微信包名進行測試,發現並無這個限制。經過反射 LoadedApk 類拿到 mReceiverResource 中的 mWhiteList 對象添加咱們的包名就能夠了。 能夠參考 github.com/llew2011/Hu… 這個項目。
在咱們項目中首頁曾經有個控件叫 「AdView」 用來顯示廣告用的,離譜的時這個 View 在咱們 setVisibility() 的時候華爲手機上又調用了一次 setVisibility(View.GONE). 後面更改 view 的名稱解決了這個問題。這個問題應該是由於 ROM 內部經過必定的規則來判斷咱們的應用中視圖 View 是否爲廣告視圖進而強制隱藏致使的。因此在寫 App 顯示廣告相關的視圖時,命名儘可能隱晦一點哈。
參考 網易考拉實現的適配方法
今天收到魅族渠道的警報稱「推送內容可能過長」。IM功能針對離線設備走設備商的推送,魅族推送限制了title標題1-32字符,content內容1-100字符。若是頻繁推送超過限制的通知,魅族推送服務器可能不會下發推送到魅族設備。故服務端限制發送到魅族服務器的消息標題和內容長度解決。
判斷啓動頁面是不是根節點(推薦)
if(!isTaskRoot()){ finish(); return } 複製代碼
或者判斷Activity是否多了 FLAG_ACTIVITY_BROUGHT_TO_FRONT ,這個tag是該場景緻使的
if ((getIntent().getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 0) { finish(); return; } 複製代碼
因爲咱們的應用LauncherActivity用於分發不一樣場景的入口,A邏輯進入特殊場景頁面A,B邏輯進入主頁面B。
if (後端控制是否須要進入特殊場景頁面) { boolean goToA = false; if (getIntent() != null) { String action = getIntent().getAction(); Set<String> category = getIntent().getCategories(); if (TextUtils.equals(action, "android.intent.action.MAIN")) { if (category != null && category.contains("android.intent.category.LAUNCHER")) { Intent intent = new Intent(this, 頁面A.class); intent.setData(getIntent().getData()); startActivity(intent); goToA = true; } } } if (! goToA) { goToMainActivity(); } } else { goToMainActivity(); } 複製代碼
可參考我另外一篇文章 對線上項目拉起應用場景的思考總結
因爲咱們項目須要處理沉浸式,因此針對 android:windowIsTranslucent 的屬性默認打開的。可是線上發現部分 8.0設備出現詭異的 crash,緣由是咱們對於頁面的 orientation 申明都統一爲 portrait 。查閱 android 源碼的更新發如今 8.0 源碼的邏輯裏面這兩個邏輯居然不兼容,隨後在 8.0 版本後谷歌進行了修復。可是國內部分 ROM 看起來並無修復這個問題。後面同事提供了一個比較取巧的方案,經過爲頁面指定 android:screenOrientation="behind" 來避免 8.0 版本的問題同時兼容全部 android 版本。
Android 9(API級別28)開始,默認狀況下禁用明文支持
<?xml version="1.0" encoding="utf-8"?> <manifest ...> <uses-permission android:name="android.permission.INTERNET" /> <application ... android:usesCleartextTraffic="true" ...> ... </application> </manifest> 複製代碼
參考 I am getting IMEI null in Android Q 的問題討論。
官網對 Android 10 設備標識符的更新說明 設備標識符 指出,本來使用 READ_PHONE_STATE
可獲取的 IMEI 和序列號的方式,已經沒法支持了。除非你的 App 被 ROM 設置爲系統級別的 App。
谷歌雖然給出了 惟一標識符最佳作法,可是該方案適配的場景有限。最好的方法仍是使用 UUID 實現本身的一套設備識別方案。
參考 Android Q 適配指南 讓你少走一堆彎路 的寫法。
public static String getUUID() { String serial = null; String m_szDevIDShort = "35" + Build.BOARD.length() % 10 + Build.BRAND.length() % 10 + Build.CPU_ABI.length() % 10 + Build.DEVICE.length() % 10 + Build.DISPLAY.length() % 10 + Build.HOST.length() % 10 + Build.ID.length() % 10 + Build.MANUFACTURER.length() % 10 + Build.MODEL.length() % 10 + Build.PRODUCT.length() % 10 + Build.TAGS.length() % 10 + Build.TYPE.length() % 10 + Build.USER.length() % 10; //13 位 try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { serial = android.os.Build.getSerial(); } else { serial = Build.SERIAL; } //API>=9 使用serial號 return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString(); } catch (Exception exception) { //serial須要一個初始化 serial = "serial"; // 隨便一個初始化 } //使用硬件信息拼湊出來的15位號碼 return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString(); } 複製代碼
參考 CI 討論區 添加 dist: precise
及 before_install 項中新增 sdkmanager指令
。具體可參考個人 開源項目配置
這個問題的緣由是構建工具繪製行比較複雜決策來肯定主 dex 文件中須要的類以便應用可以正常的啓動。若是啓動期間須要的任何類在主 dex 中未能找到,則會拋出上述異常。全部必需要 multiDexKeepFile 或 multiDexKeepProguard 屬性中聲明他們,手動將這些類指定爲主 dex 文件中的必需項。
建立 multidex-new.txt文件,寫入如下新增的類
com/example/Main2.class
com/example/Main3.class
複製代碼
建立 meltidex-new.pro,寫入如下 keep 住的類
-keep class com.example.Main2
-keep class com.example.Main3
複製代碼
而後在gradle multiDexKeepFile屬性 和 multiDexKeepProguard屬性聲明上述文件
android { buildTypes { release { multiDexKeepFile file 'multidex-new.txt' multiDexKeepProguard 'multidex-new.pro' ... } } } 複製代碼
這個問題出如今使用 Kotlin 編譯時,從 Kotlin1.3.30 版本開始 ndroid.compileOptions中的Java版本推斷出JVM目標,若是同時設置了sourceCompatibility和targetCompatibility,則選擇「1.8」到那個或更高. 能夠經過指定 JavaVersion 1.6 來解決這個問題。
sourceCompatibility JavaVersion.VERSION_1_6 targetCompatibility JavaVersion.VERSION_1_6 複製代碼
Issue 中表示,AGP(Android Gradle Plugin)3.4 已解決脫糖問題,可嘗試升級解決。
不一樣之處在於,findBinding將遍歷父節點,而若是使用getBinding時當view不是跟節點會返回null。
參考 issues 的回答
If you're using Android plugin for Gradle 3.0.0 or higher, the plugin automatically matches each variant of your app with corresponding variants of its local library module dependencies for you. That is, you should no longer target specific variants of local module dependencies, show as below
dependencies { // Adds the 'debug' varaint of the library to the debug varaint of the app debugCompile project(path: ':my-library-module', configuration: 'debug') // Adds the 'release' varaint of the library to the release varaint of the app releaseCompile project(path: ':my-library-module', configuration: 'release') } 複製代碼
linux/mac OS 上使用 「:」 分割多個classpath路徑,window使用 「;」 分割。
若是linux/mac OS 路徑存在空格,暫時避免,使用多種方式嘗試未果=。=。
java.lang.NoSuchMethodError: No direct method <init> (Landroidx/databinding/DataBindingComponent;Landroid/view/View;I)V in class Landroidx/databinding/ViewDataBinding; or its super classes (declaration of 'androidx.databinding.ViewDataBinding' 複製代碼
緣由咱們使用的 aar 庫中使用了舊版本 gradle 編譯,新版本主端 gradle 升級了,致使舊的 ViewDataBinding 構造器簽名匹配不上新版 androidx.databinding.ViewDataBinding 的簽名。
//舊版本 protected ViewDataBinding(DataBindingComponent bindingComponent, View root, int localFieldCount) //新版本 protected ViewDataBinding(Object bindingComponent, View root, int localFieldCount) 複製代碼
幸運的是,3.4.1已經修復了。更改 3.4.0 -> 3.4.1 就能夠了。
在使用 AS 鏈接華爲真機調試的時候,IDE 一直出現 「Warning: debug info can be unavailable. Please close other application using ADB: Restart ADB integration and try again」 的錯誤提示。 重啓 ADB 無數遍和關閉除 IDE 意外可能鏈接 ADB 的軟件都無效,最終重啓真機解決。緣由是ADB鏈接的問題,由於有時ADB會在真實/虛擬設備上緩存一個無效的鏈接,而且因爲該鏈接繁忙致使也沒法鏈接到該設備。
有時項目申明瞭代理或者 AS 配置了代理以後再清除代理。發現項目 run 的時候依然會跑到代理上去。緣由是本地 gradle 目錄下也緩存了一份代理信息。須要完全刪除。
mac : ~/.gradle/gradle.properties 刪除對應代理信息就能夠了。
其餘 OS:可參考這個思路找到對應文件刪除便可
複製代碼
git reset --soft HEAD^
撤銷當前的commit,若是隻是修改提示,則使用
git commit --amend
複製代碼
git rm -r --cached . git add . git commit -m 'update .gitignore' 複製代碼
緣由是多媒體焦點被通話搶奪以後播放音量被充值,解決方法可參考 github.com/google/ExoP…
解決手段: File->Project Structure中修改Build tools version
// 1 Log.d("編碼測試-字符","a".length.toString()) // 1 Log.d("編碼測試-字符","測".length.toString()) // 5 Log.d("編碼測試-字符","測試abc".length.toString()) // 1 Log.d("編碼測試-UTF_8","a".toByteArray(Charsets.UTF_8).size.toString()) // 3 Log.d("編碼測試-UTF_8","測".toByteArray(Charsets.UTF_8).size.toString()) // 9 ,UTF_8 支持使用 1,2,3,4個字節進行編碼,一箇中文佔3個字節,一個英文佔1個字節 Log.d("編碼測試-UTF_8","測試abc".toByteArray(Charsets.UTF_8).size.toString()) // 1 Log.d("編碼測試-US_ASCII","a".toByteArray(Charsets.US_ASCII).size.toString()) // 1 Log.d("編碼測試-US_ASCII","測".toByteArray(Charsets.US_ASCII).size.toString()) // 5,一箇中文佔1個字節,一個英文佔1個字節 Log.d("編碼測試-US_ASCII","測試abc".toByteArray(Charsets.US_ASCII).size.toString()) // 1 Log.d("編碼測試-ISO_8859_1","a".toByteArray(Charsets.ISO_8859_1).size.toString()) // 1 Log.d("編碼測試-ISO_8859_1","測".toByteArray(Charsets.ISO_8859_1).size.toString()) // 5,一箇中文佔1個字節,一個英文佔1個字節 Log.d("編碼測試-ISO_8859_1","測試abc".toByteArray(Charsets.ISO_8859_1).size.toString()) // 4,存在代理對 +2個字節 Log.d("編碼測試-UTF_16","a".toByteArray(Charsets.UTF_16).size.toString()) // 4,存在代理對 +2個字節 Log.d("編碼測試-UTF_16","測".toByteArray(Charsets.UTF_16).size.toString()) // 12,UTF_16只支持2或者4個字節編碼,一箇中文佔2個字節,一個英文佔2個字節 Log.d("編碼測試-UTF_16","測試abc".toByteArray(Charsets.UTF_16).size.toString()) // 2 Log.d("編碼測試-UTF_16BE","a".toByteArray(Charsets.UTF_16BE).size.toString()) // 2 Log.d("編碼測試-UTF_16BE","測".toByteArray(Charsets.UTF_16BE).size.toString()) // 10,一箇中文佔2個字節,一個英文佔2個字節 Log.d("編碼測試-UTF_16BE","測試abc".toByteArray(Charsets.UTF_16BE).size.toString()) // 2 Log.d("編碼測試-UTF_16LE","a".toByteArray(Charsets.UTF_16LE).size.toString()) // 2 Log.d("編碼測試-UTF_16LE","測".toByteArray(Charsets.UTF_16LE).size.toString()) // 10,一箇中文佔2個字節,一個英文佔2個字節 Log.d("編碼測試-UTF_16LE","測試abc".toByteArray(Charsets.UTF_16LE).size.toString()) // 8 Log.d("編碼測試-UTF_32","a".toByteArray(Charsets.UTF_32).size.toString()) // 8 Log.d("編碼測試-UTF_32","測".toByteArray(Charsets.UTF_32).size.toString()) // 24 utf-32支持 4個字節編碼,同 utf-16 原理,一箇中文佔4個字節,一個英文佔4個字節 Log.d("編碼測試-UTF_32","測試abc".toByteArray(Charsets.UTF_32).size.toString()) // 4 Log.d("編碼測試-UTF_32LE","a".toByteArray(Charsets.UTF_32LE).size.toString()) // 4 Log.d("編碼測試-UTF_32LE","測".toByteArray(Charsets.UTF_32LE).size.toString()) //20,一箇中文佔4個字節,一個英文佔4個字節 Log.d("編碼測試-UTF_32LE","測試abc".toByteArray(Charsets.UTF_32LE).size.toString()) // 4 Log.d("編碼測試-UTF_32BE","a".toByteArray(Charsets.UTF_32BE).size.toString()) // 4 Log.d("編碼測試-UTF_32BE","測".toByteArray(Charsets.UTF_32BE).size.toString()) // 20,一箇中文佔4個字節,一個英文佔4個字節 Log.d("編碼測試-UTF_32BE","測試abc".toByteArray(Charsets.UTF_32BE).size.toString()) 複製代碼
重點熟悉下 Unicode 編碼標識的 emoji 下針對多平面 emoji 的拆分邏輯。可參考這篇文章
咱們業務代碼中曾出現過 「爲了防止某個頁面在短期內重複進入,用 System.currentTimeMillis()
爲了記錄了上一次進入頁面的時刻」。結果有個用戶修改了系統時間以後,發現沒法二次進入這個頁面,緣由是系統時間修改致使二次進去時 System.currentTimeMillis()
與上次記錄的時間作差結果出現了極大的偏差。因此,推薦如下方法。
SystemClock.elapsedRealtime();
//別再用如下方法
System.currentTimeMillis()
複製代碼
想了解更細節的緣由可查看 Don't Use System.currentTimeMillis() for Time Interval
關於更細節的面試經歷,可參考 這些年,我所經歷的面試|寫給疫情下的應屆生及求職者 一文。